feat(sandbox): scoped agents provisioned by the checkpoint cycle #82

Merged
arcodange merged 1 commits from arcodange/prod-apply into main 2026-08-09 19:42:06 +02:00
3 changed files with 26 additions and 2 deletions
Showing only changes of commit 2e699e1fc6 - Show all commits
@@ -19,5 +19,18 @@ command -v deno >/dev/null || { echo "checkpoint-provision: deno not found (http
echo ">>> launching provisionSandbox.ts — complete the admin login in the browser (use PROD admin creds)"
( cd "${ROOT}/test" && deno run --allow-all provisionSandbox.ts )
# Scoped agents (test/scopes.ts). A refresh wipes every user, so they are
# re-created here rather than granted by hand — the 2026-07 failure where a
# refresh silently dropped hand-granted rights and nothing recorded it.
if [[ -f "${ROOT}/test/provisionAiUser.ts" ]]; then
for scope in read sandbox-write; do
echo ">>> provisioning scoped sandbox agent: ${scope}"
( cd "${ROOT}/test" \
&& DOLIBARR_ADDRESS="${DOLIBARR_SANDBOX_URL:-https://erp-sandbox.arcodange.lab}" \
deno run --allow-all provisionAiUser.ts --scope "${scope}" --env sandbox ) \
|| echo " WARN: scoped provisioning failed for '${scope}' — check test/scopes.ts" >&2
done
fi
echo ">>> provisioning finished; relinking the write skill .env"
exec "${SCRIPT_DIR}/checkpoint-relink-env.sh"
@@ -7,7 +7,11 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="${ARCO_ROOT:-$(cd "${SCRIPT_DIR}/../../../.." && pwd)}"
SB_URL="${DOLIBARR_SANDBOX_URL:-https://erp-sandbox.arcodange.lab}"
KEY="${ROOT}/test/.ai_agent_sandbox.key"
# Prefer the SCOPED writer (test/scopes.ts, `sandbox-write`); fall back to the
# legacy single-user key so an older checkout keeps working. The scoped user is
# what a refresh re-provisions, which is why it wins.
KEY="${ROOT}/test/.ai_agent_sandbox_sandbox_write.key"
[[ -s "${KEY}" ]] || KEY="${ROOT}/test/.ai_agent_sandbox.key"
ENV="${ROOT}/.claude/skills/dolibarr-sandbox-write/.env"
DOLW="${ROOT}/.claude/skills/dolibarr-sandbox-write/scripts/dol-write.sh"
+8 -1
View File
@@ -160,7 +160,14 @@ export const SCOPES: Readonly<Record<string, Scope>> = {
export type ScopeName = keyof typeof SCOPES;
/** Conventional login for a given scope + environment. One user per pair. */
/**
* Conventional login for a given scope + environment. One user per pair.
*
* The scope name is kept verbatim, so `prod-write` on production reads
* `ai_agent_prod_prod_write`. Redundant, deliberately left alone: renaming a
* provisioned production credential means creating a second privileged user and
* repointing the promote flow — churn for cosmetics. Don't "fix" it.
*/
export function loginFor(scope: string, env: "sandbox" | "production"): string {
return env === "sandbox" ? `ai_agent_sandbox_${scope.replace(/-/g, "_")}`
: `ai_agent_prod_${scope.replace(/-/g, "_")}`;