hanoman 0.1.53 → 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.53",
3
- "sha": "b9082276",
4
- "builtAt": "2026-08-20T22:42:53.323Z"
2
+ "version": "0.1.55",
3
+ "sha": "912f83b1",
4
+ "builtAt": "2026-08-21T09:35:37.428Z"
5
5
  }
package/dist/cli.js CHANGED
@@ -7284,6 +7284,28 @@ var init_terminal_workspace = __esm({
7284
7284
  }
7285
7285
  });
7286
7286
 
7287
+ // ../shared/src/terminal-io.ts
7288
+ var TERMINAL_RESPONSE;
7289
+ var init_terminal_io = __esm({
7290
+ "../shared/src/terminal-io.ts"() {
7291
+ "use strict";
7292
+ TERMINAL_RESPONSE = new RegExp([
7293
+ "\\x1b\\[[?>][0-9;]*c",
7294
+ // balasan DA
7295
+ "\\x1b\\[\\??[0-9]+;[0-9]+R",
7296
+ // CPR / DECXCPR
7297
+ "\\x1b\\[\\??[0-9;]*n",
7298
+ // balasan DSR
7299
+ "\\x1b\\[\\??[0-9;]*\\$y",
7300
+ // DECRPM
7301
+ "\\x1b\\][0-9][0-9;]*;[^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)",
7302
+ // balasan warna OSC
7303
+ "\\x1bP[0-9]*[$+>][a-z|][^\\x1b]*\\x1b\\\\"
7304
+ // DECRPSS / XTGETTCAP / XTVERSION
7305
+ ].join("|"), "g");
7306
+ }
7307
+ });
7308
+
7287
7309
  // ../shared/src/index.ts
