hanoman 0.1.55 → 0.1.57
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 +30 -2
- package/dist/build-info.json +3 -3
- package/dist/cli.js +869 -147
- package/dist/server.js +6931 -4914
- package/docs/agent-integration.md +15 -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 +12 -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-v02-ClJhW17L.webp +0 -0
- package/web/assets/index-CSuJUbN1.css +41 -0
- package/web/assets/{index-CZB7jrsl.js → index-dO3TC6HA.js} +1558 -1549
- 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, MAX_SUBS, zSubPage, zSubLimit, zTopicParams, zEventsClientMsg, 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({
|
|
@@ -5198,6 +5502,7 @@ var init_dto = __esm({
|
|
|
5198
5502
|
specId: external_exports.string(),
|
|
5199
5503
|
flow: external_exports.string().optional(),
|
|
5200
5504
|
branch: external_exports.string().optional(),
|
|
5505
|
+
// SPEC-903 · ADR-0143 · keadaan turunan yang sama dengan pil Terminal & pose pet, bukan isi marker.
|
|
5201
5506
|
decision: external_exports.boolean(),
|
|
5202
5507
|
exited: external_exports.boolean()
|
|
5203
5508
|
});
|
|
@@ -5297,8 +5602,10 @@ var init_dto = __esm({
|
|
|
5297
5602
|
// id sesi yang sedang disusun keputusannya (AC-3)
|
|
5298
5603
|
queued: external_exports.array(external_exports.string()).default([]),
|
|
5299
5604
|
// SPEC-479 · id sesi yang menunggu SLOT, bukan manusia
|
|
5605
|
+
// SPEC-903 · ADR-0143 · id sesi yang bit "menunggu"-nya TURUNAN (marker terisi DAN pane diam),
|
|
5606
|
+
// bukan daftar marker mentah — sumbernya `liveDecisions().waiting`, bit yang sama dengan pil
|
|
5607
|
+
// Terminal. Membangunnya ulang dari marker menghasilkan superset yang berselisih tanpa error.
|
|
5300
5608
|
waiting: external_exports.array(external_exports.string()),
|
|
5301
|
-
// id sesi ber-marker keputusan terisi
|
|
5302
5609
|
lastPulseAt: external_exports.string().nullable(),
|
|
5303
5610
|
// SPEC-479 (QA) · keadaan gerbang konkurensi. Batas yang tak terlihat operator terbaca sebagai
|
|
5304
5611
|
// "lead diam" — persis salah baca yang melahirkan tiket itu. `.default()` supaya klien lama
|
|
@@ -5473,6 +5780,11 @@ var init_dto = __esm({
|
|
|
5473
5780
|
zLogin = external_exports.object({ email: external_exports.string().email(), password: external_exports.string().min(1) });
|
|
5474
5781
|
zSignup = external_exports.object({ email: external_exports.string().email(), password: external_exports.string().min(8) });
|
|
5475
5782
|
zSetup = zSignup.extend({ setupToken: external_exports.string().min(1) });
|
|
5783
|
+
zSetupApply = external_exports.object({
|
|
5784
|
+
deployment: external_exports.enum(["local", "public"]),
|
|
5785
|
+
hardening: external_exports.boolean(),
|
|
5786
|
+
acknowledgedUnhardened: external_exports.boolean().optional()
|
|
5787
|
+
});
|
|
5476
5788
|
zChangePassword = external_exports.object({
|
|
5477
5789
|
currentPassword: external_exports.string().min(1),
|
|
5478
5790
|
newPassword: external_exports.string().min(8)
|
|
@@ -5500,8 +5812,66 @@ var init_dto = __esm({
|
|
|
5500
5812
|
});
|
|
5501
5813
|
zAttest = external_exports.object({ note: external_exports.string().max(500).optional() });
|
|
5502
5814
|
zRemediate = external_exports.object({ items: external_exports.array(external_exports.string()).min(1).max(64) });
|
|
5815
|
+
PROVISION_PROFILES = ["lab", "production"];
|
|
5816
|
+
COMPONENT_IDS = [
|
|
5817
|
+
"base",
|
|
5818
|
+
"node",
|
|
5819
|
+
"hanoman",
|
|
5820
|
+
"caddy",
|
|
5821
|
+
"podman",
|
|
5822
|
+
"agent-image",
|
|
5823
|
+
"claude",
|
|
5824
|
+
"codex",
|
|
5825
|
+
"gh"
|
|
5826
|
+
];
|
|
5827
|
+
zProvision = external_exports.object({
|
|
5828
|
+
items: external_exports.array(external_exports.enum(COMPONENT_IDS)).min(1).max(16),
|
|
5829
|
+
profile: external_exports.enum(PROVISION_PROFILES),
|
|
5830
|
+
domain: external_exports.string().min(1).regex(HOST_RE).optional(),
|
|
5831
|
+
confirm: external_exports.boolean().optional(),
|
|
5832
|
+
force: external_exports.boolean().optional()
|
|
5833
|
+
});
|
|
5503
5834
|
UPDATE_RESTART_EXIT = 75;
|
|
5835
|
+
CONFIG_RESTART_EXIT = 76;
|
|
5504
5836
|
zUpdateApplyBody = external_exports.object({ confirm: external_exports.boolean().optional() });
|
|
5837
|
+
MAX_SUBS = 16;
|
|
5838
|
+
zSubPage = external_exports.number().int().min(1).max(1e4);
|
|
5839
|
+
zSubLimit = external_exports.number().int().min(1).max(200);
|
|
5840
|
+
zTopicParams = {
|
|
5841
|
+
schedulerState: external_exports.object({}).strict(),
|
|
5842
|
+
schedulerQueue: external_exports.object({
|
|
5843
|
+
status: external_exports.enum(["queued", "launched", "done", "failed", "canceled"]),
|
|
5844
|
+
page: zSubPage,
|
|
5845
|
+
limit: zSubLimit
|
|
5846
|
+
}).strict(),
|
|
5847
|
+
tickets: external_exports.object({
|
|
5848
|
+
project: external_exports.string().max(120).optional(),
|
|
5849
|
+
status: external_exports.string().max(40).optional(),
|
|
5850
|
+
q: external_exports.string().max(200).optional(),
|
|
5851
|
+
page: zSubPage,
|
|
5852
|
+
limit: zSubLimit
|
|
5853
|
+
}).strict(),
|
|
5854
|
+
lead: external_exports.object({
|
|
5855
|
+
projectId: external_exports.string().max(120).optional(),
|
|
5856
|
+
decPage: zSubPage,
|
|
5857
|
+
flowPage: zSubPage,
|
|
5858
|
+
limit: zSubLimit
|
|
5859
|
+
}).strict(),
|
|
5860
|
+
git: external_exports.object({
|
|
5861
|
+
projectId: external_exports.string().max(120),
|
|
5862
|
+
limit: external_exports.number().int().min(1).max(2e4),
|
|
5863
|
+
branch: external_exports.string().max(200),
|
|
5864
|
+
showRemote: external_exports.boolean(),
|
|
5865
|
+
showTags: external_exports.boolean()
|
|
5866
|
+
}).strict()
|
|
5867
|
+
};
|
|
5868
|
+
zEventsClientMsg = external_exports.object({
|
|
5869
|
+
t: external_exports.literal("sub"),
|
|
5870
|
+
subs: external_exports.array(external_exports.object({
|
|
5871
|
+
topic: external_exports.string().max(40),
|
|
5872
|
+
params: external_exports.record(external_exports.unknown())
|
|
5873
|
+
})).max(MAX_SUBS)
|
|
5874
|
+
}).strict();
|
|
5505
5875
|
zTicketView = external_exports.object({
|
|
5506
5876
|
id: external_exports.string(),
|
|
5507
5877
|
projectId: external_exports.string(),
|
|
@@ -5716,6 +6086,13 @@ var init_api = __esm({
|
|
|
5716
6086
|
terminalSession: (id) => `${API}/terminal/sessions/${id}`,
|
|
5717
6087
|
terminalSteer: (id) => `${API}/terminal/sessions/${id}/steer`,
|
|
5718
6088
|
terminalInterrupt: (id) => `${API}/terminal/sessions/${id}/interrupt`,
|
|
6089
|
+
// SPEC-899 · ADR-0142 · inbox keputusan. Di bawah prefix /terminal supaya ikut capability
|
|
6090
|
+
// `sessions` yang sudah ada — GET menurunkan cabang bacanya, POST cabang tulisnya, tanpa satu
|
|
6091
|
+
// baris pun perubahan di services/agent-capabilities.ts.
|
|
6092
|
+
terminalDialog: (id) => `${API}/terminal/sessions/${id}/dialog`,
|
|
6093
|
+
terminalDialogAnswer: (id) => `${API}/terminal/sessions/${id}/dialog/answer`,
|
|
6094
|
+
// SPEC-909 · ADR-0146 · hentikan hanoman-lead untuk sesi ini sebelum ia mengetik ke pane.
|
|
6095
|
+
terminalDialogTakeover: (id) => `${API}/terminal/sessions/${id}/dialog/takeover`,
|
|
5719
6096
|
terminalPhases: (id) => `${API}/terminal/sessions/${id}/phases`,
|
|
5720
6097
|
// SPEC-230 · review + integrate ber-skop sesi (sesi project-level PRD, tanpa Spec).
|
|
5721
6098
|
sessionReview: (id) => `${API}/terminal/sessions/${id}/review`,
|
|
@@ -5747,6 +6124,11 @@ var init_api = __esm({
|
|
|
5747
6124
|
vpsItemAttest: (id, itemId) => `${API}/vps/${id}/items/${itemId}/attest`,
|
|
5748
6125
|
vpsRemediatePreview: (id) => `${API}/vps/${id}/remediate/preview`,
|
|
5749
6126
|
vpsRemediate: (id) => `${API}/vps/${id}/remediate`,
|
|
6127
|
+
// SPEC-883 · provisioning berbasis katalog
|
|
6128
|
+
vpsComponents: () => `${API}/vps/components`,
|
|
6129
|
+
vpsProbe: (id) => `${API}/vps/${id}/probe`,
|
|
6130
|
+
vpsProvisionPreview: (id) => `${API}/vps/${id}/provision/preview`,
|
|
6131
|
+
vpsProvision: (id) => `${API}/vps/${id}/provision`,
|
|
5750
6132
|
// SPEC-169 · auth
|
|
5751
6133
|
authStatus: `${API}/auth/status`,
|
|
5752
6134
|
authSetup: `${API}/auth/setup`,
|
|
@@ -5755,6 +6137,9 @@ var init_api = __esm({
|
|
|
5755
6137
|
authUsers: `${API}/auth/users`,
|
|
5756
6138
|
authUser: (id) => `${API}/auth/users/${id}`,
|
|
5757
6139
|
authChangePassword: `${API}/auth/change-password`,
|
|
6140
|
+
// SPEC-884 · ADR-0139 · wizard setup awal
|
|
6141
|
+
setupStatus: `${API}/setup/status`,
|
|
6142
|
+
setupApply: `${API}/setup`,
|
|
5758
6143
|
// SPEC-213 · device token + activity log
|
|
5759
6144
|
deviceTokens: `${API}/device-tokens`,
|
|
5760
6145
|
deviceToken: (id) => `${API}/device-tokens/${id}`,
|
|
@@ -6296,6 +6681,9 @@ var init_webhook = __esm({
|
|
|
6296
6681
|
"gitRemote",
|
|
6297
6682
|
"stack",
|
|
6298
6683
|
"helpEnabled",
|
|
6684
|
+
// SPEC-880 · ADR-0135 · penerima harus bisa tahu mesin mana yang memegang project ini
|
|
6685
|
+
// tanpa mendiff dua amplop. Entrinya snapshot: [{deviceId,name}].
|
|
6686
|
+
"handledBy",
|
|
6299
6687
|
"schedulerOptIn",
|
|
6300
6688
|
"leadOptIn",
|
|
6301
6689
|
"autoMerge",
|
|
@@ -6306,7 +6694,7 @@ var init_webhook = __esm({
|
|
|
6306
6694
|
cascade: ["spec", "ticket", "customAgent", "githubIssue"],
|
|
6307
6695
|
events: {
|
|
6308
6696
|
created: { type: "project.created", label: "Project ditambah", when: "Project baru terdaftar di workspace." },
|
|
6309
|
-
updated: { type: "project.updated", label: "Project diubah", when:
|
|
6697
|
+
updated: { type: "project.updated", label: "Project diubah", when: 'Nama, deskripsi, stack, remote, penanda "ditangani oleh", atau opt-in scheduler/lead/Help Center berubah.' },
|
|
6310
6698
|
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
6699
|
},
|
|
6312
6700
|
sample: {
|
|
@@ -6317,6 +6705,7 @@ var init_webhook = __esm({
|
|
|
6317
6705
|
gitRemote: "git@github.com:nafanesia/hanoman.git",
|
|
6318
6706
|
stack: "ts",
|
|
6319
6707
|
helpEnabled: false,
|
|
6708
|
+
handledBy: [{ deviceId: "clq0device1", name: "hm-dena" }],
|
|
6320
6709
|
schedulerOptIn: true,
|
|
6321
6710
|
leadOptIn: false,
|
|
6322
6711
|
autoMerge: null,
|
|
@@ -7306,6 +7695,62 @@ var init_terminal_io = __esm({
|
|
|
7306
7695
|
}
|
|
7307
7696
|
});
|
|
7308
7697
|
|
|
7698
|
+
// ../shared/src/session-dialog.ts
|
|
7699
|
+
var zSessionDialogAnswer;
|
|
7700
|
+
var init_session_dialog = __esm({
|
|
7701
|
+
"../shared/src/session-dialog.ts"() {
|
|
7702
|
+
"use strict";
|
|
7703
|
+
init_zod();
|
|
7704
|
+
zSessionDialogAnswer = external_exports.object({
|
|
7705
|
+
screenHash: external_exports.string().min(1),
|
|
7706
|
+
choice: external_exports.number().int().positive().optional(),
|
|
7707
|
+
choices: external_exports.array(external_exports.number().int().positive()).max(32).optional(),
|
|
7708
|
+
text: external_exports.string().trim().min(1).max(16e3).optional()
|
|
7709
|
+
}).refine(
|
|
7710
|
+
(v) => !(v.choice !== void 0 && v.choices !== void 0),
|
|
7711
|
+
{ message: "choice dan choices tak boleh dikirim bersamaan" }
|
|
7712
|
+
).refine(
|
|
7713
|
+
(v) => v.choice !== void 0 || v.choices !== void 0 || v.text !== void 0,
|
|
7714
|
+
{ message: "jawaban kosong" }
|
|
7715
|
+
);
|
|
7716
|
+
}
|
|
7717
|
+
});
|
|
7718
|
+
|
|
7719
|
+
// ../shared/src/session-ask.ts
|
|
7720
|
+
var ASK_MAX_QUESTIONS, ASK_MAX_OPTIONS, zOption, zQuestion, zAskTool, zTurnEnd;
|
|
7721
|
+
var init_session_ask = __esm({
|
|
7722
|
+
"../shared/src/session-ask.ts"() {
|
|
7723
|
+
"use strict";
|
|
7724
|
+
init_zod();
|
|
7725
|
+
ASK_MAX_QUESTIONS = 8;
|
|
7726
|
+
ASK_MAX_OPTIONS = 32;
|
|
7727
|
+
zOption = external_exports.object({
|
|
7728
|
+
label: external_exports.string().min(1),
|
|
7729
|
+
description: external_exports.string().optional()
|
|
7730
|
+
});
|
|
7731
|
+
zQuestion = external_exports.object({
|
|
7732
|
+
question: external_exports.string().min(1),
|
|
7733
|
+
header: external_exports.string().default(""),
|
|
7734
|
+
multiSelect: external_exports.boolean().default(false),
|
|
7735
|
+
options: external_exports.array(zOption).max(ASK_MAX_OPTIONS).default([])
|
|
7736
|
+
});
|
|
7737
|
+
zAskTool = external_exports.object({
|
|
7738
|
+
hook_event_name: external_exports.literal("PreToolUse"),
|
|
7739
|
+
tool_name: external_exports.literal("AskUserQuestion"),
|
|
7740
|
+
tool_use_id: external_exports.string().min(1),
|
|
7741
|
+
tool_input: external_exports.object({ questions: external_exports.array(zQuestion).min(1).max(ASK_MAX_QUESTIONS) })
|
|
7742
|
+
});
|
|
7743
|
+
zTurnEnd = external_exports.object({
|
|
7744
|
+
hook_event_name: external_exports.literal("Stop"),
|
|
7745
|
+
turn_id: external_exports.string().min(1),
|
|
7746
|
+
last_assistant_message: external_exports.string().optional(),
|
|
7747
|
+
// Codex menyalakan bendera ini saat Stop dipicu oleh hook Stop lain. Melayani giliran yang
|
|
7748
|
+
// dibangkitkan gate mode goal (ADR-0074) berarti lead menjawab dirinya sendiri.
|
|
7749
|
+
stop_hook_active: external_exports.boolean().optional()
|
|
7750
|
+
});
|
|
7751
|
+
}
|
|
7752
|
+
});
|
|
7753
|
+
|
|
7309
7754
|
// ../shared/src/index.ts
|
|
7310
7755
|
var init_src = __esm({
|
|
7311
7756
|
"../shared/src/index.ts"() {
|
|
@@ -7318,6 +7763,7 @@ var init_src = __esm({
|
|
|
7318
7763
|
init_entities();
|
|
7319
7764
|
init_agent();
|
|
7320
7765
|
init_custom_agent();
|
|
7766
|
+
init_builtin_agents();
|
|
7321
7767
|
init_agent_catalog();
|
|
7322
7768
|
init_github();
|
|
7323
7769
|
init_lead();
|
|
@@ -7337,10 +7783,13 @@ var init_src = __esm({
|
|
|
7337
7783
|
init_webhook();
|
|
7338
7784
|
init_mcp();
|
|
7339
7785
|
init_auto_merge();
|
|
7786
|
+
init_handled_by();
|
|
7340
7787
|
init_changelog();
|
|
7341
7788
|
init_cron_expr();
|
|
7342
7789
|
init_terminal_workspace();
|
|
7343
7790
|
init_terminal_io();
|
|
7791
|
+
init_session_dialog();
|
|
7792
|
+
init_session_ask();
|
|
7344
7793
|
}
|
|
7345
7794
|
});
|
|
7346
7795
|
|
|
@@ -7473,9 +7922,18 @@ var init_git = __esm({
|
|
|
7473
7922
|
});
|
|
7474
7923
|
|
|
7475
7924
|
// ../runner/src/settings.ts
|
|
7925
|
+
var EVENT_HOOK_COMMAND;
|
|
7476
7926
|
var init_settings = __esm({
|
|
7477
7927
|
"../runner/src/settings.ts"() {
|
|
7478
7928
|
"use strict";
|
|
7929
|
+
EVENT_HOOK_COMMAND = [
|
|
7930
|
+
'curl -sS -m 2 -X POST "$HANOMAN_EVENT_URL"',
|
|
7931
|
+
"-H 'content-type: application/json'",
|
|
7932
|
+
'-H "authorization: Bearer $HANOMAN_EVENT_TOKEN"',
|
|
7933
|
+
'-H "x-hanoman-session: $HANOMAN_SESSION_ID"',
|
|
7934
|
+
'${HANOMAN_EVENT_HOST:+-H "host: $HANOMAN_EVENT_HOST"}',
|
|
7935
|
+
"--data-binary @- >/dev/null 2>&1; exit 0"
|
|
7936
|
+
].join(" ");
|
|
7479
7937
|
}
|
|
7480
7938
|
});
|
|
7481
7939
|
|
|
@@ -7495,6 +7953,7 @@ var init_codex_settings = __esm({
|
|
|
7495
7953
|
"use strict";
|
|
7496
7954
|
init_src();
|
|
7497
7955
|
init_prompt();
|
|
7956
|
+
init_settings();
|
|
7498
7957
|
}
|
|
7499
7958
|
});
|
|
7500
7959
|
|
|
@@ -7818,6 +8277,149 @@ var init_spawn_helper = __esm({
|
|
|
7818
8277
|
}
|
|
7819
8278
|
});
|
|
7820
8279
|
|
|
8280
|
+
// ../runner/src/config-env.ts
|
|
8281
|
+
import { chmodSync as chmodSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
8282
|
+
import { join as join4 } from "node:path";
|
|
8283
|
+
function configEnvPath(home) {
|
|
8284
|
+
return join4(home, CONFIG_ENV_FILE);
|
|
8285
|
+
}
|
|
8286
|
+
function parseConfigEnv(text) {
|
|
8287
|
+
const out = {};
|
|
8288
|
+
for (const raw of text.split("\n")) {
|
|
8289
|
+
const line = raw.trim();
|
|
8290
|
+
if (!line || line.startsWith("#")) continue;
|
|
8291
|
+
const eq = line.indexOf("=");
|
|
8292
|
+
if (eq <= 0) continue;
|
|
8293
|
+
out[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
|
|
8294
|
+
}
|
|
8295
|
+
return out;
|
|
8296
|
+
}
|
|
8297
|
+
function readConfigEnv(home) {
|
|
8298
|
+
try {
|
|
8299
|
+
return parseConfigEnv(readFileSync2(configEnvPath(home), "utf8"));
|
|
8300
|
+
} catch {
|
|
8301
|
+
return {};
|
|
8302
|
+
}
|
|
8303
|
+
}
|
|
8304
|
+
var CONFIG_ENV_FILE;
|
|
8305
|
+
var init_config_env = __esm({
|
|
8306
|
+
"../runner/src/config-env.ts"() {
|
|
8307
|
+
"use strict";
|
|
8308
|
+
CONFIG_ENV_FILE = "config.env";
|
|
8309
|
+
}
|
|
8310
|
+
});
|
|
8311
|
+
|
|
8312
|
+
// ../runner/src/runtime-profile.ts
|
|
8313
|
+
function resolveHardening(env) {
|
|
8314
|
+
if (env.HANOMAN_HARDENING === "1") return true;
|
|
8315
|
+
return env.HANOMAN_SESSION_SANDBOX === "podman" || filled(env.HANOMAN_PUBLIC_ORIGINS) || filled(env.HANOMAN_TRUST_PROXY);
|
|
8316
|
+
}
|
|
8317
|
+
var filled;
|
|
8318
|
+
var init_runtime_profile = __esm({
|
|
8319
|
+
"../runner/src/runtime-profile.ts"() {
|
|
8320
|
+
"use strict";
|
|
8321
|
+
filled = (v) => !!v && v.trim() !== "";
|
|
8322
|
+
}
|
|
8323
|
+
});
|
|
8324
|
+
|
|
8325
|
+
// ../runner/src/sandbox-probe.ts
|
|
8326
|
+
import { execFileSync } from "node:child_process";
|
|
8327
|
+
import { accessSync, constants } from "node:fs";
|
|
8328
|
+
import { isAbsolute as isAbsolute2 } from "node:path";
|
|
8329
|
+
function trustProxyOk(raw) {
|
|
8330
|
+
const v = raw?.trim();
|
|
8331
|
+
if (!v) return false;
|
|
8332
|
+
if (/^[1-9]\d*$/.test(v)) return true;
|
|
8333
|
+
const values = v.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
8334
|
+
if (!values.length) return false;
|
|
8335
|
+
return !values.some((s2) => s2 === "true" || !s2.includes("/") && s2 !== "loopback");
|
|
8336
|
+
}
|
|
8337
|
+
function prerequisites(env, facts) {
|
|
8338
|
+
const network = env.HANOMAN_SESSION_NETWORK ?? "hanoman-egress";
|
|
8339
|
+
const credentialDir = env.HANOMAN_AGENT_CREDENTIAL_DIR;
|
|
8340
|
+
const scanner = env.HANOMAN_UPLOAD_SCANNER?.trim();
|
|
8341
|
+
return [
|
|
8342
|
+
{
|
|
8343
|
+
id: "podman",
|
|
8344
|
+
label: "Podman rootless",
|
|
8345
|
+
ok: !!facts.podman && facts.rootless,
|
|
8346
|
+
detail: facts.podman ? facts.rootless ? facts.podman : `${facts.podman} \u2014 berjalan rootful, butuh rootless` : "podman tak ada"
|
|
8347
|
+
},
|
|
8348
|
+
{
|
|
8349
|
+
id: "network",
|
|
8350
|
+
label: `Network "${network}"`,
|
|
8351
|
+
ok: facts.networkExists,
|
|
8352
|
+
detail: facts.networkExists ? network : `network ${network} belum dibuat`
|
|
8353
|
+
},
|
|
8354
|
+
{
|
|
8355
|
+
id: "egress-proxy",
|
|
8356
|
+
label: "Egress proxy",
|
|
8357
|
+
ok: filled2(env.HANOMAN_EGRESS_PROXY),
|
|
8358
|
+
detail: env.HANOMAN_EGRESS_PROXY ?? "HANOMAN_EGRESS_PROXY belum disetel"
|
|
8359
|
+
},
|
|
8360
|
+
{
|
|
8361
|
+
id: "credential-dir",
|
|
8362
|
+
label: "Dir credential agen",
|
|
8363
|
+
ok: filled2(credentialDir) && facts.credentialDirReadable,
|
|
8364
|
+
detail: !filled2(credentialDir) ? "HANOMAN_AGENT_CREDENTIAL_DIR belum disetel" : facts.credentialDirReadable ? credentialDir : `${credentialDir} \u2014 tak terbaca`
|
|
8365
|
+
},
|
|
8366
|
+
{
|
|
8367
|
+
id: "control-origin",
|
|
8368
|
+
label: "Control origin",
|
|
8369
|
+
ok: filled2(env.HANOMAN_CONTROL_ORIGINS),
|
|
8370
|
+
detail: env.HANOMAN_CONTROL_ORIGINS ?? "HANOMAN_CONTROL_ORIGINS belum disetel"
|
|
8371
|
+
},
|
|
8372
|
+
{
|
|
8373
|
+
id: "trust-proxy",
|
|
8374
|
+
label: "Trusted proxy hop/CIDR",
|
|
8375
|
+
ok: trustProxyOk(env.HANOMAN_TRUST_PROXY),
|
|
8376
|
+
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"
|
|
8377
|
+
},
|
|
8378
|
+
{
|
|
8379
|
+
id: "upload-scanner",
|
|
8380
|
+
label: "Scanner upload",
|
|
8381
|
+
ok: !!scanner && isAbsolute2(scanner),
|
|
8382
|
+
detail: !scanner ? "HANOMAN_UPLOAD_SCANNER belum disetel" : isAbsolute2(scanner) ? scanner : `${scanner} \u2014 path harus absolut`
|
|
8383
|
+
}
|
|
8384
|
+
];
|
|
8385
|
+
}
|
|
8386
|
+
function allReady(rows) {
|
|
8387
|
+
return rows.every((r) => r.ok);
|
|
8388
|
+
}
|
|
8389
|
+
function run(file, args) {
|
|
8390
|
+
try {
|
|
8391
|
+
return execFileSync(file, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
8392
|
+
} catch {
|
|
8393
|
+
return null;
|
|
8394
|
+
}
|
|
8395
|
+
}
|
|
8396
|
+
function collectProbeFacts(env) {
|
|
8397
|
+
const podman = run("podman", ["--version"]);
|
|
8398
|
+
const network = env.HANOMAN_SESSION_NETWORK ?? "hanoman-egress";
|
|
8399
|
+
const dir = env.HANOMAN_AGENT_CREDENTIAL_DIR;
|
|
8400
|
+
let credentialDirReadable = false;
|
|
8401
|
+
if (dir) {
|
|
8402
|
+
try {
|
|
8403
|
+
accessSync(dir, constants.R_OK);
|
|
8404
|
+
credentialDirReadable = true;
|
|
8405
|
+
} catch {
|
|
8406
|
+
}
|
|
8407
|
+
}
|
|
8408
|
+
return {
|
|
8409
|
+
podman,
|
|
8410
|
+
rootless: podman ? run("podman", ["info", "--format", "{{.Host.Security.Rootless}}"]) === "true" : false,
|
|
8411
|
+
networkExists: podman ? run("podman", ["network", "exists", network]) !== null : false,
|
|
8412
|
+
credentialDirReadable
|
|
8413
|
+
};
|
|
8414
|
+
}
|
|
8415
|
+
var filled2;
|
|
8416
|
+
var init_sandbox_probe = __esm({
|
|
8417
|
+
"../runner/src/sandbox-probe.ts"() {
|
|
8418
|
+
"use strict";
|
|
8419
|
+
filled2 = (v) => !!v && v.trim() !== "";
|
|
8420
|
+
}
|
|
8421
|
+
});
|
|
8422
|
+
|
|
7821
8423
|
// ../runner/src/index.ts
|
|
7822
8424
|
var init_src2 = __esm({
|
|
7823
8425
|
"../runner/src/index.ts"() {
|
|
@@ -7838,28 +8440,31 @@ var init_src2 = __esm({
|
|
|
7838
8440
|
init_skills();
|
|
7839
8441
|
init_telegram_operator();
|
|
7840
8442
|
init_spawn_helper();
|
|
8443
|
+
init_config_env();
|
|
8444
|
+
init_runtime_profile();
|
|
8445
|
+
init_sandbox_probe();
|
|
7841
8446
|
}
|
|
7842
8447
|
});
|
|
7843
8448
|
|
|
7844
8449
|
// src/layout.ts
|
|
7845
|
-
import { join as
|
|
8450
|
+
import { join as join5, resolve as resolve3 } from "node:path";
|
|
7846
8451
|
function resolveLayout(distDir2, exists) {
|
|
7847
8452
|
const pkg = resolve3(distDir2, "..");
|
|
7848
|
-
if (exists(
|
|
8453
|
+
if (exists(join5(pkg, "prisma", "schema.prisma"))) {
|
|
7849
8454
|
return {
|
|
7850
8455
|
root: pkg,
|
|
7851
|
-
schema:
|
|
7852
|
-
server:
|
|
7853
|
-
web: exists(
|
|
8456
|
+
schema: join5(pkg, "prisma", "schema.prisma"),
|
|
8457
|
+
server: join5(pkg, "dist", "server.js"),
|
|
8458
|
+
web: exists(join5(pkg, "web")) ? join5(pkg, "web") : null
|
|
7854
8459
|
};
|
|
7855
8460
|
}
|
|
7856
8461
|
const repo = resolve3(distDir2, "../..");
|
|
7857
|
-
if (exists(
|
|
8462
|
+
if (exists(join5(repo, "server", "prisma", "schema.prisma"))) {
|
|
7858
8463
|
return {
|
|
7859
8464
|
root: repo,
|
|
7860
|
-
schema:
|
|
7861
|
-
server:
|
|
7862
|
-
web: exists(
|
|
8465
|
+
schema: join5(repo, "server", "prisma", "schema.prisma"),
|
|
8466
|
+
server: join5(repo, "server", "dist", "server.js"),
|
|
8467
|
+
web: exists(join5(repo, "src", "dist")) ? join5(repo, "src", "dist") : null
|
|
7863
8468
|
};
|
|
7864
8469
|
}
|
|
7865
8470
|
throw new Error(`hanoman: prisma/schema.prisma tak ditemukan dari ${distDir2} \u2014 instalasi rusak?`);
|
|
@@ -7878,7 +8483,7 @@ __export(update_exports, {
|
|
|
7878
8483
|
default: () => update,
|
|
7879
8484
|
planUpdate: () => planUpdate
|
|
7880
8485
|
});
|
|
7881
|
-
import { execFileSync } from "node:child_process";
|
|
8486
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
7882
8487
|
function planUpdate(current, latest) {
|
|
7883
8488
|
if (!latest) return { action: "unknown", current, latest: null };
|
|
7884
8489
|
return compareSemver(latest, current) > 0 ? { action: "install", current, latest } : { action: "up-to-date", current, latest };
|
|
@@ -7916,7 +8521,7 @@ async function update(argv, ctx) {
|
|
|
7916
8521
|
return 0;
|
|
7917
8522
|
}
|
|
7918
8523
|
try {
|
|
7919
|
-
|
|
8524
|
+
execFileSync2("npm", [...INSTALL_ARGS], { stdio: "inherit" });
|
|
7920
8525
|
} catch {
|
|
7921
8526
|
ctx.stderr("npm i -g gagal \u2014 jalankan manual (mungkin butuh sudo)\n");
|
|
7922
8527
|
return 1;
|
|
@@ -7940,6 +8545,7 @@ var init_update = __esm({
|
|
|
7940
8545
|
// src/commands/start.ts
|
|
7941
8546
|
var start_exports = {};
|
|
7942
8547
|
__export(start_exports, {
|
|
8548
|
+
MAX_CONFIG_RESTARTS: () => MAX_CONFIG_RESTARTS,
|
|
7943
8549
|
MAX_UPDATE_RESTARTS: () => MAX_UPDATE_RESTARTS,
|
|
7944
8550
|
applyMigrations: () => applyMigrations,
|
|
7945
8551
|
default: () => start,
|
|
@@ -7950,30 +8556,31 @@ __export(start_exports, {
|
|
|
7950
8556
|
parseStartArgs: () => parseStartArgs,
|
|
7951
8557
|
planSupervisorStep: () => planSupervisorStep,
|
|
7952
8558
|
prismaClientUsable: () => prismaClientUsable,
|
|
7953
|
-
serverEnv: () => serverEnv
|
|
8559
|
+
serverEnv: () => serverEnv,
|
|
8560
|
+
spawnEnv: () => spawnEnv
|
|
7954
8561
|
});
|
|
7955
|
-
import { spawn, execFileSync as
|
|
8562
|
+
import { spawn, execFileSync as execFileSync3 } from "node:child_process";
|
|
7956
8563
|
import { createRequire } from "node:module";
|
|
7957
|
-
import { existsSync as existsSync3, mkdirSync, chmodSync as
|
|
8564
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, chmodSync as chmodSync3 } from "node:fs";
|
|
7958
8565
|
import { dirname as dirname4, resolve as resolvePath } from "node:path";
|
|
7959
8566
|
import { fileURLToPath } from "node:url";
|
|
7960
8567
|
function parseStartArgs(argv) {
|
|
7961
8568
|
const out = { port: null, host: null, db: null, migrate: true };
|
|
7962
|
-
const value = (i,
|
|
8569
|
+
const value = (i, flag2, inline) => {
|
|
7963
8570
|
const v = inline ?? argv[i + 1];
|
|
7964
|
-
if (v === void 0 || v.startsWith("--")) throw new Error(`${
|
|
8571
|
+
if (v === void 0 || v.startsWith("--")) throw new Error(`${flag2} butuh nilai`);
|
|
7965
8572
|
return v;
|
|
7966
8573
|
};
|
|
7967
8574
|
for (let i = 0; i < argv.length; i++) {
|
|
7968
8575
|
const raw = argv[i];
|
|
7969
8576
|
const eq = raw.indexOf("=");
|
|
7970
|
-
const
|
|
8577
|
+
const flag2 = eq === -1 ? raw : raw.slice(0, eq);
|
|
7971
8578
|
const inline = eq === -1 ? void 0 : raw.slice(eq + 1);
|
|
7972
|
-
if (
|
|
8579
|
+
if (flag2 === "--no-migrate") {
|
|
7973
8580
|
out.migrate = false;
|
|
7974
8581
|
continue;
|
|
7975
8582
|
}
|
|
7976
|
-
if (
|
|
8583
|
+
if (flag2 === "--port") {
|
|
7977
8584
|
const v = value(i, "--port", inline);
|
|
7978
8585
|
const n2 = Number(v);
|
|
7979
8586
|
if (!Number.isInteger(n2) || n2 <= 0) throw new Error(`--port harus angka, dapat "${v}"`);
|
|
@@ -7981,12 +8588,12 @@ function parseStartArgs(argv) {
|
|
|
7981
8588
|
if (inline === void 0) i++;
|
|
7982
8589
|
continue;
|
|
7983
8590
|
}
|
|
7984
|
-
if (
|
|
8591
|
+
if (flag2 === "--host") {
|
|
7985
8592
|
out.host = value(i, "--host", inline);
|
|
7986
8593
|
if (inline === void 0) i++;
|
|
7987
8594
|
continue;
|
|
7988
8595
|
}
|
|
7989
|
-
if (
|
|
8596
|
+
if (flag2 === "--db") {
|
|
7990
8597
|
out.db = value(i, "--db", inline);
|
|
7991
8598
|
if (inline === void 0) i++;
|
|
7992
8599
|
continue;
|
|
@@ -8001,7 +8608,7 @@ function distDir() {
|
|
|
8001
8608
|
function runPrisma(args, dbUrl) {
|
|
8002
8609
|
const prismaCli = prismaCliPath(createRequire(import.meta.url).resolve);
|
|
8003
8610
|
try {
|
|
8004
|
-
const out =
|
|
8611
|
+
const out = execFileSync3(process.execPath, [prismaCli, ...args], {
|
|
8005
8612
|
env: { ...process.env, DATABASE_URL: dbUrl },
|
|
8006
8613
|
encoding: "utf8",
|
|
8007
8614
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8050,7 +8657,9 @@ async function ensurePrismaClient(schema, dbUrl, ctx) {
|
|
|
8050
8657
|
function repairSpawnHelperEarly(ctx) {
|
|
8051
8658
|
repairSpawnHelper(createRequire(import.meta.url).resolve, ctx.stdout);
|
|
8052
8659
|
}
|
|
8053
|
-
function planSupervisorStep(code, restartsUsed) {
|
|
8660
|
+
function planSupervisorStep(code, restartsUsed, configRestartsUsed = 0) {
|
|
8661
|
+
if (code === CONFIG_RESTART_EXIT)
|
|
8662
|
+
return configRestartsUsed >= MAX_CONFIG_RESTARTS ? { action: "exit", code } : { action: "restart" };
|
|
8054
8663
|
if (code !== UPDATE_RESTART_EXIT) return { action: "exit", code };
|
|
8055
8664
|
if (restartsUsed >= MAX_UPDATE_RESTARTS) return { action: "exit", code };
|
|
8056
8665
|
return { action: "update" };
|
|
@@ -8068,14 +8677,20 @@ function serverEnv(o) {
|
|
|
8068
8677
|
}
|
|
8069
8678
|
function installLatest() {
|
|
8070
8679
|
try {
|
|
8071
|
-
|
|
8680
|
+
execFileSync3("npm", [...INSTALL_ARGS], { stdio: "inherit" });
|
|
8072
8681
|
return { ok: true };
|
|
8073
8682
|
} catch (e) {
|
|
8074
8683
|
return { ok: false, reason: e.message || "npm i -g gagal" };
|
|
8075
8684
|
}
|
|
8076
8685
|
}
|
|
8077
|
-
function
|
|
8078
|
-
|
|
8686
|
+
function spawnEnv(fileEnv, processEnv, server) {
|
|
8687
|
+
return { ...fileEnv, ...processEnv, ...server };
|
|
8688
|
+
}
|
|
8689
|
+
function runServer(serverJs, env, fileEnv) {
|
|
8690
|
+
const child = spawn(process.execPath, [serverJs], {
|
|
8691
|
+
stdio: "inherit",
|
|
8692
|
+
env: spawnEnv(fileEnv, process.env, env)
|
|
8693
|
+
});
|
|
8079
8694
|
const handlers = ["SIGINT", "SIGTERM"].map((sig) => [sig, () => child.kill(sig)]);
|
|
8080
8695
|
for (const [sig, h] of handlers) process.on(sig, h);
|
|
8081
8696
|
return new Promise((res) => child.on("exit", (code) => {
|
|
@@ -8107,9 +8722,9 @@ async function start(argv, ctx) {
|
|
|
8107
8722
|
if (notice) ctx.stderr(`${notice}
|
|
8108
8723
|
`);
|
|
8109
8724
|
const home = resolveHome(ctx.env);
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8725
|
+
mkdirSync2(home, { recursive: true, mode: 448 });
|
|
8726
|
+
chmodSync3(home, 448);
|
|
8727
|
+
mkdirSync2(dirname4(dbFilePath(dbUrl)), { recursive: true, mode: 448 });
|
|
8113
8728
|
if (!existsSync3(layout.server)) {
|
|
8114
8729
|
ctx.stderr(`hanoman: bundle server tak ada di ${layout.server} \u2014 jalankan \`pnpm build\` dulu
|
|
8115
8730
|
`);
|
|
@@ -8130,23 +8745,34 @@ ${hint}
|
|
|
8130
8745
|
return 1;
|
|
8131
8746
|
}
|
|
8132
8747
|
}
|
|
8133
|
-
if (existsSync3(dbFilePath(dbUrl)))
|
|
8748
|
+
if (existsSync3(dbFilePath(dbUrl))) chmodSync3(dbFilePath(dbUrl), 384);
|
|
8134
8749
|
const port = opts.port ?? Number(ctx.env.PORT ?? 8787);
|
|
8135
8750
|
const host = opts.host ?? ctx.env.HOST ?? "127.0.0.1";
|
|
8136
8751
|
ctx.stdout(`hanoman \xB7 http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}
|
|
8137
8752
|
`);
|
|
8138
8753
|
const env = serverEnv({ dbUrl, port, host, home, web: layout.web ?? null });
|
|
8139
8754
|
let restartsUsed = 0;
|
|
8755
|
+
let configRestartsUsed = 0;
|
|
8140
8756
|
for (; ; ) {
|
|
8141
|
-
const code = await runServer(layout.server, env);
|
|
8142
|
-
const step = planSupervisorStep(code, restartsUsed);
|
|
8757
|
+
const code = await runServer(layout.server, env, readConfigEnv(home));
|
|
8758
|
+
const step = planSupervisorStep(code, restartsUsed, configRestartsUsed);
|
|
8143
8759
|
if (step.action === "exit") {
|
|
8144
8760
|
if (code === UPDATE_RESTART_EXIT) {
|
|
8145
8761
|
ctx.stderr(`hanoman: jatah update-restart (${MAX_UPDATE_RESTARTS}) habis \u2014 keluar tanpa memasang
|
|
8762
|
+
`);
|
|
8763
|
+
}
|
|
8764
|
+
if (code === CONFIG_RESTART_EXIT) {
|
|
8765
|
+
ctx.stderr(`hanoman: jatah restart-konfigurasi (${MAX_CONFIG_RESTARTS}) habis \u2014 keluar
|
|
8146
8766
|
`);
|
|
8147
8767
|
}
|
|
8148
8768
|
return step.code;
|
|
8149
8769
|
}
|
|
8770
|
+
if (step.action === "restart") {
|
|
8771
|
+
configRestartsUsed++;
|
|
8772
|
+
ctx.stdout(`hanoman \xB7 konfigurasi berubah; menjalankan ulang (${configRestartsUsed}/${MAX_CONFIG_RESTARTS})
|
|
8773
|
+
`);
|
|
8774
|
+
continue;
|
|
8775
|
+
}
|
|
8150
8776
|
restartsUsed++;
|
|
8151
8777
|
ctx.stdout(`hanoman \xB7 memasang versi terbaru dari npm (${restartsUsed}/${MAX_UPDATE_RESTARTS})
|
|
8152
8778
|
`);
|
|
@@ -8176,7 +8802,7 @@ ${hint}
|
|
|
8176
8802
|
ctx.stdout("hanoman \xB7 terpasang; menjalankan ulang\n");
|
|
8177
8803
|
}
|
|
8178
8804
|
}
|
|
8179
|
-
var MAX_UPDATE_RESTARTS;
|
|
8805
|
+
var MAX_UPDATE_RESTARTS, MAX_CONFIG_RESTARTS;
|
|
8180
8806
|
var init_start = __esm({
|
|
8181
8807
|
"src/commands/start.ts"() {
|
|
8182
8808
|
"use strict";
|
|
@@ -8185,6 +8811,7 @@ var init_start = __esm({
|
|
|
8185
8811
|
init_layout();
|
|
8186
8812
|
init_update();
|
|
8187
8813
|
MAX_UPDATE_RESTARTS = 5;
|
|
8814
|
+
MAX_CONFIG_RESTARTS = 5;
|
|
8188
8815
|
}
|
|
8189
8816
|
});
|
|
8190
8817
|
|
|
@@ -8194,8 +8821,8 @@ __export(doctor_exports, {
|
|
|
8194
8821
|
default: () => doctor,
|
|
8195
8822
|
doctorReport: () => doctorReport
|
|
8196
8823
|
});
|
|
8197
|
-
import { execFileSync as
|
|
8198
|
-
import { accessSync, constants, existsSync as existsSync4 } from "node:fs";
|
|
8824
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
8825
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync4 } from "node:fs";
|
|
8199
8826
|
import { dirname as dirname5, resolve as resolve4 } from "node:path";
|
|
8200
8827
|
function doctorReport(p) {
|
|
8201
8828
|
const major = Number(/^v?(\d+)/.exec(p.node)?.[1] ?? 0);
|
|
@@ -8211,6 +8838,14 @@ function doctorReport(p) {
|
|
|
8211
8838
|
text: p.gh ? `gh ${p.gh}` : "gh \u2014 tak ada (tarik issue akan lewat HTTP + GITHUB_TOKEN)",
|
|
8212
8839
|
fatal: false
|
|
8213
8840
|
},
|
|
8841
|
+
// SPEC-909 · ADR-0146 · NON-FATAL dan itu disengaja: tanpa `curl` hook tetap `exit 0` — ia tak
|
|
8842
|
+
// pernah memblokir agen — tapi hanoman-lead tak akan menerima SATU PUN pertanyaan sesi, dan tak
|
|
8843
|
+
// ada error di mana pun yang akan mengatakannya. Kegagalan senyap justru yang layak dilaporkan.
|
|
8844
|
+
{
|
|
8845
|
+
mark: p.curl ? "\u2713" : "!",
|
|
8846
|
+
text: p.curl ? `curl ${p.curl}` : "curl \u2014 TAK ADA: hanoman-lead tak akan menerima pertanyaan sesi (SPEC-909)",
|
|
8847
|
+
fatal: false
|
|
8848
|
+
},
|
|
8214
8849
|
// Direktori turunan lahir saat dipakai, jadi izin tulis yang belum ada di situ adalah
|
|
8215
8850
|
// peringatan — bukan alasan menyatakan hanoman tak bisa menjalankan sesi. Home tetap fatal.
|
|
8216
8851
|
...p.dirs.map((d) => ({
|
|
@@ -8244,7 +8879,7 @@ function writable(path) {
|
|
|
8244
8879
|
for (let p = resolve4(path); ; ) {
|
|
8245
8880
|
if (existsSync4(p)) {
|
|
8246
8881
|
try {
|
|
8247
|
-
|
|
8882
|
+
accessSync2(p, constants2.W_OK);
|
|
8248
8883
|
return true;
|
|
8249
8884
|
} catch {
|
|
8250
8885
|
return false;
|
|
@@ -8257,7 +8892,7 @@ function writable(path) {
|
|
|
8257
8892
|
}
|
|
8258
8893
|
function version(bin, args) {
|
|
8259
8894
|
try {
|
|
8260
|
-
return
|
|
8895
|
+
return execFileSync4(bin, args, { encoding: "utf8", timeout: 1e4, stdio: ["ignore", "pipe", "ignore"] }).trim().split("\n")[0] ?? null;
|
|
8261
8896
|
} catch {
|
|
8262
8897
|
return null;
|
|
8263
8898
|
}
|
|
@@ -8287,21 +8922,10 @@ async function doctor(_argv, ctx) {
|
|
|
8287
8922
|
const s2 = scanAgentSkills(a, ctx.env);
|
|
8288
8923
|
return methodStatus(method, a, { skills: s2.skills.map((k) => k.id), packages: s2.packages });
|
|
8289
8924
|
});
|
|
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;
|
|
8925
|
+
const podmanFacts = collectProbeFacts(ctx.env);
|
|
8926
|
+
const podman = podmanFacts.podman;
|
|
8927
|
+
const sandboxRequired = resolveHardening(ctx.env);
|
|
8928
|
+
const sandboxReady = allReady(prerequisites(ctx.env, podmanFacts));
|
|
8305
8929
|
const r = doctorReport({
|
|
8306
8930
|
node: process.version,
|
|
8307
8931
|
git: version("git", ["--version"]),
|
|
@@ -8309,6 +8933,7 @@ async function doctor(_argv, ctx) {
|
|
|
8309
8933
|
claude,
|
|
8310
8934
|
codex,
|
|
8311
8935
|
gh: version(ctx.env.HANOMAN_GH_BIN ?? "gh", ["--version"]),
|
|
8936
|
+
curl: version("curl", ["--version"]),
|
|
8312
8937
|
dirs,
|
|
8313
8938
|
web: layout.web !== null,
|
|
8314
8939
|
db,
|
|
@@ -17217,13 +17842,13 @@ function fillCacheFields(method, result) {
|
|
|
17217
17842
|
const provided = result;
|
|
17218
17843
|
const ttlMs = isValidCacheTtlMs(provided["ttlMs"]) ? provided["ttlMs"] : resolveTtlMs(fallback);
|
|
17219
17844
|
const cacheScope = isValidCacheScope(provided["cacheScope"]) ? provided["cacheScope"] : resolveCacheScope(fallback);
|
|
17220
|
-
const
|
|
17845
|
+
const filled3 = {
|
|
17221
17846
|
...provided,
|
|
17222
17847
|
ttlMs,
|
|
17223
17848
|
cacheScope
|
|
17224
17849
|
};
|
|
17225
|
-
delete
|
|
17226
|
-
return
|
|
17850
|
+
delete filled3[RESULT_CACHE_HINT_FALLBACK];
|
|
17851
|
+
return filled3;
|
|
17227
17852
|
}
|
|
17228
17853
|
function isPlainObject$5(value) {
|
|
17229
17854
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -20225,9 +20850,9 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20225
20850
|
const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
|
|
20226
20851
|
return `${varKind} ${this.name}${rhs};` + _n;
|
|
20227
20852
|
}
|
|
20228
|
-
optimizeNames(names,
|
|
20853
|
+
optimizeNames(names, constants3) {
|
|
20229
20854
|
if (!names[this.name.str]) return;
|
|
20230
|
-
if (this.rhs) this.rhs = optimizeExpr(this.rhs, names,
|
|
20855
|
+
if (this.rhs) this.rhs = optimizeExpr(this.rhs, names, constants3);
|
|
20231
20856
|
return this;
|
|
20232
20857
|
}
|
|
20233
20858
|
get names() {
|
|
@@ -20244,9 +20869,9 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20244
20869
|
render({ _n }) {
|
|
20245
20870
|
return `${this.lhs} = ${this.rhs};` + _n;
|
|
20246
20871
|
}
|
|
20247
|
-
optimizeNames(names,
|
|
20872
|
+
optimizeNames(names, constants3) {
|
|
20248
20873
|
if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects) return;
|
|
20249
|
-
this.rhs = optimizeExpr(this.rhs, names,
|
|
20874
|
+
this.rhs = optimizeExpr(this.rhs, names, constants3);
|
|
20250
20875
|
return this;
|
|
20251
20876
|
}
|
|
20252
20877
|
get names() {
|
|
@@ -20305,8 +20930,8 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20305
20930
|
optimizeNodes() {
|
|
20306
20931
|
return `${this.code}` ? this : void 0;
|
|
20307
20932
|
}
|
|
20308
|
-
optimizeNames(names,
|
|
20309
|
-
this.code = optimizeExpr(this.code, names,
|
|
20933
|
+
optimizeNames(names, constants3) {
|
|
20934
|
+
this.code = optimizeExpr(this.code, names, constants3);
|
|
20310
20935
|
return this;
|
|
20311
20936
|
}
|
|
20312
20937
|
get names() {
|
|
@@ -20332,12 +20957,12 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20332
20957
|
}
|
|
20333
20958
|
return nodes.length > 0 ? this : void 0;
|
|
20334
20959
|
}
|
|
20335
|
-
optimizeNames(names,
|
|
20960
|
+
optimizeNames(names, constants3) {
|
|
20336
20961
|
const { nodes } = this;
|
|
20337
20962
|
let i = nodes.length;
|
|
20338
20963
|
while (i--) {
|
|
20339
20964
|
const n2 = nodes[i];
|
|
20340
|
-
if (n2.optimizeNames(names,
|
|
20965
|
+
if (n2.optimizeNames(names, constants3)) continue;
|
|
20341
20966
|
subtractNames(names, n2.names);
|
|
20342
20967
|
nodes.splice(i, 1);
|
|
20343
20968
|
}
|
|
@@ -20384,11 +21009,11 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20384
21009
|
if (cond === false || !this.nodes.length) return void 0;
|
|
20385
21010
|
return this;
|
|
20386
21011
|
}
|
|
20387
|
-
optimizeNames(names,
|
|
21012
|
+
optimizeNames(names, constants3) {
|
|
20388
21013
|
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,
|
|
21014
|
+
this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants3);
|
|
21015
|
+
if (!(super.optimizeNames(names, constants3) || this.else)) return;
|
|
21016
|
+
this.condition = optimizeExpr(this.condition, names, constants3);
|
|
20392
21017
|
return this;
|
|
20393
21018
|
}
|
|
20394
21019
|
get names() {
|
|
@@ -20410,9 +21035,9 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20410
21035
|
render(opts) {
|
|
20411
21036
|
return `for(${this.iteration})` + super.render(opts);
|
|
20412
21037
|
}
|
|
20413
|
-
optimizeNames(names,
|
|
20414
|
-
if (!super.optimizeNames(names,
|
|
20415
|
-
this.iteration = optimizeExpr(this.iteration, names,
|
|
21038
|
+
optimizeNames(names, constants3) {
|
|
21039
|
+
if (!super.optimizeNames(names, constants3)) return;
|
|
21040
|
+
this.iteration = optimizeExpr(this.iteration, names, constants3);
|
|
20416
21041
|
return this;
|
|
20417
21042
|
}
|
|
20418
21043
|
get names() {
|
|
@@ -20447,9 +21072,9 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20447
21072
|
render(opts) {
|
|
20448
21073
|
return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
|
|
20449
21074
|
}
|
|
20450
|
-
optimizeNames(names,
|
|
20451
|
-
if (!super.optimizeNames(names,
|
|
20452
|
-
this.iterable = optimizeExpr(this.iterable, names,
|
|
21075
|
+
optimizeNames(names, constants3) {
|
|
21076
|
+
if (!super.optimizeNames(names, constants3)) return;
|
|
21077
|
+
this.iterable = optimizeExpr(this.iterable, names, constants3);
|
|
20453
21078
|
return this;
|
|
20454
21079
|
}
|
|
20455
21080
|
get names() {
|
|
@@ -20488,11 +21113,11 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20488
21113
|
(_b = this.finally) === null || _b === void 0 || _b.optimizeNodes();
|
|
20489
21114
|
return this;
|
|
20490
21115
|
}
|
|
20491
|
-
optimizeNames(names,
|
|
21116
|
+
optimizeNames(names, constants3) {
|
|
20492
21117
|
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,
|
|
21118
|
+
super.optimizeNames(names, constants3);
|
|
21119
|
+
(_a3 = this.catch) === null || _a3 === void 0 || _a3.optimizeNames(names, constants3);
|
|
21120
|
+
(_b = this.finally) === null || _b === void 0 || _b.optimizeNames(names, constants3);
|
|
20496
21121
|
return this;
|
|
20497
21122
|
}
|
|
20498
21123
|
get names() {
|
|
@@ -20741,7 +21366,7 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20741
21366
|
function addExprNames(names, from) {
|
|
20742
21367
|
return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
|
|
20743
21368
|
}
|
|
20744
|
-
function optimizeExpr(expr, names,
|
|
21369
|
+
function optimizeExpr(expr, names, constants3) {
|
|
20745
21370
|
if (expr instanceof code_1.Name) return replaceName(expr);
|
|
20746
21371
|
if (!canOptimize(expr)) return expr;
|
|
20747
21372
|
return new code_1._Code(expr._items.reduce((items, c) => {
|
|
@@ -20751,13 +21376,13 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
20751
21376
|
return items;
|
|
20752
21377
|
}, []));
|
|
20753
21378
|
function replaceName(n2) {
|
|
20754
|
-
const c =
|
|
21379
|
+
const c = constants3[n2.str];
|
|
20755
21380
|
if (c === void 0 || names[n2.str] !== 1) return n2;
|
|
20756
21381
|
delete names[n2.str];
|
|
20757
21382
|
return c;
|
|
20758
21383
|
}
|
|
20759
21384
|
function canOptimize(e) {
|
|
20760
|
-
return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 &&
|
|
21385
|
+
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
21386
|
}
|
|
20762
21387
|
}
|
|
20763
21388
|
function subtractNames(names, from) {
|
|
@@ -29122,8 +29747,8 @@ __export(mcp_exports, {
|
|
|
29122
29747
|
agentTokenPath: () => agentTokenPath,
|
|
29123
29748
|
default: () => mcp
|
|
29124
29749
|
});
|
|
29125
|
-
import { readFileSync as
|
|
29126
|
-
import { join as
|
|
29750
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
29751
|
+
import { join as join7 } from "node:path";
|
|
29127
29752
|
async function mcp(argv, ctx) {
|
|
29128
29753
|
const cfg = resolveMcpConfig(argv, ctx.env, () => tokenFile(ctx.env));
|
|
29129
29754
|
const say = (s2) => ctx.stderr(redactToken(s2, cfg.token) + "\n");
|
|
@@ -29154,10 +29779,10 @@ var init_mcp2 = __esm({
|
|
|
29154
29779
|
init_client();
|
|
29155
29780
|
init_server();
|
|
29156
29781
|
init_redact();
|
|
29157
|
-
agentTokenPath = (env) =>
|
|
29782
|
+
agentTokenPath = (env) => join7(resolveHome(env), "agent-token");
|
|
29158
29783
|
tokenFile = (env) => {
|
|
29159
29784
|
try {
|
|
29160
|
-
return
|
|
29785
|
+
return readFileSync3(agentTokenPath(env), "utf8");
|
|
29161
29786
|
} catch {
|
|
29162
29787
|
return null;
|
|
29163
29788
|
}
|
|
@@ -29165,6 +29790,89 @@ var init_mcp2 = __esm({
|
|
|
29165
29790
|
}
|
|
29166
29791
|
});
|
|
29167
29792
|
|
|
29793
|
+
// src/commands/provision.ts
|
|
29794
|
+
var provision_exports = {};
|
|
29795
|
+
__export(provision_exports, {
|
|
29796
|
+
default: () => provisionCmd,
|
|
29797
|
+
localScriptPath: () => localScriptPath,
|
|
29798
|
+
parseProvisionArgs: () => parseProvisionArgs,
|
|
29799
|
+
scriptEnv: () => scriptEnv
|
|
29800
|
+
});
|
|
29801
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
29802
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
29803
|
+
import { join as join8 } from "node:path";
|
|
29804
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
29805
|
+
function parseProvisionArgs(args) {
|
|
29806
|
+
const probe = args.includes("--probe");
|
|
29807
|
+
const items = (flag(args, "with") ?? "").split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
29808
|
+
const profile = flag(args, "profile") ?? "lab";
|
|
29809
|
+
const domain = flag(args, "domain");
|
|
29810
|
+
if (!PROFILES.includes(profile)) return { error: `profil tak dikenal: ${profile}` };
|
|
29811
|
+
if (!probe && items.length === 0) return { error: "sebutkan komponen dengan --with=a,b" };
|
|
29812
|
+
return {
|
|
29813
|
+
mode: probe ? "probe" : "apply",
|
|
29814
|
+
items,
|
|
29815
|
+
profile,
|
|
29816
|
+
domain,
|
|
29817
|
+
dryRun: args.includes("--dry-run"),
|
|
29818
|
+
yes: args.includes("--yes")
|
|
29819
|
+
};
|
|
29820
|
+
}
|
|
29821
|
+
function scriptEnv(a) {
|
|
29822
|
+
return {
|
|
29823
|
+
MODE: a.mode,
|
|
29824
|
+
...a.mode === "apply" ? { ITEMS: a.items.join(","), PROFILE: a.profile } : {},
|
|
29825
|
+
...a.mode === "apply" && a.domain ? { DOMAIN: a.domain } : {},
|
|
29826
|
+
...a.mode === "apply" && a.dryRun ? { DRY_RUN: "1" } : {}
|
|
29827
|
+
};
|
|
29828
|
+
}
|
|
29829
|
+
function localScriptPath(cwd = process.cwd()) {
|
|
29830
|
+
const packed = fileURLToPath2(new URL("../scripts/vps/provision.sh", import.meta.url));
|
|
29831
|
+
if (existsSync5(packed)) return packed;
|
|
29832
|
+
return join8(cwd, "server", "scripts", "vps", "provision.sh");
|
|
29833
|
+
}
|
|
29834
|
+
function runScript(env, ctx) {
|
|
29835
|
+
return new Promise((resolve5) => {
|
|
29836
|
+
const p = spawn2("bash", [localScriptPath(ctx.cwd)], { env: { ...process.env, ...env } });
|
|
29837
|
+
p.stdout.on("data", (d) => ctx.stdout(String(d)));
|
|
29838
|
+
p.stderr.on("data", (d) => ctx.stderr(String(d)));
|
|
29839
|
+
p.on("close", (code) => resolve5(code ?? 1));
|
|
29840
|
+
p.on("error", (e) => {
|
|
29841
|
+
ctx.stderr(`${e}
|
|
29842
|
+
`);
|
|
29843
|
+
resolve5(127);
|
|
29844
|
+
});
|
|
29845
|
+
});
|
|
29846
|
+
}
|
|
29847
|
+
async function provisionCmd(args, ctx) {
|
|
29848
|
+
const parsed = parseProvisionArgs(args);
|
|
29849
|
+
if ("error" in parsed) {
|
|
29850
|
+
ctx.stderr(`${parsed.error}
|
|
29851
|
+
`);
|
|
29852
|
+
return 1;
|
|
29853
|
+
}
|
|
29854
|
+
if (parsed.mode === "probe" || parsed.dryRun) return runScript(scriptEnv(parsed), ctx);
|
|
29855
|
+
if (!parsed.yes) {
|
|
29856
|
+
ctx.stdout("Rencana (dry-run):\n");
|
|
29857
|
+
await runScript({ ...scriptEnv(parsed), DRY_RUN: "1" }, ctx);
|
|
29858
|
+
ctx.stdout("Lanjutkan? [y/N] ");
|
|
29859
|
+
const answer = (await ctx.readStdin?.() ?? "").trim().toLowerCase();
|
|
29860
|
+
if (answer !== "y" && answer !== "ya") {
|
|
29861
|
+
ctx.stderr("dibatalkan\n");
|
|
29862
|
+
return 1;
|
|
29863
|
+
}
|
|
29864
|
+
}
|
|
29865
|
+
return runScript(scriptEnv(parsed), ctx);
|
|
29866
|
+
}
|
|
29867
|
+
var PROFILES, flag;
|
|
29868
|
+
var init_provision = __esm({
|
|
29869
|
+
"src/commands/provision.ts"() {
|
|
29870
|
+
"use strict";
|
|
29871
|
+
PROFILES = ["lab", "production"];
|
|
29872
|
+
flag = (args, name) => args.find((a) => a.startsWith(`--${name}=`))?.split("=").slice(1).join("=");
|
|
29873
|
+
}
|
|
29874
|
+
});
|
|
29875
|
+
|
|
29168
29876
|
// src/commands/migrate-pg.ts
|
|
29169
29877
|
var migrate_pg_exports = {};
|
|
29170
29878
|
__export(migrate_pg_exports, {
|
|
@@ -29177,7 +29885,7 @@ __export(migrate_pg_exports, {
|
|
|
29177
29885
|
migrationSteps: () => migrationSteps,
|
|
29178
29886
|
parseMigrateArgs: () => parseMigrateArgs
|
|
29179
29887
|
});
|
|
29180
|
-
import { existsSync as
|
|
29888
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
29181
29889
|
import { dirname as dirname6, resolve as resolvePath2 } from "node:path";
|
|
29182
29890
|
function coerceInt8(rows, fields, model) {
|
|
29183
29891
|
const cols = fields.filter((f) => f.dataTypeID === INT8_OID).map((f) => f.name);
|
|
@@ -29245,7 +29953,7 @@ async function migratePg(argv, ctx) {
|
|
|
29245
29953
|
let layout;
|
|
29246
29954
|
let dbUrl;
|
|
29247
29955
|
try {
|
|
29248
|
-
layout = resolveLayout(distDir(),
|
|
29956
|
+
layout = resolveLayout(distDir(), existsSync6);
|
|
29249
29957
|
dbUrl = opts.to ? `file:${resolvePath2(opts.to)}` : resolveDbUrl(ctx.env, dirname6(layout.schema));
|
|
29250
29958
|
} catch (e) {
|
|
29251
29959
|
ctx.stderr(`${e.message}
|
|
@@ -29395,11 +30103,11 @@ var init_migrate_pg = __esm({
|
|
|
29395
30103
|
});
|
|
29396
30104
|
|
|
29397
30105
|
// src/config.ts
|
|
29398
|
-
import { readFileSync as
|
|
29399
|
-
import { join as
|
|
30106
|
+
import { readFileSync as readFileSync4, existsSync as existsSync7 } from "node:fs";
|
|
30107
|
+
import { join as join9 } from "node:path";
|
|
29400
30108
|
function loadConfig(repoRoot) {
|
|
29401
|
-
const p =
|
|
29402
|
-
const raw =
|
|
30109
|
+
const p = join9(repoRoot, "hanoman.config.json");
|
|
30110
|
+
const raw = existsSync7(p) ? JSON.parse(readFileSync4(p, "utf8")) : {};
|
|
29403
30111
|
return zHanomanConfig.parse(raw);
|
|
29404
30112
|
}
|
|
29405
30113
|
var init_config3 = __esm({
|
|
@@ -29411,12 +30119,12 @@ var init_config3 = __esm({
|
|
|
29411
30119
|
|
|
29412
30120
|
// src/repo.ts
|
|
29413
30121
|
import { spawnSync } from "node:child_process";
|
|
29414
|
-
import { join as
|
|
30122
|
+
import { join as join10 } from "node:path";
|
|
29415
30123
|
function resolveRepo(cwd) {
|
|
29416
30124
|
const r = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8" });
|
|
29417
30125
|
const root = r.status === 0 ? r.stdout.trim() : cwd;
|
|
29418
30126
|
const { docsDir } = loadConfig(root);
|
|
29419
|
-
return { root, docsDir, indexPath:
|
|
30127
|
+
return { root, docsDir, indexPath: join10(root, docsDir, "README.md") };
|
|
29420
30128
|
}
|
|
29421
30129
|
var init_repo = __esm({
|
|
29422
30130
|
"src/repo.ts"() {
|
|
@@ -29426,10 +30134,10 @@ var init_repo = __esm({
|
|
|
29426
30134
|
});
|
|
29427
30135
|
|
|
29428
30136
|
// src/docs-model.ts
|
|
29429
|
-
import { readFileSync as
|
|
29430
|
-
import { join as
|
|
30137
|
+
import { readFileSync as readFileSync5, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
30138
|
+
import { join as join11, relative } from "node:path";
|
|
29431
30139
|
function parseIndex(indexPath) {
|
|
29432
|
-
const md =
|
|
30140
|
+
const md = readFileSync5(indexPath, "utf8");
|
|
29433
30141
|
const out = /* @__PURE__ */ new Set();
|
|
29434
30142
|
for (const m of md.matchAll(LINK_RE2)) {
|
|
29435
30143
|
let t = m[1].trim();
|
|
@@ -29444,7 +30152,7 @@ function walkDocs(docsRoot) {
|
|
|
29444
30152
|
const walk = (dir) => {
|
|
29445
30153
|
for (const name of readdirSync3(dir)) {
|
|
29446
30154
|
if (name.startsWith(".")) continue;
|
|
29447
|
-
const abs =
|
|
30155
|
+
const abs = join11(dir, name);
|
|
29448
30156
|
if (statSync2(abs).isDirectory()) walk(abs);
|
|
29449
30157
|
else out.push(relative(docsRoot, abs).split("\\").join("/"));
|
|
29450
30158
|
}
|
|
@@ -29476,17 +30184,17 @@ var init_docs_model = __esm({
|
|
|
29476
30184
|
});
|
|
29477
30185
|
|
|
29478
30186
|
// src/verify.ts
|
|
29479
|
-
import { join as
|
|
29480
|
-
import { existsSync as
|
|
30187
|
+
import { join as join12 } from "node:path";
|
|
30188
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
|
|
29481
30189
|
function scanCoverage(cwd) {
|
|
29482
30190
|
const { root, docsDir, indexPath } = resolveRepo(cwd);
|
|
29483
|
-
const docsRoot =
|
|
29484
|
-
if (!
|
|
29485
|
-
if (!
|
|
30191
|
+
const docsRoot = join12(root, docsDir);
|
|
30192
|
+
if (!existsSync8(docsRoot)) return { coverage: 100, cats: [] };
|
|
30193
|
+
if (!existsSync8(indexPath)) throw new Error(`index Source of Truth tidak ada: ${indexPath}`);
|
|
29486
30194
|
const corpus = walkDocs(docsRoot);
|
|
29487
30195
|
const read = (rel) => {
|
|
29488
30196
|
try {
|
|
29489
|
-
return
|
|
30197
|
+
return readFileSync6(join12(docsRoot, rel), "utf8");
|
|
29490
30198
|
} catch {
|
|
29491
30199
|
return null;
|
|
29492
30200
|
}
|
|
@@ -29529,9 +30237,9 @@ var init_docs_scan = __esm({
|
|
|
29529
30237
|
});
|
|
29530
30238
|
|
|
29531
30239
|
// src/index-edit.ts
|
|
29532
|
-
import { readFileSync as
|
|
30240
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
|
|
29533
30241
|
function addLink(indexPath, relPath, category) {
|
|
29534
|
-
let md =
|
|
30242
|
+
let md = readFileSync7(indexPath, "utf8");
|
|
29535
30243
|
if (md.includes(`(${relPath})`)) return;
|
|
29536
30244
|
const base = relPath.split("/").pop().replace(/\.md$/, "");
|
|
29537
30245
|
const line = `- [${base}](${relPath})`;
|
|
@@ -29546,7 +30254,7 @@ ${head}
|
|
|
29546
30254
|
${line}
|
|
29547
30255
|
`;
|
|
29548
30256
|
}
|
|
29549
|
-
|
|
30257
|
+
writeFileSync2(indexPath, md);
|
|
29550
30258
|
}
|
|
29551
30259
|
var init_index_edit = __esm({
|
|
29552
30260
|
"src/index-edit.ts"() {
|
|
@@ -29560,23 +30268,23 @@ __export(docs_index_exports, {
|
|
|
29560
30268
|
default: () => docs_index_default
|
|
29561
30269
|
});
|
|
29562
30270
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
29563
|
-
import { join as
|
|
29564
|
-
import { existsSync as
|
|
30271
|
+
import { join as join13 } from "node:path";
|
|
30272
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
|
|
29565
30273
|
async function docs_index_default(args, ctx) {
|
|
29566
30274
|
const { values } = parseArgs2({ args, options: { check: { type: "boolean" }, fix: { type: "boolean" } }, allowPositionals: true });
|
|
29567
30275
|
const { root, docsDir, indexPath } = resolveRepo(ctx.cwd);
|
|
29568
|
-
const docsRoot =
|
|
30276
|
+
const docsRoot = join13(root, docsDir);
|
|
29569
30277
|
const corpus = walkDocs(docsRoot);
|
|
29570
30278
|
const read = (rel) => {
|
|
29571
30279
|
try {
|
|
29572
|
-
return
|
|
30280
|
+
return readFileSync8(join13(docsRoot, rel), "utf8");
|
|
29573
30281
|
} catch {
|
|
29574
30282
|
return null;
|
|
29575
30283
|
}
|
|
29576
30284
|
};
|
|
29577
30285
|
const linked = linkedSetFrom(INDEX_NAME, corpus, read);
|
|
29578
30286
|
const unlinked = corpus.filter((f) => f !== INDEX_NAME && !linked.has(f));
|
|
29579
|
-
const dangling = [...parseIndex(indexPath)].filter((p) => !
|
|
30287
|
+
const dangling = [...parseIndex(indexPath)].filter((p) => !existsSync9(join13(docsRoot, p)));
|
|
29580
30288
|
if (values.fix) {
|
|
29581
30289
|
for (const f of unlinked) addLink(indexPath, f, f.split("/")[0]);
|
|
29582
30290
|
ctx.stdout(`linked ${unlinked.length} doc(s)
|
|
@@ -29629,7 +30337,7 @@ var init_docs_link = __esm({
|
|
|
29629
30337
|
});
|
|
29630
30338
|
|
|
29631
30339
|
// src/release/pack.ts
|
|
29632
|
-
import { join as
|
|
30340
|
+
import { join as join14 } from "node:path";
|
|
29633
30341
|
function packageJsonFor(version3, deps) {
|
|
29634
30342
|
return {
|
|
29635
30343
|
name: PKG_NAME,
|
|
@@ -29652,7 +30360,7 @@ function packageJsonFor(version3, deps) {
|
|
|
29652
30360
|
prepublishOnly: "node dist/cli.js __verify"
|
|
29653
30361
|
},
|
|
29654
30362
|
engines: { node: ">=20" },
|
|
29655
|
-
files: ["bin", "dist", "web", "prisma", "docs", "README.md", "LICENSE"],
|
|
30363
|
+
files: ["bin", "dist", "web", "prisma", "docs", "scripts", "README.md", "LICENSE"],
|
|
29656
30364
|
dependencies: deps,
|
|
29657
30365
|
// MIT: paket ini didistribusikan publik supaya orang `npm i -g`. `UNLICENSED` berarti "tak ada
|
|
29658
30366
|
// izin pakai" — 0.1.0 terbit dengan kontradiksi itu, dan versi terbit tak bisa diperbaiki.
|
|
@@ -29667,17 +30375,19 @@ function verifyPackedDeps(pkg) {
|
|
|
29667
30375
|
}
|
|
29668
30376
|
function copyPlan(repo) {
|
|
29669
30377
|
return [
|
|
29670
|
-
{ from:
|
|
29671
|
-
{ from:
|
|
29672
|
-
{ from:
|
|
29673
|
-
{ from:
|
|
29674
|
-
{ from:
|
|
29675
|
-
{ from:
|
|
29676
|
-
{ from:
|
|
30378
|
+
{ from: join14(repo, "server/dist/server.js"), to: "dist/server.js" },
|
|
30379
|
+
{ from: join14(repo, "server/dist/build-info.json"), to: "dist/build-info.json" },
|
|
30380
|
+
{ from: join14(repo, "cli/dist/hanoman.js"), to: "dist/cli.js" },
|
|
30381
|
+
{ from: join14(repo, "src/dist"), to: "web", dir: true },
|
|
30382
|
+
{ from: join14(repo, "server/prisma/schema.prisma"), to: "prisma/schema.prisma" },
|
|
30383
|
+
{ from: join14(repo, "server/prisma/migrations"), to: "prisma/migrations", dir: true },
|
|
30384
|
+
{ from: join14(repo, "internal/docs/operations/npm-readme.md"), to: "README.md" },
|
|
29677
30385
|
// SPEC-489 · dibaca runtime oleh pickGuideFile (kandidat `<pkg>/docs/…`). Bukan README:
|
|
29678
30386
|
// README paket adalah npm-readme.md (berhadapan-MANUSIA), ini naskah berhadapan-AGEN.
|
|
29679
|
-
{ from:
|
|
29680
|
-
|
|
30387
|
+
{ from: join14(repo, "docs/agent-integration.md"), to: "docs/agent-integration.md" },
|
|
30388
|
+
// SPEC-883 · skrip VPS (audit/harden/remediate/provision) dibaca runtime lewat scriptPath().
|
|
30389
|
+
{ from: join14(repo, "server/scripts/vps"), to: "scripts/vps", dir: true },
|
|
30390
|
+
{ from: join14(repo, "LICENSE"), to: "LICENSE" }
|
|
29681
30391
|
];
|
|
29682
30392
|
}
|
|
29683
30393
|
var PKG_NAME, REPO_URL, RUNTIME_DEPS, REQUIRED_ARTIFACTS, BIN_SHIM;
|
|
@@ -29710,7 +30420,15 @@ var init_pack = __esm({
|
|
|
29710
30420
|
// SPEC-489 · naskah panduan AI agent — dibaca runtime & disajikan di GET /api/agent-integration.md.
|
|
29711
30421
|
// Tanpa gerbang ini paket bisa terbit tanpa dokumen dan endpoint-nya 404 di SETIAP instalasi npm,
|
|
29712
30422
|
// sementara checkout dev terlihat sehat.
|
|
29713
|
-
"docs/agent-integration.md"
|
|
30423
|
+
"docs/agent-integration.md",
|
|
30424
|
+
// SPEC-883 · skrip VPS dibaca runtime oleh scriptPath(). Sebelum spec ini keempatnya TIDAK
|
|
30425
|
+
// ikut terpaket sementara scriptPath menjangkar ke repoRoot() — di instalasi npm marker
|
|
30426
|
+
// pnpm-workspace.yaml tak ada, repoRoot jatuh ke cwd, dan audit/harden/remediate melempar
|
|
30427
|
+
// ENOENT. Gerbang ini yang membuat kegagalan itu tak bisa terbit lagi.
|
|
30428
|
+
"scripts/vps/audit.sh",
|
|
30429
|
+
"scripts/vps/harden.sh",
|
|
30430
|
+
"scripts/vps/remediate.sh",
|
|
30431
|
+
"scripts/vps/provision.sh"
|
|
29714
30432
|
];
|
|
29715
30433
|
BIN_SHIM = `#!/usr/bin/env node
|
|
29716
30434
|
// SPEC-398 \xB7 shim tipis: seluruh logika ada di dist/cli.js (bundle esbuild).
|
|
@@ -29724,10 +30442,10 @@ var pack_exports = {};
|
|
|
29724
30442
|
__export(pack_exports, {
|
|
29725
30443
|
default: () => pack
|
|
29726
30444
|
});
|
|
29727
|
-
import { cpSync, existsSync as
|
|
29728
|
-
import { dirname as dirname7, join as
|
|
30445
|
+
import { cpSync, existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync9, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
30446
|
+
import { dirname as dirname7, join as join15 } from "node:path";
|
|
29729
30447
|
function depVersions(repo) {
|
|
29730
|
-
const read = (p) => JSON.parse(
|
|
30448
|
+
const read = (p) => JSON.parse(readFileSync9(join15(repo, p), "utf8"));
|
|
29731
30449
|
const server = read("server/package.json"), cli = read("cli/package.json");
|
|
29732
30450
|
const pools = [server.dependencies, server.devDependencies, cli.dependencies, cli.devDependencies];
|
|
29733
30451
|
const out = {};
|
|
@@ -29741,19 +30459,19 @@ function depVersions(repo) {
|
|
|
29741
30459
|
async function pack(argv, ctx) {
|
|
29742
30460
|
let repo;
|
|
29743
30461
|
try {
|
|
29744
|
-
repo = resolveLayout(distDir(),
|
|
30462
|
+
repo = resolveLayout(distDir(), existsSync10).root;
|
|
29745
30463
|
} catch (e) {
|
|
29746
30464
|
ctx.stderr(`${e.message}
|
|
29747
30465
|
`);
|
|
29748
30466
|
return 1;
|
|
29749
30467
|
}
|
|
29750
|
-
if (!
|
|
30468
|
+
if (!existsSync10(join15(repo, "server/prisma/schema.prisma"))) {
|
|
29751
30469
|
ctx.stderr("pack: hanya bisa dijalankan dari checkout repo hanoman\n");
|
|
29752
30470
|
return 1;
|
|
29753
30471
|
}
|
|
29754
30472
|
const outIdx = argv.indexOf("--out");
|
|
29755
|
-
const out = outIdx === -1 ?
|
|
29756
|
-
const version3 = JSON.parse(
|
|
30473
|
+
const out = outIdx === -1 ? join15(repo, "dist-npm") : join15(repo, argv[outIdx + 1] ?? "dist-npm");
|
|
30474
|
+
const version3 = JSON.parse(readFileSync9(join15(repo, "package.json"), "utf8")).version;
|
|
29757
30475
|
if (!version3) {
|
|
29758
30476
|
ctx.stderr("pack: root package.json tanpa field version\n");
|
|
29759
30477
|
return 1;
|
|
@@ -29767,20 +30485,20 @@ async function pack(argv, ctx) {
|
|
|
29767
30485
|
return 1;
|
|
29768
30486
|
}
|
|
29769
30487
|
rmSync(out, { recursive: true, force: true });
|
|
29770
|
-
|
|
30488
|
+
mkdirSync3(join15(out, "bin"), { recursive: true });
|
|
29771
30489
|
for (const item of copyPlan(repo)) {
|
|
29772
|
-
if (!
|
|
30490
|
+
if (!existsSync10(item.from)) {
|
|
29773
30491
|
ctx.stderr(`pack: artefak hilang \u2014 ${item.from} (sudah \`pnpm build\`?)
|
|
29774
30492
|
`);
|
|
29775
30493
|
return 1;
|
|
29776
30494
|
}
|
|
29777
|
-
const dest =
|
|
29778
|
-
|
|
30495
|
+
const dest = join15(out, item.to);
|
|
30496
|
+
mkdirSync3(dirname7(dest), { recursive: true });
|
|
29779
30497
|
cpSync(item.from, dest, item.dir ? { recursive: true } : {});
|
|
29780
30498
|
}
|
|
29781
|
-
|
|
29782
|
-
|
|
29783
|
-
const missing = REQUIRED_ARTIFACTS.filter((a) => !
|
|
30499
|
+
writeFileSync3(join15(out, "bin/hanoman.mjs"), BIN_SHIM);
|
|
30500
|
+
writeFileSync3(join15(out, "package.json"), JSON.stringify(packageJsonFor(version3, deps), null, 2) + "\n");
|
|
30501
|
+
const missing = REQUIRED_ARTIFACTS.filter((a) => !existsSync10(join15(out, a)));
|
|
29784
30502
|
if (missing.length) {
|
|
29785
30503
|
ctx.stderr(`pack: artefak wajib hilang: ${missing.join(", ")}
|
|
29786
30504
|
`);
|
|
@@ -29805,13 +30523,13 @@ var verify_packed_exports = {};
|
|
|
29805
30523
|
__export(verify_packed_exports, {
|
|
29806
30524
|
default: () => verifyPacked
|
|
29807
30525
|
});
|
|
29808
|
-
import { readFileSync as
|
|
29809
|
-
import { join as
|
|
30526
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
30527
|
+
import { join as join16 } from "node:path";
|
|
29810
30528
|
function verifyPacked(_argv, ctx) {
|
|
29811
|
-
const path =
|
|
30529
|
+
const path = join16(process.cwd(), "package.json");
|
|
29812
30530
|
let pkg;
|
|
29813
30531
|
try {
|
|
29814
|
-
pkg = JSON.parse(
|
|
30532
|
+
pkg = JSON.parse(readFileSync10(path, "utf8"));
|
|
29815
30533
|
} catch {
|
|
29816
30534
|
ctx.stderr(`verify: tak bisa membaca ${path}
|
|
29817
30535
|
`);
|
|
@@ -29847,7 +30565,7 @@ function route(argv) {
|
|
|
29847
30565
|
if (argv.includes("--help")) return { cmd: "help", args: [] };
|
|
29848
30566
|
const [group, sub, ...rest] = argv;
|
|
29849
30567
|
if (group === void 0 || group.startsWith("--")) return { cmd: "start", args: argv };
|
|
29850
|
-
if (group === "start" || group === "doctor" || group === "update" || group === "mcp")
|
|
30568
|
+
if (group === "start" || group === "doctor" || group === "update" || group === "mcp" || group === "provision")
|
|
29851
30569
|
return { cmd: group, args: argv.slice(1) };
|
|
29852
30570
|
if (group === "migrate-from-postgres") return { cmd: "migrate-pg", args: argv.slice(1) };
|
|
29853
30571
|
if (group === "docs" && (sub === "scan" || sub === "index" || sub === "link")) {
|
|
@@ -29857,7 +30575,7 @@ function route(argv) {
|
|
|
29857
30575
|
if (group === "__verify") return { cmd: "__verify", args: argv.slice(1) };
|
|
29858
30576
|
return { cmd: "unknown", args: argv };
|
|
29859
30577
|
}
|
|
29860
|
-
async function
|
|
30578
|
+
async function run2(argv, ctx) {
|
|
29861
30579
|
const { cmd, args } = route(argv);
|
|
29862
30580
|
if (cmd === "version") {
|
|
29863
30581
|
ctx.stdout(currentVersion() + "\n");
|
|
@@ -29871,6 +30589,7 @@ async function run(argv, ctx) {
|
|
|
29871
30589
|
if (cmd === "doctor") return (await Promise.resolve().then(() => (init_doctor(), doctor_exports))).default(args, ctx);
|
|
29872
30590
|
if (cmd === "update") return (await Promise.resolve().then(() => (init_update(), update_exports))).default(args, ctx);
|
|
29873
30591
|
if (cmd === "mcp") return (await Promise.resolve().then(() => (init_mcp2(), mcp_exports))).default(args, ctx);
|
|
30592
|
+
if (cmd === "provision") return (await Promise.resolve().then(() => (init_provision(), provision_exports))).default(args, ctx);
|
|
29874
30593
|
if (cmd === "migrate-pg") return (await Promise.resolve().then(() => (init_migrate_pg(), migrate_pg_exports))).default(args, ctx);
|
|
29875
30594
|
if (cmd === "docs:scan") return (await Promise.resolve().then(() => (init_docs_scan(), docs_scan_exports))).default(args, ctx);
|
|
29876
30595
|
if (cmd === "docs:index") return (await Promise.resolve().then(() => (init_docs_index(), docs_index_exports))).default(args, ctx);
|
|
@@ -29893,6 +30612,9 @@ var init_router = __esm({
|
|
|
29893
30612
|
--port <n> --host <h> --db <file> --no-migrate
|
|
29894
30613
|
doctor periksa prasyarat: node, git, tmux, CLI agen, data dir
|
|
29895
30614
|
update [--check] bandingkan versi dengan registry npm; pasang yang terbaru
|
|
30615
|
+
provision [--with=a,b] [--probe] pasang komponen di MESIN INI (hanoman, caddy, claude,
|
|
30616
|
+
[--profile=lab|production] codex, gh, \u2026). --probe hanya melaporkan apa yang ada.
|
|
30617
|
+
[--domain=<d>] [--dry-run] [--yes]
|
|
29896
30618
|
mcp [--read-only] [--host <url>] MCP server stdio untuk klien AI (Claude Code/Desktop,
|
|
29897
30619
|
[--max-bytes <n>] Codex, Cursor, Copilot). Token dari HANOMAN_AGENT_TOKEN.
|
|
29898
30620
|
migrate-from-postgres --from <url> pindahkan data Postgres lama ke SQLite
|
|
@@ -29913,7 +30635,7 @@ var readStdin = () => new Promise((res) => {
|
|
|
29913
30635
|
process.stdin.on("end", () => res(d));
|
|
29914
30636
|
if (process.stdin.isTTY) res("");
|
|
29915
30637
|
});
|
|
29916
|
-
|
|
30638
|
+
run2(process.argv.slice(2), {
|
|
29917
30639
|
cwd: process.cwd(),
|
|
29918
30640
|
env: process.env,
|
|
29919
30641
|
stdout: (s2) => process.stdout.write(s2),
|