Commit Graph

6 Commits

Author SHA1 Message Date
catarrh 795a1fea49 feat: card presets move to a server-side store; the server owns the SCP80 counter (v3.8.0)
The presets lived in the browser's localStorage and the PWA wrote the SCP80
counter back after every operation, so an increment could be lost when the
response never reached the tab, when two browser contexts (or the demo origin
plus a local one) held separate copies, or when the tab was closed mid-run.
The counter is card state and the server performs every card operation, so the
store now lives next to the server:

- pysim_simple_server/presets.py: one JSON file
  (~/.pysim-simple-server/card_presets.json, --card-presets overrides; Path.home()
  resolves the same way on Windows), atomic writes (temp + fsync + os.replace),
  an RLock, stable uuid ids, the ICCID normalisation the PWA uses (digits /
  spaced / raw EF hex), a duplicate-ICCID refusal and a monotonic set_counter;
  every counter change is appended to card_presets.json.audit.jsonl.
- API: GET /api/presets (card-free), POST /api/presets and
  /api/presets/update|delete|import; the import also takes the old localStorage
  export shape - that is the migration path, there is no automatic migration.
- Operations name a preset_id and the server persists the counter they
  consumed: /api/send-ota (the plain SCP80 path reports final_cntr now too),
  the RAM install/install-app/compat steps (_ram_send_gp_apdu) and the test
  runner's SCP80 steps (_preset_counter_persist).
