hanoman 0.1.0

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.
@@ -0,0 +1,425 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ }
4
+
5
+ datasource db {
6
+ provider = "sqlite"
7
+ url = env("DATABASE_URL")
8
+ }
9
+
10
+ model Project {
11
+ id String @id
12
+ name String
13
+ desc String
14
+ kind String
15
+ repoDir String?
16
+ gitRemote String? // SPEC-213 · git remote resmi untuk clone di client; repoDir tetap LOCAL-only, tak disync
17
+ stack String @default("")
18
+ createdAt DateTime @default(now())
19
+ version Int @default(0) // SPEC-213 · version-stamp sync (ADR-0045)
20
+ updatedAt DateTime @updatedAt
21
+ ingestKeyHash String? // SPEC-249 · sha256(ingest key) hex; null = monitoring off. TAK PERNAH ke client.
22
+ ingestKeyPrefix String? // SPEC-249 · ~12-16 char awal key untuk hint UI (bukan rahasia)
23
+ helpEnabled Boolean @default(false) // SPEC-253 · opt-in Help Center publik
24
+ schedulerOptIn Boolean @default(false) // SPEC-294 · ADR-0072 · opt-in scheduler otonom (LOCAL — tak masuk FIELDS sync)
25
+ specs Spec[]
26
+ errorGroups ErrorGroup[]
27
+ tickets Ticket[]
28
+ sourceMaps SourceMapArtifact[] // SPEC-276 · source-map ter-upload per release (symbolication)
29
+ linksOut ProjectLink[] @relation("ProjectLinkFrom") // SPEC-337 · project ini bergantung pada …
30
+ linksIn ProjectLink[] @relation("ProjectLinkTo") // SPEC-337 · … yang bergantung pada project ini
31
+ }
32
+
33
+ // SPEC-337 · ADR-0075 · relasi integrasi/dependency BERARAH antar project: from BERGANTUNG PADA to.
34
+ // LOCAL-only (tak masuk SYNCED): id cuid + unique pasangan bertabrakan saat upsert-by-id lintas device.
35
+ // onUpdate: Cascade → rename project (ADR-0064) merambat gratis; tak ada ref longgar baru.
36
+ model ProjectLink {
37
+ id String @id @default(cuid())
38
+ fromProjectId String
39
+ toProjectId String
40
+ kind String // api | sdk | data | event | lainnya (zLinkKind)
41
+ note String @default("") // bentuk integrasinya — disalin apa adanya ke prompt sesi audit lintas
42
+ createdAt DateTime @default(now())
43
+ updatedAt DateTime @updatedAt
44
+ from Project @relation("ProjectLinkFrom", fields: [fromProjectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
45
+ to Project @relation("ProjectLinkTo", fields: [toProjectId], references: [id], onDelete: Cascade, onUpdate: Cascade)
46
+
47
+ @@unique([fromProjectId, toProjectId])
48
+ @@index([toProjectId])
49
+ }
50
+
51
+ model Spec {
52
+ id String @id
53
+ projectId String
54
+ title String
55
+ source String
56
+ stage String
57
+ priority String
58
+ author String
59
+ objective String
60
+ payload Json?
61
+ branchFrom String? // SPEC-143 · branch sumber worktree; null = default project (main)
62
+ baseSha String? // SPEC-176 · ADR-0030 · commit detach worktree sesi (nilai balik addWorktree)
63
+ headSha String? // SPEC-176 · ADR-0030 · commit HEAD worktree di akhir sesi (sebelum removeWorktree)
64
+ version Int @default(0) // SPEC-213 · version-stamp sync (ADR-0045)
65
+ updatedAt DateTime @updatedAt
66
+ project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
67
+ }
68
+
69
+ model Setting {
70
+ id Int @id @default(1)
71
+ data Json
72
+ }
73
+
74
+ // SPEC-180/184 · notifikasi. type "done" (backlog selesai) | "decision" (menunggu keputusan
75
+ // manusia). Dedup selesai lewat `key` unik ("done:<specId>"); decision di-dedup di sisi scan
76
+ // (key null; NULL berulang diizinkan Postgres pada kolom unik). sessionId = target redirect.
77
+ model Notification {
78
+ id String @id @default(cuid())
79
+ type String @default("done")
80
+ key String? @unique
81
+ specId String?
82
+ sessionId String?
83
+ title String
84
+ projectId String?
85
+ createdAt DateTime @default(now())
86
+ readAt DateTime?
87
+ }
88
+
89
+ // SPEC-169 · auth. passwordHash = scrypt "<saltHex>:<hashHex>". Tak ada RBAC — semua user setara.
90
+ model User {
91
+ id String @id @default(cuid())
92
+ email String @unique
93
+ passwordHash String
94
+ createdAt DateTime @default(now())
95
+ sessions Session[]
96
+ deviceTokens DeviceToken[]
97
+ }
98
+
99
+ // SPEC-169 · sesi opaque revocable. id = sha256(token); token asli hanya ada di cookie httpOnly.
100
+ model Session {
101
+ id String @id
102
+ userId String
103
+ createdAt DateTime @default(now())
104
+ expiresAt DateTime
105
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
106
+ }
107
+
108
+ // SPEC-164 · VPS yang dikelola hanoman. keyPath menunjuk berkas di mesin server —
109
+ // isi private key TIDAK PERNAH ada di database.
110
+ model Vps {
111
+ id String @id @default(cuid())
112
+ name String
113
+ host String
114
+ port Int @default(22)
115
+ user String
116
+ keyPath String?
117
+ createdAt DateTime @default(now())
118
+ lastSeenAt DateTime? // healthcheck sukses terakhir
119
+ health Json? // { uptime, disk, mem, load }
120
+ lastAuditAt DateTime?
121
+ audit Json? // VpsCheck[] — [{ check, status, detail }]
122
+ hardened Boolean @default(false) // derived: semua check kritis pass
123
+ version Int @default(0) // SPEC-213 · version-stamp sync (ADR-0045)
124
+ updatedAt DateTime @updatedAt
125
+ snapshots VpsAuditSnapshot[] // SPEC-220 · riwayat audit kepatuhan
126
+ itemStates VpsItemState[] // SPEC-220 · keputusan human per item (N/A, attest)
127
+ }
128
+
129
+ // SPEC-220 · ADR-0050 · hasil satu audit kepatuhan (append-only, fondasi drift Fase 3).
130
+ // results = { [itemId]: { status: pass|fail|warn|na|unknown, detail } } untuk item ber-probe.
131
+ model VpsAuditSnapshot {
132
+ id String @id @default(cuid())
133
+ vpsId String
134
+ createdAt DateTime @default(now())
135
+ results Json
136
+ scoreTotal Float
137
+ scoreBySection Json
138
+ detected Json? // SPEC-221 · { [section]: { present, detail } } — deteksi stack app-layer (advisory)
139
+ vps Vps @relation(fields: [vpsId], references: [id], onDelete: Cascade)
140
+
141
+ @@index([vpsId, createdAt])
142
+ }
143
+
144
+ // SPEC-220 · ADR-0050 · keputusan human durable per item: N/A (keluar denominator skor)
145
+ // dan attestasi INFO. actorEmail = jejak pelaku dari sesi auth.
146
+ model VpsItemState {
147
+ id String @id @default(cuid())
148
+ vpsId String
149
+ itemId String
150
+ na Boolean @default(false)
151
+ naReason String?
152
+ attested Boolean @default(false)
153
+ attestNote String?
154
+ actorEmail String?
155
+ updatedAt DateTime @default(now())
156
+ vps Vps @relation(fields: [vpsId], references: [id], onDelete: Cascade)
157
+
158
+ @@unique([vpsId, itemId])
159
+ }
160
+
161
+ // SPEC-213 · ADR-0044 · identitas mesin per-device. tokenHash = sha256(token); plaintext
162
+ // hanya ditampilkan sekali saat diterbitkan. Terikat ke user → atribusi author. Revoke = set revokedAt.
163
+ model DeviceToken {
164
+ id String @id @default(cuid())
165
+ userId String
166
+ name String
167
+ tokenHash String @unique
168
+ createdAt DateTime @default(now())
169
+ lastSeenAt DateTime?
170
+ revokedAt DateTime?
171
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
172
+ }
173
+
174
+ // SPEC-257 · ADR-0065 · kredensial AI agent eksternal. Server-local (cermin DeviceToken, TAK disync).
175
+ // capabilities = Json string[] divalidasi zod (@hanoman/shared). tokenHash = sha256(plaintext).
176
+ model AgentToken {
177
+ id String @id @default(cuid())
178
+ name String
179
+ tokenHash String @unique
180
+ tokenPrefix String
181
+ capabilities Json
182
+ enabled Boolean @default(true)
183
+ createdBy String?
184
+ createdAt DateTime @default(now())
185
+ lastUsedAt DateTime?
186
+ revokedAt DateTime?
187
+ }
188
+
189
+ // SPEC-213 · ADR-0047 · ringkasan hasil sesi (activity log). Append-only; whitelist field.
190
+ model SessionResult {
191
+ id String @id
192
+ projectId String
193
+ specId String?
194
+ oldStage String?
195
+ newStage String?
196
+ commitSha String?
197
+ branch String?
198
+ prUrl String?
199
+ status String
200
+ deviceId String?
201
+ author String?
202
+ version Int @default(0)
203
+ createdAt DateTime @default(now())
204
+ updatedAt DateTime @updatedAt
205
+ }
206
+
207
+ // SPEC-362 · ADR-0079 · riwayat sesi terminal. LOCAL-only: sesi hidup di tmux mesin ini dan
208
+ // transkripnya berkas di disk mesin ini — menyiarkannya ke hub akan mengirim baris yang menunjuk
209
+ // berkas yang tak ada di sana (cermin LocalBinding & SchedulerQueueItem). Tanpa `version`/notifySynced.
210
+ model SessionHistory {
211
+ id String @id
212
+ // id tmux sesi. BUKAN primary key: sessionIdForSpec() deterministik, jadi satu backlog yang
213
+ // dibuka-tutup lima kali menghasilkan lima baris dengan sessionId yang sama.
214
+ sessionId String
215
+ // Tanpa FK: sesi VPS memakai projectId sintetis "vps:<id>"/"vps-console:<id>" (routes/vps.ts).
216
+ // Konvensi yang sama dipakai SessionResult.
217
+ projectId String
218
+ specId String?
219
+ title String?
220
+ kind String
221
+ flow String?
222
+ agent String
223
+ model String?
224
+ effort String?
225
+ branch String?
226
+ cwd String
227
+ startedAt DateTime @default(now())
228
+ endedAt DateTime?
229
+ exitCode Int?
230
+ transcriptKey String?
231
+ transcriptBytes Int?
232
+ createdAt DateTime @default(now())
233
+ updatedAt DateTime @updatedAt
234
+
235
+ @@index([projectId, startedAt])
236
+ @@index([specId])
237
+ @@index([sessionId])
238
+ }
239
+
240
+ // SPEC-213 · ADR-0045 · change-feed server-authoritative. seq = kursor global sync.
241
+ model SyncLog {
242
+ // SPEC-398 · ADR-0086 · `Int`, BUKAN `BigInt`. Di SQLite sebuah kolom hanya jadi alias rowid
243
+ // (yang auto-terisi) bila tipe deklarasinya **tepat** `INTEGER`; `BigInt` membuat Prisma
244
+ // memancarkan `BIGINT NOT NULL PRIMARY KEY` — valid SQL, **tanpa** AUTOINCREMENT, tanpa
245
+ // peringatan dari `migrate diff` — sehingga setiap insert changefeed gagal
246
+ // "Null constraint violation on the fields: (`seq`)". Kursor tetap string di wire
247
+ // (`String(log.seq)`), jadi kontrak API tak berubah; 2^31 baris changefeed jauh lebih dari cukup.
248
+ seq Int @id @default(autoincrement())
249
+ entity String
250
+ recordId String
251
+ version Int
252
+ data Json
253
+ deviceId String?
254
+ createdAt DateTime @default(now())
255
+
256
+ @@index([entity, recordId])
257
+ }
258
+
259
+ // SPEC-213 · LOCAL-ONLY (tak pernah disync): map projectId → repoDir per-device (AC-6/7).
260
+ model LocalBinding {
261
+ projectId String @id
262
+ repoDir String
263
+ createdAt DateTime @default(now())
264
+ }
265
+
266
+ // SPEC-213 · LOCAL-ONLY: antre write lokal untuk push saat online (AC-17/18).
267
+ model SyncOutbox {
268
+ id String @id @default(cuid())
269
+ entity String
270
+ recordId String
271
+ createdAt DateTime @default(now())
272
+
273
+ @@unique([entity, recordId])
274
+ }
275
+
276
+ // SPEC-213 · LOCAL-ONLY: kursor pull terakhir (singleton).
277
+ model SyncState {
278
+ id Int @id @default(1)
279
+ cursor String @default("0")
280
+ }
281
+
282
+ // SPEC-270 · ADR-0067 · LOCAL-ONLY: antrean konflik dua-sisi menunggu keputusan manusia (modal).
283
+ model SyncConflict {
284
+ id String @id @default(cuid())
285
+ entity String
286
+ recordId String
287
+ localData Json
288
+ localVersion Int
289
+ localUpdatedAt DateTime
290
+ serverData Json
291
+ serverVersion Int
292
+ serverUpdatedAt DateTime
293
+ detectedAt DateTime @default(now())
294
+ resolvedAt DateTime?
295
+
296
+ @@unique([entity, recordId])
297
+ }
298
+
299
+ // SPEC-294 · ADR-0072 · LOCAL-ONLY (tak disync, cermin SyncOutbox): antrean durable kandidat peluncuran
300
+ // scheduler. specId @unique = idempoten satu-sesi-per-spec (ADR-0015). Unit peluncuran selalu Spec.
301
+ model SchedulerQueueItem {
302
+ id String @id @default(cuid())
303
+ specId String @unique
304
+ projectId String
305
+ source String // backlog | errors | triase (asal checker)
306
+ priority String // tinggi | sedang | rendah (urutan drain)
307
+ status String @default("queued") // queued | launched | done | failed
308
+ sessionId String?
309
+ note String? // alasan gagal (diisi daun #5)
310
+ enqueuedAt DateTime @default(now())
311
+ launchedAt DateTime?
312
+
313
+ @@index([status])
314
+ }
315
+
316
+ // SPEC-215 · ADR-0049 · LOCAL-ONLY: override config runtime (env → DB). Tak pernah disync.
317
+ model RuntimeConfig {
318
+ key String @id
319
+ value String
320
+ updatedAt DateTime @updatedAt
321
+ }
322
+
323
+ // SPEC-249 · ADR-0060 · grup error per project (fingerprint dedup). SPEC-268/ADR-0066: agregat
324
+ // grup kini tersync (version-stamp); ErrorEvent mentah tetap server-local (tak disync).
325
+ model ErrorGroup {
326
+ id String @id @default(cuid())
327
+ projectId String
328
+ fingerprint String
329
+ type String
330
+ message String
331
+ sampleStack String?
332
+ sampleFrames Json? // SPEC-276 · ADR-0070 · frame mentah sample (disymbolikasi saat display)
333
+ release String? // SPEC-276 · ADR-0070 · release terakhir (korelasi build + surface di UI)
334
+ environment String
335
+ status String @default("new") // new | escalated | resolved
336
+ count Int @default(0)
337
+ firstSeenAt DateTime @default(now())
338
+ lastSeenAt DateTime @default(now())
339
+ specId String?
340
+ createdAt DateTime @default(now())
341
+ updatedAt DateTime @updatedAt
342
+ version Int @default(0) // SPEC-268 · version-stamp sync (ADR-0066)
343
+ project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
344
+ events ErrorEvent[]
345
+
346
+ @@unique([projectId, fingerprint])
347
+ @@index([projectId, lastSeenAt])
348
+ }
349
+
350
+ // SPEC-249 · ADR-0060 · kejadian error mentah, dipangkas retensi (cap per grup + umur).
351
+ model ErrorEvent {
352
+ id String @id @default(cuid())
353
+ groupId String
354
+ projectId String
355
+ type String
356
+ message String
357
+ stack String?
358
+ frames Json? // SPEC-276 · ADR-0070 · frame terstruktur mentah dari SDK
359
+ environment String
360
+ release String?
361
+ context Json?
362
+ receivedAt DateTime @default(now())
363
+ group ErrorGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
364
+
365
+ @@index([groupId, receivedAt])
366
+ @@index([projectId, receivedAt])
367
+ }
368
+
369
+ // SPEC-276 · ADR-0070 · source-map ter-upload per release untuk symbolication. Byte map di
370
+ // HANOMAN_UPLOAD_DIR (server-local, TAK disync — cermin TicketAttachment biner & ErrorEvent).
371
+ model SourceMapArtifact {
372
+ id String @id @default(cuid())
373
+ projectId String
374
+ release String
375
+ filename String // basename artifact hasil-build (mis. index-4f3a2b.js) yang dipetakan map ini
376
+ debugId String?
377
+ storageKey String // berkas opaque (uuid.map) di upload dir
378
+ size Int
379
+ createdAt DateTime @default(now())
380
+ project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
381
+
382
+ @@unique([projectId, release, filename])
383
+ @@index([projectId, release])
384
+ }
385
+
386
+ // SPEC-253 · ADR-0062 · tiket keluhan Help Center. SPEC-268/ADR-0066: metadata tiket kini tersync
387
+ // (version-stamp); lampiran (TicketAttachment, file biner) tetap server-local (tak disync).
388
+ model Ticket {
389
+ id String @id @default(cuid())
390
+ projectId String
391
+ number Int // nomor pendek human-readable per project (Tiket #<n>)
392
+ category String // bug | fitur | pertanyaan | lainnya (zTicketCategory)
393
+ title String
394
+ detail String
395
+ reporterEmail String
396
+ status String @default("new") // new | accepted | rejected (zTicketStatus)
397
+ accessKeyHash String @unique // sha256(kunci opaque) — lookup cek status. Plaintext hanya sekali.
398
+ shareToken String? @unique // SPEC-293 · token bagikan link status publik (hnm_shr_…); operator → pelapor
399
+ specId String? // soft-link Spec hasil promosi (cermin ErrorGroup.specId)
400
+ createdAt DateTime @default(now())
401
+ updatedAt DateTime @updatedAt
402
+ version Int @default(0) // SPEC-268 · version-stamp sync (ADR-0066)
403
+ project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
404
+ attachments TicketAttachment[]
405
+
406
+ @@unique([projectId, number])
407
+ @@index([projectId, createdAt])
408
+ }
409
+
410
+ // SPEC-253 · ADR-0062 · lampiran gambar tiket. Berkas di HANOMAN_UPLOAD_DIR (server-local, tak disync).
411
+ model TicketAttachment {
412
+ id String @id @default(cuid())
413
+ ticketId String
414
+ projectId String // denormal — isolasi & query murah (pola ErrorEvent.projectId)
415
+ filename String // nama asli tersanitasi (display saja)
416
+ mimeType String // image/png | image/jpeg | image/webp
417
+ size Int
418
+ storageKey String // nama berkas opaque di upload dir (uuid+ext)
419
+ createdAt DateTime @default(now())
420
+ version Int @default(0) // SPEC-272 · version-stamp sync (ADR-0068)
421
+ updatedAt DateTime @updatedAt // SPEC-272 · jam LWW (lampiran immutable → praktis konstan)
422
+ ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
423
+
424
+ @@index([ticketId])
425
+ }
@@ -0,0 +1,41 @@
1
+ @import"https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400&family=IBM+Plex+Serif:ital,wght@0,400;0,500;0,600;0,700;1,400&family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;1,400&display=swap";:root{--ink-900: #17130c;--ink-800: #241d13;--ink-700: #3a3125;--ink-600: #574b3a;--ink-500: #6f6250;--ink-400: #90826c;--ink-300: #b3a794;--ink-200: #d6ccb9;--bone-000: #fffdf8;--bone-100: #faf6ec;--bone-200: #f2ebdc;--bone-300: #e8dfcc;--bone-400: #dccfb6;--brass-700: #7a5417;--brass-600: #97671f;--brass-500: #b8863b;--brass-400: #cf9d55;--brass-300: #e3c988;--brass-100: #f3e6c4;--brass-050: #faf2df;--wind-700: #2f5560;--wind-600: #3f6e7a;--wind-500: #517f8c;--wind-100: #dbe7ea;--wind-050: #eef4f5;--leaf-600: #4d6b30;--leaf-500: #5f8340;--leaf-100: #e6eed7;--amber-600: #b3771a;--amber-500: #cf8f28;--amber-100: #f7e9c9;--clay-600: #a23b2e;--clay-500: #bd4a3a;--clay-100: #f5dcd6;--term-bg: #1c1810;--term-fg: #e9e0cd;--term-dim: #8f8467;--term-line: #2c2618;--surface-page: var(--bone-100);--surface-panel: var(--bone-200);--surface-card: var(--bone-000);--surface-sunken: var(--bone-200);--surface-inverse: var(--ink-900);--surface-code: var(--term-bg);--text-strong: var(--ink-900);--text-body: var(--ink-700);--text-muted: var(--ink-500);--text-subtle: var(--ink-400);--text-on-brass: var(--ink-900);--text-on-dark: var(--term-fg);--text-link: var(--wind-600);--border-hair: var(--bone-300);--border-strong: var(--bone-400);--border-focus: var(--brass-500);--accent: var(--brass-500);--accent-hover: var(--brass-600);--accent-press: var(--brass-700);--accent-tint: var(--brass-100);--accent-on: var(--ink-900);--info: var(--wind-600);--info-tint: var(--wind-100);--status-ok: var(--leaf-600);--status-ok-tint: var(--leaf-100);--status-warn: var(--amber-600);--status-warn-tint: var(--amber-100);--status-err: var(--clay-600);--status-err-tint: var(--clay-100);--ring: 0 0 0 3px color-mix(in srgb, var(--brass-500) 45%, transparent)}:root{--font-serif: "IBM Plex Serif", Georgia, "Times New Roman", serif;--font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, sans-serif;--font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;--font-display: var(--font-serif);--font-body: var(--font-sans);--font-ui: var(--font-sans);--font-code: var(--font-mono);--text-2xs: 11px;--text-xs: 12px;--text-sm: 13px;--text-md: 14px;--text-base:16px;--text-lg: 18px;--text-xl: 20px;--text-2xl: 24px;--text-3xl: 30px;--text-4xl: 38px;--text-5xl: 48px;--text-6xl: 62px;--weight-regular: 400;--weight-medium: 500;--weight-semibold: 600;--weight-bold: 700;--leading-tight: 1.12;--leading-snug: 1.3;--leading-normal: 1.5;--leading-relaxed:1.65;--tracking-tight: -.02em;--tracking-normal: 0;--tracking-wide: .02em;--tracking-caps: .14em}:root{--space-0: 0;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 20px;--space-6: 24px;--space-7: 32px;--space-8: 40px;--space-9: 48px;--space-10: 64px;--space-11: 80px;--space-12: 96px;--radius-xs: 3px;--radius-sm: 5px;--radius-md: 8px;--radius-lg: 12px;--radius-xl: 16px;--radius-pill: 999px;--border-thin: 1px;--border-med: 1.5px;--border-thick:2px;--content-max: 1200px;--sidebar-w: 248px;--topbar-h: 56px}:root{--shadow-xs: 0 1px 1px rgba(35, 27, 15, .05);--shadow-sm: 0 1px 2px rgba(35, 27, 15, .06), 0 1px 1px rgba(35, 27, 15, .04);--shadow-md: 0 2px 4px rgba(35, 27, 15, .07), 0 4px 12px rgba(35, 27, 15, .06);--shadow-lg: 0 8px 24px rgba(35, 27, 15, .1), 0 2px 6px rgba(35, 27, 15, .06);--shadow-xl: 0 20px 48px rgba(35, 27, 15, .16), 0 4px 12px rgba(35, 27, 15, .08);--shadow-inset: inset 0 1px 2px rgba(35, 27, 15, .08);--shadow-gild: 0 1px 0 rgba(255, 253, 248, .6) inset, 0 0 0 1px var(--brass-300);--elevation-flat: none;--elevation-raised: var(--shadow-sm);--elevation-float: var(--shadow-md);--elevation-overlay:var(--shadow-lg);--elevation-modal: var(--shadow-xl);--ease-out: cubic-bezier(.22, .61, .36, 1);--ease-inout: cubic-bezier(.65, 0, .35, 1);--dur-fast: .12s;--dur-base: .18s;--dur-slow: .28s;--transition-base: all var(--dur-base) var(--ease-out);--transition-fast: all var(--dur-fast) var(--ease-out)}*,*:before,*:after{box-sizing:border-box}body{margin:0;background:var(--surface-page);color:var(--text-body);font-family:var(--font-body);font-size:var(--text-base);line-height:var(--leading-normal);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}h1,h2,h3,h4{font-family:var(--font-display);color:var(--text-strong);line-height:var(--leading-tight);letter-spacing:var(--tracking-tight);font-weight:var(--weight-semibold);margin:0 0 var(--space-3)}p{margin:0 0 var(--space-3)}a{color:var(--text-link);text-decoration:none;border-bottom:1px solid color-mix(in srgb,var(--text-link) 35%,transparent);transition:var(--transition-fast)}a:hover{color:var(--wind-700);border-bottom-color:var(--wind-700)}code,kbd,samp,pre{font-family:var(--font-code);font-size:.92em}.hn-eyebrow{font-family:var(--font-mono);font-size:var(--text-2xs);font-weight:var(--weight-medium);letter-spacing:var(--tracking-caps);text-transform:uppercase;color:var(--text-muted)}::selection{background:var(--brass-100);color:var(--ink-900)}html,body{margin:0;height:100%;background:var(--surface-page)}#root{height:100vh;overflow:hidden}button{--accent-on: #fff}a{color:var(--text-link);text-decoration:none}a:hover{color:var(--wind-700);text-decoration:underline}.hn-md{color:var(--text-body);font-family:var(--font-sans);font-size:15px;line-height:1.72;overflow-wrap:anywhere}.hn-md>:first-child{margin-top:8px}.hn-md h1,.hn-md h2,.hn-md h3,.hn-md h4{font-family:var(--font-display);font-weight:600;letter-spacing:-.02em;color:var(--ink-900);line-height:1.2;margin:1.6em 0 .5em}.hn-md h1{font-size:30px;margin-top:.4em;padding-bottom:.34em;border-bottom:1px solid var(--border-hair)}.hn-md h2{font-size:22px}.hn-md h3{font-size:18px}.hn-md h4{font-size:15px}.hn-md p{margin:.7em 0}.hn-md ul,.hn-md ol{margin:.6em 0;padding-left:1.5em}.hn-md li{margin:.28em 0}.hn-md li::marker{color:var(--brass-500)}.hn-md ul.contains-task-list,.hn-md li.task-list-item{list-style:none}.hn-md li.task-list-item{padding-left:0}.hn-md input[type=checkbox]{accent-color:var(--brass-500);margin-right:8px;transform:translateY(1px)}.hn-md strong{font-weight:600;color:var(--ink-900)}.hn-md em{color:var(--text-muted)}.hn-md a{color:var(--text-link)}.hn-md hr{border:none;border-top:1px solid var(--border-hair);margin:1.6em 0}.hn-md code{font-family:var(--font-mono);font-size:.86em;background:var(--bone-200);color:var(--ink-800);padding:1.5px 5px;border-radius:var(--radius-xs);border:1px solid var(--border-hair)}.hn-md pre{background:var(--surface-code);border:1px solid var(--term-line);border-radius:var(--radius-lg);padding:14px 16px;overflow:auto;margin:1em 0;white-space:pre-wrap;overflow-wrap:anywhere}.hn-md pre code{background:transparent;border:none;padding:0;color:var(--term-fg);font-size:12.5px;line-height:1.7}.hn-md blockquote{margin:1em 0;padding:4px 16px;border-left:3px solid var(--brass-500);background:var(--brass-050);color:var(--text-body);border-radius:0 var(--radius-sm) var(--radius-sm) 0}.hn-md blockquote p{margin:.4em 0}.hn-md table{border-collapse:collapse;width:100%;margin:1em 0;font-size:14px;table-layout:fixed}.hn-md th,.hn-md td{overflow-wrap:anywhere}.hn-md th{font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-muted);text-align:left;padding:8px 12px;background:var(--bone-100);border:1px solid var(--border-hair)}.hn-md td{padding:8px 12px;border:1px solid var(--border-hair);color:var(--text-body)}.hn-md td code{font-size:.82em}.hn-md img{max-width:100%;border-radius:var(--radius-sm)}@keyframes hn-fade-in{0%{opacity:0}to{opacity:1}}@keyframes hn-modal-in{0%{opacity:0;transform:translateY(8px) scale(.99)}to{opacity:1;transform:none}}@keyframes hn-toast-in{0%{opacity:0;transform:translate(-50%,10px)}to{opacity:1;transform:translate(-50%)}}/**
2
+ * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
+ * https://github.com/chjj/term.js
5
+ * @license MIT
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ *
25
+ * Originally forked from (with the author's permission):
26
+ * Fabrice Bellard's javascript vt100 for jslinux:
27
+ * http://bellard.org/jslinux/
28
+ * Copyright (c) 2011 Fabrice Bellard
29
+ * The original design remains. The terminal itself
30
+ * has been extended to include xterm CSI codes, among
31
+ * other features.
32
+ */.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{font-family:monospace;-webkit-user-select:text;user-select:text;white-space:pre}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;background:#0000;transition:opacity .1s linear;z-index:11}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
33
+ Theme: GitHub
34
+ Description: Light theme as seen on github.com
35
+ Author: github.com
36
+ Maintainer: @Hirse
37
+ Updated: 2021-05-15
38
+
39
+ Outdated base version: https://github.com/primer/github-syntax-light
40
+ Current colors taken from GitHub's CSS
41
+ */.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#005cc5}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-comment,.hljs-code,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}