feat: CAP memory estimation with a confirm step before install (v3.5.8)

Selecting a .cap in the RAM installer or the SCP81 "Install from .cap"
template now runs a read-only analysis (POST /api/cap-info) before any
APDU is built: the archive is validated structurally (a corrupt or
wrong-format file fails here) and the bundled capmem analyzer estimates
the code size and the persistent (NVRAM) / volatile (RAM) requirements,
with the tool's suggested C6/C7/C8 quotas shown as information.  The
form's action button (Execute / Generate) stays disabled until the
analysis succeeds - pressing it is the user's confirmation to continue.

- pysim_simple_server/capmem.py: bundled analyzer (component parsers +
  JCVM opcode table + method-bytecode allocation scan), adapted to take
  the CAP archive as bytes and return report/memory dicts; output
  verified byte-identical to the workspace tool on 21 real CAPs.
- _cap_info_body + POST /api/cap-info (read-only; the install endpoints
  stay unchanged and self-sufficient).
- PWA: shared capAnalyzeFile/capMemHtml/capGateOk helpers, estimate box
  under both CAP inputs (reusing the idle #ram-cap-info div, new
  #scripts-cap-info), data-cap-gate gating in pysimApplyAvailability,
  stale-response guard, Retry, EN/RU strings.
- Tests: tests/test_cap_memory.py (synthetic CAPs: new/newarray/
  makeTransientByteArray/static fields/unknown-opcode warnings/corrupt
  input), frontend capmem.test.js (renderer, gate, analyze flow).
- help EN/RU, docs/api.md, AGENTS; version 3.5.8; sw cache simple-v261.

567 frontend / 429 Python green.
This commit is contained in:
2026-09-26 08:50:46 +03:00
parent b88f04fc69
commit d9e6c6c9dd
10 changed files with 2461 additions and 13 deletions
+43
View File
@@ -38,6 +38,7 @@ a 3.x PWA).
| `/api/help` | POST | pySim help for a given command |
| `/api/send-ota` | POST | SCP80 OTA secured packet delivery |
| `/api/ram-install` | POST | Install a Java Card `.cap` file via SCP80 (INSTALL[for load] → LOAD ×N → INSTALL[for install]) |
| `/api/cap-info` | POST | Validate a `.cap` archive and estimate its code/NVRAM/RAM requirements (read-only) |
| `/api/sp-verify` | POST | Verify secured packet against pySim reference |
| `/api/menu` | GET | Current STK menu (title + items + active) |
| `/api/menu-select` | POST | ENVELOPE(Menu Selection) with item_id |
@@ -319,6 +320,48 @@ same `por` structure if decoding succeeds.
The SPI2 `por_in_submit` bit (0x20) selects submit-mode PoR.
### `POST /api/cap-info`
Validate a Java Card `.cap` archive and estimate its memory requirements.
Read-only: it runs the same structural parse as the install paths (so a
corrupt or wrong-format file fails here first) plus the bundled CAP analyzer
(`pysim_simple_server/capmem.py` — component parsers and a method-bytecode
allocation scan). It never touches the card, the SCP81 listener or the
scripts; the install endpoints stay self-sufficient and the estimate is not
used for installation.
```json
{"cap_hex": "504B0304..."}
```
**Response (ok):**
```json
{
"ok": true,
"load_file_aid": "AA1902BC226001", "module_aid": "AA1902BC226001",
"load_file_bytes": 1234,
"memory": {
"package_aid": "AA1902BC226001",
"applet_count": 1, "applets": ["AA1902BC226001"],
"class_count": 3, "method_count": 12,
"code": {"method_component": 850},
"nvram": {"static_image": 12, "array_init": 4, "install_objects": 100,
"header_overhead": 24, "ref_storage": 8, "total": 148, "runtime": 0},
"ram": {"transient_arrays": 16, "runtime_transient": 0, "peak_frame": 8, "total": 24},
"suggested": {"c6": 850, "c7": 272, "c8": 148},
"warnings": []
}
}
```
**Errors** (HTTP 200 with `ok: false`, like `/api/scp81/gen-install`):
`{"ok": false, "error": "cap parse failed: File is not a zip file"}` for a
corrupt/wrong archive, or `cap analysis failed: …` when a component passes
the structural parse but not the analyzer. The numbers are an estimate:
the model assumes 2-byte references, a 6-byte object header and NVM cell
rounding, and does not include applet-created runtime objects/arrays.
### `POST /api/ram-install`
Install a Java Card `.cap` file on the card via GlobalPlatform commands (INSTALL[for load] → LOAD ×N → INSTALL[for install (+ make selectable)]) wrapped in SCP80 secured packets. Each step is sent via ENVELOPE and its PoR is checked; the sequence aborts on the first PoR error. The `.cap` archive (a ZIP of nested components) is parsed server-side in `_cap_parse`; no external tooling is required.