- PWA: the Cards tab is a thin client of the store (load on connect, CRUD via
  the API, export/import), the counter write-backs became local display syncs
  (the SCP80 form's counter input stays a deliberate human edit) and the
  SCP80/RAM bodies carry preset_id; a new pysimFetchOk() surfaces the server's
  error bodies instead of silently using them.

Policy: card keys in logs and local files are not a risk for this workbench
tool (AGENTS.md) - the store is not encrypted and no redaction is required.

Tests: tests/test_presets.py (store: ICCID forms, counter monotonicity and
wrap, atomic writes, corrupt-file recovery, import), the preset endpoints in
tests/test_fetch_routes.py (incl. the counter persistence via /api/send-ota)
and the updated PWA tests.  sw cache simple-v334.
2026-09-30 13:59:47 +03:00
catarrh 18a36a8f27 feat: generic BIP terminal control (Simulator BIP pill) (v3.6.0)
BIP was only reachable through the SCP81 listener; cards that use TCP for
other purposes (an applet's OPEN CHANNEL, a push trigger without HTTP OTA)
now get a generic control surface, independent of SCP81.

Server:
- /api/bip/control starts a plain BIP session in three modes: sink
  (default; a local TcpDumpServer that accepts the card's channels and only
  logs conn/sink-rx/conn-close, never answering; port 0 = ephemeral, the
  bound port is reported), passthru (dial the OPEN CHANNEL destination, TCP
  client only) and redirect (fixed host:port).
- /api/bip/status returns {owner, bip, listener}; /api/bip/log and
  /api/bip/log-clear share the BIP event log with /api/scp81/log.
- The session state is shared with the SCP81 listener and only one session
  runs at a time: _bip_session_stop() stops whichever control started it
  and the control responses report it as `replaced` (both directions,
  SCP81 <-> BIP).  State renamed _SCP81_MODE/_TARGET/_LISTENER/_LINK_EVENTS
  -> _BIP_* plus _BIP_OWNER.
- TcpDumpServer logs conn-close and counts accepted connections; stop()
  joins the accept thread so the port is really free on restart.
- The new control endpoints are blocked during test-script runs.

PWA:
- New Simulator pill BIP (after TR Config): mode select with notes,
  Host/Port rules, Start/Stop with a "replaced" notice, status line (mode,
  bound address, owner, channels), a Channels box and the shared BIP log
  (reuses the SCP81 log renderer, now showing `peer`).
- The SCP81 status line marks a session owned by the BIP pill.

Help EN/RU 8.10, docs/api.md, AGENTS; CAT_TP/UDP recorded as not
implemented.

Tests: tests/test_bip.py (9) and frontend/tests/bip.test.js (6).
600 frontend / 482 Python green; version 3.6.0; sw simple-v273.
2026-09-27 15:28:23 +03:00
catarrh 2d2a40a73c fix: follow-up test-script review findings (v3.5.14)
- The SCP80 counter write-back resolves the preset by NAME first (the run
  snapshot prefers the name) and only falls back to an ICCID-looking
  value: presets with digits in their names are found after a page reload
  (the previous fallback ran cardsNormIccid on the name and gave up).
- testScriptProblem validates the *selected* SCP80 source (like the form
  and the server), so source=apdu with only sp filled is caught locally
  instead of failing with a server 400.
- _test_run_start cleans up (_TEST_RUNNING=False, run state error) when
  the worker thread cannot be created/started, and the endpoint answers
  500 with a clear error instead of leaving the card blocked behind a run
  that never started.
- The 5 s poll writes the counter back whenever a run is finished
  (idempotent), so a reloaded page saves it without visiting the pill.
- Mask wildcards ('?') are stripped from the hex fields that cannot carry
  a mask (APDU, secured packet, event/file data, TAR/SPI overrides, DCS,
  extra TLVs); check values keep them.

Tests: frontend +3 (write-back by name / by ICCID, mask-free fields,
source-aware script check), Python +1 (failed thread start unblocks).
586 frontend / 471 Python green; version 3.5.14; sw simple-v267.
2026-09-27 01:52:03 +03:00
catarrh d1eb88d4d7 fix: test-script review findings (v3.5.13)
PWA:
- The SCP80 "Source" switch sticks: the choice is stored in
  `params.source`, switching keeps both values (the server honors the
  explicit source when both are present, and the form validates only the
  selected one).
- The SW check field is empty by default (placeholder "default: 9000
  (91?? when polling)"), so the server defaults apply - previously the
  pre-filled 9000 defeated the polling STATUS default and a card that
  announced a command made the step fail with "expected 9000".
- Item text checks offer contains/exact only (the mask mode was rejected
  by the server validation).
- The SCP80 counter is written back to the preset even when the run is
  observed after a page reload or was started elsewhere (resolved by
  ICCID or preset name from the run snapshot).

Server:
- Step-entry mutations happen under `_TEST_LOCK`
  (`_test_entry_update`/`_test_finish_entry`): the status endpoint
  serializes the state with json.dumps, so entries must not change while
  it iterates them.
- The pending-command drains (unexpected command, error, stop) hold
  `_CARD_LOCK` like every other card conversation.
- `_int` accepts plain decimals with leading zeros and 0x hex.
- The scripted TERMINAL RESPONSE text string uses the CR-set tag `8D`
  (consistent with the other TR TLVs; both are legal).
- A script-driven menu selection mirrors `server.menu_active`.

Tests: frontend +4 (source switch/round-trip, status SW default, render
checks, item modes), Python +3 (integer parsing, menu_active, source
selection).  Help/docs unaffected beyond api.md's `source` note.

583 frontend / 470 Python green.
2026-09-27 01:48:12 +03:00
catarrh d1c6a9c27a ui: rename the Phone simulator tab to Simulator and the eSIM pill to eSIM LPA (v3.5.12)
- Top-level tab label "Phone simulator" -> "Simulator" (RU "Симулятор");
  the DOM ids stay tab-phone / phone-sub-*.
- The eSIM pill inside the Simulator tab is now "eSIM LPA" (EN/RU),
  matching the section's ES10/LPA scope; the panel id stays phone-sub-esim.
- Documentation updated: help EN/RU (section 8 Simulator, list of the four
  pills including Test script, tab lists and cross-references),
  README/README_RUS (tab lists, Simulator section, eSIM LPA), AGENTS
  repo/root.
- Test titles updated; version 3.5.12; sw cache simple-v265.

579 frontend / 467 Python green.
2026-09-27 01:33:22 +03:00
catarrh 722e237ea4 feat: Test script pill in the Phone simulator (v3.5.11)
- Phone simulator tab: a fourth pill **Test script** - script list
  (localStorage `simple_tests`) with New/Clone/Delete/Export/Import, a
  form-based step editor (modal) and a run panel.
- Actions: ENVELOPE (Event Download), Menu Selection, UPDATE/READ file by
  path, raw APDU, SCP80 (C-APDU or a pre-built packet, per-step TAR/SPI1/
  SPI2 overrides; the preset is matched by the equipped card's ICCID and
  the final counter is written back) and STATUS with an optional poll.
  Every action has SW/data checks (exact or ?-mask) and a PoR check for
  SCP80, with an error (stops) / warning (continues) fail level.
- Expectations: FETCH the command announced by the previous step, verify
  type/qualifier/text/item/raw and answer with the scripted TERMINAL
  RESPONSE (result, item id, text, extra TLVs); templates for STK menu
  browsing and an applet via SCP80.
- Run panel: progress banner (plus a marker on the pill), per-step
  OK/Warning/Error badges with expected vs actual values, sent/received
  hex, decoded commands and the SCP80 counter; 500 ms polling while a run
  is active, and a running script disables other card controls
  (data-needs gate; the server answers 409 to other card endpoints).
- Help EN/RU (8.9 Test script), AGENTS; version 3.5.11; sw simple-v264.

579 frontend / 467 Python green.
2026-09-27 01:19:32 +03:00