salai 0.1.0
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/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/commands/call.d.ts +3 -0
- package/dist/commands/call.js +44 -0
- package/dist/commands/call.js.map +1 -0
- package/dist/commands/cart.d.ts +3 -0
- package/dist/commands/cart.js +116 -0
- package/dist/commands/cart.js.map +1 -0
- package/dist/commands/prices.d.ts +3 -0
- package/dist/commands/prices.js +45 -0
- package/dist/commands/prices.js.map +1 -0
- package/dist/commands/recommend.d.ts +3 -0
- package/dist/commands/recommend.js +21 -0
- package/dist/commands/recommend.js.map +1 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.js +50 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/stores.d.ts +3 -0
- package/dist/commands/stores.js +45 -0
- package/dist/commands/stores.js.map +1 -0
- package/dist/formatters/index.d.ts +7 -0
- package/dist/formatters/index.js +392 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/mcpClient.d.ts +39 -0
- package/dist/mcpClient.js +67 -0
- package/dist/mcpClient.js.map +1 -0
- package/dist/salai.d.ts +13 -0
- package/dist/salai.js +82 -0
- package/dist/salai.js.map +1 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Salai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# salai
|
|
2
|
+
|
|
3
|
+
> Salai grocery intelligence CLI — search products, compare prices, and manage your cart from the terminal.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx salai search "חלב"
|
|
7
|
+
npx salai retailers
|
|
8
|
+
npx salai cart add 7290019489443
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> **Looking for MCP integration?** See [`@salai/mcp`](../mcp-cli) for Cursor / Claude Desktop.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Use directly with npx (no install needed)
|
|
17
|
+
npx salai <command>
|
|
18
|
+
|
|
19
|
+
# Or install globally
|
|
20
|
+
npm i -g salai
|
|
21
|
+
salai <command>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
export SALAI_API_KEY="your-api-key-here" # add to ~/.zshrc or ~/.bashrc
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Get your API key from the Salai app → **Profile → API Key → Generate**.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Commands
|
|
35
|
+
|
|
36
|
+
### Global Options
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
salai [options] <command>
|
|
40
|
+
|
|
41
|
+
-k, --api-key <key> Salai API key (or SALAI_API_KEY env var)
|
|
42
|
+
--url <url> MCP endpoint URL (default: https://mcp.salai.co.il/mcp)
|
|
43
|
+
--json Output raw JSON instead of formatted tables
|
|
44
|
+
-v, --version Print version
|
|
45
|
+
-h, --help Show help
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Search
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
salai search <query> # Semantic product search (Hebrew)
|
|
52
|
+
--limit <n> # Max results (default 20)
|
|
53
|
+
--mode <fast|hybrid|ai> # Search mode
|
|
54
|
+
--store-scope <scope> # selected_only | retailer_wide | all_limited
|
|
55
|
+
|
|
56
|
+
salai autocomplete <query> # Fast autocomplete lookup
|
|
57
|
+
salai ac <query> # Alias
|
|
58
|
+
--limit <n> # Max results (default 15)
|
|
59
|
+
--method <text|semantic> # Search method (default text)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Pricing
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
salai prices <itemCode...> # Get prices for item codes
|
|
66
|
+
--stores <rid:sid,...> # Limit to specific stores
|
|
67
|
+
|
|
68
|
+
salai compare <code:qty...> # Compare across retailers
|
|
69
|
+
--stores <rid:sid,...> # Limit to specific stores
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Stores
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
salai stores # List all online stores
|
|
77
|
+
salai retailers # List all retailers
|
|
78
|
+
salai store # Show selected store context
|
|
79
|
+
salai store set <rid> <sid> # Set your selected store
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Cart
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
salai cart # Show your current cart
|
|
86
|
+
salai cart show --cart-id <id> # Show a specific cart
|
|
87
|
+
|
|
88
|
+
salai cart add <itemCode> # Add item to cart
|
|
89
|
+
--qty <n> # Quantity (default 1)
|
|
90
|
+
--cart-id <id> # Cart ID (auto-resolved if omitted)
|
|
91
|
+
|
|
92
|
+
salai cart set-qty <code> <qty> # Set quantity (0 = remove)
|
|
93
|
+
salai cart remove <itemCode> # Remove item
|
|
94
|
+
salai cart compare # Compare cart across stores
|
|
95
|
+
salai cart delete <cartId> # Delete a cart
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Recommendations
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
salai recommend <itemCode> # Complementary product suggestions
|
|
102
|
+
salai rec <itemCode> # Alias
|
|
103
|
+
--limit <n> # Max results (default 5)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Low-Level
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
salai tools # List all available MCP tools
|
|
110
|
+
salai call <toolName> # Call any tool by name
|
|
111
|
+
--args '{"key": "value"}' # JSON arguments
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Pipe-Friendly
|
|
117
|
+
|
|
118
|
+
Every command supports `--json` for composable pipelines:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
salai search "חלב" --json | jq '.products[0].itemCode'
|
|
122
|
+
salai cart --json | jq '.items[].itemName'
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Environment Variables
|
|
128
|
+
|
|
129
|
+
| Variable | Description |
|
|
130
|
+
|---|---|
|
|
131
|
+
| `SALAI_API_KEY` | Your Salai API key from Profile |
|
|
132
|
+
| `SALAI_MCP_URL` | Override the MCP endpoint URL |
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## How It Works
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
Terminal / Script
|
|
140
|
+
│ salai search "חלב"
|
|
141
|
+
▼
|
|
142
|
+
salai CLI (commander.js)
|
|
143
|
+
│ MCP callTool() over HTTPS
|
|
144
|
+
▼
|
|
145
|
+
https://mcp.salai.co.il/mcp
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The CLI connects to the same Salai MCP HTTP endpoint used by the MCP bridge, using the same API key and accessing the same 18+ tools.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { callTool } from '../mcpClient.js';
|
|
2
|
+
import { outputResult, outputRaw } from '../formatters/index.js';
|
|
3
|
+
export function registerCallCommands(program, getClient, isJson) {
|
|
4
|
+
program
|
|
5
|
+
.command('call <toolName>')
|
|
6
|
+
.description('Call any MCP tool by name (escape hatch)')
|
|
7
|
+
.option('--args <json>', 'JSON arguments', '{}')
|
|
8
|
+
.action(async (toolName, opts) => {
|
|
9
|
+
const client = await getClient();
|
|
10
|
+
let args;
|
|
11
|
+
try {
|
|
12
|
+
args = JSON.parse(opts.args);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
console.error('Error: --args must be valid JSON');
|
|
16
|
+
process.exitCode = 1;
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const result = await callTool(client, toolName, args);
|
|
20
|
+
isJson() ? outputRaw(result) : outputResult(result, toolName);
|
|
21
|
+
await client.close();
|
|
22
|
+
});
|
|
23
|
+
program
|
|
24
|
+
.command('tools')
|
|
25
|
+
.description('List all available MCP tools')
|
|
26
|
+
.action(async () => {
|
|
27
|
+
const client = await getClient();
|
|
28
|
+
const { tools } = await client.listTools();
|
|
29
|
+
if (isJson()) {
|
|
30
|
+
console.log(JSON.stringify(tools, null, 2));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const maxName = Math.max(...tools.map((t) => t.name.length));
|
|
34
|
+
for (const tool of tools) {
|
|
35
|
+
const name = tool.name.padEnd(maxName);
|
|
36
|
+
const desc = tool.description ?? '';
|
|
37
|
+
console.log(` ${name} ${desc}`);
|
|
38
|
+
}
|
|
39
|
+
console.log(`\n${tools.length} tool(s) available`);
|
|
40
|
+
}
|
|
41
|
+
await client.close();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=call.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call.js","sourceRoot":"","sources":["../../src/commands/call.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,UAAU,oBAAoB,CAClC,OAAgB,EAChB,SAAgC,EAChC,MAAqB;IAErB,OAAO;SACJ,OAAO,CAAC,iBAAiB,CAAC;SAC1B,WAAW,CAAC,0CAA0C,CAAC;SACvD,MAAM,CAAC,eAAe,EAAE,gBAAgB,EAAE,IAAI,CAAC;SAC/C,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,IAAI,EAAE,EAAE;QACvC,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,IAAI,IAA6B,CAAC;QAClC,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;YAClD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QACtD,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9D,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,8BAA8B,CAAC;SAC3C,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QAE3C,IAAI,MAAM,EAAE,EAAE,CAAC;YACb,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YAC7D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACvC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;gBACpC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,KAAK,IAAI,EAAE,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,oBAAoB,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { callTool, extractJson } from '../mcpClient.js';
|
|
2
|
+
import { outputResult, outputRaw } from '../formatters/index.js';
|
|
3
|
+
function parseStores(raw) {
|
|
4
|
+
if (!raw)
|
|
5
|
+
return undefined;
|
|
6
|
+
return raw.split(',').map((pair) => {
|
|
7
|
+
const [retailerId, storeId] = pair.split(':');
|
|
8
|
+
return { retailerId: retailerId, storeId: storeId };
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
async function resolveCartId(client, explicit) {
|
|
12
|
+
if (explicit)
|
|
13
|
+
return explicit;
|
|
14
|
+
const result = await callTool(client, 'get_my_cart');
|
|
15
|
+
const data = extractJson(result);
|
|
16
|
+
const id = data?.id ?? data?.cartId ?? data?.raw?.id ?? data?.raw?.cartId;
|
|
17
|
+
if (!id)
|
|
18
|
+
throw new Error('Could not resolve cart ID. Pass --cart-id explicitly.');
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
export function registerCartCommands(program, getClient, isJson) {
|
|
22
|
+
const cart = program.command('cart').description('Shopping cart operations');
|
|
23
|
+
cart
|
|
24
|
+
.command('show', { isDefault: true })
|
|
25
|
+
.description('Show current cart')
|
|
26
|
+
.option('--cart-id <id>', 'specific cart ID (default: your cart)')
|
|
27
|
+
.action(async (opts) => {
|
|
28
|
+
const client = await getClient();
|
|
29
|
+
let result;
|
|
30
|
+
if (opts.cartId) {
|
|
31
|
+
result = await callTool(client, 'get_cart', { cartId: opts.cartId });
|
|
32
|
+
isJson() ? outputRaw(result) : outputResult(result, 'get_cart');
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
result = await callTool(client, 'get_my_cart');
|
|
36
|
+
isJson() ? outputRaw(result) : outputResult(result, 'get_my_cart');
|
|
37
|
+
}
|
|
38
|
+
await client.close();
|
|
39
|
+
});
|
|
40
|
+
cart
|
|
41
|
+
.command('add <itemCode>')
|
|
42
|
+
.description('Add item to cart')
|
|
43
|
+
.option('--qty <n>', 'quantity (default 1)')
|
|
44
|
+
.option('--cart-id <id>', 'cart ID (default: auto-resolve)')
|
|
45
|
+
.option('--name <name>', 'item name (optional)')
|
|
46
|
+
.action(async (itemCode, opts) => {
|
|
47
|
+
const client = await getClient();
|
|
48
|
+
const cartId = await resolveCartId(client, opts.cartId);
|
|
49
|
+
const args = {
|
|
50
|
+
cartId,
|
|
51
|
+
itemCode,
|
|
52
|
+
quantity: Number(opts.qty || 1),
|
|
53
|
+
};
|
|
54
|
+
if (opts.name)
|
|
55
|
+
args.itemName = opts.name;
|
|
56
|
+
const result = await callTool(client, 'add_cart_item', args);
|
|
57
|
+
isJson() ? outputRaw(result) : outputResult(result, 'add_cart_item');
|
|
58
|
+
await client.close();
|
|
59
|
+
});
|
|
60
|
+
cart
|
|
61
|
+
.command('set-qty <itemCode> <quantity>')
|
|
62
|
+
.description('Set item quantity (0 = remove)')
|
|
63
|
+
.option('--cart-id <id>', 'cart ID (default: auto-resolve)')
|
|
64
|
+
.action(async (itemCode, quantity, opts) => {
|
|
65
|
+
const client = await getClient();
|
|
66
|
+
const cartId = await resolveCartId(client, opts.cartId);
|
|
67
|
+
const result = await callTool(client, 'set_cart_item_quantity', {
|
|
68
|
+
cartId,
|
|
69
|
+
itemCode,
|
|
70
|
+
quantity: Number(quantity),
|
|
71
|
+
});
|
|
72
|
+
isJson() ? outputRaw(result) : outputResult(result, 'set_cart_item_quantity');
|
|
73
|
+
await client.close();
|
|
74
|
+
});
|
|
75
|
+
cart
|
|
76
|
+
.command('remove <itemCode>')
|
|
77
|
+
.description('Remove item from cart')
|
|
78
|
+
.option('--cart-id <id>', 'cart ID (default: auto-resolve)')
|
|
79
|
+
.action(async (itemCode, opts) => {
|
|
80
|
+
const client = await getClient();
|
|
81
|
+
const cartId = await resolveCartId(client, opts.cartId);
|
|
82
|
+
const result = await callTool(client, 'remove_cart_item', {
|
|
83
|
+
cartId,
|
|
84
|
+
itemCode,
|
|
85
|
+
});
|
|
86
|
+
isJson() ? outputRaw(result) : outputResult(result, 'remove_cart_item');
|
|
87
|
+
await client.close();
|
|
88
|
+
});
|
|
89
|
+
cart
|
|
90
|
+
.command('compare')
|
|
91
|
+
.description('Compare your cart across stores')
|
|
92
|
+
.option('--cart-id <id>', 'cart ID (optional)')
|
|
93
|
+
.option('--stores <pairs>', 'retailerId:storeId,... to limit scope')
|
|
94
|
+
.action(async (opts) => {
|
|
95
|
+
const client = await getClient();
|
|
96
|
+
const args = {};
|
|
97
|
+
if (opts.cartId)
|
|
98
|
+
args.cartId = opts.cartId;
|
|
99
|
+
const stores = parseStores(opts.stores);
|
|
100
|
+
if (stores)
|
|
101
|
+
args.stores = stores;
|
|
102
|
+
const result = await callTool(client, 'compare_my_cart', args);
|
|
103
|
+
isJson() ? outputRaw(result) : outputResult(result, 'compare_my_cart');
|
|
104
|
+
await client.close();
|
|
105
|
+
});
|
|
106
|
+
cart
|
|
107
|
+
.command('delete <cartId>')
|
|
108
|
+
.description('Delete a cart')
|
|
109
|
+
.action(async (cartId) => {
|
|
110
|
+
const client = await getClient();
|
|
111
|
+
const result = await callTool(client, 'delete_cart', { cartId });
|
|
112
|
+
isJson() ? outputRaw(result) : outputResult(result, 'delete_cart');
|
|
113
|
+
await client.close();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=cart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cart.js","sourceRoot":"","sources":["../../src/commands/cart.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { callTool } from '../mcpClient.js';
|
|
2
|
+
import { outputResult, outputRaw } from '../formatters/index.js';
|
|
3
|
+
function parseStores(raw) {
|
|
4
|
+
if (!raw)
|
|
5
|
+
return undefined;
|
|
6
|
+
return raw.split(',').map((pair) => {
|
|
7
|
+
const [retailerId, storeId] = pair.split(':');
|
|
8
|
+
return { retailerId: retailerId, storeId: storeId };
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export function registerPriceCommands(program, getClient, isJson) {
|
|
12
|
+
program
|
|
13
|
+
.command('prices <itemCodes...>')
|
|
14
|
+
.description('Get prices for item codes')
|
|
15
|
+
.option('--stores <pairs>', 'retailerId:storeId,... to limit scope')
|
|
16
|
+
.action(async (itemCodes, opts) => {
|
|
17
|
+
const client = await getClient();
|
|
18
|
+
const args = { itemCodes };
|
|
19
|
+
const stores = parseStores(opts.stores);
|
|
20
|
+
if (stores)
|
|
21
|
+
args.stores = stores;
|
|
22
|
+
const result = await callTool(client, 'get_product_prices', args);
|
|
23
|
+
isJson() ? outputRaw(result) : outputResult(result, 'get_product_prices');
|
|
24
|
+
await client.close();
|
|
25
|
+
});
|
|
26
|
+
program
|
|
27
|
+
.command('compare <items...>')
|
|
28
|
+
.description('Compare prices across retailers (itemCode:qty ...)')
|
|
29
|
+
.option('--stores <pairs>', 'retailerId:storeId,... to limit scope')
|
|
30
|
+
.action(async (rawItems, opts) => {
|
|
31
|
+
const client = await getClient();
|
|
32
|
+
const items = rawItems.map((raw) => {
|
|
33
|
+
const [itemCode, qty] = raw.split(':');
|
|
34
|
+
return { itemCode: itemCode, quantity: Number(qty || 1) };
|
|
35
|
+
});
|
|
36
|
+
const args = { items };
|
|
37
|
+
const stores = parseStores(opts.stores);
|
|
38
|
+
if (stores)
|
|
39
|
+
args.stores = stores;
|
|
40
|
+
const result = await callTool(client, 'compare_prices', args);
|
|
41
|
+
isJson() ? outputRaw(result) : outputResult(result, 'compare_prices');
|
|
42
|
+
await client.close();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=prices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prices.js","sourceRoot":"","sources":["../../src/commands/prices.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEjE,SAAS,WAAW,CAAC,GAAY;IAC/B,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACjC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9C,OAAO,EAAE,UAAU,EAAE,UAAW,EAAE,OAAO,EAAE,OAAQ,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,OAAgB,EAChB,SAAgC,EAChC,MAAqB;IAErB,OAAO;SACJ,OAAO,CAAC,uBAAuB,CAAC;SAChC,WAAW,CAAC,2BAA2B,CAAC;SACxC,MAAM,CAAC,kBAAkB,EAAE,uCAAuC,CAAC;SACnE,MAAM,CAAC,KAAK,EAAE,SAAmB,EAAE,IAAI,EAAE,EAAE;QAC1C,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,GAA4B,EAAE,SAAS,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,MAAM;YAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAEjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,oBAAoB,EAAE,IAAI,CAAC,CAAC;QAClE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;QAC1E,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,oBAAoB,CAAC;SAC7B,WAAW,CAAC,oDAAoD,CAAC;SACjE,MAAM,CAAC,kBAAkB,EAAE,uCAAuC,CAAC;SACnE,MAAM,CAAC,KAAK,EAAE,QAAkB,EAAE,IAAI,EAAE,EAAE;QACzC,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACjC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACvC,OAAO,EAAE,QAAQ,EAAE,QAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC;QAC7D,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,GAA4B,EAAE,KAAK,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,MAAM;YAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAEjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;QAC9D,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;QACtE,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { callTool } from '../mcpClient.js';
|
|
2
|
+
import { outputResult, outputRaw } from '../formatters/index.js';
|
|
3
|
+
export function registerRecommendCommands(program, getClient, isJson) {
|
|
4
|
+
program
|
|
5
|
+
.command('recommend <itemCode>')
|
|
6
|
+
.alias('rec')
|
|
7
|
+
.description('Get complementary product recommendations')
|
|
8
|
+
.option('--limit <n>', 'max results (default 5)')
|
|
9
|
+
.action(async (itemCode, opts) => {
|
|
10
|
+
const client = await getClient();
|
|
11
|
+
const args = { itemCode };
|
|
12
|
+
if (opts.limit)
|
|
13
|
+
args.limit = Number(opts.limit);
|
|
14
|
+
const result = await callTool(client, 'get_complementary_recommendations', args);
|
|
15
|
+
isJson()
|
|
16
|
+
? outputRaw(result)
|
|
17
|
+
: outputResult(result, 'get_complementary_recommendations');
|
|
18
|
+
await client.close();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=recommend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recommend.js","sourceRoot":"","sources":["../../src/commands/recommend.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,UAAU,yBAAyB,CACvC,OAAgB,EAChB,SAAgC,EAChC,MAAqB;IAErB,OAAO;SACJ,OAAO,CAAC,sBAAsB,CAAC;SAC/B,KAAK,CAAC,KAAK,CAAC;SACZ,WAAW,CAAC,2CAA2C,CAAC;SACxD,MAAM,CAAC,aAAa,EAAE,yBAAyB,CAAC;SAChD,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,IAAI,EAAE,EAAE;QACvC,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,GAA4B,EAAE,QAAQ,EAAE,CAAC;QACnD,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEhD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,mCAAmC,EAAE,IAAI,CAAC,CAAC;QACjF,MAAM,EAAE;YACN,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC;YACnB,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;QAC9D,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { callTool } from '../mcpClient.js';
|
|
2
|
+
import { outputResult, outputRaw } from '../formatters/index.js';
|
|
3
|
+
export function registerSearchCommands(program, getClient, isJson) {
|
|
4
|
+
program
|
|
5
|
+
.command('search <query>')
|
|
6
|
+
.description('Search products by Hebrew query')
|
|
7
|
+
.option('--limit <n>', 'max results (default 20)')
|
|
8
|
+
.option('--mode <mode>', 'fast | hybrid | ai')
|
|
9
|
+
.option('--store-scope <scope>', 'selected_only | retailer_wide | all_limited')
|
|
10
|
+
.option('--retailer <id>', 'filter by retailer ID')
|
|
11
|
+
.option('--store <id>', 'filter by store ID')
|
|
12
|
+
.action(async (query, opts) => {
|
|
13
|
+
const client = await getClient();
|
|
14
|
+
const args = { query };
|
|
15
|
+
if (opts.limit)
|
|
16
|
+
args.limit = Number(opts.limit);
|
|
17
|
+
if (opts.mode)
|
|
18
|
+
args.mode = opts.mode;
|
|
19
|
+
if (opts.storeScope)
|
|
20
|
+
args.storeScope = opts.storeScope;
|
|
21
|
+
if (opts.retailer)
|
|
22
|
+
args.retailerId = opts.retailer;
|
|
23
|
+
if (opts.store)
|
|
24
|
+
args.storeId = opts.store;
|
|
25
|
+
const result = await callTool(client, 'search_products', args);
|
|
26
|
+
isJson() ? outputRaw(result) : outputResult(result, 'search_products');
|
|
27
|
+
await client.close();
|
|
28
|
+
});
|
|
29
|
+
program
|
|
30
|
+
.command('autocomplete <query>')
|
|
31
|
+
.alias('ac')
|
|
32
|
+
.description('Fast autocomplete product lookup')
|
|
33
|
+
.option('--limit <n>', 'max results (default 15)')
|
|
34
|
+
.option('--method <m>', 'text | semantic (default text)')
|
|
35
|
+
.option('--store-scope <scope>', 'selected_only | retailer_wide | all_limited')
|
|
36
|
+
.action(async (query, opts) => {
|
|
37
|
+
const client = await getClient();
|
|
38
|
+
const args = { query };
|
|
39
|
+
if (opts.limit)
|
|
40
|
+
args.limit = Number(opts.limit);
|
|
41
|
+
if (opts.method)
|
|
42
|
+
args.method = opts.method;
|
|
43
|
+
if (opts.storeScope)
|
|
44
|
+
args.storeScope = opts.storeScope;
|
|
45
|
+
const result = await callTool(client, 'autocomplete_products', args);
|
|
46
|
+
isJson() ? outputRaw(result) : outputResult(result, 'autocomplete_products');
|
|
47
|
+
await client.close();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,UAAU,sBAAsB,CACpC,OAAgB,EAChB,SAAgC,EAChC,MAAqB;IAErB,OAAO;SACJ,OAAO,CAAC,gBAAgB,CAAC;SACzB,WAAW,CAAC,iCAAiC,CAAC;SAC9C,MAAM,CAAC,aAAa,EAAE,0BAA0B,CAAC;SACjD,MAAM,CAAC,eAAe,EAAE,oBAAoB,CAAC;SAC7C,MAAM,CAAC,uBAAuB,EAAE,6CAA6C,CAAC;SAC9E,MAAM,CAAC,iBAAiB,EAAE,uBAAuB,CAAC;SAClD,MAAM,CAAC,cAAc,EAAE,oBAAoB,CAAC;SAC5C,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,IAAI,EAAE,EAAE;QACpC,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,GAA4B,EAAE,KAAK,EAAE,CAAC;QAChD,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,IAAI,CAAC,IAAI;YAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACrC,IAAI,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QACvD,IAAI,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC;QACnD,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC;QAE1C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;QAC/D,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;QACvE,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,sBAAsB,CAAC;SAC/B,KAAK,CAAC,IAAI,CAAC;SACX,WAAW,CAAC,kCAAkC,CAAC;SAC/C,MAAM,CAAC,aAAa,EAAE,0BAA0B,CAAC;SACjD,MAAM,CAAC,cAAc,EAAE,gCAAgC,CAAC;SACxD,MAAM,CAAC,uBAAuB,EAAE,6CAA6C,CAAC;SAC9E,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,IAAI,EAAE,EAAE;QACpC,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,GAA4B,EAAE,KAAK,EAAE,CAAC;QAChD,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,IAAI,CAAC,MAAM;YAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3C,IAAI,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QAEvD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE,IAAI,CAAC,CAAC;QACrE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;QAC7E,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { callTool } from '../mcpClient.js';
|
|
2
|
+
import { outputResult, outputRaw } from '../formatters/index.js';
|
|
3
|
+
export function registerStoreCommands(program, getClient, isJson) {
|
|
4
|
+
program
|
|
5
|
+
.command('stores')
|
|
6
|
+
.description('List all online stores (one per retailer)')
|
|
7
|
+
.action(async () => {
|
|
8
|
+
const client = await getClient();
|
|
9
|
+
const result = await callTool(client, 'get_stores');
|
|
10
|
+
isJson() ? outputRaw(result) : outputResult(result, 'get_stores');
|
|
11
|
+
await client.close();
|
|
12
|
+
});
|
|
13
|
+
program
|
|
14
|
+
.command('retailers')
|
|
15
|
+
.description('List all retailers')
|
|
16
|
+
.action(async () => {
|
|
17
|
+
const client = await getClient();
|
|
18
|
+
const result = await callTool(client, 'get_retailers');
|
|
19
|
+
isJson() ? outputRaw(result) : outputResult(result, 'get_retailers');
|
|
20
|
+
await client.close();
|
|
21
|
+
});
|
|
22
|
+
const store = program.command('store').description('Store context management');
|
|
23
|
+
store
|
|
24
|
+
.command('show', { isDefault: true })
|
|
25
|
+
.description('Show current selected store context')
|
|
26
|
+
.action(async () => {
|
|
27
|
+
const client = await getClient();
|
|
28
|
+
const result = await callTool(client, 'get_my_store_context');
|
|
29
|
+
isJson() ? outputRaw(result) : outputResult(result, 'get_my_store_context');
|
|
30
|
+
await client.close();
|
|
31
|
+
});
|
|
32
|
+
store
|
|
33
|
+
.command('set <retailerId> <storeId>')
|
|
34
|
+
.description('Set selected store')
|
|
35
|
+
.action(async (retailerId, storeId) => {
|
|
36
|
+
const client = await getClient();
|
|
37
|
+
const result = await callTool(client, 'set_my_selected_store', {
|
|
38
|
+
retailerId,
|
|
39
|
+
storeId,
|
|
40
|
+
});
|
|
41
|
+
isJson() ? outputRaw(result) : outputResult(result, 'set_my_selected_store');
|
|
42
|
+
await client.close();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=stores.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.js","sourceRoot":"","sources":["../../src/commands/stores.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEjE,MAAM,UAAU,qBAAqB,CACnC,OAAgB,EAChB,SAAgC,EAChC,MAAqB;IAErB,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,2CAA2C,CAAC;SACxD,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QACpD,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAClE,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,oBAAoB,CAAC;SACjC,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QACvD,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QACrE,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEL,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAAC;IAE/E,KAAK;SACF,OAAO,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;SACpC,WAAW,CAAC,qCAAqC,CAAC;SAClD,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;QAC9D,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;QAC5E,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,4BAA4B,CAAC;SACrC,WAAW,CAAC,oBAAoB,CAAC;SACjC,MAAM,CAAC,KAAK,EAAE,UAAkB,EAAE,OAAe,EAAE,EAAE;QACpD,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,uBAAuB,EAAE;YAC7D,UAAU;YACV,OAAO;SACR,CAAC,CAAC;QACH,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;QAC7E,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatting for the Salai CLI.
|
|
3
|
+
* Renders MCP tool results as human-friendly tables or raw JSON.
|
|
4
|
+
*/
|
|
5
|
+
import type { ToolResult } from '../mcpClient.js';
|
|
6
|
+
export declare function outputRaw(result: ToolResult): void;
|
|
7
|
+
export declare function outputResult(result: ToolResult, toolName: string): void;
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatting for the Salai CLI.
|
|
3
|
+
* Renders MCP tool results as human-friendly tables or raw JSON.
|
|
4
|
+
*/
|
|
5
|
+
import { extractJson, extractText } from '../mcpClient.js';
|
|
6
|
+
// ── Helpers ──
|
|
7
|
+
const RESET = '\x1b[0m';
|
|
8
|
+
const BOLD = '\x1b[1m';
|
|
9
|
+
const DIM = '\x1b[2m';
|
|
10
|
+
const GREEN = '\x1b[32m';
|
|
11
|
+
const YELLOW = '\x1b[33m';
|
|
12
|
+
const CYAN = '\x1b[36m';
|
|
13
|
+
const RED = '\x1b[31m';
|
|
14
|
+
function c(color, text) {
|
|
15
|
+
return `${color}${text}${RESET}`;
|
|
16
|
+
}
|
|
17
|
+
function pad(str, len) {
|
|
18
|
+
const visible = str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
19
|
+
return str + ' '.repeat(Math.max(0, len - visible.length));
|
|
20
|
+
}
|
|
21
|
+
function padR(str, len) {
|
|
22
|
+
const visible = str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
23
|
+
return ' '.repeat(Math.max(0, len - visible.length)) + str;
|
|
24
|
+
}
|
|
25
|
+
function table(headers, rows, colAligns) {
|
|
26
|
+
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? '').replace(/\x1b\[[0-9;]*m/g, '').length)));
|
|
27
|
+
const aligns = colAligns ?? headers.map(() => 'l');
|
|
28
|
+
const sep = widths.map((w) => '─'.repeat(w + 2)).join('┼');
|
|
29
|
+
const formatRow = (cols) => cols
|
|
30
|
+
.map((col, i) => aligns[i] === 'r'
|
|
31
|
+
? ' ' + padR(col, widths[i]) + ' '
|
|
32
|
+
: ' ' + pad(col, widths[i]) + ' ')
|
|
33
|
+
.join('│');
|
|
34
|
+
const lines = [];
|
|
35
|
+
lines.push(formatRow(headers.map((h) => c(BOLD, h))));
|
|
36
|
+
lines.push(sep);
|
|
37
|
+
for (const row of rows) {
|
|
38
|
+
lines.push(formatRow(row));
|
|
39
|
+
}
|
|
40
|
+
return lines.join('\n');
|
|
41
|
+
}
|
|
42
|
+
function shekel(n) {
|
|
43
|
+
if (n == null)
|
|
44
|
+
return c(DIM, '—');
|
|
45
|
+
return `₪${n.toFixed(2)}`;
|
|
46
|
+
}
|
|
47
|
+
const RLM = '\u200F';
|
|
48
|
+
/** Wrap Hebrew text with RTL mark so terminals render it correctly in padded columns. */
|
|
49
|
+
function rtl(text) {
|
|
50
|
+
if (!text)
|
|
51
|
+
return text;
|
|
52
|
+
if (/[\u0590-\u05FF]/.test(text))
|
|
53
|
+
return RLM + text + RLM;
|
|
54
|
+
return text;
|
|
55
|
+
}
|
|
56
|
+
// ── Public API ──
|
|
57
|
+
export function outputRaw(result) {
|
|
58
|
+
const json = extractJson(result);
|
|
59
|
+
console.log(JSON.stringify(json ?? result, null, 2));
|
|
60
|
+
}
|
|
61
|
+
export function outputResult(result, toolName) {
|
|
62
|
+
if (result.isError) {
|
|
63
|
+
const text = extractText(result);
|
|
64
|
+
console.error(c(RED, `Error: ${text ?? 'Unknown error'}`));
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const data = extractJson(result);
|
|
69
|
+
if (!data) {
|
|
70
|
+
const text = extractText(result);
|
|
71
|
+
if (text) {
|
|
72
|
+
console.log(text);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.log(JSON.stringify(result, null, 2));
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
switch (toolName) {
|
|
80
|
+
case 'search_products':
|
|
81
|
+
formatSearchProducts(data);
|
|
82
|
+
break;
|
|
83
|
+
case 'autocomplete_products':
|
|
84
|
+
formatAutocomplete(data);
|
|
85
|
+
break;
|
|
86
|
+
case 'get_product_prices':
|
|
87
|
+
formatProductPrices(data);
|
|
88
|
+
break;
|
|
89
|
+
case 'compare_prices':
|
|
90
|
+
formatComparePrices(data);
|
|
91
|
+
break;
|
|
92
|
+
case 'get_stores':
|
|
93
|
+
formatStores(data);
|
|
94
|
+
break;
|
|
95
|
+
case 'get_retailers':
|
|
96
|
+
formatRetailers(data);
|
|
97
|
+
break;
|
|
98
|
+
case 'get_my_store_context':
|
|
99
|
+
formatStoreContext(data);
|
|
100
|
+
break;
|
|
101
|
+
case 'set_my_selected_store':
|
|
102
|
+
formatStoreContext(data);
|
|
103
|
+
break;
|
|
104
|
+
case 'get_my_cart':
|
|
105
|
+
case 'get_cart':
|
|
106
|
+
formatCart(data);
|
|
107
|
+
break;
|
|
108
|
+
case 'add_cart_item':
|
|
109
|
+
case 'set_cart_item_quantity':
|
|
110
|
+
case 'remove_cart_item':
|
|
111
|
+
formatCartMutation(data, toolName);
|
|
112
|
+
break;
|
|
113
|
+
case 'compare_my_cart':
|
|
114
|
+
formatCartComparison(data);
|
|
115
|
+
break;
|
|
116
|
+
case 'delete_cart':
|
|
117
|
+
console.log(c(GREEN, '✓ Cart deleted'));
|
|
118
|
+
break;
|
|
119
|
+
case 'get_complementary_recommendations':
|
|
120
|
+
formatRecommendations(data);
|
|
121
|
+
break;
|
|
122
|
+
default:
|
|
123
|
+
console.log(JSON.stringify(data, null, 2));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// ── Tool-specific formatters ──
|
|
127
|
+
function formatSearchProducts(data) {
|
|
128
|
+
// structuredContent: { numberedProducts: [{ itemCode, itemName, price }] }
|
|
129
|
+
if (Array.isArray(data.numberedProducts)) {
|
|
130
|
+
const products = data.numberedProducts;
|
|
131
|
+
if (products.length === 0) {
|
|
132
|
+
console.log(c(DIM, 'No products found.'));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (data.query)
|
|
136
|
+
console.log(c(DIM, `Search: "${data.query}"\n`));
|
|
137
|
+
const rows = products.map((p) => [
|
|
138
|
+
p.itemCode ?? '',
|
|
139
|
+
rtl(p.itemName ?? ''),
|
|
140
|
+
p.displayPrice ?? shekel(p.price),
|
|
141
|
+
]);
|
|
142
|
+
console.log(table(['Code', 'Name', 'Price'], rows, ['l', 'l', 'r']));
|
|
143
|
+
console.log(c(DIM, `\n${products.length} result(s)`));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
// Raw JSON: { recommendations: [{ product: {...}, bestPrice: {...} }] }
|
|
147
|
+
const recs = data.recommendations;
|
|
148
|
+
if (Array.isArray(recs)) {
|
|
149
|
+
if (recs.length === 0) {
|
|
150
|
+
console.log(c(DIM, 'No products found.'));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (data.query)
|
|
154
|
+
console.log(c(DIM, `Search: "${data.query}"\n`));
|
|
155
|
+
const rows = recs.map((rec) => {
|
|
156
|
+
const p = rec.product ?? {};
|
|
157
|
+
return [
|
|
158
|
+
p.itemCode ?? '',
|
|
159
|
+
rtl(p.manufactureItemDescription ?? p.itemName ?? ''),
|
|
160
|
+
shekel(rec.bestPrice?.price),
|
|
161
|
+
];
|
|
162
|
+
});
|
|
163
|
+
console.log(table(['Code', 'Name', 'Price'], rows, ['l', 'l', 'r']));
|
|
164
|
+
console.log(c(DIM, `\n${recs.length} result(s)`));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
// Fallback: flat array
|
|
168
|
+
const products = data.products ?? data.results ?? data;
|
|
169
|
+
if (Array.isArray(products) && products.length > 0) {
|
|
170
|
+
const rows = products.map((p) => [
|
|
171
|
+
p.itemCode ?? '',
|
|
172
|
+
rtl(p.itemName ?? p.name ?? ''),
|
|
173
|
+
shekel(p.price ?? p.itemPrice),
|
|
174
|
+
]);
|
|
175
|
+
console.log(table(['Code', 'Name', 'Price'], rows, ['l', 'l', 'r']));
|
|
176
|
+
console.log(c(DIM, `\n${products.length} result(s)`));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
console.log(c(DIM, 'No products found.'));
|
|
180
|
+
}
|
|
181
|
+
function formatAutocomplete(data) {
|
|
182
|
+
// structuredContent: { numberedProducts: [...] }
|
|
183
|
+
if (Array.isArray(data.numberedProducts)) {
|
|
184
|
+
const products = data.numberedProducts;
|
|
185
|
+
if (products.length === 0) {
|
|
186
|
+
console.log(c(DIM, 'No suggestions.'));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const rows = products.map((p) => [
|
|
190
|
+
p.itemCode ?? '',
|
|
191
|
+
rtl(p.itemName ?? ''),
|
|
192
|
+
p.displayPrice ?? shekel(p.price),
|
|
193
|
+
]);
|
|
194
|
+
console.log(table(['Code', 'Name', 'Price'], rows, ['l', 'l', 'r']));
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
// Raw JSON: flat array [{ itemCode, itemName, bestPrice }]
|
|
198
|
+
const products = Array.isArray(data) ? data : (data.products ?? data.suggestions ?? []);
|
|
199
|
+
if (!Array.isArray(products) || products.length === 0) {
|
|
200
|
+
console.log(c(DIM, 'No suggestions.'));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const rows = products.map((p) => [
|
|
204
|
+
p.itemCode ?? '',
|
|
205
|
+
rtl(p.itemName ?? p.name ?? ''),
|
|
206
|
+
shekel(p.bestPrice ?? p.price ?? p.itemPrice),
|
|
207
|
+
]);
|
|
208
|
+
console.log(table(['Code', 'Name', 'Price'], rows, ['l', 'l', 'r']));
|
|
209
|
+
}
|
|
210
|
+
function formatProductPrices(data) {
|
|
211
|
+
const items = data.prices ?? data.items ?? data;
|
|
212
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
213
|
+
console.log(c(DIM, 'No price data.'));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const rows = items.map((p) => [
|
|
217
|
+
p.itemCode ?? '',
|
|
218
|
+
rtl(p.itemName ?? ''),
|
|
219
|
+
shekel(p.price ?? p.itemPrice),
|
|
220
|
+
rtl(p.retailerName ?? p.retailerId ?? ''),
|
|
221
|
+
rtl(p.storeName ?? p.storeId ?? ''),
|
|
222
|
+
]);
|
|
223
|
+
console.log(table(['Code', 'Name', 'Price', 'Retailer', 'Store'], rows, ['l', 'l', 'r', 'l', 'l']));
|
|
224
|
+
}
|
|
225
|
+
function formatComparePrices(data) {
|
|
226
|
+
// structuredContent: { viewType: 'store_comparison', stores: [...], summary: {...} }
|
|
227
|
+
if (data.viewType === 'store_comparison' && Array.isArray(data.stores)) {
|
|
228
|
+
const stores = data.stores;
|
|
229
|
+
if (stores.length === 0) {
|
|
230
|
+
console.log(c(DIM, 'No comparison data.'));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const rows = stores.map((s) => {
|
|
234
|
+
const missing = s.availabilitySummary?.missingCount ?? 0;
|
|
235
|
+
const cheapest = s.isCheapest ? c(GREEN, ' ★') : '';
|
|
236
|
+
return [
|
|
237
|
+
rtl(s.storeLabel ?? s.retailerName ?? s.retailerId ?? ''),
|
|
238
|
+
s.displaySubtotal ?? shekel(s.subtotal),
|
|
239
|
+
String(missing),
|
|
240
|
+
cheapest,
|
|
241
|
+
];
|
|
242
|
+
});
|
|
243
|
+
console.log(table(['Store', 'Total', 'Missing', ''], rows, ['l', 'r', 'r', 'l']));
|
|
244
|
+
if (data.summary?.cheapestStoreLabel) {
|
|
245
|
+
console.log(`\n${c(GREEN, '★')} Cheapest: ${c(BOLD, data.summary.cheapestStoreLabel)}`);
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
// Raw JSON: { comparison: { retailers: [...] } } or { retailers: [...] }
|
|
250
|
+
const comparePayload = data.comparison ?? data;
|
|
251
|
+
const retailers = comparePayload.retailers ?? data.stores ?? data;
|
|
252
|
+
if (!Array.isArray(retailers) || retailers.length === 0) {
|
|
253
|
+
console.log(c(DIM, 'No comparison data.'));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const rows = retailers.map((s) => {
|
|
257
|
+
const total = s.subtotal ?? s.total ?? s.totalPrice ?? s.basketTotal;
|
|
258
|
+
const missing = (s.items ?? []).filter((i) => !i.available).length;
|
|
259
|
+
return [
|
|
260
|
+
rtl(s.retailerName ?? s.retailerId ?? ''),
|
|
261
|
+
shekel(total),
|
|
262
|
+
String(missing),
|
|
263
|
+
];
|
|
264
|
+
});
|
|
265
|
+
console.log(table(['Retailer', 'Total', 'Missing'], rows, ['l', 'r', 'r']));
|
|
266
|
+
}
|
|
267
|
+
function formatStores(data) {
|
|
268
|
+
const stores = data.stores ?? data;
|
|
269
|
+
if (!Array.isArray(stores) || stores.length === 0) {
|
|
270
|
+
console.log(c(DIM, 'No stores.'));
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const rows = stores.map((s) => [
|
|
274
|
+
s.retailerId ?? '',
|
|
275
|
+
s.storeId ?? '',
|
|
276
|
+
rtl(s.retailerName ?? ''),
|
|
277
|
+
rtl(s.storeName ?? s.name ?? ''),
|
|
278
|
+
]);
|
|
279
|
+
console.log(table(['Retailer ID', 'Store ID', 'Retailer', 'Store'], rows));
|
|
280
|
+
}
|
|
281
|
+
function formatRetailers(data) {
|
|
282
|
+
const retailers = data.retailers ?? data;
|
|
283
|
+
if (!Array.isArray(retailers) || retailers.length === 0) {
|
|
284
|
+
console.log(c(DIM, 'No retailers.'));
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const rows = retailers.map((r) => [
|
|
288
|
+
r.id ?? r.retailerId ?? '',
|
|
289
|
+
rtl(r.name ?? r.displayName ?? ''),
|
|
290
|
+
]);
|
|
291
|
+
console.log(table(['ID', 'Name'], rows));
|
|
292
|
+
}
|
|
293
|
+
function formatStoreContext(data) {
|
|
294
|
+
const sel = data.selectedStore ?? data;
|
|
295
|
+
if (sel?.retailerName || sel?.retailerId) {
|
|
296
|
+
console.log(`${c(BOLD, 'Selected store:')} ${rtl(sel.retailerName ?? sel.retailerId)} — ${rtl(sel.storeName ?? sel.storeId ?? '')}`);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
console.log(c(YELLOW, 'No store selected. Use: salai store set <retailerId> <storeId>'));
|
|
300
|
+
}
|
|
301
|
+
if (data.activeStores && Array.isArray(data.activeStores)) {
|
|
302
|
+
console.log(c(DIM, `\n${data.activeStores.length} active store(s) available`));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function formatCart(data) {
|
|
306
|
+
// structuredContent: { viewType: 'cart_overview', items: [...], summary: {...} }
|
|
307
|
+
if (data.viewType === 'cart_overview') {
|
|
308
|
+
const summary = data.summary ?? {};
|
|
309
|
+
console.log(`${c(BOLD, 'Cart')} ${c(DIM, '—')} ${rtl(summary.storeLabel ?? '')}`);
|
|
310
|
+
const items = data.items ?? [];
|
|
311
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
312
|
+
console.log(c(DIM, 'Cart is empty.'));
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const rows = items.map((it) => [
|
|
316
|
+
it.itemCode ?? '',
|
|
317
|
+
rtl(it.itemName ?? ''),
|
|
318
|
+
String(it.quantity ?? 1),
|
|
319
|
+
it.displayPrice ?? shekel(it.unitPrice),
|
|
320
|
+
it.displaySubtotal ?? shekel(it.subtotal),
|
|
321
|
+
]);
|
|
322
|
+
console.log(table(['Code', 'Name', 'Qty', 'Unit', 'Subtotal'], rows, ['l', 'l', 'r', 'r', 'r']));
|
|
323
|
+
console.log(`\n${c(BOLD, 'Total:')} ${c(GREEN, summary.displayGrandTotal ?? shekel(summary.grandTotal))}`);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// Raw JSON fallback
|
|
327
|
+
const cartId = data.id ?? data.cartId ?? '';
|
|
328
|
+
const items = data.items ?? data.itemsWithPrices ?? data.storeCarts?.[0]?.items ?? [];
|
|
329
|
+
console.log(`${c(BOLD, 'Cart:')} ${c(DIM, cartId)}`);
|
|
330
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
331
|
+
console.log(c(DIM, 'Cart is empty.'));
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const rows = items.map((entry) => {
|
|
335
|
+
const it = entry.item ?? entry;
|
|
336
|
+
const price = entry.productPrice?.itemPrice ?? it.price ?? it.itemPrice;
|
|
337
|
+
const qty = it.quantity ?? 1;
|
|
338
|
+
return [
|
|
339
|
+
it.itemCode ?? '',
|
|
340
|
+
rtl(it.itemName ?? it.name ?? ''),
|
|
341
|
+
String(qty),
|
|
342
|
+
shekel(price),
|
|
343
|
+
shekel(entry.subtotal ?? (price != null ? price * qty : null)),
|
|
344
|
+
];
|
|
345
|
+
});
|
|
346
|
+
console.log(table(['Code', 'Name', 'Qty', 'Unit', 'Subtotal'], rows, ['l', 'l', 'r', 'r', 'r']));
|
|
347
|
+
const total = data.grandTotal ?? data.total ?? data.totalPrice;
|
|
348
|
+
if (total != null) {
|
|
349
|
+
console.log(`\n${c(BOLD, 'Total:')} ${c(GREEN, shekel(total))}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function formatCartMutation(data, tool) {
|
|
353
|
+
// structuredContent: cart_overview after mutation
|
|
354
|
+
if (data.viewType === 'cart_overview') {
|
|
355
|
+
const verb = tool === 'add_cart_item' ? 'Added to' : tool === 'remove_cart_item' ? 'Removed from' : 'Updated';
|
|
356
|
+
console.log(`${c(GREEN, '✓')} ${verb} cart`);
|
|
357
|
+
formatCart(data);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const verb = tool === 'add_cart_item'
|
|
361
|
+
? 'Added'
|
|
362
|
+
: tool === 'remove_cart_item'
|
|
363
|
+
? 'Removed'
|
|
364
|
+
: 'Updated';
|
|
365
|
+
const addResult = data._addItemResult ?? data;
|
|
366
|
+
const itemAdded = addResult.itemAdded ?? addResult.item ?? addResult;
|
|
367
|
+
const name = itemAdded?.itemName ?? itemAdded?.itemCode ?? '';
|
|
368
|
+
console.log(`${c(GREEN, '✓')} ${verb} ${c(BOLD, rtl(name))}`);
|
|
369
|
+
if (addResult.notAvailable) {
|
|
370
|
+
console.log(c(YELLOW, '⚠ Item is not available at selected store'));
|
|
371
|
+
}
|
|
372
|
+
if (data.itemsWithPrices || data.items || data.cart) {
|
|
373
|
+
formatCart(data.cart ?? data);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function formatCartComparison(data) {
|
|
377
|
+
formatComparePrices(data);
|
|
378
|
+
}
|
|
379
|
+
function formatRecommendations(data) {
|
|
380
|
+
const recs = data.recommendations ?? data.products ?? data;
|
|
381
|
+
if (!Array.isArray(recs) || recs.length === 0) {
|
|
382
|
+
console.log(c(DIM, 'No recommendations.'));
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
const rows = recs.map((r) => [
|
|
386
|
+
r.itemCode ?? '',
|
|
387
|
+
rtl(r.itemName ?? r.name ?? ''),
|
|
388
|
+
shekel(r.price ?? r.itemPrice),
|
|
389
|
+
]);
|
|
390
|
+
console.log(table(['Code', 'Name', 'Price'], rows, ['l', 'l', 'r']));
|
|
391
|
+
}
|
|
392
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/formatters/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared MCP client factory.
|
|
3
|
+
* Connects to the Salai MCP HTTP endpoint and provides tool-calling helpers.
|
|
4
|
+
*/
|
|
5
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
6
|
+
export declare const DEFAULT_MCP_URL = "https://mcp.salai.co.il/mcp";
|
|
7
|
+
export declare const PACKAGE_VERSION = "0.1.0";
|
|
8
|
+
export interface McpClientOptions {
|
|
9
|
+
apiKey: string | null;
|
|
10
|
+
url: string;
|
|
11
|
+
clientName?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function resolveConfig(overrides?: {
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
url?: string;
|
|
16
|
+
}): {
|
|
17
|
+
apiKey: string | null;
|
|
18
|
+
url: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function buildHeaders(apiKey: string | null): Record<string, string>;
|
|
21
|
+
export declare function createClient(opts: McpClientOptions): Promise<Client>;
|
|
22
|
+
export interface ToolResult {
|
|
23
|
+
content?: Array<{
|
|
24
|
+
type: string;
|
|
25
|
+
text?: string;
|
|
26
|
+
[k: string]: unknown;
|
|
27
|
+
}>;
|
|
28
|
+
structuredContent?: unknown;
|
|
29
|
+
isError?: boolean;
|
|
30
|
+
[k: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
export declare function callTool(client: Client, name: string, args?: Record<string, unknown>): Promise<ToolResult>;
|
|
33
|
+
/** Extract the first text content block from an MCP tool result. */
|
|
34
|
+
export declare function extractText(result: ToolResult): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Extract parsed JSON from an MCP tool result.
|
|
37
|
+
* Prefers structuredContent, then tries each text block for valid JSON.
|
|
38
|
+
*/
|
|
39
|
+
export declare function extractJson(result: ToolResult): unknown;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared MCP client factory.
|
|
3
|
+
* Connects to the Salai MCP HTTP endpoint and provides tool-calling helpers.
|
|
4
|
+
*/
|
|
5
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
6
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
7
|
+
export const DEFAULT_MCP_URL = 'https://mcp.salai.co.il/mcp';
|
|
8
|
+
export const PACKAGE_VERSION = '0.1.0';
|
|
9
|
+
export function resolveConfig(overrides) {
|
|
10
|
+
return {
|
|
11
|
+
apiKey: overrides?.apiKey ||
|
|
12
|
+
process.env.SALAI_API_KEY ||
|
|
13
|
+
process.env.MCP_API_KEY ||
|
|
14
|
+
null,
|
|
15
|
+
url: overrides?.url || process.env.SALAI_MCP_URL || DEFAULT_MCP_URL,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function buildHeaders(apiKey) {
|
|
19
|
+
const headers = {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
};
|
|
22
|
+
if (apiKey) {
|
|
23
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
24
|
+
}
|
|
25
|
+
return headers;
|
|
26
|
+
}
|
|
27
|
+
export async function createClient(opts) {
|
|
28
|
+
const headers = buildHeaders(opts.apiKey);
|
|
29
|
+
const transport = new StreamableHTTPClientTransport(new URL(opts.url), {
|
|
30
|
+
requestInit: { headers },
|
|
31
|
+
});
|
|
32
|
+
const client = new Client({ name: opts.clientName ?? 'salai-cli', version: PACKAGE_VERSION }, { capabilities: {} });
|
|
33
|
+
await client.connect(transport);
|
|
34
|
+
return client;
|
|
35
|
+
}
|
|
36
|
+
export async function callTool(client, name, args = {}) {
|
|
37
|
+
return (await client.callTool({ name, arguments: args }));
|
|
38
|
+
}
|
|
39
|
+
/** Extract the first text content block from an MCP tool result. */
|
|
40
|
+
export function extractText(result) {
|
|
41
|
+
if (!result.content)
|
|
42
|
+
return null;
|
|
43
|
+
const textBlock = result.content.find((c) => c.type === 'text');
|
|
44
|
+
return textBlock?.text ?? null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Extract parsed JSON from an MCP tool result.
|
|
48
|
+
* Prefers structuredContent, then tries each text block for valid JSON.
|
|
49
|
+
*/
|
|
50
|
+
export function extractJson(result) {
|
|
51
|
+
if (result.structuredContent != null) {
|
|
52
|
+
return result.structuredContent;
|
|
53
|
+
}
|
|
54
|
+
if (!result.content)
|
|
55
|
+
return null;
|
|
56
|
+
const textBlocks = result.content.filter((c) => c.type === 'text' && c.text);
|
|
57
|
+
for (const block of textBlocks) {
|
|
58
|
+
try {
|
|
59
|
+
return JSON.parse(block.text);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// not JSON, try next block
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return textBlocks[0]?.text ?? null;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=mcpClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpClient.js","sourceRoot":"","sources":["../src/mcpClient.ts"],"names":[],"mappings":"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"}
|
package/dist/salai.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Salai CLI — human-facing command-line interface for Salai grocery intelligence.
|
|
4
|
+
*
|
|
5
|
+
* Calls the Salai MCP server over HTTPS, same endpoint and auth as the MCP bridge.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* salai search "חלב"
|
|
9
|
+
* salai cart add 7290019489443 --qty 2
|
|
10
|
+
* salai compare 7290019489443:1 7290000123456:3
|
|
11
|
+
* salai tools
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
package/dist/salai.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Salai CLI — human-facing command-line interface for Salai grocery intelligence.
|
|
4
|
+
*
|
|
5
|
+
* Calls the Salai MCP server over HTTPS, same endpoint and auth as the MCP bridge.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* salai search "חלב"
|
|
9
|
+
* salai cart add 7290019489443 --qty 2
|
|
10
|
+
* salai compare 7290019489443:1 7290000123456:3
|
|
11
|
+
* salai tools
|
|
12
|
+
*/
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
import { createClient, resolveConfig, PACKAGE_VERSION } from './mcpClient.js';
|
|
15
|
+
import { registerSearchCommands } from './commands/search.js';
|
|
16
|
+
import { registerPriceCommands } from './commands/prices.js';
|
|
17
|
+
import { registerStoreCommands } from './commands/stores.js';
|
|
18
|
+
import { registerCartCommands } from './commands/cart.js';
|
|
19
|
+
import { registerRecommendCommands } from './commands/recommend.js';
|
|
20
|
+
import { registerCallCommands } from './commands/call.js';
|
|
21
|
+
const program = new Command()
|
|
22
|
+
.name('salai')
|
|
23
|
+
.description('Salai grocery intelligence CLI')
|
|
24
|
+
.version(PACKAGE_VERSION, '-v, --version')
|
|
25
|
+
.option('-k, --api-key <key>', 'Salai API key (or SALAI_API_KEY env)')
|
|
26
|
+
.option('--url <url>', 'MCP endpoint URL (or SALAI_MCP_URL env)')
|
|
27
|
+
.option('--json', 'output raw JSON instead of formatted tables')
|
|
28
|
+
.configureOutput({
|
|
29
|
+
writeErr: (str) => process.stderr.write(str),
|
|
30
|
+
});
|
|
31
|
+
let _client = null;
|
|
32
|
+
async function getClient() {
|
|
33
|
+
if (_client)
|
|
34
|
+
return _client;
|
|
35
|
+
const opts = program.opts();
|
|
36
|
+
const config = resolveConfig({
|
|
37
|
+
apiKey: opts.apiKey,
|
|
38
|
+
url: opts.url,
|
|
39
|
+
});
|
|
40
|
+
if (!config.apiKey) {
|
|
41
|
+
console.error('Warning: No API key. Set SALAI_API_KEY or use --api-key.\n' +
|
|
42
|
+
'Get your key: https://app.salai.co.il → Profile → API Key');
|
|
43
|
+
}
|
|
44
|
+
_client = await createClient({
|
|
45
|
+
...config,
|
|
46
|
+
clientName: 'salai-cli',
|
|
47
|
+
});
|
|
48
|
+
return _client;
|
|
49
|
+
}
|
|
50
|
+
function isJson() {
|
|
51
|
+
return !!program.opts().json;
|
|
52
|
+
}
|
|
53
|
+
registerSearchCommands(program, getClient, isJson);
|
|
54
|
+
registerPriceCommands(program, getClient, isJson);
|
|
55
|
+
registerStoreCommands(program, getClient, isJson);
|
|
56
|
+
registerCartCommands(program, getClient, isJson);
|
|
57
|
+
registerRecommendCommands(program, getClient, isJson);
|
|
58
|
+
registerCallCommands(program, getClient, isJson);
|
|
59
|
+
program.exitOverride();
|
|
60
|
+
async function main() {
|
|
61
|
+
try {
|
|
62
|
+
await program.parseAsync(process.argv);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const isCommanderExit = err && typeof err === 'object' && 'code' in err && err.code === 'commander.helpDisplayed';
|
|
66
|
+
if (isCommanderExit)
|
|
67
|
+
return;
|
|
68
|
+
const versionExit = err && typeof err === 'object' && 'code' in err && err.code === 'commander.version';
|
|
69
|
+
if (versionExit)
|
|
70
|
+
return;
|
|
71
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
72
|
+
console.error(`Error: ${msg}`);
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
if (_client) {
|
|
77
|
+
await _client.close().catch(() => { });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
main();
|
|
82
|
+
//# sourceMappingURL=salai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"salai.js","sourceRoot":"","sources":["../src/salai.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "salai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Salai grocery intelligence CLI — search products, compare prices, manage your cart from the terminal",
|
|
5
|
+
"keywords": ["salai", "grocery", "price-comparison", "cli", "israel"],
|
|
6
|
+
"homepage": "https://salai.co.il",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/Salai-app/salai-cli"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"bin": {
|
|
14
|
+
"salai": "dist/salai.js"
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/salai.js",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"!dist/**/*.test.*",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"dev": "tsx src/salai.ts",
|
|
26
|
+
"test": "tsc && node --test dist/salai.test.js",
|
|
27
|
+
"clean": "rm -rf dist",
|
|
28
|
+
"prepublishOnly": "pnpm run build"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
32
|
+
"commander": "^13.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.8.0",
|
|
36
|
+
"tsx": "^4.4.0",
|
|
37
|
+
"typescript": "^5.2.2"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=20.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|