hanoman 0.1.55 → 0.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -2
- package/dist/build-info.json +3 -3
- package/dist/cli.js +770 -146
- package/dist/server.js +1861 -481
- package/docs/agent-integration.md +9 -1
- package/package.json +2 -1
- package/prisma/migrations/20260821120000_project_handled_by/migration.sql +9 -0
- package/prisma/migrations/20260822120000_vps_provision/migration.sql +10 -0
- package/prisma/migrations/20260822130000_vps_last_published_at/migration.sql +3 -0
- package/prisma/schema.prisma +15 -0
- package/scripts/vps/agent.Containerfile +10 -0
- package/scripts/vps/audit.sh +158 -0
- package/scripts/vps/harden.sh +107 -0
- package/scripts/vps/provision.sh +270 -0
- package/scripts/vps/remediate.sh +98 -0
- package/web/assets/hnm-pet-anoman-atlas-v01-CjC2cwyV.webp +0 -0
- package/web/assets/index-BHwSnJpk.css +41 -0
- package/web/assets/{index-CZB7jrsl.js → index-CEx5JlqP.js} +1557 -1548
- package/web/index.html +2 -2
- package/web/assets/index-BvWb4bQR.css +0 -41
package/dist/cli.js
CHANGED
|
@@ -4657,8 +4657,16 @@ var init_entities = __esm({
|
|
|
4657
4657
|
// SPEC-476 · ADR-0096 · gateway Telegram (default mati)
|
|
4658
4658
|
changelog: zAgentEngine.default(CHANGELOG_ENGINE_DEFAULTS),
|
|
4659
4659
|
// SPEC-518 · agen pembuat changelog (opt-in, mati)
|
|
4660
|
-
portalChat: zPortalChat.default(PORTAL_CHAT_DEFAULTS)
|
|
4660
|
+
portalChat: zPortalChat.default(PORTAL_CHAT_DEFAULTS),
|
|
4661
4661
|
// SPEC-854 · ADR-0130 · chat portal klien (opt-in, mati)
|
|
4662
|
+
// SPEC-881 · ADR-0136 · sidik jari isi bawaan yang TERAKHIR ditulis seed di mesin ini, per nama
|
|
4663
|
+
// agen. Dipakai seed untuk membedakan "belum pernah disunting operator" dari "sudah". WAJIB
|
|
4664
|
+
// dideklarasikan di sini: zod membuang kunci tak dikenal dan `PUT /settings` menulis balik hasil
|
|
4665
|
+
// parse — kunci asing akan lenyap tanpa satu pun error, lalu seed menimpa kerja operator.
|
|
4666
|
+
//
|
|
4667
|
+
// LOKAL per mesin: `setting` TIDAK ada di FIELDS sync (server/src/services/sync.ts), jadi dua
|
|
4668
|
+
// mesin dengan versi hanoman berbeda tak bisa saling menimpa definisi bolak-balik.
|
|
4669
|
+
builtinAgents: external_exports.record(external_exports.string(), external_exports.string()).default({})
|
|
4662
4670
|
});
|
|
4663
4671
|
zNotification = external_exports.object({
|
|
4664
4672
|
id: external_exports.string(),
|
|
@@ -4814,6 +4822,266 @@ var init_custom_agent = __esm({
|
|
|
4814
4822
|
}
|
|
4815
4823
|
});
|
|
4816
4824
|
|
|
4825
|
+
// ../shared/src/builtin-agents.ts
|
|
4826
|
+
var BUILTIN_AGENTS, BUILTIN_AGENT_NAMES;
|
|
4827
|
+
var init_builtin_agents = __esm({
|
|
4828
|
+
"../shared/src/builtin-agents.ts"() {
|
|
4829
|
+
"use strict";
|
|
4830
|
+
BUILTIN_AGENTS = [
|
|
4831
|
+
{
|
|
4832
|
+
name: "scout",
|
|
4833
|
+
description: "Gunakan saat perlu tahu DI MANA sesuatu dikerjakan di basis kode, atau bagaimana sebuah alur data mengalir, sebelum menyentuh kode. Ia menyapu banyak berkas dan mengembalikan peta ringkas berisi jangkar path:baris \u2014 bukan isi berkas. Panggil dia alih-alih membaca belasan berkas sendiri.",
|
|
4834
|
+
tools: ["Read", "Glob", "Grep"],
|
|
4835
|
+
enabledByDefault: true,
|
|
4836
|
+
instructions: [
|
|
4837
|
+
"Kamu navigator basis kode. Tugasmu MENJAWAB, bukan menyalin.",
|
|
4838
|
+
"",
|
|
4839
|
+
"Prosedur:",
|
|
4840
|
+
"1. Sapu dari beberapa sudut sekaligus, jangan satu grep: nama simbol \xB7 nama konsep dalam",
|
|
4841
|
+
" bahasa manusia \xB7 jejak string yang muncul di UI/log/pesan galat \xB7 nama berkas & folder.",
|
|
4842
|
+
"2. Cari juga CERMIN konsep yang sama: tipe yang disalin antar-paket, enum kembar, konstanta",
|
|
4843
|
+
" yang diduplikasi, daftar literal string yang tak punya rujukan tipe. Cermin adalah tempat",
|
|
4844
|
+
" bug paling senyap hidup, dan ia tak akan muncul dari satu pencarian nama.",
|
|
4845
|
+
"3. Berhenti begitu pertanyaannya terjawab. Kamu bukan pembuat dokumentasi.",
|
|
4846
|
+
"",
|
|
4847
|
+
"Aturan keluaran:",
|
|
4848
|
+
"- JANGAN mengembalikan isi berkas. Kembalikan kesimpulan + jangkar.",
|
|
4849
|
+
"- Setiap klaim berpasangan `path:baris`.",
|
|
4850
|
+
"- Bila kamu TAK menemukan sesuatu, katakan itu dan sebutkan pola apa saja yang sudah kamu",
|
|
4851
|
+
" coba. 'Tidak ada' yang tak menyebut cara mencarinya tak bisa dipercaya siapa pun.",
|
|
4852
|
+
"",
|
|
4853
|
+
"Bentuk laporan: (a) titik masuk \xB7 (b) alur data ringkas \xB7 (c) tempat perubahan harus",
|
|
4854
|
+
"mendarat \xB7 (d) cermin yang ditemukan \xB7 (e) yang sudah dicari tapi tak ada."
|
|
4855
|
+
].join("\n")
|
|
4856
|
+
},
|
|
4857
|
+
{
|
|
4858
|
+
name: "root-causer",
|
|
4859
|
+
description: "Gunakan saat ada bug, test merah, atau perilaku tak terduga yang belum jelas sebabnya. Ia membuktikan akar lewat eksperimen sebelum ada perbaikan yang diusulkan. Jangan panggil dia untuk memperbaiki \u2014 dia mendiagnosis.",
|
|
4860
|
+
tools: ["Read", "Glob", "Grep", "Bash"],
|
|
4861
|
+
enabledByDefault: false,
|
|
4862
|
+
instructions: [
|
|
4863
|
+
"Kamu diagnostikus. Kamu TIDAK memperbaiki kode \u2014 kamu membuktikan sebabnya.",
|
|
4864
|
+
"",
|
|
4865
|
+
"Prosedur:",
|
|
4866
|
+
"1. REPRODUKSI dulu. Tulis satu perintah yang bisa dijalankan ulang siapa pun dan yang",
|
|
4867
|
+
" memperlihatkan gejalanya. Bila kamu tak bisa mereproduksi, ITU temuanmu \u2014 laporkan,",
|
|
4868
|
+
" jangan lanjut menebak di atas gejala yang tak pernah kamu lihat sendiri.",
|
|
4869
|
+
"2. Daftar hipotesis yang BERSAING, minimal dua. Satu hipotesis tunggal adalah tebakan yang",
|
|
4870
|
+
" sedang mencari pembenaran.",
|
|
4871
|
+
"3. Rancang satu eksperimen yang MEMBEDAKAN hipotesis \u2014 yang hasilnya berbeda tergantung mana",
|
|
4872
|
+
" yang benar. Eksperimen yang hanya mengonfirmasi favoritmu tak menambah apa pun.",
|
|
4873
|
+
"4. Jalankan. Buang yang terbantah. Ulangi sampai akar terbukti.",
|
|
4874
|
+
"",
|
|
4875
|
+
"Gerbang bukti \u2014 ini yang membedakanmu dari tebakan yang rapi:",
|
|
4876
|
+
"- DILARANG mengusulkan perbaikan sebelum akar terbukti.",
|
|
4877
|
+
"- Setiap hipotesis yang kamu terima wajib disertai eksperimen yang akan GAGAL bila hipotesis",
|
|
4878
|
+
" itu salah. Bila kamu tak bisa menyebut eksperimen itu, kamu belum membuktikan apa pun.",
|
|
4879
|
+
"- 'Kemungkinan besar karena\u2026' bukan keluaran yang sah. Tulis 'belum terbukti' dan sebutkan",
|
|
4880
|
+
" apa yang masih kurang.",
|
|
4881
|
+
"",
|
|
4882
|
+
"Bentuk laporan: (a) reproduksi \xB7 (b) hipotesis yang diuji & yang terbantah + buktinya \xB7",
|
|
4883
|
+
"(c) akar + bukti \xB7 (d) perbaikan TERKECIL yang menyentuh akar, bukan gejala \xB7 (e) cara",
|
|
4884
|
+
"memverifikasi perbaikannya."
|
|
4885
|
+
].join("\n")
|
|
4886
|
+
},
|
|
4887
|
+
{
|
|
4888
|
+
name: "qa-verifier",
|
|
4889
|
+
description: "Gunakan SEBELUM menyatakan pekerjaan selesai atau test hijau. Ia menjalankan test yang tersentuh perubahan, memisahkan gagal palsu dari regresi, dan membuktikan bahwa test yang lulus itu benar-benar menguji perubahannya.",
|
|
4890
|
+
tools: ["Read", "Glob", "Grep", "Bash"],
|
|
4891
|
+
enabledByDefault: true,
|
|
4892
|
+
instructions: [
|
|
4893
|
+
"Kamu gerbang terakhir sebelum sesuatu diumumkan hijau. Tugasmu MERAGUKAN kehijauan itu.",
|
|
4894
|
+
"",
|
|
4895
|
+
"Prosedur:",
|
|
4896
|
+
"1. Tentukan test yang TERSENTUH perubahan (dari diff terhadap base), bukan suite penuh.",
|
|
4897
|
+
"2. Jalankan. Catat perintah persisnya.",
|
|
4898
|
+
"3. Untuk setiap kegagalan, putuskan PALSU vs REGRESI dengan bukti, bukan firasat. Kandidat",
|
|
4899
|
+
" gagal palsu yang wajib kamu periksa dulu: berkas DB/state yang dibagi antar run,",
|
|
4900
|
+
" paralelisme antar-berkas test, sisa proses/soket/port dari run sebelumnya, variabel",
|
|
4901
|
+
" lingkungan yang bocor dari shell, dan test yang memang sudah merah SEBELUM perubahan.",
|
|
4902
|
+
" Cara memutuskannya: jalankan ulang test itu SENDIRIAN, dengan state yang bersih.",
|
|
4903
|
+
"4. UJI RELEVANSI \u2014 langkah yang hampir tak pernah dilakukan siapa pun, dan tanpa ini 'hijau'",
|
|
4904
|
+
" tak berarti apa-apa: siapkan pohon kerja terpisah di commit SEBELUM perubahan",
|
|
4905
|
+
" (`git worktree add --detach <dir> <base-sha>`), pasang test barunya di sana, jalankan,",
|
|
4906
|
+
" dan tuntut test itu MERAH. Test yang tetap hijau tanpa perubahan tidak membuktikan apa",
|
|
4907
|
+
" pun tentang perubahan itu.",
|
|
4908
|
+
"5. Bersihkan pohon kerja sementara (`git worktree remove`).",
|
|
4909
|
+
"",
|
|
4910
|
+
"Larangan keras:",
|
|
4911
|
+
"- JANGAN `git stash` untuk apa pun. Tumpukan stash milik REPO, bukan pohon kerja \u2014 sesi lain",
|
|
4912
|
+
" bisa mem-pop stash milikmu, dan kamu bisa mem-pop milik mereka. Isolasi memakai",
|
|
4913
|
+
" `git worktree add`, titik.",
|
|
4914
|
+
"- JANGAN mengubah test agar lulus. Bila test-nya yang salah, itu temuan, bukan pekerjaan.",
|
|
4915
|
+
"",
|
|
4916
|
+
"Gerbang bukti: setiap klaim membawa perintah DAN potongan keluarannya. Tanpa keluaran, tanpa",
|
|
4917
|
+
"klaim. 'Semua test lulus' tanpa keluaran adalah kegagalanmu, bukan laporan.",
|
|
4918
|
+
"",
|
|
4919
|
+
"Bentuk laporan: satu baris per test \u2014 lulus-dan-relevan \xB7 lulus-tapi-tak-membuktikan-apa-pun",
|
|
4920
|
+
"\xB7 regresi \xB7 gagal-palsu (+ sebabnya) \u2014 lalu satu putusan akhir: layak diumumkan selesai atau",
|
|
4921
|
+
"belum, dan apa yang kurang."
|
|
4922
|
+
].join("\n")
|
|
4923
|
+
},
|
|
4924
|
+
{
|
|
4925
|
+
name: "edge-case-hunter",
|
|
4926
|
+
description: "Gunakan saat test yang ada hanya menguji jalur mulus dan kamu ingin batas-batas kontrak benar-benar tertutup. Ia menulis test yang hilang dan membuktikan tiap test baru merah dulu sebelum menyimpannya.",
|
|
4927
|
+
tools: ["Read", "Glob", "Grep", "Bash", "Write", "Edit"],
|
|
4928
|
+
enabledByDefault: false,
|
|
4929
|
+
instructions: [
|
|
4930
|
+
"Kamu penambal jalur bahagia. Cakupan yang terlihat baik bukan urusanmu \u2014 kontrak yang tak",
|
|
4931
|
+
"pernah diuji itu urusanmu.",
|
|
4932
|
+
"",
|
|
4933
|
+
"Prosedur:",
|
|
4934
|
+
"1. Baca kontrak unit yang berubah: apa yang ia janjikan, apa yang ia terima, apa yang ia",
|
|
4935
|
+
" lakukan saat janji itu tak bisa dipenuhi.",
|
|
4936
|
+
"2. Enumerasi batas SECARA SISTEMATIS, jangan mengandalkan ingatan: kosong \xB7 null/undefined \xB7",
|
|
4937
|
+
" nol & negatif \xB7 unicode & string sangat panjang \xB7 urutan terbalik \xB7 kedatangan ganda",
|
|
4938
|
+
" (idempotensi) \xB7 kegagalan separuh jalan \xB7 timeout & retry \xB7 nilai asing dari luar batas",
|
|
4939
|
+
" kepercayaan (input pengguna, berkas konfigurasi, data dari mesin lain).",
|
|
4940
|
+
"3. Adu daftar itu dengan test yang sudah ada. Tandai yang belum tertutup.",
|
|
4941
|
+
"4. Tulis test yang hilang. Ikuti gaya berkas test tetangga \u2014 nama, struktur, helper.",
|
|
4942
|
+
"5. Jalankan.",
|
|
4943
|
+
"",
|
|
4944
|
+
"Gerbang bukti: setiap test baru WAJIB kamu tunjukkan MERAH dulu terhadap kode yang belum",
|
|
4945
|
+
"diperbaiki. Test yang lahir langsung hijau kamu laporkan sebagai 'tak membuktikan apa-apa' \u2014",
|
|
4946
|
+
"jangan disimpan diam-diam, karena ia akan tetap hijau saat kodenya kelak rusak.",
|
|
4947
|
+
"",
|
|
4948
|
+
"Batas: kamu menulis TEST. Jangan mengubah kode produksi agar test lulus \u2014 bila test barumu",
|
|
4949
|
+
"menemukan bug sungguhan, laporkan bugnya, biarkan test itu merah, dan katakan dengan jelas",
|
|
4950
|
+
"bahwa ia merah karena bug, bukan karena test-nya salah.",
|
|
4951
|
+
"",
|
|
4952
|
+
"Bentuk laporan: (a) batas yang kini tertutup \xB7 (b) batas yang sengaja dilewati + alasannya \xB7",
|
|
4953
|
+
"(c) bug yang ditemukan test baru."
|
|
4954
|
+
].join("\n")
|
|
4955
|
+
},
|
|
4956
|
+
{
|
|
4957
|
+
name: "blast-radius",
|
|
4958
|
+
description: "Gunakan sesudah perubahan selesai untuk menemukan tempat LAIN yang seharusnya ikut berubah tapi tidak: daftar kolom, cermin tipe antar-paket, enum kembar, dokumen kontrak, tabel konstanta. Ia mencari kegagalan senyap \u2014 yang tak memunculkan satu pun error.",
|
|
4959
|
+
tools: ["Read", "Glob", "Grep", "Bash"],
|
|
4960
|
+
enabledByDefault: true,
|
|
4961
|
+
instructions: [
|
|
4962
|
+
"Kamu pencari cermin yang hanyut. Kelas bug yang kamu buru punya satu ciri: TIDAK ADA yang",
|
|
4963
|
+
"error. Satu kontrak hidup di beberapa tempat, satu tempat diperbarui, sisanya diam.",
|
|
4964
|
+
"",
|
|
4965
|
+
"Prosedur:",
|
|
4966
|
+
"1. Baca diff terhadap base. Tarik daftar yang berubah: simbol, kolom, nilai enum, kunci",
|
|
4967
|
+
" konfigurasi, nama berkas, bentuk payload.",
|
|
4968
|
+
"2. Untuk TIAP satu, sapu seluruh repo untuk semua tempat lain yang menyebutnya \u2014 ATAU yang",
|
|
4969
|
+
" seharusnya menyebutnya. Yang kedua ini yang penting, dan ia tak akan muncul dari pencarian",
|
|
4970
|
+
" nama saja. Tempat yang wajib kamu periksa:",
|
|
4971
|
+
" - daftar/array literal yang mencacah field atau kolom secara manual;",
|
|
4972
|
+
" - tabel konstanta & peta yang kuncinya harus lengkap tapi tak punya rujukan tipe;",
|
|
4973
|
+
" - tipe yang disalin (bukan diimpor) antar-paket, dan enum kembar;",
|
|
4974
|
+
" - skema validasi di batas HTTP vs bentuk yang benar-benar disimpan;",
|
|
4975
|
+
" - dokumen kontrak (API, data model) dan berkas contoh/konfigurasi;",
|
|
4976
|
+
" - berkas test yang mengunci bentuk lama.",
|
|
4977
|
+
"3. Laporkan yang belum ikut berubah.",
|
|
4978
|
+
"",
|
|
4979
|
+
"Gerbang bukti: tiap temuan menyebut `path:baris` DAN apa yang terjadi bila dibiarkan. Bila",
|
|
4980
|
+
"konsekuensinya 'gagal senyap' \u2014 nilai default palsu, kolom yang hilang tanpa error, cabang",
|
|
4981
|
+
"yang tak pernah dijalankan \u2014 NAIKKAN prioritasnya, jangan turunkan. Yang berteriak akan",
|
|
4982
|
+
"ketahuan sendiri; yang diam tidak.",
|
|
4983
|
+
"",
|
|
4984
|
+
"Bentuk laporan: daftar cermin yang hanyut, diurut dari yang paling senyap, tiap baris:",
|
|
4985
|
+
"jangkar \xB7 apa yang hanyut \xB7 akibat bila dibiarkan."
|
|
4986
|
+
].join("\n")
|
|
4987
|
+
},
|
|
4988
|
+
{
|
|
4989
|
+
name: "spec-auditor",
|
|
4990
|
+
description: "Gunakan sebelum menutup pekerjaan untuk mengadu apa yang DIMINTA dengan apa yang benar-benar ada di diff. Ia menolak 'sepertinya sudah' dan memperlakukan kriteria tanpa jejak sebagai tak terpenuhi, walau kotaknya sudah tercentang.",
|
|
4991
|
+
tools: ["Read", "Glob", "Grep", "Bash"],
|
|
4992
|
+
enabledByDefault: false,
|
|
4993
|
+
instructions: [
|
|
4994
|
+
"Kamu pengadu janji. Kamu tak menilai bagus atau tidaknya kode \u2014 kamu menilai apakah yang",
|
|
4995
|
+
"diminta benar-benar ada.",
|
|
4996
|
+
"",
|
|
4997
|
+
"Prosedur:",
|
|
4998
|
+
"1. Baca sumber permintaannya: spec, plan, issue, atau deskripsi tugas. Bila ada beberapa,",
|
|
4999
|
+
" baca semuanya \u2014 plan bisa menyimpang dari spec, dan penyimpangan itu sendiri temuan.",
|
|
5000
|
+
"2. Ubah jadi daftar kriteria yang bisa diperiksa SATU PER SATU. Kalimat yang tak bisa",
|
|
5001
|
+
" diperiksa ('lebih baik', 'rapi') kamu tandai sebagai tak terukur, bukan kamu tafsirkan.",
|
|
5002
|
+
"3. Untuk tiap kriteria, cari JEJAKNYA di diff. Bukan di niat, bukan di komentar kode.",
|
|
5003
|
+
"4. Putuskan: terpenuhi (+jangkar) \xB7 tak terpenuhi \xB7 terpenuhi BERBEDA dari yang diminta \xB7",
|
|
5004
|
+
" dikerjakan TANPA diminta.",
|
|
5005
|
+
"",
|
|
5006
|
+
"Gerbang bukti:",
|
|
5007
|
+
"- Kriteria tanpa jangkar di diff = TAK TERPENUHI. Kotak yang sudah tercentang di berkas plan",
|
|
5008
|
+
" bukan bukti \u2014 ia klaim, dan klaim itu justru yang sedang kamu periksa.",
|
|
5009
|
+
"- Pekerjaan yang dikerjakan tanpa diminta dilaporkan TERPISAH, bukan dipuji. Ia menambah",
|
|
5010
|
+
" permukaan yang tak pernah diminta siapa pun untuk dipelihara.",
|
|
5011
|
+
"",
|
|
5012
|
+
"Bentuk laporan: tabel \u2014 kriteria \xB7 putusan \xB7 jangkar; lalu daftar pekerjaan di luar minta;",
|
|
5013
|
+
"lalu satu putusan akhir: boleh ditutup atau belum, dan apa yang kurang."
|
|
5014
|
+
].join("\n")
|
|
5015
|
+
},
|
|
5016
|
+
{
|
|
5017
|
+
name: "security-reviewer",
|
|
5018
|
+
description: "Gunakan sebelum menggabungkan perubahan yang menyentuh route, handler, job, CLI, atau apa pun yang menerima input dari luar. Ia menelusuri jalur konkret dari input tak terpercaya sampai ke tempat ia melukai, dan menolak melaporkan kekhawatiran yang tak bisa ia buktikan jalurnya.",
|
|
5019
|
+
tools: ["Read", "Glob", "Grep", "Bash"],
|
|
5020
|
+
enabledByDefault: true,
|
|
5021
|
+
instructions: [
|
|
5022
|
+
"Kamu penelusur sumber-ke-sink. Daftar kekhawatiran umum tak mengubah apa pun; yang mengubah",
|
|
5023
|
+
"adalah satu jalur konkret dari input yang tak dipercaya sampai ke tempat ia melukai.",
|
|
5024
|
+
"",
|
|
5025
|
+
"Prosedur:",
|
|
5026
|
+
"1. Enumerasi TITIK MASUK yang tersentuh diff: route HTTP, handler pesan/webhook, job",
|
|
5027
|
+
" terjadwal, perintah CLI, pembaca berkas konfigurasi, dan apa pun yang membaca input",
|
|
5028
|
+
" pengguna atau data dari mesin lain.",
|
|
5029
|
+
"2. Untuk tiap titik masuk, telusuri input tak terpercaya sampai SINK: query basis data,",
|
|
5030
|
+
" `exec`/shell, path berkas, template/render, deserialisasi, permintaan keluar, redirect,",
|
|
5031
|
+
" dan apa pun yang ditulis ke log atau dikembalikan ke pemanggil.",
|
|
5032
|
+
"3. Di sepanjang jalur itu, periksa gerbang yang seharusnya ada:",
|
|
5033
|
+
" - autentikasi \u2014 siapa dia;",
|
|
5034
|
+
" - OTORISASI KEPEMILIKAN OBJEK \u2014 apakah dia berhak atas objek INI. Ini yang paling sering",
|
|
5035
|
+
" hilang, dan justru hilangnya di endpoint yang autentikasinya sudah benar;",
|
|
5036
|
+
" - validasi bentuk di batas, bukan di dalam;",
|
|
5037
|
+
" - batas ukuran & jumlah (payload, unggahan, paginasi, perulangan);",
|
|
5038
|
+
" - kredensial: bocor ke log, ke response, ke pesan galat, atau ikut ter-commit.",
|
|
5039
|
+
"",
|
|
5040
|
+
"Gerbang bukti \u2014 ini yang membedakanmu dari daftar kekhawatiran:",
|
|
5041
|
+
"- Temuan TANPA jalur konkret input \u2192 dampak TIDAK kamu laporkan. Tahan.",
|
|
5042
|
+
"- Sebutkan juga jalur mana saja yang sudah kamu telusuri dan BERSIH. Tanpa itu, diammu tak",
|
|
5043
|
+
" bisa dibedakan dari tidak memeriksa.",
|
|
5044
|
+
"- Jangan menilai dari nama fungsi atau kecocokan pola. Baca jalurnya.",
|
|
5045
|
+
"",
|
|
5046
|
+
"Bentuk laporan: per temuan \u2014 jalur (dengan jangkar) \xB7 dampak \xB7 perbaikan TERKECIL; lalu",
|
|
5047
|
+
"daftar titik masuk yang dinyatakan bersih."
|
|
5048
|
+
].join("\n")
|
|
5049
|
+
},
|
|
5050
|
+
{
|
|
5051
|
+
name: "dep-auditor",
|
|
5052
|
+
description: "Gunakan saat diff menambah atau menaikkan versi dependensi. Ia memeriksa advisory, lisensi, tanda pemeliharaan, dan \u2014 yang paling sering terlewat \u2014 apakah fungsinya sudah tersedia tanpa dependensi baru itu.",
|
|
5053
|
+
tools: ["Read", "Glob", "Grep", "Bash", "WebSearch", "WebFetch"],
|
|
5054
|
+
enabledByDefault: false,
|
|
5055
|
+
instructions: [
|
|
5056
|
+
"Kamu gerbang rantai pasok. Satu dependensi masuk lewat satu baris diff dan tak pernah",
|
|
5057
|
+
"diperiksa lagi seumur hidup proyek \u2014 pemeriksaan itu terjadi sekarang atau tidak sama sekali.",
|
|
5058
|
+
"",
|
|
5059
|
+
"Prosedur:",
|
|
5060
|
+
"1. Dari diff, ambil dependensi yang BERTAMBAH atau NAIK VERSI (termasuk devDependencies).",
|
|
5061
|
+
" Lockfile ikut dibaca: dependensi transitif baru yang besar juga temuan.",
|
|
5062
|
+
"2. Untuk tiap satu, periksa dan sebutkan sumbernya:",
|
|
5063
|
+
" - advisory/CVE yang diketahui untuk rentang versi itu;",
|
|
5064
|
+
" - tanggal rilis terakhir & tanda pemeliharaan (isu terbuka menumpuk, maintainer tunggal);",
|
|
5065
|
+
" - lisensi, dan apakah ia cocok dengan lisensi proyek ini;",
|
|
5066
|
+
" - ukuran pohon transitifnya;",
|
|
5067
|
+
" - apakah paket menjalankan skrip saat instalasi.",
|
|
5068
|
+
"3. Pertanyaan yang paling sering dilewati, dan tanyakan SELALU: apakah fungsi yang dipakai",
|
|
5069
|
+
" sudah tersedia di dependensi yang SUDAH ada di proyek ini, atau di runtime-nya? Cek dulu",
|
|
5070
|
+
" sebelum menerima. Satu dependensi yang tak jadi masuk lebih berharga daripada sepuluh",
|
|
5071
|
+
" yang diaudit.",
|
|
5072
|
+
"",
|
|
5073
|
+
"Gerbang bukti: klaim CVE atau lisensi WAJIB membawa URL sumbernya. Tanpa sumber, tulis 'tak",
|
|
5074
|
+
"terverifikasi' \u2014 jangan hilangkan, dan jangan naikkan jadi fakta.",
|
|
5075
|
+
"",
|
|
5076
|
+
"Bentuk laporan: per dependensi \u2014 aman \xB7 aman dengan catatan \xB7 tolak (+ penggantinya, atau",
|
|
5077
|
+
"cara mengerjakannya tanpa dependensi itu)."
|
|
5078
|
+
].join("\n")
|
|
5079
|
+
}
|
|
5080
|
+
];
|
|
5081
|
+
BUILTIN_AGENT_NAMES = BUILTIN_AGENTS.map((a) => a.name);
|
|
5082
|
+
}
|
|
5083
|
+
});
|
|
5084
|
+
|
|
4817
5085
|
// ../shared/src/agent-catalog.ts
|
|
4818
5086
|
var BUILTIN_AGENT_TOOLS;
|
|
4819
5087
|
var init_agent_catalog = __esm({
|
|
@@ -4961,6 +5229,31 @@ var init_lead = __esm({
|
|
|
4961
5229
|
}
|
|
4962
5230
|
});
|
|
4963
5231
|
|
|
5232
|
+
// ../shared/src/handled-by.ts
|
|
5233
|
+
var zHandledByEntry, HANDLED_BY_MAX, zHandledBy, zHandledByView;
|
|
5234
|
+
var init_handled_by = __esm({
|
|
5235
|
+
"../shared/src/handled-by.ts"() {
|
|
5236
|
+
"use strict";
|
|
5237
|
+
init_zod();
|
|
5238
|
+
zHandledByEntry = external_exports.object({
|
|
5239
|
+
deviceId: external_exports.string().min(1),
|
|
5240
|
+
name: external_exports.string().min(1)
|
|
5241
|
+
});
|
|
5242
|
+
HANDLED_BY_MAX = 32;
|
|
5243
|
+
zHandledBy = external_exports.array(zHandledByEntry).max(HANDLED_BY_MAX).superRefine((list, ctx) => {
|
|
5244
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5245
|
+
for (const e of list) {
|
|
5246
|
+
if (seen.has(e.deviceId)) {
|
|
5247
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, message: `deviceId duplikat: ${e.deviceId}` });
|
|
5248
|
+
return;
|
|
5249
|
+
}
|
|
5250
|
+
seen.add(e.deviceId);
|
|
5251
|
+
}
|
|
5252
|
+
});
|
|
5253
|
+
zHandledByView = zHandledByEntry.extend({ revoked: external_exports.boolean().default(false) });
|
|
5254
|
+
}
|
|
5255
|
+
});
|
|
5256
|
+
|
|
4964
5257
|
// ../shared/src/prd-status.ts
|
|
4965
5258
|
var PRD_STATUSES, zPrdStatus;
|
|
4966
5259
|
var init_prd_status = __esm({
|
|
@@ -5033,7 +5326,7 @@ var init_cron_expr = __esm({
|
|
|
5033
5326
|
});
|
|
5034
5327
|
|
|
5035
5328
|
// ../shared/src/dto.ts
|
|
5036
|
-
var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zChangeSpecSource, zMarkSpecDone, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, USER_ROLES, zUserRole, zLogin, zSignup, zSetup, zChangePassword, zCreateClientAccount, zUpdateClientAccount, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, UPDATE_RESTART_EXIT, zUpdateApplyBody, zTicketView, zTicketAttachmentView, zSpecAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus, zCronExpr, zCreateCron, zPatchCron, zSchedulerCron, zSchedulerCronRun;
|
|
5329
|
+
var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zChangeSpecSource, zMarkSpecDone, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, USER_ROLES, zUserRole, zLogin, zSignup, zSetup, zSetupApply, zChangePassword, zCreateClientAccount, zUpdateClientAccount, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, PROVISION_PROFILES, COMPONENT_IDS, zProvision, UPDATE_RESTART_EXIT, CONFIG_RESTART_EXIT, zUpdateApplyBody, zTicketView, zTicketAttachmentView, zSpecAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus, zCronExpr, zCreateCron, zPatchCron, zSchedulerCron, zSchedulerCronRun;
|
|
5037
5330
|
var init_dto = __esm({
|
|
5038
5331
|
"../shared/src/dto.ts"() {
|
|
5039
5332
|
"use strict";
|
|
@@ -5041,6 +5334,7 @@ var init_dto = __esm({
|
|
|
5041
5334
|
init_entities();
|
|
5042
5335
|
init_lead();
|
|
5043
5336
|
init_auto_merge();
|
|
5337
|
+
init_handled_by();
|
|
5044
5338
|
init_prd_status();
|
|
5045
5339
|
init_enums();
|
|
5046
5340
|
init_spec_source();
|
|
@@ -5087,6 +5381,8 @@ var init_dto = __esm({
|
|
|
5087
5381
|
kind: zProjectKind,
|
|
5088
5382
|
repoDir: external_exports.string().optional(),
|
|
5089
5383
|
gitRemote: external_exports.string().optional(),
|
|
5384
|
+
// SPEC-880 · ADR-0135 · penanda "ditangani oleh" boleh di-set sejak awal (disync).
|
|
5385
|
+
handledBy: zHandledBy.optional(),
|
|
5090
5386
|
desc: external_exports.string().default("")
|
|
5091
5387
|
});
|
|
5092
5388
|
zProjectId = external_exports.string().regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, "slug tak sah");
|
|
@@ -5096,6 +5392,10 @@ var init_dto = __esm({
|
|
|
5096
5392
|
desc: external_exports.string().optional(),
|
|
5097
5393
|
gitRemote: external_exports.string().optional(),
|
|
5098
5394
|
// SPEC-213 · set git remote resmi project
|
|
5395
|
+
// SPEC-880 · ADR-0135 · daftar hanoman client pemegang project (DISYNC — beda dari repoDir &
|
|
5396
|
+
// opt-in di bawah yang lokal per-instance). `null` maupun `[]` = kosongkan. Digerbangi
|
|
5397
|
+
// `checkHandledBy`: deviceId wajib dikenal HANYA bila instance ini punya katalog device.
|
|
5398
|
+
handledBy: zHandledBy.nullable().optional(),
|
|
5099
5399
|
repoDir: external_exports.string().nullable().optional(),
|
|
5100
5400
|
// SPEC-217 · path default/server editable (null = kosongkan)
|
|
5101
5401
|
schedulerOptIn: external_exports.boolean().optional(),
|
|
@@ -5171,6 +5471,10 @@ var init_dto = __esm({
|
|
|
5171
5471
|
// SPEC-294 · opt-in scheduler otonom
|
|
5172
5472
|
leadOptIn: external_exports.boolean().default(false),
|
|
5173
5473
|
// SPEC-409 · ADR-0091 · opt-in hanoman-lead
|
|
5474
|
+
// SPEC-880 · ADR-0135 · penanda "ditangani oleh" yang SUDAH diperkaya: `name` = nama hidup bila
|
|
5475
|
+
// baris DeviceToken-nya ada di instance ini, else snapshot tersimpan; `revoked` diturunkan.
|
|
5476
|
+
// `[]` = belum ditetapkan — kolom NULL tak pernah bocor ke UI sebagai bentuk kedua "kosong".
|
|
5477
|
+
handledBy: zHandledByView.array().default([]),
|
|
5174
5478
|
autoMerge: zAutoMerge.nullable().default(null)
|
|
5175
5479
|
});
|
|
5176
5480
|
zSchedulerQueueItem = external_exports.object({
|
|
@@ -5473,6 +5777,11 @@ var init_dto = __esm({
|
|
|
5473
5777
|
zLogin = external_exports.object({ email: external_exports.string().email(), password: external_exports.string().min(1) });
|
|
5474
5778
|
zSignup = external_exports.object({ email: external_exports.string().email(), password: external_exports.string().min(8) });
|
|
5475
5779
|
zSetup = zSignup.extend({ setupToken: external_exports.string().min(1) });
|
|
5780
|
+
zSetupApply = external_exports.object({
|
|
5781
|
+
deployment: external_exports.enum(["local", "public"]),
|
|
5782
|
+
hardening: external_exports.boolean(),
|
|
5783
|
+
acknowledgedUnhardened: external_exports.boolean().optional()
|
|
5784
|
+
});
|
|
5476
5785
|
zChangePassword = external_exports.object({
|
|
5477
5786
|
currentPassword: external_exports.string().min(1),
|
|
5478
5787
|
newPassword: external_exports.string().min(8)
|
|
@@ -5500,7 +5809,27 @@ var init_dto = __esm({
|
|
|
5500
5809
|
});
|
|
5501
5810
|
zAttest = external_exports.object({ note: external_exports.string().max(500).optional() });
|
|
5502
5811
|
zRemediate = external_exports.object({ items: external_exports.array(external_exports.string()).min(1).max(64) });
|
|
5812
|
+
PROVISION_PROFILES = ["lab", "production"];
|
|
5813
|
+
COMPONENT_IDS = [
|
|
5814
|
+
"base",
|
|
5815
|
+
"node",
|
|
5816
|
+
"hanoman",
|
|
5817
|
+
"caddy",
|
|
5818
|
+
"podman",
|
|
5819
|
+
"agent-image",
|
|
5820
|
+
"claude",
|
|
5821
|
+
"codex",
|
|
5822
|
+
"gh"
|
|
5823
|
+
];
|
|
5824
|
+
zProvision = external_exports.object({
|
|
5825
|
+
items: external_exports.array(external_exports.enum(COMPONENT_IDS)).min(1).max(16),
|
|
5826
|
+
profile: external_exports.enum(PROVISION_PROFILES),
|
|
5827
|
+
domain: external_exports.string().min(1).regex(HOST_RE).optional(),
|
|
5828
|
+
confirm: external_exports.boolean().optional(),
|
|
5829
|
+
force: external_exports.boolean().optional()
|
|
5830
|
+
});
|
|
5503
5831
|
UPDATE_RESTART_EXIT = 75;
|
|
5832
|
+
CONFIG_RESTART_EXIT = 76;
|
|
5504
5833
|
zUpdateApplyBody = external_exports.object({ confirm: external_exports.boolean().optional() });
|
|
5505
5834
|
zTicketView = external_exports.object({
|
|
5506
5835
|
id: external_exports.string(),
|
|
@@ -5716,6 +6045,11 @@ var init_api = __esm({
|
|
|
5716
6045
|
terminalSession: (id) => `${API}/terminal/sessions/${id}`,
|
|
5717
6046
|
terminalSteer: (id) => `${API}/terminal/sessions/${id}/steer`,
|
|
5718
6047
|
terminalInterrupt: (id) => `${API}/terminal/sessions/${id}/interrupt`,
|
|
6048
|
+
// SPEC-899 · ADR-0142 · inbox keputusan. Di bawah prefix /terminal supaya ikut capability
|
|
6049
|
+
// `sessions` yang sudah ada — GET menurunkan cabang bacanya, POST cabang tulisnya, tanpa satu
|
|
6050
|
+
// baris pun perubahan di services/agent-capabilities.ts.
|
|
6051
|
+
terminalDialog: (id) => `${API}/terminal/sessions/${id}/dialog`,
|
|
6052
|
+
terminalDialogAnswer: (id) => `${API}/terminal/sessions/${id}/dialog/answer`,
|
|
5719
6053
|
terminalPhases: (id) => `${API}/terminal/sessions/${id}/phases`,
|
|
5720
6054
|
// SPEC-230 · review + integrate ber-skop sesi (sesi project-level PRD, tanpa Spec).
|
|
5721
6055
|
sessionReview: (id) => `${API}/terminal/sessions/${id}/review`,
|
|
@@ -5747,6 +6081,11 @@ var init_api = __esm({
|
|
|
5747
6081
|
vpsItemAttest: (id, itemId) => `${API}/vps/${id}/items/${itemId}/attest`,
|
|
5748
6082
|
vpsRemediatePreview: (id) => `${API}/vps/${id}/remediate/preview`,
|
|
5749
6083
|
vpsRemediate: (id) => `${API}/vps/${id}/remediate`,
|
|
6084
|
+
// SPEC-883 · provisioning berbasis katalog
|
|
6085
|
+
vpsComponents: () => `${API}/vps/components`,
|
|
6086
|
+
vpsProbe: (id) => `${API}/vps/${id}/probe`,
|
|
6087
|
+
vpsProvisionPreview: (id) => `${API}/vps/${id}/provision/preview`,
|
|
6088
|
+
vpsProvision: (id) => `${API}/vps/${id}/provision`,
|
|
5750
6089
|
// SPEC-169 · auth
|
|
5751
6090
|
authStatus: `${API}/auth/status`,
|
|
5752
6091
|
authSetup: `${API}/auth/setup`,
|
|
@@ -5755,6 +6094,9 @@ var init_api = __esm({
|
|
|
5755
6094
|
authUsers: `${API}/auth/users`,
|
|
5756
6095
|
authUser: (id) => `${API}/auth/users/${id}`,
|
|
5757
6096
|
authChangePassword: `${API}/auth/change-password`,
|
|
6097
|
+
// SPEC-884 · ADR-0139 · wizard setup awal
|
|
6098
|
+
setupStatus: `${API}/setup/status`,
|
|
6099
|
+
setupApply: `${API}/setup`,
|
|
5758
6100
|
// SPEC-213 · device token + activity log
|
|
5759
6101
|
deviceTokens: `${API}/device-tokens`,
|
|
5760
6102
|
deviceToken: (id) => `${API}/device-tokens/${id}`,
|
|
@@ -6296,6 +6638,9 @@ var init_webhook = __esm({
|
|
|
6296
6638
|
"gitRemote",
|
|
6297
6639
|
"stack",
|
|
6298
6640
|
"helpEnabled",
|
|
6641
|
+
// SPEC-880 · ADR-0135 · penerima harus bisa tahu mesin mana yang memegang project ini
|
|
6642
|
+
// tanpa mendiff dua amplop. Entrinya snapshot: [{deviceId,name}].
|
|
6643
|
+
"handledBy",
|
|
6299
6644
|
"schedulerOptIn",
|
|
6300
6645
|
"leadOptIn",
|
|
6301
6646
|
"autoMerge",
|
|
@@ -6306,7 +6651,7 @@ var init_webhook = __esm({
|
|
|
6306
6651
|
cascade: ["spec", "ticket", "customAgent", "githubIssue"],
|
|
6307
6652
|
events: {
|
|
6308
6653
|
created: { type: "project.created", label: "Project ditambah", when: "Project baru terdaftar di workspace." },
|
|
6309
|
-
updated: { type: "project.updated", label: "Project diubah", when:
|
|
6654
|
+
updated: { type: "project.updated", label: "Project diubah", when: 'Nama, deskripsi, stack, remote, penanda "ditangani oleh", atau opt-in scheduler/lead/Help Center berubah.' },
|
|
6310
6655
|
deleted: { type: "project.deleted", label: "Project dihapus", when: "Project dihapus. data.cascade menyebut jumlah anak yang ikut terhapus \u2014 anaknya sendiri TIDAK memancarkan deleted, sebab cascade dieksekusi SQLite, di luar jangkauan tap." }
|
|
6311
6656
|
},
|
|
6312
6657
|
sample: {
|
|
@@ -6317,6 +6662,7 @@ var init_webhook = __esm({
|
|
|
6317
6662
|
gitRemote: "git@github.com:nafanesia/hanoman.git",
|
|
6318
6663
|
stack: "ts",
|
|
6319
6664
|
helpEnabled: false,
|
|
6665
|
+
handledBy: [{ deviceId: "clq0device1", name: "hm-dena" }],
|
|
6320
6666
|
schedulerOptIn: true,
|
|
6321
6667
|
leadOptIn: false,
|
|
6322
6668
|
autoMerge: null,
|
|
@@ -7306,6 +7652,27 @@ var init_terminal_io = __esm({
|
|
|
7306
7652
|
}
|
|
7307
7653
|
});
|
|
7308
7654
|
|
|
7655
|
+
// ../shared/src/session-dialog.ts
|
|
7656
|
+
var zSessionDialogAnswer;
|
|
7657
|
+
var init_session_dialog = __esm({
|
|
7658
|
+
"../shared/src/session-dialog.ts"() {
|
|
7659
|
+
"use strict";
|
|
7660
|
+
init_zod();
|
|
7661
|
+
zSessionDialogAnswer = external_exports.object({
|
|
7662
|
+
screenHash: external_exports.string().min(1),
|
|
7663
|
+
choice: external_exports.number().int().positive().optional(),
|
|
7664
|
+
choices: external_exports.array(external_exports.number().int().positive()).max(32).optional(),
|
|
7665
|
+
text: external_exports.string().trim().min(1).max(16e3).optional()
|
|
7666
|
+
}).refine(
|
|
7667
|
+
(v) => !(v.choice !== void 0 && v.choices !== void 0),
|
|
7668
|
+
{ message: "choice dan choices tak boleh dikirim bersamaan" }
|
|
7669
|
+
).refine(
|
|
7670
|
+
(v) => v.choice !== void 0 || v.choices !== void 0 || v.text !== void 0,
|
|
7671
|
+
{ message: "jawaban kosong" }
|
|
7672
|
+
);
|
|
7673
|
+
}
|
|
7674
|
+
});
|
|
7675
|
+
|
|
7309
7676
|
// ../shared/src/index.ts
|
|
7310
7677
|
var init_src = __esm({
|
|
7311
7678
|
"../shared/src/index.ts"() {
|
|
@@ -7318,6 +7685,7 @@ var init_src = __esm({
|
|
|
7318
7685
|
init_entities();
|
|
7319
7686
|
init_agent();
|
|
7320
7687
|
init_custom_agent();
|
|
7688
|
+
init_builtin_agents();
|
|
7321
7689
|
init_agent_catalog();
|
|
7322
7690
|
init_github();
|
|
7323
7691
|
init_lead();
|
|
@@ -7337,10 +7705,12 @@ var init_src = __esm({
|
|
|
7337
7705
|
init_webhook();
|
|
7338
7706
|
init_mcp();
|
|
7339
7707
|
init_auto_merge();
|
|
7708
|
+
init_handled_by();
|
|
7340
7709
|
init_changelog();
|
|
7341
7710
|
init_cron_expr();
|
|
7342
7711
|
init_terminal_workspace();
|
|
7343
7712
|
init_terminal_io();
|
|
7713
|
+
init_session_dialog();
|
|
7344
7714
|
}
|
|
7345
7715
|
});
|
|
7346
7716
|
|
|
@@ -7818,6 +8188,149 @@ var init_spawn_helper = __esm({
|
|
|
7818
8188
|
}
|
|
7819
8189
|
});
|
|
7820
8190
|
|
|
8191
|
+
// ../runner/src/config-env.ts
|
|
8192
|
+
import { chmodSync as chmodSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
8193
|
+
import { join as join4 } from "node:path";
|
|
8194
|
+
function configEnvPath(home) {
|
|
8195
|
+
return join4(home, CONFIG_ENV_FILE);
|
|
8196
|
+
}
|
|
8197
|
+
function parseConfigEnv(text) {
|
|
8198
|
+
const out = {};
|
|
8199
|
+
for (const raw of text.split("\n")) {
|
|
8200
|
+
const line = raw.trim();
|
|
8201
|
+
if (!line || line.startsWith("#")) continue;
|
|
8202
|
+
const eq = line.indexOf("=");
|
|
8203
|
+
if (eq <= 0) continue;
|
|
8204
|
+
out[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
|
|
8205
|
+
}
|
|
8206
|
+
return out;
|
|
8207
|
+
}
|
|
8208
|
+
function readConfigEnv(home) {
|
|
8209
|
+
try {
|
|
8210
|
+
return parseConfigEnv(readFileSync2(configEnvPath(home), "utf8"));
|
|
8211
|
+
} catch {
|
|
8212
|
+
return {};
|
|
8213
|
+
}
|
|
8214
|
+
}
|
|
8215
|
+
var CONFIG_ENV_FILE;
|
|
8216
|
+
var init_config_env = __esm({
|
|
8217
|
+
"../runner/src/config-env.ts"() {
|
|
8218
|
+
"use strict";
|
|
8219
|
+
CONFIG_ENV_FILE = "config.env";
|
|
8220
|
+
}
|
|
8221
|
+
});
|
|
8222
|
+
|
|
8223
|
+
// ../runner/src/runtime-profile.ts
|
|
8224
|
+
function resolveHardening(env) {
|
|
8225
|
+
if (env.HANOMAN_HARDENING === "1") return true;
|
|
8226
|
+
return env.HANOMAN_SESSION_SANDBOX === "podman" || filled(env.HANOMAN_PUBLIC_ORIGINS) || filled(env.HANOMAN_TRUST_PROXY);
|
|
8227
|
+
}
|
|
8228
|
+
var filled;
|
|
8229
|
+
var init_runtime_profile = __esm({
|
|
8230
|
+
"../runner/src/runtime-profile.ts"() {
|
|
8231
|
+
"use strict";
|
|
8232
|
+
filled = (v) => !!v && v.trim() !== "";
|
|
8233
|
+
}
|
|
8234
|
+
});
|
|
8235
|
+
|
|
8236
|
+
// ../runner/src/sandbox-probe.ts
|
|
8237
|
+
import { execFileSync } from "node:child_process";
|
|
8238
|
+
import { accessSync, constants } from "node:fs";
|
|
8239
|
+
import { isAbsolute as isAbsolute2 } from "node:path";
|
|
8240
|
+
function trustProxyOk(raw) {
|
|
8241
|
+
const v = raw?.trim();
|
|
8242
|
+
if (!v) return false;
|
|
8243
|
+
if (/^[1-9]\d*$/.test(v)) return true;
|
|
8244
|
+
const values = v.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
8245
|
+
if (!values.length) return false;
|
|
8246
|
+
return !values.some((s2) => s2 === "true" || !s2.includes("/") && s2 !== "loopback");
|
|
8247
|
+
}
|
|
8248
|
+
function prerequisites(env, facts) {
|
|
8249
|
+
const network = env.HANOMAN_SESSION_NETWORK ?? "hanoman-egress";
|
|
8250
|
+
const credentialDir = env.HANOMAN_AGENT_CREDENTIAL_DIR;
|
|
8251
|
+
const scanner = env.HANOMAN_UPLOAD_SCANNER?.trim();
|
|
8252
|
+
return [
|
|
8253
|
+
{
|
|
8254
|
+
id: "podman",
|
|
8255
|
+
label: "Podman rootless",
|
|
8256
|
+
ok: !!facts.podman && facts.rootless,
|
|
8257
|
+
detail: facts.podman ? facts.rootless ? facts.podman : `${facts.podman} \u2014 berjalan rootful, butuh rootless` : "podman tak ada"
|
|
8258
|
+
},
|
|
8259
|
+
{
|
|
8260
|
+
id: "network",
|
|
8261
|
+
label: `Network "${network}"`,
|
|
8262
|
+
ok: facts.networkExists,
|
|
8263
|
+
detail: facts.networkExists ? network : `network ${network} belum dibuat`
|
|
8264
|
+
},
|
|
8265
|
+
{
|
|
8266
|
+
id: "egress-proxy",
|
|
8267
|
+
label: "Egress proxy",
|
|
8268
|
+
ok: filled2(env.HANOMAN_EGRESS_PROXY),
|
|
8269
|
+
detail: env.HANOMAN_EGRESS_PROXY ?? "HANOMAN_EGRESS_PROXY belum disetel"
|
|
8270
|
+
},
|
|
8271
|
+
{
|
|
8272
|
+
id: "credential-dir",
|
|
8273
|
+
label: "Dir credential agen",
|
|
8274
|
+
ok: filled2(credentialDir) && facts.credentialDirReadable,
|
|
8275
|
+
detail: !filled2(credentialDir) ? "HANOMAN_AGENT_CREDENTIAL_DIR belum disetel" : facts.credentialDirReadable ? credentialDir : `${credentialDir} \u2014 tak terbaca`
|
|
8276
|
+
},
|
|
8277
|
+
{
|
|
8278
|
+
id: "control-origin",
|
|
8279
|
+
label: "Control origin",
|
|
8280
|
+
ok: filled2(env.HANOMAN_CONTROL_ORIGINS),
|
|
8281
|
+
detail: env.HANOMAN_CONTROL_ORIGINS ?? "HANOMAN_CONTROL_ORIGINS belum disetel"
|
|
8282
|
+
},
|
|
8283
|
+
{
|
|
8284
|
+
id: "trust-proxy",
|
|
8285
|
+
label: "Trusted proxy hop/CIDR",
|
|
8286
|
+
ok: trustProxyOk(env.HANOMAN_TRUST_PROXY),
|
|
8287
|
+
detail: filled2(env.HANOMAN_TRUST_PROXY) ? trustProxyOk(env.HANOMAN_TRUST_PROXY) ? env.HANOMAN_TRUST_PROXY : "harus hop atau CIDR eksplisit" : "HANOMAN_TRUST_PROXY belum disetel"
|
|
8288
|
+
},
|
|
8289
|
+
{
|
|
8290
|
+
id: "upload-scanner",
|
|
8291
|
+
label: "Scanner upload",
|
|
8292
|
+
ok: !!scanner && isAbsolute2(scanner),
|
|
8293
|
+
detail: !scanner ? "HANOMAN_UPLOAD_SCANNER belum disetel" : isAbsolute2(scanner) ? scanner : `${scanner} \u2014 path harus absolut`
|
|
8294
|
+
}
|
|
8295
|
+
];
|
|
8296
|
+
}
|
|
8297
|
+
function allReady(rows) {
|
|
8298
|
+
return rows.every((r) => r.ok);
|
|
8299
|
+
}
|
|
8300
|
+
function run(file, args) {
|
|
8301
|
+
try {
|
|
8302
|
+
return execFileSync(file, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
8303
|
+
} catch {
|
|
8304
|
+
return null;
|
|
8305
|
+
}
|
|
8306
|
+
}
|
|
8307
|
+
function collectProbeFacts(env) {
|
|
8308
|
+
const podman = run("podman", ["--version"]);
|
|
8309
|
+
const network = env.HANOMAN_SESSION_NETWORK ?? "hanoman-egress";
|
|
8310
|
+
const dir = env.HANOMAN_AGENT_CREDENTIAL_DIR;
|
|
8311
|
+
let credentialDirReadable = false;
|
|
8312
|
+
if (dir) {
|
|
8313
|
+
try {
|
|
8314
|
+
accessSync(dir, constants.R_OK);
|
|
8315
|
+
credentialDirReadable = true;
|
|
8316
|
+
} catch {
|
|
8317
|
+
}
|
|
8318
|
+
}
|
|
8319
|
+
return {
|
|
8320
|
+
podman,
|
|
8321
|
+
rootless: podman ? run("podman", ["info", "--format", "{{.Host.Security.Rootless}}"]) === "true" : false,
|
|
8322
|
+
networkExists: podman ? run("podman", ["network", "exists", network]) !== null : false,
|
|
8323
|
+
credentialDirReadable
|
|
8324
|
+
};
|
|
8325
|
+
}
|
|
8326
|
+
var filled2;
|
|
8327
|
+
var init_sandbox_probe = __esm({
|
|
8328
|
+
"../runner/src/sandbox-probe.ts"() {
|
|
8329
|
+
"use strict";
|
|
8330
|
+
filled2 = (v) => !!v && v.trim() !== "";
|
|
8331
|
+
}
|
|
8332
|
+
});
|
|
8333
|
+
|
|
7821
8334
|
// ../runner/src/index.ts
|
|
7822
8335
|
var init_src2 = __esm({
|
|
7823
8336
|
"../runner/src/index.ts"() {
|
|
@@ -7838,28 +8351,31 @@ var init_src2 = __esm({
|
|
|
7838
8351
|
init_skills();
|
|
7839
8352
|
init_telegram_operator();
|
|
7840
8353
|
init_spawn_helper();
|
|
8354
|
+
init_config_env();
|
|
8355
|
+
init_runtime_profile();
|
|
8356
|
+
init_sandbox_probe();
|
|
7841
8357
|
}
|
|
7842
8358
|
});
|
|
7843
8359
|
|
|
7844
8360
|
// src/layout.ts
|
|
7845
|
-
import { join as
|
|
8361
|
+
import { join as join5, resolve as resolve3 } from "node:path";
|
|
7846
8362
|
function resolveLayout(distDir2, exists) {
|
|
7847
8363
|
const pkg = resolve3(distDir2, "..");
|
|
7848
|
-
if (exists(
|
|
8364
|
+
if (exists(join5(pkg, "prisma", "schema.prisma"))) {
|
|
7849
8365
|
return {
|
|
7850
8366
|
root: pkg,
|
|
7851
|
-
schema:
|
|
7852
|
-
server:
|
|
7853
|
-
web: exists(
|
|
8367
|
+
schema: join5(pkg, "prisma", "schema.prisma"),
|
|
8368
|
+
server: join5(pkg, "dist", "server.js"),
|
|
8369
|
+
web: exists(join5(pkg, "web")) ? join5(pkg, "web") : null
|
|
7854
8370
|
};
|
|
7855
8371
|
}
|
|
7856
8372
|
const repo = resolve3(distDir2, "../..");
|
|
7857
|
-
if (exists(
|
|
8373
|
+
if (exists(join5(repo, "server", "prisma", "schema.prisma"))) {
|
|
7858
8374
|
return {
|
|
7859
8375
|
root: repo,
|
|
7860
|
-
schema:
|
|
7861
|
-
server:
|
|
7862
|
-
web: exists(
|
|
8376
|
+
schema: join5(repo, "server", "prisma", "schema.prisma"),
|
|
8377
|
+
server: join5(repo, "server", "dist", "server.js"),
|
|
8378
|
+
web: exists(join5(repo, "src", "dist")) ? join5(repo, "src", "dist") : null
|
|
7863
8379
|
};
|
|
7864
8380
|
}
|
|
7865
8381
|
throw new Error(`hanoman: prisma/schema.prisma tak ditemukan dari ${distDir2} \u2014 instalasi rusak?`);
|
|
@@ -7878,7 +8394,7 @@ __export(update_exports, {
|
|
|
7878
8394
|
default: () => update,
|
|
7879
8395
|
planUpdate: () => planUpdate
|
|
7880
8396
|
});
|
|
7881
|
-
import { execFileSync } from "node:child_process";
|
|
8397
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
7882
8398
|
function planUpdate(current, latest) {
|
|
7883
8399
|
if (!latest) return { action: "unknown", current, latest: null };
|
|
7884
8400
|
return compareSemver(latest, current) > 0 ? { action: "install", current, latest } : { action: "up-to-date", current, latest };
|
|
@@ -7916,7 +8432,7 @@ async function update(argv, ctx) {
|
|
|
7916
8432
|
return 0;
|
|
7917
8433
|
}
|
|
7918
8434
|
try {
|
|
7919
|
-
|
|
8435
|
+
execFileSync2("npm", [...INSTALL_ARGS], { stdio: "inherit" });
|
|
7920
8436
|
} catch {
|
|
7921
8437
|
ctx.stderr("npm i -g gagal \u2014 jalankan manual (mungkin butuh sudo)\n");
|
|
7922
8438
|
return 1;
|
|
@@ -7940,6 +8456,7 @@ var init_update = __esm({
|
|
|
7940
8456
|
// src/commands/start.ts
|
|
7941
8457
|
var start_exports = {};
|
|
7942
8458
|
__export(start_exports, {
|
|
8459
|
+
MAX_CONFIG_RESTARTS: () => MAX_CONFIG_RESTARTS,
|
|
7943
8460
|
MAX_UPDATE_RESTARTS: () => MAX_UPDATE_RESTARTS,
|
|
7944
8461
|
applyMigrations: () => applyMigrations,
|
|
7945
8462
|
default: () => start,
|
|
@@ -7950,30 +8467,31 @@ __export(start_exports, {
|
|
|
7950
8467
|
parseStartArgs: () => parseStartArgs,
|
|
7951
8468
|
planSupervisorStep: () => planSupervisorStep,
|
|
7952
8469
|
prismaClientUsable: () => prismaClientUsable,
|
|
7953
|
-
serverEnv: () => serverEnv
|
|
8470
|
+
serverEnv: () => serverEnv,
|
|
8471
|
+
spawnEnv: () => spawnEnv
|
|
7954
8472
|
});
|
|
7955
|
-
import { spawn, execFileSync as
|
|
8473
|
+
import { spawn, execFileSync as execFileSync3 } from "node:child_process";
|
|
7956
8474
|
import { createRequire } from "node:module";
|
|
7957
|
-
import { existsSync as existsSync3, mkdirSync, chmodSync as
|
|
8475
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, chmodSync as chmodSync3 } from "node:fs";
|
|
7958
8476
|
import { dirname as dirname4, resolve as resolvePath } from "node:path";
|
|
7959
8477
|
import { fileURLToPath } from "node:url";
|
|
7960
8478
|
function parseStartArgs(argv) {
|
|
7961
8479
|
const out = { port: null, host: null, db: null, migrate: true };
|
|
7962
|
-
const value = (i,
|
|
8480
|
+
const value = (i, flag2, inline) => {
|
|
7963
8481
|
const v = inline ?? argv[i + 1];
|
|
7964
|
-
if (v === void 0 || v.startsWith("--")) throw new Error(`${
|
|
8482
|
+
if (v === void 0 || v.startsWith("--")) throw new Error(`${flag2} butuh nilai`);
|
|
7965
8483
|
return v;
|
|
7966
8484
|
};
|
|
7967
8485
|
for (let i = 0; i < argv.length; i++) {
|
|
7968
8486
|
const raw = argv[i];
|
|
7969
8487
|
const eq = raw.indexOf("=");
|
|
7970
|
-
const
|
|
8488
|
+
const flag2 = eq === -1 ? raw : raw.slice(0, eq);
|
|
7971
8489
|
const inline = eq === -1 ? void 0 : raw.slice(eq + 1);
|
|
7972
|
-
if (
|
|
8490
|
+
if (flag2 === "--no-migrate") {
|
|
7973
8491
|
out.migrate = false;
|
|
7974
8492
|
continue;
|
|
7975
8493
|
}
|
|
7976
|
-
if (
|
|
8494
|
+
if (flag2 === "--port") {
|
|
7977
8495
|
const v = value(i, "--port", inline);
|
|
7978
8496
|
const n2 = Number(v);
|
|
7979
8497
|
if (!Number.isInteger(n2) || n2 <= 0) throw new Error(`--port harus angka, dapat "${v}"`);
|
|
@@ -7981,12 +8499,12 @@ function parseStartArgs(argv) {
|
|
|
7981
8499
|
if (inline === void 0) i++;
|
|
7982
8500
|
continue;
|
|
7983
8501
|
}
|
|
7984
|
-
if (
|
|
8502
|
+
if (flag2 === "--host") {
|
|
7985
8503
|
out.host = value(i, "--host", inline);
|
|
7986
8504
|
if (inline === void 0) i++;
|
|
7987
8505
|
continue;
|
|
7988
8506
|
}
|
|
7989
|
-
if (
|
|
8507
|
+
if (flag2 === "--db") {
|
|
7990
8508
|
out.db = value(i, "--db", inline);
|
|
7991
8509
|
if (inline === void 0) i++;
|
|
7992
8510
|
continue;
|
|
@@ -8001,7 +8519,7 @@ function distDir() {
|
|
|
8001
8519
|
function runPrisma(args, dbUrl) {
|
|
8002
8520
|
const prismaCli = prismaCliPath(createRequire(import.meta.url).resolve);
|
|
8003
8521
|
try {
|
|
8004
|
-
const out =
|
|
8522
|
+
const out = execFileSync3(process.execPath, [prismaCli, ...args], {
|
|
8005
8523
|
env: { ...process.env, DATABASE_URL: dbUrl },
|
|
8006
8524
|
encoding: "utf8",
|
|
8007
8525
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8050,7 +8568,9 @@ async function ensurePrismaClient(schema, dbUrl, ctx) {
|
|
|
8050
8568
|
function repairSpawnHelperEarly(ctx) {
|
|
8051
8569
|
repairSpawnHelper(createRequire(import.meta.url).resolve, ctx.stdout);
|
|
8052
8570
|
}
|
|
8053
|
-
function planSupervisorStep(code, restartsUsed) {
|
|
8571
|
+
function planSupervisorStep(code, restartsUsed, configRestartsUsed = 0) {
|
|
8572
|
+
if (code === CONFIG_RESTART_EXIT)
|
|
8573
|
+
return configRestartsUsed >= MAX_CONFIG_RESTARTS ? { action: "exit", code } : { action: "restart" };
|
|
8054
8574
|
if (code !== UPDATE_RESTART_EXIT) return { action: "exit", code };
|
|
8055
8575
|
if (restartsUsed >= MAX_UPDATE_RESTARTS) return { action: "exit", code };
|
|
8056
8576
|
return { action: "update" };
|
|
@@ -8068,14 +8588,20 @@ function serverEnv(o) {
|
|
|
8068
8588
|
}
|
|
8069
8589
|
function installLatest() {
|
|
8070
8590
|
try {
|
|
8071
|
-
|
|
8591
|
+
execFileSync3("npm", [...INSTALL_ARGS], { stdio: "inherit" });
|
|
8072
8592
|
return { ok: true };
|
|
8073
8593
|
} catch (e) {
|
|
8074
8594
|
return { ok: false, reason: e.message || "npm i -g gagal" };
|
|
8075
8595
|
}
|
|
8076
8596
|
}
|
|
8077
|
-
function
|
|
8078
|
-
|
|
8597
|
+
function spawnEnv(fileEnv, processEnv, server) {
|
|
8598
|
+
return { ...fileEnv, ...processEnv, ...server };
|
|
8599
|
+
}
|
|
8600
|
+
function runServer(serverJs, env, fileEnv) {
|
|
8601
|
+
const child = spawn(process.execPath, [serverJs], {
|
|
8602
|
+
stdio: "inherit",
|
|
8603
|
+
env: spawnEnv(fileEnv, process.env, env)
|
|
8604
|
+
});
|
|
8079
8605
|
const handlers = ["SIGINT", "SIGTERM"].map((sig) => [sig, () => child.kill(sig)]);
|
|
8080
8606
|
for (const [sig, h] of handlers) process.on(sig, h);
|
|
8081
8607
|
return new Promise((res) => child.on("exit", (code) => {
|
|
@@ -8107,9 +8633,9 @@ async function start(argv, ctx) {
|
|
|
8107
8633
|
if (notice) ctx.stderr(`${notice}
|
|
8108
8634
|
`);
|
|
8109
8635
|
const home = resolveHome(ctx.env);
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8636
|
+
mkdirSync2(home, { recursive: true, mode: 448 });
|
|
8637
|
+
chmodSync3(home, 448);
|
|
8638
|
+
mkdirSync2(dirname4(dbFilePath(dbUrl)), { recursive: true, mode: 448 });
|
|
8113
8639
|
if (!existsSync3(layout.server)) {
|
|
8114
8640
|
ctx.stderr(`hanoman: bundle server tak ada di ${layout.server} \u2014 jalankan \`pnpm build\` dulu
|
|
8115
8641
|
`);
|
|
@@ -8130,23 +8656,34 @@ ${hint}
|
|
|
8130
8656
|
return 1;
|
|
8131
8657
|
}
|
|
8132
8658
|
}
|
|
8133
|
-
if (existsSync3(dbFilePath(dbUrl)))
|
|
8659
|
+
if (existsSync3(dbFilePath(dbUrl))) chmodSync3(dbFilePath(dbUrl), 384);
|
|
8134
8660
|
const port = opts.port ?? Number(ctx.env.PORT ?? 8787);
|
|
8135
8661
|
const host = opts.host ?? ctx.env.HOST ?? "127.0.0.1";
|
|
8136
8662
|
ctx.stdout(`hanoman \xB7 http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}
|
|
8137
8663
|
`);
|
|
8138
8664
|
const env = serverEnv({ dbUrl, port, host, home, web: layout.web ?? null });
|
|
8139
8665
|
let restartsUsed = 0;
|
|
8666
|
+
let configRestartsUsed = 0;
|
|
8140
8667
|
for (; ; ) {
|
|
8141
|
-
const code = await runServer(layout.server, env);
|
|
8142
|
-
const step = planSupervisorStep(code, restartsUsed);
|
|
8668
|
+
const code = await runServer(layout.server, env, readConfigEnv(home));
|
|
8669
|
+
const step = planSupervisorStep(code, restartsUsed, configRestartsUsed);
|
|
8143
8670
|
if (step.action === "exit") {
|
|
8144
8671
|
if (code === UPDATE_RESTART_EXIT) {
|
|
8145
8672
|
ctx.stderr(`hanoman: jatah update-restart (${MAX_UPDATE_RESTARTS}) habis \u2014 keluar tanpa memasang
|
|
8673
|
+
`);
|
|
8674
|
+
}
|
|
8675
|
+
if (code === CONFIG_RESTART_EXIT) {
|
|
8676
|
+
ctx.stderr(`hanoman: jatah restart-konfigurasi (${MAX_CONFIG_RESTARTS}) habis \u2014 keluar
|
|
8146
8677
|
`);
|
|
8147
8678
|
}
|
|
8148
8679
|
return step.code;
|
|
8149
8680
|
}
|
|
8681
|
+
if (step.action === "restart") {
|
|
8682
|
+
configRestartsUsed++;
|
|
8683
|
+
ctx.stdout(`hanoman \xB7 konfigurasi berubah; menjalankan ulang (${configRestartsUsed}/${MAX_CONFIG_RESTARTS})
|
|
8684
|
+
`);
|
|
8685
|
+
continue;
|
|
8686
|
+
}
|
|
8150
8687
|
restartsUsed++;
|
|
8151
8688
|
ctx.stdout(`hanoman \xB7 memasang versi terbaru dari npm (${restartsUsed}/${MAX_UPDATE_RESTARTS})
|
|
8152
8689
|
`);
|
|
@@ -8176,7 +8713,7 @@ ${hint}
|
|
|
8176
8713
|
ctx.stdout("hanoman \xB7 terpasang; menjalankan ulang\n");
|
|
8177
8714
|
}
|
|
8178
8715
|
}
|
|
8179
|
-
var MAX_UPDATE_RESTARTS;
|
|
8716
|
+
var MAX_UPDATE_RESTARTS, MAX_CONFIG_RESTARTS;
|
|
8180
8717
|
var init_start = __esm({
|
|
8181
8718
|
"src/commands/start.ts"() {
|
|
8182
8719
|
"use strict";
|
|
@@ -8185,6 +8722,7 @@ var init_start = __esm({
|
|
|
8185
8722
|
init_layout();
|
|
8186
8723
|
init_update();
|
|
8187
8724
|
MAX_UPDATE_RESTARTS = 5;
|
|
8725
|
+
MAX_CONFIG_RESTARTS = 5;
|
|
8188
8726
|
}
|
|
8189
8727
|
});
|
|
8190
8728
|
|
|
@@ -8194,8 +8732,8 @@ __export(doctor_exports, {
|
|
|
8194
8732
|
default: () => doctor,
|
|
8195
8733
|
doctorReport: () => doctorReport
|
|
8196
8734
|
});
|
|
8197
|
-
import { execFileSync as
|
|
8198
|
-
import { accessSync, constants, existsSync as existsSync4 } from "node:fs";
|
|
8735
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
8736
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync4 } from "node:fs";
|
|
8199
8737
|
import { dirname as dirname5, resolve as resolve4 } from "node:path";
|
|
8200
8738
|
function doctorReport(p) {
|
|
8201
8739
|
const major = Number(/^v?(\d+)/.exec(p.node)?.[1] ?? 0);
|
|
@@ -8244,7 +8782,7 @@ function writable(path) {
|
|
|
8244
8782
|
for (let p = resolve4(path); ; ) {
|
|
8245
8783
|
if (existsSync4(p)) {
|
|
8246
8784
|
try {
|
|
8247
|
-
|
|
8785
|
+
accessSync2(p, constants2.W_OK);
|
|
8248
8786
|
return true;
|
|
8249
8787
|
} catch {
|
|
8250
8788
|
return false;
|
|
@@ -8257,7 +8795,7 @@ function writable(path) {
|
|
|
8257
8795
|
}
|
|
8258
8796
|
function version(bin, args) {
|
|
8259
8797
|
try {
|
|
8260
|
-
return
|
|
8798
|
+
return execFileSync4(bin, args, { encoding: "utf8", timeout: 1e4, stdio: ["ignore", "pipe", "ignore"] }).trim().split("\n")[0] ?? null;
|
|
8261
8799
|
} catch {
|
|
8262
8800
|
return null;
|
|
8263
8801
|
}
|
|
@@ -8287,21 +8825,10 @@ async function doctor(_argv, ctx) {
|
|
|
8287
8825
|
const s2 = scanAgentSkills(a, ctx.env);
|
|
8288
8826
|
return methodStatus(method, a, { skills: s2.skills.map((k) => k.id), packages: s2.packages });
|
|
8289
8827
|
});
|
|
8290
|
-
const
|
|
8291
|
-
const
|
|
8292
|
-
const
|
|
8293
|
-
const
|
|
8294
|
-
let credentialsReadable = false;
|
|
8295
|
-
if (credentialDir) {
|
|
8296
|
-
try {
|
|
8297
|
-
accessSync(credentialDir, constants.R_OK);
|
|
8298
|
-
credentialsReadable = true;
|
|
8299
|
-
} catch {
|
|
8300
|
-
}
|
|
8301
|
-
}
|
|
8302
|
-
const network = ctx.env.HANOMAN_SESSION_NETWORK ?? "hanoman-egress";
|
|
8303
|
-
const networkReady = podman ? version("podman", ["network", "exists", network]) !== null : false;
|
|
8304
|
-
const sandboxReady = ctx.env.HANOMAN_SESSION_SANDBOX === "podman" && rootless && credentialsReadable && !!ctx.env.HANOMAN_EGRESS_PROXY && networkReady;
|
|
8828
|
+
const podmanFacts = collectProbeFacts(ctx.env);
|
|
8829
|
+
const podman = podmanFacts.podman;
|
|
8830
|
+
const sandboxRequired = resolveHardening(ctx.env);
|
|
8831
|
+
const sandboxReady = allReady(prerequisites(ctx.env, podmanFacts));
|
|
8305
8832
|
const r = doctorReport({
|
|
8306
8833
|
node: process.version,
|
|
8307
8834
|
git: version("git", ["--version"]),
|
|
@@ -17217,13 +17744,13 @@ function fillCacheFields(method, result) {
|
|
|
17217
17744
|
const provided = result;
|
|
17218
17745
|
const ttlMs = isValidCacheTtlMs(provided["ttlMs"]) ? provided["ttlMs"] : resolveTtlMs(fallback);
|
|
17219
17746
|
const cacheScope = isValidCacheScope(provided["cacheScope"]) ? provided["cacheScope"] : resolveCacheScope(fallback);
|
|
17220
|
-
const
|
|
17747
|
+
const filled3 = {
|
|
17221
17748
|
...provided,
|
|
17222
17749
|
ttlMs,
|
|
17223
17750
|
cacheScope
|
|
17224
17751
|
};
|
|
17225
|
-
delete
|
|
17226
|
-
return
|
|
17752
|
+
delete filled3[RESULT_CACHE_HINT_FALLBACK];
|
|
17753
|
+
return filled3;
|
|
17227
17754
|
}
|
|
17228
17755
|
function isPlainObject$5(value) {
|
|
17229
17756
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -20225,9 +20752,9 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20225
20752
|
const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
|
|
20226
20753
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
20227
20754
|
}
|
|
20228
|
-
optimizeNames(names,
|
|
20755
|
+
optimizeNames(names, constants3) {
|
|
20229
20756
|
if (!names[this.name.str]) return;
|
|
20230
|
-
if (this.rhs) this.rhs = optimizeExpr(this.rhs, names,
|
|
20757
|
+
if (this.rhs) this.rhs = optimizeExpr(this.rhs, names, constants3);
|
|
20231
20758
|
return this;
|
|
20232
20759
|
}
|
|
20233
20760
|
get names() {
|
|
@@ -20244,9 +20771,9 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20244
20771
|
render({ _n }) {
|
|
20245
20772
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
20246
20773
|
}
|
|
20247
|
-
optimizeNames(names,
|
|
20774
|
+
optimizeNames(names, constants3) {
|
|
20248
20775
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects) return;
|
|
20249
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
20776
|
+
this.rhs = optimizeExpr(this.rhs, names, constants3);
|
|
20250
20777
|
return this;
|
|
20251
20778
|
}
|
|
20252
20779
|
get names() {
|
|
@@ -20305,8 +20832,8 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20305
20832
|
optimizeNodes() {
|
|
20306
20833
|
return `${this.code}` ? this : void 0;
|
|
20307
20834
|
}
|
|
20308
|
-
optimizeNames(names,
|
|
20309
|
-
this.code = optimizeExpr(this.code, names,
|
|
20835
|
+
optimizeNames(names, constants3) {
|
|
20836
|
+
this.code = optimizeExpr(this.code, names, constants3);
|
|
20310
20837
|
return this;
|
|
20311
20838
|
}
|
|
20312
20839
|
get names() {
|
|
@@ -20332,12 +20859,12 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20332
20859
|
}
|
|
20333
20860
|
return nodes.length > 0 ? this : void 0;
|
|
20334
20861
|
}
|
|
20335
|
-
optimizeNames(names,
|
|
20862
|
+
optimizeNames(names, constants3) {
|
|
20336
20863
|
const { nodes } = this;
|
|
20337
20864
|
let i = nodes.length;
|
|
20338
20865
|
while (i--) {
|
|
20339
20866
|
const n2 = nodes[i];
|
|
20340
|
-
if (n2.optimizeNames(names,
|
|
20867
|
+
if (n2.optimizeNames(names, constants3)) continue;
|
|
20341
20868
|
subtractNames(names, n2.names);
|
|
20342
20869
|
nodes.splice(i, 1);
|
|
20343
20870
|
}
|
|
@@ -20384,11 +20911,11 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20384
20911
|
if (cond === false || !this.nodes.length) return void 0;
|
|
20385
20912
|
return this;
|
|
20386
20913
|
}
|
|
20387
|
-
optimizeNames(names,
|
|
20914
|
+
optimizeNames(names, constants3) {
|
|
20388
20915
|
var _a3;
|
|
20389
|
-
this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names,
|
|
20390
|
-
if (!(super.optimizeNames(names,
|
|
20391
|
-
this.condition = optimizeExpr(this.condition, names,
|
|
20916
|
+
this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants3);
|
|
20917
|
+
if (!(super.optimizeNames(names, constants3) || this.else)) return;
|
|
20918
|
+
this.condition = optimizeExpr(this.condition, names, constants3);
|
|
20392
20919
|
return this;
|
|
20393
20920
|
}
|
|
20394
20921
|
get names() {
|
|
@@ -20410,9 +20937,9 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20410
20937
|
render(opts) {
|
|
20411
20938
|
return `for(${this.iteration})` + super.render(opts);
|
|
20412
20939
|
}
|
|
20413
|
-
optimizeNames(names,
|
|
20414
|
-
if (!super.optimizeNames(names,
|
|
20415
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
20940
|
+
optimizeNames(names, constants3) {
|
|
20941
|
+
if (!super.optimizeNames(names, constants3)) return;
|
|
20942
|
+
this.iteration = optimizeExpr(this.iteration, names, constants3);
|
|
20416
20943
|
return this;
|
|
20417
20944
|
}
|
|
20418
20945
|
get names() {
|
|
@@ -20447,9 +20974,9 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20447
20974
|
render(opts) {
|
|
20448
20975
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
20449
20976
|
}
|
|
20450
|
-
optimizeNames(names,
|
|
20451
|
-
if (!super.optimizeNames(names,
|
|
20452
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
20977
|
+
optimizeNames(names, constants3) {
|
|
20978
|
+
if (!super.optimizeNames(names, constants3)) return;
|
|
20979
|
+
this.iterable = optimizeExpr(this.iterable, names, constants3);
|
|
20453
20980
|
return this;
|
|
20454
20981
|
}
|
|
20455
20982
|
get names() {
|
|
@@ -20488,11 +21015,11 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20488
21015
|
(_b = this.finally) === null || _b === void 0 || _b.optimizeNodes();
|
|
20489
21016
|
return this;
|
|
20490
21017
|
}
|
|
20491
|
-
optimizeNames(names,
|
|
21018
|
+
optimizeNames(names, constants3) {
|
|
20492
21019
|
var _a3, _b;
|
|
20493
|
-
super.optimizeNames(names,
|
|
20494
|
-
(_a3 = this.catch) === null || _a3 === void 0 || _a3.optimizeNames(names,
|
|
20495
|
-
(_b = this.finally) === null || _b === void 0 || _b.optimizeNames(names,
|
|
21020
|
+
super.optimizeNames(names, constants3);
|
|
21021
|
+
(_a3 = this.catch) === null || _a3 === void 0 || _a3.optimizeNames(names, constants3);
|
|
21022
|
+
(_b = this.finally) === null || _b === void 0 || _b.optimizeNames(names, constants3);
|
|
20496
21023
|
return this;
|
|
20497
21024
|
}
|
|
20498
21025
|
get names() {
|
|
@@ -20741,7 +21268,7 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20741
21268
|
function addExprNames(names, from) {
|
|
20742
21269
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
20743
21270
|
}
|
|
20744
|
-
function optimizeExpr(expr, names,
|
|
21271
|
+
function optimizeExpr(expr, names, constants3) {
|
|
20745
21272
|
if (expr instanceof code_1.Name) return replaceName(expr);
|
|
20746
21273
|
if (!canOptimize(expr)) return expr;
|
|
20747
21274
|
return new code_1._Code(expr._items.reduce((items, c) => {
|
|
@@ -20751,13 +21278,13 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20751
21278
|
return items;
|
|
20752
21279
|
}, []));
|
|
20753
21280
|
function replaceName(n2) {
|
|
20754
|
-
const c =
|
|
21281
|
+
const c = constants3[n2.str];
|
|
20755
21282
|
if (c === void 0 || names[n2.str] !== 1) return n2;
|
|
20756
21283
|
delete names[n2.str];
|
|
20757
21284
|
return c;
|
|
20758
21285
|
}
|
|
20759
21286
|
function canOptimize(e) {
|
|
20760
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
21287
|
+
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants3[c.str] !== void 0);
|
|
20761
21288
|
}
|
|
20762
21289
|
}
|
|
20763
21290
|
function subtractNames(names, from) {
|
|
@@ -29122,8 +29649,8 @@ __export(mcp_exports, {
|
|
|
29122
29649
|
agentTokenPath: () => agentTokenPath,
|
|
29123
29650
|
default: () => mcp
|
|
29124
29651
|
});
|
|
29125
|
-
import { readFileSync as
|
|
29126
|
-
import { join as
|
|
29652
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
29653
|
+
import { join as join7 } from "node:path";
|
|
29127
29654
|
async function mcp(argv, ctx) {
|
|
29128
29655
|
const cfg = resolveMcpConfig(argv, ctx.env, () => tokenFile(ctx.env));
|
|
29129
29656
|
const say = (s2) => ctx.stderr(redactToken(s2, cfg.token) + "\n");
|
|
@@ -29154,10 +29681,10 @@ var init_mcp2 = __esm({
|
|
|
29154
29681
|
init_client();
|
|
29155
29682
|
init_server();
|
|
29156
29683
|
init_redact();
|
|
29157
|
-
agentTokenPath = (env) =>
|
|
29684
|
+
agentTokenPath = (env) => join7(resolveHome(env), "agent-token");
|
|
29158
29685
|
tokenFile = (env) => {
|
|
29159
29686
|
try {
|
|
29160
|
-
return
|
|
29687
|
+
return readFileSync3(agentTokenPath(env), "utf8");
|
|
29161
29688
|
} catch {
|
|
29162
29689
|
return null;
|
|
29163
29690
|
}
|
|
@@ -29165,6 +29692,89 @@ var init_mcp2 = __esm({
|
|
|
29165
29692
|
}
|
|
29166
29693
|
});
|
|
29167
29694
|
|
|
29695
|
+
// src/commands/provision.ts
|
|
29696
|
+
var provision_exports = {};
|
|
29697
|
+
__export(provision_exports, {
|
|
29698
|
+
default: () => provisionCmd,
|
|
29699
|
+
localScriptPath: () => localScriptPath,
|
|
29700
|
+
parseProvisionArgs: () => parseProvisionArgs,
|
|
29701
|
+
scriptEnv: () => scriptEnv
|
|
29702
|
+
});
|
|
29703
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
29704
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
29705
|
+
import { join as join8 } from "node:path";
|
|
29706
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
29707
|
+
function parseProvisionArgs(args) {
|
|
29708
|
+
const probe = args.includes("--probe");
|
|
29709
|
+
const items = (flag(args, "with") ?? "").split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
29710
|
+
const profile = flag(args, "profile") ?? "lab";
|
|
29711
|
+
const domain = flag(args, "domain");
|
|
29712
|
+
if (!PROFILES.includes(profile)) return { error: `profil tak dikenal: ${profile}` };
|
|
29713
|
+
if (!probe && items.length === 0) return { error: "sebutkan komponen dengan --with=a,b" };
|
|
29714
|
+
return {
|
|
29715
|
+
mode: probe ? "probe" : "apply",
|
|
29716
|
+
items,
|
|
29717
|
+
profile,
|
|
29718
|
+
domain,
|
|
29719
|
+
dryRun: args.includes("--dry-run"),
|
|
29720
|
+
yes: args.includes("--yes")
|
|
29721
|
+
};
|
|
29722
|
+
}
|
|
29723
|
+
function scriptEnv(a) {
|
|
29724
|
+
return {
|
|
29725
|
+
MODE: a.mode,
|
|
29726
|
+
...a.mode === "apply" ? { ITEMS: a.items.join(","), PROFILE: a.profile } : {},
|
|
29727
|
+
...a.mode === "apply" && a.domain ? { DOMAIN: a.domain } : {},
|
|
29728
|
+
...a.mode === "apply" && a.dryRun ? { DRY_RUN: "1" } : {}
|
|
29729
|
+
};
|
|
29730
|
+
}
|
|
29731
|
+
function localScriptPath(cwd = process.cwd()) {
|
|
29732
|
+
const packed = fileURLToPath2(new URL("../scripts/vps/provision.sh", import.meta.url));
|
|
29733
|
+
if (existsSync5(packed)) return packed;
|
|
29734
|
+
return join8(cwd, "server", "scripts", "vps", "provision.sh");
|
|
29735
|
+
}
|
|
29736
|
+
function runScript(env, ctx) {
|
|
29737
|
+
return new Promise((resolve5) => {
|
|
29738
|
+
const p = spawn2("bash", [localScriptPath(ctx.cwd)], { env: { ...process.env, ...env } });
|
|
29739
|
+
p.stdout.on("data", (d) => ctx.stdout(String(d)));
|
|
29740
|
+
p.stderr.on("data", (d) => ctx.stderr(String(d)));
|
|
29741
|
+
p.on("close", (code) => resolve5(code ?? 1));
|
|
29742
|
+
p.on("error", (e) => {
|
|
29743
|
+
ctx.stderr(`${e}
|
|
29744
|
+
`);
|
|
29745
|
+
resolve5(127);
|
|
29746
|
+
});
|
|
29747
|
+
});
|
|
29748
|
+
}
|
|
29749
|
+
async function provisionCmd(args, ctx) {
|
|
29750
|
+
const parsed = parseProvisionArgs(args);
|
|
29751
|
+
if ("error" in parsed) {
|
|
29752
|
+
ctx.stderr(`${parsed.error}
|
|
29753
|
+
`);
|
|
29754
|
+
return 1;
|
|
29755
|
+
}
|
|
29756
|
+
if (parsed.mode === "probe" || parsed.dryRun) return runScript(scriptEnv(parsed), ctx);
|
|
29757
|
+
if (!parsed.yes) {
|
|
29758
|
+
ctx.stdout("Rencana (dry-run):\n");
|
|
29759
|
+
await runScript({ ...scriptEnv(parsed), DRY_RUN: "1" }, ctx);
|
|
29760
|
+
ctx.stdout("Lanjutkan? [y/N] ");
|
|
29761
|
+
const answer = (await ctx.readStdin?.() ?? "").trim().toLowerCase();
|
|
29762
|
+
if (answer !== "y" && answer !== "ya") {
|
|
29763
|
+
ctx.stderr("dibatalkan\n");
|
|
29764
|
+
return 1;
|
|
29765
|
+
}
|
|
29766
|
+
}
|
|
29767
|
+
return runScript(scriptEnv(parsed), ctx);
|
|
29768
|
+
}
|
|
29769
|
+
var PROFILES, flag;
|
|
29770
|
+
var init_provision = __esm({
|
|
29771
|
+
"src/commands/provision.ts"() {
|
|
29772
|
+
"use strict";
|
|
29773
|
+
PROFILES = ["lab", "production"];
|
|
29774
|
+
flag = (args, name) => args.find((a) => a.startsWith(`--${name}=`))?.split("=").slice(1).join("=");
|
|
29775
|
+
}
|
|
29776
|
+
});
|
|
29777
|
+
|
|
29168
29778
|
// src/commands/migrate-pg.ts
|
|
29169
29779
|
var migrate_pg_exports = {};
|
|
29170
29780
|
__export(migrate_pg_exports, {
|
|
@@ -29177,7 +29787,7 @@ __export(migrate_pg_exports, {
|
|
|
29177
29787
|
migrationSteps: () => migrationSteps,
|
|
29178
29788
|
parseMigrateArgs: () => parseMigrateArgs
|
|
29179
29789
|
});
|
|
29180
|
-
import { existsSync as
|
|
29790
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
29181
29791
|
import { dirname as dirname6, resolve as resolvePath2 } from "node:path";
|
|
29182
29792
|
function coerceInt8(rows, fields, model) {
|
|
29183
29793
|
const cols = fields.filter((f) => f.dataTypeID === INT8_OID).map((f) => f.name);
|
|
@@ -29245,7 +29855,7 @@ async function migratePg(argv, ctx) {
|
|
|
29245
29855
|
let layout;
|
|
29246
29856
|
let dbUrl;
|
|
29247
29857
|
try {
|
|
29248
|
-
layout = resolveLayout(distDir(),
|
|
29858
|
+
layout = resolveLayout(distDir(), existsSync6);
|
|
29249
29859
|
dbUrl = opts.to ? `file:${resolvePath2(opts.to)}` : resolveDbUrl(ctx.env, dirname6(layout.schema));
|
|
29250
29860
|
} catch (e) {
|
|
29251
29861
|
ctx.stderr(`${e.message}
|
|
@@ -29395,11 +30005,11 @@ var init_migrate_pg = __esm({
|
|
|
29395
30005
|
});
|
|
29396
30006
|
|
|
29397
30007
|
// src/config.ts
|
|
29398
|
-
import { readFileSync as
|
|
29399
|
-
import { join as
|
|
30008
|
+
import { readFileSync as readFileSync4, existsSync as existsSync7 } from "node:fs";
|
|
30009
|
+
import { join as join9 } from "node:path";
|
|
29400
30010
|
function loadConfig(repoRoot) {
|
|
29401
|
-
const p =
|
|
29402
|
-
const raw =
|
|
30011
|
+
const p = join9(repoRoot, "hanoman.config.json");
|
|
30012
|
+
const raw = existsSync7(p) ? JSON.parse(readFileSync4(p, "utf8")) : {};
|
|
29403
30013
|
return zHanomanConfig.parse(raw);
|
|
29404
30014
|
}
|
|
29405
30015
|
var init_config3 = __esm({
|
|
@@ -29411,12 +30021,12 @@ var init_config3 = __esm({
|
|
|
29411
30021
|
|
|
29412
30022
|
// src/repo.ts
|
|
29413
30023
|
import { spawnSync } from "node:child_process";
|
|
29414
|
-
import { join as
|
|
30024
|
+
import { join as join10 } from "node:path";
|
|
29415
30025
|
function resolveRepo(cwd) {
|
|
29416
30026
|
const r = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8" });
|
|
29417
30027
|
const root = r.status === 0 ? r.stdout.trim() : cwd;
|
|
29418
30028
|
const { docsDir } = loadConfig(root);
|
|
29419
|
-
return { root, docsDir, indexPath:
|
|
30029
|
+
return { root, docsDir, indexPath: join10(root, docsDir, "README.md") };
|
|
29420
30030
|
}
|
|
29421
30031
|
var init_repo = __esm({
|
|
29422
30032
|
"src/repo.ts"() {
|
|
@@ -29426,10 +30036,10 @@ var init_repo = __esm({
|
|
|
29426
30036
|
});
|
|
29427
30037
|
|
|
29428
30038
|
// src/docs-model.ts
|
|
29429
|
-
import { readFileSync as
|
|
29430
|
-
import { join as
|
|
30039
|
+
import { readFileSync as readFileSync5, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
30040
|
+
import { join as join11, relative } from "node:path";
|
|
29431
30041
|
function parseIndex(indexPath) {
|
|
29432
|
-
const md =
|
|
30042
|
+
const md = readFileSync5(indexPath, "utf8");
|
|
29433
30043
|
const out = /* @__PURE__ */ new Set();
|
|
29434
30044
|
for (const m of md.matchAll(LINK_RE2)) {
|
|
29435
30045
|
let t = m[1].trim();
|
|
@@ -29444,7 +30054,7 @@ function walkDocs(docsRoot) {
|
|
|
29444
30054
|
const walk = (dir) => {
|
|
29445
30055
|
for (const name of readdirSync3(dir)) {
|
|
29446
30056
|
if (name.startsWith(".")) continue;
|
|
29447
|
-
const abs =
|
|
30057
|
+
const abs = join11(dir, name);
|
|
29448
30058
|
if (statSync2(abs).isDirectory()) walk(abs);
|
|
29449
30059
|
else out.push(relative(docsRoot, abs).split("\\").join("/"));
|
|
29450
30060
|
}
|
|
@@ -29476,17 +30086,17 @@ var init_docs_model = __esm({
|
|
|
29476
30086
|
});
|
|
29477
30087
|
|
|
29478
30088
|
// src/verify.ts
|
|
29479
|
-
import { join as
|
|
29480
|
-
import { existsSync as
|
|
30089
|
+
import { join as join12 } from "node:path";
|
|
30090
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
|
|
29481
30091
|
function scanCoverage(cwd) {
|
|
29482
30092
|
const { root, docsDir, indexPath } = resolveRepo(cwd);
|
|
29483
|
-
const docsRoot =
|
|
29484
|
-
if (!
|
|
29485
|
-
if (!
|
|
30093
|
+
const docsRoot = join12(root, docsDir);
|
|
30094
|
+
if (!existsSync8(docsRoot)) return { coverage: 100, cats: [] };
|
|
30095
|
+
if (!existsSync8(indexPath)) throw new Error(`index Source of Truth tidak ada: ${indexPath}`);
|
|
29486
30096
|
const corpus = walkDocs(docsRoot);
|
|
29487
30097
|
const read = (rel) => {
|
|
29488
30098
|
try {
|
|
29489
|
-
return
|
|
30099
|
+
return readFileSync6(join12(docsRoot, rel), "utf8");
|
|
29490
30100
|
} catch {
|
|
29491
30101
|
return null;
|
|
29492
30102
|
}
|
|
@@ -29529,9 +30139,9 @@ var init_docs_scan = __esm({
|
|
|
29529
30139
|
});
|
|
29530
30140
|
|
|
29531
30141
|
// src/index-edit.ts
|
|
29532
|
-
import { readFileSync as
|
|
30142
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
|
|
29533
30143
|
function addLink(indexPath, relPath, category) {
|
|
29534
|
-
let md =
|
|
30144
|
+
let md = readFileSync7(indexPath, "utf8");
|
|
29535
30145
|
if (md.includes(`(${relPath})`)) return;
|
|
29536
30146
|
const base = relPath.split("/").pop().replace(/\.md$/, "");
|
|
29537
30147
|
const line = `- [${base}](${relPath})`;
|
|
@@ -29546,7 +30156,7 @@ ${head}
|
|
|
29546
30156
|
${line}
|
|
29547
30157
|
`;
|
|
29548
30158
|
}
|
|
29549
|
-
|
|
30159
|
+
writeFileSync2(indexPath, md);
|
|
29550
30160
|
}
|
|
29551
30161
|
var init_index_edit = __esm({
|
|
29552
30162
|
"src/index-edit.ts"() {
|
|
@@ -29560,23 +30170,23 @@ __export(docs_index_exports, {
|
|
|
29560
30170
|
default: () => docs_index_default
|
|
29561
30171
|
});
|
|
29562
30172
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
29563
|
-
import { join as
|
|
29564
|
-
import { existsSync as
|
|
30173
|
+
import { join as join13 } from "node:path";
|
|
30174
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
|
|
29565
30175
|
async function docs_index_default(args, ctx) {
|
|
29566
30176
|
const { values } = parseArgs2({ args, options: { check: { type: "boolean" }, fix: { type: "boolean" } }, allowPositionals: true });
|
|
29567
30177
|
const { root, docsDir, indexPath } = resolveRepo(ctx.cwd);
|
|
29568
|
-
const docsRoot =
|
|
30178
|
+
const docsRoot = join13(root, docsDir);
|
|
29569
30179
|
const corpus = walkDocs(docsRoot);
|
|
29570
30180
|
const read = (rel) => {
|
|
29571
30181
|
try {
|
|
29572
|
-
return
|
|
30182
|
+
return readFileSync8(join13(docsRoot, rel), "utf8");
|
|
29573
30183
|
} catch {
|
|
29574
30184
|
return null;
|
|
29575
30185
|
}
|
|
29576
30186
|
};
|
|
29577
30187
|
const linked = linkedSetFrom(INDEX_NAME, corpus, read);
|
|
29578
30188
|
const unlinked = corpus.filter((f) => f !== INDEX_NAME && !linked.has(f));
|
|
29579
|
-
const dangling = [...parseIndex(indexPath)].filter((p) => !
|
|
30189
|
+
const dangling = [...parseIndex(indexPath)].filter((p) => !existsSync9(join13(docsRoot, p)));
|
|
29580
30190
|
if (values.fix) {
|
|
29581
30191
|
for (const f of unlinked) addLink(indexPath, f, f.split("/")[0]);
|
|
29582
30192
|
ctx.stdout(`linked ${unlinked.length} doc(s)
|
|
@@ -29629,7 +30239,7 @@ var init_docs_link = __esm({
|
|
|
29629
30239
|
});
|
|
29630
30240
|
|
|
29631
30241
|
// src/release/pack.ts
|
|
29632
|
-
import { join as
|
|
30242
|
+
import { join as join14 } from "node:path";
|
|
29633
30243
|
function packageJsonFor(version3, deps) {
|
|
29634
30244
|
return {
|
|
29635
30245
|
name: PKG_NAME,
|
|
@@ -29652,7 +30262,7 @@ function packageJsonFor(version3, deps) {
|
|
|
29652
30262
|
prepublishOnly: "node dist/cli.js __verify"
|
|
29653
30263
|
},
|
|
29654
30264
|
engines: { node: ">=20" },
|
|
29655
|
-
files: ["bin", "dist", "web", "prisma", "docs", "README.md", "LICENSE"],
|
|
30265
|
+
files: ["bin", "dist", "web", "prisma", "docs", "scripts", "README.md", "LICENSE"],
|
|
29656
30266
|
dependencies: deps,
|
|
29657
30267
|
// MIT: paket ini didistribusikan publik supaya orang `npm i -g`. `UNLICENSED` berarti "tak ada
|
|
29658
30268
|
// izin pakai" — 0.1.0 terbit dengan kontradiksi itu, dan versi terbit tak bisa diperbaiki.
|
|
@@ -29667,17 +30277,19 @@ function verifyPackedDeps(pkg) {
|
|
|
29667
30277
|
}
|
|
29668
30278
|
function copyPlan(repo) {
|
|
29669
30279
|
return [
|
|
29670
|
-
{ from:
|
|
29671
|
-
{ from:
|
|
29672
|
-
{ from:
|
|
29673
|
-
{ from:
|
|
29674
|
-
{ from:
|
|
29675
|
-
{ from:
|
|
29676
|
-
{ from:
|
|
30280
|
+
{ from: join14(repo, "server/dist/server.js"), to: "dist/server.js" },
|
|
30281
|
+
{ from: join14(repo, "server/dist/build-info.json"), to: "dist/build-info.json" },
|
|
30282
|
+
{ from: join14(repo, "cli/dist/hanoman.js"), to: "dist/cli.js" },
|
|
30283
|
+
{ from: join14(repo, "src/dist"), to: "web", dir: true },
|
|
30284
|
+
{ from: join14(repo, "server/prisma/schema.prisma"), to: "prisma/schema.prisma" },
|
|
30285
|
+
{ from: join14(repo, "server/prisma/migrations"), to: "prisma/migrations", dir: true },
|
|
30286
|
+
{ from: join14(repo, "internal/docs/operations/npm-readme.md"), to: "README.md" },
|
|
29677
30287
|
// SPEC-489 · dibaca runtime oleh pickGuideFile (kandidat `<pkg>/docs/…`). Bukan README:
|
|
29678
30288
|
// README paket adalah npm-readme.md (berhadapan-MANUSIA), ini naskah berhadapan-AGEN.
|
|
29679
|
-
{ from:
|
|
29680
|
-
|
|
30289
|
+
{ from: join14(repo, "docs/agent-integration.md"), to: "docs/agent-integration.md" },
|
|
30290
|
+
// SPEC-883 · skrip VPS (audit/harden/remediate/provision) dibaca runtime lewat scriptPath().
|
|
30291
|
+
{ from: join14(repo, "server/scripts/vps"), to: "scripts/vps", dir: true },
|
|
30292
|
+
{ from: join14(repo, "LICENSE"), to: "LICENSE" }
|
|
29681
30293
|
];
|
|
29682
30294
|
}
|
|
29683
30295
|
var PKG_NAME, REPO_URL, RUNTIME_DEPS, REQUIRED_ARTIFACTS, BIN_SHIM;
|
|
@@ -29710,7 +30322,15 @@ var init_pack = __esm({
|
|
|
29710
30322
|
// SPEC-489 · naskah panduan AI agent — dibaca runtime & disajikan di GET /api/agent-integration.md.
|
|
29711
30323
|
// Tanpa gerbang ini paket bisa terbit tanpa dokumen dan endpoint-nya 404 di SETIAP instalasi npm,
|
|
29712
30324
|
// sementara checkout dev terlihat sehat.
|
|
29713
|
-
"docs/agent-integration.md"
|
|
30325
|
+
"docs/agent-integration.md",
|
|
30326
|
+
// SPEC-883 · skrip VPS dibaca runtime oleh scriptPath(). Sebelum spec ini keempatnya TIDAK
|
|
30327
|
+
// ikut terpaket sementara scriptPath menjangkar ke repoRoot() — di instalasi npm marker
|
|
30328
|
+
// pnpm-workspace.yaml tak ada, repoRoot jatuh ke cwd, dan audit/harden/remediate melempar
|
|
30329
|
+
// ENOENT. Gerbang ini yang membuat kegagalan itu tak bisa terbit lagi.
|
|
30330
|
+
"scripts/vps/audit.sh",
|
|
30331
|
+
"scripts/vps/harden.sh",
|
|
30332
|
+
"scripts/vps/remediate.sh",
|
|
30333
|
+
"scripts/vps/provision.sh"
|
|
29714
30334
|
];
|
|
29715
30335
|
BIN_SHIM = `#!/usr/bin/env node
|
|
29716
30336
|
// SPEC-398 \xB7 shim tipis: seluruh logika ada di dist/cli.js (bundle esbuild).
|
|
@@ -29724,10 +30344,10 @@ var pack_exports = {};
|
|
|
29724
30344
|
__export(pack_exports, {
|
|
29725
30345
|
default: () => pack
|
|
29726
30346
|
});
|
|
29727
|
-
import { cpSync, existsSync as
|
|
29728
|
-
import { dirname as dirname7, join as
|
|
30347
|
+
import { cpSync, existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync9, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
30348
|
+
import { dirname as dirname7, join as join15 } from "node:path";
|
|
29729
30349
|
function depVersions(repo) {
|
|
29730
|
-
const read = (p) => JSON.parse(
|
|
30350
|
+
const read = (p) => JSON.parse(readFileSync9(join15(repo, p), "utf8"));
|
|
29731
30351
|
const server = read("server/package.json"), cli = read("cli/package.json");
|
|
29732
30352
|
const pools = [server.dependencies, server.devDependencies, cli.dependencies, cli.devDependencies];
|
|
29733
30353
|
const out = {};
|
|
@@ -29741,19 +30361,19 @@ function depVersions(repo) {
|
|
|
29741
30361
|
async function pack(argv, ctx) {
|
|
29742
30362
|
let repo;
|
|
29743
30363
|
try {
|
|
29744
|
-
repo = resolveLayout(distDir(),
|
|
30364
|
+
repo = resolveLayout(distDir(), existsSync10).root;
|
|
29745
30365
|
} catch (e) {
|
|
29746
30366
|
ctx.stderr(`${e.message}
|
|
29747
30367
|
`);
|
|
29748
30368
|
return 1;
|
|
29749
30369
|
}
|
|
29750
|
-
if (!
|
|
30370
|
+
if (!existsSync10(join15(repo, "server/prisma/schema.prisma"))) {
|
|
29751
30371
|
ctx.stderr("pack: hanya bisa dijalankan dari checkout repo hanoman\n");
|
|
29752
30372
|
return 1;
|
|
29753
30373
|
}
|
|
29754
30374
|
const outIdx = argv.indexOf("--out");
|
|
29755
|
-
const out = outIdx === -1 ?
|
|
29756
|
-
const version3 = JSON.parse(
|
|
30375
|
+
const out = outIdx === -1 ? join15(repo, "dist-npm") : join15(repo, argv[outIdx + 1] ?? "dist-npm");
|
|
30376
|
+
const version3 = JSON.parse(readFileSync9(join15(repo, "package.json"), "utf8")).version;
|
|
29757
30377
|
if (!version3) {
|
|
29758
30378
|
ctx.stderr("pack: root package.json tanpa field version\n");
|
|
29759
30379
|
return 1;
|
|
@@ -29767,20 +30387,20 @@ async function pack(argv, ctx) {
|
|
|
29767
30387
|
return 1;
|
|
29768
30388
|
}
|
|
29769
30389
|
rmSync(out, { recursive: true, force: true });
|
|
29770
|
-
|
|
30390
|
+
mkdirSync3(join15(out, "bin"), { recursive: true });
|
|
29771
30391
|
for (const item of copyPlan(repo)) {
|
|
29772
|
-
if (!
|
|
30392
|
+
if (!existsSync10(item.from)) {
|
|
29773
30393
|
ctx.stderr(`pack: artefak hilang \u2014 ${item.from} (sudah \`pnpm build\`?)
|
|
29774
30394
|
`);
|
|
29775
30395
|
return 1;
|
|
29776
30396
|
}
|
|
29777
|
-
const dest =
|
|
29778
|
-
|
|
30397
|
+
const dest = join15(out, item.to);
|
|
30398
|
+
mkdirSync3(dirname7(dest), { recursive: true });
|
|
29779
30399
|
cpSync(item.from, dest, item.dir ? { recursive: true } : {});
|
|
29780
30400
|
}
|
|
29781
|
-
|
|
29782
|
-
|
|
29783
|
-
const missing = REQUIRED_ARTIFACTS.filter((a) => !
|
|
30401
|
+
writeFileSync3(join15(out, "bin/hanoman.mjs"), BIN_SHIM);
|
|
30402
|
+
writeFileSync3(join15(out, "package.json"), JSON.stringify(packageJsonFor(version3, deps), null, 2) + "\n");
|
|
30403
|
+
const missing = REQUIRED_ARTIFACTS.filter((a) => !existsSync10(join15(out, a)));
|
|
29784
30404
|
if (missing.length) {
|
|
29785
30405
|
ctx.stderr(`pack: artefak wajib hilang: ${missing.join(", ")}
|
|
29786
30406
|
`);
|
|
@@ -29805,13 +30425,13 @@ var verify_packed_exports = {};
|
|
|
29805
30425
|
__export(verify_packed_exports, {
|
|
29806
30426
|
default: () => verifyPacked
|
|
29807
30427
|
});
|
|
29808
|
-
import { readFileSync as
|
|
29809
|
-
import { join as
|
|
30428
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
30429
|
+
import { join as join16 } from "node:path";
|
|
29810
30430
|
function verifyPacked(_argv, ctx) {
|
|
29811
|
-
const path =
|
|
30431
|
+
const path = join16(process.cwd(), "package.json");
|
|
29812
30432
|
let pkg;
|
|
29813
30433
|
try {
|
|
29814
|
-
pkg = JSON.parse(
|
|
30434
|
+
pkg = JSON.parse(readFileSync10(path, "utf8"));
|
|
29815
30435
|
} catch {
|
|
29816
30436
|
ctx.stderr(`verify: tak bisa membaca ${path}
|
|
29817
30437
|
`);
|
|
@@ -29847,7 +30467,7 @@ function route(argv) {
|
|
|
29847
30467
|
if (argv.includes("--help")) return { cmd: "help", args: [] };
|
|
29848
30468
|
const [group, sub, ...rest] = argv;
|
|
29849
30469
|
if (group === void 0 || group.startsWith("--")) return { cmd: "start", args: argv };
|
|
29850
|
-
if (group === "start" || group === "doctor" || group === "update" || group === "mcp")
|
|
30470
|
+
if (group === "start" || group === "doctor" || group === "update" || group === "mcp" || group === "provision")
|
|
29851
30471
|
return { cmd: group, args: argv.slice(1) };
|
|
29852
30472
|
if (group === "migrate-from-postgres") return { cmd: "migrate-pg", args: argv.slice(1) };
|
|
29853
30473
|
if (group === "docs" && (sub === "scan" || sub === "index" || sub === "link")) {
|
|
@@ -29857,7 +30477,7 @@ function route(argv) {
|
|
|
29857
30477
|
if (group === "__verify") return { cmd: "__verify", args: argv.slice(1) };
|
|
29858
30478
|
return { cmd: "unknown", args: argv };
|
|
29859
30479
|
}
|
|
29860
|
-
async function
|
|
30480
|
+
async function run2(argv, ctx) {
|
|
29861
30481
|
const { cmd, args } = route(argv);
|
|
29862
30482
|
if (cmd === "version") {
|
|
29863
30483
|
ctx.stdout(currentVersion() + "\n");
|
|
@@ -29871,6 +30491,7 @@ async function run(argv, ctx) {
|
|
|
29871
30491
|
if (cmd === "doctor") return (await Promise.resolve().then(() => (init_doctor(), doctor_exports))).default(args, ctx);
|
|
29872
30492
|
if (cmd === "update") return (await Promise.resolve().then(() => (init_update(), update_exports))).default(args, ctx);
|
|
29873
30493
|
if (cmd === "mcp") return (await Promise.resolve().then(() => (init_mcp2(), mcp_exports))).default(args, ctx);
|
|
30494
|
+
if (cmd === "provision") return (await Promise.resolve().then(() => (init_provision(), provision_exports))).default(args, ctx);
|
|
29874
30495
|
if (cmd === "migrate-pg") return (await Promise.resolve().then(() => (init_migrate_pg(), migrate_pg_exports))).default(args, ctx);
|
|
29875
30496
|
if (cmd === "docs:scan") return (await Promise.resolve().then(() => (init_docs_scan(), docs_scan_exports))).default(args, ctx);
|
|
29876
30497
|
if (cmd === "docs:index") return (await Promise.resolve().then(() => (init_docs_index(), docs_index_exports))).default(args, ctx);
|
|
@@ -29893,6 +30514,9 @@ var init_router = __esm({
|
|
|
29893
30514
|
--port <n> --host <h> --db <file> --no-migrate
|
|
29894
30515
|
doctor periksa prasyarat: node, git, tmux, CLI agen, data dir
|
|
29895
30516
|
update [--check] bandingkan versi dengan registry npm; pasang yang terbaru
|
|
30517
|
+
provision [--with=a,b] [--probe] pasang komponen di MESIN INI (hanoman, caddy, claude,
|
|
30518
|
+
[--profile=lab|production] codex, gh, \u2026). --probe hanya melaporkan apa yang ada.
|
|
30519
|
+
[--domain=<d>] [--dry-run] [--yes]
|
|
29896
30520
|
mcp [--read-only] [--host <url>] MCP server stdio untuk klien AI (Claude Code/Desktop,
|
|
29897
30521
|
[--max-bytes <n>] Codex, Cursor, Copilot). Token dari HANOMAN_AGENT_TOKEN.
|
|
29898
30522
|
migrate-from-postgres --from <url> pindahkan data Postgres lama ke SQLite
|
|
@@ -29913,7 +30537,7 @@ var readStdin = () => new Promise((res) => {
|
|
|
29913
30537
|
process.stdin.on("end", () => res(d));
|
|
29914
30538
|
if (process.stdin.isTTY) res("");
|
|
29915
30539
|
});
|
|
29916
|
-
|
|
30540
|
+
run2(process.argv.slice(2), {
|
|
29917
30541
|
cwd: process.cwd(),
|
|
29918
30542
|
env: process.env,
|
|
29919
30543
|
stdout: (s2) => process.stdout.write(s2),
|