hanoman 0.1.54 → 0.1.55

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.54",
3
- "sha": "60388865",
4
- "builtAt": "2026-08-21T02:07:30.384Z"
2
+ "version": "0.1.55",
3
+ "sha": "912f83b1",
4
+ "builtAt": "2026-08-21T09:35:37.428Z"
5
5
  }
package/dist/server.js CHANGED
@@ -39259,6 +39259,33 @@ function installCommand(m, agent, shell = shellBin()) {
39259
39259
  return [shell, "-lc", `${m.install[agent].join(" && ")}; exec '${shell}' -l`];
39260
39260
  }
39261
39261
 
39262
+ // src/services/terminal-diag.ts
39263
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync8, statSync as statSync3, rmSync as rmSync4 } from "node:fs";
39264
+ import { join as join15 } from "node:path";
39265
+ var DIAG_MAX_BYTES = 2 * 1024 * 1024;
39266
+ var KINDS = /* @__PURE__ */ new Set(["key", "comp", "data", "ack", "pred"]);
39267
+ var ID = /^[A-Za-z0-9_-]{1,64}$/;
39268
+ function diagFile(home3, sessionId2) {
39269
+ if (!ID.test(sessionId2)) throw new Error(`id sesi tak sah untuk diag: ${sessionId2}`);
39270
+ return join15(home3, "diag", `${sessionId2}.jsonl`);
39271
+ }
39272
+ function usable(ev) {
39273
+ if (!ev || typeof ev !== "object") return false;
39274
+ const e = ev;
39275
+ return typeof e.t === "number" && typeof e.k === "string" && KINDS.has(e.k) && typeof e.v === "string" && (e.n === void 0 || typeof e.n === "number");
39276
+ }
39277
+ function appendDiag(home3, sessionId2, events2) {
39278
+ const file = diagFile(home3, sessionId2);
39279
+ const rows = events2.filter(usable);
39280
+ if (!rows.length) return;
39281
+ mkdirSync8(join15(home3, "diag"), { recursive: true });
39282
+ try {
39283
+ if (statSync3(file).size > DIAG_MAX_BYTES) rmSync4(file);
39284
+ } catch {
39285
+ }
39286
+ appendFileSync2(file, rows.map((r) => JSON.stringify(r)).join("\n") + "\n");
39287
+ }
39288
+
39262
39289
  // src/routes/terminal.ts
39263
39290
  init_notifications();
39264
39291
  init_stage_machine();
@@ -39608,6 +39635,12 @@ async function terminal_default(app2, opts) {
39608
39635
  writeTo(id, m.d, client);
39609
39636
  if (typeof m.seq === "number") socket2.send(JSON.stringify({ t: "ack", seq: m.seq }));
39610
39637
  } else if (m.t === "resize" && m.cols && m.rows) resize(id, m.cols, m.rows);
39638
+ else if (m.t === "diag" && Array.isArray(m.ev)) {
39639
+ try {
39640
+ appendDiag(resolveHome(), id, m.ev);
39641
+ } catch {
39642
+ }
39643
+ }
39611
39644
  });
