diff --git a/.claude/skills/dolibarr-sandbox-checkpoint/SKILL.md b/.claude/skills/dolibarr-sandbox-checkpoint/SKILL.md index efb1a7a..e5a59d1 100644 --- a/.claude/skills/dolibarr-sandbox-checkpoint/SKILL.md +++ b/.claude/skills/dolibarr-sandbox-checkpoint/SKILL.md @@ -29,7 +29,12 @@ arcodange sandbox checkpoint relink-env ``` 1. **`status`** — HTTP liveness + whether the write agent (`ai_agent_sandbox`) is - *armed* (its key authenticates `GET /users/info`). Read-only, no cluster access. + *armed*. The armed probe calls `GET /users/info` with the linked key; three + outcomes: **200** → `ARMED`; **401** → the key itself is rejected (stale, e.g. + an iso-prod refresh re-encrypted it) → re-run `provision`; **403** → the key + authenticates but lacks right **251** (`user → lire`, which `/users/info` + requires) → re-provision (a fresh run grants it via `WRITE_IDS` in + `test/provisionSandbox.ts`) or grant 251 by hand. Read-only, no cluster access. 2. **`refresh --yes`** — re-seed the sandbox iso-prod from prod, wrapping `ops/sandbox/sandbox-lifecycle.sh` (read-only `pg_dump` of prod → `DROP OWNED` → `pg_restore`, then documents/logo sync). **Destructive**: requires `--yes`, and @@ -40,7 +45,8 @@ arcodange sandbox checkpoint relink-env (`test/provisionSandbox.ts`). It opens a browser; **you complete the admin login** — with the **PROD** admin credentials, since the sandbox is iso-prod (they come from `test/.env.sandbox`). The POC re-grants the agent's rights - (including `banque lire`) and writes the key to `test/.ai_agent_sandbox.key`, + (including `banque lire` and `user lire` = right 251, which the armed probe + needs) and writes the key to `test/.ai_agent_sandbox.key`, then this command auto-runs `relink-env`. Needs `deno`. 4. **`relink-env`** — (re)write `dolibarr-sandbox-write/.env` from `test/.ai_agent_sandbox.key` (mode 600) and verify it authenticates. Run it diff --git a/.claude/skills/dolibarr-sandbox-checkpoint/scripts/checkpoint-relink-env.sh b/.claude/skills/dolibarr-sandbox-checkpoint/scripts/checkpoint-relink-env.sh index 73bf73e..3fa4607 100755 --- a/.claude/skills/dolibarr-sandbox-checkpoint/scripts/checkpoint-relink-env.sh +++ b/.claude/skills/dolibarr-sandbox-checkpoint/scripts/checkpoint-relink-env.sh @@ -24,7 +24,13 @@ printf 'verify: ' "${DOLW}" GET /users/info | python3 -c "import json,sys d = json.load(sys.stdin) if isinstance(d, dict) and d.get('login'): - print('OK — armed as %s (id %s)' % (d['login'], d.get('id'))) + print('OK — armed as %s (id %s)' % (d['login'], d.get('id'))); sys.exit(0) +err = d.get('error', {}) if isinstance(d, dict) else {} +code = str(err.get('code', '')) +if code == '401': + print('FAILED — 401 key rejected (stale/instance-encrypted) → re-run checkpoint provision') +elif code == '403': + print('FAILED — 403 key authenticates but right 251 (user lire) is missing → re-provision or grant 251 (WRITE_IDS, test/provisionSandbox.ts)') else: - msg = d.get('error', {}).get('message', '?') if isinstance(d, dict) else str(d) - print('FAILED — %s' % msg); sys.exit(1)" + print('FAILED — %s' % (err.get('message', '?') if isinstance(d, dict) else str(d))) +sys.exit(1)" diff --git a/.claude/skills/dolibarr-sandbox-checkpoint/scripts/checkpoint-status.sh b/.claude/skills/dolibarr-sandbox-checkpoint/scripts/checkpoint-status.sh index 1e9e7c0..1bdeb49 100755 --- a/.claude/skills/dolibarr-sandbox-checkpoint/scripts/checkpoint-status.sh +++ b/.claude/skills/dolibarr-sandbox-checkpoint/scripts/checkpoint-status.sh @@ -22,10 +22,15 @@ try: except Exception: print('NOT armed — no/invalid response'); sys.exit(0) if isinstance(d, dict) and d.get('login'): - print('ARMED — login=%s id=%s' % (d['login'], d.get('id'))) + print('ARMED — login=%s id=%s' % (d['login'], d.get('id'))); sys.exit(0) +err = d.get('error', {}) if isinstance(d, dict) else {} +code = str(err.get('code', '')) +if code == '401': + print('NOT armed — 401 key rejected (stale after a refresh?) → run checkpoint provision') +elif code == '403': + print('NOT armed — 403 key OK but right 251 (user lire) missing → re-provision or grant 251 (WRITE_IDS, test/provisionSandbox.ts)') else: - msg = d.get('error', {}).get('message', '?') if isinstance(d, dict) else 'unexpected' - print('NOT armed — %s' % msg[:80])" + print('NOT armed — %s' % str(err.get('message', 'unexpected'))[:80])" else echo " write .env : ABSENT" echo " write agent : not linked — run 'arcodange sandbox checkpoint relink-env' after provisioning" diff --git a/.claude/skills/dolibarr-sandbox-write/SKILL.md b/.claude/skills/dolibarr-sandbox-write/SKILL.md index e8eb64a..26c31a9 100644 --- a/.claude/skills/dolibarr-sandbox-write/SKILL.md +++ b/.claude/skills/dolibarr-sandbox-write/SKILL.md @@ -183,6 +183,9 @@ sandbox KissMetrics on `--target sandbox` and the prod one on `--target prod`. - **`banque lire`** (rights id 111) is granted → `scripts/bank-accounts.sh` lists accounts (id/label/bank) so a payment can pick its `account_id`. It's in the provisioner's `WRITE_IDS`, so a fresh `provisionSandbox.ts` run includes it. +- **`user lire`** (rights id 251) is granted → the checkpoint *armed* probe + (`GET /users/info` in `status`/`relink-env`) answers 200 instead of 403. Also in + the provisioner's `WRITE_IDS`. - **Avoirs (credit notes)** → `creditnote-create.sh` (customer invoice `type=2` referencing `source_invoice`; amounts negative, ref `AVC…`). Supplier avoirs are a follow-up. diff --git a/test/README.md b/test/README.md index b35d417..cb3e00c 100644 --- a/test/README.md +++ b/test/README.md @@ -95,12 +95,15 @@ Vault at `kvv2/erp-sandbox/ai_agent`. ### Write rights granted -The `ai_agent_sandbox` user is created non-admin and granted read + create on: +The `ai_agent_sandbox` user is created non-admin and granted (the authoritative +list is `WRITE_IDS` in `provisionSandbox.ts`): | Module | rights ids | | ---------------- | ---------------------------------- | | facture | lire=11, creer=12 | -| societe | lire=121, creer=122 | +| societe | lire=121, creer=122, client voir=262 | | societe contact | lire=281, creer=282 | | fournisseur | lire=1181, facture lire=1231, facture creer=1232 | | produit | lire=31, creer=32 | +| banque | lire=111 | +| user | lire=251 — requis par le probe armé `GET /users/info` (checkpoint `status`/`relink-env`) | diff --git a/test/provisionSandbox.ts b/test/provisionSandbox.ts index bd3819a..2b4375e 100644 --- a/test/provisionSandbox.ts +++ b/test/provisionSandbox.ts @@ -39,6 +39,7 @@ import userSetup from "./scripts/admin/userSetup.ts"; fournisseur: lire=1181, facture lire=1231, facture creer=1232 produit: lire=31, creer=32 banque: lire=111 + user: lire=251 */ const WRITE_IDS = [ 11, // facture lire @@ -56,6 +57,9 @@ const WRITE_IDS = [ 31, // produit lire 32, // produit creer 111, // banque lire — list bank accounts (GET /bankaccounts) to pick account_id + 251, // user lire — requis par le probe armé GET /users/info (checkpoint status/relink) + // Sans lui l'endpoint répond 403 alors que la clé authentifie, et le + // checkpoint affiche NOT armed sur un agent pourtant fonctionnel. ]; const KEY_FILE = ".ai_agent_sandbox.key";