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
+35 -1
View File
@@ -19,6 +19,7 @@ from pysim_simple_server import netsim
from pysim_simple_server import netstate
from pysim_simple_server import scp81
from pysim_simple_server import esim
from pysim_simple_server import capmem
from smartcard.CardMonitoring import CardMonitor, CardObserver
from cmd2.exceptions import CommandSetRegistrationError
@@ -29,7 +30,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad
VERSION = '3.5.7'
VERSION = '3.5.8'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE
@@ -2412,6 +2413,30 @@ def _scp81_gen_install(body):
'module_aid': module_aid}
def _cap_info_body(body):
"""Validate a .cap archive and estimate its memory requirements.
Read-only: runs the same structural parse as the install paths (so a
corrupt/wrong file fails here first) plus the capmem analyzer. Never
touches the card, the SCP81 listener or the scripts; the estimate is
informational - the install paths stay self-sufficient."""
body = body or {}
cap_hex = re.sub(r'\s', '', body.get('cap_hex') or '')
if not cap_hex:
return {'ok': False, 'error': 'No cap_hex provided'}
try:
loadfile_aid, module_aid, loadfile_data = _cap_parse(cap_hex)
except Exception as e:
return {'ok': False, 'error': 'cap parse failed: %s' % e}
try:
report, memory = capmem.analyze_bytes(bytes.fromhex(cap_hex))
info = capmem.memory_json(report, memory)
except Exception as e:
return {'ok': False, 'error': 'cap analysis failed: %s' % e}
return {'ok': True, 'load_file_aid': loadfile_aid, 'module_aid': module_aid,
'load_file_bytes': len(loadfile_data) // 2, 'memory': info}
def _scp81_bip_control(body):
global _SCP81_LISTENER, _SCP81_PSKS, _SCP81_PSK_LEGACY
global _SCP81_MODE, _SCP81_TARGET
@@ -5285,6 +5310,15 @@ class PysimHandler(BaseHTTPRequestHandler):
resp = {'ok': False, 'error': str(e)}
self._send_json(resp)
self._log_resp(resp)
elif self.path == '/api/cap-info':
body = self._read_body()
self._log_req(body)
try:
resp = _cap_info_body(body)
except Exception as e:
resp = {'ok': False, 'error': str(e)}
self._send_json(resp)
self._log_resp(resp)
elif self.path == '/api/scp81/log-clear':
body = self._read_body()
self._log_req(body)