7288
7310
  var init_src = __esm({
7289
7311
  "../shared/src/index.ts"() {
@@ -7318,6 +7340,7 @@ var init_src = __esm({
7318
7340
  init_changelog();
7319
7341
  init_cron_expr();
7320
7342
  init_terminal_workspace();
7343
+ init_terminal_io();
7321
7344
  }
7322
7345
  });
7323
7346
 
package/dist/server.js CHANGED
@@ -7889,6 +7889,29 @@ var init_terminal_workspace = __esm({
7889
7889
  }
7890
7890
  });
7891
7891
 
7892
+ // ../shared/src/terminal-io.ts
7893
+ var TERMINAL_RESPONSE, isTerminalResponse;
7894
+ var init_terminal_io = __esm({
7895
+ "../shared/src/terminal-io.ts"() {
7896
+ "use strict";
7897
+ TERMINAL_RESPONSE = new RegExp([
7898
+ "\\x1b\\[[?>][0-9;]*c",
7899
+ // balasan DA
7900
+ "\\x1b\\[\\??[0-9]+;[0-9]+R",
7901
+ // CPR / DECXCPR
7902
+ "\\x1b\\[\\??[0-9;]*n",
7903
+ // balasan DSR
7904
+ "\\x1b\\[\\??[0-9;]*\\$y",
7905
+ // DECRPM
7906
+ "\\x1b\\][0-9][0-9;]*;[^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)",
7907
+ // balasan warna OSC
7908
+ "\\x1bP[0-9]*[$+>][a-z|][^\\x1b]*\\x1b\\\\"
7909
+ // DECRPSS / XTGETTCAP / XTVERSION
7910
+ ].join("|"), "g");
7911
+ isTerminalResponse = (d) => d.length > 0 && d.replace(TERMINAL_RESPONSE, "") === "";
7912
+ }
7913
+ });
7914
+
7892
7915
  // ../shared/src/index.ts
7893
7916
  var init_src = __esm({
7894
7917
  "../shared/src/index.ts"() {
@@ -7923,6 +7946,7 @@ var init_src = __esm({
7923
7946
  init_changelog();
7924
7947
  init_cron_expr();
7925
7948
  init_terminal_workspace();
7949
+ init_terminal_io();
7926
7950
  }
7927
7951
  });
7928
7952
 
@@ -10736,7 +10760,7 @@ function spawnPty(...args) {
10736
10760
  );
10737
10761
  }
10738
10762
  }
10739
- var socket, PREFIX, MAX_SCROLLBACK, SCROLLBACK_SLACK, POLL_MS, markerFilled, attached, claudeBin, shellBin, codexBin, agentBin, rootBypassEnv, frame, name, promptFilePath, goalGatePath, goalStatePath, agentsFilePath, askpassDenyPath, ASKPASS_DENY, NO_SERVER, TmuxError, sq, idFor, FMT, listSessions, liveDecisions, getSession, hooks, emitBirth, emitDeath, customAgentSource, customAgentsFor, sleep, paneText, dialogIO, DIALOG_CAPTURE_LINES, GOAL_ARMED_MARKERS, goalArmed, COALESCE_MS, COALESCE_MAX_BYTES, trimScrollback, phaseKey, paneComplete, sessionFinished, poll, TERMINAL_QUERY, stripTerminalQueries, TERMINAL_RESPONSE, isTerminalResponse, detach;
10763
+ var socket, PREFIX, MAX_SCROLLBACK, SCROLLBACK_SLACK, POLL_MS, markerFilled, attached, claudeBin, shellBin, codexBin, agentBin, rootBypassEnv, frame, name, promptFilePath, goalGatePath, goalStatePath, agentsFilePath, askpassDenyPath, ASKPASS_DENY, NO_SERVER, TmuxError, sq, idFor, FMT, listSessions, liveDecisions, getSession, hooks, emitBirth, emitDeath, customAgentSource, customAgentsFor, sleep, paneText, dialogIO, DIALOG_CAPTURE_LINES, GOAL_ARMED_MARKERS, goalArmed, COALESCE_MS, COALESCE_MAX_BYTES, trimScrollback, phaseKey, paneComplete, sessionFinished, poll, TERMINAL_QUERY, stripTerminalQueries, detach;
10740
10764
  var init_pty = __esm({
10741
10765
  "src/services/pty.ts"() {
10742
10766
  "use strict";
@@ -10898,21 +10922,6 @@ exit 1
10898
10922
  // XTWINOPS laporan — berparameter TUNGGAL
10899
10923
  ].join("|"), "g");
10900
10924
  stripTerminalQueries = (s2) => s2.replace(TERMINAL_QUERY, "");
10901
- TERMINAL_RESPONSE = new RegExp([
10902
- "\\x1b\\[[?>][0-9;]*c",
10903
- // balasan DA
10904
- "\\x1b\\[\\??[0-9]+;[0-9]+R",
10905
- // CPR / DECXCPR
10906
- "\\x1b\\[\\??[0-9;]*n",
10907
- // balasan DSR
10908
- "\\x1b\\[\\??[0-9;]*\\$y",
10909
- // DECRPM
10910
- "\\x1b\\][0-9][0-9;]*;[^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)",
10911
- // balasan warna OSC
10912
- "\\x1bP[0-9]*[$+>][a-z|][^\\x1b]*\\x1b\\\\"
10913
- // DECRPSS / XTGETTCAP / XTVERSION
10914
- ].join("|"), "g");
10915
- isTerminalResponse = (d) => d.length > 0 && d.replace(TERMINAL_RESPONSE, "") === "";
10916
10925
  detach = (id, c) => {
10917
10926
  attached.get(id)?.clients.delete(c);
10918
10927
  };
@@ -39250,6 +39259,33 @@ function installCommand(m, agent, shell = shellBin()) {
39250
39259
  return [shell, "-lc", `${m.install[agent].join(" && ")}; exec '${shell}' -l`];
39251
39260
  }
39252
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
+
39253
39289
  // src/routes/terminal.ts
39254
39290
  init_notifications();
39255
39291
  init_stage_machine();
@@ -39595,8 +39631,16 @@ async function terminal_default(app2, opts) {
39595
39631
  } catch {
39596
39632
  return;
39597
39633
  }
39598
- if (m.t === "in" && typeof m.d === "string") writeTo(id, m.d, client);
39599
- else if (m.t === "resize" && m.cols && m.rows) resize(id, m.cols, m.rows);
39634
+ if (m.t === "in" && typeof m.d === "string") {
39635
+ writeTo(id, m.d, client);
39636
+ if (typeof m.seq === "number") socket2.send(JSON.stringify({ t: "ack", seq: m.seq }));
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
+ }
39600
39644
  });
