EL-KITE MCP
OfficialREADME.md
# EL-KITE MCP
Aplikasi untuk membaca dan merapikan usulan jabatan akademik dosen (JAD) di
EL-KITE LLDIKTI III (<https://elkite-lldikti3.kemdiktisaintek.go.id>) dari
asisten AI, terminal, atau program lain.
Paket ini membaca profil dosen beserta riwayat jabatan fungsionalnya, daftar
usulan JAD (Draft, Proses, Selesai), kedelapan tab draf usulan, dan AK
Penyetaraan. Untuk merapikan draf, ia bisa mengubah empat isian (jenjang
pendidikan pada jabatan sebelumnya, JAD usulan, bidang ilmu, dan tanggal
penerbitan ijazah), mengunggah dokumen persyaratan, mengisi kegiatan IKD, dan
menambah entri Bidang B. Semua alat yang menulis bawaannya uji coba dan membaca
ulang EL-KITE sesudah mengirim; rinciannya di
[Mengubah draf usulan](#mengubah-draf-usulan). Mengajukan usulan, mengubah
status usulan, dan menghapus apa pun tidak bisa dilakukan lewat paket ini.
Tersedia dalam tiga bentuk, semuanya dari paket yang sama:
| Mode | Untuk siapa | Referensi |
|----------|--------------------------------|------------------------------|
| MCP | asisten AI (Claude Code, dsb.) | [docs/MCP.md](docs/MCP.md) |
| REST API | aplikasi apa pun lewat HTTP | [docs/API.md](docs/API.md) |
| Pustaka | program Python Anda sendiri | [bagian Pustaka](#3-pustaka) |
Ada juga CLI (`./elkite draf`, `./elkite ubah`, dan seterusnya) untuk pemakaian
langsung dari terminal; lihat [docs/CLI.md](docs/CLI.md).
## Memakainya lewat asisten AI
Server MCP-nya bisa didaftarkan ke Claude Code, Codex, dan Antigravity. Yang
tercepat, untuk Claude Code dari klona repositori ini:
```bash
claude mcp add elkite-mcp -s user -- /path/ke/elkite-mcp/elkite mcp
```
Sebelum tool-nya dipakai, login sekali di terminal dengan `./elkite login` (lihat
[Login](#login)). Langkah untuk ketiga klien dan pengaturan izin pemanggilan
tool ada di [docs/MCP.md](docs/MCP.md).
## Prasyarat
- Python 3.10 atau lebih baru
- Google Chrome (`google-chrome`) untuk login di jendela browser; xdotool
opsional, untuk menaikkan jendela login ke paling atas
- Akun EL-KITE LLDIKTI III yang aktif. Simpan username dan password di `.env`
dengan izin 600 agar `./elkite login` dapat mengisinya otomatis.
## Pasang
Ada dua cara, pilih salah satu.
A. Berdiri sendiri. Klona lalu jalankan; cocok kalau Anda hanya mau memakai
aplikasinya:
```bash
git clone https://github.com/alfa-yohannis/elkite-mcp.git
cd elkite-mcp
./elkite uji # jalankan pertama menyiapkan .venv + dependensi sendiri
```
Peluncur membuat virtualenv dengan `python3.14`. Kalau versi itu tidak ada di
mesin Anda, pilih interpreter lain: `ELKITE_PYTHON=python3.12 ./elkite uji`.
Letak virtualenv bisa dipindah lewat `ELKITE_VENV`. Setiap kali
`pyproject.toml` berubah, peluncur memasang ulang paketnya sendiri.
B. Sebagai paket, kalau aplikasi Anda sendiri yang akan memakainya. Paketnya
belum ada di PyPI, jadi pasang dari repositorinya:
```bash
pip install "elkite-mcp[api,mcp] @ git+https://github.com/alfa-yohannis/elkite-mcp.git"
pip install "git+https://github.com/alfa-yohannis/elkite-mcp.git" # pustaka saja
```
## Login
Halaman login EL-KITE meminta username, password, dan jawaban soal hitungan
(captcha). Simpan dua kredensial pertama di `.env`:
```dotenv
ELKITE_USERNAME=...
ELKITE_PASSWORD=...
```
`./elkite login` mengisi keduanya otomatis. Paket tidak menjawab captcha dan
tidak mengirim form; Anda cukup menjawab captcha dan menekan Login. Setelah itu
semua perintah, tool MCP, dan endpoint REST memakai sesi yang tersimpan:
```bash
./elkite login # jendela Chrome terbuka di halaman login EL-KITE
./elkite cek-login # periksa sesi yang tersimpan masih hidup
```
`./elkite login` menjalankan Google Chrome dengan profil khusus
`.elkite-browser/` langsung di halaman login, lalu menaikkan jendelanya ke
paling atas kalau xdotool terpasang. Username/password dari `.env` diisi hanya
ke tab HTTPS pada host EL-KITE dan kursor dipindahkan ke kolom captcha. Begitu
Anda menjawab captcha, menekan Login, dan tab mendarat di beranda (`/usulan`),
cookie untuk host EL-KITE disimpan ke `.elkite-sesi.json` dengan izin 600, lalu
Chrome ditutup. Batas tunggunya 10 menit.
Perintah lain memakai cookie itu. Pemeriksaan sesinya satu GET ke `/login`:
sesi yang hidup dialihkan EL-KITE ke beranda. Kalau sesi belum ada atau sudah
habis, perintah berhenti dengan pesan "Sesi EL-KITE belum ada atau sudah habis.
Jalankan ./elkite login, lalu selesaikan CAPTCHA dan tekan Login di jendela
browser yang muncul."
Tool MCP dan REST API meneruskan pesan itu apa adanya. Tidak ada yang login
ulang secara otomatis. Sesi baru dari `./elkite login` langsung terpakai tanpa
menjalankan ulang server MCP atau REST API, dan kalau EL-KITE mengganti cookie
sesinya di tengah jalan, cookie baru itu ditulis balik ke `.elkite-sesi.json`.
Kiriman yang menulis tidak pernah diulang.
`.env`, `.elkite-sesi.json`, dan `.elkite-browser/` ada di `.gitignore`. Ketiganya
memberi akses ke akun EL-KITE Anda, jadi jangan disalin ke mana-mana. Lindungi
`.env` dan `.elkite-sesi.json` dengan izin 600. Username, password, dan isi
cookie tidak pernah dicetak.
## Menjalankan
### 1. MCP
```bash
claude mcp add elkite-mcp -- /path/ke/elkite-mcp/elkite mcp # dari klona
claude mcp add elkite-mcp -- elkite-mcp # setelah pip install
```
Cek servernya hidup tanpa mendaftarkannya dulu:
```bash
./elkite mcp # bicara JSON-RPC lewat stdio; Ctrl-C untuk berhenti
```
Lalu minta asisten Anda, misalnya: "Tampilkan draf usulan JAD saya dan jelaskan
berapa angka kredit yang masih kurang." Ada 16 tool. Delapan hanya membaca:
`cek_login`, `profil_dosen`, `daftar_usulan`, `baca_draf`,
`baca_ak_penyetaraan`, `daftar_persyaratan`, `daftar_ikd`, dan
`daftar_bidang_b`. Delapan lainnya menulis ke draf dan bawaannya uji coba:
`ubah_jenjang_lama`, `ubah_jad_usulan`, `ubah_bidang_ilmu`,
`ubah_tanggal_ijazah`, `unggah_persyaratan`, `tambah_ikd`, `ubah_ikd`, dan
`tambah_bidang_b`. Rinciannya di [docs/MCP.md](docs/MCP.md).
### 2. REST API
```bash
./elkite api # http://localhost:8000
PORT=9001 ./elkite api # ganti port
uvicorn elkite_mcp.api:app --port 8000 # kalau terpasang sebagai paket
```
Dokumentasi interaktif ada di `/docs`. Semua endpoint memakai POST dengan badan
JSON. Coba:
```bash
curl -X POST localhost:8000/usulan -H 'Content-Type: application/json' -d '{}'
curl -X POST localhost:8000/draf -H 'Content-Type: application/json' \
-d '{"tab": "resume"}'
```
Untuk menempelkannya ke aplikasi FastAPI Anda sendiri:
```python
from elkite_mcp.api import router
app.include_router(router, prefix="/elkite")
```
Endpoint selengkapnya di [docs/API.md](docs/API.md).
### 3. Pustaka
```python
from elkite_mcp import KlienElkite, daftar_usulan, draf_usulan
klien = KlienElkite() # cookie dari .elkite-sesi.json; EL-KITE belum dihubungi
for baris in daftar_usulan(klien, "draft"): # sesi diperiksa di sini
print(baris.no_usulan, baris.status, baris.jad_usulan)
draf = draf_usulan(klien, tab="resume") # nomor kosong: satu-satunya draf
print(draf["resume"]["total_ak"], draf["resume"]["rekomendasi"])
```
Setelan, alamat EL-KITE, dan letak berkas sesi boleh diberikan dari kode tanpa
berkas setelan apa pun:
```python
from elkite_mcp import KlienElkite, atur_setelan
atur_setelan(batas_halaman_detik=60)
klien = KlienElkite(base_url="https://elkite.contoh-karangan.ac.id",
berkas_sesi="/path/ke/.elkite-sesi.json")
```
Login dari kode juga lewat jendela Chrome, sama seperti `./elkite login`:
```python
from elkite_mcp.login_browser import login_lewat_browser
hasil = login_lewat_browser(kabar=print) # kredensial terisi; Anda menyelesaikan captcha
print(hasil.berkas_sesi, hasil.jumlah_cookie)
```
Fungsi yang menulis bawaannya uji coba. Dengan `uji_coba=False`, form dikirim
sekali, lalu tab-nya dibaca ulang untuk verifikasi:
```python
from elkite_mcp import KlienElkite, ubah_tanggal_ijazah
klien = KlienElkite()
rencana = ubah_tanggal_ijazah(klien, "2004-07-18", jenjang="S-2")
print(rencana.sebelum, rencana.sesudah, rencana.isian, rencana.hambatan)
hasil = ubah_tanggal_ijazah(klien, "2004-07-18", jenjang="S-2", uji_coba=False)
print(hasil.berubah, hasil.sesudah, hasil.flash)
```
Fungsi baca lainnya `profil_dosen`, `baca_ak_penyetaraan`, `daftar_persyaratan`,
`daftar_ikd`, dan `daftar_bidang_b`; fungsi tulis lainnya `ubah_jenjang_lama`,
`ubah_jad_usulan`, `ubah_bidang_ilmu`, `unggah_persyaratan`, `tambah_ikd`,
`ubah_ikd`, dan `tambah_bidang_b`. Semuanya menerima `KlienElkite` sebagai
argumen pertama. Pembaca HTML (`baca_profil`, `baca_data_dasar`,
`baca_pendidikan`, `baca_ikd`, `baca_bidang_b`, `baca_persyaratan`, `baca_ak`,
`baca_resume`, `baca_log`, `baca_data_dasar_pak`, `baca_resume_pak`) menerima
HTML beserta alamatnya dan tidak menyentuh jaringan.
Semua kegagalan berupa exception biasa: `ElkiteError` beserta turunannya
(`LoginPerluManusia`, `AlamatDitolak`, `PengubahanDitolak`, `HasilTidakPasti`), dan
`KonfigurasiError` untuk setelan yang tidak bisa dipakai. Pustaka tidak pernah
memanggil `sys.exit`.
### CLI
```bash
./elkite login # kredensial terisi; selesaikan captcha; sesi disimpan
./elkite cek-login # sesi yang tersimpan masih hidup?
./elkite profil # profil dosen dan riwayat jabatan fungsional
./elkite usulan --status proses # daftar usulan JAD: draft, proses, selesai
./elkite draf --tab resume # isi tab draf usulan JAD
./elkite ak-penyetaraan <nomor> # data dasar, pendidikan, dan resume AK Penyetaraan
./elkite ubah jenjang S-2 # uji coba; --kirim untuk mengirim
./elkite persyaratan # dokumen persyaratan; `unggah` mengunggah satu
./elkite ikd # butir IKD; `tambah` dan `ubah` mengisi kegiatan
./elkite bidang-b --butir B0006 # butir Bidang B dan form TAMBAH butir itu
./elkite petakan <path> # form, tabel, tautan, endpoint skrip
```
Terpasang sebagai paket, perintahnya `elkite-login`, `elkite-cek-login`, `elkite-profil`,
`elkite-usulan`, `elkite-draf`, `elkite-ak-penyetaraan`, `elkite-ubah`,
`elkite-persyaratan`, `elkite-ikd`, `elkite-bidang-b`, `elkite-petakan`, dan
`elkite-mcp`, dengan argumen yang sama. Argumen selengkapnya di
[docs/CLI.md](docs/CLI.md).
## Mengubah draf usulan
| Yang dikerjakan | CLI | Tool MCP | Tujuan POST di EL-KITE |
|-------------------------------------------------|-------------------------------------------------|-----------------------|----------------------------------------------------|
| ubah jenjang pendidikan pada jabatan sebelumnya | `./elkite ubah jenjang` | `ubah_jenjang_lama` | `/usulan/usulan/update_jenjang` |
| ubah JAD usulan | `./elkite ubah jad` | `ubah_jad_usulan` | `/usulan/usulan/update_jad` |
| ubah bidang ilmu | `./elkite ubah bidang-ilmu` | `ubah_bidang_ilmu` | `/usulan/usulan/update_bid_ilmu` |
| ubah tanggal ijazah di draf JAD | `./elkite ubah tanggal-ijazah` | `ubah_tanggal_ijazah` | `/usulan/usulan/update_tgl_ijazah/<dosen_no>` |
| ubah tanggal ijazah di AK Penyetaraan | `./elkite ubah tanggal-ijazah --ak-penyetaraan` | `ubah_tanggal_ijazah` | `/usulan/pak/update_tgl_ijazah` |
| unggah dokumen persyaratan | `./elkite persyaratan unggah` | `unggah_persyaratan` | `/usulan/usulan/tambah_syarat` |
| tambah kegiatan IKD | `./elkite ikd tambah` | `tambah_ikd` | `/usulan/usulan/simpan_ikd` |
| ubah kegiatan IKD | `./elkite ikd ubah` | `ubah_ikd` | `/usulan/usulan/update_ikd` |
| tambah entri butir Bidang B | `./elkite bidang-b tambah` | `tambah_bidang_b` | `/usulan/usulan_dupak_b/tambah_B0004/<kode>/<kum>` |
Semuanya mengikuti alur yang sama:
1. Tab yang memuat form-nya dibaca lebih dulu. Form dicari menurut action-nya,
dan form yang memuat isian tak dikenal ditolak sebelum apa pun dikirim. Kode
yang dibutuhkan form (jenis dokumen, `id_detail_dharma`, id kegiatan, AK)
diambil dari tombol dan isian di halaman; kalau tombolnya tidak ada, kiriman
sungguhan ditolak.
2. Nilai baru diperiksa terhadap pilihan sah di form itu, dan nilai yang sudah
sama tidak dikirim ulang. Berkas harus PDF (berakhiran `.pdf` dan berawalan
`%PDF`) dan tidak melebihi batas yang tertulis di form: 10 MB untuk
Persyaratan dan IKD, 5 MB untuk Bidang B.
3. Tanpa `--kirim` di CLI, atau selama `uji_coba` dibiarkan pada bawaannya di
pustaka, MCP, dan REST API, hasilnya rencana kiriman: tujuan form, isian yang
akan dikirim, nilai sebelum dan sesudah, catatan, dan `hambatan`. Rencana
yang membawa `hambatan` ditolak kalau dikirim sungguhan.
4. Berkas atau entri yang sudah ada tidak ditimpa kecuali dengan `--paksa` di
CLI atau `paksa` bernilai true. `tambah_ikd` tetap menolak butir yang sudah
berisi kegiatan, karena EL-KITE hanya menerima satu kegiatan per butir dan
satu kegiatan per dharma; untuk mengganti kegiatan yang ada pakai `ubah_ikd`.
5. Kiriman sungguhan berangkat tepat sekali sebagai multipart/form-data, sama
seperti tombol di browser, lalu tab-nya dibaca ulang. Berkas yang diunggah
diunduh ulang dari tautannya di halaman dan md5-nya dicocokkan dengan berkas
lokal. Kalau tautan itu membalas halaman kecil berisi `<iframe>` (tombol
"Lihat Bukti" IKD begitu), berkas yang disematkannya yang diunduh.
`berubah` bernilai true hanya kalau semuanya cocok; kalau tidak, kode keluar
CLI bernilai 1 dan `ok` di MCP serta REST API bernilai false.
6. Kalau kiriman sudah berangkat tetapi hasilnya tidak bisa dipastikan
(EL-KITE membalas galat, sesinya ternyata habis, atau pengalihannya mengarah
ke alamat yang tidak dibuka klien), yang muncul `HasilTidakPasti`. Kiriman
tidak pernah diulang otomatis; baca ulang tab-nya sebelum mencoba lagi. Kalau
penyebabnya sesi habis, jalankan `./elkite login` dulu.
Unggahan dan entri yang sudah terkirim tidak bisa dihapus lewat paket ini; yang
keliru harus dihapus sendiri di browser. Tanggal ijazah draf JAD dan AK
Penyetaraan dikirim ke endpoint yang berbeda. Pada akun yang diamati keduanya
menunjuk baris riwayat pendidikan yang sama, jadi tanggal yang diubah lewat draf
JAD langsung terbaca juga di AK Penyetaraan; tetap baca ulang keduanya sesudah
mengubah. Pada draf yang diamati per Oktober 2026, kolom Aksi tab Persyaratan
kosong untuk semua dokumen, sehingga `unggah_persyaratan` menolak mengirim
sampai EL-KITE menampilkan tombol unggahnya.
## Setelan
Semua setelan opsional. Yang menang, dari yang paling khusus:
```
argumen pemanggil -> atur_setelan() -> environment -> .env -> elkite.yaml -> nilai bawaan
```
Di environment dan `.env` namanya berawalan `ELKITE_` dan huruf besar
(`ELKITE_BASE_URL`); di `elkite.yaml` tanpa awalan dan huruf kecil (`base_url`).
Salin `elkite.yaml.contoh` menjadi `elkite.yaml` kalau perlu mengubah sesuatu.
`elkite.yaml` dibaca sekali per proses, jadi jalankan ulang server MCP atau REST
API sesudah mengubahnya.
| `elkite.yaml` | Bawaan | Keterangan |
|-----------------------|-------------------------------------------------------|------------------------------------------------------------------------|
| `website` | `https://elkite-lldikti3.kemdiktisaintek.go.id/login` | halaman login; alamat EL-KITE diturunkan darinya |
| `base_url` | `https://elkite-lldikti3.kemdiktisaintek.go.id` | alamat EL-KITE, kalau perlu disebut terpisah |
| `no_usulan` | kosong | nomor draf JAD kalau tidak disebut; kosong berarti satu-satunya draf |
| `no_ak_penyetaraan` | kosong | nomor AK Penyetaraan kalau tidak disebut |
| `batas_login_detik` | `60` | pemeriksaan sesi (GET `/login`) |
| `batas_halaman_detik` | `120` | permintaan lainnya |
| `maks_pengalihan` | `10` | pengalihan yang diikuti per permintaan |
| `user_agent` | User-Agent Chrome saat `./elkite login` | User-Agent permintaan HTTP; tanpa berkas sesi, Chrome 126 di X11 |
Khusus `./elkite login`, username dan password dibaca dari environment atau
`.env` sebagai `ELKITE_USERNAME` dan `ELKITE_PASSWORD`; keduanya tidak diterima
dari `elkite.yaml`, CLI, REST API, atau parameter tool MCP. Alamat EL-KITE dan
halaman login harus https dan berada di host yang sama. Nomor usulan milik satu
akun, jadi kalau `elkite.yaml` dibagikan, simpan nomornya di `.env`
(`ELKITE_NO_USULAN`, `ELKITE_NO_AK_PENYETARAAN`).
Peluncur `./elkite` membaca tiga variabel lain dari environment: `ELKITE_PYTHON`
(interpreter pembuat virtualenv, bawaan `python3.14`), `ELKITE_VENV` (letak
virtualenv, bawaan `.venv` di repositori), dan `PORT` (port `./elkite api`,
bawaan 8000).
## Akar proyek
Akar proyek menentukan letak `.elkite-sesi.json`, `.elkite-browser/`, `.env`,
`elkite.yaml`, dan `data/`, serta titik hitung path relatif, termasuk path berkas
PDF yang akan diunggah:
- dijalankan dari klona repositori ini: direktori repositori itu sendiri;
peluncur `./elkite` menyetelnya lewat `ELKITE_AKAR_PROYEK`
- dipasang sebagai paket: direktori kerja proyek pemakai
Direktori di atasnya tidak pernah ikut ditelusuri, jadi hasilnya tidak berubah
hanya karena aplikasi dipindah. Untuk menentukan sendiri, setel
`ELKITE_AKAR_PROYEK` di environment atau panggil `atur_akar_proyek()`.
## Rancangan
```
pyproject.toml metadata paket, dependensi, dan perintah terpasang
elkite peluncur untuk pemakaian berdiri sendiri
.env.contoh contoh kredensial login dan setelan pribadi
elkite.yaml.contoh contoh setelan yang aman dibagikan
elkite_mcp/__init__.py API publik paket ini
elkite_mcp/konfigurasi.py akar proyek, setelan, letak berkas sesi, sesi HTTP
elkite_mcp/sesi.py berkas sesi: simpan (izin 600), muat, dan pasang cookie
elkite_mcp/login_browser.py ./elkite login: Chrome berprofil khusus, pantauan CDP, simpan cookie
elkite_mcp/elkite_client.py KlienElkite: sesi dari berkas, pemeriksaan sesi, pagar GET dan POST, kiriman tepat sekali
elkite_mcp/halaman.py pembaca HTML umum: teks, pasangan label-nilai, tabel, form, pesan flash (tanpa jaringan)
elkite_mcp/privasi.py penyaring data pribadi sebelum keluar lewat CLI, REST API, atau MCP
elkite_mcp/profil.py profil dosen dan riwayat jabatan fungsional
elkite_mcp/usulan.py daftar usulan JAD dan pembaca kedelapan tab draf
elkite_mcp/ak_penyetaraan.py data dasar, riwayat pendidikan, dan resume AK Penyetaraan
elkite_mcp/ubah.py pengubah isian draf beserta bantuan bersama alat penulis
elkite_mcp/unggah.py unggahan Persyaratan, IKD, dan Bidang B
elkite_mcp/cli.py CLI (elkite-login, elkite-cek-login, elkite-draf, elkite-ubah, dan seterusnya)
elkite_mcp/api.py REST API: router yang bisa ditempel + app siap pakai
elkite_mcp/mcp_server.py MCP server (perintah elkite-mcp)
elkite_mcp/petakan_form.py alat bantu memetakan halaman saat menambah kemampuan
tests/ unit test dengan EL-KITE dan Chrome tiruan, tanpa jaringan
docs/ referensi CLI, REST API, dan MCP
```
## Catatan perilaku EL-KITE
Diamati pada EL-KITE asli pada Oktober 2026. Nama isian form ubah data, kode
tombol, dan pilihan `<select>` dibaca ulang dari halaman setiap kali dipakai;
yang tertulis di kode hanya path halaman dan tujuan POST yang diizinkan.
- EL-KITE adalah aplikasi CodeIgniter. Alamatnya dipetakan langsung ke method
controller, jadi membuka sebuah alamat dengan GET sudah bisa menjalankan
aksinya. Karena itu klien menolak GET ke alamat yang tampak seperti aksi.
- `GET /login` berisi form POST ke `/login/login` dengan isian `username`,
`password`, dan `captcha_answer`, dengan soal hitungan di labelnya (misalnya
"Berapa hasil dari 8 + 7 ?"). Paket ini tidak mengisi form itu dan tidak
pernah mengirim apa pun ke `/login/login`; yang login selalu pemakai sendiri
di browser. Login yang berhasil dibalas 303 ke `/usulan/usulan`, dan sesi yang
masih hidup membuat `GET /login` dialihkan ke beranda. Pengalihan itulah yang
dipakai untuk memeriksa sesi tersimpan.
- Sesinya cookie `ci_session` CodeIgniter. Cookie yang diganti EL-KITE di tengah
jalan ikut disimpan ulang ke `.elkite-sesi.json`.
- Beranda juga memuat form berisian password, yaitu modal Ganti Password, tetapi
tanpa captcha. Sebuah halaman dianggap halaman login hanya kalau isian
password dan captcha ada di form yang sama.
- Rantai sertifikat TLS-nya dikirim lengkap: Sectigo Public Server
Authentication CA DV R36, lalu Root R46 yang ditandatangani silang USERTrust
RSA. Bundel CA bawaan requests sudah cukup.
- Templatnya admin Bootstrap. Isi halaman ada di `.page-wrapper` dan menu di
`aside.left-sidebar`. Halaman detail menampilkan data sebagai baris
`form-group` berisian readonly atau sebagai tabel "label : nilai", kadang dua
pasangan per baris.
- Form ubah data ada di modal Bootstrap yang dibuka tombol bertanda
`data-target="#id-modal"`, dan dikirim sebagai multipart/form-data. Form
tanggal ijazah dan unggah persyaratan justru membungkus modalnya.
- Pesan sesudah kiriman form dititipkan di atribut `data-flashdata`,
`data-flasherror`, dan `data-flashwarning`, lalu ditampilkan skrip halaman.
Pembacanya mencatat ketiganya sebagai `berhasil`, `galat`, dan `peringatan`.
- Daftar usulan JAD dimuat DataTables lewat `POST /usulan/usulan/usulan/<kode>`,
dengan kode 1 untuk Draft, 99 untuk Proses, dan 9 untuk Selesai. Kolom
pertamanya memuat tombol Detail Ajuan dan Hapus; hanya tautan Detail Ajuan
yang dipakai. Klien meminta 100 baris pertama per status.
- Nomor usulan JAD terdiri atas 14 digit waktu pembuatan diikuti `dosen_no`.
Nomor AK Penyetaraan sebaliknya: `dosen_no` diikuti enam digit jam pembuatan.
- Draf usulan JAD punya delapan tab di `/usulan/usulan/<tab>/<no_usulan>`:
`usulans` (Data Dasar), `show_pendidikan` (Riwayat Pendidikan), `ikd`,
`bidangB`, `persyaratan`, `show_skp` (AK Integrasi/Penyetaraan, Konversi &
Prestasi), `show_resume`, dan `show_log`.
- Tab Data Dasar memuat form `update_jenjang` (`no_usulan`, `jenjang_id_lama`),
`update_jad` (`no_usulan`, `jabatan_usulan_no`), dan `update_bid_ilmu`
(`dosen_no`, `no_usulan`, `sta`, `bid_ilmu`). Pada draf yang diamati, pilihan
jenjangnya 30 S-1, 32 Sp-1, 35 S-2, 36 S-2 Terapan, 37 Sp-2, 40 S-3, dan 41
S-3 Terapan, sedangkan pilihan JAD-nya 4 Lektor (200), 5 Lektor (300), 8
Lektor Kepala (400), 9 Lektor Kepala (550), dan 10 Lektor Kepala (700). Label
opsinya diawali ikon, yang dibuang saat dibaca.
- Di tab Riwayat Pendidikan, tanggal penerbitan ijazah tampil di isian readonly,
dan tombol "Ubah data" membuka modal `#edit_tgl<id_rwy_didik_formal>`. Form
draf JAD menuju `/usulan/usulan/update_tgl_ijazah/<dosen_no>` (isian
`id_rwy_didik_formal`, `no_usulan`, `tgl_ijazah`), sedangkan AK Penyetaraan
memakai `/usulan/pak/update_tgl_ijazah` dengan tambahan `id_sdm`. Keduanya
endpoint terpisah, tetapi `id_rwy_didik_formal`-nya sama, dan tanggal yang
diubah lewat salah satunya terbaca di keduanya.
- Menu AK Penyetaraan dibuka lewat `/usulan/pak/pak_baru`, dan halaman itu
membuat Draft AK Penyetaraan baru. Klien tidak pernah membukanya, jadi nomor
AK Penyetaraan harus disebut sendiri (salin dari alamat
`/usulan/pak/detail/<nomor>` di browser) atau disimpan di
`ELKITE_NO_AK_PENYETARAAN`. Tab yang dibaca adalah `detail`,
`show_pendidikan`, dan `show_resume` di bawah `/usulan/pak/`. Tombol "Buat
Usulan Baru" menuju `/usulan/usulan/tambah`, yang juga tidak pernah dibuka.
- Tab Persyaratan punya form unggah `POST /usulan/usulan/tambah_syarat` berisi
`jenis`, `no_usulan`, dan `berkas` (PDF, maks. 10 MB). Kode `jenis` dikirim
tombol unggah lewat atribut `data-nama`. Pada draf yang diamati, kolom Aksi
kosong untuk semua dokumen, jadi tombol dan kodenya belum ada, dan alat
unggah menolak menebak.
- Di tab IKD, tombol Tambah tiap butir membawa `data-id` (`id_detail_dharma`)
dan `data-sudah-ada="1"` kalau butir itu, atau butir lain di dharma yang sama,
sudah terisi: EL-KITE hanya menerima satu kegiatan per dharma ("pilih salah
satu dari setiap Dharma"). Tombol "Lihat Bukti" membalas halaman kecil berisi
`<iframe>` ke `/assets/download_file_ikd/<berkas>`. Form tambahnya
`POST /usulan/usulan/simpan_ikd` (`usulan_no`, `id_detail_dharma`, `ket_ikd`,
`file_bukti_ikd` PDF maks. 10 MB). Ubahnya lewat `/usulan/usulan/update_ikd`
(`id_ikd`, `usulan_no`, `file_lama_ikd`, `ket_ikd`, dan `file_bukti_ikd` yang
opsional).
- Halaman butir Bidang B ada di `/usulan/usulan_dupak_b/dupak/<kode>/<no_usulan>`.
Form TAMBAH-nya `POST /usulan/usulan_dupak_b/tambah_B0004/<kode>/<kum>`
dengan isian `semester`, `thn_akademik`, `jp`, `pk`, `tgl`, `satuan_hasil`,
`syarat`, `uraian`, `url_web`, `url`, `keterangan`, dan `berkas` (PDF maks.
5 MB), ditambah isian tersembunyi `no_usulan`, `kum` (AK), dan `volum`. Butir
B0004 juga punya `k` dan `kategori_jurnal`. Untuk usulan Lektor, satu poin
hanya berisi satu data. Paket ini tidak bisa menghapus entri.
- Halaman Profil (`/usulan/profil`) memuat identitas dalam tabel "label : nilai"
dua pasangan per baris, kartu "Perhatian !" tentang kelayakan mengajukan JAD
berikutnya, kartu Sinta Score dan H Index, jabatan fungsional dan pangkat
saat ini beserta riwayatnya, dan riwayat pendidikan.
- Aksi yang tidak pernah dipanggil paket ini: form login (`/login/login`),
`proses_usulanbaru` (mengajukan usulan, `usulan_status` 10),
`update_statusjad`, `ganti_password`, apa pun
yang memuat `hapus` atau `delete`, `logout`, `pak_baru`, serta tombol sinkron
SISTER/PDDIKTI dan ubah foto di Profil. Pagar klien menolak semuanya sebelum
dikirim.
## Keamanan dan batasan
- Verifikasi sertifikat TLS selalu aktif dan tidak ada setelan untuk
mematikannya. Uji memastikan `verify=False` tidak muncul di kode paket.
- Setiap permintaan dibatasi ke host EL-KITE lewat https. Alamat lain, alamat
`http://`, dan pengalihan ke host lain ditolak sebelum dikirim.
- GET hanya membaca. Alamat yang path atau query-nya memuat potongan kata aksi
ditolak, juga pada setiap langkah pengalihan. Daftarnya
`POTONGAN_GET_DILARANG` di `elkite_client.py`, antara lain `hapus`, `delete`,
`logout`, `pak_baru`, `tambah`, `update`, `upload`, `unggah`, `simpan`,
`sinkron`, `kirim`, `ajukan`, `batal`, dan `submit`.
- POST hanya ke path terdaftar tanpa query: tiga tabel DataTables yang hanya
membaca (`PATH_POST_BACA`) dan form ubah data yang masing-masing punya alat
beruji coba (`PATH_POST_TULIS`, `POLA_POST_TULIS`). Path yang memuat `login`,
`proses_usulan`, `usulanbaru`, `statusjad`, `ganti_password`, `hapus`,
`delete`, `logout`, atau `pak_baru` selalu ditolak, juga kalau kelak ada yang
menambahkannya ke daftar, dan juga pada lapisan permintaan terbawah.
- Kiriman ubah data berangkat tepat sekali dan tidak diulang, juga saat sesinya
ternyata habis. Pengalihan sesudahnya diikuti sebagai GET selama masih di
EL-KITE; pengalihan 307/308 yang meminta POST diulang tidak diikuti.
- Tidak ada alat untuk mengajukan usulan, mengubah status, menghapus, mengganti
password, logout, atau membuka menu AK Penyetaraan. Unggahan dan entri yang
sudah terkirim tidak bisa dihapus lewat paket ini.
- Username dan password hanya dibaca dari environment atau `.env`, lalu dikirim
ke dua kolom pada tab login HTTPS di host EL-KITE. Nilainya tidak dicetak dan
tidak diterima oleh CLI, REST API, atau tool MCP. Paket tidak menjawab captcha
dan tidak mengirim form login. Isi cookie sesi tidak muncul di `repr()`, pesan
galat, maupun keluaran CLI; `.elkite-sesi.json` selalu ditulis dengan izin 600.
- `./elkite login` memakai `Target.getTargets` untuk memastikan tab yang benar,
`Target.attachToTarget` dan `Runtime.evaluate` untuk mengisi dua kredensial,
lalu `Target.detachFromTarget`, `Storage.getCookies`, `Browser.getVersion`, dan
`Browser.close`. Skrip browser hanya mengisi dua input, memicu event input dan
change, serta memfokuskan kolom captcha; tidak ada jawaban captcha, klik, atau
pengiriman form. Port DevTools dipilih Chrome sendiri dan hanya mendengarkan
`127.0.0.1`, tetapi selama jendela login terbuka, program lain di mesin yang
sama bisa menyambung ke sana. Jangan menjalankannya di mesin bersama.
- Nilai berlabel data pribadi (tempat dan tanggal lahir, NIK, NPWP, nomor HP,
alamat, rekening) ditulis `(disembunyikan)` saat halaman dibaca, sebelum keluar
lewat CLI, REST API, atau MCP. Penyaringnya mencocokkan kata pada label
(`KATA_PRIBADI` di `privasi.py`).
- REST API tidak punya autentikasi sendiri dan memakai sesi EL-KITE yang
tersimpan di mesin server, jadi siapa pun yang bisa menghubunginya bertindak
atas nama akun itu. uvicorn bawaannya hanya mendengarkan `127.0.0.1`; jangan
membukanya ke jaringan lain tanpa pelindung di depannya.
- Berkas biner seperti PDF ditolak pembaca halaman. Unduhan hanya dipakai untuk
mencocokkan md5 berkas yang baru diunggah.
- `data/`, `.elkite-sesi.json`, dan `.elkite-browser/` sudah masuk
`.gitignore`. `./elkite petakan --html` menyimpan HTML mentah yang memuat data
pribadi di `data/`.
## Menambah kemampuan baru
```bash
./elkite login # 1. kredensial terisi; selesaikan captcha kalau sesi belum ada
./elkite petakan <path> # 2. petakan halamannya ke data/form_<path>.json
./elkite petakan <path> --html # simpan juga HTML mentahnya untuk dibaca
# 3. tulis pembaca HTML tanpa jaringan (baca_*) beserta dataclass hasilnya
# 4. tiru struktur halamannya di tests/halaman_tiruan.py dengan data karangan,
# lalu tulis ujinya
# 5. daftarkan di cli.py, api.py, dan mcp_server.py
```
Path halaman diambil dari alamat di browser. `./elkite petakan` hanya melakukan
GET lewat pagar yang sama dengan alat lain, jadi alamat yang memuat kata aksi
ditolak. Nilai isian password dan isian berlabel data pribadi ditulis
`<disamarkan>`, tetapi HTML mentah dari `--html` tidak disaring. Keduanya
tersimpan di `data/`.
Alat yang menulis mengikuti pola `ubah.py`. Ia menerima `uji_coba` yang
bawaannya True dan mengembalikan `HasilUbah` berisi rencana kiriman. Form-nya
dibaca dari halaman pada sesi yang sama, dan isian yang tidak dikenal ditolak.
Tujuan POST-nya ditambahkan ke `PATH_POST_TULIS` atau `POLA_POST_TULIS` di
`elkite_client.py`, kirimannya lewat `kirim_sekali()`, lalu tab-nya dibaca ulang
untuk verifikasi. Di CLI, kiriman sungguhan butuh `--kirim`, dan instruksi
MCP-nya meminta asisten menjalankan uji coba lebih dulu lalu meminta
persetujuan pemakai. Path yang memuat potongan di `POTONGAN_POST_DILARANG` tetap
ditolak walau sudah didaftarkan.
## Konvensi penulisan skrip
- Satu modul untuk satu menu EL-KITE (`profil.py`, `usulan.py`,
`ak_penyetaraan.py`); pengubah dan pengunggah dipisah di `ubah.py` dan
`unggah.py`.
- Pembaca HTML (`baca_*`) tidak menyentuh jaringan dan menghasilkan dataclass;
fungsi yang mengambil halaman memanggil pembaca itu. CLI, REST API, dan MCP
memakai bentuk dict dari dataclass yang sama.
- Setiap kelas, method, dan fungsi punya docstring.
- Fungsi ditulis datar, tanpa fungsi bersarang di dalam fungsi.
- Nama variabel dan fungsi ditulis utuh dan deskriptif (`sesi_http`,
`alamat_kirim`), tanpa singkatan satu-dua huruf.
- Komentar hanya untuk hal yang tidak terbaca dari kodenya.
- Nilai rahasia dan data pribadi tidak pernah dicetak.
## Pengujian
```bash
./elkite uji
```
237 unit test, semuanya berjalan tanpa jaringan, tanpa akun, dan tanpa
membuka browser. EL-KITE digantikan `SitusTiruan` di `tests/conftest.py`, yang
menjawab seperti aplikasi CodeIgniter aslinya: sesi lewat cookie `ci_session`,
pengalihan ke `/login` saat sesi habis, DataTables lewat POST, form ubah data di
modal, dan berkas unggahan yang bisa diunduh ulang untuk dicocokkan md5-nya.
Sesi yang sudah login diberikan lewat berkas sesi karangan. Halaman tiruannya
ada di `tests/halaman_tiruan.py`.
`./elkite login` diuji dengan Chrome, xdotool, dan DevTools tiruan: argumen
peluncurnya, pembacaan DevToolsActivePort, pembatasan pengisian kredensial ke tab
login HTTPS pada host EL-KITE (termasuk menunggu tab selesai dimuat), tidak
adanya jawaban captcha atau pengiriman form, deteksi beranda, penyaringan cookie,
izin berkas sesi, dan penutupan Chrome saat waktu habis. Satu uji memakai klien
websockets sungguhan ke server CDP tiruan di `127.0.0.1`. Ada juga uji yang
memastikan tidak satu pun antarmuka HTTP mengirim apa pun ke `/login/login`.
Selama uji, akar proyek dipindah ke direktori sementara supaya `.env` dan
`.elkite-sesi.json` asli tidak terbaca, variabel `ELKITE_*` di environment
dibuang, sesi HTTP sungguhan diblokir, dan `subprocess.Popen` diblokir sehingga
Chrome atau program lain tidak bisa jalan. Uji REST API dan MCP dilewati kalau
FastAPI, httpx2, atau paket mcp belum terpasang. Nama dosen, NIDN, nomor
usulan, perguruan tinggi, tanggal, dan nilai cookie di berkas uji dan
dokumentasi seluruhnya dikarang.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo recent community issues