auchan-mcp 0.2.1 → 0.3.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/.env.example +21 -0
- package/README.md +17 -3
- package/dist/index.js +104 -51
- package/package.json +2 -1
package/.env.example
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# auchan-mcp: variáveis de ambiente
|
|
2
|
+
# Com o plugin supermercados-mcp, o ficheiro é criado e aberto sozinho na primeira vez:
|
|
3
|
+
# Windows: %USERPROFILE%\.supermercados-mcp\credentials.env
|
|
4
|
+
# macOS/Linux: ~/.supermercados-mcp/credentials.env
|
|
5
|
+
# Sem o plugin, podes pôr as duas linhas abaixo nesse ficheiro ou em ~/.auchan-mcp/credentials.env.
|
|
6
|
+
# Esse ficheiro nunca vai para o git. A pesquisa funciona sem ele.
|
|
7
|
+
|
|
8
|
+
# Necessárias para o carrinho (get_cart, add_to_cart, update_cart_item).
|
|
9
|
+
# AUCHAN_EMAIL=tu@exemplo.pt
|
|
10
|
+
# AUCHAN_PASSWORD=a-tua-palavra-passe
|
|
11
|
+
|
|
12
|
+
# As seguintes definem-se no "env" do servidor no cliente MCP, não no ficheiro acima.
|
|
13
|
+
|
|
14
|
+
# Outro ficheiro de credenciais (por omissão: o do plugin e depois ~/.auchan-mcp/credentials.env).
|
|
15
|
+
# AUCHAN_ENV_PATH=C:\caminho\para\credentials.env
|
|
16
|
+
|
|
17
|
+
# Outra pasta para a sessão guardada e a cache (por omissão: ~/.auchan-mcp).
|
|
18
|
+
# AUCHAN_STATE_DIR=C:\caminho\para\pasta
|
|
19
|
+
|
|
20
|
+
# Põe false só para ver o browser durante o login, ao procurar um problema.
|
|
21
|
+
# AUCHAN_LOGIN_HEADLESS=true
|
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Feito à imagem do [continente-mcp](https://github.com/davidmarqu3s/continente-m
|
|
|
10
10
|
|
|
11
11
|
- **Pesquisa sem conta**: nome, marca, preço, preço e preço por litro, quilo ou unidade.
|
|
12
12
|
- **Carrinho**: ver, juntar e mudar quantidades; cada alteração é confirmada no carrinho a seguir.
|
|
13
|
+
- **Entra sozinho**: usa o email e a palavra-passe de um ficheiro teu, em segundo plano e sem janela, e volta a entrar sempre que o Auchan termina a sessão.
|
|
13
14
|
- **Respeita o site**: no máximo um pedido por segundo, resultados guardados durante 6 horas e um user-agent que diz quem é.
|
|
14
15
|
- **Tu decides**: a finalização da encomenda e o pagamento fazem-se sempre no Auchan.pt. O servidor não tem nenhuma ferramenta para isso.
|
|
15
16
|
|
|
@@ -31,7 +32,16 @@ Precisas do Node.js 20.18.1 ou mais recente e de um cliente MCP, por exemplo o C
|
|
|
31
32
|
}
|
|
32
33
|
```
|
|
33
34
|
|
|
34
|
-
A pesquisa funciona logo, sem conta. Para o carrinho
|
|
35
|
+
A pesquisa funciona logo, sem conta. Para o carrinho, cria o ficheiro `%USERPROFILE%\.auchan-mcp\credentials.env` (no macOS e Linux, `~/.auchan-mcp/credentials.env`) com duas linhas, sem aspas e sem espaços à volta do `=`:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
AUCHAN_EMAIL=o-teu-email
|
|
39
|
+
AUCHAN_PASSWORD=a-tua-palavra-passe
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Também lê `~/.supermercados-mcp/credentials.env`, o ficheiro que o plugin [supermercados-mcp](https://github.com/vasco-branco06/supermercados-mcp) cria e abre sozinho na primeira vez, e o que `AUCHAN_ENV_PATH` indicar. O Auchan só aceita produtos no carrinho depois de escolheres uma vez, no site, entrega em casa ou levantamento (com o código postal); essa escolha fica guardada na tua conta.
|
|
43
|
+
|
|
44
|
+
A palavra-passe fica em texto simples nesse ficheiro, na tua pasta de utilizador. Não o partilhes. Só é lida para preencher o formulário de login e nunca aparece nas respostas nem nos registos. A sessão (cookies e armazenamento local) fica em `%USERPROFILE%\.auchan-mcp\cookies.json`.
|
|
35
45
|
|
|
36
46
|
## Usar
|
|
37
47
|
|
|
@@ -42,12 +52,16 @@ Pede coisas como "procura leite meio-gordo no Auchan" ou "junta 2 pacotes ao car
|
|
|
42
52
|
| `search_products` | Pesquisar o catálogo (devolve JSON) |
|
|
43
53
|
| `get_cart` | Ver o carrinho |
|
|
44
54
|
| `add_to_cart` / `update_cart_item` | Juntar um produto / mudar a quantidade (0 tira-o) |
|
|
45
|
-
| `login` |
|
|
46
|
-
| `logout` | Desligar a conta: apaga a sessão guardada neste computador |
|
|
55
|
+
| `login` | Verificar ou renovar a sessão com o ficheiro de credenciais (as ferramentas do carrinho fazem-no sozinhas) |
|
|
56
|
+
| `logout` | Desligar a conta: apaga a sessão guardada neste computador (o ficheiro de credenciais fica) |
|
|
47
57
|
| `close_session` | Libertar recursos |
|
|
48
58
|
|
|
59
|
+
Com o servidor a correr, faz um pedido leve de 10 em 10 minutos para o Auchan não fechar a sessão por inatividade, e guarda o que o site renova.
|
|
60
|
+
|
|
49
61
|
## Mais
|
|
50
62
|
|
|
63
|
+
- **Sem ficheiro de credenciais:** existe o ramo [`login-por-janela`](https://github.com/vasco-branco06/supermercados-mcp/tree/login-por-janela), em que a pessoa entra numa janela do browser e a palavra-passe nunca é guardada.
|
|
64
|
+
- **Definições:** variáveis em [`.env.example`](.env.example).
|
|
51
65
|
- **Problemas:** [docs/troubleshooting.md](docs/troubleshooting.md).
|
|
52
66
|
- **Testes:** `npm test` corre sem internet. `LIVE=1` liga um teste que faz um pedido real ao Auchan.pt (no PowerShell: `$env:LIVE=1; npm test`).
|
|
53
67
|
|
package/dist/index.js
CHANGED
|
@@ -176,21 +176,47 @@ async function fetchPage(url2, { fetchImpl = fetch, gate = throttle } = {}) {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
// src/paths.js
|
|
179
|
+
import { existsSync, readFileSync as readFileSync2 } from "node:fs";
|
|
179
180
|
import { homedir } from "node:os";
|
|
180
181
|
import { join } from "node:path";
|
|
181
182
|
function resolveStatePaths(env = process.env) {
|
|
182
|
-
const
|
|
183
|
+
const home = env.HOME || env.USERPROFILE || homedir();
|
|
184
|
+
const stateDir = env.AUCHAN_STATE_DIR || join(home, ".auchan-mcp");
|
|
183
185
|
return {
|
|
184
186
|
stateDir,
|
|
185
187
|
cacheDir: join(stateDir, "cache"),
|
|
188
|
+
// Where the plugin's first run creates the template, shared by every supermarket.
|
|
189
|
+
sharedCredentialFile: env.SUPERMERCADOS_MCP_CREDENTIALS || join(env.SUPERMERCADOS_MCP_DIR || join(home, ".supermercados-mcp"), "credentials.env"),
|
|
190
|
+
credentialFile: join(stateDir, "credentials.env"),
|
|
186
191
|
cookieFile: join(stateDir, "cookies.json")
|
|
187
192
|
};
|
|
188
193
|
}
|
|
194
|
+
function parseEnvFile(contents) {
|
|
195
|
+
const parsed = {};
|
|
196
|
+
for (const line of contents.split(/\r?\n/)) {
|
|
197
|
+
const raw = line.trim().replace(/^export\s+/, "");
|
|
198
|
+
const index = raw.indexOf("=");
|
|
199
|
+
if (!raw || raw.startsWith("#") || index === -1) continue;
|
|
200
|
+
const key = raw.slice(0, index).trim();
|
|
201
|
+
const value = raw.slice(index + 1).trim().replace(/^(["'])(.*)\1$/, "$2");
|
|
202
|
+
if (/^[A-Z_][A-Z0-9_]*$/.test(key)) parsed[key] = value;
|
|
203
|
+
}
|
|
204
|
+
return parsed;
|
|
205
|
+
}
|
|
206
|
+
function loadCredentials(env = process.env) {
|
|
207
|
+
const { credentialFile, sharedCredentialFile } = resolveStatePaths(env);
|
|
208
|
+
for (const file2 of [env.AUCHAN_ENV_PATH, sharedCredentialFile, credentialFile]) {
|
|
209
|
+
if (!file2 || !existsSync(file2)) continue;
|
|
210
|
+
const { AUCHAN_EMAIL: email3, AUCHAN_PASSWORD: password } = parseEnvFile(readFileSync2(file2, "utf8"));
|
|
211
|
+
if (email3 && password) return { email: email3, password };
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
189
215
|
|
|
190
216
|
// src/search.js
|
|
191
217
|
import * as cheerio2 from "cheerio";
|
|
192
218
|
import { createHash } from "node:crypto";
|
|
193
|
-
import { mkdirSync, readFileSync as
|
|
219
|
+
import { mkdirSync, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
194
220
|
import { join as join2 } from "node:path";
|
|
195
221
|
|
|
196
222
|
// ../../node_modules/zod/v4/classic/external.js
|
|
@@ -20010,7 +20036,7 @@ var searchUrl = (query) => `${AUCHAN_BASE}/pt/pesquisa?q=${encodeURIComponent(qu
|
|
|
20010
20036
|
var cacheFile = (cacheDir, query) => join2(cacheDir, `${createHash("sha256").update(query.trim().toLowerCase()).digest("hex").slice(0, 32)}.json`);
|
|
20011
20037
|
function readCache(file2, now) {
|
|
20012
20038
|
try {
|
|
20013
|
-
const entry = JSON.parse(
|
|
20039
|
+
const entry = JSON.parse(readFileSync3(file2, "utf8"));
|
|
20014
20040
|
if (now - Date.parse(entry.fetched_at) < CACHE_TTL_MS && Array.isArray(entry.raw)) return entry;
|
|
20015
20041
|
} catch {
|
|
20016
20042
|
}
|
|
@@ -20035,23 +20061,30 @@ async function searchProducts(query, limit = 10, { cacheDir, fetchImpl, now = Da
|
|
|
20035
20061
|
}
|
|
20036
20062
|
|
|
20037
20063
|
// src/session.js
|
|
20038
|
-
import { chmodSync, existsSync, mkdirSync as mkdirSync2, readFileSync as
|
|
20064
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
20039
20065
|
import { dirname } from "node:path";
|
|
20040
20066
|
var LOGIN_URL = `${AUCHAN_BASE}/on/demandware.store/Sites-AuchanPT-Site/pt_PT/Login-OAuthLogin?oauthProvider=Salesforce&oauthLoginTargetEndPoint=1`;
|
|
20041
20067
|
var DELIVERY_URL = `${AUCHAN_BASE}/on/demandware.store/Sites-AuchanPT-Site/pt_PT/Delivery-Header`;
|
|
20042
20068
|
var CART_URL = `${AUCHAN_BASE}/on/demandware.store/Sites-AuchanPT-Site/pt_PT/Cart-Show`;
|
|
20069
|
+
var EMAIL_FIELD = 'input[name="uname1"], input[type="email"]';
|
|
20070
|
+
var PASSWORD_FIELD = 'input[name="passwordLogin"], input[type="password"]';
|
|
20071
|
+
var SUBMIT_BUTTON = '#btnSubmit_login, input[name="btnSubmit_login"], button[type="submit"]:visible';
|
|
20072
|
+
var CHALLENGE = 'iframe[src*="captcha"], iframe[title*="challenge"], .g-recaptcha, .h-captcha, #px-captcha';
|
|
20043
20073
|
var BROWSER_CHANNELS = ["msedge", "chrome", void 0];
|
|
20044
20074
|
var NO_PLAYWRIGHT = "falta o Playwright; instala o pacote de novo (npm install auchan-mcp)";
|
|
20045
|
-
var NO_BROWSER = 'n\xE3o encontrei o Microsoft Edge nem o Google Chrome para
|
|
20075
|
+
var NO_BROWSER = 'n\xE3o encontrei o Microsoft Edge nem o Google Chrome para fazer o login; instala um deles ou corre "npx playwright install chromium"';
|
|
20076
|
+
var FORM_CHANGED = "o formul\xE1rio de login do Auchan n\xE3o \xE9 o esperado (o site pode ter mudado); \xE9 preciso atualizar o servidor";
|
|
20077
|
+
var LOGIN_FAILED = "o login n\xE3o terminou; confirma o email e a palavra-passe no ficheiro de credenciais";
|
|
20078
|
+
var NO_DELIVERY = "o Auchan ainda n\xE3o tem a tua entrega escolhida; escolhe-a uma vez no auchan.pt (entrega em casa ou levantamento, com o c\xF3digo postal) e tenta de novo";
|
|
20046
20079
|
function writeState(path, state) {
|
|
20047
20080
|
mkdirSync2(dirname(path), { recursive: true, mode: 448 });
|
|
20048
20081
|
writeFileSync2(path, JSON.stringify(state, null, 2), { mode: 384 });
|
|
20049
20082
|
chmodSync(path, 384);
|
|
20050
20083
|
}
|
|
20051
20084
|
function readState(path) {
|
|
20052
|
-
if (!
|
|
20085
|
+
if (!existsSync2(path)) return null;
|
|
20053
20086
|
try {
|
|
20054
|
-
const saved = JSON.parse(
|
|
20087
|
+
const saved = JSON.parse(readFileSync4(path, "utf8"));
|
|
20055
20088
|
const state = Array.isArray(saved) ? { cookies: saved, origins: [] } : { cookies: saved?.cookies, origins: saved?.origins ?? [] };
|
|
20056
20089
|
return Array.isArray(state.cookies) && state.cookies.length ? state : null;
|
|
20057
20090
|
} catch {
|
|
@@ -20059,13 +20092,14 @@ function readState(path) {
|
|
|
20059
20092
|
}
|
|
20060
20093
|
}
|
|
20061
20094
|
function createSession({
|
|
20095
|
+
credentials,
|
|
20096
|
+
credentialFile = "credentials.env",
|
|
20062
20097
|
cookieFile,
|
|
20063
20098
|
log = console.error,
|
|
20064
20099
|
loadPlaywright = () => import("playwright"),
|
|
20065
|
-
|
|
20066
|
-
|
|
20067
|
-
|
|
20068
|
-
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
20100
|
+
headless = true,
|
|
20101
|
+
loginTimeoutMs = 3e4,
|
|
20102
|
+
keepAliveMs = 10 * 60 * 1e3
|
|
20069
20103
|
}) {
|
|
20070
20104
|
let playwright = null;
|
|
20071
20105
|
let api = null;
|
|
@@ -20105,7 +20139,7 @@ function createSession({
|
|
|
20105
20139
|
assertNotBlocked(res.status(), "");
|
|
20106
20140
|
return res.ok() && /data-has-context="true"/.test(await res.text());
|
|
20107
20141
|
}
|
|
20108
|
-
async function launch(chromium
|
|
20142
|
+
async function launch(chromium) {
|
|
20109
20143
|
for (const channel of BROWSER_CHANNELS) {
|
|
20110
20144
|
try {
|
|
20111
20145
|
return await chromium.launch({ headless, ...channel && { channel } });
|
|
@@ -20114,31 +20148,49 @@ function createSession({
|
|
|
20114
20148
|
}
|
|
20115
20149
|
return null;
|
|
20116
20150
|
}
|
|
20117
|
-
|
|
20151
|
+
function scrubbed(error62) {
|
|
20152
|
+
for (const secret of [credentials?.email, credentials?.password]) {
|
|
20153
|
+
if (secret) error62.message = String(error62.message).split(secret).join("***");
|
|
20154
|
+
}
|
|
20155
|
+
return error62;
|
|
20156
|
+
}
|
|
20157
|
+
async function autoLogin() {
|
|
20158
|
+
if (!credentials) {
|
|
20159
|
+
throw new Error(`falta o ficheiro ${credentialFile} com as linhas AUCHAN_EMAIL=... e AUCHAN_PASSWORD=... (ver o README)`);
|
|
20160
|
+
}
|
|
20118
20161
|
const { chromium } = await pw();
|
|
20119
|
-
const browser = await launch(chromium
|
|
20162
|
+
const browser = await launch(chromium);
|
|
20120
20163
|
if (!browser) throw new Error(NO_BROWSER);
|
|
20121
|
-
const closed = new Promise((resolve) => browser.on("disconnected", () => resolve("closed")));
|
|
20122
20164
|
try {
|
|
20123
|
-
const context = await browser.newContext({ locale: "pt-PT",
|
|
20165
|
+
const context = await browser.newContext({ locale: "pt-PT", userAgent: USER_AGENT });
|
|
20124
20166
|
const page = await context.newPage();
|
|
20125
|
-
log(
|
|
20126
|
-
await throttle(() => page.goto(
|
|
20127
|
-
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
}
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
|
|
20137
|
-
|
|
20138
|
-
|
|
20167
|
+
log("[auth] A iniciar sess\xE3o no Auchan...");
|
|
20168
|
+
await throttle(() => page.goto(LOGIN_URL, { waitUntil: "domcontentloaded", timeout: 3e4 }));
|
|
20169
|
+
await page.locator("#onetrust-accept-btn-handler").click({ timeout: 2e3 }).catch(() => {
|
|
20170
|
+
});
|
|
20171
|
+
try {
|
|
20172
|
+
await page.locator(EMAIL_FIELD).first().fill(credentials.email, { timeout: 2e4 });
|
|
20173
|
+
await page.locator(PASSWORD_FIELD).first().fill(credentials.password, { timeout: 1e4 });
|
|
20174
|
+
await throttle(() => page.locator(SUBMIT_BUTTON).first().click({ timeout: 1e4 }));
|
|
20175
|
+
} catch {
|
|
20176
|
+
throw new Error(FORM_CHANGED);
|
|
20177
|
+
}
|
|
20178
|
+
try {
|
|
20179
|
+
await page.waitForURL((url2) => url2.hostname === "www.auchan.pt" && !/login/i.test(url2.pathname), { timeout: loginTimeoutMs });
|
|
20180
|
+
} catch {
|
|
20181
|
+
if (await page.locator(CHALLENGE).count()) {
|
|
20182
|
+
throw new BlockedError("o site pediu uma verifica\xE7\xE3o anti-rob\xF4 (CAPTCHA) no login; entra uma vez no auchan.pt pelo browser e tenta mais tarde");
|
|
20139
20183
|
}
|
|
20184
|
+
throw new Error(LOGIN_FAILED);
|
|
20140
20185
|
}
|
|
20141
|
-
throw new Error(
|
|
20186
|
+
if (!await isLoggedIn(context.request)) throw new Error(LOGIN_FAILED);
|
|
20187
|
+
const next = await context.storageState();
|
|
20188
|
+
writeState(cookieFile, next);
|
|
20189
|
+
log("[auth] Sess\xE3o iniciada.");
|
|
20190
|
+
return next;
|
|
20191
|
+
} catch (error62) {
|
|
20192
|
+
if (error62 instanceof BlockedError || error62.message === LOGIN_FAILED || error62.message === FORM_CHANGED || error62.message.startsWith("falta ")) throw scrubbed(error62);
|
|
20193
|
+
throw new Error(LOGIN_FAILED);
|
|
20142
20194
|
} finally {
|
|
20143
20195
|
await browser.close().catch(() => {
|
|
20144
20196
|
});
|
|
@@ -20167,8 +20219,10 @@ function createSession({
|
|
|
20167
20219
|
await persist(true);
|
|
20168
20220
|
return;
|
|
20169
20221
|
}
|
|
20170
|
-
|
|
20222
|
+
if (credentials) await openApi(await autoLogin());
|
|
20223
|
+
else loggedIn = false;
|
|
20171
20224
|
} catch {
|
|
20225
|
+
loggedIn = false;
|
|
20172
20226
|
} finally {
|
|
20173
20227
|
beating = false;
|
|
20174
20228
|
}
|
|
@@ -20179,26 +20233,19 @@ function createSession({
|
|
|
20179
20233
|
if (api && loggedIn) return Promise.resolve("saved");
|
|
20180
20234
|
pending ??= (async () => {
|
|
20181
20235
|
try {
|
|
20236
|
+
let how = "login";
|
|
20182
20237
|
const saved = readState(cookieFile);
|
|
20183
20238
|
if (saved) {
|
|
20184
20239
|
await openApi(saved);
|
|
20185
|
-
|
|
20186
|
-
|
|
20187
|
-
loggedIn = true;
|
|
20188
|
-
await persist(true);
|
|
20189
|
-
startKeepAlive();
|
|
20190
|
-
return "saved";
|
|
20191
|
-
}
|
|
20192
|
-
log(logged ? "[auth] Falta escolher a entrega." : "[auth] A sess\xE3o guardada expirou.");
|
|
20193
|
-
await openApi(await interactiveLogin(logged ? saved : null));
|
|
20194
|
-
loggedIn = true;
|
|
20195
|
-
startKeepAlive();
|
|
20196
|
-
return "window";
|
|
20240
|
+
if (await isLoggedIn(api)) how = "saved";
|
|
20241
|
+
else log("[auth] A sess\xE3o guardada expirou; vou entrar outra vez.");
|
|
20197
20242
|
}
|
|
20198
|
-
await openApi(await
|
|
20243
|
+
if (how === "login") await openApi(await autoLogin());
|
|
20244
|
+
if (!await hasDeliveryContext(api)) throw new Error(NO_DELIVERY);
|
|
20199
20245
|
loggedIn = true;
|
|
20246
|
+
await persist(true);
|
|
20200
20247
|
startKeepAlive();
|
|
20201
|
-
return
|
|
20248
|
+
return how;
|
|
20202
20249
|
} finally {
|
|
20203
20250
|
pending = null;
|
|
20204
20251
|
}
|
|
@@ -20302,12 +20349,12 @@ var CART_TOOLS = [
|
|
|
20302
20349
|
var LOGIN_TOOLS = [
|
|
20303
20350
|
{
|
|
20304
20351
|
name: "login",
|
|
20305
|
-
description: "Connect your Auchan account
|
|
20352
|
+
description: "Connect your Auchan account with the email and password in credentials.env (no window). Logs in again by itself whenever the session ends. Cart tools call this automatically.",
|
|
20306
20353
|
inputSchema: { type: "object", properties: {} }
|
|
20307
20354
|
},
|
|
20308
20355
|
{
|
|
20309
20356
|
name: "logout",
|
|
20310
|
-
description: "Disconnect your Auchan account: deletes the saved session from this computer.",
|
|
20357
|
+
description: "Disconnect your Auchan account: deletes the saved session from this computer (credentials.env is kept).",
|
|
20311
20358
|
inputSchema: { type: "object", properties: {} }
|
|
20312
20359
|
}
|
|
20313
20360
|
];
|
|
@@ -20340,7 +20387,13 @@ var AuchanServer = class {
|
|
|
20340
20387
|
const paths = resolveStatePaths(env);
|
|
20341
20388
|
this.cacheDir = paths.cacheDir;
|
|
20342
20389
|
this.fetchImpl = fetchImpl;
|
|
20343
|
-
this.
|
|
20390
|
+
this.credentialsFound = Boolean(loadCredentials(env));
|
|
20391
|
+
this.session = session ?? createSession({
|
|
20392
|
+
credentials: loadCredentials(env),
|
|
20393
|
+
credentialFile: paths.sharedCredentialFile,
|
|
20394
|
+
cookieFile: paths.cookieFile,
|
|
20395
|
+
headless: env.AUCHAN_LOGIN_HEADLESS !== "false"
|
|
20396
|
+
});
|
|
20344
20397
|
this.tools = [SEARCH_TOOL, ...CART_TOOLS, ...LOGIN_TOOLS, CLOSE_TOOL];
|
|
20345
20398
|
this.server = new Server({ name: "auchan-mcp", version: VERSION }, { capabilities: { tools: {} } });
|
|
20346
20399
|
this.server.onclose = () => {
|
|
@@ -20384,7 +20437,7 @@ var AuchanServer = class {
|
|
|
20384
20437
|
);
|
|
20385
20438
|
}
|
|
20386
20439
|
case "login":
|
|
20387
|
-
return text(await this.session.login() === "saved" ? "J\xE1 tens a sess\xE3o do Auchan iniciada." : "Sess\xE3o do Auchan iniciada. J\xE1 posso usar o teu carrinho.");
|
|
20440
|
+
return text(await this.session.login() === "saved" ? "J\xE1 tens a sess\xE3o do Auchan iniciada." : "Sess\xE3o do Auchan iniciada com o ficheiro de credenciais. J\xE1 posso usar o teu carrinho.");
|
|
20388
20441
|
case "logout":
|
|
20389
20442
|
await this.session.logout();
|
|
20390
20443
|
return text("Sess\xE3o do Auchan terminada: apaguei a sess\xE3o guardada neste computador.");
|
|
@@ -20398,7 +20451,7 @@ var AuchanServer = class {
|
|
|
20398
20451
|
}
|
|
20399
20452
|
async start() {
|
|
20400
20453
|
await this.server.connect(new StdioServerTransport());
|
|
20401
|
-
console.error(`Auchan MCP v${VERSION} started`);
|
|
20454
|
+
console.error(`Auchan MCP v${VERSION} started${this.credentialsFound ? "" : " (search only until credentials.env exists)"}`);
|
|
20402
20455
|
}
|
|
20403
20456
|
};
|
|
20404
20457
|
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auchan-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Servidor MCP para o Auchan.pt: pesquisa de produtos e gestão do carrinho.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"files": [
|
|
30
30
|
"dist/",
|
|
31
|
+
".env.example",
|
|
31
32
|
"LICENSE",
|
|
32
33
|
"README.md"
|
|
33
34
|
],
|