39601
39645
  const revalidate = setInterval(() => {
39602
39646
  void revalidateWsPrincipal(req, principal).then((ok2) => {
@@ -39790,9 +39834,9 @@ import { readFileSync as readFileSync8 } from "node:fs";
39790
39834
  // src/services/vps-ssh.ts
39791
39835
  init_config2();
39792
39836
  import { spawn as spawn4 } from "node:child_process";
39793
- import { mkdtempSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
39837
+ import { mkdtempSync, rmSync as rmSync5, writeFileSync as writeFileSync4 } from "node:fs";
39794
39838
  import { tmpdir as tmpdir3 } from "node:os";
39795
- import { dirname as dirname10, join as join15 } from "node:path";
39839
+ import { dirname as dirname10, join as join16 } from "node:path";
39796
39840
  var sshBin = () => effectiveStr("HANOMAN_SSH_BIN") ?? "ssh";
39797
39841
  function consoleArgv(t) {
39798
39842
  return [
@@ -39807,8 +39851,8 @@ function consoleArgv(t) {
39807
39851
  ];
39808
39852
  }
39809
39853
  function askpassScript() {
39810
- const dir2 = mkdtempSync(join15(tmpdir3(), "hanoman-askpass-"));
39811
- const path2 = join15(dir2, "askpass.sh");
39854
+ const dir2 = mkdtempSync(join16(tmpdir3(), "hanoman-askpass-"));
39855
+ const path2 = join16(dir2, "askpass.sh");
39812
39856
  writeFileSync4(path2, `#!/bin/sh
39813
39857
  printf '%s' "$HANOMAN_SSH_PASSWORD"
39814
39858
  `, { mode: 448 });
@@ -39848,7 +39892,7 @@ function sshExec(t, remoteCmd, opts = {}) {
39848
39892
  const timer9 = setTimeout(() => p.kill("SIGKILL"), opts.timeoutMs ?? 6e4);
39849
39893
  const done = (r) => {
39850
39894
  clearTimeout(timer9);
39851
- if (askpass) rmSync4(dirname10(askpass), { recursive: true, force: true });
39895
+ if (askpass) rmSync5(dirname10(askpass), { recursive: true, force: true });
39852
39896
  resolve21(r);
39853
39897
  };
39854
39898
  p.stdout.on("data", (d) => {
@@ -39869,15 +39913,15 @@ function sshExec(t, remoteCmd, opts = {}) {
39869
39913
  // src/services/vps-audit.ts
39870
39914
  init_db();
39871
39915
  import { readFileSync as readFileSync7 } from "node:fs";
39872
- import { join as join17 } from "node:path";
39916
+ import { join as join18 } from "node:path";
39873
39917
 
39874
39918
  // src/runner/deps.ts
39875
39919
  import { existsSync as existsSync11 } from "node:fs";
39876
- import { dirname as dirname11, join as join16 } from "node:path";
39920
+ import { dirname as dirname11, join as join17 } from "node:path";
39877
39921
  function repoRootFrom(startDir) {
39878
39922
  let dir2 = startDir;
39879
39923
  for (let i = 0; i < 8; i++) {
39880
- if (existsSync11(join16(dir2, "pnpm-workspace.yaml"))) return dir2;
39924
+ if (existsSync11(join17(dir2, "pnpm-workspace.yaml"))) return dir2;
39881
39925
  const parent = dirname11(dir2);
39882
39926
  if (parent === dir2) break;
39883
39927
  dir2 = parent;
@@ -39983,7 +40027,7 @@ function parseHealth(out4) {
39983
40027
  return { uptime: h.uptime ?? "", disk: h.disk ?? "", mem: h.mem ?? "", load: h.load ?? "" };
39984
40028
  }
39985
40029
  var target = (v) => ({ host: v.host, port: v.port, user: v.user, keyPath: v.keyPath });
39986
- var scriptPath = (f) => join17(repoRoot(), "server", "scripts", "vps", f);
40030
+ var scriptPath = (f) => join18(repoRoot(), "server", "scripts", "vps", f);
39987
40031
  async function itemStatesOf(vpsId) {
39988
40032
  const rows = await prisma.vpsItemState.findMany({ where: { vpsId } });
39989
40033
  return Object.fromEntries(rows.map((s2) => [s2.itemId, { na: s2.na, attested: s2.attested }]));
@@ -40131,31 +40175,31 @@ async function buildChecklist(vpsId) {
40131
40175
  init_src2();
40132
40176
  init_config2();
40133
40177
  import { execFileSync as execFileSync2 } from "node:child_process";
40134
- 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";
40135
40179
  import { homedir as homedir5 } from "node:os";
40136
- import { join as join18 } from "node:path";
40180
+ import { join as join19 } from "node:path";
40137
40181
  var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? resolveDataDirs().sshKeys;
40138
40182
  var KEY_FILES = ["id_ed25519", "id_ed25519.pub"];
40139
40183
  function adoptLegacyKey(dir2) {
40140
40184
  if (effectiveStr("HANOMAN_SSH_KEY_DIR")) return;
40141
- const legacy = join18(homedir5(), ".hanoman");
40185
+ const legacy = join19(homedir5(), ".hanoman");
40142
40186
  if (legacy === dir2) return;
40143
- if (!KEY_FILES.every((f) => existsSync12(join18(legacy, f)))) return;
40144
- mkdirSync8(dir2, { recursive: true, mode: 448 });
40187
+ if (!KEY_FILES.every((f) => existsSync12(join19(legacy, f)))) return;
40188
+ mkdirSync9(dir2, { recursive: true, mode: 448 });
40145
40189
  for (const f of KEY_FILES) {
40146
- copyFileSync(join18(legacy, f), join18(dir2, f));
40147
- chmodSync3(join18(dir2, f), 384);
40190
+ copyFileSync(join19(legacy, f), join19(dir2, f));
40191
+ chmodSync3(join19(dir2, f), 384);
40148
40192
  }
40149
- for (const f of KEY_FILES) unlinkSync2(join18(legacy, f));
40193
+ for (const f of KEY_FILES) unlinkSync2(join19(legacy, f));
40150
40194
  console.log(`vps: key SSH dipindah dari ${legacy} ke ${dir2} (SPEC-846 \u2014 satu batas backup)`);
40151
40195
  }
40152
40196
  function ensureHanomanKey() {
40153
40197
  const dir2 = keyDir();
40154
- const privPath = join18(dir2, KEY_FILES[0]);
40198
+ const privPath = join19(dir2, KEY_FILES[0]);
40155
40199
  const pubPath = `${privPath}.pub`;
40156
40200
  if (!existsSync12(privPath)) adoptLegacyKey(dir2);
40157
40201
  if (!existsSync12(privPath)) {
40158
- mkdirSync8(dir2, { recursive: true, mode: 448 });
40202
+ mkdirSync9(dir2, { recursive: true, mode: 448 });
40159
40203
  execFileSync2(
40160
40204
  "ssh-keygen",
40161
40205
  ["-t", "ed25519", "-N", "", "-C", "hanoman", "-f", privPath],
@@ -40390,7 +40434,7 @@ init_config2();
40390
40434
  import { execFileSync as execFileSync3 } from "node:child_process";
40391
40435
  import { readFileSync as readFileSync11 } from "node:fs";
40392
40436
  import { homedir as homedir7 } from "node:os";
40393
- import { join as join19 } from "node:path";
40437
+ import { join as join20 } from "node:path";
40394
40438
  var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
40395
40439
  var TTL_MS2 = 3e4;
40396
40440
  var lastOk = null;
@@ -40403,7 +40447,7 @@ var LABELS = {
40403
40447
  var humanize = (k) => k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
40404
40448
  var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
40405
40449
  function credsFile() {
40406
- return join19(effectiveStr("CLAUDE_CONFIG_DIR") ?? join19(homedir7(), ".claude"), ".credentials.json");
40450
+ return join20(effectiveStr("CLAUDE_CONFIG_DIR") ?? join20(homedir7(), ".claude"), ".credentials.json");
40407
40451
  }
40408
40452
  function readAccessToken() {
40409
40453
  if (process.platform === "darwin" && !effectiveStr("CLAUDE_CONFIG_DIR")) {
@@ -40477,14 +40521,14 @@ async function getLimits() {
40477
40521
  // src/services/codex-limits.ts
40478
40522
  import { open as open3, readdir as readdir4, stat as stat2 } from "node:fs/promises";
40479
40523
  import { homedir as homedir8 } from "node:os";
40480
- import { join as join20 } from "node:path";
40524
+ import { join as join21 } from "node:path";
40481
40525
  var TTL_MS3 = 3e4;
40482
40526
  var STALE_AFTER_MS = 12 * 36e5;
40483
40527
  var TAIL_BYTES = 512 * 1024;
40484
40528
  var MAX_FILES = 8;
40485
40529
  var cache3 = null;
40486
40530
  var freshUntil2 = 0;
40487
- var codexSessionsDir = () => join20(process.env.CODEX_HOME ?? join20(homedir8(), ".codex"), "sessions");
40531
+ var codexSessionsDir = () => join21(process.env.CODEX_HOME ?? join21(homedir8(), ".codex"), "sessions");
40488
40532
  var UNAVAILABLE = { status: "unavailable", windows: [], fetchedAt: null, plan: null };
40489
40533
  function windowLabel(minutes) {
40490
40534
  if (minutes === 300) return "Sesi 5 jam";
@@ -40517,7 +40561,7 @@ async function recentRollouts(dir2) {
40517
40561
  return [];
40518
40562
  }
40519
40563
  const stamped = await Promise.all(entries.map(async (rel) => {
40520
- const full = join20(dir2, rel);
40564
+ const full = join21(dir2, rel);
40521
40565
  try {
40522
40566
  return { full, mtime: (await stat2(full)).mtimeMs };
40523
40567
  } catch {
@@ -41188,7 +41232,7 @@ init_config2();
41188
41232
  init_src2();
41189
41233
  import { randomUUID as randomUUID7 } from "node:crypto";
41190
41234
  import { mkdir as mkdir5, writeFile as writeFile4, readFile as readFile4, unlink as unlink4, readdir as readdir5, stat as stat3 } from "node:fs/promises";
41191
- 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";
41192
41236
  var MAX_TRANSCRIPT_BYTES = 1024 * 1024;
41193
41237
  function transcriptDir() {
41194
41238
  return resolve19(effectiveStr("HANOMAN_TRANSCRIPT_DIR")?.trim() || resolveDataDirs().transcripts);
@@ -41209,13 +41253,13 @@ async function saveTranscript(text) {
41209
41253
  const dir2 = transcriptDir();
41210
41254
  await mkdir5(dir2, { recursive: true, mode: 448 });
41211
41255
  const key = `${randomUUID7()}.log`;
41212
- await writeFile4(join21(dir2, key), body, { encoding: "utf8", mode: 384 });
41256
+ await writeFile4(join22(dir2, key), body, { encoding: "utf8", mode: 384 });
41213
41257
  return { key, bytes: Buffer.byteLength(body, "utf8"), truncated };
41214
41258
  }
41215
41259
  async function readTranscript(key) {
41216
41260
  if (!key) return null;
41217
41261
  try {
41218
- return await readFile4(join21(transcriptDir(), basename6(key)), "utf8");
41262
+ return await readFile4(join22(transcriptDir(), basename6(key)), "utf8");
41219
41263
  } catch {
41220
41264
  return null;
41221
41265
  }
@@ -41223,7 +41267,7 @@ async function readTranscript(key) {
41223
41267
  async function deleteTranscript(key) {
41224
41268
  if (!key) return;
41225
41269
  try {
41226
- await unlink4(join21(transcriptDir(), basename6(key)));
41270
+ await unlink4(join22(transcriptDir(), basename6(key)));
41227
41271
  } catch (e) {
41228
41272
  if (e.code !== "ENOENT") throw e;
41229
41273
  }
@@ -41241,7 +41285,7 @@ async function listTranscripts() {
41241
41285
  for (const name2 of names) {
41242
41286
  if (!name2.endsWith(".log")) continue;
41243
41287
  try {
41244
- rows.push({ key: name2, mtimeMs: (await stat3(join21(dir2, name2))).mtimeMs });
41288
+ rows.push({ key: name2, mtimeMs: (await stat3(join22(dir2, name2))).mtimeMs });
41245
41289
  } catch {
41246
41290
  }
41247
41291
  }
@@ -42701,9 +42745,9 @@ init_pty();
42701
42745
  init_session_sandbox();
42702
42746
  import { execFile as execFile12 } from "node:child_process";
42703
42747
  import { randomUUID as randomUUID9 } from "node:crypto";
42704
- 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";
42705
42749
  import { tmpdir as tmpdir4 } from "node:os";
42706
- import { join as join22 } from "node:path";
42750
+ import { join as join23 } from "node:path";
42707
42751
  var binFor = (agent) => agent === "codex" ? effectiveStr("HANOMAN_CODEX_BIN") ?? "codex" : effectiveStr("HANOMAN_CLAUDE_BIN") ?? "claude";
42708
42752
  function leadArgv(o) {
42709
42753
  if (o.agent === "codex") {
@@ -42731,12 +42775,12 @@ function leadProcess(prompt, o, env = process.env) {
42731
42775
  const mode = env.HANOMAN_SESSION_SANDBOX ?? (env.NODE_ENV === "production" ? "required" : "off");
42732
42776
  if (mode === "off") return { file, args: directArgs, cwd: o.cwd, cleanup: () => {
42733
42777
  } };
42734
- const promptDir = join22(tmpdir4(), "hanoman-prompts");
42735
- mkdirSync9(promptDir, { recursive: true, mode: 448 });
42736
- 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()}`);
42737
42781
  writeFileSync5(promptFile, prompt, { flag: "wx", mode: 384 });
42738
- const workspace = o.cwd ?? join22(tmpdir4(), `hanoman-oneshot-${randomUUID9()}`);
42739
- 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 });
42740
42784
  try {
42741
42785
  const argsWithoutPrompt = directArgs.slice(0, -1);
42742
42786
  const command = [file, ...argsWithoutPrompt].map(shellQuote).join(" ") + ` "$(cat ${shellQuote(promptFile)})"`;
@@ -42753,13 +42797,13 @@ function leadProcess(prompt, o, env = process.env) {
42753
42797
  args: sandbox.slice(1),
42754
42798
  promptFile,
42755
42799
  cleanup: () => {
42756
- rmSync5(promptFile, { force: true });
42757
- if (!o.cwd) rmSync5(workspace, { recursive: true, force: true });
42800
+ rmSync6(promptFile, { force: true });
42801
+ if (!o.cwd) rmSync6(workspace, { recursive: true, force: true });
42758
42802
  }
42759
42803
  };
42760
42804
  } catch (error) {
42761
- rmSync5(promptFile, { force: true });
42762
- if (!o.cwd) rmSync5(workspace, { recursive: true, force: true });
42805
+ rmSync6(promptFile, { force: true });
42806
+ if (!o.cwd) rmSync6(workspace, { recursive: true, force: true });
42763
42807
  throw error;
42764
42808
  }
42765
42809
  }
@@ -44261,9 +44305,9 @@ var US2 = "";
44261
44305
  var RS = "";
44262
44306
  var NO_REPO = "project ini belum ditautkan ke repo di mesin ini";
44263
44307
  var NO_TAG = "repo project ini belum punya tag rilis";
44264
- var usable = (repoDir) => !!repoDir && existsSync15(repoDir);
44308
+ var usable2 = (repoDir) => !!repoDir && existsSync15(repoDir);
44265
44309
  async function listTags(repoDir) {
44266
- if (!usable(repoDir)) return { tags: [], head: null, reason: NO_REPO };
44310
+ if (!usable2(repoDir)) return { tags: [], head: null, reason: NO_REPO };
44267
44311
  try {
44268
44312
  const { stdout } = await exec10("git", ["tag", "--list", "--sort=-creatordate"], { cwd: repoDir, ...GIT8 });
44269
44313
  const tags = stdout.split("\n").map((s2) => s2.trim()).filter(Boolean);
@@ -44291,7 +44335,7 @@ async function logRange(repoDir, range) {
44291
44335
  }).filter((x) => x !== null);
44292
44336
  }
44293
44337
  async function collectCommits(repoDir, fromSha, toSha) {
44294
- if (!usable(repoDir)) return { ok: false, reason: NO_REPO };
44338
+ if (!usable2(repoDir)) return { ok: false, reason: NO_REPO };
44295
44339
  for (const rev of [fromSha, toSha])
44296
44340
  if (!await revExists(repoDir, rev)) return { ok: false, reason: `revisi "${rev}" tak dikenal di repo project` };
44297
44341
  try {
@@ -44306,7 +44350,7 @@ async function collectCommits(repoDir, fromSha, toSha) {
44306
44350
  async function collectVersions(repoDir, fromTag, toTag) {
44307
44351
  const { tags, reason } = await listTags(repoDir);
44308
44352
  if (reason) return { ok: false, reason };
44309
- if (!usable(repoDir)) return { ok: false, reason: NO_REPO };
44353
+ if (!usable2(repoDir)) return { ok: false, reason: NO_REPO };
44310
44354
  if (!tags.includes(toTag)) return { ok: false, reason: `tag "${toTag}" tak ada di repo project` };
44311
44355
  if (fromTag && !tags.includes(fromTag)) return { ok: false, reason: `tag "${fromTag}" tak ada di repo project` };
44312
44356
  const prev = fromTag ?? await previousTag(repoDir, toTag);
@@ -44524,7 +44568,7 @@ init_db();
44524
44568
  init_src();
44525
44569
  import { readFileSync as readFileSync13 } from "node:fs";
44526
44570
  import { homedir as homedir9 } from "node:os";
44527
- import { join as join23 } from "node:path";
44571
+ import { join as join24 } from "node:path";
44528
44572
  var home = () => process.env.HOME || homedir9();
44529
44573
  var readJson2 = (path2) => {
44530
44574
  try {
@@ -44541,7 +44585,7 @@ var serversOf = (node) => {
44541
44585
  var codexServers = () => {
44542
44586
  let text;
44543
44587
  try {
44544
- text = readFileSync13(join23(home(), ".codex", "config.toml"), "utf8");
44588
+ text = readFileSync13(join24(home(), ".codex", "config.toml"), "utf8");
44545
44589
  } catch {
44546
44590
  return [];
44547
44591
  }
@@ -44554,12 +44598,12 @@ var codexServers = () => {
44554
44598
  };
44555
44599
  function mcpServerNames(repoDir) {
44556
44600
  const names = [];
44557
- const claudeJson = readJson2(join23(home(), ".claude.json"));
44601
+ const claudeJson = readJson2(join24(home(), ".claude.json"));
44558
44602
  names.push(...serversOf(claudeJson));
44559
44603
  if (repoDir) {
44560
44604
  const projects = claudeJson?.projects;
44561
44605
  if (projects && typeof projects === "object") names.push(...serversOf(projects[repoDir]));
44562
- names.push(...serversOf(readJson2(join23(repoDir, ".mcp.json"))));
44606
+ names.push(...serversOf(readJson2(join24(repoDir, ".mcp.json"))));
44563
44607
  }
44564
44608
  names.push(...codexServers());
44565
44609
  return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
@@ -46043,9 +46087,9 @@ ${baru}`;
46043
46087
  // src/services/portal-chat/workspace.ts
46044
46088
  init_src();
46045
46089
  init_db();
46046
- 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";
46047
46091
  import { tmpdir as tmpdir5 } from "node:os";
46048
- import { join as join24 } from "node:path";
46092
+ import { join as join25 } from "node:path";
46049
46093
  var STAGE_LABEL = {
46050
46094
  brainstorming: "Dirumuskan",
46051
46095
  objective: "Dirumuskan",
@@ -46100,7 +46144,7 @@ ${c.body}
46100
46144
  ${baris.join("\n")}`;
46101
46145
  }
46102
46146
  async function buildChatWorkspace(projectId) {
46103
- const dir2 = mkdtempSync2(join24(tmpdir5(), "hanoman-portal-chat-"));
46147
+ const dir2 = mkdtempSync2(join25(tmpdir5(), "hanoman-portal-chat-"));
46104
46148
  try {
46105
46149
  const project = await prisma.project.findUnique({
46106
46150
  where: { id: projectId },
@@ -46121,7 +46165,7 @@ async function buildChatWorkspace(projectId) {
46121
46165
  });
46122
46166
  const files = [];
46123
46167
  const tulis = (rel, isi) => {
46124
- writeFileSync7(join24(dir2, rel), isi, { mode: 384 });
46168
+ writeFileSync7(join25(dir2, rel), isi, { mode: 384 });
46125
46169
  files.push(rel);
46126
46170
  };
46127
46171
  tulis("project.md", renderProjectDoc(project));
@@ -46130,16 +46174,16 @@ async function buildChatWorkspace(projectId) {
46130
46174
  tulis("catatan-rilis.md", renderChangelogDoc(changelogs));
46131
46175
  const prds = await listPrds(projectId);
46132
46176
  if (prds.length) {
46133
- mkdirSync10(join24(dir2, "dokumen"), { mode: 448 });
46177
+ mkdirSync11(join25(dir2, "dokumen"), { mode: 448 });
46134
46178
  for (const prd of prds) {
46135
46179
  const isi = await readPrd(projectId, prd.path);
46136
46180
  if (!isi) continue;
46137
- tulis(join24("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
46181
+ tulis(join25("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
46138
46182
  }
46139
46183
  }
46140
- return { dir: dir2, files, cleanup: () => rmSync6(dir2, { recursive: true, force: true }) };
46184
+ return { dir: dir2, files, cleanup: () => rmSync7(dir2, { recursive: true, force: true }) };
46141
46185
  } catch (error) {
46142
- rmSync6(dir2, { recursive: true, force: true });
46186
+ rmSync7(dir2, { recursive: true, force: true });
46143
46187
  throw error;
46144
46188
  }
46145
46189
  }
@@ -46646,13 +46690,13 @@ init_db();
46646
46690
  // src/services/bootstrap.ts
46647
46691
  import { createHash as createHash9, randomBytes as randomBytes10, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
46648
46692
  import { chmod, lstat as lstat3, mkdir as mkdir6, readFile as readFile5, unlink as unlink5, writeFile as writeFile5 } from "node:fs/promises";
46649
- import { join as join25 } from "node:path";
46693
+ import { join as join26 } from "node:path";
46650
46694
  var TTL_MS5 = 15 * 6e4;
46651
46695
  var SETUP_TOKEN_FILE = "setup.token";
46652
46696
  var BootstrapError = class extends Error {
46653
46697
  code = "BOOTSTRAP_PROOF";
46654
46698
  };
46655
- var tokenPath = (home3) => join25(home3, SETUP_TOKEN_FILE);
46699
+ var tokenPath = (home3) => join26(home3, SETUP_TOKEN_FILE);
46656
46700
  async function readStored(home3) {
46657
46701
  try {
46658
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.53",
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