hanoman 0.1.43 → 0.1.44

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
@@ -6092,6 +6092,9 @@ var init_api = __esm({
6092
6092
  ideCompare: (id, from, to) => `${API}/projects/${id}/compare?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`,
6093
6093
  ideCompareFile: (id, from, to, path2) => `${API}/projects/${id}/compare/file?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}&path=${encodeURIComponent(path2)}`,
6094
6094
  ideGit: (id) => `${API}/projects/${id}/git`,
6095
+ // ADR-0121 · operasi berkas Explorer: satu path untuk buat/rename/hapus, satu untuk unggah.
6096
+ ideEntry: (id, path2) => `${API}/projects/${id}/entry${path2 ? `?path=${encodeURIComponent(path2)}` : ""}`,
6097
+ ideUpload: (id) => `${API}/projects/${id}/upload`,
6095
6098
  ideGitMerge: (id) => `${API}/projects/${id}/git/merge`,
6096
6099
  // SPEC-229 · merge git graph isolasi
6097
6100
  // SPEC-234 · status working tree (staged/unstaged) + diff satu file working tree
@@ -6266,11 +6269,11 @@ function isExternalLink(target2) {
6266
6269
  return !target2 || /^(https?:|#|mailto:)/.test(target2);
6267
6270
  }
6268
6271
  function resolveLink(fromRel, target2) {
6269
- const clean = target2.trim().split(/\s+/)[0].split("#")[0].split("\\").join("/");
6270
- if (!clean) return "";
6271
- if (clean.startsWith("/")) return clean.slice(1).split("/").filter((p) => p && p !== ".").join("/");
6272
+ const clean2 = target2.trim().split(/\s+/)[0].split("#")[0].split("\\").join("/");
6273
+ if (!clean2) return "";
6274
+ if (clean2.startsWith("/")) return clean2.slice(1).split("/").filter((p) => p && p !== ".").join("/");
6272
6275
  const dir2 = fromRel.includes("/") ? fromRel.slice(0, fromRel.lastIndexOf("/")) : "";
6273
- const parts = (dir2 ? dir2.split("/") : []).concat(clean.replace(/^\.\//, "").split("/"));
6276
+ const parts = (dir2 ? dir2.split("/") : []).concat(clean2.replace(/^\.\//, "").split("/"));
6274
6277
  const out3 = [];
6275
6278
  for (const p of parts) {
6276
6279
  if (p === "" || p === ".") continue;
@@ -11626,9 +11629,9 @@ var init_config3 = __esm({
11626
11629
  // src/services/telegram/protocol.ts
11627
11630
  import { createHash as createHash2 } from "node:crypto";
11628
11631
  function normalizeTelegramCommand(text) {
11629
- const clean = text.trim();
11630
- const match = clean.match(/^\/([a-z0-9_]+)(?:@[a-z0-9_]+)?(?:\s+([\s\S]*))?$/i);
11631
- if (!match) return clean;
11632
+ const clean2 = text.trim();
11633
+ const match = clean2.match(/^\/([a-z0-9_]+)(?:@[a-z0-9_]+)?(?:\s+([\s\S]*))?$/i);
11634
+ if (!match) return clean2;
11632
11635
  const args = match[2]?.trim().replace(/\s+/g, " ");
11633
11636
  return `/${match[1].toLowerCase()}${args ? ` ${args}` : ""}`;
11634
11637
  }
@@ -11694,19 +11697,19 @@ function inboundDigest(input) {
11694
11697
  return createHash2("sha256").update(`${input.updateId}\0${input.chatId}\0${input.userId}\0${input.kind}\0${input.text}`, "utf8").digest("hex");
11695
11698
  }
11696
11699
  function sanitizeTelegramOutput(text, exactSecrets = []) {
11697
- let clean = text.replace(ANSI, "").replace(CONTROLS, "");
11700
+ let clean2 = text.replace(ANSI, "").replace(CONTROLS, "");
11698
11701
  for (const secret of [...exactSecrets].filter(Boolean).sort((a, b) => b.length - a.length)) {
11699
- clean = clean.split(secret).join("[REDACTED]");
11702
+ clean2 = clean2.split(secret).join("[REDACTED]");
11700
11703
  }
11701
- for (const pattern of CREDENTIAL_PATTERNS) clean = clean.replace(pattern, "[REDACTED]");
11702
- return clean.trim();
11704
+ for (const pattern of CREDENTIAL_PATTERNS) clean2 = clean2.replace(pattern, "[REDACTED]");
11705
+ return clean2.trim();
11703
11706
  }
11704
11707
  function splitTelegramText(text, max = 4096, exactSecrets = []) {
11705
11708
  if (!Number.isInteger(max) || max < 1) throw new Error("max harus integer positif");
11706
- const clean = sanitizeTelegramOutput(text, exactSecrets);
11707
- if (!clean) return [];
11709
+ const clean2 = sanitizeTelegramOutput(text, exactSecrets);
11710
+ if (!clean2) return [];
11708
11711
  const chunks = [];
11709
- let rest = clean;
11712
+ let rest = clean2;
11710
11713
  while (rest.length > max) {
11711
11714
  let cut = max;
11712
11715
  const before2 = rest.charCodeAt(cut - 1);
@@ -18558,11 +18561,11 @@ var require_clean = __commonJS({
18558
18561
  "../node_modules/.pnpm/semver@7.8.5/node_modules/semver/functions/clean.js"(exports, module) {
18559
18562
  "use strict";
18560
18563
  var parse3 = require_parse();
18561
- var clean = (version2, options2) => {
18564
+ var clean2 = (version2, options2) => {
18562
18565
  const s2 = parse3(version2.trim().replace(/^[=v]+/, ""), options2);
18563
18566
  return s2 ? s2.version : null;
18564
18567
  };
18565
- module.exports = clean;
18568
+ module.exports = clean2;
18566
18569
  }
18567
18570
  });
18568
18571
 
@@ -19959,7 +19962,7 @@ var require_semver2 = __commonJS({
19959
19962
  var identifiers = require_identifiers();
19960
19963
  var parse3 = require_parse();
19961
19964
  var valid = require_valid();
19962
- var clean = require_clean();
19965
+ var clean2 = require_clean();
19963
19966
  var inc = require_inc();
19964
19967
  var diff = require_diff();
19965
19968
  var major = require_major();
@@ -19998,7 +20001,7 @@ var require_semver2 = __commonJS({
19998
20001
  module.exports = {
19999
20002
  parse: parse3,
20000
20003
  valid,
20001
- clean,
20004
+ clean: clean2,
20002
20005
  inc,
20003
20006
  diff,
20004
20007
  major,
@@ -24356,7 +24359,7 @@ var require_multipart2 = __commonJS({
24356
24359
  var Busboy = require_main();
24357
24360
  var os = __require("node:os");
24358
24361
  var fp = require_fastify_plugin();
24359
- var { createWriteStream } = __require("node:fs");
24362
+ var { createWriteStream: createWriteStream2 } = __require("node:fs");
24360
24363
  var { unlink: unlink6 } = __require("node:fs/promises");
24361
24364
  var path2 = __require("node:path");
24362
24365
  var { generateId } = require_generateId();
@@ -24736,7 +24739,7 @@ var require_multipart2 = __commonJS({
24736
24739
  continue;
24737
24740
  }
24738
24741
  const filepath = path2.join(tmpdir5, generateId() + path2.extname(part.filename || "file" + i++));
24739
- const target2 = createWriteStream(filepath);
24742
+ const target2 = createWriteStream2(filepath);
24740
24743
  try {
24741
24744
  this.tmpUploads.push(filepath);
24742
24745
  await pump2(part.file, target2);
@@ -24983,6 +24986,7 @@ async function resolveRepoEntry(root, rel, opts = {}) {
24983
24986
  for (let i = 0; i < parts.length; i++) {
24984
24987
  current = join6(current, parts[i]);
24985
24988
  const stat2 = await lstat(current).catch((error) => {
24989
+ if (error.code === "ENOENT" && opts.allowMissingTail) return null;
24986
24990
  if (error.code === "ENOENT" && opts.allowMissingFinal && i === parts.length - 1) return null;
24987
24991
  return denied("komponen tidak ada");
24988
24992
  });
@@ -29373,10 +29377,10 @@ async function listGraph(repoDir, limit = 200, opts = {}) {
29373
29377
  const [sha, parents, author, at, subject, refs2] = line.split(US);
29374
29378
  const tags = [], branchRefs = [];
29375
29379
  for (const d of (refs2 ?? "").split(",").map((r) => r.trim()).filter(Boolean)) {
29376
- const clean = d.replace(/^HEAD -> /, "");
29377
- if (clean === "HEAD") continue;
29378
- if (clean.startsWith("tag: ")) tags.push(clean.slice(5));
29379
- else branchRefs.push(clean);
29380
+ const clean2 = d.replace(/^HEAD -> /, "");
29381
+ if (clean2 === "HEAD") continue;
29382
+ if (clean2.startsWith("tag: ")) tags.push(clean2.slice(5));
29383
+ else branchRefs.push(clean2);
29380
29384
  }
29381
29385
  return {
29382
29386
  sha,
@@ -29852,6 +29856,85 @@ async function deleteBranches(repoDir, names, opts) {
29852
29856
  return { base: report.base, results };
29853
29857
  }
29854
29858
 
29859
+ // src/services/repo-fs.ts
29860
+ import { lstat as lstat2, rename as rename2, rm as rm3 } from "node:fs/promises";
29861
+ import { createWriteStream } from "node:fs";
29862
+ import { join as join9 } from "node:path";
29863
+ import { randomUUID as randomUUID5 } from "node:crypto";
29864
+ import { pipeline } from "node:stream/promises";
29865
+ var EntryExistsError = class extends Error {
29866
+ code = "ENTRY_EXISTS";
29867
+ };
29868
+ var EntryMissingError = class extends Error {
29869
+ code = "ENTRY_MISSING";
29870
+ };
29871
+ var EntryTargetInsideError = class extends Error {
29872
+ code = "ENTRY_TARGET_INSIDE";
29873
+ };
29874
+ function assertNotGit(rel) {
29875
+ if (rel.split(/[\\/]/).includes(".git"))
29876
+ throw new PathContainmentError("repository path ditolak: tidak boleh menyentuh .git");
29877
+ }
29878
+ var clean = (s2) => s2.trim().replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
29879
+ function joinRel(dir2, name2) {
29880
+ const d = clean(dir2), n2 = clean(name2);
29881
+ if (!n2) throw new PathContainmentError("repository path ditolak: nama berkas kosong");
29882
+ return d ? `${d}/${n2}` : n2;
29883
+ }
29884
+ async function entryKind(repoDir, rel) {
29885
+ assertNotGit(rel);
29886
+ const { path: path2 } = await resolveRepoEntry(repoDir, rel, { allowMissingTail: true });
29887
+ const st = await lstat2(path2).catch(() => null);
29888
+ if (!st) return null;
29889
+ return st.isDirectory() ? "dir" : "file";
29890
+ }
29891
+ async function createEntry(repoDir, rel, kind) {
29892
+ if (await entryKind(repoDir, rel)) throw new EntryExistsError(`sudah ada: ${rel}`);
29893
+ await writeRepoFileAtomic(repoDir, kind === "dir" ? `${clean(rel)}/.gitkeep` : rel, "");
29894
+ return { path: rel };
29895
+ }
29896
+ async function renameEntry(repoDir, from, to) {
29897
+ const kind = await entryKind(repoDir, from);
29898
+ if (!kind) throw new EntryMissingError(`tidak ada: ${from}`);
29899
+ if (await entryKind(repoDir, to)) throw new EntryExistsError(`sudah ada: ${to}`);
29900
+ if (kind === "dir" && `${clean(to)}/`.startsWith(`${clean(from)}/`))
29901
+ throw new EntryTargetInsideError("tujuan di dalam sumber");
29902
+ await ensureRepoParents(repoDir, to);
29903
+ const src = await resolveRepoEntry(repoDir, from);
29904
+ const dst = await resolveRepoEntry(repoDir, to, { allowMissingFinal: true });
29905
+ await rename2(src.path, dst.path);
29906
+ return { from, to };
29907
+ }
29908
+ async function deleteEntry(repoDir, rel) {
29909
+ const kind = await entryKind(repoDir, rel);
29910
+ if (!kind) throw new EntryMissingError(`tidak ada: ${rel}`);
29911
+ const { path: path2 } = await resolveRepoEntry(repoDir, rel);
29912
+ await rm3(path2, { recursive: kind === "dir", force: false });
29913
+ return { path: rel, kind };
29914
+ }
29915
+ async function saveUpload(repoDir, rel, source, opts = {}) {
29916
+ assertNotGit(rel);
29917
+ await ensureRepoParents(repoDir, rel);
29918
+ const entry = await resolveRepoEntry(repoDir, rel, { allowMissingFinal: true });
29919
+ const current = await lstat2(entry.path).catch(() => null);
29920
+ if (current && !opts.overwrite) return { status: "exists" };
29921
+ if (current && !current.isFile())
29922
+ throw new PathContainmentError("repository path ditolak: target bukan file regular");
29923
+ const temp = join9(entry.parent, `.hanoman-${randomUUID5()}.tmp`);
29924
+ try {
29925
+ await pipeline(source, createWriteStream(temp, { flags: "wx", mode: 384 }));
29926
+ if (opts.isTruncated?.()) {
29927
+ await rm3(temp, { force: true });
29928
+ return { status: "too-large" };
29929
+ }
29930
+ await rename2(temp, entry.path);
29931
+ return { status: "written" };
29932
+ } catch (error) {
29933
+ await rm3(temp, { force: true });
29934
+ throw error;
29935
+ }
29936
+ }
29937
+
29855
29938
  // src/routes/ide.ts
29856
29939
  async function repoOf(id) {
29857
29940
  const p = await prisma.project.findUnique({ where: { id } });
@@ -29870,6 +29953,19 @@ async function lockInputs(id) {
29870
29953
  sessionBranches: new Set(sessions)
29871
29954
  };
29872
29955
  }
29956
+ function entryError(reply, e) {
29957
+ if (e instanceof EntryMissingError) return reply.code(404).send({ error: "not found" });
29958
+ if (e instanceof EntryExistsError) return reply.code(409).send({ error: "sudah ada" });
29959
+ if (e instanceof EntryTargetInsideError) return reply.code(400).send({ error: "tujuan di dalam sumber" });
29960
+ return reply.code(400).send({ error: e.message });
29961
+ }
29962
+ var UPLOAD_LIMITS = {
29963
+ fileSize: 100 * 1024 * 1024,
29964
+ files: 1e3,
29965
+ fields: 10,
29966
+ fieldSize: 1024 * 1024
29967
+ };
29968
+ var UPLOAD_TOTAL_MAX = 2 * 1024 * 1024 * 1024;
29873
29969
  async function ide_default(app2) {
29874
29970
  app2.get("/projects/:id/tree", async (req, reply) => {
29875
29971
  const repoDir = await repoOf(req.params.id);
@@ -29934,6 +30030,102 @@ async function ide_default(app2) {
29934
30030
  return reply.code(400).send({ error: e.message });
29935
30031
  }
29936
30032
  });
30033
+ app2.post("/projects/:id/entry", async (req, reply) => {
30034
+ const repoDir = await repoOf(req.params.id);
30035
+ if (repoDir === void 0) return reply.code(404).send({ error: "not found" });
30036
+ if (!repoDir) return reply.code(400).send({ error: "project tidak punya repoDir" });
30037
+ const b = req.body;
30038
+ if (!b?.path || typeof b.path !== "string") return reply.code(400).send({ error: "path wajib" });
30039
+ if (b.kind !== "file" && b.kind !== "dir") return reply.code(400).send({ error: "kind harus file atau dir" });
30040
+ try {
30041
+ return reply.code(201).send(await createEntry(repoDir, b.path, b.kind));
30042
+ } catch (e) {
30043
+ return entryError(reply, e);
30044
+ }
30045
+ });
30046
+ app2.patch("/projects/:id/entry", async (req, reply) => {
30047
+ const repoDir = await repoOf(req.params.id);
30048
+ if (repoDir === void 0) return reply.code(404).send({ error: "not found" });
30049
+ if (!repoDir) return reply.code(400).send({ error: "project tidak punya repoDir" });
30050
+ const b = req.body;
30051
+ if (!b?.from || !b?.to || typeof b.from !== "string" || typeof b.to !== "string")
30052
+ return reply.code(400).send({ error: "from & to wajib" });
30053
+ try {
30054
+ return await renameEntry(repoDir, b.from, b.to);
30055
+ } catch (e) {
30056
+ return entryError(reply, e);
30057
+ }
30058
+ });
30059
+ app2.delete("/projects/:id/entry", async (req, reply) => {
30060
+ const repoDir = await repoOf(req.params.id);
30061
+ if (repoDir === void 0) return reply.code(404).send({ error: "not found" });
30062
+ if (!repoDir) return reply.code(400).send({ error: "project tidak punya repoDir" });
30063
+ const { path: path2 } = req.query;
30064
+ if (!path2) return reply.code(400).send({ error: "path wajib" });
30065
+ try {
30066
+ return await deleteEntry(repoDir, path2);
30067
+ } catch (e) {
30068
+ return entryError(reply, e);
30069
+ }
30070
+ });
30071
+ app2.post("/projects/:id/upload", async (req, reply) => {
30072
+ const repoDir = await repoOf(req.params.id);
30073
+ if (repoDir === void 0) return reply.code(404).send({ error: "not found" });
30074
+ if (!repoDir) return reply.code(400).send({ error: "project tidak punya repoDir" });
30075
+ if (!req.isMultipart?.()) return reply.code(400).send({ error: "butuh multipart/form-data" });
30076
+ const totalMax = Number(process.env.HANOMAN_IDE_UPLOAD_MAX_BYTES) || UPLOAD_TOTAL_MAX;
30077
+ let dir2 = "", overwrite = false, manifest = null, seen = 0, total = 0;
30078
+ const written = [];
30079
+ const skipped2 = [];
30080
+ for await (const part of req.parts({ limits: UPLOAD_LIMITS })) {
30081
+ if (part.type === "field") {
30082
+ if (part.fieldname === "dir") dir2 = String(part.value ?? "");
30083
+ else if (part.fieldname === "overwrite") overwrite = part.value === "1" || part.value === "true";
30084
+ else if (part.fieldname === "manifest") {
30085
+ try {
30086
+ const parsed = JSON.parse(String(part.value));
30087
+ if (!Array.isArray(parsed) || parsed.some((p) => typeof p !== "string")) throw new Error("bentuk");
30088
+ manifest = parsed;
30089
+ } catch {
30090
+ return reply.code(400).send({ error: "manifest tak sah" });
30091
+ }
30092
+ }
30093
+ continue;
30094
+ }
30095
+ const name2 = manifest ? manifest[seen] : part.filename;
30096
+ seen++;
30097
+ if (name2 === void 0) {
30098
+ part.file.resume();
30099
+ return reply.code(400).send({ error: "manifest tak cocok dengan berkas" });
30100
+ }
30101
+ let rel = name2;
30102
+ try {
30103
+ rel = joinRel(dir2, name2);
30104
+ if (total >= totalMax) {
30105
+ part.file.resume();
30106
+ skipped2.push({ path: rel, reason: "budget" });
30107
+ continue;
30108
+ }
30109
+ const r = await saveUpload(repoDir, rel, part.file, {
30110
+ overwrite,
30111
+ isTruncated: () => part.file.truncated === true
30112
+ });
30113
+ if (r.status === "written") {
30114
+ written.push(rel);
30115
+ total += Number(part.file.bytesRead ?? 0);
30116
+ } else {
30117
+ part.file.resume();
30118
+ skipped2.push({ path: rel, reason: r.status });
30119
+ }
30120
+ } catch {
30121
+ part.file.resume();
30122
+ skipped2.push({ path: name2, reason: "denied" });
30123
+ }
30124
+ }
30125
+ if (manifest && seen !== manifest.length)
30126
+ return reply.code(400).send({ error: "manifest tak cocok dengan berkas" });
30127
+ return { written, skipped: skipped2 };
30128
+ });
29937
30129
  app2.get("/projects/:id/graph", async (req, reply) => {
29938
30130
  const repoDir = await repoOf(req.params.id);
29939
30131
  if (repoDir === void 0) return reply.code(404).send({ error: "not found" });
@@ -30172,14 +30364,14 @@ async function finishGraphOp(reply, id, repoDir, r, verb) {
30172
30364
  // src/routes/fs.ts
30173
30365
  import { readdir } from "node:fs/promises";
30174
30366
  import { homedir as homedir4 } from "node:os";
30175
- import { resolve as resolve13, dirname as dirname9, join as join9 } from "node:path";
30367
+ import { resolve as resolve13, dirname as dirname9, join as join10 } from "node:path";
30176
30368
  async function fs_default(app2) {
30177
30369
  app2.get("/fs/browse", async (req, reply) => {
30178
30370
  const q = req.query.path;
30179
30371
  const dir2 = q && q.trim() ? resolve13(q.trim()) : homedir4();
30180
30372
  try {
30181
30373
  const ents = await readdir(dir2, { withFileTypes: true });
30182
- const entries = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join9(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
30374
+ const entries = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join10(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
30183
30375
  const parent = dirname9(dir2);
30184
30376
  return { path: dir2, parent: parent === dir2 ? null : parent, entries };
30185
30377
  } catch {
@@ -30625,15 +30817,15 @@ function ownsWorktree(repoDir, cwd) {
30625
30817
  init_src2();
30626
30818
  init_db();
30627
30819
  import { execFile as execFile8 } from "node:child_process";
30628
- import { readdir as readdir2, rm as rm3 } from "node:fs/promises";
30820
+ import { readdir as readdir2, rm as rm4 } from "node:fs/promises";
30629
30821
  import { promisify as promisify9 } from "node:util";
30630
- import { join as join10, resolve as resolve15 } from "node:path";
30822
+ import { join as join11, resolve as resolve15 } from "node:path";
30631
30823
  init_notifications();
30632
30824
  var TICK_MS = 6e4;
30633
30825
  var trashDirOf = (repoDir) => resolve15(repoDir, ".worktrees", ".trash");
30634
30826
  var exec8 = promisify9(execFile8);
30635
30827
  var prodReaperDeps = {
30636
- rm: (path2) => rm3(path2, { recursive: true, force: true }),
30828
+ rm: (path2) => rm4(path2, { recursive: true, force: true }),
30637
30829
  prune: async (repoDir) => {
30638
30830
  try {
30639
30831
  await exec8("git", ["worktree", "prune"], { cwd: repoDir, timeout: 3e4 });
@@ -30687,7 +30879,7 @@ async function sweepRepo(repoDir, projectId, deps = prodReaperDeps) {
30687
30879
  }
30688
30880
  let removed = 0;
30689
30881
  for (const entry of entries) {
30690
- const path2 = join10(dir2, entry);
30882
+ const path2 = join11(dir2, entry);
30691
30883
  const known = pending.get(path2);
30692
30884
  const row = known ?? { path: path2, repoDir, projectId, entry, sessionId: sessionIdOf(entry), since: Date.now() };
30693
30885
  pending.set(path2, row);
@@ -31306,7 +31498,7 @@ init_config2();
31306
31498
  import { spawn as spawn3 } from "node:child_process";
31307
31499
  import { mkdtempSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
31308
31500
  import { tmpdir as tmpdir3 } from "node:os";
31309
- import { dirname as dirname10, join as join11 } from "node:path";
31501
+ import { dirname as dirname10, join as join12 } from "node:path";
31310
31502
  var sshBin = () => effectiveStr("HANOMAN_SSH_BIN") ?? "ssh";
31311
31503
  function consoleArgv(t) {
31312
31504
  return [
@@ -31321,8 +31513,8 @@ function consoleArgv(t) {
31321
31513
  ];
31322
31514
  }
31323
31515
  function askpassScript() {
31324
- const dir2 = mkdtempSync(join11(tmpdir3(), "hanoman-askpass-"));
31325
- const path2 = join11(dir2, "askpass.sh");
31516
+ const dir2 = mkdtempSync(join12(tmpdir3(), "hanoman-askpass-"));
31517
+ const path2 = join12(dir2, "askpass.sh");
31326
31518
  writeFileSync4(path2, `#!/bin/sh
31327
31519
  printf '%s' "$HANOMAN_SSH_PASSWORD"
31328
31520
  `, { mode: 448 });
@@ -31383,15 +31575,15 @@ function sshExec(t, remoteCmd, opts = {}) {
31383
31575
  // src/services/vps-audit.ts
31384
31576
  init_db();
31385
31577
  import { readFileSync as readFileSync7 } from "node:fs";
31386
- import { join as join13 } from "node:path";
31578
+ import { join as join14 } from "node:path";
31387
31579
 
31388
31580
  // src/runner/deps.ts
31389
31581
  import { existsSync as existsSync11 } from "node:fs";
31390
- import { dirname as dirname11, join as join12 } from "node:path";
31582
+ import { dirname as dirname11, join as join13 } from "node:path";
31391
31583
  function repoRootFrom(startDir) {
31392
31584
  let dir2 = startDir;
31393
31585
  for (let i = 0; i < 8; i++) {
31394
- if (existsSync11(join12(dir2, "pnpm-workspace.yaml"))) return dir2;
31586
+ if (existsSync11(join13(dir2, "pnpm-workspace.yaml"))) return dir2;
31395
31587
  const parent = dirname11(dir2);
31396
31588
  if (parent === dir2) break;
31397
31589
  dir2 = parent;
@@ -31497,7 +31689,7 @@ function parseHealth(out3) {
31497
31689
  return { uptime: h.uptime ?? "", disk: h.disk ?? "", mem: h.mem ?? "", load: h.load ?? "" };
31498
31690
  }
31499
31691
  var target = (v) => ({ host: v.host, port: v.port, user: v.user, keyPath: v.keyPath });
31500
- var scriptPath = (f) => join13(repoRoot(), "server", "scripts", "vps", f);
31692
+ var scriptPath = (f) => join14(repoRoot(), "server", "scripts", "vps", f);
31501
31693
  async function itemStatesOf(vpsId) {
31502
31694
  const rows = await prisma.vpsItemState.findMany({ where: { vpsId } });
31503
31695
  return Object.fromEntries(rows.map((s2) => [s2.itemId, { na: s2.na, attested: s2.attested }]));
@@ -31646,11 +31838,11 @@ init_config2();
31646
31838
  import { execFileSync as execFileSync2 } from "node:child_process";
31647
31839
  import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync9 } from "node:fs";
31648
31840
  import { homedir as homedir5 } from "node:os";
31649
- import { join as join14 } from "node:path";
31650
- var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? join14(homedir5(), ".hanoman");
31841
+ import { join as join15 } from "node:path";
31842
+ var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? join15(homedir5(), ".hanoman");
31651
31843
  function ensureHanomanKey() {
31652
31844
  const dir2 = keyDir();
31653
- const privPath = join14(dir2, "id_ed25519");
31845
+ const privPath = join15(dir2, "id_ed25519");
31654
31846
  const pubPath = `${privPath}.pub`;
31655
31847
  if (!existsSync12(privPath)) {
31656
31848
  mkdirSync8(dir2, { recursive: true, mode: 448 });
@@ -31888,7 +32080,7 @@ init_config2();
31888
32080
  import { execFileSync as execFileSync3 } from "node:child_process";
31889
32081
  import { readFileSync as readFileSync11 } from "node:fs";
31890
32082
  import { homedir as homedir7 } from "node:os";
31891
- import { join as join15 } from "node:path";
32083
+ import { join as join16 } from "node:path";
31892
32084
  var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
31893
32085
  var TTL_MS2 = 3e4;
31894
32086
  var lastOk = null;
@@ -31901,7 +32093,7 @@ var LABELS = {
31901
32093
  var humanize = (k) => k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
31902
32094
  var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
31903
32095
  function credsFile() {
31904
- return join15(effectiveStr("CLAUDE_CONFIG_DIR") ?? join15(homedir7(), ".claude"), ".credentials.json");
32096
+ return join16(effectiveStr("CLAUDE_CONFIG_DIR") ?? join16(homedir7(), ".claude"), ".credentials.json");
31905
32097
  }
31906
32098
  function readAccessToken() {
31907
32099
  if (process.platform === "darwin" && !effectiveStr("CLAUDE_CONFIG_DIR")) {
@@ -31975,14 +32167,14 @@ async function getLimits() {
31975
32167
  // src/services/codex-limits.ts
31976
32168
  import { open as open3, readdir as readdir3, stat } from "node:fs/promises";
31977
32169
  import { homedir as homedir8 } from "node:os";
31978
- import { join as join16 } from "node:path";
32170
+ import { join as join17 } from "node:path";
31979
32171
  var TTL_MS3 = 3e4;
31980
32172
  var STALE_AFTER_MS = 12 * 36e5;
31981
32173
  var TAIL_BYTES = 512 * 1024;
31982
32174
  var MAX_FILES = 8;
31983
32175
  var cache2 = null;
31984
32176
  var freshUntil2 = 0;
31985
- var codexSessionsDir = () => join16(process.env.CODEX_HOME ?? join16(homedir8(), ".codex"), "sessions");
32177
+ var codexSessionsDir = () => join17(process.env.CODEX_HOME ?? join17(homedir8(), ".codex"), "sessions");
31986
32178
  var UNAVAILABLE = { status: "unavailable", windows: [], fetchedAt: null, plan: null };
31987
32179
  function windowLabel(minutes) {
31988
32180
  if (minutes === 300) return "Sesi 5 jam";
@@ -32015,7 +32207,7 @@ async function recentRollouts(dir2) {
32015
32207
  return [];
32016
32208
  }
32017
32209
  const stamped = await Promise.all(entries.map(async (rel) => {
32018
- const full = join16(dir2, rel);
32210
+ const full = join17(dir2, rel);
32019
32211
  try {
32020
32212
  return { full, mtime: (await stat(full)).mtimeMs };
32021
32213
  } catch {
@@ -32671,17 +32863,17 @@ async function session_results_default(app2) {
32671
32863
  // src/services/session-history.ts
32672
32864
  init_db();
32673
32865
  init_pty();
32674
- import { randomUUID as randomUUID6 } from "node:crypto";
32866
+ import { randomUUID as randomUUID7 } from "node:crypto";
32675
32867
 
32676
32868
  // src/services/transcript-store.ts
32677
32869
  init_config2();
32678
32870
  init_src2();
32679
- import { randomUUID as randomUUID5 } from "node:crypto";
32871
+ import { randomUUID as randomUUID6 } from "node:crypto";
32680
32872
  import { mkdir as mkdir3, writeFile as writeFile2, readFile as readFile3, unlink as unlink3 } from "node:fs/promises";
32681
- import { join as join17, resolve as resolve17, basename as basename4 } from "node:path";
32873
+ import { join as join18, resolve as resolve17, basename as basename4 } from "node:path";
32682
32874
  var MAX_TRANSCRIPT_BYTES = 1024 * 1024;
32683
32875
  function transcriptDir() {
32684
- return resolve17(effectiveStr("HANOMAN_TRANSCRIPT_DIR") ?? join17(resolveHome(), "transcripts"));
32876
+ return resolve17(effectiveStr("HANOMAN_TRANSCRIPT_DIR") ?? join18(resolveHome(), "transcripts"));
32685
32877
  }
32686
32878
  function clamp(text) {
32687
32879
  const buf = Buffer.from(text, "utf8");
@@ -32698,21 +32890,21 @@ async function saveTranscript(text) {
32698
32890
  const { body, truncated } = clamp(text);
32699
32891
  const dir2 = transcriptDir();
32700
32892
  await mkdir3(dir2, { recursive: true, mode: 448 });
32701
- const key = `${randomUUID5()}.log`;
32702
- await writeFile2(join17(dir2, key), body, { encoding: "utf8", mode: 384 });
32893
+ const key = `${randomUUID6()}.log`;
32894
+ await writeFile2(join18(dir2, key), body, { encoding: "utf8", mode: 384 });
32703
32895
  return { key, bytes: Buffer.byteLength(body, "utf8"), truncated };
32704
32896
  }
32705
32897
  async function readTranscript(key) {
32706
32898
  if (!key) return null;
32707
32899
  try {
32708
- return await readFile3(join17(transcriptDir(), basename4(key)), "utf8");
32900
+ return await readFile3(join18(transcriptDir(), basename4(key)), "utf8");
32709
32901
  } catch {
32710
32902
  return null;
32711
32903
  }
32712
32904
  }
32713
32905
  async function deleteTranscript(key) {
32714
32906
  if (!key) return;
32715
- await unlink3(join17(transcriptDir(), basename4(key))).catch(() => {
32907
+ await unlink3(join18(transcriptDir(), basename4(key))).catch(() => {
32716
32908
  });
32717
32909
  }
32718
32910
 
@@ -32743,7 +32935,7 @@ async function titleFor(specId) {
32743
32935
  async function beginSession(b) {
32744
32936
  await prisma.sessionHistory.create({
32745
32937
  data: {
32746
- id: randomUUID6(),
32938
+ id: randomUUID7(),
32747
32939
  sessionId: b.sessionId,
32748
32940
  projectId: b.projectId,
32749
32941
  specId: b.specId ?? null,
@@ -32998,10 +33190,10 @@ init_notifications();
32998
33190
  init_uploads();
32999
33191
 
33000
33192
  // src/services/upload-pipeline.ts
33001
- import { randomUUID as randomUUID7 } from "node:crypto";
33193
+ import { randomUUID as randomUUID8 } from "node:crypto";
33002
33194
  import { spawn as spawn4 } from "node:child_process";
33003
- import { mkdir as mkdir4, rename as rename2, unlink as unlink4, writeFile as writeFile3 } from "node:fs/promises";
33004
- import { basename as basename5, isAbsolute as isAbsolute4, join as join18 } from "node:path";
33195
+ import { mkdir as mkdir4, rename as rename3, unlink as unlink4, writeFile as writeFile3 } from "node:fs/promises";
33196
+ import { basename as basename5, isAbsolute as isAbsolute4, join as join19 } from "node:path";
33005
33197
 
33006
33198
  // ../node_modules/.pnpm/strtok3@10.3.5/node_modules/strtok3/lib/stream/Errors.js
33007
33199
  var defaultMessages = "End-Of-Stream";
@@ -40365,7 +40557,7 @@ var dist_default = constructor_default;
40365
40557
  // src/services/upload-pipeline.ts
40366
40558
  init_db();
40367
40559
  init_uploads();
40368
- var UPLOAD_LIMITS = {
40560
+ var UPLOAD_LIMITS2 = {
40369
40561
  fileBytes: 5 * 1024 * 1024,
40370
40562
  ticketBytes: 10 * 1024 * 1024,
40371
40563
  projectBytes: 250 * 1024 * 1024,
@@ -40417,7 +40609,7 @@ function scannerFromEnv(path2) {
40417
40609
  const timer9 = setTimeout(() => {
40418
40610
  child.kill("SIGKILL");
40419
40611
  reject2(new UploadError("UPLOAD_SCAN", "scanner timeout"));
40420
- }, UPLOAD_LIMITS.scanMs);
40612
+ }, UPLOAD_LIMITS2.scanMs);
40421
40613
  child.once("error", (error) => {
40422
40614
  clearTimeout(timer9);
40423
40615
  reject2(error);
@@ -40434,9 +40626,9 @@ function safeFilename(input, extension2) {
40434
40626
  return `${stem || "upload"}${extension2}`;
40435
40627
  }
40436
40628
  async function processUpload(input, deps = {}) {
40437
- if (input.buffer.byteLength === 0 || input.buffer.byteLength > UPLOAD_LIMITS.fileBytes)
40629
+ if (input.buffer.byteLength === 0 || input.buffer.byteLength > UPLOAD_LIMITS2.fileBytes)
40438
40630
  throw new UploadError("UPLOAD_QUOTA", "file quota exceeded");
40439
- if (input.ticketBytes + input.buffer.byteLength > UPLOAD_LIMITS.ticketBytes)
40631
+ if (input.ticketBytes + input.buffer.byteLength > UPLOAD_LIMITS2.ticketBytes)
40440
40632
  throw new UploadError("UPLOAD_QUOTA", "ticket quota exceeded");
40441
40633
  const detected = await fileTypeFromBuffer(input.buffer);
40442
40634
  if (!detected || !(detected.mime in TYPES) || detected.mime !== input.clientMime)
@@ -40447,14 +40639,14 @@ async function processUpload(input, deps = {}) {
40447
40639
  let height;
40448
40640
  let normalized;
40449
40641
  try {
40450
- const source = dist_default(input.buffer, { failOn: "error", limitInputPixels: UPLOAD_LIMITS.pixels, sequentialRead: true });
40451
- const metadata2 = await timeout2(source.metadata(), UPLOAD_LIMITS.decodeMs, "UPLOAD_DECODE");
40642
+ const source = dist_default(input.buffer, { failOn: "error", limitInputPixels: UPLOAD_LIMITS2.pixels, sequentialRead: true });
40643
+ const metadata2 = await timeout2(source.metadata(), UPLOAD_LIMITS2.decodeMs, "UPLOAD_DECODE");
40452
40644
  width = metadata2.width ?? 0;
40453
40645
  height = metadata2.height ?? 0;
40454
- if (!width || !height || width > UPLOAD_LIMITS.dimension || height > UPLOAD_LIMITS.dimension || width * height > UPLOAD_LIMITS.pixels) throw new UploadError("UPLOAD_PIXELS", "image dimensions exceed policy");
40646
+ if (!width || !height || width > UPLOAD_LIMITS2.dimension || height > UPLOAD_LIMITS2.dimension || width * height > UPLOAD_LIMITS2.pixels) throw new UploadError("UPLOAD_PIXELS", "image dimensions exceed policy");
40455
40647
  normalized = await timeout2(
40456
- dist_default(input.buffer, { failOn: "error", limitInputPixels: UPLOAD_LIMITS.pixels, sequentialRead: true }).rotate().toFormat(type.format).toBuffer(),
40457
- UPLOAD_LIMITS.decodeMs,
40648
+ dist_default(input.buffer, { failOn: "error", limitInputPixels: UPLOAD_LIMITS2.pixels, sequentialRead: true }).rotate().toFormat(type.format).toBuffer(),
40649
+ UPLOAD_LIMITS2.decodeMs,
40458
40650
  "UPLOAD_DECODE"
40459
40651
  );
40460
40652
  } catch (error) {
@@ -40462,21 +40654,21 @@ async function processUpload(input, deps = {}) {
40462
40654
  throw new UploadError("UPLOAD_DECODE", "image decode failed");
40463
40655
  }
40464
40656
  const usage2 = await (deps.usage ?? defaultUsage)(input.projectId);
40465
- if (usage2.project + normalized.byteLength > UPLOAD_LIMITS.projectBytes || usage2.global + normalized.byteLength > UPLOAD_LIMITS.globalBytes)
40657
+ if (usage2.project + normalized.byteLength > UPLOAD_LIMITS2.projectBytes || usage2.global + normalized.byteLength > UPLOAD_LIMITS2.globalBytes)
40466
40658
  throw new UploadError("UPLOAD_QUOTA", "storage quota exceeded");
40467
40659
  const storageDir = deps.storageDir ?? uploadDir();
40468
- const quarantineDir = join18(storageDir, ".quarantine");
40660
+ const quarantineDir = join19(storageDir, ".quarantine");
40469
40661
  await mkdir4(quarantineDir, { recursive: true, mode: 448 });
40470
40662
  await mkdir4(storageDir, { recursive: true, mode: 448 });
40471
- const quarantine = join18(quarantineDir, `${randomUUID7()}.upload`);
40472
- const storageKey = `${randomUUID7()}${type.extension}`;
40663
+ const quarantine = join19(quarantineDir, `${randomUUID8()}.upload`);
40664
+ const storageKey = `${randomUUID8()}${type.extension}`;
40473
40665
  await writeFile3(quarantine, normalized, { mode: 384, flag: "wx" });
40474
40666
  try {
40475
- await timeout2((deps.scanner ?? scannerFromEnv)(quarantine), UPLOAD_LIMITS.scanMs, "UPLOAD_SCAN");
40667
+ await timeout2((deps.scanner ?? scannerFromEnv)(quarantine), UPLOAD_LIMITS2.scanMs, "UPLOAD_SCAN");
40476
40668
  const finalUsage = await (deps.usage ?? defaultUsage)(input.projectId);
40477
- if (finalUsage.project + normalized.byteLength > UPLOAD_LIMITS.projectBytes || finalUsage.global + normalized.byteLength > UPLOAD_LIMITS.globalBytes)
40669
+ if (finalUsage.project + normalized.byteLength > UPLOAD_LIMITS2.projectBytes || finalUsage.global + normalized.byteLength > UPLOAD_LIMITS2.globalBytes)
40478
40670
  throw new UploadError("UPLOAD_QUOTA", "storage quota exceeded");
40479
- await rename2(quarantine, join18(storageDir, storageKey));
40671
+ await rename3(quarantine, join19(storageDir, storageKey));
40480
40672
  } catch (error) {
40481
40673
  await unlink4(quarantine).catch(() => {
40482
40674
  });
@@ -41594,10 +41786,10 @@ init_config2();
41594
41786
  init_pty();
41595
41787
  init_session_sandbox();
41596
41788
  import { execFile as execFile10 } from "node:child_process";
41597
- import { randomUUID as randomUUID8 } from "node:crypto";
41789
+ import { randomUUID as randomUUID9 } from "node:crypto";
41598
41790
  import { mkdirSync as mkdirSync9, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "node:fs";
41599
41791
  import { tmpdir as tmpdir4 } from "node:os";
41600
- import { join as join19 } from "node:path";
41792
+ import { join as join20 } from "node:path";
41601
41793
  var binFor = (agent) => agent === "codex" ? effectiveStr("HANOMAN_CODEX_BIN") ?? "codex" : effectiveStr("HANOMAN_CLAUDE_BIN") ?? "claude";
41602
41794
  function leadArgv(o) {
41603
41795
  if (o.agent === "codex") {
@@ -41625,11 +41817,11 @@ function leadProcess(prompt, o, env = process.env) {
41625
41817
  const mode = env.HANOMAN_SESSION_SANDBOX ?? (env.NODE_ENV === "production" ? "required" : "off");
41626
41818
  if (mode === "off") return { file, args: directArgs, cwd: o.cwd, cleanup: () => {
41627
41819
  } };
41628
- const promptDir = join19(tmpdir4(), "hanoman-prompts");
41820
+ const promptDir = join20(tmpdir4(), "hanoman-prompts");
41629
41821
  mkdirSync9(promptDir, { recursive: true, mode: 448 });
41630
- const promptFile = join19(promptDir, `oneshot-${randomUUID8()}`);
41822
+ const promptFile = join20(promptDir, `oneshot-${randomUUID9()}`);
41631
41823
  writeFileSync5(promptFile, prompt, { flag: "wx", mode: 384 });
41632
- const workspace = o.cwd ?? join19(tmpdir4(), `hanoman-oneshot-${randomUUID8()}`);
41824
+ const workspace = o.cwd ?? join20(tmpdir4(), `hanoman-oneshot-${randomUUID9()}`);
41633
41825
  if (!o.cwd) mkdirSync9(workspace, { recursive: false, mode: 448 });
41634
41826
  try {
41635
41827
  const argsWithoutPrompt = directArgs.slice(0, -1);
@@ -43418,7 +43610,7 @@ init_db();
43418
43610
  init_src();
43419
43611
  import { readFileSync as readFileSync13 } from "node:fs";
43420
43612
  import { homedir as homedir9 } from "node:os";
43421
- import { join as join20 } from "node:path";
43613
+ import { join as join21 } from "node:path";
43422
43614
  var home = () => process.env.HOME || homedir9();
43423
43615
  var readJson2 = (path2) => {
43424
43616
  try {
@@ -43435,7 +43627,7 @@ var serversOf = (node) => {
43435
43627
  var codexServers = () => {
43436
43628
  let text;
43437
43629
  try {
43438
- text = readFileSync13(join20(home(), ".codex", "config.toml"), "utf8");
43630
+ text = readFileSync13(join21(home(), ".codex", "config.toml"), "utf8");
43439
43631
  } catch {
43440
43632
  return [];
43441
43633
  }
@@ -43448,12 +43640,12 @@ var codexServers = () => {
43448
43640
  };
43449
43641
  function mcpServerNames(repoDir) {
43450
43642
  const names = [];
43451
- const claudeJson = readJson2(join20(home(), ".claude.json"));
43643
+ const claudeJson = readJson2(join21(home(), ".claude.json"));
43452
43644
  names.push(...serversOf(claudeJson));
43453
43645
  if (repoDir) {
43454
43646
  const projects = claudeJson?.projects;
43455
43647
  if (projects && typeof projects === "object") names.push(...serversOf(projects[repoDir]));
43456
- names.push(...serversOf(readJson2(join20(repoDir, ".mcp.json"))));
43648
+ names.push(...serversOf(readJson2(join21(repoDir, ".mcp.json"))));
43457
43649
  }
43458
43650
  names.push(...codexServers());
43459
43651
  return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
@@ -44906,17 +45098,17 @@ init_db();
44906
45098
 
44907
45099
  // src/services/bootstrap.ts
44908
45100
  import { createHash as createHash9, randomBytes as randomBytes9, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
44909
- import { chmod, lstat as lstat2, mkdir as mkdir5, readFile as readFile4, unlink as unlink5, writeFile as writeFile4 } from "node:fs/promises";
44910
- import { join as join21 } from "node:path";
45101
+ import { chmod, lstat as lstat3, mkdir as mkdir5, readFile as readFile4, unlink as unlink5, writeFile as writeFile4 } from "node:fs/promises";
45102
+ import { join as join22 } from "node:path";
44911
45103
  var TTL_MS5 = 15 * 6e4;
44912
45104
  var SETUP_TOKEN_FILE = "setup.token";
44913
45105
  var BootstrapError = class extends Error {
44914
45106
  code = "BOOTSTRAP_PROOF";
44915
45107
  };
44916
- var tokenPath = (home3) => join21(home3, SETUP_TOKEN_FILE);
45108
+ var tokenPath = (home3) => join22(home3, SETUP_TOKEN_FILE);
44917
45109
  async function readStored(home3) {
44918
45110
  try {
44919
- const info = await lstat2(tokenPath(home3));
45111
+ const info = await lstat3(tokenPath(home3));
44920
45112
  if (info.isSymbolicLink() || !info.isFile()) throw new BootstrapError("invalid setup proof");
44921
45113
  const [token, expires] = (await readFile4(tokenPath(home3), "utf8")).trim().split("\n");
44922
45114
  const expiresAt = Date.parse(expires ?? "");
@@ -44929,7 +45121,7 @@ async function readStored(home3) {
44929
45121
  }
44930
45122
  async function ensureSetupToken(home3, now = Date.now()) {
44931
45123
  await mkdir5(home3, { recursive: true, mode: 448 });
44932
- const homeInfo = await lstat2(home3);
45124
+ const homeInfo = await lstat3(home3);
44933
45125
  if (homeInfo.isSymbolicLink() || !homeInfo.isDirectory()) throw new BootstrapError("invalid setup home");
44934
45126
  await chmod(home3, 448);
44935
45127
  try {
@@ -45123,7 +45315,12 @@ var IDE_SUBS = /* @__PURE__ */ new Set([
45123
45315
  "remotes",
45124
45316
  "compare",
45125
45317
  "archive",
45126
- "pr-url"
45318
+ "pr-url",
45319
+ // ADR-0121 · operasi berkas Explorer. `rw()` menurunkan read/write DARI METHOD, jadi
45320
+ // POST/PATCH/DELETE menuntut ide:write — capability yang sudah memberi hak menimpa isi
45321
+ // berkas apa pun lewat PUT /file (hindari kelas bug SPEC-405: prefix tanpa lihat method).
45322
+ "entry",
45323
+ "upload"
45127
45324
  ]);
45128
45325
  function capabilityForRoute(method, path2) {
45129
45326
  const read2 = method === "GET" || method === "HEAD";
@@ -45828,7 +46025,7 @@ init_tap();
45828
46025
  // src/services/webhooks/emit.ts
45829
46026
  init_src();
45830
46027
  init_db();
45831
- import { randomUUID as randomUUID9 } from "node:crypto";
46028
+ import { randomUUID as randomUUID10 } from "node:crypto";
45832
46029
  function skipped(def2, row) {
45833
46030
  if (!def2.skipWhen || !row) return false;
45834
46031
  return row[def2.skipWhen.field] === def2.skipWhen.equals;
@@ -45894,7 +46091,7 @@ async function emitWebhook(i) {
45894
46091
  const targets = matchingEndpoints(type, projectId);
45895
46092
  if (!targets.length) return;
45896
46093
  const name2 = projectId ? (await prisma.project.findUnique({ where: { id: projectId }, select: { name: true } }))?.name ?? null : null;
45897
- const env = buildEnvelope(i, name2, (/* @__PURE__ */ new Date()).toISOString(), `evt_${randomUUID9().replace(/-/g, "")}`);
46094
+ const env = buildEnvelope(i, name2, (/* @__PURE__ */ new Date()).toISOString(), `evt_${randomUUID10().replace(/-/g, "")}`);
45898
46095
  if (!env) return;
45899
46096
  await enqueueEnvelope(env, targets);
45900
46097
  } catch (e) {
@@ -46206,14 +46403,14 @@ init_session_sandbox();
46206
46403
  init_src2();
46207
46404
 
46208
46405
  // src/services/secure-home.ts
46209
- import { chmod as chmod2, lstat as lstat3, mkdir as mkdir6 } from "node:fs/promises";
46406
+ import { chmod as chmod2, lstat as lstat4, mkdir as mkdir6 } from "node:fs/promises";
46210
46407
  import { isAbsolute as isAbsolute6, resolve as resolve18 } from "node:path";
46211
46408
  var HomePermissionError = class extends Error {
46212
46409
  code = "HOME_SYMLINK";
46213
46410
  };
46214
46411
  async function assertNoSymlink(path2, allowMissing) {
46215
46412
  try {
46216
- const info = await lstat3(resolve18(path2));
46413
+ const info = await lstat4(resolve18(path2));
46217
46414
  if (info.isSymbolicLink()) throw new HomePermissionError(`symlink ditolak: ${path2}`);
46218
46415
  } catch (error) {
46219
46416
  if (error.code === "ENOENT" && allowMissing) return;