feat(test): provision erp-sandbox via Playwright (REST API + write-scoped ai_agent_sandbox user) #14

Merged
arcodange merged 1 commits from claude/poc-provision-sandbox into main 2026-06-29 07:43:34 +02:00
Owner

What this adds

Extends the existing Deno + Playwright UI-automation POC (test/) to provision the erp-sandbox Dolibarr so the AI agent can write to it. New entrypoint provisionSandbox.ts runs end-to-end:

  1. Enable the REST API modulemoduleSetup.ts gains enableApiModule(ctx) (activation maps to llx_const.MAIN_MODULE_API=1; UI toggle on /admin/modules.php?mode=commonkanban).
  2. Create a write-scoped ai_agent_sandbox user (non-admin) — userSetup.ts createUser.
  3. Grant write rightsuserSetup.ts assignRights.
  4. Generate the user's API keyuserSetup.ts generateApiKey, then emit it safely.

New / changed files

  • test/scripts/admin/moduleSetup.ts — add enableApiModule(ctx). Resilient by design: tries the fr_FR card label "API/Web services REST (serveur)" (anchored exact match, same path as configureModule); if that card isn't present, falls back to matching any module card whose title matches /API.*REST|REST.*API/i. Throws a descriptive error if neither matches.
  • test/scripts/admin/userSetup.ts (new):
    • createUser(ctx, {login, password, lastname?, admin?}) → fills /user/card.php?action=create via forms.fillForm, toggles the admin checkbox explicitly, submits, and returns the new numeric id (parsed from the resulting ?id= URL, falling back to a hidden input[name="id"]).
    • assignRights(ctx, userId, rightIds[]) → for each right, navigates /user/perms.php?id=<userId> and clicks the a[href*="action=addrights"][href*="rights=<id>"] link. Idempotent: if no addrights link exists (already granted), it skips.
    • generateApiKey(ctx, userId) → on the user card edit page, triggers the generate control and reads the value back out of input[name="api_key"], returning it as a string. Reuses an existing key if already set. Does not log the value.
  • test/provisionSandbox.ts (new entrypoint — main.ts untouched) — builds globalCtx from env (default DOLIBARR_ADDRESS=https://erp-sandbox.arcodange.lab), runs the full flow, then writes the key to test/.ai_agent_sandbox.key (gitignored) with the console line AI_AGENT_SANDBOX API KEY WRITTEN TO test/.ai_agent_sandbox.key. A comment documents the next step (load into the dolibarr skill's sandbox config / Vault kvv2/erp-sandbox/ai_agent); Vault writes are intentionally not implemented in the POC.
  • test/.gitignore (new).env, .ai_agent_sandbox.key, *.key (defense-in-depth; root .gitignore already covers .env/*.key).
  • test/.env.example — adds a sandbox-provisioning section + the kubectl source for each secret.
  • test/README.md (new) — adds a ## Provision the sandbox section with the run command and the kubectl one-liners for the erp-sandbox namespace.

Why Playwright (UI), not SQL

API keys are stored encrypted with the instance key DOLI_INSTANCE_UNIQUE_ID (observed stored lengths 66/82 vs the 32-char plaintext). The sandbox has its own instance uuid, so a key can only be produced correctly by the sandbox itself — it must be generated through the UI and read back, never INSERTed raw. Passwords likewise use MAIN_SECURITY_HASH_ALGO=password_hash (bcrypt), so we let Dolibarr hash them via the create form. This is the whole reason the provisioning is driven through Playwright rather than direct DB writes.

Write rights granted to ai_agent_sandbox

Read + create on each module (stable Dolibarr rights ids, verified against prod Dolibarr 22.0.4):

Module rights ids
facture lire=11, creer=12
societe lire=121, creer=122
societe contact lire=281, creer=282
fournisseur lire=1181, facture lire=1231, facture creer=1232
produit lire=31, creer=32

Granting is done by clicking the perm rows in the UI; the ids are used to target the addrights links (and documented as the SQL-fallback reference).

Validation status — runtime check deferred

  • deno check provisionSandbox.ts scripts/admin/userSetup.ts passes (also re-checked main.ts + moduleSetup.ts); the repo uses checkJs: true.
  • Not run end-to-end. The sandbox Dolibarr is not installed/provisioned yet (empty DB, fresh install wizard — there is no UI to drive). Selector correctness is therefore best-effort, based on Dolibarr 22 conventions, and must be verified on the first real run after the install wizard completes. This is not a "tested" claim.

Selectors / labels GUESSED (confirm on first real run, marked GUESS: in code)

  • REST API module card title — fr_FR "API/Web services REST (serveur)" (fuzzy /API.*REST|REST.*API/i fallback in place).
  • User create form field names — login, lastname, password, and the admin checkbox (input[name="admin"]; some setups render it as a <select>).
  • New-user id source — ?id= in the post-submit URL, fallback hidden input[name="id"].
  • Perms grant control — a[href*="action=addrights"][href*="rights=<id>"].
  • API key field + generate control — input[name="api_key"] plus one of #generate_api_key / a[href*="action=generateapikey"] / span.fa-dice / a.fa-refresh.

Secrets hygiene

The generated API key is written only to the gitignored test/.ai_agent_sandbox.key and never printed; admin/DB passwords come from env and are never echoed. Staged diff was grep-scanned — no secret values committed.

🤖 Generated with Claude Code

## What this adds Extends the existing Deno + Playwright UI-automation POC (`test/`) to **provision the `erp-sandbox` Dolibarr** so the AI agent can write to it. New entrypoint `provisionSandbox.ts` runs end-to-end: 1. **Enable the REST API module** — `moduleSetup.ts` gains `enableApiModule(ctx)` (activation maps to `llx_const.MAIN_MODULE_API=1`; UI toggle on `/admin/modules.php?mode=commonkanban`). 2. **Create a write-scoped `ai_agent_sandbox` user** (non-admin) — `userSetup.ts` `createUser`. 3. **Grant write rights** — `userSetup.ts` `assignRights`. 4. **Generate the user's API key** — `userSetup.ts` `generateApiKey`, then emit it safely. ### New / changed files - **`test/scripts/admin/moduleSetup.ts`** — add `enableApiModule(ctx)`. Resilient by design: tries the fr_FR card label `"API/Web services REST (serveur)"` (anchored exact match, same path as `configureModule`); if that card isn't present, falls back to matching any module card whose title matches `/API.*REST|REST.*API/i`. Throws a descriptive error if neither matches. - **`test/scripts/admin/userSetup.ts`** *(new)*: - `createUser(ctx, {login, password, lastname?, admin?})` → fills `/user/card.php?action=create` via `forms.fillForm`, toggles the `admin` checkbox explicitly, submits, and returns the new numeric id (parsed from the resulting `?id=` URL, falling back to a hidden `input[name="id"]`). - `assignRights(ctx, userId, rightIds[])` → for each right, navigates `/user/perms.php?id=<userId>` and clicks the `a[href*="action=addrights"][href*="rights=<id>"]` link. **Idempotent**: if no addrights link exists (already granted), it skips. - `generateApiKey(ctx, userId)` → on the user card edit page, triggers the generate control and **reads the value back out of `input[name="api_key"]`**, returning it as a string. Reuses an existing key if already set. Does not log the value. - **`test/provisionSandbox.ts`** *(new entrypoint — `main.ts` untouched)* — builds `globalCtx` from env (default `DOLIBARR_ADDRESS=https://erp-sandbox.arcodange.lab`), runs the full flow, then **writes the key to `test/.ai_agent_sandbox.key`** (gitignored) with the console line `AI_AGENT_SANDBOX API KEY WRITTEN TO test/.ai_agent_sandbox.key`. A comment documents the next step (load into the dolibarr skill's sandbox config / Vault `kvv2/erp-sandbox/ai_agent`); Vault writes are intentionally **not** implemented in the POC. - **`test/.gitignore`** *(new)* — `.env`, `.ai_agent_sandbox.key`, `*.key` (defense-in-depth; root `.gitignore` already covers `.env`/`*.key`). - **`test/.env.example`** — adds a sandbox-provisioning section + the kubectl source for each secret. - **`test/README.md`** *(new)* — adds a `## Provision the sandbox` section with the run command and the kubectl one-liners for the `erp-sandbox` namespace. ## Why Playwright (UI), not SQL API keys are stored **encrypted with the instance key `DOLI_INSTANCE_UNIQUE_ID`** (observed stored lengths 66/82 vs the 32-char plaintext). The sandbox has its **own** instance uuid, so a key can only be produced correctly **by the sandbox itself** — it must be generated through the UI and read back, never `INSERT`ed raw. Passwords likewise use `MAIN_SECURITY_HASH_ALGO=password_hash` (bcrypt), so we let Dolibarr hash them via the create form. This is the whole reason the provisioning is driven through Playwright rather than direct DB writes. ## Write rights granted to `ai_agent_sandbox` Read + create on each module (stable Dolibarr rights ids, verified against prod Dolibarr 22.0.4): | Module | rights ids | | ---------------- | ---------- | | facture | lire=11, creer=12 | | societe | lire=121, creer=122 | | societe contact | lire=281, creer=282 | | fournisseur | lire=1181, facture lire=1231, facture creer=1232 | | produit | lire=31, creer=32 | Granting is done by clicking the perm rows in the UI; the ids are used to target the `addrights` links (and documented as the SQL-fallback reference). ## Validation status — runtime check deferred - `deno check provisionSandbox.ts scripts/admin/userSetup.ts` **passes** (also re-checked `main.ts` + `moduleSetup.ts`); the repo uses `checkJs: true`. - **Not run end-to-end.** The sandbox Dolibarr is **not installed/provisioned yet** (empty DB, fresh install wizard — there is no UI to drive). Selector correctness is therefore **best-effort**, based on Dolibarr 22 conventions, and must be verified on the **first real run after the install wizard completes**. This is **not** a "tested" claim. ### Selectors / labels GUESSED (confirm on first real run, marked `GUESS:` in code) - REST API module **card title** — fr_FR `"API/Web services REST (serveur)"` (fuzzy `/API.*REST|REST.*API/i` fallback in place). - User create form field names — `login`, `lastname`, `password`, and the `admin` checkbox (`input[name="admin"]`; some setups render it as a `<select>`). - New-user id source — `?id=` in the post-submit URL, fallback hidden `input[name="id"]`. - Perms grant control — `a[href*="action=addrights"][href*="rights=<id>"]`. - API key field + generate control — `input[name="api_key"]` plus one of `#generate_api_key` / `a[href*="action=generateapikey"]` / `span.fa-dice` / `a.fa-refresh`. ## Secrets hygiene The generated API key is written only to the gitignored `test/.ai_agent_sandbox.key` and never printed; admin/DB passwords come from env and are never echoed. Staged diff was grep-scanned — no secret values committed. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
arcodange added 1 commit 2026-06-29 07:35:06 +02:00
Extend the Deno + Playwright UI-automation POC to provision the erp-sandbox
Dolibarr for the AI agent:

- moduleSetup.ts: add enableApiModule(ctx) — toggles the REST API / Web services
  module on /admin/modules.php (kanban). Resilient: tries the fr_FR card label
  "API/Web services REST (serveur)" first, falls back to a /API.*REST|REST.*API/i
  title match if the exact label is absent.
- userSetup.ts (new): createUser (returns the new numeric id), assignRights
  (clicks each addrights link on /user/perms.php, idempotent), generateApiKey
  (triggers Dolibarr's generate control on the user card and reads the value back).
- provisionSandbox.ts (new entrypoint, main.ts untouched): login → enable API →
  create ai_agent_sandbox (non-admin) → grant write rights → generate API key,
  then write the key to test/.ai_agent_sandbox.key (gitignored) instead of
  printing it.
- .gitignore (new), .env.example + README.md: sandbox vars, the
  deno run --allow-all provisionSandbox.ts command, and kubectl one-liners to
  pull DOLI_ADMIN_PASSWORD (secretkv) / DOLI_DB_PASSWORD (vso-db-credentials)
  from the erp-sandbox namespace.

Why UI not SQL: API keys are encrypted with the instance's DOLI_INSTANCE_UNIQUE_ID,
so the key must be generated by the sandbox itself, not INSERTed raw.

deno check passes for provisionSandbox.ts and scripts/admin/userSetup.ts.
NOT run end-to-end: the sandbox Dolibarr is not installed yet (empty DB / fresh
install wizard), so the selectors are best-effort Dolibarr 22 conventions and
must be confirmed on the first real run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
arcodange merged commit fbc0cc6962 into main 2026-06-29 07:43:34 +02:00
arcodange deleted branch claude/poc-provision-sandbox 2026-06-29 07:43:34 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: arcodange-org/erp#14