hanoman 0.1.13 → 0.1.15
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/dist/build-info.json +3 -3
- package/dist/cli.js +59 -21
- package/dist/server.js +667 -134
- package/docs/agent-integration.md +429 -0
- package/package.json +2 -1
- package/web/assets/{index-DRwKLKdp.js → index-DRv31V2x.js} +1540 -1536
- package/web/index.html +1 -1
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# hanoman — dokumentasi AI Agent
|
|
2
|
+
|
|
3
|
+
**Halaman ini ditulis untuk kamu, agen.** Kalau kamu diberi tautan ini dan satu **agent token**,
|
|
4
|
+
tak ada lagi yang perlu dijelaskan manusia: semua yang kamu butuhkan ada di bawah.
|
|
5
|
+
|
|
6
|
+
Naskah ini punya **satu sumber** dan tiga cara membacanya — isinya byte yang sama:
|
|
7
|
+
|
|
8
|
+
| Cara | Alamat |
|
|
9
|
+
|---|---|
|
|
10
|
+
| **markdown mentah** (paling berguna untukmu) | `GET $HANOMAN_HOST/api/agent-integration.md` — **publik, tanpa auth** |
|
|
11
|
+
| repo GitHub | [`docs/agent-integration.md`](https://github.com/denameidina/hanoman/blob/main/docs/agent-integration.md) |
|
|
12
|
+
| dashboard | Settings → **Dokumentasi AI Agent** |
|
|
13
|
+
|
|
14
|
+
> SPEC-257/265/489 · ADR-0065 (agent token & capability) · ADR-0099 (MCP server).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 0. Apa itu hanoman, dan bagaimana ia bekerja
|
|
19
|
+
|
|
20
|
+
hanoman adalah **orchestrator + dashboard** untuk pengembangan yang digerakkan dokumentasi. Ia tidak
|
|
21
|
+
menulis kode sendiri; ia **menjalankan agen** (Claude Code atau Codex CLI) sebagai sesi interaktif,
|
|
22
|
+
lalu memantau semuanya dalam satu tempat.
|
|
23
|
+
|
|
24
|
+
Model kerjanya tiga tingkat, dan penting kamu pegang sebelum memanggil endpoint apa pun:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
backlog item (Spec) → sesi agen (tmux) → git worktree terisolasi
|
|
28
|
+
"SPEC-489" satu sesi per item <repo>/.worktrees/spec-489
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- **Backlog item** (`Spec`, id `SPEC-nnn`) adalah unit kerja: sebuah brief, temuan QA, laporan
|
|
32
|
+
audit, tiket Help Center, atau satu goal. Ia milik sebuah **project**.
|
|
33
|
+
- **Satu backlog = satu sesi.** Menekan Start dua kali bukan melahirkan sesi kedua — ia menyambung
|
|
34
|
+
ke sesi yang sudah ada.
|
|
35
|
+
- **Sesi hidup di worktree-nya sendiri**, bercabang dari branch basis, dan mendorong hasilnya ke
|
|
36
|
+
`hanoman/<id>`. Isolasi worktree itulah satu-satunya batas keamanan eksekusi di hanoman —
|
|
37
|
+
tak ada guardrail perintah.
|
|
38
|
+
- **Fase bukan proses, melainkan giliran** di dalam satu sesi: `Brainstorm → Objective → Spec →
|
|
39
|
+
Plan → Execute` (bervariasi per jenis kerja). Kemajuannya dibaca dari berkas fase, bukan dari
|
|
40
|
+
status proses.
|
|
41
|
+
- **Dokumentasi project (`internal/docs/**`) adalah Source of Truth.** Sebelum mengusulkan apa pun,
|
|
42
|
+
baca dokumen project-nya — itu sikap yang diharapkan dari agen di sini.
|
|
43
|
+
|
|
44
|
+
Dashboard React hanyalah **satu klien** dari REST API di bawah `/api`. Kamu memakai API yang persis
|
|
45
|
+
sama; yang berbeda hanya jalur auth-nya: **`Authorization: Bearer`**, bukan cookie sesi.
|
|
46
|
+
|
|
47
|
+
## 1. Nyalakan akses & buat token (manusia, sekali)
|
|
48
|
+
|
|
49
|
+
Langkah ini **bukan** milikmu — mintalah ke manusia bila belum dilakukan. Di dashboard hanoman:
|
|
50
|
+
**Settings → Akses AI Agent**.
|
|
51
|
+
|
|
52
|
+
1. **Aktifkan "Akses AI Agent"** (master switch). Selagi mati, *semua* agent token dibalas **401**
|
|
53
|
+
apa pun capability-nya.
|
|
54
|
+
2. **Buat token:** beri nama (mis. `agent-ci`), centang **capability** yang dibutuhkan (baca/tulis
|
|
55
|
+
per domain), klik **Buat token**.
|
|
56
|
+
3. **Salin token plaintext sekarang** — bentuknya `hnm_agt_<hex>` dan **hanya ditampilkan sekali**
|
|
57
|
+
(di server hanya `sha256` yang tersimpan). Simpan di rahasia agen (mis. env
|
|
58
|
+
`HANOMAN_AGENT_TOKEN`).
|
|
59
|
+
|
|
60
|
+
Cabut/nonaktifkan token atau matikan master switch kapan saja → efek **instan**.
|
|
61
|
+
|
|
62
|
+
## 2. Base URL & autentikasi
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
export HANOMAN_HOST="https://hanoman.example" # TANPA "/" di ekor
|
|
66
|
+
export HANOMAN_AGENT_TOKEN="hnm_agt_…" # dari langkah §1
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- **Seluruh path berawalan `/api`.** `$HANOMAN_HOST/api/specs`, bukan `$HANOMAN_HOST/specs`.
|
|
70
|
+
- **`HANOMAN_HOST` tanpa garis miring di ekor** — path di dokumen ini selalu dimulai dengan `/`,
|
|
71
|
+
jadi ekor ganda menghasilkan `//api/...` yang tak dikenal router.
|
|
72
|
+
- Sertakan token di **tiap** request:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
Authorization: Bearer hnm_agt_xxxxxxxxxxxx
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- Untuk **WebSocket** (terminal PTY, event stream) yang tak bisa memasang header dari browser,
|
|
79
|
+
kirim sebagai query: `?agent_token=hnm_agt_...`.
|
|
80
|
+
- **Token diterbitkan per-instance.** Token dari instance lain selalu 401 di sini.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
curl -s "$HANOMAN_HOST/api/specs" -H "Authorization: Bearer $HANOMAN_AGENT_TOKEN"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Probe host lebih dulu.** `GET /api/health` bersifat **publik** (tanpa auth), begitu pula halaman
|
|
87
|
+
ini (`GET /api/agent-integration.md`). Keduanya memisahkan tiga sebab yang tampak identik sebagai
|
|
88
|
+
"401 telanjang": host salah · master switch mati · token dicabut. Kalau `/api/health` menjawab 200,
|
|
89
|
+
host-mu benar dan masalahnya ada pada token atau master switch.
|
|
90
|
+
|
|
91
|
+
## 3. Capability
|
|
92
|
+
|
|
93
|
+
Capability berformat `"<domain>:<access>"`, `access ∈ {read, write}`, dan **write meng-implikasikan
|
|
94
|
+
read** pada domain yang sama. Ada **12 domain × 2 = 24 capability**. Katalog resmi (dengan label &
|
|
95
|
+
deskripsi) tampil di panel **Settings → Akses AI Agent** saat manusia membuat token; endpoint
|
|
96
|
+
katalognya (`GET /api/agent-tokens/capabilities`) bersifat **cookie-only** (lihat §5) — kamu tak
|
|
97
|
+
perlu mengambilnya, cukup rujuk tabel di bawah:
|
|
98
|
+
|
|
99
|
+
| Domain | Cakupan endpoint | Catatan |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `projects` | `/api/projects*` | project, branch, binding, Help Center |
|
|
102
|
+
| `backlog` | `/api/specs*` | spec/backlog, dokumen, review diff, integrate |
|
|
103
|
+
| `sessions` | `/api/terminal*` (+ WS terminal) | jalankan sesi agen/shell, kirim input — **high-risk (RCE)** |
|
|
104
|
+
| `docs` | `/api/prds*`, `/api/projects/:id/{docs,prds}*` | dokumen SoT project & PRD |
|
|
105
|
+
| `ide` | `/api/projects/:id/{tree,file,file-diff,working-status,graph,commit,git,status,stashes,remotes,compare,archive,pr-url}*` | tree/file working tree, operasi git |
|
|
106
|
+
| `vps` | `/api/vps*` | kelola VPS, audit, harden, konsol — **high-risk (remote exec)** |
|
|
107
|
+
| `settings` | `/api/settings*`, `/api/config*`, `/api/scheduler*` | setelan instance & config runtime |
|
|
108
|
+
| `support` | `/api/tickets*`, `/api/github-issues*`, `/api/projects/:id/github*` | tiket Help Center & issue GitHub (triase) |
|
|
109
|
+
| `notifications` | `/api/notifications*` | notifikasi |
|
|
110
|
+
| `lead` | `/api/lead*` | minta putusan ke hanoman-lead & baca jejaknya — **`lead:write` bisa menggerakkan sesi** (ADR-0091) |
|
|
111
|
+
| `agents` | `/api/custom-agents*` | katalog custom agent global & per project — **`agents:write` mengubah apa yang dilihat SETIAP sesi baru** (ADR-0094) |
|
|
112
|
+
| `telegram` | `/api/telegram*` kecuali sub-path kredensial | context/memory/reply/audit kanal operator Telegram (ADR-0096) |
|
|
113
|
+
|
|
114
|
+
Aturan pemetaan **deterministik** (`server/src/services/agent-capabilities.ts`): `GET`/`HEAD` →
|
|
115
|
+
`:read`, metode lain → `:write`. Itu berlaku untuk domain `lead` juga — **`POST /api/lead/decisions`
|
|
116
|
+
menuntut `lead:write`**, dan `lead:read` tak pernah cukup: meminta putusan melahirkan baris jejak
|
|
117
|
+
permanen dan keputusannya bisa menggerakkan sesi. Sub-path `/api/projects/:id/{docs,prds}` dihitung
|
|
118
|
+
domain **`docs`**; sub-path IDE/git di atas dihitung domain **`ide`**; WebSocket terminal butuh
|
|
119
|
+
**`sessions:write`**.
|
|
120
|
+
|
|
121
|
+
## 4. Aturan gate & kode status
|
|
122
|
+
|
|
123
|
+
Gate `onRequest` yang sama menegakkan semuanya:
|
|
124
|
+
|
|
125
|
+
| Situasi | Balasan |
|
|
126
|
+
|---|---|
|
|
127
|
+
| Master switch mati, atau token invalid/nonaktif/dicabut | **401** `{ error: "unauthorized" }` |
|
|
128
|
+
| Token valid tapi capability kurang | **403** `{ error: "capability required", need: "<domain>:<access>" }` |
|
|
129
|
+
| Route cookie-only (§5) diakses agen | **403** `{ error: "cookie session required" }` |
|
|
130
|
+
| Capability cukup | request diproses seperti biasa |
|
|
131
|
+
|
|
132
|
+
Field **`need`** pada 403 memberi tahu capability persis yang harus ditambahkan ke token. Baca 403
|
|
133
|
+
seperti itu bukan sebagai "gagal" melainkan sebagai **instruksi**: sampaikan `need` ke manusia dan
|
|
134
|
+
minta capability itu ditambahkan di Settings.
|
|
135
|
+
|
|
136
|
+
## 5. Yang tak bisa didelegasikan (cookie-only)
|
|
137
|
+
|
|
138
|
+
Untuk mencegah privilege-escalation, endpoint berikut **hanya** untuk sesi cookie manusia — agent
|
|
139
|
+
token selalu **403**, apa pun capability-nya, dan tak ada capability yang bisa membukanya:
|
|
140
|
+
|
|
141
|
+
- `/api/auth/*` — kelola user & password
|
|
142
|
+
- `/api/agent-tokens*` — agen tak boleh mencetak/menaikkan token sendiri
|
|
143
|
+
- `/api/device-tokens*`, `/api/sync*` — identitas mesin & sync hub
|
|
144
|
+
- `/api/webhooks*` — memegang secret penandatanganan **dan** menentukan ke mana data workspace
|
|
145
|
+
mengalir keluar (ADR-0100)
|
|
146
|
+
- `/api/telegram/settings`, `/api/telegram/test`, `/api/telegram/credentials` — permukaan
|
|
147
|
+
**kredensial** (bot token & agent token), beda dari sisa `/api/telegram*` (ADR-0097)
|
|
148
|
+
- `POST /api/update/apply` dan tulis lain di bawah prefix status (`/api/limits`, `/api/update`,
|
|
149
|
+
`/api/events`, `/api/fs`, `/api/health`) — **baca**-nya terbuka untuk token mana pun, **tulis**-nya
|
|
150
|
+
cookie-only
|
|
151
|
+
|
|
152
|
+
Route yang tak dikenal peta juga **default cookie-only** (aman): endpoint baru tak pernah terbuka
|
|
153
|
+
karena kelalaian. Endpoint `/api/help*` (Help Center publik) punya otorisasi sendiri (kunci tiket)
|
|
154
|
+
dan tak memakai agent token.
|
|
155
|
+
|
|
156
|
+
## 6. Endpoint yang paling sering dipakai
|
|
157
|
+
|
|
158
|
+
| Method & path | Capability | Catatan |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| `GET /api/health` | — (publik) | probe host. Tanpa auth. |
|
|
161
|
+
| `GET /api/agent-integration.md` | — (publik) | halaman ini, markdown mentah. |
|
|
162
|
+
| `GET /api/projects` | `projects:read` | daftar project. `id` di sini yang dipakai `POST /api/specs`. |
|
|
163
|
+
| `GET /api/projects/:id` | `projects:read` | detail satu project. |
|
|
164
|
+
| `GET /api/specs` | `backlog:read` | backlog. Filter: `project`, `source`, `q`, `stage`, `priority`, `startable=true`, `dateField=created\|started` + `from`/`to` (`YYYY-MM-DD`, inklusif), `page`, `limit`. |
|
|
165
|
+
| `POST /api/specs` | `backlog:write` | buat backlog item — bentuk payload di §7. |
|
|
166
|
+
| `PATCH /api/specs/:id` | `backlog:write` | ubah item; konten hanya selagi belum dimulai. |
|
|
167
|
+
| `GET /api/specs/:id/docs` | `backlog:read` | dokumen yang ditulis sesi item itu. |
|
|
168
|
+
| `GET /api/specs/:id/review` | `backlog:read` | diff hasil kerja sesi. |
|
|
169
|
+
| `GET /api/projects/:id/docs` | `docs:read` | index Source of Truth project. |
|
|
170
|
+
| `GET /api/projects/:id/docs/<path>` | `docs:read` | isi satu dokumen. |
|
|
171
|
+
| `GET /api/terminal/sessions` | `sessions:read` | sesi yang sedang hidup. |
|
|
172
|
+
| `GET /api/notifications` | `notifications:read` | notifikasi. |
|
|
173
|
+
| `GET /api/tickets` | `support:read` | tiket Help Center. |
|
|
174
|
+
| `GET /api/lead/decisions` | `lead:read` | jejak keputusan hanoman-lead. |
|
|
175
|
+
| `POST /api/lead/decisions` | `lead:write` | minta putusan — baca **§8** dan **§11** dulu. |
|
|
176
|
+
|
|
177
|
+
## 7. `POST /api/specs` — bentuk payload per `source`
|
|
178
|
+
|
|
179
|
+
`source` dan bentuk `payload` **saling mengikat**. Salah pasang → **400**
|
|
180
|
+
`"bentuk payload tak cocok dengan source"`. Union saja tak menjaganya (objek non-strict), jadi
|
|
181
|
+
server menegakkannya di boundary.
|
|
182
|
+
|
|
183
|
+
| `source` | Bentuk `payload` | Field |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `brief` | brief | `context`, `outcome`, `constraints`, `priority` |
|
|
186
|
+
| `audit` | brief | idem — audit-only: hasilnya dokumen temuan, tanpa Execute |
|
|
187
|
+
| `help` | brief | idem — item yang lahir dari tiket Help Center |
|
|
188
|
+
| `qa` | qa | `severity` (`critical`\|`major`\|`minor`), `steps`, `expected`, `actual`, `env` |
|
|
189
|
+
| `goal` | goal | `goal` (wajib), `done`, `constraints`, `priority` |
|
|
190
|
+
|
|
191
|
+
Body lengkap: `project` (slug project), `source`, `title`, `priority`
|
|
192
|
+
(`tinggi`\|`sedang`\|`rendah`), `payload`; opsional `branchFrom` (branch basis — harus benar-benar
|
|
193
|
+
ada di repo project) dan `dependsOn` (array id backlog yang harus selesai & ter-merge lebih dulu).
|
|
194
|
+
|
|
195
|
+
Yang **tak** kamu kirim karena diturunkan server: `objective` (dari `outcome`/`context` untuk brief,
|
|
196
|
+
`actual`/`steps` untuk qa, `goal` untuk goal) dan — khusus `qa` — `priority`, yang diturunkan dari
|
|
197
|
+
`severity`.
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"project": "hanoman",
|
|
202
|
+
"source": "qa",
|
|
203
|
+
"title": "Tombol Lanjutkan diam saat pane mati",
|
|
204
|
+
"priority": "tinggi",
|
|
205
|
+
"payload": {
|
|
206
|
+
"severity": "major",
|
|
207
|
+
"steps": "Buka Terminal → tunggu sesi keluar → klik Lanjutkan",
|
|
208
|
+
"expected": "Sesi dilanjutkan dari fase terakhir",
|
|
209
|
+
"actual": "Tak terjadi apa-apa",
|
|
210
|
+
"env": "hanoman 0.1.13, macOS"
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Balasannya **201** dengan seluruh baris `Spec`, termasuk `id` (`SPEC-nnn`) yang diterbitkan server.
|
|
216
|
+
|
|
217
|
+
## 8. Tindakan berbahaya — wajib konfirmasi manusia
|
|
218
|
+
|
|
219
|
+
Tiga permukaan ini **wajib** kamu konfirmasikan ke manusia lebih dulu, walaupun token-mu sudah punya
|
|
220
|
+
capability-nya. Capability menjawab "boleh?", bukan "sebaiknya?".
|
|
221
|
+
|
|
222
|
+
| Tindakan | Kenapa |
|
|
223
|
+
|---|---|
|
|
224
|
+
| `POST /api/terminal/sessions` | melahirkan proses agen `--dangerously-skip-permissions` di sebuah worktree — **RCE efektif**. Batas satu-satunya adalah isolasi git worktree (ADR-0037). |
|
|
225
|
+
| `POST`/`PUT`/`DELETE` di bawah `/api/vps` | **remote exec** di server produksi. |
|
|
226
|
+
| `POST /api/lead/decisions` | putusannya bisa **menggerakkan sesi** (integrate ke main, menghentikan sesi) dan selalu melahirkan baris jejak permanen (ADR-0091/0098). |
|
|
227
|
+
|
|
228
|
+
Perlakukan `POST /api/specs/:id/integrate`, `DELETE /api/specs/:id`, dan perubahan `stage` dengan
|
|
229
|
+
disiplin yang sama: ketiganya mengubah sejarah git atau membuang pekerjaan.
|
|
230
|
+
|
|
231
|
+
**Preseden yang mengikat:** MCP server resmi (`hanoman mcp`, §13) sengaja **tak punya tool** untuk
|
|
232
|
+
satu pun dari yang di atas — batasnya ada di katalog tool, bukan di token. Token yang punya
|
|
233
|
+
`sessions:write` sekalipun tak akan menemukan tool untuk memakainya. Lewat REST kamu *bisa*
|
|
234
|
+
memanggilnya; jangan lakukan tanpa manusia.
|
|
235
|
+
|
|
236
|
+
## 9. Jebakan yang sudah diketahui
|
|
237
|
+
|
|
238
|
+
| Jebakan | Yang benar |
|
|
239
|
+
|---|---|
|
|
240
|
+
| `startable` hanya bereaksi pada string **`"true"`**; nilai lain (`false`, `1`, `yes`) diabaikan **senyap** dan kamu menerima daftar penuh yang terlihat sah | kirim `?startable=true`, atau jangan kirim sama sekali |
|
|
241
|
+
| `q` mencari di `id`, `title`, dan `objective` saja — ia **tak menyentuh `payload`** | untuk mencari isi brief/QA, ambil itemnya lalu baca `payload` sendiri |
|
|
242
|
+
| `id` dan `stage` yang kamu sertakan di `POST /api/specs` **dibuang diam-diam** — tak ada galat | `id` diterbitkan server (`SPEC-nnn` berikutnya), `stage` selalu mulai `brainstorming`. Untuk mengubah stage pakai `PATCH /api/specs/:id`, dan ia hanya boleh **mundur** (ADR-0027) |
|
|
243
|
+
| **`GET /api/specs/:id` tidak ada** | `GET /api/specs?q=SPEC-489` lalu cocokkan `id` **persis** — `q` itu substring, jadi ia bisa mengembalikan lebih dari satu |
|
|
244
|
+
| daftar mengembalikan amplop `{ items, total, page, pageSize }` | jangan perlakukan responsnya sebagai array |
|
|
245
|
+
| tanpa `limit`, daftar mengembalikan **seluruh** item dalam satu halaman | kirim `limit` untuk backlog besar |
|
|
246
|
+
| `PATCH /api/specs/:id` menolak edit konten begitu item pernah dimulai | ubah `title`/`payload` hanya selagi item belum punya sesi |
|
|
247
|
+
| `branchFrom` yang tak ada di repo project → **400**, bukan diterima lalu gagal di tengah sesi | ambil kandidatnya dari `GET /api/projects/:id/branches` |
|
|
248
|
+
| **401 telanjang** tak memisahkan "host salah" dari "token salah" dari "master switch mati" | probe `GET /api/health` sekali: 200 = host benar → masalahnya token atau master switch |
|
|
249
|
+
| **403** bukan kegagalan permanen | bacalah field `need`, sampaikan ke manusia, minta capability itu ditambahkan |
|
|
250
|
+
|
|
251
|
+
## 10. Contoh alur end-to-end
|
|
252
|
+
|
|
253
|
+
Bisa disalin apa adanya.
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
export HANOMAN_HOST="https://hanoman.example" # tanpa "/" di ekor
|
|
257
|
+
export HANOMAN_AGENT_TOKEN="hnm_agt_…" # dari Settings → Akses AI Agent
|
|
258
|
+
auth=(-H "Authorization: Bearer $HANOMAN_AGENT_TOKEN")
|
|
259
|
+
|
|
260
|
+
# 0. Host benar? (publik, tanpa auth — memisahkan "host salah" dari "token salah")
|
|
261
|
+
curl -fsS "$HANOMAN_HOST/api/health"
|
|
262
|
+
|
|
263
|
+
# 0b. Baca halaman ini sendiri (publik, markdown mentah)
|
|
264
|
+
curl -fsS "$HANOMAN_HOST/api/agent-integration.md"
|
|
265
|
+
|
|
266
|
+
# 1. Project apa saja yang ada? (projects:read) — `id` di sini yang dipakai langkah berikutnya
|
|
267
|
+
curl -fsS "${auth[@]}" "$HANOMAN_HOST/api/projects"
|
|
268
|
+
|
|
269
|
+
# 2. Backlog yang belum selesai di satu project (backlog:read)
|
|
270
|
+
curl -fsS "${auth[@]}" "$HANOMAN_HOST/api/specs?project=hanoman&startable=true&limit=20"
|
|
271
|
+
|
|
272
|
+
# 3. Sudah ada item tentang "webhook"? (q = substring atas id+title+objective, BUKAN payload)
|
|
273
|
+
curl -fsS "${auth[@]}" "$HANOMAN_HOST/api/specs?project=hanoman&q=webhook"
|
|
274
|
+
|
|
275
|
+
# 4. Filekan temuan sebagai backlog item (backlog:write)
|
|
276
|
+
curl -fsS -X POST "$HANOMAN_HOST/api/specs" "${auth[@]}" \
|
|
277
|
+
-H "Content-Type: application/json" \
|
|
278
|
+
-d '{
|
|
279
|
+
"project": "hanoman",
|
|
280
|
+
"source": "qa",
|
|
281
|
+
"title": "Preview docs menggulir ke samping",
|
|
282
|
+
"priority": "sedang",
|
|
283
|
+
"payload": { "severity": "minor", "steps": "Buka Docs → pilih .md panjang",
|
|
284
|
+
"expected": "Teks membungkus", "actual": "Muncul scrollbar horizontal",
|
|
285
|
+
"env": "hanoman 0.1.13, Chrome" }
|
|
286
|
+
}'
|
|
287
|
+
# → 201 { "id": "SPEC-490", ... } ← id datang dari server; jangan pernah dikirim
|
|
288
|
+
|
|
289
|
+
# 5. Ambil satu item (tak ada GET /api/specs/:id — pakai q lalu cocokkan persis)
|
|
290
|
+
curl -fsS "${auth[@]}" "$HANOMAN_HOST/api/specs?q=SPEC-490" \
|
|
291
|
+
| python3 -c 'import json,sys; print([s for s in json.load(sys.stdin)["items"] if s["id"]=="SPEC-490"])'
|
|
292
|
+
|
|
293
|
+
# 6. Baca Source of Truth project sebelum mengusulkan apa pun (docs:read)
|
|
294
|
+
curl -fsS "${auth[@]}" "$HANOMAN_HOST/api/projects/hanoman/docs"
|
|
295
|
+
|
|
296
|
+
# 7. 403? Bacanya bukan "gagal" — bacanya "tambahkan capability ini ke token":
|
|
297
|
+
# { "error": "capability required", "need": "backlog:write" }
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Yang TIDAK kamu lakukan tanpa manusia:** menjalankan backlog itu
|
|
301
|
+
(`POST /api/terminal/sessions`) — lihat §8.
|
|
302
|
+
|
|
303
|
+
## 11. Minta putusan ke hanoman-lead
|
|
304
|
+
|
|
305
|
+
Agen yang menemui persimpangan tak selalu harus berhenti menunggu manusia — bila project-nya
|
|
306
|
+
meng-opt-in **hanoman-lead**, ia boleh **meminta putusan**:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
curl -s -X POST "$HANOMAN_HOST/api/lead/decisions" \
|
|
310
|
+
-H "Authorization: Bearer $HANOMAN_AGENT_TOKEN" \
|
|
311
|
+
-H "Content-Type: application/json" \
|
|
312
|
+
-d '{
|
|
313
|
+
"projectId": "hanoman",
|
|
314
|
+
"specId": "SPEC-409",
|
|
315
|
+
"question": "Tambah kolom baru di Spec, atau turunkan dari updatedAt?",
|
|
316
|
+
"options": ["kolom baru", "turunkan dari updatedAt"],
|
|
317
|
+
"context": "Filter rentang tanggal butuh waktu item dibuat."
|
|
318
|
+
}'
|
|
319
|
+
# 201 { id, decision, reason, refs: ["ADR-0090", "internal/docs/..."], confidence: "tinggi", action: "none" }
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Jawabannya **terbaca mesin**, bukan prosa bebas, dan `refs` hanya memuat rujukan yang benar-benar
|
|
323
|
+
ada di repo — jadi kamu bisa memverifikasi sendiri dasar keputusannya. `confidence: "ragu"` berarti
|
|
324
|
+
lead tetap memutuskan tapi memilih opsi yang paling mudah dibatalkan, dan operator sudah
|
|
325
|
+
dinotifikasi.
|
|
326
|
+
|
|
327
|
+
Kode balasan yang perlu ditangani:
|
|
328
|
+
|
|
329
|
+
| Kode | Artinya |
|
|
330
|
+
|---|---|
|
|
331
|
+
| **409** | lead tak aktif / project belum opt-in → **kembali ke perilaku lama**: berhenti & tunggu manusia |
|
|
332
|
+
| **503** + `Retry-After` | lead sedang penuh (batas konkurensi) → **boleh diulang** sesudah jeda; ini bukan kegagalan lead |
|
|
333
|
+
| **504** | lead tak berhasil memutuskan dalam batas waktu; kegagalannya sudah tercatat & dinotifikasi |
|
|
334
|
+
| **403** `{ need: "lead:write" }` | token cuma punya `lead:read` |
|
|
335
|
+
|
|
336
|
+
Ingat §8: permintaan putusan **bisa menggerakkan sesi**. Konfirmasikan ke manusia dulu.
|
|
337
|
+
|
|
338
|
+
## 12. Keamanan
|
|
339
|
+
|
|
340
|
+
- Token = rahasia. Simpan di env/secret manager, **jangan commit**, dan **jangan pernah** berikan
|
|
341
|
+
lewat argumen baris perintah — ARGV terbaca proses lain di mesin yang sama. Bocor → **Cabut** di
|
|
342
|
+
Settings (efek instan).
|
|
343
|
+
- Beri capability **seminimal** mungkin. `sessions:write` (spawn agen
|
|
344
|
+
`--dangerously-skip-permissions`) dan `vps:write` (remote exec) adalah RCE efektif — batas
|
|
345
|
+
eksekusi sesungguhnya tetap **isolasi git worktree** (ADR-0037), tapi tetap tandai high-risk.
|
|
346
|
+
- `lastUsedAt` per token = jejak audit ringan. Matikan master switch untuk kill-switch seluruh
|
|
347
|
+
workspace.
|
|
348
|
+
- Halaman ini sendiri **tak pernah memuat token nyata** — hanya format/placeholder. Kalau kamu
|
|
349
|
+
melihat sesuatu yang menyerupai token asli di sini, itu bug; laporkan.
|
|
350
|
+
|
|
351
|
+
## 13. MCP server (ADR-0099)
|
|
352
|
+
|
|
353
|
+
Agen yang berbicara **MCP** tak perlu menulis pembungkus sendiri. `hanoman mcp` adalah MCP server
|
|
354
|
+
**stdio** yang membungkus permukaan REST di atas sebagai **17 tool**. Ia memakai **agent token dan
|
|
355
|
+
capability yang sama** — bukan jalur otorisasi baru — jadi seluruh aturan §3–§5 berlaku apa adanya.
|
|
356
|
+
|
|
357
|
+
Prasyarat: `npm i -g hanoman` di mesin tempat klien AI-nya jalan.
|
|
358
|
+
|
|
359
|
+
**Claude Code / Claude Desktop / Cursor / Copilot** (`~/.claude.json`,
|
|
360
|
+
`claude_desktop_config.json`, `~/.cursor/mcp.json`, `.vscode/mcp.json` — Cursor & Copilot memakai
|
|
361
|
+
kunci `"servers"` alih-alih `"mcpServers"`):
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"mcpServers": {
|
|
366
|
+
"hanoman": {
|
|
367
|
+
"command": "hanoman",
|
|
368
|
+
"args": ["mcp"],
|
|
369
|
+
"env": {
|
|
370
|
+
"HANOMAN_HOST": "https://hanoman.example",
|
|
371
|
+
"HANOMAN_AGENT_TOKEN": "hnm_agt_…"
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**Codex** (`~/.codex/config.toml`):
|
|
379
|
+
|
|
380
|
+
```toml
|
|
381
|
+
[mcp_servers.hanoman]
|
|
382
|
+
command = "hanoman"
|
|
383
|
+
args = ["mcp"]
|
|
384
|
+
env = { HANOMAN_HOST = "https://hanoman.example", HANOMAN_AGENT_TOKEN = "hnm_agt_…" }
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Panduan siap salin untuk keempat klien, berikut tabel tool → capability, ada di dashboard:
|
|
388
|
+
**Settings → Akses AI Agent → MCP server**.
|
|
389
|
+
|
|
390
|
+
### Tool
|
|
391
|
+
|
|
392
|
+
| Tool | Mode | Capability |
|
|
393
|
+
|---|---|---|
|
|
394
|
+
| `hanoman_about` | baca | — (tak memanggil `/api` selain `/health`) |
|
|
395
|
+
| `hanoman_projects_list`, `hanoman_project_get` | baca | `projects:read` |
|
|
396
|
+
| `hanoman_backlog_search`, `hanoman_backlog_get`, `hanoman_backlog_docs_list`, `hanoman_backlog_doc_read` | baca | `backlog:read` |
|
|
397
|
+
| `hanoman_sessions_list` | baca | `sessions:read` |
|
|
398
|
+
| `hanoman_notifications_list` | baca | `notifications:read` |
|
|
399
|
+
| `hanoman_tickets_list`, `hanoman_ticket_get`, `hanoman_github_issues_list` | baca | `support:read` |
|
|
400
|
+
| `hanoman_lead_decisions_list` | baca | `lead:read` |
|
|
401
|
+
| `hanoman_backlog_create`, `hanoman_backlog_update` | tulis | `backlog:write` |
|
|
402
|
+
| `hanoman_notifications_mark_read` | tulis | `notifications:write` |
|
|
403
|
+
| `hanoman_lead_ask` | tulis | `lead:write` |
|
|
404
|
+
|
|
405
|
+
### Yang sengaja TIDAK tersedia lewat MCP
|
|
406
|
+
|
|
407
|
+
Membuat sesi terminal (`POST /api/terminal/sessions` — menjalankan agen di worktree, RCE efektif)
|
|
408
|
+
dan seluruh `/api/vps*` (remote exec) **tidak ikut**, begitu pula merge/rebase (`integrate`),
|
|
409
|
+
penghapusan backlog, dan perubahan `stage`. Batasan ini ada di katalog toolnya, bukan di token:
|
|
410
|
+
token yang punya `sessions:write` sekalipun tak akan menemukan tool untuk memakainya. Lihat §8.
|
|
411
|
+
|
|
412
|
+
### Opsi
|
|
413
|
+
|
|
414
|
+
| Variabel / flag | Arti |
|
|
415
|
+
|---|---|
|
|
416
|
+
| `HANOMAN_HOST` / `--host <url>` | **Wajib.** Instance yang dituju. Agent token diterbitkan per-instance — token dari instance lain selalu 401 di sini, dan MCP server menjelaskannya, bukan meneruskan 401 telanjang. |
|
|
417
|
+
| `HANOMAN_AGENT_TOKEN` | **Wajib.** Hanya dari env atau `~/.hanoman/agent-token` — **tak pernah** dari argumen baris perintah (ARGV terbaca proses lain di mesin yang sama). |
|
|
418
|
+
| `HANOMAN_MCP_READ_ONLY=1` / `--read-only` | Menyembunyikan seluruh tool tulis dari `tools/list`. |
|
|
419
|
+
| `HANOMAN_MCP_MAX_BYTES` / `--max-bytes <n>` | Plafon ukuran balasan tool. Default 24576. Balasan yang dipotong ditandai `truncated: true` + `shown`/`total`. |
|
|
420
|
+
|
|
421
|
+
Skema tool berversi (`MCP_TOOL_SCHEMA_VERSION`, saat ini **1**) dan aditif dalam satu versi:
|
|
422
|
+
menambah tool tak mematahkan klien lama.
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
*Doc-of-record fitur: [ADR-0065](../internal/docs/adr/0065-ai-agent-capability-agent-token.md) dan,
|
|
427
|
+
untuk permukaan MCP, [ADR-0099](../internal/docs/adr/0099-mcp-server-hanoman.md). Kontrak API penuh:
|
|
428
|
+
[`internal/docs/architecture/api-contract.md`](../internal/docs/architecture/api-contract.md) —
|
|
429
|
+
permukaan REST-nya identik dengan yang dipakai dashboard.*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hanoman",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Orchestrator + dashboard workflow docs-driven untuk sesi Claude Code / Codex",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"dist",
|
|
23
23
|
"web",
|
|
24
24
|
"prisma",
|
|
25
|
+
"docs",
|
|
25
26
|
"README.md",
|
|
26
27
|
"LICENSE"
|
|
27
28
|
],
|