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:
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).
Create a write-scoped ai_agent_sandbox user (non-admin) — userSetup.tscreateUser.
Grant write rights — userSetup.tsassignRights.
Generate the user's API key — userSetup.tsgenerateApiKey, 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/.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).
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.
## 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)
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) <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What this adds
Extends the existing Deno + Playwright UI-automation POC (
test/) to provision theerp-sandboxDolibarr so the AI agent can write to it. New entrypointprovisionSandbox.tsruns end-to-end:moduleSetup.tsgainsenableApiModule(ctx)(activation maps tollx_const.MAIN_MODULE_API=1; UI toggle on/admin/modules.php?mode=commonkanban).ai_agent_sandboxuser (non-admin) —userSetup.tscreateUser.userSetup.tsassignRights.userSetup.tsgenerateApiKey, then emit it safely.New / changed files
test/scripts/admin/moduleSetup.ts— addenableApiModule(ctx). Resilient by design: tries the fr_FR card label"API/Web services REST (serveur)"(anchored exact match, same path asconfigureModule); 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=createviaforms.fillForm, toggles theadmincheckbox explicitly, submits, and returns the new numeric id (parsed from the resulting?id=URL, falling back to a hiddeninput[name="id"]).assignRights(ctx, userId, rightIds[])→ for each right, navigates/user/perms.php?id=<userId>and clicks thea[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 ofinput[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.tsuntouched) — buildsglobalCtxfrom env (defaultDOLIBARR_ADDRESS=https://erp-sandbox.arcodange.lab), runs the full flow, then writes the key totest/.ai_agent_sandbox.key(gitignored) with the console lineAI_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 / Vaultkvv2/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.gitignorealready 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 sandboxsection with the run command and the kubectl one-liners for theerp-sandboxnamespace.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, neverINSERTed raw. Passwords likewise useMAIN_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_sandboxRead + create on each module (stable Dolibarr rights ids, verified against prod Dolibarr 22.0.4):
Granting is done by clicking the perm rows in the UI; the ids are used to target the
addrightslinks (and documented as the SQL-fallback reference).Validation status — runtime check deferred
deno check provisionSandbox.ts scripts/admin/userSetup.tspasses (also re-checkedmain.ts+moduleSetup.ts); the repo usescheckJs: true.Selectors / labels GUESSED (confirm on first real run, marked
GUESS:in code)"API/Web services REST (serveur)"(fuzzy/API.*REST|REST.*API/ifallback in place).login,lastname,password, and theadmincheckbox (input[name="admin"]; some setups render it as a<select>).?id=in the post-submit URL, fallback hiddeninput[name="id"].a[href*="action=addrights"][href*="rights=<id>"].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.keyand 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