rename OTAMan → SIMple (v2.3.0)

- package pysim_otaman_server → pysim_simple_server; distribution and
  console script pysim-otaman-server → pysim-simple-server
- PWA branding SIMple: title, header, manifest, help EN/RU, GitHub links
- localStorage keys otaman_* → simple_* (clean break, no migration)
- export filenames simple-cards.json / simple-custom-files.json
- service worker cache simple-v189
- README/docs/setup/start scripts updated; no 'otaman' left in the tree
This commit is contained in:
2026-09-19 08:25:20 +03:00
parent 9c64da86a3
commit ac18c3e2cf
35 changed files with 207 additions and 207 deletions
+16 -16
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OTAMan — Справка</title>
<title>SIMple — Справка</title>
<link rel="stylesheet" href="style.css">
</head>
<body class="bg-neutral-50 dark:bg-slate-900 text-gray-800 dark:text-slate-200">
@@ -16,12 +16,12 @@
</aside>
<main class="flex-1 min-w-0 px-6 py-8">
<h1 class="text-2xl font-bold text-heading mb-6">OTAMan <span class="text-xs text-gray-400 dark:text-slate-500 ml-2">Документация</span></h1>
<h1 class="text-2xl font-bold text-heading mb-6">SIMple <span class="text-xs text-gray-400 dark:text-slate-500 ml-2">Документация</span></h1>
<section class="mb-10">
<h2 id="overview" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">1. Обзор</h2>
<p class="mb-3">OTAMan — Progressive Web App (PWA) для построения APDU-команд, сборки защищённых пакетов SCP80, просмотра меню SIM Toolkit (STK) и симуляции реальной сетевой среды для тестирования SIM/USIM/UICC-карт через PC/SC-ридер.</p>
<p class="mb-3">Приложение — один статический файл <code class="font-mono text-sm">index.html</code>. Все вычисления выполняются в браузере; доступ к карте — через локальный HTTP-сервер (<code class="font-mono text-sm">pysim-otaman-server</code>), оборачивающий библиотеку <a href="https://osmocom.org/projects/pysim" class="text-blue-600 dark:text-blue-400 hover:underline">pySim</a>.</p>
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">Браузер (OTAMan PWA) &rarr; HTTP :8080 &rarr; pysim-otaman-server &rarr; pySim &rarr; PC/SC &rarr; ридер &rarr; UICC/SIM</pre>
<p class="mb-3">SIMple — Progressive Web App (PWA) для построения APDU-команд, сборки защищённых пакетов SCP80, просмотра меню SIM Toolkit (STK) и симуляции реальной сетевой среды для тестирования SIM/USIM/UICC-карт через PC/SC-ридер.</p>
<p class="mb-3">Приложение — один статический файл <code class="font-mono text-sm">index.html</code>. Все вычисления выполняются в браузере; доступ к карте — через локальный HTTP-сервер (<code class="font-mono text-sm">pysim-simple-server</code>), оборачивающий библиотеку <a href="https://osmocom.org/projects/pysim" class="text-blue-600 dark:text-blue-400 hover:underline">pySim</a>.</p>
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">Браузер (SIMple PWA) &rarr; HTTP :8080 &rarr; pysim-simple-server &rarr; pySim &rarr; PC/SC &rarr; ридер &rarr; UICC/SIM</pre>
<p class="mb-3">Стандарты, на которые опирается приложение:</p>
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
<li>ETSI TS 102 221 — интерфейс UICC-терминал (CLA 00, файлы USIM)</li>
@@ -331,7 +331,7 @@
<p class="mb-2"><strong>Сценарий</strong> выбирает список команд, отдаваемый в сессии — <strong>Нет</strong> (оставить настроенный на сервере скрипт) или один из скриптов подвкладки <strong>&laquo;Скрипты&raquo;</strong>; выбранный список передаётся серверу при старте слушателя.</p>
<p class="text-sm mb-3">Строка состояния показывает слушатель, согласованный идентификатор и активные каналы (байты in/out). Панель <strong>Script results (R-APDUs)</strong> перечисляет каждый отданный C-APDU с его R-APDU/SW и прогрессом <em>done/total</em>; журнал <strong>HTTP OTA log</strong> фиксирует OPEN/CLOSE CHANNEL, SEND/RECEIVE DATA и каждый шаг TLS/HTTP/скрипта (<code class="font-mono text-sm">tls-handshake</code>, <code class="font-mono text-sm">tls-request</code>, <code class="font-mono text-sm">script-send</code>, <code class="font-mono text-sm">script-rapdu</code>, <code class="font-mono text-sm">script-page</code>, <code class="font-mono text-sm">script-done</code>, <code class="font-mono text-sm">data-available</code>, <code class="font-mono text-sm">peer-close</code>), кнопка <strong>Clear</strong> очищает его.</p>
<h3 id="scp81-scripts" class="text-lg font-medium mb-2">5.2 Скрипты</h3>
<p class="text-sm mb-2">Именованные списки APDU хранятся локально (<code class="font-mono text-sm">otaman_scripts</code>) и передаются серверу при старте слушателя. Каждый список — последовательность hex C-APDU (по одной в строке; допускаются комментарии <code class="font-mono text-sm">#</code>/<code class="font-mono text-sm">;</code>). Кнопка <strong>Новый</strong> создаёт список из шаблона:</p>
<p class="text-sm mb-2">Именованные списки APDU хранятся локально (<code class="font-mono text-sm">simple_scripts</code>) и передаются серверу при старте слушателя. Каждый список — последовательность hex C-APDU (по одной в строке; допускаются комментарии <code class="font-mono text-sm">#</code>/<code class="font-mono text-sm">;</code>). Кнопка <strong>Новый</strong> создаёт список из шаблона:</p>
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
<li><strong>Empty</strong> — начать с пустого списка.</li>
<li><strong>Explore</strong> — эталонная административная последовательность (<code class="font-mono text-sm">GET DATA FF21</code>, листинги GET STATUS ISD/ELF/приложений, <code class="font-mono text-sm">GET DATA 0085</code>); длинные листинги автоматически продолжаются страницами <code class="font-mono text-sm">SW 6310/CAFE</code>.</li>
@@ -343,7 +343,7 @@
<section class="mb-10">
<h2 id="card-reader" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">6. Вкладка &laquo;Картридер&raquo; (pySim)</h2>
<p class="mb-3">Подключение к локальному <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> для работы с картой: введите URL сервера (по умолчанию <code class="font-mono text-sm">http://127.0.0.1:8080</code>) и нажмите <strong>Подключиться</strong>. Область статуса показывает состояние ридера/карты, а <strong>Подключить карту</strong> (пере)инициализирует карту после вставки. Подвкладки: <strong>Файловый менеджер</strong>, <strong>Командная строка pySim</strong> и <strong>Отправка APDU</strong>. <strong>&laquo;Профайлер&raquo;</strong> и <strong>&laquo;Симулятор телефона&raquo;</strong> — отдельные вкладки верхнего уровня.</p>
<p class="mb-3">Подключение к локальному <a href="https://github.com/anttro/simple" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-simple-server</a> для работы с картой: введите URL сервера (по умолчанию <code class="font-mono text-sm">http://127.0.0.1:8080</code>) и нажмите <strong>Подключиться</strong>. Область статуса показывает состояние ридера/карты, а <strong>Подключить карту</strong> (пере)инициализирует карту после вставки. Подвкладки: <strong>Файловый менеджер</strong>, <strong>Командная строка pySim</strong> и <strong>Отправка APDU</strong>. <strong>&laquo;Профайлер&raquo;</strong> и <strong>&laquo;Симулятор телефона&raquo;</strong> — отдельные вкладки верхнего уровня.</p>
<h3 id="file-manager" class="text-lg font-medium mb-2">6.1 Файловый менеджер</h3>
<p class="text-sm mb-2">Дерево файловой системы отображается слева; выбор файла открывает панель деталей справа. Элементы сгруппированы: DF выше EF, сортировка по <strong>FID</strong> или символьному <strong>имени</strong> (пиллы и кнопка <strong>«Проверить все файлы»</strong> закреплены над прокручиваемым деревом; выбор сохраняется в <code class="font-mono text-sm">localStorage</code>). При выборе файла над содержимым также показываются FID, тип файла, размер / структура записей и декодированный FCI.</p>
@@ -478,7 +478,7 @@
<section class="mb-10">
<h2 id="server" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">9. Установка сервера</h2>
<p class="mb-3">Для работы с картой (вкладка &laquo;Картридер&raquo;, &laquo;Симулятор телефона&raquo;, доставка OTA) нужен локальный <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> — встроенный в OTAMan HTTP-сервер, оборачивающий pySim, работающий с ридером через PC/SC или serial и раздающий сам PWA (откройте <code class="font-mono text-sm">http://127.0.0.1:8080</code>).</p>
<p class="mb-3">Для работы с картой (вкладка &laquo;Картридер&raquo;, &laquo;Симулятор телефона&raquo;, доставка OTA) нужен локальный <a href="https://github.com/anttro/simple" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-simple-server</a> — встроенный в SIMple HTTP-сервер, оборачивающий pySim, работающий с ридером через PC/SC или serial и раздающий сам PWA (откройте <code class="font-mono text-sm">http://127.0.0.1:8080</code>).</p>
<h3 id="prerequisites" class="text-lg font-medium mb-2">9.1 Требования</h3>
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
@@ -489,15 +489,15 @@
</ul>
<h3 id="quickstart-linux" class="text-lg font-medium mb-2">9.2 Быстрый старт — Linux / macOS</h3>
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/otaman.git
cd otaman
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/simple.git
cd simple
chmod +x setup.sh start.sh
./setup.sh # создаёт .venv, устанавливает pysim + сервер (однократно)
./start.sh # запускает сервер (PWA + API, автоопределение ридера)</pre>
<h3 id="quickstart-windows" class="text-lg font-medium mb-2">9.3 Быстрый старт — Windows</h3>
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/otaman.git
cd otaman
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/simple.git
cd simple
setup.bat # создаёт .venv, устанавливает pysim + сервер (однократно)
start.bat # запускает сервер (PWA + API)</pre>
@@ -528,19 +528,19 @@ source .venv/bin/activate # Linux/macOS
# Установить pysim
pip install git+https://github.com/osmocom/pysim.git
# Установить pysim-otaman-server (editable — раздаёт встроенный PWA)
# Установить pysim-simple-server (editable — раздаёт встроенный PWA)
pip install -e .
# Запустить сервер (PWA + API)
pysim-otaman-server --http-port 8080</pre>
pysim-simple-server --http-port 8080</pre>
<p class="text-sm mb-3">Подключите PC/SC-ридер с SIM-картой и откройте <code class="font-mono text-sm">http://127.0.0.1:8080</code> — PWA и API на одном origin, поэтому CORS не требуется.</p>
<p class="text-sm mb-3">Если PWA раздаётся с публичного HTTPS-хоста (например, <code class="font-mono text-sm">https://otaman.example.com</code>), для доступа к локальному серверу карт нужны два условия: (1) сервер отвечает на preflight заголовком <code class="font-mono text-sm">Access-Control-Allow-Private-Network: true</code> (pysim-otaman-server &ge; 1.6.1 делает это автоматически), и (2) браузеру должно быть разрешено обращаться к локальной сети &mdash; в Chrome/Edge/Vivaldi: Настройки сайта &rarr; Доступ к локальной сети &rarr; разрешить сайт (или подтвердить запрос). Без разрешения браузера запрос к <code class="font-mono text-sm">127.0.0.1</code> блокируется ещё до отправки preflight.</p>
<p class="text-sm mb-3">Если PWA раздаётся с публичного HTTPS-хоста (например, <code class="font-mono text-sm">https://simple.example.com</code>), для доступа к локальному серверу карт нужны два условия: (1) сервер отвечает на preflight заголовком <code class="font-mono text-sm">Access-Control-Allow-Private-Network: true</code> (pysim-simple-server &ge; 1.6.1 делает это автоматически), и (2) браузеру должно быть разрешено обращаться к локальной сети &mdash; в Chrome/Edge/Vivaldi: Настройки сайта &rarr; Доступ к локальной сети &rarr; разрешить сайт (или подтвердить запрос). Без разрешения браузера запрос к <code class="font-mono text-sm">127.0.0.1</code> блокируется ещё до отправки preflight.</p>
<section class="mb-10">
<h2 id="compatibility" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">10. Совместимость версий</h2>
<table class="w-full text-sm mb-3 border-collapse">
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">PWA (OTAMan)</th><th class="text-left py-1 px-2">Сервер</th><th class="text-left py-1 px-2">Статус</th></tr></thead>
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">PWA (SIMple)</th><th class="text-left py-1 px-2">Сервер</th><th class="text-left py-1 px-2">Статус</th></tr></thead>
<tbody>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2">&#9989; Совместимы</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">0.x.x</td><td class="py-1 px-2">&#10060; Устарел &mdash; обновите сервер</td></tr>
+16 -16
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OTAMan — Help</title>
<title>SIMple — Help</title>
<link rel="stylesheet" href="style.css">
</head>
<body class="bg-neutral-50 dark:bg-slate-900 text-gray-800 dark:text-slate-200">
@@ -16,12 +16,12 @@
</aside>
<main class="flex-1 min-w-0 px-6 py-8">
<h1 class="text-2xl font-bold text-heading mb-6">OTAMan <span class="text-xs text-gray-400 dark:text-slate-500 ml-2">Documentation</span></h1>
<h1 class="text-2xl font-bold text-heading mb-6">SIMple <span class="text-xs text-gray-400 dark:text-slate-500 ml-2">Documentation</span></h1>
<section class="mb-10">
<h2 id="overview" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">1. Overview</h2>
<p class="mb-3">OTAMan is a Progressive Web App (PWA) for building APDU commands, assembling SCP80 secured packets, browsing the SIM Toolkit (STK) menu, and simulating a real network environment against a SIM/USIM/UICC card via a PC/SC reader.</p>
<p class="mb-3">The application is a single static <code class="font-mono text-sm">index.html</code> file. All computation runs in the browser; the card is accessed through a local HTTP server (<code class="font-mono text-sm">pysim-otaman-server</code>) that wraps the <a href="https://osmocom.org/projects/pysim" class="text-blue-600 dark:text-blue-400 hover:underline">pySim</a> library.</p>
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">Browser (OTAMan PWA) &rarr; HTTP :8080 &rarr; pysim-otaman-server &rarr; pySim &rarr; PC/SC &rarr; card reader &rarr; UICC/SIM</pre>
<p class="mb-3">SIMple is a Progressive Web App (PWA) for building APDU commands, assembling SCP80 secured packets, browsing the SIM Toolkit (STK) menu, and simulating a real network environment against a SIM/USIM/UICC card via a PC/SC reader.</p>
<p class="mb-3">The application is a single static <code class="font-mono text-sm">index.html</code> file. All computation runs in the browser; the card is accessed through a local HTTP server (<code class="font-mono text-sm">pysim-simple-server</code>) that wraps the <a href="https://osmocom.org/projects/pysim" class="text-blue-600 dark:text-blue-400 hover:underline">pySim</a> library.</p>
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">Browser (SIMple PWA) &rarr; HTTP :8080 &rarr; pysim-simple-server &rarr; pySim &rarr; PC/SC &rarr; card reader &rarr; UICC/SIM</pre>
<p class="mb-3">Standards referenced across the application:</p>
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
<li>ETSI TS 102 221 — UICC-Terminal interface (CLA 00, USIM files)</li>
@@ -331,7 +331,7 @@
<p class="mb-2"><strong>Script</strong> selects the command list served over the session — <strong>None</strong> (leave the server's configured script) or one of the scripts from the <strong>Scripts</strong> pill; the chosen list is sent to the server when the listener starts.</p>
<p class="text-sm mb-3">The state line shows the listener, the negotiated identity and live channels (bytes in/out). <strong>Script results (R-APDUs)</strong> lists each served C-APDU with its R-APDU/SW and a <em>done/total</em> progress; the <strong>HTTP OTA log</strong> records OPEN/CLOSE CHANNEL, SEND/RECEIVE DATA and every TLS/HTTP/script step (<code class="font-mono text-sm">tls-handshake</code>, <code class="font-mono text-sm">tls-request</code>, <code class="font-mono text-sm">script-send</code>, <code class="font-mono text-sm">script-rapdu</code>, <code class="font-mono text-sm">script-page</code>, <code class="font-mono text-sm">script-done</code>, <code class="font-mono text-sm">data-available</code>, <code class="font-mono text-sm">peer-close</code>), with <strong>Clear</strong>.</p>
<h3 id="scp81-scripts" class="text-lg font-medium mb-2">5.2 Scripts</h3>
<p class="text-sm mb-2">Named APDU lists stored locally (<code class="font-mono text-sm">otaman_scripts</code>) and sent to the server when a listener starts. Each list is a sequence of hex C-APDUs (one per line; <code class="font-mono text-sm">#</code>/<code class="font-mono text-sm">;</code> comments allowed). <strong>New</strong> creates one from a template:</p>
<p class="text-sm mb-2">Named APDU lists stored locally (<code class="font-mono text-sm">simple_scripts</code>) and sent to the server when a listener starts. Each list is a sequence of hex C-APDUs (one per line; <code class="font-mono text-sm">#</code>/<code class="font-mono text-sm">;</code> comments allowed). <strong>New</strong> creates one from a template:</p>
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
<li><strong>Empty</strong> — start from scratch.</li>
<li><strong>Explore</strong> — the reference administration sequence (<code class="font-mono text-sm">GET DATA FF21</code>, GET STATUS ISD/ELF/application listings, <code class="font-mono text-sm">GET DATA 0085</code>); long listings auto-continue through the <code class="font-mono text-sm">SW 6310/CAFE</code> pages.</li>
@@ -343,7 +343,7 @@
<section class="mb-10">
<h2 id="card-reader" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">6. Card reader (pySim) tab</h2>
<p class="mb-3">Connects to a local <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> for live card operations: enter the server URL (default <code class="font-mono text-sm">http://127.0.0.1:8080</code>) and press <strong>Connect</strong>. The status area shows the reader/card state, and <strong>Equip card</strong> (re)initializes the card after insertion. Sub-tabs: <strong>File manager</strong>, <strong>pySim command line</strong>, and <strong>Raw APDU</strong>. The <strong>Profiler</strong> and <strong>Phone simulator</strong> are separate top-level tabs.</p>
<p class="mb-3">Connects to a local <a href="https://github.com/anttro/simple" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-simple-server</a> for live card operations: enter the server URL (default <code class="font-mono text-sm">http://127.0.0.1:8080</code>) and press <strong>Connect</strong>. The status area shows the reader/card state, and <strong>Equip card</strong> (re)initializes the card after insertion. Sub-tabs: <strong>File manager</strong>, <strong>pySim command line</strong>, and <strong>Raw APDU</strong>. The <strong>Profiler</strong> and <strong>Phone simulator</strong> are separate top-level tabs.</p>
<h3 id="file-manager" class="text-lg font-medium mb-2">6.1 File manager</h3>
<p class="text-sm mb-2">The file system tree is displayed on the left; selecting a file opens its detail pane on the right. Entries are grouped with DFs above EFs and sorted by <strong>FID</strong> or symbolic <strong>Name</strong> (pills pinned above the scrolling tree together with <strong>Probe all files</strong>; the choice is remembered in <code class="font-mono text-sm">localStorage</code>). Selecting a file also shows its FID, file type, size / record layout and the decoded FCI above the content pane.</p>
@@ -478,7 +478,7 @@
<section class="mb-10">
<h2 id="server" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">9. Server installation</h2>
<p class="mb-3">Live card operations (Card reader tab, Phone simulator, OTA delivery) require the local <a href="https://github.com/anttro/otaman" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-otaman-server</a> — a small HTTP server bundled with OTAMan that wraps pySim, talks to the reader over PC/SC or serial, and also serves the PWA itself (open <code class="font-mono text-sm">http://127.0.0.1:8080</code>).</p>
<p class="mb-3">Live card operations (Card reader tab, Phone simulator, OTA delivery) require the local <a href="https://github.com/anttro/simple" class="text-blue-600 dark:text-blue-400 hover:underline">pysim-simple-server</a> — a small HTTP server bundled with SIMple that wraps pySim, talks to the reader over PC/SC or serial, and also serves the PWA itself (open <code class="font-mono text-sm">http://127.0.0.1:8080</code>).</p>
<h3 id="prerequisites" class="text-lg font-medium mb-2">9.1 Prerequisites</h3>
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
@@ -489,15 +489,15 @@
</ul>
<h3 id="quickstart-linux" class="text-lg font-medium mb-2">9.2 Quick start — Linux / macOS</h3>
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/otaman.git
cd otaman
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/simple.git
cd simple
chmod +x setup.sh start.sh
./setup.sh # creates .venv, installs pysim + server (run once)
./start.sh # starts the server (serves PWA + API, auto-detects reader)</pre>
<h3 id="quickstart-windows" class="text-lg font-medium mb-2">9.3 Quick start — Windows</h3>
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/otaman.git
cd otaman
<pre class="font-mono text-xs bg-gray-100 dark:bg-slate-800 rounded p-3 mb-3">git clone https://github.com/anttro/simple.git
cd simple
setup.bat # creates .venv, installs pysim + server (run once)
start.bat # starts the server (serves PWA + API)</pre>
@@ -528,19 +528,19 @@ source .venv/bin/activate # Linux/macOS
# Install pysim
pip install git+https://github.com/osmocom/pysim.git
# Install pysim-otaman-server (editable, so it serves the bundled PWA)
# Install pysim-simple-server (editable, so it serves the bundled PWA)
pip install -e .
# Start the server (serves PWA + API)
pysim-otaman-server --http-port 8080</pre>
pysim-simple-server --http-port 8080</pre>
<p class="text-sm mb-3">Connect a PC/SC reader with a SIM card, then open <code class="font-mono text-sm">http://127.0.0.1:8080</code> — the PWA and API share one origin, so no CORS is involved.</p>
<p class="text-sm mb-3">If the PWA is served from a public HTTPS host (e.g. <code class="font-mono text-sm">https://otaman.example.com</code>), two things are required to reach a local card server: (1) the server must answer the preflight with <code class="font-mono text-sm">Access-Control-Allow-Private-Network: true</code> (pysim-otaman-server &ge; 1.6.1 does this automatically), and (2) the browser must be allowed to access the local network &mdash; in Chrome/Edge/Vivaldi: Site settings &rarr; Local network access &rarr; allow the site (or accept the permission prompt). Without the browser permission, the request to <code class="font-mono text-sm">127.0.0.1</code> is blocked before any preflight is sent.</p>
<p class="text-sm mb-3">If the PWA is served from a public HTTPS host (e.g. <code class="font-mono text-sm">https://simple.example.com</code>), two things are required to reach a local card server: (1) the server must answer the preflight with <code class="font-mono text-sm">Access-Control-Allow-Private-Network: true</code> (pysim-simple-server &ge; 1.6.1 does this automatically), and (2) the browser must be allowed to access the local network &mdash; in Chrome/Edge/Vivaldi: Site settings &rarr; Local network access &rarr; allow the site (or accept the permission prompt). Without the browser permission, the request to <code class="font-mono text-sm">127.0.0.1</code> is blocked before any preflight is sent.</p>
<section class="mb-10">
<h2 id="compatibility" class="text-xl font-semibold mb-3 border-b border-gray-300 dark:border-slate-700 pb-1">10. Version compatibility</h2>
<table class="w-full text-sm mb-3 border-collapse">
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">PWA (OTAMan)</th><th class="text-left py-1 px-2">Server</th><th class="text-left py-1 px-2">Status</th></tr></thead>
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">PWA (SIMple)</th><th class="text-left py-1 px-2">Server</th><th class="text-left py-1 px-2">Status</th></tr></thead>
<tbody>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2">&#9989; Compatible</td></tr>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2 font-mono">1.x.x</td><td class="py-1 px-2 font-mono">0.x.x</td><td class="py-1 px-2">&#10060; Outdated &mdash; update server</td></tr>
+28 -28
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OTAMan</title>
<title>SIMple</title>
<link rel="stylesheet" href="style.css">
<link rel="icon" type="image/svg+xml" href="favicon.svg">
<link rel="icon" type="image/png" href="sim.png" sizes="462x462">
@@ -18,7 +18,7 @@
<div class="max-w-7xl mx-auto px-6 py-2">
<div class="flex items-center justify-between mb-3">
<h1 class="text-2xl font-bold text-heading">OTAMan <span id="slogan" class="text-sm font-normal text-gray-500 dark:text-slate-400 ml-2" data-l10n="SIM OTA with a Human Face">SIM OTA with a Human Face</span> <span class="text-xs text-gray-400 dark:text-slate-500 ml-1">v2.2.21</span></h1>
<h1 class="text-2xl font-bold text-heading">SIMple <span id="slogan" class="text-sm font-normal text-gray-500 dark:text-slate-400 ml-2" data-l10n="SIM OTA with a Human Face">SIM OTA with a Human Face</span> <span class="text-xs text-gray-400 dark:text-slate-500 ml-1">v2.3.0</span></h1>
<div class="flex items-center gap-4">
<span id="state-indicator" class="flex items-center select-none" style="cursor:default" title="Connecting...">
<span id="state-indicator-dot" class="text-xs text-gray-400" title="Connecting..."></span>
@@ -26,7 +26,7 @@
<span id="state-indicator-adm" class="hidden ml-0.5 text-xs font-mono" title=""></span>
</span>
<button id="install-btn" class="px-2 py-1 text-xs rounded border border-gray-300 dark:border-slate-600 hover:bg-gray-200 dark:hover:bg-slate-700" style="display:none">INSTALL PWA [for offline use]</button>
<a href="https://github.com/anttro/otaman" target="_blank" class="text-xs text-gray-400 hover:text-gray-600 dark:text-slate-500 dark:hover:text-slate-300">github</a>
<a href="https://github.com/anttro/simple" target="_blank" class="text-xs text-gray-400 hover:text-gray-600 dark:text-slate-500 dark:hover:text-slate-300">github</a>
<a id="help-link" href="help.html" target="_blank" rel="noopener" class="text-xs text-gray-400 hover:text-gray-600 dark:text-slate-500 dark:hover:text-slate-300" data-l10n="help">help</a>
<button onclick="toggleLang()" id="lang-btn" class="px-2 py-1 text-xs rounded border border-gray-300 dark:border-slate-600 hover:bg-gray-200 dark:hover:bg-slate-700">EN</button>
<button onclick="toggleTheme()" id="theme-btn" class="px-2 py-1 text-sm rounded border border-gray-300 dark:border-slate-600 hover:bg-gray-200 dark:hover:bg-slate-700">🌙</button>
@@ -815,8 +815,8 @@
</div>
</div>
<div id="pysim-connect-info" class="mb-3 text-xs text-gray-500 dark:text-slate-400 leading-relaxed">
<p data-l10n="To access local card reader, you need pysim-otaman-server running locally.">To access local card reader, you need pysim-otaman-server running locally.</p>
<p><span data-l10n="To connect, install">To connect, install</span> <a href="https://github.com/anttro/otaman" target="_blank" class="text-blue-600 dark:text-blue-400 underline">pysim-otaman-server</a>: <code class="font-mono bg-gray-100 dark:bg-slate-700 px-1 rounded">git clone https://github.com/anttro/otaman.git</code> <span data-l10n="and start it.">and start it.</span><br><span data-l10n="Refer to pysim-otaman-server README files for details.">Refer to pysim-otaman-server README files for details.</span> <span data-l10n="Click">Click</span> <b data-l10n="Connect">Connect</b> <span data-l10n="when pysim-otaman-server is ready">when pysim-otaman-server is ready</span></p>
<p data-l10n="To access local card reader, you need pysim-simple-server running locally.">To access local card reader, you need pysim-simple-server running locally.</p>
<p><span data-l10n="To connect, install">To connect, install</span> <a href="https://github.com/anttro/simple" target="_blank" class="text-blue-600 dark:text-blue-400 underline">pysim-simple-server</a>: <code class="font-mono bg-gray-100 dark:bg-slate-700 px-1 rounded">git clone https://github.com/anttro/simple.git</code> <span data-l10n="and start it.">and start it.</span><br><span data-l10n="Refer to pysim-simple-server README files for details.">Refer to pysim-simple-server README files for details.</span> <span data-l10n="Click">Click</span> <b data-l10n="Connect">Connect</b> <span data-l10n="when pysim-simple-server is ready">when pysim-simple-server is ready</span></p>
<div id="pysim-connect-msg" class="text-xs"></div>
</div>
<div id="pysim-connected-row" class="mb-3 hidden">
@@ -4679,7 +4679,7 @@ function decodeResponse() {
// ===== pysim integration =====
// Default to a local server on 127.0.0.1:8080. If this PWA is served by
// pysim-otaman-server itself (same origin), detect the co-located API once
// pysim-simple-server itself (same origin), detect the co-located API once
// and switch to a relative base; otherwise keep the default and wait for the
// user to point at a server.
let pysimBase = 'http://127.0.0.1:8080';
@@ -4913,13 +4913,13 @@ async function pysimConnect() {
_pysimServerAvailable = true;
const sv = (ver.version || '').split('.').map(Number);
if (sv[0] < 1 || (sv[0] === 1 && sv[1] < 0)) {
msgEl.innerHTML = '<span class="text-red-500">Server version ' + esc(ver.version) + ' is outdated. Please update pysim-otaman-server.</span>';
msgEl.innerHTML = '<span class="text-red-500">Server version ' + esc(ver.version) + ' is outdated. Please update pysim-simple-server.</span>';
btn.textContent = t('Connect');
btn.disabled = false;
return;
}
if (sv[0] > 1) {
msgEl.innerHTML = '<span class="text-yellow-500">Warning: server version ' + esc(ver.version) + ' is newer than expected. Update OTAMan for full compatibility.</span>';
msgEl.innerHTML = '<span class="text-yellow-500">Warning: server version ' + esc(ver.version) + ' is newer than expected. Update SIMple for full compatibility.</span>';
}
const status = await pysimFetch('/api/status');
if (!status.app_ready) {
@@ -5947,7 +5947,7 @@ let _cardsEditIdx = null;
function cardsLoad() {
try {
const d = localStorage.getItem('otaman_cards');
const d = localStorage.getItem('simple_cards');
cards = d ? JSON.parse(d) : [];
} catch (e) { cards = []; }
cardsRebuildSelect();
@@ -5955,7 +5955,7 @@ function cardsLoad() {
}
function cardsSave() {
localStorage.setItem('otaman_cards', JSON.stringify(cards));
localStorage.setItem('simple_cards', JSON.stringify(cards));
cardsRebuildSelect();
cardsRender();
scp81PushPskMap();
@@ -6225,7 +6225,7 @@ function downloadJson(name, obj) {
}
function cardsExportFile() {
downloadJson('otaman-cards.json', cards);
downloadJson('simple-cards.json', cards);
}
async function cardsImport(text) {
@@ -6511,7 +6511,7 @@ function pysimCustomRefreshTree() {
pysimFsRenderTree();
}
let pysimFsSort = localStorage.getItem('otaman_fs_sort') === 'name' ? 'name' : 'fid';
let pysimFsSort = localStorage.getItem('simple_fs_sort') === 'name' ? 'name' : 'fid';
function pysimFsSortChildren(children, mode) {
return (children || []).slice().sort((a, b) => {
@@ -6536,7 +6536,7 @@ function pysimFsSortChildren(children, mode) {
function pysimFsSetSort(mode) {
pysimFsSort = mode === 'name' ? 'name' : 'fid';
localStorage.setItem('otaman_fs_sort', pysimFsSort);
localStorage.setItem('simple_fs_sort', pysimFsSort);
document.querySelectorAll('.pysim-fs-sort-pill').forEach(p => {
const active = p.dataset.fsSort === pysimFsSort;
p.classList.toggle('bg-blue-600', active);
@@ -8036,7 +8036,7 @@ function scp81OptionsBadge() {
function scp81OptionsLoad() {
let saved = {};
try { saved = JSON.parse(localStorage.getItem('otaman_scp81_opts') || '{}') || {}; } catch (e) { saved = {}; }
try { saved = JSON.parse(localStorage.getItem('simple_scp81_opts') || '{}') || {}; } catch (e) { saved = {}; }
const opt = Object.assign({}, SCP81_OPT_DEFAULTS, saved);
if (saved.nextUriOn === undefined) opt.nextUriOn = (opt.nextUri || '') !== '';
const set = (id, v) => { const el = document.getElementById(id); if (el) el.value = v; };
@@ -8057,13 +8057,13 @@ function scp81OptionsLoad() {
}
function scp81OptionsPersist() {
localStorage.setItem('otaman_scp81_opts', JSON.stringify(scp81OptionsState()));
localStorage.setItem('simple_scp81_opts', JSON.stringify(scp81OptionsState()));
scp81OptionsSyncDisabled();
scp81OptionsBadge();
}
function scp81OptionsReset() {
localStorage.removeItem('otaman_scp81_opts');
localStorage.removeItem('simple_scp81_opts');
scp81OptionsLoad();
}
@@ -8602,14 +8602,14 @@ let _scriptsNewKind = 'empty';
function scriptsLoad() {
try {
const d = localStorage.getItem('otaman_scripts');
const d = localStorage.getItem('simple_scripts');
scripts = d ? JSON.parse(d) : [];
} catch (e) { scripts = []; }
scriptsRebuildSelect();
}
function scriptsPersist() {
localStorage.setItem('otaman_scripts', JSON.stringify(scripts));
localStorage.setItem('simple_scripts', JSON.stringify(scripts));
scriptsRebuildSelect();
scriptsRender();
}
@@ -9444,7 +9444,7 @@ function pysimCustomExport() {
}
function pysimCustomExportFile() {
downloadJson('otaman-custom-files.json', pysimCustomFiles);
downloadJson('simple-custom-files.json', pysimCustomFiles);
}
async function pysimCustomImport(text) {
@@ -9529,25 +9529,25 @@ const PROFILER_MASK_PREFIX4_FIDS = {
function profilerLoad() {
try {
const d = localStorage.getItem('otaman_profiles');
const d = localStorage.getItem('simple_profiles');
profiles = d ? JSON.parse(d) : [];
} catch (e) { profiles = []; }
}
function profilerSave() {
localStorage.setItem('otaman_profiles', JSON.stringify(profiles));
localStorage.setItem('simple_profiles', JSON.stringify(profiles));
}
function snapshotsLoad() {
try {
const d = localStorage.getItem('otaman_snapshots');
const d = localStorage.getItem('simple_snapshots');
snapshots = d ? JSON.parse(d) : [];
} catch (e) { snapshots = []; }
}
function snapshotsSave() {
try {
localStorage.setItem('otaman_snapshots', JSON.stringify(snapshots));
localStorage.setItem('simple_snapshots', JSON.stringify(snapshots));
} catch (e) {
alert('Snapshot save failed: ' + e.message);
}
@@ -11499,12 +11499,12 @@ const LANG_RU = {
'Install Package (.cap file)': 'Установка пакета (.cap файл)',
'Server URL': 'URL сервера',
'Connect': 'Подключиться',
'To access local card reader, you need pysim-otaman-server running locally.': 'Для доступа к локальному картридеру необходим запущенный pysim-otaman-server.',
'To access local card reader, you need pysim-simple-server running locally.': 'Для доступа к локальному картридеру необходим запущенный pysim-simple-server.',
'To connect, install': 'Для подключения установите',
'and start it.': 'и запустите его.',
'Refer to pysim-otaman-server README files for details.': 'Подробности — в README файлах pysim-otaman-server.',
'Refer to pysim-simple-server README files for details.': 'Подробности — в README файлах pysim-simple-server.',
'Click': 'Нажмите кнопку',
'when pysim-otaman-server is ready': 'когда pysim-otaman-server будет готов',
'when pysim-simple-server is ready': 'когда pysim-simple-server будет готов',
'Equip card': 'Подключить карту',
'Check status': 'Проверить статус',
'Reset card': 'Сброс карты',
@@ -11532,8 +11532,8 @@ const LANG_RU = {
'Checking card...': 'Проверка карты...',
'Connected': 'Подключено',
'Connection failed:': 'Ошибка подключения:',
'pysim-blocked-hint': 'Браузер, вероятно, заблокировал доступ к локальному серверу карт. Разрешите доступ к локальной сети для этого сайта (Chrome/Edge/Vivaldi: Настройки сайта → Доступ к локальной сети → Разрешить). Также проверьте, что pysim-otaman-server запущен по адресу {url}.',
'pysim-unreachable-hint': 'Не удалось подключиться к серверу карт. Проверьте, что pysim-otaman-server запущен и адрес указан верно ({url}). Если эта страница открыта по HTTPS, также разрешите доступ к локальной сети в браузере (Chrome/Edge/Vivaldi: Настройки сайта → Доступ к локальной сети → Разрешить).',
'pysim-blocked-hint': 'Браузер, вероятно, заблокировал доступ к локальному серверу карт. Разрешите доступ к локальной сети для этого сайта (Chrome/Edge/Vivaldi: Настройки сайта → Доступ к локальной сети → Разрешить). Также проверьте, что pysim-simple-server запущен по адресу {url}.',
'pysim-unreachable-hint': 'Не удалось подключиться к серверу карт. Проверьте, что pysim-simple-server запущен и адрес указан верно ({url}). Если эта страница открыта по HTTPS, также разрешите доступ к локальной сети в браузере (Chrome/Edge/Vivaldi: Настройки сайта → Доступ к локальной сети → Разрешить).',
'No card detected. Insert card and click Equip card button': 'Карта не обнаружена. Вставьте карту и нажмите Подключить карту',
'Card inserted — press Equip': 'Карта вставлена — нажмите «Подключить карту»',
'Card inserted — initializing...': 'Карта вставлена — инициализация...',
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "OTAMan",
"short_name": "OTAMan",
"name": "SIMple",
"short_name": "SIMple",
"description": "Offline APDU helper for SIM/USIM/RAM commands",
"start_url": "index.html",
"display": "standalone",
+2 -2
View File
@@ -1,11 +1,11 @@
{
"name": "otaman",
"name": "simple",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "otaman",
"name": "simple",
"version": "1.0.0",
"dependencies": {
"aes-js": "^3.1.2",
+2 -2
View File
@@ -1,5 +1,5 @@
{
"name": "otaman",
"name": "simple",
"version": "1.0.0",
"description": "Standalone offline HTML/JS tool for building APDU commands for SIM, USIM, and GlobalPlatform RAM, plus encoding conversions.",
"main": "index.js",
@@ -10,7 +10,7 @@
},
"repository": {
"type": "git",
"url": "https://github.com/anttro/otaman.git"
"url": "https://github.com/anttro/simple.git"
},
"keywords": [],
"type": "commonjs",
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'otaman-v188';
const CACHE = 'simple-v189';
const URLS = [
'index.html',
'help.html',
+4 -4
View File
@@ -167,9 +167,9 @@ test('load/persist/reset round-trip through localStorage', () => {
'opt-targeted-app': '//aid/A000000151000000'
});
scp81OptionsPersist();
assert.ok(store['otaman_scp81_opts'].includes('"chunkSize":100'));
assert.ok(store['otaman_scp81_opts'].includes('"targetedAppOn":true'));
assert.ok(!store['otaman_scp81_opts'].includes('keepAlive'));
assert.ok(store['simple_scp81_opts'].includes('"chunkSize":100'));
assert.ok(store['simple_scp81_opts'].includes('"targetedAppOn":true'));
assert.ok(!store['simple_scp81_opts'].includes('keepAlive'));
// a reload restores the saved setup
setup({});
scp81OptionsLoad();
@@ -184,7 +184,7 @@ test('load/persist/reset round-trip through localStorage', () => {
assert.strictEqual(get('opt-targeted-app').value, '//aid/A000000151000000');
// reset clears the saved entry and restores the defaults
scp81OptionsReset();
assert.ok(!('otaman_scp81_opts' in store));
assert.ok(!('simple_scp81_opts' in store));
scp81OptionsLoad();
assert.strictEqual(get('opt-chunked').checked, true);
assert.strictEqual(get('opt-next-uri').checked, true);