From fa600cb45d9a5140a3e7013df62ed0bb96d78cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD=20=D0=A2=D1=80=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Tue, 22 Sep 2026 22:07:57 +0300 Subject: [PATCH] ui: rename the SCP81 script templates to Explore ISD / Delete AID (v3.1.3) 'Explore' and 'Delete' were ambiguous next to the other SCP81 actions. - template selector: Explore -> Explore ISD, Delete -> Delete AID (EN/RU); - a new Explore script is named 'Explore ISD'; the Delete template keeps its count-based name (e.g. 'Delete 2 AIDs'); - new scriptKindLabel() maps the stored kinds (unchanged: explore/delete/install/empty, so existing localStorage scripts keep working) to those labels and is now used for the scripts table kind column, which used to print the raw lowercase kind untranslated; - docs: help EN/RU, README/RUS, docs/api.md examples, AGENTS; - sw.js simple-v238. --- README.md | 4 ++-- README_RUS.md | 4 ++-- docs/api.md | 4 ++-- frontend/help-ru.html | 4 ++-- frontend/help.html | 4 ++-- frontend/index.html | 23 +++++++++++++++++------ frontend/sw.js | 2 +- frontend/tests/scripts.test.js | 13 +++++++++++-- pyproject.toml | 2 +- pysim_simple_server/server.py | 2 +- 10 files changed, 41 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d36d1f7..cb3324f 100644 --- a/README.md +++ b/README.md @@ -624,9 +624,9 @@ The **Scripts** pill manages named APDU lists stored in `localStorage` (`simple_ | Template | What it builds | |---|---| | **Empty** | an empty list | -| **Explore** | the reference administration sequence (`GET DATA FF21`, GET STATUS ISD/ELF/application listings, `GET DATA 0085`); long listings auto-continue through `SW 6310/CAFE` pages | +| **Explore ISD** | the reference administration sequence (`GET DATA FF21`, GET STATUS ISD/ELF/application listings, `GET DATA 0085`); long listings auto-continue through `SW 6310/CAFE` pages | | **Install from .cap** | INSTALL [for load] → LOAD ×N → INSTALL [for install] from a `.cap` (optional SD AID, install/STK parameters, make selectable) via `POST /api/scp81/gen-install`; the file is only used to generate the APDUs — it is not stored, not even its name | -| **Delete** | DELETE APDUs from an AID list (one per line) with P2 = object only / object and related objects | +| **Delete AID** | DELETE APDUs from an AID list (one per line) with P2 = object only / object and related objects | The table lists each script with kind, APDU count and creation time; **Edit** opens the name + APDU editor and **Delete** removes it. Over a session the server serves one C-APDU per card POST and tracks execution: the card reports status in its next POST (`X-Admin-Script-Status`), a session that dies resends only the unexecuted APDUs (`X-Admin-Resume` continues, a fresh dialog restarts), and a completed script is closed with `204 No Content`. The Remote APDU → **RAM/GP** builder can feed a command chain straight into the run with **Queue in SCP81**. diff --git a/README_RUS.md b/README_RUS.md index 29a29e6..45a0a78 100644 --- a/README_RUS.md +++ b/README_RUS.md @@ -595,9 +595,9 @@ Delivery PoR (SPI2 `01`) проще — карта возвращает PoR на | Шаблон | Что строит | |---|---| | **Empty** | пустой список | -| **Explore** | эталонная административная последовательность (`GET DATA FF21`, листинги GET STATUS ISD/ELF/приложений, `GET DATA 0085`); длинные листинги автоматически продолжаются страницами `SW 6310/CAFE` | +| **Explore ISD** | эталонная административная последовательность (`GET DATA FF21`, листинги GET STATUS ISD/ELF/приложений, `GET DATA 0085`); длинные листинги автоматически продолжаются страницами `SW 6310/CAFE` | | **Install from .cap** | INSTALL [for load] → LOAD ×N → INSTALL [for install] из `.cap` (опционально SD AID, параметры install/STK, make selectable) через `POST /api/scp81/gen-install`; файл используется только для генерации APDU — он не сохраняется, как и его имя | -| **Delete** | APDU DELETE из списка AID (по одному в строке) с P2 = object only / object and related objects | +| **Delete AID** | APDU DELETE из списка AID (по одному в строке) с P2 = object only / object and related objects | Таблица показывает имя, тип, число APDU и время создания каждого скрипта; **Edit** открывает редактор имени и списка APDU, **Delete** удаляет скрипт. В ходе сессии сервер отдаёт по одному C-APDU на каждый POST карты и отслеживает выполнение: карта сообщает статус в следующем POST (`X-Admin-Script-Status`), оборвавшаяся сессия досылает только невыполненные APDU (`X-Admin-Resume` продолжает, новый диалог начинает заново), а завершённый скрипт закрывается ответом `204 No Content`. Конструктор RAM/GP вкладки Remote APDU может отправить цепочку команд прямо в прогон кнопкой **«В очередь SCP81»**. diff --git a/docs/api.md b/docs/api.md index 39ec2f0..5ff5de9 100644 --- a/docs/api.md +++ b/docs/api.md @@ -788,7 +788,7 @@ Keys are never stored or logged. {"action": "start", "mode": "tls", "host": "127.0.0.1", "port": 8443, "psk_map": [{"identity": "89012345678901234567", "psk_hex": "00112233445566778899aabbccddeeff"}], - "script": ["80CAFF2100", "80F28002024F0000"], "script_kind": "Explore"} + "script": ["80CAFF2100", "80F28002024F0000"], "script_kind": "Explore ISD"} ``` `script` is the APDU list served to the card (an explicit list, or `none`); @@ -873,7 +873,7 @@ Returns the configured command script and the execution state: ```json {"script": ["80CAFF2100", "80F28002024F0000"], "next": 2, "total": 2, - "done": [0, 1], "kind": "Explore", + "done": [0, 1], "kind": "Explore ISD", "pending": {"index": 17, "pos": null, "page": true, "apdu": "80F28003024F0000"}, "pages": 11, "pages_queued": 0, "complete": false, "results": [{"index": 1, "pos": 0, "page": false, "sw": "9000", diff --git a/frontend/help-ru.html b/frontend/help-ru.html index ac3dadb..089e818 100644 --- a/frontend/help-ru.html +++ b/frontend/help-ru.html @@ -338,9 +338,9 @@

Именованные списки APDU хранятся локально (simple_scripts) и передаются серверу при старте слушателя. Каждый список — последовательность hex C-APDU (по одной в строке; допускаются комментарии #/;). Кнопка Новый создаёт список из шаблона:

Таблица показывает имя, тип, число APDU и время создания каждого скрипта, а также кнопки Редактировать и Удалить; в редакторе есть поле имени и текстовая область APDU. В ходе сессии сервер отдаёт по одному C-APDU на каждый POST карты и отслеживает выполнение: карта сообщает статус в следующем POST (X-Admin-Script-Status), оборвавшаяся сессия досылает только невыполненные APDU (X-Admin-Resume продолжает, новый диалог начинает заново), а завершённый скрипт закрывается ответом 204 No Content. Конструктор RAM/GP вкладки Remote APDU может отправить цепочку команд прямо в прогон кнопкой «В очередь SCP81».

diff --git a/frontend/help.html b/frontend/help.html index 9f2b671..46b53ab 100644 --- a/frontend/help.html +++ b/frontend/help.html @@ -338,9 +338,9 @@

Named APDU lists stored locally (simple_scripts) and sent to the server when a listener starts. Each list is a sequence of hex C-APDUs (one per line; #/; comments allowed). New creates one from a template:

The table lists each script with its kind, APDU count and creation time, plus Edit and Delete; the editor has a name field and the APDU textarea. Over a session the server serves one C-APDU per card POST and tracks execution: the card reports status in its next POST (X-Admin-Script-Status), a session that dies resends only the unexecuted APDUs (X-Admin-Resume continues, a fresh dialog restarts), and a completed script is closed with 204 No Content. The Remote APDU tab's RAM/GP builder can feed a command chain straight into the run with Queue in SCP81.

diff --git a/frontend/index.html b/frontend/index.html index 543c837..5279b9d 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1180,9 +1180,9 @@ @@ -1436,7 +1436,7 @@ // ===== Version ===== // Single source of truth for the PWA version: shown in the header and used // by the server version check in pysimConnect(). -const SIMPLE_VERSION = '3.1.2'; +const SIMPLE_VERSION = '3.1.3'; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; // ===== Tab switching ===== @@ -9472,7 +9472,7 @@ function scriptsNew() { const tpl = document.getElementById('scripts-template').value; scriptsMsg(''); if (tpl === 'empty') { scriptsEdit(null, 'empty', '', []); return; } - if (tpl === 'explore') { scriptsEdit(null, 'explore', 'Explore', SCP81_EXPLORE_APDUS.slice()); return; } + if (tpl === 'explore') { scriptsEdit(null, 'explore', 'Explore ISD', SCP81_EXPLORE_APDUS.slice()); return; } // Install/delete: fill the parameter form above, then press Generate. scriptsTemplateChanged(); } @@ -9547,6 +9547,16 @@ function scriptsDelete(id) { scriptsPersist(); } +// Script template kind -> display label. The stored kind stays +// 'explore'/'delete'/'install'/'empty' (existing localStorage scripts). +function scriptKindLabel(kind) { + const k = String(kind || '').toLowerCase(); + if (k === 'explore') return t('Explore ISD'); + if (k === 'delete') return t('Delete AID'); + if (k === 'install') return t('Install from .cap'); + return t('Empty'); +} + function scriptsRender() { const tbody = document.getElementById('scripts-tbody'); if (!tbody) return; @@ -9558,7 +9568,7 @@ function scriptsRender() { for (const s of scripts) { html += ''; html += '' + esc(s.name) + ''; - html += '' + esc(t(s.kind || 'empty')) + ''; + html += '' + esc(scriptKindLabel(s.kind)) + ''; html += '' + ((s.apdus || []).length) + ''; html += '' + esc((s.created || '').slice(0, 10)) + ''; html += '' + @@ -13909,7 +13919,8 @@ const LANG_RU = { 'Listener': 'Слушатель', 'Scripts': 'Скрипты', 'Empty': 'Пустой', - 'Explore': 'Обзор', + 'Explore ISD': 'Обзор ISD', + 'Delete AID': 'Удалить AID', 'New': 'Новый', 'New script from': 'Новый скрипт из', 'Generate': 'Сгенерировать', diff --git a/frontend/sw.js b/frontend/sw.js index 60ffa3d..0ce9f0a 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simple-v237'; +const CACHE = 'simple-v238'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/scripts.test.js b/frontend/tests/scripts.test.js index 3038b77..8865493 100644 --- a/frontend/tests/scripts.test.js +++ b/frontend/tests/scripts.test.js @@ -31,11 +31,20 @@ eval(extractFunc(html, 'scp81LogLine')); eval(extractFunc(html, 'scp81LogEntryHtml')); eval(extractFunc(html, 'scp81GroupResults')); eval(extractFunc(html, 'scp81ScriptStateText')); +eval(extractFunc(html, 'scriptKindLabel')); global.esc = (s) => String(s == null ? '' : s) .replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -test('explore template is the reference administration sequence', () => { - const m = /const SCP81_EXPLORE_APDUS = \[(.*?)\];/s.exec(html); +test('scriptKindLabel maps the stored kinds to the template labels', () => { + assert.strictEqual(scriptKindLabel('explore'), 'Explore ISD'); + assert.strictEqual(scriptKindLabel('Explore'), 'Explore ISD'); + assert.strictEqual(scriptKindLabel('delete'), 'Delete AID'); + assert.strictEqual(scriptKindLabel('install'), 'Install from .cap'); + assert.strictEqual(scriptKindLabel('empty'), 'Empty'); + assert.strictEqual(scriptKindLabel(undefined), 'Empty'); +}); + +test('explore template is the reference administration sequence', () => { const m = /const SCP81_EXPLORE_APDUS = \[(.*?)\];/s.exec(html); assert.ok(m, 'SCP81_EXPLORE_APDUS not found'); const apdus = [...m[1].matchAll(/'([0-9A-F]+)'/g)].map(x => x[1]); assert.deepStrictEqual(apdus, ['80CAFF2100', '80F28002024F0000', '80CA008500', diff --git a/pyproject.toml b/pyproject.toml index 61bcd78..30b8ec4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pysim-simple-server" -version = "3.1.2" +version = "3.1.3" description = "HTTP REST server wrapping pysim for the SIMple PWA" requires-python = ">=3.8" # pysim is a git-only dependency installed explicitly by setup.bat/setup.sh. diff --git a/pysim_simple_server/server.py b/pysim_simple_server/server.py index 3c761e9..ea1e38b 100644 --- a/pysim_simple_server/server.py +++ b/pysim_simple_server/server.py @@ -28,7 +28,7 @@ from osmocom.tlv import BER_TLV_IE from osmocom.utils import rpad -VERSION = '3.1.2' +VERSION = '3.1.3' MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE