hanoman 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -8929,6 +8929,61 @@ var init_telegram_operator = __esm({
8929
8929
  }
8930
8930
  });
8931
8931
 
8932
+ // ../runner/src/spawn-helper.ts
8933
+ import { readdirSync as readdirSync2, existsSync as existsSync4, statSync, chmodSync } from "node:fs";
8934
+ import { dirname as dirname4, join as join3 } from "node:path";
8935
+ function spawnHelperPaths(ptyDir, listDir, exists) {
8936
+ const dirs = [join3(ptyDir, "build", "Release")];
8937
+ for (const name2 of listDir(join3(ptyDir, "prebuilds"))) dirs.push(join3(ptyDir, "prebuilds", name2));
8938
+ return dirs.map((d) => join3(d, "spawn-helper")).filter(exists);
8939
+ }
8940
+ function ensureSpawnHelpersExecutable(paths2, ops) {
8941
+ const fixed = [];
8942
+ for (const p of paths2) {
8943
+ try {
8944
+ const mode = ops.mode(p) & 4095;
8945
+ if ((mode & 73) === 73) continue;
8946
+ ops.chmod(p, mode | 73);
8947
+ fixed.push(p);
8948
+ } catch {
8949
+ }
8950
+ }
8951
+ return fixed;
8952
+ }
8953
+ function repairSpawnHelper(resolve19, notify) {
8954
+ let ptyDir;
8955
+ try {
8956
+ ptyDir = dirname4(resolve19("node-pty/package.json"));
8957
+ } catch {
8958
+ return [];
8959
+ }
8960
+ const paths2 = spawnHelperPaths(ptyDir, (d) => {
8961
+ try {
8962
+ return readdirSync2(d);
8963
+ } catch {
8964
+ return [];
8965
+ }
8966
+ }, existsSync4);
8967
+ const fixed = ensureSpawnHelpersExecutable(paths2, {
8968
+ mode: (p) => statSync(p).mode,
8969
+ chmod: (p, m) => chmodSync(p, m)
8970
+ });
8971
+ if (fixed.length) notify?.("hanoman \xB7 memperbaiki izin `spawn-helper` node-pty (sekali per instalasi)\n");
8972
+ return fixed;
8973
+ }
8974
+ function ensureSpawnHelperOnce(resolve19, notify) {
8975
+ if (repaired) return [];
8976
+ repaired = true;
8977
+ return repairSpawnHelper(resolve19, notify);
8978
+ }
8979
+ var repaired;
8980
+ var init_spawn_helper = __esm({
8981
+ "../runner/src/spawn-helper.ts"() {
8982
+ "use strict";
8983
+ repaired = false;
8984
+ }
8985
+ });
8986
+
8932
8987
  // ../runner/src/index.ts
8933
8988
  var init_src2 = __esm({
8934
8989
  "../runner/src/index.ts"() {
@@ -8948,6 +9003,7 @@ var init_src2 = __esm({
8948
9003
  init_paths();
8949
9004
  init_skills();
8950
9005
  init_telegram_operator();
9006
+ init_spawn_helper();
8951
9007
  }
8952
9008
  });
8953
9009
 
@@ -9083,7 +9139,7 @@ var init_tap = __esm({
9083
9139
 
9084
9140
  // src/db.ts
9085
9141
  import { mkdirSync as mkdirSync2 } from "node:fs";
9086
- import { dirname as dirname4, resolve as resolve7 } from "node:path";
9142
+ import { dirname as dirname5, resolve as resolve7 } from "node:path";
9087
9143
  import { fileURLToPath as fileURLToPath2 } from "node:url";
9088
9144
  import { PrismaClient } from "@prisma/client";
9089
9145
  var schemaDir, notice, url, base, prisma;
@@ -9093,13 +9149,13 @@ var init_db = __esm({
9093
9149
  init_env();
9094
9150
  init_src2();
9095
9151
  init_tap();
9096
- schemaDir = resolve7(dirname4(fileURLToPath2(import.meta.url)), "../prisma");
9152
+ schemaDir = resolve7(dirname5(fileURLToPath2(import.meta.url)), "../prisma");
9097
9153
  process.umask(63);
9098
9154
  notice = dbUrlNotice(process.env);
9099
9155
  if (notice) console.warn(notice);
9100
9156
  url = resolveDbUrl(process.env, schemaDir);
9101
9157
  process.env.DATABASE_URL = url;
9102
- mkdirSync2(dirname4(dbFilePath(url)), { recursive: true, mode: 448 });
9158
+ mkdirSync2(dirname5(dbFilePath(url)), { recursive: true, mode: 448 });
9103
9159
  base = new PrismaClient();
9104
9160
  prisma = base.$extends(webhookTap(base));
9105
9161
  }
@@ -9115,7 +9171,7 @@ var init_stage_machine = __esm({
9115
9171
  });
9116
9172
 
9117
9173
  // src/services/session-phases.ts
9118
- import { readFileSync as readFileSync3, readdirSync as readdirSync2 } from "node:fs";
9174
+ import { readFileSync as readFileSync3, readdirSync as readdirSync3 } from "node:fs";
9119
9175
  function recorded(file) {
9120
9176
  const out3 = /* @__PURE__ */ new Map();
9121
9177
  let raw2;
@@ -9163,7 +9219,7 @@ function planComplete(worktree, specId) {
9163
9219
  const dir2 = `${worktree}/${rel}`;
9164
9220
  let names;
9165
9221
  try {
9166
- names = readdirSync2(dir2);
9222
+ names = readdirSync3(dir2);
9167
9223
  } catch {
9168
9224
  continue;
9169
9225
  }
@@ -9453,9 +9509,9 @@ var init_tui_dialog = __esm({
9453
9509
  });
9454
9510
 
9455
9511
  // src/services/secret-box.ts
9456
- import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync } from "node:fs";
9512
+ import { chmodSync as chmodSync2, existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync } from "node:fs";
9457
9513
  import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
9458
- import { dirname as dirname5, join as join3 } from "node:path";
9514
+ import { dirname as dirname6, join as join4 } from "node:path";
9459
9515
  function isEncrypted(value) {
9460
9516
  return value.startsWith(ENC_PREFIX);
9461
9517
  }
@@ -9487,7 +9543,7 @@ function fromEnv(raw2) {
9487
9543
  return null;
9488
9544
  }
9489
9545
  function secretKeyPath() {
9490
- return join3(resolveHome(), "secret.key");
9546
+ return join4(resolveHome(), "secret.key");
9491
9547
  }
9492
9548
  function secretKey() {
9493
9549
  if (cached) return cached;
@@ -9499,16 +9555,16 @@ function secretKey() {
9499
9555
  return key2;
9500
9556
  }
9501
9557
  const path2 = secretKeyPath();
9502
- if (existsSync4(path2)) {
9558
+ if (existsSync5(path2)) {
9503
9559
  const key2 = Buffer.from(readFileSync4(path2, "utf8").trim(), "base64url");
9504
9560
  if (key2.length !== KEY_BYTES) throw new Error(`kunci di ${path2} rusak \u2014 panjangnya bukan 32 byte`);
9505
9561
  cached = key2;
9506
9562
  return key2;
9507
9563
  }
9508
9564
  const key = randomBytes(KEY_BYTES);
9509
- mkdirSync3(dirname5(path2), { recursive: true });
9565
+ mkdirSync3(dirname6(path2), { recursive: true });
9510
9566
  writeFileSync(path2, key.toString("base64url"), { mode: 384 });
9511
- chmodSync(path2, 384);
9567
+ chmodSync2(path2, 384);
9512
9568
  cached = key;
9513
9569
  return key;
9514
9570
  }
@@ -9693,9 +9749,10 @@ var init_session_sandbox = __esm({
9693
9749
  // src/services/pty.ts
9694
9750
  import { spawn } from "node-pty";
9695
9751
  import { execFileSync } from "node:child_process";
9752
+ import { createRequire } from "node:module";
9696
9753
  import { randomUUID } from "node:crypto";
9697
- import { mkdirSync as mkdirSync4, statSync, writeFileSync as writeFileSync2 } from "node:fs";
9698
- import { dirname as dirname6 } from "node:path";
9754
+ import { mkdirSync as mkdirSync4, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
9755
+ import { dirname as dirname7 } from "node:path";
9699
9756
  import { tmpdir } from "node:os";
9700
9757
  function tmux(...args) {
9701
9758
  try {
@@ -9778,7 +9835,7 @@ function createSession(projectId, cwd, opts = {}) {
9778
9835
  let promptFile;
9779
9836
  if (!opts.command && opts.prompt) {
9780
9837
  promptFile = promptFilePath(id);
9781
- mkdirSync4(dirname6(promptFile), { recursive: true, mode: 448 });
9838
+ mkdirSync4(dirname7(promptFile), { recursive: true, mode: 448 });
9782
9839
  writeFileSync2(promptFile, opts.prompt + rosterBlock, { mode: 384 });
9783
9840
  promptArg = `"$(cat ${sq(promptFile)})"`;
9784
9841
  }
@@ -9790,7 +9847,7 @@ function createSession(projectId, cwd, opts = {}) {
9790
9847
  let goalGate;
9791
9848
  if (agent === "codex" && opts.goal && opts.flow && opts.specId) {
9792
9849
  goalGate = goalGatePath(id);
9793
- mkdirSync4(dirname6(goalGate), { recursive: true, mode: 448 });
9850
+ mkdirSync4(dirname7(goalGate), { recursive: true, mode: 448 });
9794
9851
  writeFileSync2(goalGate, codexGoalScript({
9795
9852
  flow: opts.flow,
9796
9853
  specId: opts.specId,
@@ -9806,7 +9863,7 @@ function createSession(projectId, cwd, opts = {}) {
9806
9863
  const json = renderAgentsJson(customDefs);
9807
9864
  if (json) {
9808
9865
  agentsFile = agentsFilePath(id);
9809
- mkdirSync4(dirname6(agentsFile), { recursive: true, mode: 448 });
9866
+ mkdirSync4(dirname7(agentsFile), { recursive: true, mode: 448 });
9810
9867
  writeFileSync2(agentsFile, json, { mode: 384 });
9811
9868
  }
9812
9869
  }
@@ -9826,7 +9883,7 @@ function createSession(projectId, cwd, opts = {}) {
9826
9883
  for (const [k, v] of Object.entries(rootBypassEnv())) envPairs.push(`${k}=${sq(v)}`);
9827
9884
  }
9828
9885
  if (opts.phaseFile) {
9829
- mkdirSync4(dirname6(opts.phaseFile), { recursive: true });
9886
+ mkdirSync4(dirname7(opts.phaseFile), { recursive: true });
9830
9887
  envPairs.push(`HANOMAN_PHASE_FILE=${sq(opts.phaseFile)}`);
9831
9888
  }
9832
9889
  for (const [k, v] of Object.entries(opts.env ?? {})) envPairs.push(`${k}=${sq(v)}`);
@@ -9837,7 +9894,7 @@ function createSession(projectId, cwd, opts = {}) {
9837
9894
  phaseFile: opts.phaseFile,
9838
9895
  promptFile
9839
9896
  });
9840
- if (opts.decisionFile) mkdirSync4(dirname6(opts.decisionFile), { recursive: true });
9897
+ if (opts.decisionFile) mkdirSync4(dirname7(opts.decisionFile), { recursive: true });
9841
9898
  tmux(
9842
9899
  "set-option",
9843
9900
  "-g",
@@ -10132,6 +10189,7 @@ function detachAll() {
10132
10189
  for (const id of [...attached.keys()]) drop(id);
10133
10190
  }
10134
10191
  function spawnPty(...args) {
10192
+ ensureSpawnHelperOnce(createRequire(import.meta.url).resolve, (m) => process.stdout.write(m));
10135
10193
  try {
10136
10194
  return spawn("tmux", ["-L", socket(), "-f", "/dev/null", ...args], {
10137
10195
  name: "xterm-256color",
@@ -10164,7 +10222,7 @@ var init_pty = __esm({
10164
10222
  POLL_MS = 500;
10165
10223
  markerFilled = (f) => {
10166
10224
  try {
10167
- return statSync(f).size > 0;
10225
+ return statSync2(f).size > 0;
10168
10226
  } catch {
10169
10227
  return false;
10170
10228
  }
@@ -24258,8 +24316,8 @@ import fastifyStatic from "@fastify/static";
24258
24316
  import websocket from "@fastify/websocket";
24259
24317
  import cookie from "@fastify/cookie";
24260
24318
  import { fileURLToPath as fileURLToPath4 } from "node:url";
24261
- import { dirname as dirname12 } from "node:path";
24262
- import { existsSync as existsSync15 } from "node:fs";
24319
+ import { dirname as dirname13 } from "node:path";
24320
+ import { existsSync as existsSync16 } from "node:fs";
24263
24321
 
24264
24322
  // src/web-dir.ts
24265
24323
  import { resolve } from "node:path";
@@ -24322,13 +24380,13 @@ init_stage_machine();
24322
24380
  init_src();
24323
24381
  import { execFile } from "node:child_process";
24324
24382
  import { promisify } from "node:util";
24325
- import { existsSync as existsSync5, rmSync as rmSync2 } from "node:fs";
24383
+ import { existsSync as existsSync6, rmSync as rmSync2 } from "node:fs";
24326
24384
  import { resolve as resolve9 } from "node:path";
24327
24385
 
24328
24386
  // src/services/safe-repo-path.ts
24329
24387
  import { constants } from "node:fs";
24330
24388
  import { lstat, mkdir, open as open2, realpath, rename, unlink } from "node:fs/promises";
24331
- import { isAbsolute as isAbsolute3, join as join4, relative, resolve as resolve8, sep as sep2 } from "node:path";
24389
+ import { isAbsolute as isAbsolute3, join as join5, relative, resolve as resolve8, sep as sep2 } from "node:path";
24332
24390
  import { randomUUID as randomUUID2 } from "node:crypto";
24333
24391
  import { closeSync, fstatSync, lstatSync, mkdirSync as mkdirSync5, openSync, readFileSync as readFileSync5, realpathSync as realpathSync2, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
24334
24392
  var PathContainmentError = class extends Error {
@@ -24358,7 +24416,7 @@ async function ensureRepoParents(root, rel) {
24358
24416
  const parts = components(rel).slice(0, -1);
24359
24417
  let current = base2;
24360
24418
  for (const part of parts) {
24361
- current = join4(current, part);
24419
+ current = join5(current, part);
24362
24420
  try {
24363
24421
  await mkdir(current, { mode: 448 });
24364
24422
  } catch (error) {
@@ -24375,7 +24433,7 @@ function ensureRepoParentsSync(root, rel) {
24375
24433
  const parts = components(rel).slice(0, -1);
24376
24434
  let current = base2;
24377
24435
  for (const part of parts) {
24378
- current = join4(current, part);
24436
+ current = join5(current, part);
24379
24437
  try {
24380
24438
  mkdirSync5(current, { mode: 448 });
24381
24439
  } catch (error) {
@@ -24391,7 +24449,7 @@ async function resolveRepoEntry(root, rel, opts = {}) {
24391
24449
  const parts = components(rel);
24392
24450
  let current = base2;
24393
24451
  for (let i = 0; i < parts.length; i++) {
24394
- current = join4(current, parts[i]);
24452
+ current = join5(current, parts[i]);
24395
24453
  const stat2 = await lstat(current).catch((error) => {
24396
24454
  if (error.code === "ENOENT" && opts.allowMissingFinal && i === parts.length - 1) return null;
24397
24455
  return denied("komponen tidak ada");
@@ -24411,7 +24469,7 @@ function assertSafeRepoPathSync(root, rel, allowMissingFinal = false, allowMissi
24411
24469
  const parts = components(rel);
24412
24470
  let current = base2;
24413
24471
  for (let i = 0; i < parts.length; i++) {
24414
- current = join4(current, parts[i]);
24472
+ current = join5(current, parts[i]);
24415
24473
  try {
24416
24474
  const stat2 = lstatSync(current);
24417
24475
  if (stat2.isSymbolicLink()) denied("symlink");
@@ -24441,7 +24499,7 @@ function writeRepoFileAtomicSync(root, rel, data) {
24441
24499
  const parent = resolve8(path2, "..");
24442
24500
  const parentRel = relative(realpathSync2(root), parent);
24443
24501
  if (parentRel) assertSafeRepoPathSync(root, parentRel);
24444
- const temp = join4(parent, `.hanoman-${randomUUID2()}.tmp`);
24502
+ const temp = join5(parent, `.hanoman-${randomUUID2()}.tmp`);
24445
24503
  const fd = openSync(temp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 384);
24446
24504
  try {
24447
24505
  writeFileSync3(fd, data);
@@ -24488,7 +24546,7 @@ async function writeRepoFileAtomic(root, rel, data) {
24488
24546
  await mkdir(entry.parent, { recursive: false, mode: 448 }).catch((error) => {
24489
24547
  if (error.code !== "EEXIST") throw error;
24490
24548
  });
24491
- const temp = join4(entry.parent, `.hanoman-${randomUUID2()}.tmp`);
24549
+ const temp = join5(entry.parent, `.hanoman-${randomUUID2()}.tmp`);
24492
24550
  const handle = await open2(temp, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 384);
24493
24551
  try {
24494
24552
  await handle.writeFile(data);
@@ -24533,12 +24591,12 @@ function docsDirOf(repoDir) {
24533
24591
  }
24534
24592
  function resolveIndex(repoDir, docsDir) {
24535
24593
  const rel = `${docsDir}/README.md`;
24536
- return existsSync5(resolve9(repoDir, rel)) ? rel : "";
24594
+ return existsSync6(resolve9(repoDir, rel)) ? rel : "";
24537
24595
  }
24538
24596
  var catOf = (rel) => rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : ".";
24539
24597
  var nameOf = (rel) => rel.includes("/") ? rel.slice(rel.lastIndexOf("/") + 1) : rel;
24540
24598
  async function scanRepoDocs(repoDir) {
24541
- if (!repoDir || !existsSync5(repoDir)) return { coverage: 0, tree: [] };
24599
+ if (!repoDir || !existsSync6(repoDir)) return { coverage: 0, tree: [] };
24542
24600
  const files = await listRepoDocs(repoDir);
24543
24601
  const docsDir = docsDirOf(repoDir);
24544
24602
  const index = resolveIndex(repoDir, docsDir);
@@ -24586,7 +24644,7 @@ function writeDocFile(repoDir, rel, content) {
24586
24644
  }
24587
24645
  function deleteDocFile(repoDir, rel) {
24588
24646
  const abs = docAbsPath(repoDir, rel);
24589
- if (!existsSync5(abs)) return false;
24647
+ if (!existsSync6(abs)) return false;
24590
24648
  rmSync2(abs);
24591
24649
  return true;
24592
24650
  }
@@ -24873,13 +24931,13 @@ async function projects_default(app2) {
24873
24931
  // src/routes/specs.ts
24874
24932
  init_src();
24875
24933
  init_src2();
24876
- import { existsSync as existsSync6 } from "node:fs";
24934
+ import { existsSync as existsSync7 } from "node:fs";
24877
24935
 
24878
24936
  // src/services/integrate.ts
24879
24937
  import { execFile as execFile3 } from "node:child_process";
24880
24938
  import { promisify as promisify3 } from "node:util";
24881
24939
  import { rmSync as rmSync3 } from "node:fs";
24882
- import { join as join5 } from "node:path";
24940
+ import { join as join6 } from "node:path";
24883
24941
  var sanitize = (id) => id.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
24884
24942
  var sourceBranch = (specId) => `hanoman/${sanitize(specId)}`;
24885
24943
  var exec3 = promisify3(execFile3);
@@ -24923,7 +24981,7 @@ async function integrateBranch(repoDir, src, op, target2) {
24923
24981
  const tgt = await resolveTarget(repoDir, target2);
24924
24982
  if (!tgt) return { status: "error", code: 400, error: `target "${target2}" tidak dikenal` };
24925
24983
  await sh(repoDir, ["fetch", "origin"]);
24926
- const wt = join5(repoDir, ".worktrees", `merge-${sanitize(src.mergeId)}`);
24984
+ const wt = join6(repoDir, ".worktrees", `merge-${sanitize(src.mergeId)}`);
24927
24985
  await reclaim(repoDir, wt);
24928
24986
  const baseRef = op === "merge" ? tgt.ref : source;
24929
24987
  const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `${baseRef}^{commit}`]);
@@ -24984,7 +25042,7 @@ async function mergeIntoCurrent(repoDir, source, opts = {}) {
24984
25042
  const src = await resolveGraphSource(repoDir, source);
24985
25043
  if (!src) return { status: "error", code: 400, error: `source "${source}" tak dikenal` };
24986
25044
  await sh(repoDir, ["fetch", "origin"]);
24987
- const wt = join5(repoDir, ".worktrees", `merge-${sanitize(current)}`);
25045
+ const wt = join6(repoDir, ".worktrees", `merge-${sanitize(current)}`);
24988
25046
  await reclaim(repoDir, wt);
24989
25047
  const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `refs/heads/${current}^{commit}`]);
24990
25048
  if (!await ok(repoDir, ["worktree", "add", "--detach", "-q", wt, baseSha]))
@@ -25022,7 +25080,7 @@ async function replayCurrent(repoDir, source, cmd) {
25022
25080
  const current = await out(repoDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
25023
25081
  if (!current || current === "HEAD")
25024
25082
  return { status: "error", code: 409, error: "HEAD detached \u2014 checkout sebuah branch dulu" };
25025
- const wt = join5(repoDir, ".worktrees", `merge-${sanitize(current)}`);
25083
+ const wt = join6(repoDir, ".worktrees", `merge-${sanitize(current)}`);
25026
25084
  await reclaim(repoDir, wt);
25027
25085
  const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `refs/heads/${current}^{commit}`]);
25028
25086
  if (!await ok(repoDir, ["worktree", "add", "--detach", "-q", wt, baseSha]))
@@ -25071,15 +25129,15 @@ import { execFile as execFile4 } from "node:child_process";
25071
25129
  import { promisify as promisify4 } from "node:util";
25072
25130
  import { mkdtemp, copyFile, rm } from "node:fs/promises";
25073
25131
  import { tmpdir as tmpdir2 } from "node:os";
25074
- import { join as join6, resolve as resolve10 } from "node:path";
25132
+ import { join as join7, resolve as resolve10 } from "node:path";
25075
25133
  var exec4 = promisify4(execFile4);
25076
25134
  var GIT3 = { maxBuffer: 1 << 24 };
25077
25135
  var MAX = 256 * 1024;
25078
- var worktreeDir = (repoDir, specId) => join6(repoDir, ".worktrees", specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
25136
+ var worktreeDir = (repoDir, specId) => join7(repoDir, ".worktrees", specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
25079
25137
  async function withTempIndex(wt, fn2) {
25080
25138
  const idx = (await exec4("git", ["rev-parse", "--git-path", "index"], { cwd: wt, ...GIT3 })).stdout.trim();
25081
- const dir2 = await mkdtemp(join6(tmpdir2(), "hanoman-idx-"));
25082
- const tmp = join6(dir2, "index");
25139
+ const dir2 = await mkdtemp(join7(tmpdir2(), "hanoman-idx-"));
25140
+ const tmp = join7(dir2, "index");
25083
25141
  await copyFile(resolve10(wt, idx), tmp);
25084
25142
  const env = { ...process.env, GIT_INDEX_FILE: tmp };
25085
25143
  try {
@@ -28388,7 +28446,7 @@ ${item.context}` : item.context;
28388
28446
  });
28389
28447
  const specWithProject = (id) => prisma.spec.findUnique({ where: { id }, include: { project: true } });
28390
28448
  const resolveReview = async (repoDir, spec) => {
28391
- if (existsSync6(worktreeDir(repoDir, spec.id))) return { wt: true };
28449
+ if (existsSync7(worktreeDir(repoDir, spec.id))) return { wt: true };
28392
28450
  if (spec.baseSha && spec.headSha && await shaResolvable(repoDir, spec.baseSha) && await shaResolvable(repoDir, spec.headSha))
28393
28451
  return { wt: false, base: spec.baseSha, head: spec.headSha };
28394
28452
  const r = await specCommitRange(repoDir, spec.id);
@@ -28629,7 +28687,7 @@ import { spawn as spawn2 } from "node:child_process";
28629
28687
  // src/services/git-remotes.ts
28630
28688
  import { execFile as execFile5 } from "node:child_process";
28631
28689
  import { promisify as promisify5 } from "node:util";
28632
- import { existsSync as existsSync7 } from "node:fs";
28690
+ import { existsSync as existsSync8 } from "node:fs";
28633
28691
  var exec5 = promisify5(execFile5);
28634
28692
  var GIT4 = { maxBuffer: 1 << 24, encoding: "utf8" };
28635
28693
  function parseRemote(url2) {
@@ -28648,7 +28706,7 @@ function prUrl(remoteUrl, branch, base2 = "main") {
28648
28706
  return null;
28649
28707
  }
28650
28708
  async function listRemotes(repoDir) {
28651
- if (!repoDir || !existsSync7(repoDir)) return [];
28709
+ if (!repoDir || !existsSync8(repoDir)) return [];
28652
28710
  try {
28653
28711
  const { stdout } = await exec5("git", ["remote", "-v"], { cwd: repoDir, ...GIT4 });
28654
28712
  const map = /* @__PURE__ */ new Map();
@@ -28689,7 +28747,7 @@ import { promisify as promisify7 } from "node:util";
28689
28747
  // src/services/git-ide.ts
28690
28748
  import { execFile as execFile6 } from "node:child_process";
28691
28749
  import { promisify as promisify6 } from "node:util";
28692
- import { existsSync as existsSync8 } from "node:fs";
28750
+ import { existsSync as existsSync9 } from "node:fs";
28693
28751
  import { resolve as resolve11 } from "node:path";
28694
28752
  var exec6 = promisify6(execFile6);
28695
28753
  var GIT5 = { maxBuffer: 1 << 24 };
@@ -28701,7 +28759,7 @@ function repoAbsPath(repoDir, rel, allowMissingFinal = false) {
28701
28759
  return resolve11(repoDir, rel);
28702
28760
  }
28703
28761
  async function listRepoTree(repoDir, ref = "") {
28704
- if (!repoDir || !existsSync8(repoDir)) return [];
28762
+ if (!repoDir || !existsSync9(repoDir)) return [];
28705
28763
  try {
28706
28764
  const { stdout } = ref ? await exec6("git", ["ls-tree", "-r", "--name-only", "-z", ref], { cwd: repoDir, ...GIT5 }) : await exec6("git", ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], { cwd: repoDir, ...GIT5 });
28707
28765
  return [...new Set(splitZ2(stdout))].sort();
@@ -28727,7 +28785,7 @@ async function currentBranch(repoDir) {
28727
28785
  }
28728
28786
  async function repoStatus(repoDir) {
28729
28787
  const empty = { branch: "", ahead: 0, behind: 0, staged: [], unstaged: [], untracked: [], clean: true };
28730
- if (!repoDir || !existsSync8(repoDir)) return empty;
28788
+ if (!repoDir || !existsSync9(repoDir)) return empty;
28731
28789
  try {
28732
28790
  const { stdout } = await exec6("git", ["status", "--porcelain=v1", "-z", "-b"], { cwd: repoDir, ...GIT5 });
28733
28791
  const parts = stdout.split("\0");
@@ -28760,7 +28818,7 @@ async function repoStatus(repoDir) {
28760
28818
  }
28761
28819
  }
28762
28820
  async function listStashes(repoDir) {
28763
- if (!repoDir || !existsSync8(repoDir)) return [];
28821
+ if (!repoDir || !existsSync9(repoDir)) return [];
28764
28822
  try {
28765
28823
  const { stdout } = await exec6("git", ["stash", "list", `--format=%gd${US}%s${US}%aI`], { cwd: repoDir, ...GIT5 });
28766
28824
  return stdout.split("\n").filter(Boolean).map((line) => {
@@ -28772,7 +28830,7 @@ async function listStashes(repoDir) {
28772
28830
  }
28773
28831
  }
28774
28832
  async function listGraph(repoDir, limit = 200, opts = {}) {
28775
- if (!repoDir || !existsSync8(repoDir)) return { commits: [], current: "", total: 0 };
28833
+ if (!repoDir || !existsSync9(repoDir)) return { commits: [], current: "", total: 0 };
28776
28834
  try {
28777
28835
  const fmt = ["%H", "%P", "%an", "%aI", "%s", "%D"].join(US);
28778
28836
  const refArgs = opts.branches?.length ? ["--end-of-options", ...opts.branches] : [
@@ -28842,7 +28900,7 @@ async function changedOf(repoDir, sha) {
28842
28900
  }
28843
28901
  async function compareCommits(repoDir, from, to) {
28844
28902
  const empty = { from, to, changed: [] };
28845
- if (!repoDir || !existsSync8(repoDir)) return empty;
28903
+ if (!repoDir || !existsSync9(repoDir)) return empty;
28846
28904
  try {
28847
28905
  const [num, name2] = await Promise.all([
28848
28906
  exec6("git", ["diff", "--numstat", "-z", "--no-renames", "--end-of-options", from, to], { cwd: repoDir, ...GIT5 }),
@@ -28854,7 +28912,7 @@ async function compareCommits(repoDir, from, to) {
28854
28912
  }
28855
28913
  }
28856
28914
  async function searchCommits(repoDir, q, by = "all") {
28857
- if (!repoDir || !existsSync8(repoDir) || !q) return [];
28915
+ if (!repoDir || !existsSync9(repoDir) || !q) return [];
28858
28916
  const run2 = async (extra) => {
28859
28917
  try {
28860
28918
  return (await exec6("git", ["log", "--all", "--format=%H", ...extra], { cwd: repoDir, ...GIT5 })).stdout.split("\n").filter(Boolean);
@@ -29101,7 +29159,7 @@ async function runGitOp(repoDir, op) {
29101
29159
  }
29102
29160
  }
29103
29161
  async function workingStatus(repoDir) {
29104
- if (!repoDir || !existsSync8(repoDir)) return { branch: "", staged: [], unstaged: [] };
29162
+ if (!repoDir || !existsSync9(repoDir)) return { branch: "", staged: [], unstaged: [] };
29105
29163
  const [branch, staged, unstaged] = await Promise.all([
29106
29164
  currentBranch(repoDir),
29107
29165
  changedFiles(repoDir, ["--cached"]),
@@ -29110,7 +29168,7 @@ async function workingStatus(repoDir) {
29110
29168
  return { branch, staged, unstaged };
29111
29169
  }
29112
29170
  async function workingFileDiff(repoDir, path2, staged) {
29113
- if (!repoDir || !existsSync8(repoDir)) return null;
29171
+ if (!repoDir || !existsSync9(repoDir)) return null;
29114
29172
  repoAbsPath(repoDir, path2, true);
29115
29173
  const changed = staged ? await changedFiles(repoDir, ["--cached"]) : await withTempIndex(repoDir, (env) => changedFiles(repoDir, [], env));
29116
29174
  const cf = changed.find((c) => c.path === path2);
@@ -29588,15 +29646,15 @@ async function finishGraphOp(reply, id, repoDir, r, verb) {
29588
29646
  // src/routes/fs.ts
29589
29647
  import { readdir } from "node:fs/promises";
29590
29648
  import { homedir as homedir4 } from "node:os";
29591
- import { resolve as resolve12, dirname as dirname8, join as join7 } from "node:path";
29649
+ import { resolve as resolve12, dirname as dirname9, join as join8 } from "node:path";
29592
29650
  async function fs_default(app2) {
29593
29651
  app2.get("/fs/browse", async (req, reply) => {
29594
29652
  const q = req.query.path;
29595
29653
  const dir2 = q && q.trim() ? resolve12(q.trim()) : homedir4();
29596
29654
  try {
29597
29655
  const ents = await readdir(dir2, { withFileTypes: true });
29598
- const entries = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join7(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
29599
- const parent = dirname8(dir2);
29656
+ const entries = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join8(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
29657
+ const parent = dirname9(dir2);
29600
29658
  return { path: dir2, parent: parent === dir2 ? null : parent, entries };
29601
29659
  } catch {
29602
29660
  return reply.code(400).send({ error: `tak bisa membaca folder "${dir2}"` });
@@ -29609,7 +29667,7 @@ init_db();
29609
29667
  init_src();
29610
29668
  init_src2();
29611
29669
  init_session_phases();
29612
- import { existsSync as existsSync9 } from "node:fs";
29670
+ import { existsSync as existsSync10 } from "node:fs";
29613
29671
  init_settings2();
29614
29672
  init_codex_trust();
29615
29673
 
@@ -29930,11 +29988,32 @@ function wsControlOrigins(env) {
29930
29988
  function assertWsOrigin(origin, allowed) {
29931
29989
  if (!origin || !allowed.has(origin)) throw new Error("WebSocket Origin rejected");
29932
29990
  }
29991
+ function wsAllowlistFor(configured, host2, env) {
29992
+ if (configured.size > 0 || env.NODE_ENV === "production") return configured;
29993
+ const value = host2?.trim().toLowerCase();
29994
+ if (!value) return configured;
29995
+ let parsed;
29996
+ try {
29997
+ parsed = new URL(`http://${value}`);
29998
+ } catch {
29999
+ return configured;
30000
+ }
30001
+ if (!parsed.host || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) return configured;
30002
+ const hosts = /* @__PURE__ */ new Set([parsed.host]);
30003
+ if (parsed.port === "80" || parsed.port === "443") hosts.add(parsed.hostname);
30004
+ const allowed = /* @__PURE__ */ new Set();
30005
+ for (const candidate of hosts) {
30006
+ allowed.add(`http://${candidate}`);
30007
+ allowed.add(`https://${candidate}`);
30008
+ }
30009
+ return allowed;
30010
+ }
29933
30011
  function admitBrowserWs(req, target2, allowedOrigins) {
29934
30012
  const origin = typeof req.headers.origin === "string" ? req.headers.origin : void 0;
29935
30013
  if (!origin && process.env.NODE_ENV === "test" && allowedOrigins.size === 0)
29936
30014
  return { kind: "test", id: "test" };
29937
- assertWsOrigin(origin, allowedOrigins);
30015
+ const host2 = typeof req.headers.host === "string" ? req.headers.host : void 0;
30016
+ assertWsOrigin(origin, wsAllowlistFor(allowedOrigins, host2, process.env));
29938
30017
  const token = ticketFromProtocol(req.headers["sec-websocket-protocol"]);
29939
30018
  if (!token) throw new Error("WebSocket ticket required");
29940
30019
  const principal = consumeWsTicket(token, target2);
@@ -30022,7 +30101,7 @@ init_db();
30022
30101
  import { execFile as execFile8 } from "node:child_process";
30023
30102
  import { readdir as readdir2, rm as rm2 } from "node:fs/promises";
30024
30103
  import { promisify as promisify9 } from "node:util";
30025
- import { join as join8, resolve as resolve14 } from "node:path";
30104
+ import { join as join9, resolve as resolve14 } from "node:path";
30026
30105
  var TICK_MS = 6e4;
30027
30106
  var trashDirOf = (repoDir) => resolve14(repoDir, ".worktrees", ".trash");
30028
30107
  var exec8 = promisify9(execFile8);
@@ -30081,7 +30160,7 @@ async function sweepRepo(repoDir, projectId, deps = prodReaperDeps) {
30081
30160
  }
30082
30161
  let removed = 0;
30083
30162
  for (const entry of entries) {
30084
- const path2 = join8(dir2, entry);
30163
+ const path2 = join9(dir2, entry);
30085
30164
  const known = pending.get(path2);
30086
30165
  const row = known ?? { path: path2, repoDir, projectId, entry, sessionId: sessionIdOf(entry), since: Date.now() };
30087
30166
  pending.set(path2, row);
@@ -30401,7 +30480,7 @@ async function terminal_default(app2, opts) {
30401
30480
  if (!s2) return { ok: false, code: 404, msg: "not found" };
30402
30481
  const repoDir = await resolveRepoDir(s2.projectId);
30403
30482
  if (!repoDir) return { ok: false, code: 409, msg: "project belum punya repoDir" };
30404
- if (!s2.cwd.includes("/.worktrees/") || !existsSync9(s2.cwd))
30483
+ if (!s2.cwd.includes("/.worktrees/") || !existsSync10(s2.cwd))
30405
30484
  return { ok: false, code: 409, msg: "belum ada worktree untuk di-review \u2014 jalankan/lanjutkan sesi dulu" };
30406
30485
  return { ok: true, id: s2.id, repoDir };
30407
30486
  };
@@ -30533,7 +30612,7 @@ async function terminal_default(app2, opts) {
30533
30612
  // src/routes/vps.ts
30534
30613
  init_db();
30535
30614
  init_src();
30536
- import { readFileSync as readFileSync10, existsSync as existsSync12 } from "node:fs";
30615
+ import { readFileSync as readFileSync10, existsSync as existsSync13 } from "node:fs";
30537
30616
  import { homedir as homedir6 } from "node:os";
30538
30617
 
30539
30618
  // src/vps/catalog/catalog.data.ts
@@ -30623,7 +30702,7 @@ init_config2();
30623
30702
  import { spawn as spawn3 } from "node:child_process";
30624
30703
  import { mkdtempSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
30625
30704
  import { tmpdir as tmpdir3 } from "node:os";
30626
- import { dirname as dirname9, join as join9 } from "node:path";
30705
+ import { dirname as dirname10, join as join10 } from "node:path";
30627
30706
  var sshBin = () => effectiveStr("HANOMAN_SSH_BIN") ?? "ssh";
30628
30707
  function consoleArgv(t) {
30629
30708
  return [
@@ -30638,8 +30717,8 @@ function consoleArgv(t) {
30638
30717
  ];
30639
30718
  }
30640
30719
  function askpassScript() {
30641
- const dir2 = mkdtempSync(join9(tmpdir3(), "hanoman-askpass-"));
30642
- const path2 = join9(dir2, "askpass.sh");
30720
+ const dir2 = mkdtempSync(join10(tmpdir3(), "hanoman-askpass-"));
30721
+ const path2 = join10(dir2, "askpass.sh");
30643
30722
  writeFileSync4(path2, `#!/bin/sh
30644
30723
  printf '%s' "$HANOMAN_SSH_PASSWORD"
30645
30724
  `, { mode: 448 });
@@ -30679,7 +30758,7 @@ function sshExec(t, remoteCmd, opts = {}) {
30679
30758
  const timer9 = setTimeout(() => p.kill("SIGKILL"), opts.timeoutMs ?? 6e4);
30680
30759
  const done = (r) => {
30681
30760
  clearTimeout(timer9);
30682
- if (askpass) rmSync4(dirname9(askpass), { recursive: true, force: true });
30761
+ if (askpass) rmSync4(dirname10(askpass), { recursive: true, force: true });
30683
30762
  resolve19(r);
30684
30763
  };
30685
30764
  p.stdout.on("data", (d) => {
@@ -30700,16 +30779,16 @@ function sshExec(t, remoteCmd, opts = {}) {
30700
30779
  // src/services/vps-audit.ts
30701
30780
  init_db();
30702
30781
  import { readFileSync as readFileSync7 } from "node:fs";
30703
- import { join as join11 } from "node:path";
30782
+ import { join as join12 } from "node:path";
30704
30783
 
30705
30784
  // src/runner/deps.ts
30706
- import { existsSync as existsSync10 } from "node:fs";
30707
- import { dirname as dirname10, join as join10 } from "node:path";
30785
+ import { existsSync as existsSync11 } from "node:fs";
30786
+ import { dirname as dirname11, join as join11 } from "node:path";
30708
30787
  function repoRootFrom(startDir) {
30709
30788
  let dir2 = startDir;
30710
30789
  for (let i = 0; i < 8; i++) {
30711
- if (existsSync10(join10(dir2, "pnpm-workspace.yaml"))) return dir2;
30712
- const parent = dirname10(dir2);
30790
+ if (existsSync11(join11(dir2, "pnpm-workspace.yaml"))) return dir2;
30791
+ const parent = dirname11(dir2);
30713
30792
  if (parent === dir2) break;
30714
30793
  dir2 = parent;
30715
30794
  }
@@ -30813,7 +30892,7 @@ function parseHealth(out3) {
30813
30892
  return { uptime: h.uptime ?? "", disk: h.disk ?? "", mem: h.mem ?? "", load: h.load ?? "" };
30814
30893
  }
30815
30894
  var target = (v) => ({ host: v.host, port: v.port, user: v.user, keyPath: v.keyPath });
30816
- var scriptPath = (f) => join11(repoRoot(), "server", "scripts", "vps", f);
30895
+ var scriptPath = (f) => join12(repoRoot(), "server", "scripts", "vps", f);
30817
30896
  async function itemStatesOf(vpsId) {
30818
30897
  const rows = await prisma.vpsItemState.findMany({ where: { vpsId } });
30819
30898
  return Object.fromEntries(rows.map((s2) => [s2.itemId, { na: s2.na, attested: s2.attested }]));
@@ -30960,15 +31039,15 @@ async function buildChecklist(vpsId) {
30960
31039
  // src/services/vps-key.ts
30961
31040
  init_config2();
30962
31041
  import { execFileSync as execFileSync2 } from "node:child_process";
30963
- import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync9 } from "node:fs";
31042
+ import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync9 } from "node:fs";
30964
31043
  import { homedir as homedir5 } from "node:os";
30965
- import { join as join12 } from "node:path";
30966
- var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? join12(homedir5(), ".hanoman");
31044
+ import { join as join13 } from "node:path";
31045
+ var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? join13(homedir5(), ".hanoman");
30967
31046
  function ensureHanomanKey() {
30968
31047
  const dir2 = keyDir();
30969
- const privPath = join12(dir2, "id_ed25519");
31048
+ const privPath = join13(dir2, "id_ed25519");
30970
31049
  const pubPath = `${privPath}.pub`;
30971
- if (!existsSync11(privPath)) {
31050
+ if (!existsSync12(privPath)) {
30972
31051
  mkdirSync8(dir2, { recursive: true, mode: 448 });
30973
31052
  execFileSync2(
30974
31053
  "ssh-keygen",
@@ -30995,7 +31074,7 @@ async function bootstrapKey(t, password) {
30995
31074
  init_pty();
30996
31075
  init_settings2();
30997
31076
  function keyMissing(v) {
30998
- return !!v.keyPath && !existsSync12(v.keyPath);
31077
+ return !!v.keyPath && !existsSync13(v.keyPath);
30999
31078
  }
31000
31079
  async function vps_default(app2) {
31001
31080
  app2.get("/vps", async () => prisma.vps.findMany({ orderBy: { createdAt: "asc" } }));
@@ -31207,7 +31286,7 @@ init_config2();
31207
31286
  import { execFileSync as execFileSync3 } from "node:child_process";
31208
31287
  import { readFileSync as readFileSync11 } from "node:fs";
31209
31288
  import { homedir as homedir7 } from "node:os";
31210
- import { join as join13 } from "node:path";
31289
+ import { join as join14 } from "node:path";
31211
31290
  var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
31212
31291
  var TTL_MS2 = 3e4;
31213
31292
  var lastOk = null;
@@ -31220,7 +31299,7 @@ var LABELS = {
31220
31299
  var humanize = (k) => k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
31221
31300
  var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
31222
31301
  function credsFile() {
31223
- return join13(effectiveStr("CLAUDE_CONFIG_DIR") ?? join13(homedir7(), ".claude"), ".credentials.json");
31302
+ return join14(effectiveStr("CLAUDE_CONFIG_DIR") ?? join14(homedir7(), ".claude"), ".credentials.json");
31224
31303
  }
31225
31304
  function readAccessToken() {
31226
31305
  if (process.platform === "darwin" && !effectiveStr("CLAUDE_CONFIG_DIR")) {
@@ -31294,14 +31373,14 @@ async function getLimits() {
31294
31373
  // src/services/codex-limits.ts
31295
31374
  import { open as open3, readdir as readdir3, stat } from "node:fs/promises";
31296
31375
  import { homedir as homedir8 } from "node:os";
31297
- import { join as join14 } from "node:path";
31376
+ import { join as join15 } from "node:path";
31298
31377
  var TTL_MS3 = 3e4;
31299
31378
  var STALE_AFTER_MS = 12 * 36e5;
31300
31379
  var TAIL_BYTES = 512 * 1024;
31301
31380
  var MAX_FILES = 8;
31302
31381
  var cache2 = null;
31303
31382
  var freshUntil2 = 0;
31304
- var codexSessionsDir = () => join14(process.env.CODEX_HOME ?? join14(homedir8(), ".codex"), "sessions");
31383
+ var codexSessionsDir = () => join15(process.env.CODEX_HOME ?? join15(homedir8(), ".codex"), "sessions");
31305
31384
  var UNAVAILABLE = { status: "unavailable", windows: [], fetchedAt: null, plan: null };
31306
31385
  function windowLabel(minutes) {
31307
31386
  if (minutes === 300) return "Sesi 5 jam";
@@ -31334,7 +31413,7 @@ async function recentRollouts(dir2) {
31334
31413
  return [];
31335
31414
  }
31336
31415
  const stamped = await Promise.all(entries.map(async (rel) => {
31337
- const full = join14(dir2, rel);
31416
+ const full = join15(dir2, rel);
31338
31417
  try {
31339
31418
  return { full, mtime: (await stat(full)).mtimeMs };
31340
31419
  } catch {
@@ -31462,7 +31541,7 @@ init_src();
31462
31541
  init_src();
31463
31542
  init_config2();
31464
31543
  import { readFileSync as readFileSync12 } from "node:fs";
31465
- import { resolve as resolve15, dirname as dirname11 } from "node:path";
31544
+ import { resolve as resolve15, dirname as dirname12 } from "node:path";
31466
31545
  import { fileURLToPath as fileURLToPath3 } from "node:url";
31467
31546
  var UPDATE_COMMAND = "npm i -g hanoman@latest";
31468
31547
  function composeUpdate(x) {
@@ -31487,7 +31566,7 @@ var lastFetchAt = 0;
31487
31566
  var lastLatest = null;
31488
31567
  var lastStatus = "unavailable";
31489
31568
  function runningVersion() {
31490
- const here = dirname11(fileURLToPath3(import.meta.url));
31569
+ const here = dirname12(fileURLToPath3(import.meta.url));
31491
31570
  for (const p of [resolve15(here, "build-info.json"), resolve15(here, "../package.json")]) {
31492
31571
  try {
31493
31572
  const v = JSON.parse(readFileSync12(p, "utf8")).version;
@@ -31844,17 +31923,17 @@ init_config2();
31844
31923
  init_safe_outbound_request();
31845
31924
  init_src2();
31846
31925
  import { mkdir as mkdir2, writeFile, readFile as readFile2, unlink as unlink2 } from "node:fs/promises";
31847
- import { join as join15, resolve as resolve16 } from "node:path";
31926
+ import { join as join16, resolve as resolve16 } from "node:path";
31848
31927
  function uploadDir() {
31849
- return resolve16(effectiveStr("HANOMAN_UPLOAD_DIR") ?? join15(resolveHome(), "uploads"));
31928
+ return resolve16(effectiveStr("HANOMAN_UPLOAD_DIR") ?? join16(resolveHome(), "uploads"));
31850
31929
  }
31851
31930
  async function readUpload(storageKey) {
31852
31931
  const safe = storageKey.replace(/[/\\]/g, "");
31853
- return readFile2(join15(uploadDir(), safe));
31932
+ return readFile2(join16(uploadDir(), safe));
31854
31933
  }
31855
31934
  async function readUploadOrFetch(storageKey) {
31856
31935
  const safe = storageKey.replace(/[/\\]/g, "");
31857
- const target2 = join15(uploadDir(), safe);
31936
+ const target2 = join16(uploadDir(), safe);
31858
31937
  try {
31859
31938
  return await readFile2(target2);
31860
31939
  } catch {
@@ -31881,7 +31960,7 @@ async function readUploadOrFetch(storageKey) {
31881
31960
  }
31882
31961
  async function deleteUpload(storageKey) {
31883
31962
  const safe = storageKey.replace(/[/\\]/g, "");
31884
- await unlink2(join15(uploadDir(), safe)).catch(() => {
31963
+ await unlink2(join16(uploadDir(), safe)).catch(() => {
31885
31964
  });
31886
31965
  }
31887
31966
 
@@ -32034,10 +32113,10 @@ init_config2();
32034
32113
  init_src2();
32035
32114
  import { randomUUID as randomUUID4 } from "node:crypto";
32036
32115
  import { mkdir as mkdir3, writeFile as writeFile2, readFile as readFile3, unlink as unlink3 } from "node:fs/promises";
32037
- import { join as join16, resolve as resolve17, basename as basename4 } from "node:path";
32116
+ import { join as join17, resolve as resolve17, basename as basename4 } from "node:path";
32038
32117
  var MAX_TRANSCRIPT_BYTES = 1024 * 1024;
32039
32118
  function transcriptDir() {
32040
- return resolve17(effectiveStr("HANOMAN_TRANSCRIPT_DIR") ?? join16(resolveHome(), "transcripts"));
32119
+ return resolve17(effectiveStr("HANOMAN_TRANSCRIPT_DIR") ?? join17(resolveHome(), "transcripts"));
32041
32120
  }
32042
32121
  function clamp(text) {
32043
32122
  const buf = Buffer.from(text, "utf8");
@@ -32055,20 +32134,20 @@ async function saveTranscript(text) {
32055
32134
  const dir2 = transcriptDir();
32056
32135
  await mkdir3(dir2, { recursive: true, mode: 448 });
32057
32136
  const key = `${randomUUID4()}.log`;
32058
- await writeFile2(join16(dir2, key), body, { encoding: "utf8", mode: 384 });
32137
+ await writeFile2(join17(dir2, key), body, { encoding: "utf8", mode: 384 });
32059
32138
  return { key, bytes: Buffer.byteLength(body, "utf8"), truncated };
32060
32139
  }
32061
32140
  async function readTranscript(key) {
32062
32141
  if (!key) return null;
32063
32142
  try {
32064
- return await readFile3(join16(transcriptDir(), basename4(key)), "utf8");
32143
+ return await readFile3(join17(transcriptDir(), basename4(key)), "utf8");
32065
32144
  } catch {
32066
32145
  return null;
32067
32146
  }
32068
32147
  }
32069
32148
  async function deleteTranscript(key) {
32070
32149
  if (!key) return;
32071
- await unlink3(join16(transcriptDir(), basename4(key))).catch(() => {
32150
+ await unlink3(join17(transcriptDir(), basename4(key))).catch(() => {
32072
32151
  });
32073
32152
  }
32074
32153
 
@@ -32354,7 +32433,7 @@ init_db();
32354
32433
  import { randomUUID as randomUUID6 } from "node:crypto";
32355
32434
  import { spawn as spawn4 } from "node:child_process";
32356
32435
  import { mkdir as mkdir4, rename as rename2, unlink as unlink4, writeFile as writeFile3 } from "node:fs/promises";
32357
- import { basename as basename5, isAbsolute as isAbsolute4, join as join17 } from "node:path";
32436
+ import { basename as basename5, isAbsolute as isAbsolute4, join as join18 } from "node:path";
32358
32437
 
32359
32438
  // ../node_modules/.pnpm/strtok3@10.3.5/node_modules/strtok3/lib/stream/Errors.js
32360
32439
  var defaultMessages = "End-Of-Stream";
@@ -36280,7 +36359,7 @@ var is_default = {
36280
36359
 
36281
36360
  // ../node_modules/.pnpm/sharp@0.35.3_@types+node@20.19.43/node_modules/sharp/dist/sharp.mjs
36282
36361
  var import_detect_libc2 = __toESM(require_detect_libc(), 1);
36283
- import { createRequire } from "node:module";
36362
+ import { createRequire as createRequire2 } from "node:module";
36284
36363
 
36285
36364
  // ../node_modules/.pnpm/sharp@0.35.3_@types+node@20.19.43/node_modules/sharp/dist/libvips.mjs
36286
36365
  var import_semver = __toESM(require_semver2(), 1);
@@ -36704,7 +36783,7 @@ var libvips_default = {
36704
36783
  };
36705
36784
 
36706
36785
  // ../node_modules/.pnpm/sharp@0.35.3_@types+node@20.19.43/node_modules/sharp/dist/sharp.mjs
36707
- var require2 = createRequire(import.meta.url);
36786
+ var require2 = createRequire2(import.meta.url);
36708
36787
  var { version } = package_default;
36709
36788
  var { runtimePlatformArch: runtimePlatformArch2, isUnsupportedNodeRuntime: isUnsupportedNodeRuntime2, prebuiltPlatforms: prebuiltPlatforms2, minimumLibvipsVersion: minimumLibvipsVersion2 } = libvips_default;
36710
36789
  var runtimePlatform = runtimePlatformArch2();
@@ -39585,9 +39664,9 @@ var output_default = (Sharp2) => {
39585
39664
  // ../node_modules/.pnpm/sharp@0.35.3_@types+node@20.19.43/node_modules/sharp/dist/utility.mjs
39586
39665
  var import_detect_libc3 = __toESM(require_detect_libc(), 1);
39587
39666
  import events from "node:events";
39588
- import { createRequire as createRequire2 } from "node:module";
39667
+ import { createRequire as createRequire3 } from "node:module";
39589
39668
  import { availableParallelism } from "node:os";
39590
- var require3 = createRequire2(import.meta.url);
39669
+ var require3 = createRequire3(import.meta.url);
39591
39670
  var runtimePlatform2 = libvips_default.runtimePlatformArch();
39592
39671
  var libvipsVersion = sharp_default.libvipsVersion();
39593
39672
  var format = sharp_default.format();
@@ -39817,10 +39896,10 @@ async function processUpload(input, deps = {}) {
39817
39896
  if (usage2.project + normalized.byteLength > UPLOAD_LIMITS.projectBytes || usage2.global + normalized.byteLength > UPLOAD_LIMITS.globalBytes)
39818
39897
  throw new UploadError("UPLOAD_QUOTA", "storage quota exceeded");
39819
39898
  const storageDir = deps.storageDir ?? uploadDir();
39820
- const quarantineDir = join17(storageDir, ".quarantine");
39899
+ const quarantineDir = join18(storageDir, ".quarantine");
39821
39900
  await mkdir4(quarantineDir, { recursive: true, mode: 448 });
39822
39901
  await mkdir4(storageDir, { recursive: true, mode: 448 });
39823
- const quarantine = join17(quarantineDir, `${randomUUID6()}.upload`);
39902
+ const quarantine = join18(quarantineDir, `${randomUUID6()}.upload`);
39824
39903
  const storageKey = `${randomUUID6()}${type.extension}`;
39825
39904
  await writeFile3(quarantine, normalized, { mode: 384, flag: "wx" });
39826
39905
  try {
@@ -39828,7 +39907,7 @@ async function processUpload(input, deps = {}) {
39828
39907
  const finalUsage = await (deps.usage ?? defaultUsage)(input.projectId);
39829
39908
  if (finalUsage.project + normalized.byteLength > UPLOAD_LIMITS.projectBytes || finalUsage.global + normalized.byteLength > UPLOAD_LIMITS.globalBytes)
39830
39909
  throw new UploadError("UPLOAD_QUOTA", "storage quota exceeded");
39831
- await rename2(quarantine, join17(storageDir, storageKey));
39910
+ await rename2(quarantine, join18(storageDir, storageKey));
39832
39911
  } catch (error) {
39833
39912
  await unlink4(quarantine).catch(() => {
39834
39913
  });
@@ -40753,7 +40832,7 @@ function leadPrompt(q, c) {
40753
40832
 
40754
40833
  // src/services/lead/verdict.ts
40755
40834
  init_src();
40756
- import { existsSync as existsSync13 } from "node:fs";
40835
+ import { existsSync as existsSync14 } from "node:fs";
40757
40836
  import { isAbsolute as isAbsolute5, resolve as resolvePath, relative as relative2 } from "node:path";
40758
40837
  function extractJsonBlock(raw2) {
40759
40838
  const re = /```json\s*\n([\s\S]*?)```/g;
@@ -40791,7 +40870,7 @@ function keepExistingRefs(refs2, repoDir) {
40791
40870
  const full = resolvePath(repoDir, ref);
40792
40871
  const rel = relative2(repoDir, full);
40793
40872
  if (rel.startsWith("..")) continue;
40794
- if (existsSync13(full)) out3.push(ref);
40873
+ if (existsSync14(full)) out3.push(ref);
40795
40874
  }
40796
40875
  return [...new Set(out3)];
40797
40876
  }
@@ -40946,7 +41025,7 @@ import { execFile as execFile10 } from "node:child_process";
40946
41025
  import { randomUUID as randomUUID7 } from "node:crypto";
40947
41026
  import { mkdirSync as mkdirSync9, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "node:fs";
40948
41027
  import { tmpdir as tmpdir4 } from "node:os";
40949
- import { join as join18 } from "node:path";
41028
+ import { join as join19 } from "node:path";
40950
41029
  var binFor = (agent) => agent === "codex" ? effectiveStr("HANOMAN_CODEX_BIN") ?? "codex" : effectiveStr("HANOMAN_CLAUDE_BIN") ?? "claude";
40951
41030
  function leadArgv(o) {
40952
41031
  if (o.agent === "codex") {
@@ -40974,11 +41053,11 @@ function leadProcess(prompt, o, env = process.env) {
40974
41053
  const mode = env.HANOMAN_SESSION_SANDBOX ?? (env.NODE_ENV === "production" ? "required" : "off");
40975
41054
  if (mode === "off") return { file, args: directArgs, cwd: o.cwd, cleanup: () => {
40976
41055
  } };
40977
- const promptDir = join18(tmpdir4(), "hanoman-prompts");
41056
+ const promptDir = join19(tmpdir4(), "hanoman-prompts");
40978
41057
  mkdirSync9(promptDir, { recursive: true, mode: 448 });
40979
- const promptFile = join18(promptDir, `oneshot-${randomUUID7()}`);
41058
+ const promptFile = join19(promptDir, `oneshot-${randomUUID7()}`);
40980
41059
  writeFileSync5(promptFile, prompt, { flag: "wx", mode: 384 });
40981
- const workspace = o.cwd ?? join18(tmpdir4(), `hanoman-oneshot-${randomUUID7()}`);
41060
+ const workspace = o.cwd ?? join19(tmpdir4(), `hanoman-oneshot-${randomUUID7()}`);
40982
41061
  if (!o.cwd) mkdirSync9(workspace, { recursive: false, mode: 448 });
40983
41062
  try {
40984
41063
  const argsWithoutPrompt = directArgs.slice(0, -1);
@@ -42433,7 +42512,7 @@ init_db();
42433
42512
  init_db();
42434
42513
  import { execFile as execFile11 } from "node:child_process";
42435
42514
  import { promisify as promisify11 } from "node:util";
42436
- import { existsSync as existsSync14 } from "node:fs";
42515
+ import { existsSync as existsSync15 } from "node:fs";
42437
42516
 
42438
42517
  // src/services/changelog/scrub.ts
42439
42518
  var MERGE = /^merge\s+(branch|pull request|remote-tracking branch|tag)\b/i;
@@ -42500,7 +42579,7 @@ var US2 = "";
42500
42579
  var RS = "";
42501
42580
  var NO_REPO = "project ini belum ditautkan ke repo di mesin ini";
42502
42581
  var NO_TAG = "repo project ini belum punya tag rilis";
42503
- var usable = (repoDir) => !!repoDir && existsSync14(repoDir);
42582
+ var usable = (repoDir) => !!repoDir && existsSync15(repoDir);
42504
42583
  async function listTags(repoDir) {
42505
42584
  if (!usable(repoDir)) return { tags: [], head: null, reason: NO_REPO };
42506
42585
  try {
@@ -42763,7 +42842,7 @@ init_db();
42763
42842
  init_src();
42764
42843
  import { readFileSync as readFileSync13 } from "node:fs";
42765
42844
  import { homedir as homedir9 } from "node:os";
42766
- import { join as join19 } from "node:path";
42845
+ import { join as join20 } from "node:path";
42767
42846
  var home = () => process.env.HOME || homedir9();
42768
42847
  var readJson2 = (path2) => {
42769
42848
  try {
@@ -42780,7 +42859,7 @@ var serversOf = (node) => {
42780
42859
  var codexServers = () => {
42781
42860
  let text;
42782
42861
  try {
42783
- text = readFileSync13(join19(home(), ".codex", "config.toml"), "utf8");
42862
+ text = readFileSync13(join20(home(), ".codex", "config.toml"), "utf8");
42784
42863
  } catch {
42785
42864
  return [];
42786
42865
  }
@@ -42793,12 +42872,12 @@ var codexServers = () => {
42793
42872
  };
42794
42873
  function mcpServerNames(repoDir) {
42795
42874
  const names = [];
42796
- const claudeJson = readJson2(join19(home(), ".claude.json"));
42875
+ const claudeJson = readJson2(join20(home(), ".claude.json"));
42797
42876
  names.push(...serversOf(claudeJson));
42798
42877
  if (repoDir) {
42799
42878
  const projects = claudeJson?.projects;
42800
42879
  if (projects && typeof projects === "object") names.push(...serversOf(projects[repoDir]));
42801
- names.push(...serversOf(readJson2(join19(repoDir, ".mcp.json"))));
42880
+ names.push(...serversOf(readJson2(join20(repoDir, ".mcp.json"))));
42802
42881
  }
42803
42882
  names.push(...codexServers());
42804
42883
  return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
@@ -44252,13 +44331,13 @@ init_db();
44252
44331
  // src/services/bootstrap.ts
44253
44332
  import { createHash as createHash9, randomBytes as randomBytes9, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
44254
44333
  import { chmod, lstat as lstat2, mkdir as mkdir5, readFile as readFile4, unlink as unlink5, writeFile as writeFile4 } from "node:fs/promises";
44255
- import { join as join20 } from "node:path";
44334
+ import { join as join21 } from "node:path";
44256
44335
  var TTL_MS5 = 15 * 6e4;
44257
44336
  var SETUP_TOKEN_FILE = "setup.token";
44258
44337
  var BootstrapError = class extends Error {
44259
44338
  code = "BOOTSTRAP_PROOF";
44260
44339
  };
44261
- var tokenPath = (home3) => join20(home3, SETUP_TOKEN_FILE);
44340
+ var tokenPath = (home3) => join21(home3, SETUP_TOKEN_FILE);
44262
44341
  async function readStored(home3) {
44263
44342
  try {
44264
44343
  const info = await lstat2(tokenPath(home3));
@@ -44693,7 +44772,7 @@ var PUBLIC = /* @__PURE__ */ new Set([
44693
44772
  "GET /api/agent-integration.md"
44694
44773
  ]);
44695
44774
  function buildApp({ requireAuth = true, agentDocFile, env = process.env } = {}) {
44696
- const docFile = agentDocFile !== void 0 ? agentDocFile : pickGuideFile(dirname12(fileURLToPath4(import.meta.url)), env, existsSync15);
44775
+ const docFile = agentDocFile !== void 0 ? agentDocFile : pickGuideFile(dirname13(fileURLToPath4(import.meta.url)), env, existsSync16);
44697
44776
  const ingress = loadIngressPolicy(env);
44698
44777
  const app2 = Fastify({ logger: false, trustProxy: trustProxyFromEnv(env) });
44699
44778
  app2.addHook("onRequest", async (req, reply) => {
@@ -44791,7 +44870,7 @@ function buildApp({ requireAuth = true, agentDocFile, env = process.env } = {})
44791
44870
  await api.register(client_accounts_default);
44792
44871
  }, { prefix: "/api" });
44793
44872
  if (shouldServeWeb(env)) {
44794
- const dist = pickWebDir(dirname12(fileURLToPath4(import.meta.url)), env, existsSync15);
44873
+ const dist = pickWebDir(dirname13(fileURLToPath4(import.meta.url)), env, existsSync16);
44795
44874
  if (dist) {
44796
44875
  app2.register(fastifyStatic, { root: dist });
44797
44876
  app2.setNotFoundHandler((req, reply) => req.url.startsWith("/api") ? reply.code(404).send({ error: "not found" }) : reply.sendFile("index.html"));