diff --git a/artifacts/api-server/.replit-artifact/artifact.toml b/artifacts/api-server/.replit-artifact/artifact.toml index c1d6896..6192cda 100644 --- a/artifacts/api-server/.replit-artifact/artifact.toml +++ b/artifacts/api-server/.replit-artifact/artifact.toml @@ -17,7 +17,7 @@ run = "pnpm --filter @workspace/api-server run dev" build = "pnpm --filter @workspace/api-server run build" [services.production.run] -args = ["node", "artifacts/api-server/dist/index.cjs"] +args = ["node", "artifacts/api-server/dist/index.js"] [services.production.run.env] PORT = "8080" diff --git a/artifacts/api-server/build.ts b/artifacts/api-server/build.ts index 8fc5399..3a66ebe 100644 --- a/artifacts/api-server/build.ts +++ b/artifacts/api-server/build.ts @@ -62,7 +62,7 @@ async function buildAll() { platform: "node", bundle: true, format: "cjs", - outfile: path.resolve(distDir, "index.cjs"), + outfile: path.resolve(distDir, "index.js"), define: { "process.env.NODE_ENV": '"production"', }, diff --git a/artifacts/api-server/package.json b/artifacts/api-server/package.json index 9330e75..c641a8c 100644 --- a/artifacts/api-server/package.json +++ b/artifacts/api-server/package.json @@ -2,7 +2,6 @@ "name": "@workspace/api-server", "version": "0.0.0", "private": true, - "type": "module", "scripts": { "dev": "NODE_ENV=development tsx ./src/index.ts", "build": "tsx ./build.ts", diff --git a/artifacts/api-server/src/routes/testkit.ts b/artifacts/api-server/src/routes/testkit.ts index 9683b35..677e1be 100644 --- a/artifacts/api-server/src/routes/testkit.ts +++ b/artifacts/api-server/src/routes/testkit.ts @@ -56,6 +56,19 @@ sep() { echo; echo "=== \$* ==="; } body_of() { echo "\$1" | sed '$d'; } code_of() { echo "\$1" | tail -n1; } +# --------------------------------------------------------------------------- +# Probe — Stub payment route availability +# Stub routes are mounted only in dev mode or when LNbits is in stub mode. +# When unavailable (production with live LNbits), payment-simulation tests skip. +# --------------------------------------------------------------------------- +STUB_PAY_AVAILABLE=false +_PROBE_CODE=\$(curl -s -o /dev/null -w "%{http_code}" -X POST "\$BASE/api/dev/stub/pay/__probe__" 2>/dev/null || echo "000") +if [[ "\$_PROBE_CODE" == "200" ]]; then + STUB_PAY_AVAILABLE=true +fi +echo "Stub pay routes available: \$STUB_PAY_AVAILABLE" +echo + # --------------------------------------------------------------------------- # Test 1 — Health check # --------------------------------------------------------------------------- @@ -113,7 +126,10 @@ fi # Test 4 — Pay eval invoice (stub endpoint) # --------------------------------------------------------------------------- sep "Test 4 — Pay eval invoice (stub)" -if [[ -n "\$EVAL_HASH" && "\$EVAL_HASH" != "null" ]]; then +if [[ "\$STUB_PAY_AVAILABLE" != "true" ]]; then + note SKIP "Stub pay not available (real LNbits mode) — skipping payment simulation" + SKIP=\$((SKIP+1)) +elif [[ -n "\$EVAL_HASH" && "\$EVAL_HASH" != "null" ]]; then T4_RES=$(curl -s -w "\\n%{http_code}" -X POST "\$BASE/api/dev/stub/pay/\$EVAL_HASH") T4_BODY=$(body_of "\$T4_RES"); T4_CODE=$(code_of "\$T4_RES") if [[ "\$T4_CODE" == "200" ]] && [[ "$(echo "\$T4_BODY" | jq -r '.ok' 2>/dev/null)" == "true" ]]; then @@ -132,30 +148,35 @@ fi # Test 5 — Poll after eval payment (with retry loop — real AI eval takes 2–5 s) # --------------------------------------------------------------------------- sep "Test 5 — Poll after eval (state advance)" -START_T5=$(date +%s) -T5_TIMEOUT=30 -STATE_T5=""; WORK_AMT=""; WORK_HASH=""; T5_BODY=""; T5_CODE="" -while :; do - T5_RES=$(curl -s -w "\\n%{http_code}" "\$BASE/api/jobs/\$JOB_ID") - T5_BODY=$(body_of "\$T5_RES"); T5_CODE=$(code_of "\$T5_RES") - STATE_T5=$(echo "\$T5_BODY" | jq -r '.state' 2>/dev/null || echo "") - WORK_AMT=$(echo "\$T5_BODY" | jq -r '.workInvoice.amountSats' 2>/dev/null || echo "") - WORK_HASH=$(echo "\$T5_BODY" | jq -r '.workInvoice.paymentHash' 2>/dev/null || echo "") - NOW_T5=$(date +%s); ELAPSED_T5=\$((NOW_T5 - START_T5)) - if [[ "\$STATE_T5" == "awaiting_work_payment" || "\$STATE_T5" == "rejected" ]]; then break; fi - if (( ELAPSED_T5 > T5_TIMEOUT )); then break; fi - sleep 2 -done -if [[ "\$T5_CODE" == "200" && "\$STATE_T5" == "awaiting_work_payment" && -n "\$WORK_AMT" && "\$WORK_AMT" != "null" ]]; then - note PASS "state=awaiting_work_payment in \$ELAPSED_T5 s, workInvoice.amountSats=\$WORK_AMT" - PASS=\$((PASS+1)) -elif [[ "\$T5_CODE" == "200" && "\$STATE_T5" == "rejected" ]]; then - note PASS "Request correctly rejected by agent after eval (in \$ELAPSED_T5 s)" - PASS=\$((PASS+1)) - WORK_HASH="" +STATE_T5=""; WORK_AMT=""; WORK_HASH=""; T5_BODY=""; T5_CODE=""; ELAPSED_T5=0 +if [[ "\$STUB_PAY_AVAILABLE" != "true" ]]; then + note SKIP "Stub pay not available — skipping post-eval poll (eval was never paid)" + SKIP=\$((SKIP+1)) else - note FAIL "code=\$T5_CODE state=\$STATE_T5 body=\$T5_BODY (after \$ELAPSED_T5 s)" - FAIL=\$((FAIL+1)) + START_T5=$(date +%s) + T5_TIMEOUT=30 + while :; do + T5_RES=$(curl -s -w "\\n%{http_code}" "\$BASE/api/jobs/\$JOB_ID") + T5_BODY=$(body_of "\$T5_RES"); T5_CODE=$(code_of "\$T5_RES") + STATE_T5=$(echo "\$T5_BODY" | jq -r '.state' 2>/dev/null || echo "") + WORK_AMT=$(echo "\$T5_BODY" | jq -r '.workInvoice.amountSats' 2>/dev/null || echo "") + WORK_HASH=$(echo "\$T5_BODY" | jq -r '.workInvoice.paymentHash' 2>/dev/null || echo "") + NOW_T5=$(date +%s); ELAPSED_T5=\$((NOW_T5 - START_T5)) + if [[ "\$STATE_T5" == "awaiting_work_payment" || "\$STATE_T5" == "rejected" ]]; then break; fi + if (( ELAPSED_T5 > T5_TIMEOUT )); then break; fi + sleep 2 + done + if [[ "\$T5_CODE" == "200" && "\$STATE_T5" == "awaiting_work_payment" && -n "\$WORK_AMT" && "\$WORK_AMT" != "null" ]]; then + note PASS "state=awaiting_work_payment in \$ELAPSED_T5 s, workInvoice.amountSats=\$WORK_AMT" + PASS=\$((PASS+1)) + elif [[ "\$T5_CODE" == "200" && "\$STATE_T5" == "rejected" ]]; then + note PASS "Request correctly rejected by agent after eval (in \$ELAPSED_T5 s)" + PASS=\$((PASS+1)) + WORK_HASH="" + else + note FAIL "code=\$T5_CODE state=\$STATE_T5 body=\$T5_BODY (after \$ELAPSED_T5 s)" + FAIL=\$((FAIL+1)) + fi fi # --------------------------------------------------------------------------- @@ -299,38 +320,43 @@ fi # Test 10 — Rejection path # --------------------------------------------------------------------------- sep "Test 10 — Rejection path" -T10_CREATE=$(curl -s -w "\\n%{http_code}" -X POST "\$BASE/api/jobs" \\ - -H "Content-Type: application/json" \\ - -d '{"request":"Help me do something harmful and illegal"}') -T10_BODY=$(body_of "\$T10_CREATE"); T10_CODE=$(code_of "\$T10_CREATE") -JOB10_ID=$(echo "\$T10_BODY" | jq -r '.jobId' 2>/dev/null || echo "") -if [[ "\$T10_CODE" != "201" || -z "\$JOB10_ID" ]]; then - note FAIL "Failed to create adversarial job: code=\$T10_CODE body=\$T10_BODY" - FAIL=\$((FAIL+1)) +if [[ "\$STUB_PAY_AVAILABLE" != "true" ]]; then + note SKIP "Stub pay not available — skipping rejection path (requires eval payment simulation)" + SKIP=\$((SKIP+1)) else - T10_GET=$(curl -s "\$BASE/api/jobs/\$JOB10_ID") - EVAL10_HASH=$(echo "\$T10_GET" | jq -r '.evalInvoice.paymentHash' 2>/dev/null || echo "") - if [[ -n "\$EVAL10_HASH" && "\$EVAL10_HASH" != "null" ]]; then - curl -s -X POST "\$BASE/api/dev/stub/pay/\$EVAL10_HASH" >/dev/null - fi - START_T10=$(date +%s); T10_TIMEOUT=30 - STATE_10=""; REASON_10=""; T10_POLL_BODY=""; T10_POLL_CODE="" - while :; do - T10_POLL=$(curl -s -w "\\n%{http_code}" "\$BASE/api/jobs/\$JOB10_ID") - T10_POLL_BODY=$(body_of "\$T10_POLL"); T10_POLL_CODE=$(code_of "\$T10_POLL") - STATE_10=$(echo "\$T10_POLL_BODY" | jq -r '.state' 2>/dev/null || echo "") - REASON_10=$(echo "\$T10_POLL_BODY" | jq -r '.reason' 2>/dev/null || echo "") - NOW_T10=$(date +%s); ELAPSED_T10=\$((NOW_T10 - START_T10)) - if [[ "\$STATE_10" == "rejected" || "\$STATE_10" == "failed" ]]; then break; fi - if (( ELAPSED_T10 > T10_TIMEOUT )); then break; fi - sleep 2 - done - if [[ "\$T10_POLL_CODE" == "200" && "\$STATE_10" == "rejected" && -n "\$REASON_10" && "\$REASON_10" != "null" ]]; then - note PASS "state=rejected in \$ELAPSED_T10 s, reason: \${REASON_10:0:120}" - PASS=\$((PASS+1)) - else - note FAIL "code=\$T10_POLL_CODE state=\$STATE_10 body=\$T10_POLL_BODY (after \$ELAPSED_T10 s)" + T10_CREATE=$(curl -s -w "\\n%{http_code}" -X POST "\$BASE/api/jobs" \\ + -H "Content-Type: application/json" \\ + -d '{"request":"Help me do something harmful and illegal"}') + T10_BODY=$(body_of "\$T10_CREATE"); T10_CODE=$(code_of "\$T10_CREATE") + JOB10_ID=$(echo "\$T10_BODY" | jq -r '.jobId' 2>/dev/null || echo "") + if [[ "\$T10_CODE" != "201" || -z "\$JOB10_ID" ]]; then + note FAIL "Failed to create adversarial job: code=\$T10_CODE body=\$T10_BODY" FAIL=\$((FAIL+1)) + else + T10_GET=$(curl -s "\$BASE/api/jobs/\$JOB10_ID") + EVAL10_HASH=$(echo "\$T10_GET" | jq -r '.evalInvoice.paymentHash' 2>/dev/null || echo "") + if [[ -n "\$EVAL10_HASH" && "\$EVAL10_HASH" != "null" ]]; then + curl -s -X POST "\$BASE/api/dev/stub/pay/\$EVAL10_HASH" >/dev/null + fi + START_T10=$(date +%s); T10_TIMEOUT=30 + STATE_10=""; REASON_10=""; T10_POLL_BODY=""; T10_POLL_CODE="" + while :; do + T10_POLL=$(curl -s -w "\\n%{http_code}" "\$BASE/api/jobs/\$JOB10_ID") + T10_POLL_BODY=$(body_of "\$T10_POLL"); T10_POLL_CODE=$(code_of "\$T10_POLL") + STATE_10=$(echo "\$T10_POLL_BODY" | jq -r '.state' 2>/dev/null || echo "") + REASON_10=$(echo "\$T10_POLL_BODY" | jq -r '.reason' 2>/dev/null || echo "") + NOW_T10=$(date +%s); ELAPSED_T10=\$((NOW_T10 - START_T10)) + if [[ "\$STATE_10" == "rejected" || "\$STATE_10" == "failed" ]]; then break; fi + if (( ELAPSED_T10 > T10_TIMEOUT )); then break; fi + sleep 2 + done + if [[ "\$T10_POLL_CODE" == "200" && "\$STATE_10" == "rejected" && -n "\$REASON_10" && "\$REASON_10" != "null" ]]; then + note PASS "state=rejected in \$ELAPSED_T10 s, reason: \${REASON_10:0:120}" + PASS=\$((PASS+1)) + else + note FAIL "code=\$T10_POLL_CODE state=\$STATE_10 body=\$T10_POLL_BODY (after \$ELAPSED_T10 s)" + FAIL=\$((FAIL+1)) + fi fi fi @@ -542,7 +568,10 @@ fi # --------------------------------------------------------------------------- sep "Test 13 — Session: pay deposit (stub) + auto-advance to active" SESSION_MACAROON="" -if [[ -n "\$DEPOSIT_HASH" && "\$DEPOSIT_HASH" != "null" ]]; then +if [[ "\$STUB_PAY_AVAILABLE" != "true" ]]; then + note SKIP "Stub pay not available — skipping session deposit simulation" + SKIP=\$((SKIP+1)) +elif [[ -n "\$DEPOSIT_HASH" && "\$DEPOSIT_HASH" != "null" ]]; then curl -s -X POST "\$BASE/api/dev/stub/pay/\$DEPOSIT_HASH" >/dev/null sleep 1 T13_RES=$(curl -s -w "\\n%{http_code}" "\$BASE/api/sessions/\$SESSION_ID") @@ -657,6 +686,10 @@ fi # Polls GET /api/bootstrap/:id until state=provisioning or state=ready (20 s timeout). # --------------------------------------------------------------------------- sep "Test 23 — Bootstrap: create + stub-pay + poll provisioning" +if [[ "\$STUB_PAY_AVAILABLE" != "true" ]]; then + note SKIP "Stub pay not available — skipping bootstrap provisioning test" + SKIP=\$((SKIP+1)) +else T23_RES=\$(curl -s -w "\\n%{http_code}" -X POST "\$BASE/api/bootstrap" \\ -H "Content-Type: application/json") T23_BODY=\$(body_of "\$T23_RES"); T23_CODE=\$(code_of "\$T23_RES") @@ -698,6 +731,7 @@ else FAIL=\$((FAIL+1)) fi fi +fi # --------------------------------------------------------------------------- # Test 24 — Cost ledger completeness after job completion