39612
39645
  const revalidate = setInterval(() => {
39613
39646
  void revalidateWsPrincipal(req, principal).then((ok2) => {
@@ -39801,9 +39834,9 @@ import { readFileSync as readFileSync8 } from "node:fs";
39801
39834
  // src/services/vps-ssh.ts
39802
39835
  init_config2();
39803
39836
  import { spawn as spawn4 } from "node:child_process";
39804
- import { mkdtempSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
39837
+ import { mkdtempSync, rmSync as rmSync5, writeFileSync as writeFileSync4 } from "node:fs";
39805
39838
  import { tmpdir as tmpdir3 } from "node:os";
39806
- import { dirname as dirname10, join as join15 } from "node:path";
39839
+ import { dirname as dirname10, join as join16 } from "node:path";
39807
39840
  var sshBin = () => effectiveStr("HANOMAN_SSH_BIN") ?? "ssh";
39808
39841
  function consoleArgv(t) {
39809
39842
  return [
@@ -39818,8 +39851,8 @@ function consoleArgv(t) {
39818
39851
  ];
39819
39852
  }
39820
39853
  function askpassScript() {
39821
- const dir2 = mkdtempSync(join15(tmpdir3(), "hanoman-askpass-"));
39822
- const path2 = join15(dir2, "askpass.sh");
39854
+ const dir2 = mkdtempSync(join16(tmpdir3(), "hanoman-askpass-"));
39855
+ const path2 = join16(dir2, "askpass.sh");
39823
39856
  writeFileSync4(path2, `#!/bin/sh
39824
39857
  printf '%s' "$HANOMAN_SSH_PASSWORD"
39825
39858
  `, { mode: 448 });
@@ -39859,7 +39892,7 @@ function sshExec(t, remoteCmd, opts = {}) {
39859
39892
  const timer9 = setTimeout(() => p.kill("SIGKILL"), opts.timeoutMs ?? 6e4);
39860
39893
  const done = (r) => {
39861
39894
  clearTimeout(timer9);
39862
- if (askpass) rmSync4(dirname10(askpass), { recursive: true, force: true });
39895
+ if (askpass) rmSync5(dirname10(askpass), { recursive: true, force: true });
39863
39896
  resolve21(r);
39864
39897
  };
39865
39898
  p.stdout.on("data", (d) => {
@@ -39880,15 +39913,15 @@ function sshExec(t, remoteCmd, opts = {}) {
39880
39913
  // src/services/vps-audit.ts
39881
39914
  init_db();
39882
39915
  import { readFileSync as readFileSync7 } from "node:fs";
39883
- import { join as join17 } from "node:path";
39916
+ import { join as join18 } from "node:path";
39884
39917
 
39885
39918
  // src/runner/deps.ts
39886
39919
  import { existsSync as existsSync11 } from "node:fs";
39887
- import { dirname as dirname11, join as join16 } from "node:path";
39920
+ import { dirname as dirname11, join as join17 } from "node:path";
39888
39921
  function repoRootFrom(startDir) {
39889
39922
  let dir2 = startDir;
39890
39923
  for (let i = 0; i < 8; i++) {
39891
- if (existsSync11(join16(dir2, "pnpm-workspace.yaml"))) return dir2;
39924
+ if (existsSync11(join17(dir2, "pnpm-workspace.yaml"))) return dir2;
39892
39925
  const parent = dirname11(dir2);
39893
39926
  if (parent === dir2) break;
39894
39927
  dir2 = parent;
@@ -39994,7 +40027,7 @@ function parseHealth(out4) {
39994
40027
  return { uptime: h.uptime ?? "", disk: h.disk ?? "", mem: h.mem ?? "", load: h.load ?? "" };
39995
40028
  }
39996
40029
  var target = (v) => ({ host: v.host, port: v.port, user: v.user, keyPath: v.keyPath });
39997
- var scriptPath = (f) => join17(repoRoot(), "server", "scripts", "vps", f);
40030
+ var scriptPath = (f) => join18(repoRoot(), "server", "scripts", "vps", f);
39998
40031
  async function itemStatesOf(vpsId) {
39999
40032
  const rows = await prisma.vpsItemState.findMany({ where: { vpsId } });
40000
40033
  return Object.fromEntries(rows.map((s2) => [s2.itemId, { na: s2.na, attested: s2.attested }]));
@@ -40142,31 +40175,31 @@ async function buildChecklist(vpsId) {
40142
40175
  init_src2();
40143
40176
  init_config2();
40144
40177
  import { execFileSync as execFileSync2 } from "node:child_process";
40145
- import { chmodSync as chmodSync3, copyFileSync, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync9, unlinkSync as unlinkSync2 } from "node:fs";
40178
+ import { chmodSync as chmodSync3, copyFileSync, existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync9, unlinkSync as unlinkSync2 } from "node:fs";
40146
40179
  import { homedir as homedir5 } from "node:os";
40147
- import { join as join18 } from "node:path";
40180
+ import { join as join19 } from "node:path";
40148
40181
  var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? resolveDataDirs().sshKeys;
40149
40182
  var KEY_FILES = ["id_ed25519", "id_ed25519.pub"];
40150
40183
  function adoptLegacyKey(dir2) {
40151
40184
  if (effectiveStr("HANOMAN_SSH_KEY_DIR")) return;
40152
- const legacy = join18(homedir5(), ".hanoman");
40185
+ const legacy = join19(homedir5(), ".hanoman");
40153
40186
  if (legacy === dir2) return;
40154
- if (!KEY_FILES.every((f) => existsSync12(join18(legacy, f)))) return;
40155
- mkdirSync8(dir2, { recursive: true, mode: 448 });
40187
+ if (!KEY_FILES.every((f) => existsSync12(join19(legacy, f)))) return;
40188
+ mkdirSync9(dir2, { recursive: true, mode: 448 });
40156
40189
  for (const f of KEY_FILES) {
40157
- copyFileSync(join18(legacy, f), join18(dir2, f));
40158
- chmodSync3(join18(dir2, f), 384);
40190
+ copyFileSync(join19(legacy, f), join19(dir2, f));
40191
+ chmodSync3(join19(dir2, f), 384);
40159
40192
  }
40160
- for (const f of KEY_FILES) unlinkSync2(join18(legacy, f));
40193
+ for (const f of KEY_FILES) unlinkSync2(join19(legacy, f));
40161
40194
  console.log(`vps: key SSH dipindah dari ${legacy} ke ${dir2} (SPEC-846 \u2014 satu batas backup)`);
40162
40195
  }
40163
40196
  function ensureHanomanKey() {
40164
40197
  const dir2 = keyDir();
40165
- const privPath = join18(dir2, KEY_FILES[0]);
40198
+ const privPath = join19(dir2, KEY_FILES[0]);
40166
40199
  const pubPath = `${privPath}.pub`;
40167
40200
  if (!existsSync12(privPath)) adoptLegacyKey(dir2);
40168
40201
  if (!existsSync12(privPath)) {
40169
- mkdirSync8(dir2, { recursive: true, mode: 448 });
40202
+ mkdirSync9(dir2, { recursive: true, mode: 448 });
40170
40203
  execFileSync2(
40171
40204
  "ssh-keygen",
40172
40205
  ["-t", "ed25519", "-N", "", "-C", "hanoman", "-f", privPath],
@@ -40401,7 +40434,7 @@ init_config2();
40401
40434
  import { execFileSync as execFileSync3 } from "node:child_process";
40402
40435
  import { readFileSync as readFileSync11 } from "node:fs";
40403
40436
  import { homedir as homedir7 } from "node:os";
40404
- import { join as join19 } from "node:path";
40437
+ import { join as join20 } from "node:path";
40405
40438
  var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
40406
40439
  var TTL_MS2 = 3e4;
40407
40440
  var lastOk = null;
@@ -40414,7 +40447,7 @@ var LABELS = {
40414
40447
  var humanize = (k) => k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
40415
40448
  var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
40416
40449
  function credsFile() {
40417
- return join19(effectiveStr("CLAUDE_CONFIG_DIR") ?? join19(homedir7(), ".claude"), ".credentials.json");
40450
+ return join20(effectiveStr("CLAUDE_CONFIG_DIR") ?? join20(homedir7(), ".claude"), ".credentials.json");
40418
40451
  }
40419
40452
  function readAccessToken() {
40420
40453
  if (process.platform === "darwin" && !effectiveStr("CLAUDE_CONFIG_DIR")) {
@@ -40488,14 +40521,14 @@ async function getLimits() {
40488
40521
  // src/services/codex-limits.ts
40489
40522
  import { open as open3, readdir as readdir4, stat as stat2 } from "node:fs/promises";
40490
40523
  import { homedir as homedir8 } from "node:os";
40491
- import { join as join20 } from "node:path";
40524
+ import { join as join21 } from "node:path";
40492
40525
  var TTL_MS3 = 3e4;
40493
40526
  var STALE_AFTER_MS = 12 * 36e5;
40494
40527
  var TAIL_BYTES = 512 * 1024;
40495
40528
  var MAX_FILES = 8;
40496
40529
  var cache3 = null;
40497
40530
  var freshUntil2 = 0;
40498
- var codexSessionsDir = () => join20(process.env.CODEX_HOME ?? join20(homedir8(), ".codex"), "sessions");
40531
+ var codexSessionsDir = () => join21(process.env.CODEX_HOME ?? join21(homedir8(), ".codex"), "sessions");
40499
40532
  var UNAVAILABLE = { status: "unavailable", windows: [], fetchedAt: null, plan: null };
40500
40533
  function windowLabel(minutes) {
40501
40534
  if (minutes === 300) return "Sesi 5 jam";
@@ -40528,7 +40561,7 @@ async function recentRollouts(dir2) {
40528
40561
  return [];
40529
40562
  }
40530
40563
  const stamped = await Promise.all(entries.map(async (rel) => {
40531
- const full = join20(dir2, rel);
40564
+ const full = join21(dir2, rel);
40532
40565
  try {
40533
40566
  return { full, mtime: (await stat2(full)).mtimeMs };
40534
40567
  } catch {
@@ -41199,7 +41232,7 @@ init_config2();
41199
41232
  init_src2();
41200
41233
  import { randomUUID as randomUUID7 } from "node:crypto";
41201
41234
  import { mkdir as mkdir5, writeFile as writeFile4, readFile as readFile4, unlink as unlink4, readdir as readdir5, stat as stat3 } from "node:fs/promises";
41202
- import { join as join21, resolve as resolve19, basename as basename6 } from "node:path";
41235
+ import { join as join22, resolve as resolve19, basename as basename6 } from "node:path";
41203
41236
  var MAX_TRANSCRIPT_BYTES = 1024 * 1024;
41204
41237
  function transcriptDir() {
41205
41238
  return resolve19(effectiveStr("HANOMAN_TRANSCRIPT_DIR")?.trim() || resolveDataDirs().transcripts);
@@ -41220,13 +41253,13 @@ async function saveTranscript(text) {
41220
41253
  const dir2 = transcriptDir();
41221
41254
  await mkdir5(dir2, { recursive: true, mode: 448 });
41222
41255
  const key = `${randomUUID7()}.log`;
41223
- await writeFile4(join21(dir2, key), body, { encoding: "utf8", mode: 384 });
41256
+ await writeFile4(join22(dir2, key), body, { encoding: "utf8", mode: 384 });
41224
41257
  return { key, bytes: Buffer.byteLength(body, "utf8"), truncated };
41225
41258
  }
41226
41259
  async function readTranscript(key) {
41227
41260
  if (!key) return null;
41228
41261
  try {
41229
- return await readFile4(join21(transcriptDir(), basename6(key)), "utf8");
41262
+ return await readFile4(join22(transcriptDir(), basename6(key)), "utf8");
41230
41263
  } catch {
41231
41264
  return null;
41232
41265
  }
@@ -41234,7 +41267,7 @@ async function readTranscript(key) {
41234
41267
  async function deleteTranscript(key) {
41235
41268
  if (!key) return;
41236
41269
  try {
41237
- await unlink4(join21(transcriptDir(), basename6(key)));
41270
+ await unlink4(join22(transcriptDir(), basename6(key)));
41238
41271
  } catch (e) {
41239
41272
  if (e.code !== "ENOENT") throw e;
41240
41273
  }
@@ -41252,7 +41285,7 @@ async function listTranscripts() {
41252
41285
  for (const name2 of names) {
41253
41286
  if (!name2.endsWith(".log")) continue;
41254
41287
  try {
41255
- rows.push({ key: name2, mtimeMs: (await stat3(join21(dir2, name2))).mtimeMs });
41288
+ rows.push({ key: name2, mtimeMs: (await stat3(join22(dir2, name2))).mtimeMs });
41256
41289
  } catch {
41257
41290
  }
41258
41291
  }
@@ -42712,9 +42745,9 @@ init_pty();
42712
42745
  init_session_sandbox();
42713
42746
  import { execFile as execFile12 } from "node:child_process";
42714
42747
  import { randomUUID as randomUUID9 } from "node:crypto";
42715
- import { mkdirSync as mkdirSync9, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "node:fs";
42748
+ import { mkdirSync as mkdirSync10, rmSync as rmSync6, writeFileSync as writeFileSync5 } from "node:fs";
42716
42749
  import { tmpdir as tmpdir4 } from "node:os";
42717
- import { join as join22 } from "node:path";
42750
+ import { join as join23 } from "node:path";
42718
42751
  var binFor = (agent) => agent === "codex" ? effectiveStr("HANOMAN_CODEX_BIN") ?? "codex" : effectiveStr("HANOMAN_CLAUDE_BIN") ?? "claude";
42719
42752
  function leadArgv(o) {
42720
42753
  if (o.agent === "codex") {
@@ -42742,12 +42775,12 @@ function leadProcess(prompt, o, env = process.env) {
42742
42775
  const mode = env.HANOMAN_SESSION_SANDBOX ?? (env.NODE_ENV === "production" ? "required" : "off");
42743
42776
  if (mode === "off") return { file, args: directArgs, cwd: o.cwd, cleanup: () => {
42744
42777
  } };
42745
- const promptDir = join22(tmpdir4(), "hanoman-prompts");
42746
- mkdirSync9(promptDir, { recursive: true, mode: 448 });
42747
- const promptFile = join22(promptDir, `oneshot-${randomUUID9()}`);
42778
+ const promptDir = join23(tmpdir4(), "hanoman-prompts");
42779
+ mkdirSync10(promptDir, { recursive: true, mode: 448 });
42780
+ const promptFile = join23(promptDir, `oneshot-${randomUUID9()}`);
42748
42781
  writeFileSync5(promptFile, prompt, { flag: "wx", mode: 384 });
42749
- const workspace = o.cwd ?? join22(tmpdir4(), `hanoman-oneshot-${randomUUID9()}`);
42750
- if (!o.cwd) mkdirSync9(workspace, { recursive: false, mode: 448 });
42782
+ const workspace = o.cwd ?? join23(tmpdir4(), `hanoman-oneshot-${randomUUID9()}`);
42783
+ if (!o.cwd) mkdirSync10(workspace, { recursive: false, mode: 448 });
42751
42784
  try {
42752
42785
  const argsWithoutPrompt = directArgs.slice(0, -1);
42753
42786
  const command = [file, ...argsWithoutPrompt].map(shellQuote).join(" ") + ` "$(cat ${shellQuote(promptFile)})"`;
@@ -42764,13 +42797,13 @@ function leadProcess(prompt, o, env = process.env) {
42764
42797
  args: sandbox.slice(1),
42765
42798
  promptFile,
42766
42799
  cleanup: () => {
42767
- rmSync5(promptFile, { force: true });
42768
- if (!o.cwd) rmSync5(workspace, { recursive: true, force: true });
42800
+ rmSync6(promptFile, { force: true });
42801
+ if (!o.cwd) rmSync6(workspace, { recursive: true, force: true });
42769
42802
  }
42770
42803
  };
42771
42804
  } catch (error) {
42772
- rmSync5(promptFile, { force: true });
42773
- if (!o.cwd) rmSync5(workspace, { recursive: true, force: true });
42805
+ rmSync6(promptFile, { force: true });
42806
+ if (!o.cwd) rmSync6(workspace, { recursive: true, force: true });
42774
42807
  throw error;
42775
42808
  }
42776
42809
  }
@@ -44272,9 +44305,9 @@ var US2 = "";
44272
44305
  var RS = "";
44273
44306
  var NO_REPO = "project ini belum ditautkan ke repo di mesin ini";
44274
44307
  var NO_TAG = "repo project ini belum punya tag rilis";
44275
- var usable = (repoDir) => !!repoDir && existsSync15(repoDir);
44308
+ var usable2 = (repoDir) => !!repoDir && existsSync15(repoDir);
44276
44309
  async function listTags(repoDir) {
44277
- if (!usable(repoDir)) return { tags: [], head: null, reason: NO_REPO };
44310
+ if (!usable2(repoDir)) return { tags: [], head: null, reason: NO_REPO };
44278
44311
  try {
44279
44312
  const { stdout } = await exec10("git", ["tag", "--list", "--sort=-creatordate"], { cwd: repoDir, ...GIT8 });
44280
44313
  const tags = stdout.split("\n").map((s2) => s2.trim()).filter(Boolean);
@@ -44302,7 +44335,7 @@ async function logRange(repoDir, range) {
44302
44335
  }).filter((x) => x !== null);
44303
44336
  }
44304
44337
  async function collectCommits(repoDir, fromSha, toSha) {
44305
- if (!usable(repoDir)) return { ok: false, reason: NO_REPO };
44338
+ if (!usable2(repoDir)) return { ok: false, reason: NO_REPO };
44306
44339
  for (const rev of [fromSha, toSha])
44307
44340
  if (!await revExists(repoDir, rev)) return { ok: false, reason: `revisi "${rev}" tak dikenal di repo project` };
44308
44341
  try {
@@ -44317,7 +44350,7 @@ async function collectCommits(repoDir, fromSha, toSha) {
44317
44350
  async function collectVersions(repoDir, fromTag, toTag) {
44318
44351
  const { tags, reason } = await listTags(repoDir);
44319
44352
  if (reason) return { ok: false, reason };
44320
- if (!usable(repoDir)) return { ok: false, reason: NO_REPO };
44353
+ if (!usable2(repoDir)) return { ok: false, reason: NO_REPO };
44321
44354
  if (!tags.includes(toTag)) return { ok: false, reason: `tag "${toTag}" tak ada di repo project` };
44322
44355
  if (fromTag && !tags.includes(fromTag)) return { ok: false, reason: `tag "${fromTag}" tak ada di repo project` };
44323
44356
  const prev = fromTag ?? await previousTag(repoDir, toTag);
@@ -44535,7 +44568,7 @@ init_db();
44535
44568
  init_src();
44536
44569
  import { readFileSync as readFileSync13 } from "node:fs";
44537
44570
  import { homedir as homedir9 } from "node:os";
44538
- import { join as join23 } from "node:path";
44571
+ import { join as join24 } from "node:path";
44539
44572
  var home = () => process.env.HOME || homedir9();
44540
44573
  var readJson2 = (path2) => {
44541
44574
  try {
@@ -44552,7 +44585,7 @@ var serversOf = (node) => {
44552
44585
  var codexServers = () => {
44553
44586
  let text;
44554
44587
  try {
44555
- text = readFileSync13(join23(home(), ".codex", "config.toml"), "utf8");
44588
+ text = readFileSync13(join24(home(), ".codex", "config.toml"), "utf8");
44556
44589
  } catch {
44557
44590
  return [];
44558
44591
  }
@@ -44565,12 +44598,12 @@ var codexServers = () => {
44565
44598
  };
44566
44599
  function mcpServerNames(repoDir) {
44567
44600
  const names = [];
44568
- const claudeJson = readJson2(join23(home(), ".claude.json"));
44601
+ const claudeJson = readJson2(join24(home(), ".claude.json"));
44569
44602
  names.push(...serversOf(claudeJson));
44570
44603
  if (repoDir) {
44571
44604
  const projects = claudeJson?.projects;
44572
44605
  if (projects && typeof projects === "object") names.push(...serversOf(projects[repoDir]));
44573
- names.push(...serversOf(readJson2(join23(repoDir, ".mcp.json"))));
44606
+ names.push(...serversOf(readJson2(join24(repoDir, ".mcp.json"))));
44574
44607
  }
44575
44608
  names.push(...codexServers());
44576
44609
  return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
@@ -46054,9 +46087,9 @@ ${baru}`;
46054
46087
  // src/services/portal-chat/workspace.ts
46055
46088
  init_src();
46056
46089
  init_db();
46057
- import { mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
46090
+ import { mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync2, rmSync as rmSync7, writeFileSync as writeFileSync7 } from "node:fs";
46058
46091
  import { tmpdir as tmpdir5 } from "node:os";
46059
- import { join as join24 } from "node:path";
46092
+ import { join as join25 } from "node:path";
46060
46093
  var STAGE_LABEL = {
46061
46094
  brainstorming: "Dirumuskan",
46062
46095
  objective: "Dirumuskan",
@@ -46111,7 +46144,7 @@ ${c.body}
46111
46144
  ${baris.join("\n")}`;
46112
46145
  }
46113
46146
  async function buildChatWorkspace(projectId) {
46114
- const dir2 = mkdtempSync2(join24(tmpdir5(), "hanoman-portal-chat-"));
46147
+ const dir2 = mkdtempSync2(join25(tmpdir5(), "hanoman-portal-chat-"));
46115
46148
  try {
46116
46149
  const project = await prisma.project.findUnique({
46117
46150
  where: { id: projectId },
@@ -46132,7 +46165,7 @@ async function buildChatWorkspace(projectId) {
46132
46165
  });
46133
46166
  const files = [];
46134
46167
  const tulis = (rel, isi) => {
46135
- writeFileSync7(join24(dir2, rel), isi, { mode: 384 });
46168
+ writeFileSync7(join25(dir2, rel), isi, { mode: 384 });
46136
46169
  files.push(rel);
46137
46170
  };
46138
46171
  tulis("project.md", renderProjectDoc(project));
@@ -46141,16 +46174,16 @@ async function buildChatWorkspace(projectId) {
46141
46174
  tulis("catatan-rilis.md", renderChangelogDoc(changelogs));
46142
46175
  const prds = await listPrds(projectId);
46143
46176
  if (prds.length) {
46144
- mkdirSync10(join24(dir2, "dokumen"), { mode: 448 });
46177
+ mkdirSync11(join25(dir2, "dokumen"), { mode: 448 });
46145
46178
  for (const prd of prds) {
46146
46179
  const isi = await readPrd(projectId, prd.path);
46147
46180
  if (!isi) continue;
46148
- tulis(join24("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
46181
+ tulis(join25("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
46149
46182
  }
46150
46183
  }
46151
- return { dir: dir2, files, cleanup: () => rmSync6(dir2, { recursive: true, force: true }) };
46184
+ return { dir: dir2, files, cleanup: () => rmSync7(dir2, { recursive: true, force: true }) };
46152
46185
  } catch (error) {
46153
- rmSync6(dir2, { recursive: true, force: true });
46186
+ rmSync7(dir2, { recursive: true, force: true });
46154
46187
  throw error;
46155
46188
  }
46156
46189
  }
@@ -46657,13 +46690,13 @@ init_db();
46657
46690
  // src/services/bootstrap.ts
46658
46691
  import { createHash as createHash9, randomBytes as randomBytes10, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
46659
46692
  import { chmod, lstat as lstat3, mkdir as mkdir6, readFile as readFile5, unlink as unlink5, writeFile as writeFile5 } from "node:fs/promises";
46660
- import { join as join25 } from "node:path";
46693
+ import { join as join26 } from "node:path";
46661
46694
  var TTL_MS5 = 15 * 6e4;
46662
46695
  var SETUP_TOKEN_FILE = "setup.token";
46663
46696
  var BootstrapError = class extends Error {
46664
46697
  code = "BOOTSTRAP_PROOF";
46665
46698
  };
46666
- var tokenPath = (home3) => join25(home3, SETUP_TOKEN_FILE);
46699
+ var tokenPath = (home3) => join26(home3, SETUP_TOKEN_FILE);
46667
46700
  async function readStored(home3) {
46668
46701
  try {
46669
46702
  const info = await lstat3(tokenPath(home3));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hanoman",
3
- "version": "0.1.54",
3
+ "version": "0.1.55",
4
4
  "description": "Orchestrator + dashboard workflow docs-driven untuk sesi Claude Code / Codex",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,4 +1,4 @@
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;--page-gutter-x: 28px;--page-gutter-y: 24px;--touch-target: 44px;--safe-top: env(safe-area-inset-top, 0px);--safe-right: env(safe-area-inset-right, 0px);--safe-bottom: env(safe-area-inset-bottom, 0px);--safe-left: env(safe-area-inset-left, 0px)}@media (min-width: 768px) and (max-width: 1199px){:root{--sidebar-w: 72px;--page-gutter-x: 20px;--page-gutter-y: 20px}}@media (max-width: 767px){:root{--sidebar-w: 0px;--page-gutter-x: 14px;--page-gutter-y: 14px}}: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;--dur-pet-active: 2.24s;--dur-pet-attention: 5.04s;--dur-pet-calm: 5.6s;--dur-pet-heavy: 6.72s;--dur-pet-flourish: .84s;--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;height:100dvh;overflow:hidden;max-width:100%}.hn-dynamic-viewport{height:100vh;height:100dvh;min-height:100vh;min-height:100dvh}button:focus-visible,a[href]:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[role=button]:focus-visible,[role=tab]:focus-visible,[role=checkbox]:focus-visible,[role=radio]:focus-visible,[role=switch]:focus-visible{outline:2px solid var(--border-focus)!important;outline-offset:2px}.hn-select select:focus,.hn-select select:focus-visible{outline:none!important}.hn-shell{display:flex;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;background:var(--surface-page);color:var(--text-body)}.hn-shell-sidebar{width:var(--sidebar-w);flex:0 0 auto;display:flex;min-height:0;flex-direction:column;box-sizing:border-box;border-right:1px solid var(--border-hair);background:var(--bone-100);padding:18px 14px}.hn-shell-wordmark{padding:2px 4px 20px}.hn-shell-workspace{padding:0 10px 8px}.hn-shell-nav{display:flex;min-height:0;flex-direction:column;gap:2px;overflow-y:auto}.hn-shell-column{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column}.hn-shell-topbar{min-height:var(--topbar-h);flex:0 0 auto;display:flex;align-items:center;gap:16px;padding:0 22px;border-bottom:1px solid var(--border-hair);background:color-mix(in srgb,var(--bone-100) 80%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);position:relative;z-index:90}.hn-shell-menu{display:none;width:var(--touch-target);height:var(--touch-target);flex:0 0 auto;align-items:center;justify-content:center;border:1px solid var(--border-hair);border-radius:var(--radius-sm);background:var(--surface-card);color:var(--text-body)}.hn-shell-heading{min-width:0}.hn-shell-tools{margin-left:auto;display:flex;min-width:0;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:10px}.hn-shell-search{width:220px}.hn-shell-main{flex:1;min-width:0;min-height:0;overflow-x:hidden;overflow-y:auto}.hn-shell-content{width:100%;min-width:0;min-height:100%;margin:0 auto;padding:var(--page-gutter-y) var(--page-gutter-x) calc(32px + var(--safe-bottom));box-sizing:border-box;display:flex;flex-direction:column}.hn-shell-backdrop{display:none}.hn-viewport-popover{box-sizing:border-box;max-width:calc(100vw - var(--safe-left) - var(--safe-right) - 28px)}.hn-modal-overlay{box-sizing:border-box;padding:calc(24px + var(--safe-top)) calc(24px + var(--safe-right)) calc(24px + var(--safe-bottom)) calc(24px + var(--safe-left))}.hn-modal-panel{min-width:0}.hn-modal-footer{flex-wrap:wrap;padding-bottom:calc(14px + var(--safe-bottom))}.hn-board-local-overflow{max-width:100%;overscroll-behavior-inline:contain}.hn-row-actions{flex-wrap:wrap}.hn-topbar-label-short{display:none}.hn-grid-mobile,.hn-stack-mobile,.hn-wrap-mobile,.hn-dense-row,.hn-vps-row{min-width:0}@media (min-width: 768px) and (max-width: 1199px){.hn-shell-sidebar{width:var(--sidebar-w);padding:14px 8px}.hn-shell-sidebar .hn-nav-label,.hn-shell-sidebar .hn-shell-workspace,.hn-shell-sidebar .hn-shell-wordmark>div>div>span:last-child{display:none}.hn-shell-sidebar nav button{justify-content:center;padding-inline:0}.hn-shell-wordmark{display:flex;justify-content:center;padding-inline:0}}@media (max-width: 767px){.hn-shell-menu{display:inline-flex}.hn-shell-sidebar{position:fixed;z-index:120;inset:0 auto 0 0;width:min(320px,calc(100vw - 48px));padding:calc(14px + var(--safe-top)) 14px calc(14px + var(--safe-bottom)) calc(14px + var(--safe-left));box-shadow:var(--shadow-xl);transform:translate(-105%);transition:transform var(--transition-fast)}.hn-shell[data-nav-open=true] .hn-shell-sidebar{transform:translate(0)}.hn-shell-backdrop{display:block;position:fixed;z-index:119;top:0;right:0;bottom:0;left:0;width:100%;height:100%;border:0;background:color-mix(in srgb,var(--ink-900) 42%,transparent)}.hn-shell-topbar{align-items:flex-start;flex-wrap:wrap;gap:8px 10px;padding:calc(6px + var(--safe-top)) var(--page-gutter-x) 8px;-webkit-backdrop-filter:none;backdrop-filter:none}.hn-shell-heading{flex:1 1 calc(100% - 54px);align-self:center}.hn-shell-tools{flex:1 1 100%;margin-left:0;justify-content:flex-start;gap:6px}.hn-shell-search{flex:1 1 100%;width:100%}.hn-topbar-label{display:none}.hn-topbar-label-short{display:inline}.hn-shell button{min-width:var(--touch-target);min-height:var(--touch-target)}.hn-viewport-popover{position:fixed!important;inset:auto max(var(--page-gutter-x),var(--safe-right)) calc(var(--page-gutter-y) + var(--safe-bottom)) max(var(--page-gutter-x),var(--safe-left))!important;width:auto!important;max-width:none;max-height:calc(100dvh - var(--safe-top) - var(--safe-bottom) - var(--page-gutter-y) - var(--page-gutter-y))!important;overflow:auto}.hn-modal-overlay{align-items:flex-end!important;padding:var(--safe-top) var(--safe-right) var(--safe-bottom) var(--safe-left)}.hn-modal-panel{width:100%!important;max-width:100%!important;max-height:calc(100dvh - var(--safe-top) - var(--safe-bottom))!important;border-radius:var(--radius-lg) var(--radius-lg) 0 0!important}.hn-modal-header,.hn-modal-body,.hn-modal-footer{padding-inline:var(--page-gutter-x)!important}.hn-modal-footer>*{min-height:var(--touch-target)}.hn-stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))!important}.hn-project-header{display:none!important}.hn-project-row{grid-template-columns:minmax(0,1fr)!important;align-items:stretch!important;gap:10px!important;margin-bottom:10px;padding:14px!important;border:1px solid var(--border-hair);border-left-width:3px;border-radius:var(--radius-md)}.hn-project-row>[data-label]{min-width:0}.hn-project-row>[data-label]:before{content:attr(data-label);display:block;margin-bottom:4px;color:var(--text-subtle);font:var(--weight-semibold) var(--text-2xs)/1.2 var(--font-mono);letter-spacing:.08em;text-transform:uppercase}.hn-project-row>[data-label=Aktivitas]{white-space:normal!important}.hn-backlog-topline,.hn-backlog-view-actions,.hn-backlog-filters{align-items:stretch!important}.hn-backlog-topline>*,.hn-backlog-view-actions>*,.hn-backlog-filters>*{max-width:100%}.hn-backlog-view-actions{flex-wrap:wrap}.hn-backlog-filters .hn-touch-target{flex:1 1 145px!important;width:auto!important}.hn-backlog-grid{grid-template-columns:minmax(0,1fr)!important}.hn-backlog-row{align-items:flex-start!important;flex-wrap:wrap}.hn-backlog-row>div{flex-basis:100%!important}.hn-card-footer{align-items:flex-start!important;flex-direction:column}.hn-grid-mobile{grid-template-columns:minmax(0,1fr)!important}.hn-stack-mobile{align-items:stretch!important;flex-direction:column}.hn-wrap-mobile,.hn-dense-row,.hn-vps-row{align-items:flex-start!important;flex-wrap:wrap}.hn-vps-row>div{flex-basis:calc(100% - 32px)}.hn-vps-row>button{flex:1 1 auto}.hn-dense-row>[style*="flex: 1"]{min-width:min(220px,100%)}.hn-picker-row{flex-wrap:wrap}.hn-picker-row>.hn-picker-title{order:2;flex:1 1 100%!important;min-width:100%!important;white-space:normal!important;overflow:visible!important}.hn-card-header{align-items:stretch!important;flex-direction:column}.hn-card-actions{max-width:100%}.hn-card-actions>*{max-width:100%}.hn-settings-layout{width:100%}.hn-portal-header{height:auto!important;min-height:var(--topbar-h);flex-wrap:wrap;padding:max(8px,var(--safe-top)) max(var(--page-gutter-x),var(--safe-right)) 8px max(var(--page-gutter-x),var(--safe-left))!important}.hn-portal-header>div{flex-basis:1px}.hn-portal-content{box-sizing:border-box;padding:16px max(var(--page-gutter-x),var(--safe-right)) calc(24px + var(--safe-bottom)) max(var(--page-gutter-x),var(--safe-left))!important}.hn-portal-toolbar{align-items:stretch!important;flex-direction:column}.hn-portal-row{align-items:flex-start!important;flex-wrap:wrap;min-height:var(--touch-target)}.hn-portal-row>span:nth-child(2){flex-basis:calc(100% - 110px)}}.hn-responsive-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:10px;min-width:0;max-width:100%}@media (max-width: 767px){.hn-setting-row{align-items:stretch!important;flex-direction:column}.hn-setting-control{width:100%}.hn-setting-control>.hn-touch-target,.hn-setting-control>div{width:100%!important;max-width:100%}.hn-setting-control>div{flex-wrap:wrap}.hn-setting-control>div>div.hn-touch-target{flex:1 1 200px;width:auto!important;max-width:100%}}.hn-local-overflow{max-width:100%;min-width:0;overflow:auto;overscroll-behavior-inline:contain;-webkit-overflow-scrolling:touch}.hn-tabs{max-width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-inline:contain}@media (pointer: coarse),(max-width: 767px){button:not(.hn-shell-backdrop){min-width:var(--touch-target);min-height:var(--touch-target)}input,select,textarea{min-width:var(--touch-target);min-height:var(--touch-target)}.hn-touch-target,.hn-project-open,.hn-terminal-unplaced-action,[role=button][tabindex="0"]{min-width:var(--touch-target)!important;min-height:var(--touch-target)!important}.hn-tabs [role=tab]{min-height:var(--touch-target)}.hn-choice-target{min-width:var(--touch-target);min-height:var(--touch-target)}.hn-choice-control{min-width:var(--touch-target);min-height:var(--touch-target);outline-offset:8px}}.hn-responsive-panels{display:flex;flex:1 1 auto;min-width:0;min-height:0;flex-direction:column;gap:12px}.hn-panel-switcher{flex:0 0 auto;max-width:100%;overflow-x:auto;overscroll-behavior-inline:contain}.hn-panels-grid{display:grid;grid-template-columns:minmax(0,1fr);flex:1 1 auto;min-width:0;min-height:0;gap:16px}.hn-responsive-panel{min-width:0;min-height:0}.hn-panel-flex{display:flex;min-width:0;min-height:0;flex-direction:column}.hn-panel-flex>*{min-width:0}.hn-graph-action{display:inline-grid;min-width:28px;min-height:28px;place-items:center;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--text-muted);cursor:pointer}.hn-graph-action:hover{background:var(--bone-200)}.hn-terminal-actions{display:inline-flex;flex:0 0 auto;align-items:center;gap:2px}.hn-terminal-action{display:inline-grid;min-width:28px;min-height:28px;padding:0;place-items:center;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--text-subtle);font:inherit;cursor:pointer}.hn-terminal-action--text{padding-inline:5px}.hn-terminal-action:hover{background:var(--bone-300);color:var(--text-strong)}.hn-terminal-keys{display:flex;flex:0 0 auto;gap:4px;max-width:100%;padding:4px 2px;overflow-x:auto;overscroll-behavior-inline:contain;border-top:1px solid var(--border-hair)}.hn-terminal-key{flex:0 0 auto;min-width:40px;min-height:32px;padding:0 8px;border:1px solid var(--border-hair);border-radius:var(--radius-sm);background:var(--surface-card);color:var(--text-body);font:var(--weight-medium) var(--text-sm)/1 var(--font-mono);cursor:pointer}.hn-terminal-key:hover{background:var(--bone-200);color:var(--text-strong)}@media (max-width: 767px){.hn-terminal-cell-header{flex-wrap:wrap}}@media (pointer: coarse),(max-width: 767px){.hn-graph-action,.hn-terminal-action,.hn-terminal-key,.hn-terminal-group-control{min-width:var(--touch-target);min-height:var(--touch-target)}}.hn-responsive-panel[aria-hidden=true]{display:none}@media (min-width: 768px){.hn-responsive-panels--tablet .hn-panel-switcher{display:none}.hn-responsive-panels--tablet .hn-panels-grid{grid-template-columns:minmax(0,var(--hn-master-width, 280px)) minmax(0,1fr)}.hn-responsive-panels--tablet .hn-responsive-panel[aria-hidden=true]{display:block}}@media (min-width: 1200px){.hn-responsive-panels--desktop .hn-panel-switcher{display:none}.hn-responsive-panels--desktop .hn-panels-grid{grid-template-columns:minmax(0,var(--hn-master-width, 300px)) minmax(0,1fr)}.hn-responsive-panels--desktop .hn-responsive-panel[aria-hidden=true]{display:block}}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation:none!important;scroll-behavior:auto!important;transition:none!important}}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%)}}.hn-pet-reactor{transform-origin:50% 86%}.hn-pet-stage:not([data-reduced-motion=true]):hover .hn-pet-reactor{transform:translateY(-2px) scale(1.035) rotate(-1deg)}@keyframes hn-pet-idle-ready{0%,to{transform:translateY(0) scale(1)}50%{transform:translateY(-2px) scale(1.018)}}@keyframes hn-pet-idle-working{0%,to{transform:translateY(0) rotate(0) scale(1)}20%{transform:translateY(-2px) rotate(-.8deg) scale(1.018)}40%{transform:translateY(0) rotate(.6deg) scale(1)}60%{transform:translateY(-3px) rotate(-.5deg) scale(1.022)}80%{transform:translateY(-1px) rotate(.4deg) scale(1.008)}}@keyframes hn-pet-idle-waiting{0%,64%,to{transform:translateY(0) rotate(0)}70%{transform:translateY(-1px) rotate(-2deg)}76%{transform:translateY(-2px) rotate(3deg)}82%{transform:translateY(-1px) rotate(-2deg)}88%{transform:translateY(0) rotate(0)}}@keyframes hn-pet-idle-blocked{0%,to{transform:translateY(1px) scaleY(.99)}50%{transform:translateY(3px) scale(.99,.97)}}@keyframes hn-pet-idle-review{0%,64%,to{transform:translateY(0) rotate(0) scale(1)}74%{transform:translateY(-1px) rotate(-1.6deg) scale(1.012)}84%{transform:translateY(-1px) rotate(.8deg) scale(1.006)}}@keyframes hn-pet-idle-shipped{0%,to{transform:translateY(0) scale(1)}50%{transform:translateY(-2px) scale(1.015)}}@keyframes hn-pet-idle-docs{0%,66%,to{transform:translateY(0) rotate(0) scale(1)}72%{transform:translateY(-1px) rotate(-1.8deg) scale(1.01)}78%{transform:translateY(-1px) rotate(1.5deg) scale(1.012)}84%{transform:translateY(0) rotate(-.7deg) scale(1)}}@keyframes hn-pet-celebrate{0%{transform:translateY(3px) scale(.92) rotate(0)}38%{transform:translateY(-5px) scale(1.08) rotate(-3deg)}62%{transform:translateY(-2px) scale(1.03) rotate(3deg)}to{transform:translateY(0) scale(1) rotate(0)}}@keyframes hn-pet-pose-in{0%{opacity:0;transform:translateY(4px) scale(.76,.92)}62%{opacity:1;transform:translateY(-2px) scale(1.07,.97)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes hn-pet-pose-out{0%{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:translateY(3px) scale(.86,1.04)}}@keyframes hn-pet-click{0%,to{transform:translateY(0) scale(1) rotate(0)}38%{transform:translateY(2px) scale(.9,1.06) rotate(-2deg)}72%{transform:translateY(-3px) scale(1.06,.96) rotate(1.5deg)}}@keyframes hn-pet-panel-in{0%{opacity:0;transform:translateY(6px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes hn-pet-panel-out{0%{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:translateY(5px) scale(.985)}}@keyframes hn-pet-reveal{0%{opacity:0;transform:translateY(7px) scale(.86)}70%{opacity:1;transform:translateY(-1px) scale(1.035)}to{opacity:1;transform:translateY(0) scale(1)}}/**
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;--page-gutter-x: 28px;--page-gutter-y: 24px;--touch-target: 44px;--safe-top: env(safe-area-inset-top, 0px);--safe-right: env(safe-area-inset-right, 0px);--safe-bottom: env(safe-area-inset-bottom, 0px);--safe-left: env(safe-area-inset-left, 0px)}@media (min-width: 768px) and (max-width: 1199px){:root{--sidebar-w: 72px;--page-gutter-x: 20px;--page-gutter-y: 20px}}@media (max-width: 767px){:root{--sidebar-w: 0px;--page-gutter-x: 14px;--page-gutter-y: 14px}}: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;--dur-pet-active: 2.24s;--dur-pet-attention: 5.04s;--dur-pet-calm: 5.6s;--dur-pet-heavy: 6.72s;--dur-pet-flourish: .84s;--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;height:100dvh;overflow:hidden;max-width:100%}.hn-dynamic-viewport{height:100vh;height:100dvh;min-height:100vh;min-height:100dvh}button:focus-visible,a[href]:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[role=button]:focus-visible,[role=tab]:focus-visible,[role=checkbox]:focus-visible,[role=radio]:focus-visible,[role=switch]:focus-visible{outline:2px solid var(--border-focus)!important;outline-offset:2px}.hn-select select:focus,.hn-select select:focus-visible{outline:none!important}.hn-shell{display:flex;width:100%;height:100%;min-width:0;min-height:0;overflow:hidden;background:var(--surface-page);color:var(--text-body)}.hn-shell-sidebar{width:var(--sidebar-w);flex:0 0 auto;display:flex;min-height:0;flex-direction:column;box-sizing:border-box;border-right:1px solid var(--border-hair);background:var(--bone-100);padding:18px 14px}.hn-shell-wordmark{padding:2px 4px 20px}.hn-shell-workspace{padding:0 10px 8px}.hn-shell-nav{display:flex;min-height:0;flex-direction:column;gap:2px;overflow-y:auto}.hn-shell-column{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column}.hn-shell-topbar{min-height:var(--topbar-h);flex:0 0 auto;display:flex;align-items:center;gap:16px;padding:0 22px;border-bottom:1px solid var(--border-hair);background:color-mix(in srgb,var(--bone-100) 80%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);position:relative;z-index:90}.hn-shell-menu{display:none;width:var(--touch-target);height:var(--touch-target);flex:0 0 auto;align-items:center;justify-content:center;border:1px solid var(--border-hair);border-radius:var(--radius-sm);background:var(--surface-card);color:var(--text-body)}.hn-shell-heading{min-width:0}.hn-shell-tools{margin-left:auto;display:flex;min-width:0;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:10px}.hn-shell-search{width:220px}.hn-shell-main{flex:1;min-width:0;min-height:0;overflow-x:hidden;overflow-y:auto}.hn-shell-content{width:100%;min-width:0;min-height:100%;margin:0 auto;padding:var(--page-gutter-y) var(--page-gutter-x) calc(32px + var(--safe-bottom));box-sizing:border-box;display:flex;flex-direction:column}.hn-shell-backdrop{display:none}.hn-viewport-popover{box-sizing:border-box;max-width:calc(100vw - var(--safe-left) - var(--safe-right) - 28px)}.hn-modal-overlay{box-sizing:border-box;padding:calc(24px + var(--safe-top)) calc(24px + var(--safe-right)) calc(24px + var(--safe-bottom)) calc(24px + var(--safe-left))}.hn-modal-panel{min-width:0}.hn-modal-footer{flex-wrap:wrap;padding-bottom:calc(14px + var(--safe-bottom))}.hn-board-local-overflow{max-width:100%;overscroll-behavior-inline:contain}.hn-row-actions{flex-wrap:wrap}.hn-topbar-label-short{display:none}.hn-grid-mobile,.hn-stack-mobile,.hn-wrap-mobile,.hn-dense-row,.hn-vps-row{min-width:0}@media (min-width: 768px) and (max-width: 1199px){.hn-shell-sidebar{width:var(--sidebar-w);padding:14px 8px}.hn-shell-sidebar .hn-nav-label,.hn-shell-sidebar .hn-shell-workspace,.hn-shell-sidebar .hn-shell-wordmark>div>div>span:last-child{display:none}.hn-shell-sidebar nav button{justify-content:center;padding-inline:0}.hn-shell-wordmark{display:flex;justify-content:center;padding-inline:0}}@media (max-width: 767px){.hn-shell-menu{display:inline-flex}.hn-shell-sidebar{position:fixed;z-index:120;inset:0 auto 0 0;width:min(320px,calc(100vw - 48px));padding:calc(14px + var(--safe-top)) 14px calc(14px + var(--safe-bottom)) calc(14px + var(--safe-left));box-shadow:var(--shadow-xl);transform:translate(-105%);transition:transform var(--transition-fast)}.hn-shell[data-nav-open=true] .hn-shell-sidebar{transform:translate(0)}.hn-shell-backdrop{display:block;position:fixed;z-index:119;top:0;right:0;bottom:0;left:0;width:100%;height:100%;border:0;background:color-mix(in srgb,var(--ink-900) 42%,transparent)}.hn-shell-topbar{align-items:flex-start;flex-wrap:wrap;gap:8px 10px;padding:calc(6px + var(--safe-top)) var(--page-gutter-x) 8px;-webkit-backdrop-filter:none;backdrop-filter:none}.hn-shell-heading{flex:1 1 calc(100% - 54px);align-self:center}.hn-shell-tools{flex:1 1 100%;margin-left:0;justify-content:flex-start;gap:6px}.hn-shell-search{flex:1 1 100%;width:100%}.hn-topbar-label{display:none}.hn-topbar-label-short{display:inline}.hn-shell button{min-width:var(--touch-target);min-height:var(--touch-target)}.hn-viewport-popover{position:fixed!important;inset:auto max(var(--page-gutter-x),var(--safe-right)) calc(var(--page-gutter-y) + var(--safe-bottom)) max(var(--page-gutter-x),var(--safe-left))!important;width:auto!important;max-width:none;max-height:calc(100dvh - var(--safe-top) - var(--safe-bottom) - var(--page-gutter-y) - var(--page-gutter-y))!important;overflow:auto}.hn-modal-overlay{align-items:flex-end!important;padding:var(--safe-top) var(--safe-right) var(--safe-bottom) var(--safe-left)}.hn-modal-panel{width:100%!important;max-width:100%!important;max-height:calc(100dvh - var(--safe-top) - var(--safe-bottom))!important;border-radius:var(--radius-lg) var(--radius-lg) 0 0!important}.hn-modal-header,.hn-modal-body,.hn-modal-footer{padding-inline:var(--page-gutter-x)!important}.hn-modal-footer>*{min-height:var(--touch-target)}.hn-stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))!important}.hn-project-header{display:none!important}.hn-project-row{grid-template-columns:minmax(0,1fr)!important;align-items:stretch!important;gap:10px!important;margin-bottom:10px;padding:14px!important;border:1px solid var(--border-hair);border-left-width:3px;border-radius:var(--radius-md)}.hn-project-row>[data-label]{min-width:0}.hn-project-row>[data-label]:before{content:attr(data-label);display:block;margin-bottom:4px;color:var(--text-subtle);font:var(--weight-semibold) var(--text-2xs)/1.2 var(--font-mono);letter-spacing:.08em;text-transform:uppercase}.hn-project-row>[data-label=Aktivitas]{white-space:normal!important}.hn-backlog-topline,.hn-backlog-view-actions,.hn-backlog-filters{align-items:stretch!important}.hn-backlog-topline>*,.hn-backlog-view-actions>*,.hn-backlog-filters>*{max-width:100%}.hn-backlog-view-actions{flex-wrap:wrap}.hn-backlog-filters .hn-touch-target{flex:1 1 145px!important;width:auto!important}.hn-backlog-grid{grid-template-columns:minmax(0,1fr)!important}.hn-backlog-row{align-items:flex-start!important;flex-wrap:wrap}.hn-backlog-row>div{flex-basis:100%!important}.hn-card-footer{align-items:flex-start!important;flex-direction:column}.hn-grid-mobile{grid-template-columns:minmax(0,1fr)!important}.hn-stack-mobile{align-items:stretch!important;flex-direction:column}.hn-wrap-mobile,.hn-dense-row,.hn-vps-row{align-items:flex-start!important;flex-wrap:wrap}.hn-vps-row>div{flex-basis:calc(100% - 32px)}.hn-vps-row>button{flex:1 1 auto}.hn-dense-row>[style*="flex: 1"]{min-width:min(220px,100%)}.hn-picker-row{flex-wrap:wrap}.hn-picker-row>.hn-picker-title{order:2;flex:1 1 100%!important;min-width:100%!important;white-space:normal!important;overflow:visible!important}.hn-card-header{align-items:stretch!important;flex-direction:column}.hn-card-actions{max-width:100%}.hn-card-actions>*{max-width:100%}.hn-settings-layout{width:100%}.hn-portal-header{height:auto!important;min-height:var(--topbar-h);flex-wrap:wrap;padding:max(8px,var(--safe-top)) max(var(--page-gutter-x),var(--safe-right)) 8px max(var(--page-gutter-x),var(--safe-left))!important}.hn-portal-header>div{flex-basis:1px}.hn-portal-content{box-sizing:border-box;padding:16px max(var(--page-gutter-x),var(--safe-right)) calc(24px + var(--safe-bottom)) max(var(--page-gutter-x),var(--safe-left))!important}.hn-portal-toolbar{align-items:stretch!important;flex-direction:column}.hn-portal-row{align-items:flex-start!important;flex-wrap:wrap;min-height:var(--touch-target)}.hn-portal-row>span:nth-child(2){flex-basis:calc(100% - 110px)}}.hn-responsive-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:10px;min-width:0;max-width:100%}@media (max-width: 767px){.hn-setting-row{align-items:stretch!important;flex-direction:column}.hn-setting-control{width:100%}.hn-setting-control>.hn-touch-target,.hn-setting-control>div{width:100%!important;max-width:100%}.hn-setting-control>div{flex-wrap:wrap}.hn-setting-control>div>div.hn-touch-target{flex:1 1 200px;width:auto!important;max-width:100%}}.hn-local-overflow{max-width:100%;min-width:0;overflow:auto;overscroll-behavior-inline:contain;-webkit-overflow-scrolling:touch}.hn-tabs{max-width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-inline:contain}.hn-ide-head{display:flex;align-items:center;flex-wrap:wrap;gap:12px;min-width:0}.hn-ide-head>.hn-tabs{flex:0 0 auto}.hn-ide-toolbar{display:flex;align-items:center;flex:1 1 480px;flex-wrap:nowrap;gap:10px;min-width:0;max-width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-inline:contain}.hn-ide-toolbar>*{flex:0 0 auto}.hn-ide-toolbar>:first-child{margin-inline-start:auto}@media (pointer: coarse),(max-width: 767px){button:not(.hn-shell-backdrop){min-width:var(--touch-target);min-height:var(--touch-target)}input,select,textarea{min-width:var(--touch-target);min-height:var(--touch-target)}.hn-touch-target,.hn-project-open,.hn-terminal-unplaced-action,[role=button][tabindex="0"]{min-width:var(--touch-target)!important;min-height:var(--touch-target)!important}.hn-tabs [role=tab]{min-height:var(--touch-target)}.hn-choice-target{min-width:var(--touch-target);min-height:var(--touch-target)}.hn-choice-control{min-width:var(--touch-target);min-height:var(--touch-target);outline-offset:8px}}.hn-responsive-panels{display:flex;flex:1 1 auto;min-width:0;min-height:0;flex-direction:column;gap:12px}.hn-panel-switcher{flex:0 0 auto;max-width:100%;overflow-x:auto;overscroll-behavior-inline:contain}.hn-panels-grid{display:grid;grid-template-columns:minmax(0,1fr);flex:1 1 auto;min-width:0;min-height:0;gap:16px}.hn-responsive-panel{min-width:0;min-height:0}.hn-panel-flex{display:flex;min-width:0;min-height:0;flex-direction:column}.hn-panel-flex>*{min-width:0}.hn-graph-action{display:inline-grid;min-width:28px;min-height:28px;place-items:center;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--text-muted);cursor:pointer}.hn-graph-action:hover{background:var(--bone-200)}.hn-terminal-actions{display:inline-flex;flex:0 0 auto;align-items:center;gap:2px}.hn-terminal-action{display:inline-grid;min-width:28px;min-height:28px;padding:0;place-items:center;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--text-subtle);font:inherit;cursor:pointer}.hn-terminal-action--text{padding-inline:5px}.hn-terminal-action:hover{background:var(--bone-300);color:var(--text-strong)}.hn-terminal-keys{display:flex;flex:0 0 auto;gap:4px;max-width:100%;padding:4px 2px;overflow-x:auto;overscroll-behavior-inline:contain;border-top:1px solid var(--border-hair)}.hn-terminal-key{flex:0 0 auto;min-width:40px;min-height:32px;padding:0 8px;border:1px solid var(--border-hair);border-radius:var(--radius-sm);background:var(--surface-card);color:var(--text-body);font:var(--weight-medium) var(--text-sm)/1 var(--font-mono);cursor:pointer}.hn-terminal-key:hover{background:var(--bone-200);color:var(--text-strong)}@media (max-width: 767px){.hn-terminal-cell-header{flex-wrap:wrap}}@media (pointer: coarse),(max-width: 767px){.hn-graph-action,.hn-terminal-action,.hn-terminal-key,.hn-terminal-group-control{min-width:var(--touch-target);min-height:var(--touch-target)}}.hn-responsive-panel[aria-hidden=true]{display:none}@media (min-width: 768px){.hn-responsive-panels--tablet .hn-panel-switcher{display:none}.hn-responsive-panels--tablet .hn-panels-grid{grid-template-columns:minmax(0,var(--hn-master-width, 280px)) minmax(0,1fr)}.hn-responsive-panels--tablet .hn-responsive-panel[aria-hidden=true]{display:block}}@media (min-width: 1200px){.hn-responsive-panels--desktop .hn-panel-switcher{display:none}.hn-responsive-panels--desktop .hn-panels-grid{grid-template-columns:minmax(0,var(--hn-master-width, 300px)) minmax(0,1fr)}.hn-responsive-panels--desktop .hn-responsive-panel[aria-hidden=true]{display:block}}@media (prefers-reduced-motion: reduce){*,*:before,*:after{animation:none!important;scroll-behavior:auto!important;transition:none!important}}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%)}}.hn-pet-reactor{transform-origin:50% 86%}.hn-pet-stage:not([data-reduced-motion=true]):hover .hn-pet-reactor{transform:translateY(-2px) scale(1.035) rotate(-1deg)}@keyframes hn-pet-idle-ready{0%,to{transform:translateY(0) scale(1)}50%{transform:translateY(-2px) scale(1.018)}}@keyframes hn-pet-idle-working{0%,to{transform:translateY(0) rotate(0) scale(1)}20%{transform:translateY(-2px) rotate(-.8deg) scale(1.018)}40%{transform:translateY(0) rotate(.6deg) scale(1)}60%{transform:translateY(-3px) rotate(-.5deg) scale(1.022)}80%{transform:translateY(-1px) rotate(.4deg) scale(1.008)}}@keyframes hn-pet-idle-waiting{0%,64%,to{transform:translateY(0) rotate(0)}70%{transform:translateY(-1px) rotate(-2deg)}76%{transform:translateY(-2px) rotate(3deg)}82%{transform:translateY(-1px) rotate(-2deg)}88%{transform:translateY(0) rotate(0)}}@keyframes hn-pet-idle-blocked{0%,to{transform:translateY(1px) scaleY(.99)}50%{transform:translateY(3px) scale(.99,.97)}}@keyframes hn-pet-idle-review{0%,64%,to{transform:translateY(0) rotate(0) scale(1)}74%{transform:translateY(-1px) rotate(-1.6deg) scale(1.012)}84%{transform:translateY(-1px) rotate(.8deg) scale(1.006)}}@keyframes hn-pet-idle-shipped{0%,to{transform:translateY(0) scale(1)}50%{transform:translateY(-2px) scale(1.015)}}@keyframes hn-pet-idle-docs{0%,66%,to{transform:translateY(0) rotate(0) scale(1)}72%{transform:translateY(-1px) rotate(-1.8deg) scale(1.01)}78%{transform:translateY(-1px) rotate(1.5deg) scale(1.012)}84%{transform:translateY(0) rotate(-.7deg) scale(1)}}@keyframes hn-pet-celebrate{0%{transform:translateY(3px) scale(.92) rotate(0)}38%{transform:translateY(-5px) scale(1.08) rotate(-3deg)}62%{transform:translateY(-2px) scale(1.03) rotate(3deg)}to{transform:translateY(0) scale(1) rotate(0)}}@keyframes hn-pet-pose-in{0%{opacity:0;transform:translateY(4px) scale(.76,.92)}62%{opacity:1;transform:translateY(-2px) scale(1.07,.97)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes hn-pet-pose-out{0%{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:translateY(3px) scale(.86,1.04)}}@keyframes hn-pet-click{0%,to{transform:translateY(0) scale(1) rotate(0)}38%{transform:translateY(2px) scale(.9,1.06) rotate(-2deg)}72%{transform:translateY(-3px) scale(1.06,.96) rotate(1.5deg)}}@keyframes hn-pet-panel-in{0%{opacity:0;transform:translateY(6px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes hn-pet-panel-out{0%{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:translateY(5px) scale(.985)}}@keyframes hn-pet-reveal{0%{opacity:0;transform:translateY(7px) scale(.86)}70%{opacity:1;transform:translateY(-1px) scale(1.035)}to{opacity:1;transform:translateY(0) scale(1)}}/**
2
2
  * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3
3
  * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4
4
  * https://github.com/chjj/term.js