hanoman 0.9.31 → 0.9.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.9.31",
3
- "sha": "762018b6",
4
- "builtAt": "2026-10-19T06:42:39.892Z"
2
+ "version": "0.9.32",
3
+ "sha": "5946ca19",
4
+ "builtAt": "2026-10-19T07:56:38.058Z"
5
5
  }
package/dist/cli.js CHANGED
@@ -6783,7 +6783,11 @@ var init_session_admission = __esm({
6783
6783
  });
6784
6784
 
6785
6785
  // ../shared/src/dto.ts
6786
- var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zChangeSpecSource, zMarkSpecDone, zMarkSpecReviewed, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, USER_ROLES, zUserRole, zLogin, zSignup, zSetup, zSetupApply, zChangePassword, zAdminPasswordReset, zCreateClientAccount, zUpdateClientAccount, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, PROVISION_PROFILES, COMPONENT_IDS, zProvision, UPDATE_RESTART_EXIT, CONFIG_RESTART_EXIT, MANUAL_RESTART_EXIT, zUpdateApplyBody, MAX_SUBS, zSubPage, zSubLimit, zTopicParams, zEventsClientMsg, zTicketView, zTicketAttachmentView, zSpecAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus, zCronExpr, zCreateCron, zPatchCron, zSchedulerCron, zSchedulerCronRun;
6786
+ function parseExitIntent(text) {
6787
+ const m = /^(\d+) (\d+)\s*$/.exec(text ?? "");
6788
+ return m ? { pid: Number(m[1]), code: Number(m[2]) } : null;
6789
+ }
6790
+ var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zChangeSpecSource, zMarkSpecDone, zMarkSpecReviewed, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, USER_ROLES, zUserRole, zLogin, zSignup, zSetup, zSetupApply, zChangePassword, zAdminPasswordReset, zCreateClientAccount, zUpdateClientAccount, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, PROVISION_PROFILES, COMPONENT_IDS, zProvision, UPDATE_RESTART_EXIT, CONFIG_RESTART_EXIT, MANUAL_RESTART_EXIT, EXIT_INTENT_FILE, zUpdateApplyBody, MAX_SUBS, zSubPage, zSubLimit, zTopicParams, zEventsClientMsg, zTicketView, zTicketAttachmentView, zSpecAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus, zCronExpr, zCreateCron, zPatchCron, zSchedulerCron, zSchedulerCronRun;
6787
6791
  var init_dto = __esm({
6788
6792
  "../shared/src/dto.ts"() {
6789
6793
  "use strict";
@@ -7317,6 +7321,7 @@ var init_dto = __esm({
7317
7321
  UPDATE_RESTART_EXIT = 75;
7318
7322
  CONFIG_RESTART_EXIT = 76;
7319
7323
  MANUAL_RESTART_EXIT = 77;
7324
+ EXIT_INTENT_FILE = "exit-intent";
7320
7325
  zUpdateApplyBody = external_exports.object({ confirm: external_exports.boolean().optional() });
7321
7326
  MAX_SUBS = 16;
7322
7327
  zSubPage = external_exports.number().int().min(1).max(1e4);
@@ -16187,13 +16192,14 @@ __export(start_exports, {
16187
16192
  parseStartArgs: () => parseStartArgs,
16188
16193
  planSupervisorStep: () => planSupervisorStep,
16189
16194
  prismaClientUsable: () => prismaClientUsable,
16195
+ resolveChildExit: () => resolveChildExit,
16190
16196
  serverEnv: () => serverEnv,
16191
16197
  spawnEnv: () => spawnEnv
16192
16198
  });
16193
16199
  import { spawn, execFileSync as execFileSync3 } from "node:child_process";
16194
16200
  import { createRequire as createRequire2 } from "node:module";
16195
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, chmodSync as chmodSync3 } from "node:fs";
16196
- import { dirname as dirname4, resolve as resolvePath } from "node:path";
16201
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, chmodSync as chmodSync3, readFileSync as readFileSync3, rmSync } from "node:fs";
16202
+ import { dirname as dirname4, join as join6, resolve as resolvePath } from "node:path";
16197
16203
  import { fileURLToPath } from "node:url";
16198
16204
  function parseStartArgs(argv) {
16199
16205
  const out = { port: null, host: null, db: null, migrate: true };
@@ -16318,6 +16324,23 @@ function installLatest() {
16318
16324
  function spawnEnv(fileEnv, processEnv, server) {
16319
16325
  return { ...fileEnv, ...processEnv, ...server };
16320
16326
  }
16327
+ function resolveChildExit(exit, intentText) {
16328
+ const intent = parseExitIntent(intentText);
16329
+ if (exit.signal === "SIGKILL" && intent && exit.pid !== void 0 && intent.pid === exit.pid) {
16330
+ return { code: intent.code, forced: true };
16331
+ }
16332
+ return { code: exit.code ?? 0, forced: false };
16333
+ }
16334
+ function takeExitIntent(home) {
16335
+ const path = join6(home, EXIT_INTENT_FILE);
16336
+ try {
16337
+ const text = readFileSync3(path, "utf8");
16338
+ rmSync(path, { force: true });
16339
+ return text;
16340
+ } catch {
16341
+ return null;
16342
+ }
16343
+ }
16321
16344
  function runServer(serverJs, env, fileEnv) {
16322
16345
  const child = spawn(process.execPath, [serverJs], {
16323
16346
  stdio: "inherit",
@@ -16325,9 +16348,9 @@ function runServer(serverJs, env, fileEnv) {
16325
16348
  });
16326
16349
  const handlers = ["SIGINT", "SIGTERM"].map((sig) => [sig, () => child.kill(sig)]);
16327
16350
  for (const [sig, h] of handlers) process.on(sig, h);
16328
- return new Promise((res) => child.on("exit", (code) => {
16351
+ return new Promise((res) => child.on("exit", (code, signal) => {
16329
16352
  for (const [sig, h] of handlers) process.off(sig, h);
16330
- res(code ?? 0);
16353
+ res({ code, signal, pid: child.pid });
16331
16354
  }));
16332
16355
  }
16333
16356
  async function start(argv, ctx) {
@@ -16386,7 +16409,10 @@ ${hint}
16386
16409
  let restartsUsed = 0;
16387
16410
  let configRestartsUsed = 0;
16388
16411
  for (; ; ) {
16389
- const code = await runServer(layout.server, env, readConfigEnv(home));
16412
+ const exit = await runServer(layout.server, env, readConfigEnv(home));
16413
+ const { code, forced } = resolveChildExit(exit, takeExitIntent(home));
16414
+ if (forced) ctx.stderr(`hanoman: server macet saat keluar \u2014 dihentikan watchdog; dilanjutkan sebagai exit ${code} (ADR-0227)
16415
+ `);
16390
16416
  const step = planSupervisorStep(code, restartsUsed, configRestartsUsed);
16391
16417
  if (step.action === "exit") {
16392
16418
  if (code === UPDATE_RESTART_EXIT) {
@@ -37833,7 +37859,7 @@ __export(mcp_exports, {
37833
37859
  agentTokenPath: () => agentTokenPath,
37834
37860
  default: () => mcp
37835
37861
  });
37836
- import { readFileSync as readFileSync3 } from "node:fs";
37862
+ import { readFileSync as readFileSync4 } from "node:fs";
37837
37863
  import { join as join8 } from "node:path";
37838
37864
  async function mcp(argv, ctx) {
37839
37865
  const cfg = resolveMcpConfig(argv, ctx.env, () => tokenFile(ctx.env));
@@ -37868,7 +37894,7 @@ var init_mcp2 = __esm({
37868
37894
  agentTokenPath = (env) => join8(resolveHome(env), "agent-token");
37869
37895
  tokenFile = (env) => {
37870
37896
  try {
37871
- return readFileSync3(agentTokenPath(env), "utf8");
37897
+ return readFileSync4(agentTokenPath(env), "utf8");
37872
37898
  } catch {
37873
37899
  return null;
37874
37900
  }
@@ -38209,11 +38235,11 @@ var init_migrate_pg = __esm({
38209
38235
  });
38210
38236
 
38211
38237
  // src/config.ts
38212
- import { readFileSync as readFileSync4, existsSync as existsSync7 } from "node:fs";
38238
+ import { readFileSync as readFileSync5, existsSync as existsSync7 } from "node:fs";
38213
38239
  import { join as join10 } from "node:path";
38214
38240
  function loadConfig(repoRoot) {
38215
38241
  const p3 = join10(repoRoot, "hanoman.config.json");
38216
- const raw = existsSync7(p3) ? JSON.parse(readFileSync4(p3, "utf8")) : {};
38242
+ const raw = existsSync7(p3) ? JSON.parse(readFileSync5(p3, "utf8")) : {};
38217
38243
  return zHanomanConfig.parse(raw);
38218
38244
  }
38219
38245
  var init_config3 = __esm({
@@ -38240,10 +38266,10 @@ var init_repo = __esm({
38240
38266
  });
38241
38267
 
38242
38268
  // src/docs-model.ts
38243
- import { readFileSync as readFileSync5, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
38269
+ import { readFileSync as readFileSync6, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
38244
38270
  import { join as join12, relative as relative2 } from "node:path";
38245
38271
  function parseIndex(indexPath) {
38246
- const md = readFileSync5(indexPath, "utf8");
38272
+ const md = readFileSync6(indexPath, "utf8");
38247
38273
  const out = /* @__PURE__ */ new Set();
38248
38274
  for (const m of md.matchAll(LINK_RE2)) {
38249
38275
  let t = m[1].trim();
@@ -38291,7 +38317,7 @@ var init_docs_model = __esm({
38291
38317
 
38292
38318
  // src/verify.ts
38293
38319
  import { join as join13 } from "node:path";
38294
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
38320
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
38295
38321
  function scanCoverage(cwd) {
38296
38322
  const { root, docsDir, indexPath } = resolveRepo(cwd);
38297
38323
  const docsRoot = join13(root, docsDir);
@@ -38300,7 +38326,7 @@ function scanCoverage(cwd) {
38300
38326
  const corpus = walkDocs(docsRoot);
38301
38327
  const read = (rel) => {
38302
38328
  try {
38303
- return readFileSync6(join13(docsRoot, rel), "utf8");
38329
+ return readFileSync7(join13(docsRoot, rel), "utf8");
38304
38330
  } catch {
38305
38331
  return null;
38306
38332
  }
@@ -38343,9 +38369,9 @@ var init_docs_scan = __esm({
38343
38369
  });
38344
38370
 
38345
38371
  // src/index-edit.ts
38346
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
38372
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "node:fs";
38347
38373
  function addLink(indexPath, relPath, category) {
38348
- let md = readFileSync7(indexPath, "utf8");
38374
+ let md = readFileSync8(indexPath, "utf8");
38349
38375
  if (md.includes(`(${relPath})`)) return;
38350
38376
  const base2 = relPath.split("/").pop().replace(/\.md$/, "");
38351
38377
  const line = `- [${base2}](${relPath})`;
@@ -38375,7 +38401,7 @@ __export(docs_index_exports, {
38375
38401
  });
38376
38402
  import { parseArgs as parseArgs2 } from "node:util";
38377
38403
  import { join as join14 } from "node:path";
38378
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
38404
+ import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
38379
38405
  async function docs_index_default(args, ctx) {
38380
38406
  const { values } = parseArgs2({ args, options: { check: { type: "boolean" }, fix: { type: "boolean" } }, allowPositionals: true });
38381
38407
  const { root, docsDir, indexPath } = resolveRepo(ctx.cwd);
@@ -38383,7 +38409,7 @@ async function docs_index_default(args, ctx) {
38383
38409
  const corpus = walkDocs(docsRoot);
38384
38410
  const read = (rel) => {
38385
38411
  try {
38386
- return readFileSync8(join14(docsRoot, rel), "utf8");
38412
+ return readFileSync9(join14(docsRoot, rel), "utf8");
38387
38413
  } catch {
38388
38414
  return null;
38389
38415
  }
@@ -38548,10 +38574,10 @@ var pack_exports = {};
38548
38574
  __export(pack_exports, {
38549
38575
  default: () => pack
38550
38576
  });
38551
- import { cpSync, existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync9, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
38577
+ import { cpSync, existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync10, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
38552
38578
  import { dirname as dirname8, join as join16 } from "node:path";
38553
38579
  function depVersions(repo) {
38554
- const read = (p3) => JSON.parse(readFileSync9(join16(repo, p3), "utf8"));
38580
+ const read = (p3) => JSON.parse(readFileSync10(join16(repo, p3), "utf8"));
38555
38581
  const server = read("server/package.json"), cli = read("cli/package.json");
38556
38582
  const pools = [server.dependencies, server.devDependencies, cli.dependencies, cli.devDependencies];
38557
38583
  const out = {};
@@ -38577,7 +38603,7 @@ async function pack(argv, ctx) {
38577
38603
  }
38578
38604
  const outIdx = argv.indexOf("--out");
38579
38605
  const out = outIdx === -1 ? join16(repo, "dist-npm") : join16(repo, argv[outIdx + 1] ?? "dist-npm");
38580
- const version2 = JSON.parse(readFileSync9(join16(repo, "package.json"), "utf8")).version;
38606
+ const version2 = JSON.parse(readFileSync10(join16(repo, "package.json"), "utf8")).version;
38581
38607
  if (!version2) {
38582
38608
  ctx.stderr("pack: root package.json tanpa field version\n");
38583
38609
  return 1;
@@ -38590,7 +38616,7 @@ async function pack(argv, ctx) {
38590
38616
  `);
38591
38617
  return 1;
38592
38618
  }
38593
- rmSync(out, { recursive: true, force: true });
38619
+ rmSync2(out, { recursive: true, force: true });
38594
38620
  mkdirSync3(join16(out, "bin"), { recursive: true });
38595
38621
  for (const item of copyPlan(repo)) {
38596
38622
  if (!existsSync10(item.from)) {
@@ -38629,13 +38655,13 @@ var verify_packed_exports = {};
38629
38655
  __export(verify_packed_exports, {
38630
38656
  default: () => verifyPacked
38631
38657
  });
38632
- import { readFileSync as readFileSync10 } from "node:fs";
38658
+ import { readFileSync as readFileSync11 } from "node:fs";
38633
38659
  import { join as join17 } from "node:path";
38634
38660
  function verifyPacked(_argv, ctx) {
38635
38661
  const path = join17(process.cwd(), "package.json");
38636
38662
  let pkg;
38637
38663
  try {
38638
- pkg = JSON.parse(readFileSync10(path, "utf8"));
38664
+ pkg = JSON.parse(readFileSync11(path, "utf8"));
38639
38665
  } catch {
38640
38666
  ctx.stderr(`verify: tak bisa membaca ${path}
38641
38667
  `);
package/dist/server.js CHANGED
@@ -7615,12 +7615,16 @@ var init_session_admission = __esm({
7615
7615
  function flowForSource(source) {
7616
7616
  return source === "qa" ? "qa" : source === "audit" ? "audit" : source === "goal" ? "goal" : source === "no_effort" ? "no_effort" : "feature";
7617
7617
  }
7618
+ function formatExitIntent(pid, code) {
7619
+ return `${pid} ${code}
7620
+ `;
7621
+ }
7618
7622
  function subKey(topic, params) {
7619
7623
  const canon = {};
7620
7624
  for (const k of Object.keys(params).sort()) if (params[k] !== void 0) canon[k] = params[k];
7621
7625
  return `${topic}|${JSON.stringify(canon)}`;
7622
7626
  }
7623
- var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zChangeSpecSource, zMarkSpecDone, zMarkSpecReviewed, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, isGoalShapedFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, USER_ROLES, zUserRole, zLogin, zSignup, zSetup, zSetupApply, zChangePassword, zAdminPasswordReset, zCreateClientAccount, zUpdateClientAccount, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, PROVISION_PROFILES, COMPONENT_IDS, zProvision, UPDATE_RESTART_EXIT, CONFIG_RESTART_EXIT, MANUAL_RESTART_EXIT, zUpdateApplyBody, MAX_SUBS, zSubPage, zSubLimit, zTopicParams, zEventsClientMsg, zTicketView, zTicketAttachmentView, zSpecAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus, zCronExpr, zCreateCron, zPatchCron, zSchedulerCron, zSchedulerCronRun;
7627
+ var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zChangeSpecSource, zMarkSpecDone, zMarkSpecReviewed, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, isGoalShapedFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, USER_ROLES, zUserRole, zLogin, zSignup, zSetup, zSetupApply, zChangePassword, zAdminPasswordReset, zCreateClientAccount, zUpdateClientAccount, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, PROVISION_PROFILES, COMPONENT_IDS, zProvision, UPDATE_RESTART_EXIT, CONFIG_RESTART_EXIT, MANUAL_RESTART_EXIT, EXIT_INTENT_FILE, zUpdateApplyBody, MAX_SUBS, zSubPage, zSubLimit, zTopicParams, zEventsClientMsg, zTicketView, zTicketAttachmentView, zSpecAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus, zCronExpr, zCreateCron, zPatchCron, zSchedulerCron, zSchedulerCronRun;
7624
7628
  var init_dto = __esm({
7625
7629
  "../shared/src/dto.ts"() {
7626
7630
  "use strict";
@@ -8155,6 +8159,7 @@ var init_dto = __esm({
8155
8159
  UPDATE_RESTART_EXIT = 75;
8156
8160
  CONFIG_RESTART_EXIT = 76;
8157
8161
  MANUAL_RESTART_EXIT = 77;
8162
+ EXIT_INTENT_FILE = "exit-intent";
8158
8163
  zUpdateApplyBody = external_exports.object({ confirm: external_exports.boolean().optional() });
8159
8164
  MAX_SUBS = 16;
8160
8165
  zSubPage = external_exports.number().int().min(1).max(1e4);
@@ -56479,6 +56484,45 @@ var init_device_sockets = __esm({
56479
56484
  }
56480
56485
  });
56481
56486
 
56487
+ // src/services/exit-guard.ts
56488
+ import { spawn as spawn3 } from "node:child_process";
56489
+ import { writeFileSync as writeFileSync15 } from "node:fs";
56490
+ import { join as join29 } from "node:path";
56491
+ function watchdogScript(pid, graceMs) {
56492
+ const secs = Math.max(1, Math.ceil(graceMs / 1e3));
56493
+ return `s=$(ps -o lstart= -p ${pid}); sleep ${secs}; [ -n "$s" ] && [ "$(ps -o lstart= -p ${pid})" = "$s" ] && kill -9 ${pid}`;
56494
+ }
56495
+ function launchWatchdog(script2, env = process.env) {
56496
+ if (env.VITEST) return false;
56497
+ const child = spawn3("/bin/sh", ["-c", script2], { detached: true, stdio: "ignore" });
56498
+ child.on("error", () => {
56499
+ });
56500
+ child.unref();
56501
+ return true;
56502
+ }
56503
+ function exitGuarded(code, d = {}) {
56504
+ const pid = d.pid ?? process.pid;
56505
+ try {
56506
+ const write2 = d.writeIntent ?? ((p3, t) => writeFileSync15(p3, t, { mode: 384 }));
56507
+ write2(join29(d.home ?? resolveHome(), EXIT_INTENT_FILE), formatExitIntent(pid, code));
56508
+ } catch {
56509
+ }
56510
+ try {
56511
+ (d.launch ?? launchWatchdog)(watchdogScript(pid, d.graceMs ?? EXIT_WATCHDOG_MS));
56512
+ } catch {
56513
+ }
56514
+ (d.exit ?? ((c) => process.exit(c)))(code);
56515
+ }
56516
+ var EXIT_WATCHDOG_MS;
56517
+ var init_exit_guard = __esm({
56518
+ "src/services/exit-guard.ts"() {
56519
+ "use strict";
56520
+ init_src();
56521
+ init_src2();
56522
+ EXIT_WATCHDOG_MS = 5e3;
56523
+ }
56524
+ });
56525
+
56482
56526
  // src/services/update.ts
56483
56527
  import { readFileSync as readFileSync16 } from "node:fs";
56484
56528
  import { resolve as resolve20, dirname as dirname13 } from "node:path";
@@ -56514,7 +56558,7 @@ function supervised() {
56514
56558
  }
56515
56559
  function requestRestartForUpdate() {
56516
56560
  const delay2 = effectiveInt("HANOMAN_UPDATE_RESTART_DELAY_MS") ?? 250;
56517
- setTimeout(() => (exiter ?? ((c) => process.exit(c)))(UPDATE_RESTART_EXIT), delay2);
56561
+ setTimeout(() => (exiter ?? exitGuarded)(UPDATE_RESTART_EXIT), delay2);
56518
56562
  }
56519
56563
  async function maybeFetch() {
56520
56564
  if (!effectiveBool("HANOMAN_UPDATE_FETCH")) return;
@@ -56560,6 +56604,7 @@ var init_update = __esm({
56560
56604
  "use strict";
56561
56605
  init_src();
56562
56606
  init_config2();
56607
+ init_exit_guard();
56563
56608
  UPDATE_COMMAND = "npm i -g hanoman@latest --prefer-online";
56564
56609
  RESULT_TTL_MS = 15e3;
56565
56610
  FETCH_TTL_MS = 5 * 6e4;
@@ -62836,15 +62881,15 @@ var init_client2 = __esm({
62836
62881
 
62837
62882
  // src/services/logs/spool.ts
62838
62883
  import { appendFile, mkdir as mkdir7, readFile as readFile7, readdir as readdir7, rm as rm6, stat as stat6 } from "node:fs/promises";
62839
- import { join as join32 } from "node:path";
62884
+ import { join as join33 } from "node:path";
62840
62885
  function spoolDir(lane) {
62841
- return join32(resolveDataDirs().home, "log-spool", lane);
62886
+ return join33(resolveDataDirs().home, "log-spool", lane);
62842
62887
  }
62843
62888
  async function currentSegment(lane) {
62844
62889
  const dir2 = spoolDir(lane);
62845
62890
  await mkdir7(dir2, { recursive: true, mode: 448 });
62846
62891
  if (currentFile && currentBytes < LOG_SPOOL_SEGMENT_BYTES) return currentFile;
62847
- currentFile = join32(dir2, `${Date.now()}-${++segCounter}.ndjson`);
62892
+ currentFile = join33(dir2, `${Date.now()}-${++segCounter}.ndjson`);
62848
62893
  currentBytes = 0;
62849
62894
  return currentFile;
62850
62895
  }
@@ -62866,7 +62911,7 @@ async function readSpoolSegments(lane) {
62866
62911
  const files2 = names.filter((n2) => n2.endsWith(".ndjson")).sort();
62867
62912
  const out4 = [];
62868
62913
  for (const name2 of files2) {
62869
- const file = join32(dir2, name2);
62914
+ const file = join33(dir2, name2);
62870
62915
  const raw = await readFile7(file, "utf8").catch(() => "");
62871
62916
  const entries4 = raw.split("\n").filter(Boolean).map((l) => JSON.parse(l));
62872
62917
  out4.push({ file, entries: entries4 });
@@ -63553,15 +63598,15 @@ var init_codex_version = __esm({
63553
63598
  // src/services/agent-tool-catalog.ts
63554
63599
  import { readFileSync as readFileSync26 } from "node:fs";
63555
63600
  import { homedir as homedir13 } from "node:os";
63556
- import { join as join48 } from "node:path";
63601
+ import { join as join49 } from "node:path";
63557
63602
  function mcpServerNames(repoDir) {
63558
63603
  const names = [];
63559
- const claudeJson = readJson2(join48(home(), ".claude.json"));
63604
+ const claudeJson = readJson2(join49(home(), ".claude.json"));
63560
63605
  names.push(...serversOf(claudeJson));
63561
63606
  if (repoDir) {
63562
63607
  const projects = claudeJson?.projects;
63563
63608
  if (projects && typeof projects === "object") names.push(...serversOf(projects[repoDir]));
63564
- names.push(...serversOf(readJson2(join48(repoDir, ".mcp.json"))));
63609
+ names.push(...serversOf(readJson2(join49(repoDir, ".mcp.json"))));
63565
63610
  }
63566
63611
  names.push(...codexServers());
63567
63612
  return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
@@ -63590,7 +63635,7 @@ var init_agent_tool_catalog = __esm({
63590
63635
  codexServers = () => {
63591
63636
  let text2;
63592
63637
  try {
63593
- text2 = readFileSync26(join48(home(), ".codex", "config.toml"), "utf8");
63638
+ text2 = readFileSync26(join49(home(), ".codex", "config.toml"), "utf8");
63594
63639
  } catch {
63595
63640
  return [];
63596
63641
  }
@@ -70533,8 +70578,8 @@ init_local_binding();
70533
70578
  init_session_id();
70534
70579
  init_uploads();
70535
70580
  import { mkdir as mkdir6, readdir as readdir5, readFile as readFile6, rm as rm4, writeFile as writeFile5 } from "node:fs/promises";
70536
- import { join as join29 } from "node:path";
70537
- var specAttachmentsDir = (repoDir, sessionId2) => join29(repoDir, ".worktrees", ".attachments", sessionId2);
70581
+ import { join as join30 } from "node:path";
70582
+ var specAttachmentsDir = (repoDir, sessionId2) => join30(repoDir, ".worktrees", ".attachments", sessionId2);
70538
70583
  var INDEX = "INDEX.md";
70539
70584
  var humanSize = (n2) => n2 >= 1024 * 1024 ? `${(n2 / 1024 / 1024).toFixed(1)} MB` : n2 >= 1024 ? `${Math.round(n2 / 1024)} KB` : `${n2} B`;
70540
70585
  function uniqueName(taken, filename) {
@@ -70584,19 +70629,19 @@ async function syncSpecAttachmentsDir(specId, projectId) {
70584
70629
  for (const a of rows) {
70585
70630
  let bytes;
70586
70631
  try {
70587
- bytes = await readFile6(join29(uploadDir(), a.storageKey));
70632
+ bytes = await readFile6(join30(uploadDir(), a.storageKey));
70588
70633
  } catch {
70589
70634
  continue;
70590
70635
  }
70591
70636
  const filename = uniqueName(taken, a.filename);
70592
- const path = join29(dir2, filename);
70637
+ const path = join30(dir2, filename);
70593
70638
  await writeFile5(path, bytes, { mode: 384 });
70594
70639
  items.push({ filename, mimeType: a.mimeType, size: a.size, path });
70595
70640
  }
70596
- await writeFile5(join29(dir2, INDEX), renderIndex(specId, items), { mode: 384 });
70641
+ await writeFile5(join30(dir2, INDEX), renderIndex(specId, items), { mode: 384 });
70597
70642
  const keep = /* @__PURE__ */ new Set([INDEX, ...items.map((a) => a.filename)]);
70598
70643
  for (const name2 of await readdir5(dir2)) {
70599
- if (!keep.has(name2)) await rm4(join29(dir2, name2), { recursive: true, force: true }).catch(() => {
70644
+ if (!keep.has(name2)) await rm4(join30(dir2, name2), { recursive: true, force: true }).catch(() => {
70600
70645
  });
70601
70646
  }
70602
70647
  return items;
@@ -74042,7 +74087,7 @@ async function docs_default(app3) {
74042
74087
  // src/routes/ide.ts
74043
74088
  init_src2();
74044
74089
  import { basename as basename10 } from "node:path";
74045
- import { spawn as spawn3 } from "node:child_process";
74090
+ import { spawn as spawn4 } from "node:child_process";
74046
74091
 
74047
74092
  // src/services/git-remotes.ts
74048
74093
  import { execFile as execFile14 } from "node:child_process";
@@ -74112,7 +74157,7 @@ init_codex_trust();
74112
74157
  // src/services/repo-fs.ts
74113
74158
  import { lstat as lstat2, rename as rename4, rm as rm5 } from "node:fs/promises";
74114
74159
  import { createWriteStream } from "node:fs";
74115
- import { join as join30 } from "node:path";
74160
+ import { join as join31 } from "node:path";
74116
74161
  import { randomUUID as randomUUID11 } from "node:crypto";
74117
74162
  import { pipeline } from "node:stream/promises";
74118
74163
  var EntryExistsError = class extends Error {
@@ -74173,7 +74218,7 @@ async function saveUpload2(repoDir, rel, source, opts2 = {}) {
74173
74218
  if (current && !opts2.overwrite) return { status: "exists" };
74174
74219
  if (current && !current.isFile())
74175
74220
  throw new PathContainmentError("repository path ditolak: target bukan file regular");
74176
- const temp = join30(entry2.parent, `.hanoman-${randomUUID11()}.tmp`);
74221
+ const temp = join31(entry2.parent, `.hanoman-${randomUUID11()}.tmp`);
74177
74222
  try {
74178
74223
  await pipeline(source, createWriteStream(temp, { flags: "wx", mode: 384 }));
74179
74224
  if (opts2.isTruncated?.()) {
@@ -74457,7 +74502,7 @@ async function ide_default(app3) {
74457
74502
  const ref = q2.ref || "HEAD";
74458
74503
  if (!/^[\w./@{}-]+$/.test(ref)) return reply.code(400).send({ error: "ref tidak valid" });
74459
74504
  const fmt = q2.format === "tar" ? "tar" : "zip";
74460
- const child = spawn3("git", ["archive", `--format=${fmt}`, "--end-of-options", ref], { cwd: repoDir });
74505
+ const child = spawn4("git", ["archive", `--format=${fmt}`, "--end-of-options", ref], { cwd: repoDir });
74461
74506
  reply.header("content-type", fmt === "zip" ? "application/zip" : "application/x-tar");
74462
74507
  reply.header("content-disposition", `attachment; filename="${basename10(repoDir)}-${ref.replace(/[^\w.-]/g, "_")}.${fmt}"`);
74463
74508
  return reply.send(child.stdout);
@@ -74664,14 +74709,14 @@ async function finishGraphOp(reply, id2, repoDir, r, verb) {
74664
74709
  // src/routes/fs.ts
74665
74710
  import { readdir as readdir6 } from "node:fs/promises";
74666
74711
  import { homedir as homedir6 } from "node:os";
74667
- import { resolve as resolve22, dirname as dirname14, join as join31 } from "node:path";
74712
+ import { resolve as resolve22, dirname as dirname14, join as join32 } from "node:path";
74668
74713
  async function fs_default(app3) {
74669
74714
  app3.get("/fs/browse", async (req, reply) => {
74670
74715
  const q2 = req.query.path;
74671
74716
  const dir2 = q2 && q2.trim() ? resolve22(q2.trim()) : homedir6();
74672
74717
  try {
74673
74718
  const ents = await readdir6(dir2, { withFileTypes: true });
74674
- const entries4 = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join31(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
74719
+ const entries4 = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join32(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
74675
74720
  const parent = dirname14(dir2);
74676
74721
  return { path: dir2, parent: parent === dir2 ? null : parent, entries: entries4 };
74677
74722
  } catch {
@@ -74700,11 +74745,11 @@ init_src2();
74700
74745
  init_db();
74701
74746
  import { execFile as execFile15, spawnSync as spawnSync4 } from "node:child_process";
74702
74747
  import { existsSync as existsSync16, readdirSync as readdirSync8, rmSync as rmSync9 } from "node:fs";
74703
- import { join as join33 } from "node:path";
74748
+ import { join as join34 } from "node:path";
74704
74749
  import { promisify as promisify14 } from "node:util";
74705
74750
 
74706
74751
  // src/services/git-auth.ts
74707
- import { chmodSync as chmodSync8, mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "node:fs";
74752
+ import { chmodSync as chmodSync8, mkdirSync as mkdirSync15, writeFileSync as writeFileSync16 } from "node:fs";
74708
74753
  import { tmpdir as tmpdir6 } from "node:os";
74709
74754
  import { dirname as dirname15 } from "node:path";
74710
74755
  var gitAskpassPath = () => `${tmpdir6()}/hanoman-askpass/token.sh`;
@@ -74725,7 +74770,7 @@ var isGithubHttps = (remote) => /^https:\/\/github\.com\//.test(remote);
74725
74770
  function gitAuthEnv(token) {
74726
74771
  const path = gitAskpassPath();
74727
74772
  mkdirSync15(dirname15(path), { recursive: true, mode: 448 });
74728
- writeFileSync15(path, ASKPASS_TOKEN, { mode: 448 });
74773
+ writeFileSync16(path, ASKPASS_TOKEN, { mode: 448 });
74729
74774
  chmodSync8(path, 448);
74730
74775
  return { GIT_ASKPASS: path, GIT_TERMINAL_PROMPT: "0", HANOMAN_GIT_TOKEN: token };
74731
74776
  }
@@ -74767,7 +74812,7 @@ var CheckoutError = class extends Error {
74767
74812
  var CLONE_TIMEOUT_MS = 10 * 6e4;
74768
74813
  var FETCH_TIMEOUT_MS = 2 * 6e4;
74769
74814
  var AUTH_RE = /Authentication failed|could not read (Username|Password)|Invalid username or password|HTTP 40[13]|returned error: 40[13]|Repository not found|Permission denied \(publickey\)/i;
74770
- var checkoutRoot = () => join33(resolveHome(), "projects");
74815
+ var checkoutRoot = () => join34(resolveHome(), "projects");
74771
74816
  var slug = (id2) => id2.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "project";
74772
74817
  var sameRemote = (a, b) => {
74773
74818
  const norm5 = (u) => httpsRemote(u).replace(/\.git$/, "").replace(/\/+$/, "").toLowerCase();
@@ -74801,7 +74846,7 @@ async function doClone(projectId, opts2) {
74801
74846
  if (!project) throw new CheckoutError(`project "${projectId}" tidak ada`, "not-found");
74802
74847
  if (!project.gitRemote) throw new CheckoutError(`project "${projectId}" tidak punya gitRemote untuk di-clone`, "no-remote");
74803
74848
  const remote = project.gitRemote;
74804
- const dest = opts2.dir ?? join33(checkoutRoot(), slug(project.id));
74849
+ const dest = opts2.dir ?? join34(checkoutRoot(), slug(project.id));
74805
74850
  if (existsSync16(dest) && !isEmptyDir(dest)) {
74806
74851
  const origin = originOf2(dest);
74807
74852
  if (origin && sameRemote(origin, remote)) {
@@ -75385,13 +75430,13 @@ init_local_binding();
75385
75430
 
75386
75431
  // src/services/terminal-diag.ts
75387
75432
  import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync16, statSync as statSync4, rmSync as rmSync10 } from "node:fs";
75388
- import { join as join34 } from "node:path";
75433
+ import { join as join35 } from "node:path";
75389
75434
  var DIAG_MAX_BYTES = 2 * 1024 * 1024;
75390
75435
  var KINDS = /* @__PURE__ */ new Set(["key", "comp", "data", "ack", "pred"]);
75391
75436
  var ID = /^[A-Za-z0-9_-]{1,64}$/;
75392
75437
  function diagFile(home3, sessionId2) {
75393
75438
  if (!ID.test(sessionId2)) throw new Error(`id sesi tak sah untuk diag: ${sessionId2}`);
75394
- return join34(home3, "diag", `${sessionId2}.jsonl`);
75439
+ return join35(home3, "diag", `${sessionId2}.jsonl`);
75395
75440
  }
75396
75441
  function usable(ev) {
75397
75442
  if (!ev || typeof ev !== "object") return false;
@@ -75402,7 +75447,7 @@ function appendDiag(home3, sessionId2, events) {
75402
75447
  const file = diagFile(home3, sessionId2);
75403
75448
  const rows = events.filter(usable);
75404
75449
  if (!rows.length) return;
75405
- mkdirSync16(join34(home3, "diag"), { recursive: true });
75450
+ mkdirSync16(join35(home3, "diag"), { recursive: true });
75406
75451
  try {
75407
75452
  if (statSync4(file).size > DIAG_MAX_BYTES) rmSync10(file);
75408
75453
  } catch {
@@ -75496,7 +75541,7 @@ init_session_event_spool();
75496
75541
  init_ingress_policy();
75497
75542
  import { constants as constants4 } from "node:fs";
75498
75543
  import { mkdir as mkdir8, open as open3, readdir as readdir8, rm as rm7 } from "node:fs/promises";
75499
- import { join as join35 } from "node:path";
75544
+ import { join as join36 } from "node:path";
75500
75545
  var MAX_EVENT_BYTES = 1e6;
75501
75546
  var MAX_FILES_PER_DRAIN = 1e3;
75502
75547
  var SESSION_ID_RE = /^[a-z0-9_-]+$/;
@@ -75547,7 +75592,7 @@ async function drainSpool(app3, root, observation, host2) {
75547
75592
  let readBuffer;
75548
75593
  for (const session of await readdir8(root, { withFileTypes: true })) {
75549
75594
  if (!session.isDirectory() || !SESSION_ID_RE.test(session.name)) continue;
75550
- const dir2 = join35(root, session.name);
75595
+ const dir2 = join36(root, session.name);
75551
75596
  let entries4;
75552
75597
  try {
75553
75598
  entries4 = await readdir8(dir2, { withFileTypes: true });
@@ -75559,7 +75604,7 @@ async function drainSpool(app3, root, observation, host2) {
75559
75604
  if (!entry2.isFile() || !entry2.name.endsWith(".json")) continue;
75560
75605
  const eventAt = /^(\d{1,15})-/.exec(entry2.name)?.[1];
75561
75606
  if (++examined > MAX_FILES_PER_DRAIN) return delivered;
75562
- const path = join35(dir2, entry2.name);
75607
+ const path = join36(dir2, entry2.name);
75563
75608
  let payload;
75564
75609
  let handle;
75565
75610
  try {
@@ -76586,9 +76631,9 @@ init_session_sandbox();
76586
76631
  init_session_sandbox();
76587
76632
  import { execFile as execFile16 } from "node:child_process";
76588
76633
  import { randomUUID as randomUUID13 } from "node:crypto";
76589
- import { mkdirSync as mkdirSync17, rmSync as rmSync11, writeFileSync as writeFileSync16 } from "node:fs";
76634
+ import { mkdirSync as mkdirSync17, rmSync as rmSync11, writeFileSync as writeFileSync17 } from "node:fs";
76590
76635
  import { constants as constants5, tmpdir as tmpdir7 } from "node:os";
76591
- import { join as join36 } from "node:path";
76636
+ import { join as join37 } from "node:path";
76592
76637
  var binFor = (agent) => agent === "antigravity" ? antigravityBin() : agent === "codex" ? effectiveStr("HANOMAN_CODEX_BIN") ?? "codex" : effectiveStr("HANOMAN_CLAUDE_BIN") ?? "claude";
76593
76638
  function leadArgv(o) {
76594
76639
  if (o.agent === "antigravity") return [
@@ -76626,11 +76671,11 @@ function leadProcess(prompt, o, env = process.env) {
76626
76671
  const mode = env.HANOMAN_SESSION_SANDBOX ?? (resolveHardening(env) ? "required" : "off");
76627
76672
  if (mode === "off") return { file, args: directArgs, cwd: o.cwd, cleanup: () => {
76628
76673
  } };
76629
- const promptDir = join36(tmpdir7(), "hanoman-prompts");
76674
+ const promptDir = join37(tmpdir7(), "hanoman-prompts");
76630
76675
  mkdirSync17(promptDir, { recursive: true, mode: 448 });
76631
- const promptFile = join36(promptDir, `oneshot-${randomUUID13()}`);
76632
- writeFileSync16(promptFile, prompt, { flag: "wx", mode: 384 });
76633
- const workspace = o.cwd ?? join36(tmpdir7(), `hanoman-oneshot-${randomUUID13()}`);
76676
+ const promptFile = join37(promptDir, `oneshot-${randomUUID13()}`);
76677
+ writeFileSync17(promptFile, prompt, { flag: "wx", mode: 384 });
76678
+ const workspace = o.cwd ?? join37(tmpdir7(), `hanoman-oneshot-${randomUUID13()}`);
76634
76679
  if (!o.cwd) mkdirSync17(workspace, { recursive: false, mode: 448 });
76635
76680
  try {
76636
76681
  const argsWithoutPrompt = directArgs.slice(0, -1);
@@ -77489,13 +77534,13 @@ async function run2(sessionId2, deps) {
77489
77534
  // src/services/decision-guard.ts
77490
77535
  init_src2();
77491
77536
  init_db();
77492
- import { mkdirSync as mkdirSync18, readFileSync as readFileSync18, readdirSync as readdirSync9, rmSync as rmSync12, statSync as statSync6, writeFileSync as writeFileSync17 } from "node:fs";
77493
- import { join as join37 } from "node:path";
77537
+ import { mkdirSync as mkdirSync18, readFileSync as readFileSync18, readdirSync as readdirSync9, rmSync as rmSync12, statSync as statSync6, writeFileSync as writeFileSync18 } from "node:fs";
77538
+ import { join as join38 } from "node:path";
77494
77539
  var SESSION_HEADER = "x-hanoman-session";
77495
77540
  var SAFE_ID = /^[A-Za-z0-9._-]{1,200}$/;
77496
77541
  var launchers = /* @__PURE__ */ new Map();
77497
77542
  var STALE_MS = 30 * 24 * 60 * 6e4;
77498
- var launcherDir = (env = process.env) => join37(resolveHome(env), "session-launchers");
77543
+ var launcherDir = (env = process.env) => join38(resolveHome(env), "session-launchers");
77499
77544
  function recordLauncher(sessionId2, agentId, env = process.env) {
77500
77545
  if (!SAFE_ID.test(sessionId2)) return;
77501
77546
  const known2 = launchersFor(sessionId2, env);
@@ -77503,11 +77548,11 @@ function recordLauncher(sessionId2, agentId, env = process.env) {
77503
77548
  try {
77504
77549
  const dir2 = launcherDir(env);
77505
77550
  mkdirSync18(dir2, { recursive: true, mode: 448 });
77506
- writeFileSync17(join37(dir2, sessionId2), [...known2].join("\n"), { mode: 384 });
77551
+ writeFileSync18(join38(dir2, sessionId2), [...known2].join("\n"), { mode: 384 });
77507
77552
  const now = Date.now();
77508
77553
  for (const f of readdirSync9(dir2)) {
77509
77554
  try {
77510
- if (now - statSync6(join37(dir2, f)).mtimeMs > STALE_MS) rmSync12(join37(dir2, f), { force: true });
77555
+ if (now - statSync6(join38(dir2, f)).mtimeMs > STALE_MS) rmSync12(join38(dir2, f), { force: true });
77511
77556
  } catch {
77512
77557
  }
77513
77558
  }
@@ -77519,7 +77564,7 @@ function launchersFor(sessionId2, env) {
77519
77564
  if (hot) return hot;
77520
77565
  const ids = /* @__PURE__ */ new Set();
77521
77566
  try {
77522
- for (const l of readFileSync18(join37(launcherDir(env), sessionId2), "utf8").split("\n")) if (l.trim()) ids.add(l.trim());
77567
+ for (const l of readFileSync18(join38(launcherDir(env), sessionId2), "utf8").split("\n")) if (l.trim()) ids.add(l.trim());
77523
77568
  } catch {
77524
77569
  }
77525
77570
  launchers.set(sessionId2, ids);
@@ -78417,10 +78462,10 @@ import { readFileSync as readFileSync20 } from "node:fs";
78417
78462
 
78418
78463
  // src/services/vps-ssh.ts
78419
78464
  init_config2();
78420
- import { spawn as spawn4 } from "node:child_process";
78421
- import { mkdtempSync as mkdtempSync4, rmSync as rmSync13, writeFileSync as writeFileSync18 } from "node:fs";
78465
+ import { spawn as spawn5 } from "node:child_process";
78466
+ import { mkdtempSync as mkdtempSync4, rmSync as rmSync13, writeFileSync as writeFileSync19 } from "node:fs";
78422
78467
  import { tmpdir as tmpdir8 } from "node:os";
78423
- import { dirname as dirname16, join as join38 } from "node:path";
78468
+ import { dirname as dirname16, join as join39 } from "node:path";
78424
78469
  var sshBin = () => effectiveStr("HANOMAN_SSH_BIN") ?? "ssh";
78425
78470
  function consoleArgv(t) {
78426
78471
  return [
@@ -78435,9 +78480,9 @@ function consoleArgv(t) {
78435
78480
  ];
78436
78481
  }
78437
78482
  function askpassScript() {
78438
- const dir2 = mkdtempSync4(join38(tmpdir8(), "hanoman-askpass-"));
78439
- const path = join38(dir2, "askpass.sh");
78440
- writeFileSync18(path, `#!/bin/sh
78483
+ const dir2 = mkdtempSync4(join39(tmpdir8(), "hanoman-askpass-"));
78484
+ const path = join39(dir2, "askpass.sh");
78485
+ writeFileSync19(path, `#!/bin/sh
78441
78486
  printf '%s' "$HANOMAN_SSH_PASSWORD"
78442
78487
  `, { mode: 448 });
78443
78488
  return path;
@@ -78471,7 +78516,7 @@ function sshExec(t, remoteCmd, opts2 = {}) {
78471
78516
  HANOMAN_SSH_PASSWORD: opts2.password
78472
78517
  } : process.env;
78473
78518
  return new Promise((resolve25) => {
78474
- const p3 = spawn4(sshBin(), args, { stdio: ["pipe", "pipe", "pipe"], env });
78519
+ const p3 = spawn5(sshBin(), args, { stdio: ["pipe", "pipe", "pipe"], env });
78475
78520
  let out4 = "";
78476
78521
  const timer11 = setTimeout(() => p3.kill("SIGKILL"), opts2.timeoutMs ?? 6e4);
78477
78522
  const done = (r) => {
@@ -78497,16 +78542,16 @@ function sshExec(t, remoteCmd, opts2 = {}) {
78497
78542
  // src/services/vps-audit.ts
78498
78543
  init_db();
78499
78544
  import { existsSync as existsSync20, readFileSync as readFileSync19 } from "node:fs";
78500
- import { join as join40 } from "node:path";
78545
+ import { join as join41 } from "node:path";
78501
78546
  import { fileURLToPath as fileURLToPath4 } from "node:url";
78502
78547
 
78503
78548
  // src/runner/deps.ts
78504
78549
  import { existsSync as existsSync19 } from "node:fs";
78505
- import { dirname as dirname17, join as join39 } from "node:path";
78550
+ import { dirname as dirname17, join as join40 } from "node:path";
78506
78551
  function repoRootFrom(startDir) {
78507
78552
  let dir2 = startDir;
78508
78553
  for (let i = 0; i < 8; i++) {
78509
- if (existsSync19(join39(dir2, "pnpm-workspace.yaml"))) return dir2;
78554
+ if (existsSync19(join40(dir2, "pnpm-workspace.yaml"))) return dir2;
78510
78555
  const parent = dirname17(dir2);
78511
78556
  if (parent === dir2) break;
78512
78557
  dir2 = parent;
@@ -78619,7 +78664,7 @@ var packagedScript = (f) => fileURLToPath4(new URL(`../scripts/vps/${f}`, import
78619
78664
  var moduleDir = () => fileURLToPath4(new URL(".", import.meta.url));
78620
78665
  var scriptPath = (f) => {
78621
78666
  const packed = packagedScript(f);
78622
- return existsSync20(packed) ? packed : join40(repoRoot(moduleDir()), "server", "scripts", "vps", f);
78667
+ return existsSync20(packed) ? packed : join41(repoRoot(moduleDir()), "server", "scripts", "vps", f);
78623
78668
  };
78624
78669
  async function itemStatesOf(vpsId) {
78625
78670
  const rows = await prisma.vpsItemState.findMany({ where: { vpsId } });
@@ -78866,25 +78911,25 @@ init_config2();
78866
78911
  import { execFileSync as execFileSync7 } from "node:child_process";
78867
78912
  import { chmodSync as chmodSync9, copyFileSync, existsSync as existsSync21, mkdirSync as mkdirSync19, readFileSync as readFileSync22, unlinkSync as unlinkSync5 } from "node:fs";
78868
78913
  import { homedir as homedir8 } from "node:os";
78869
- import { join as join41 } from "node:path";
78914
+ import { join as join42 } from "node:path";
78870
78915
  var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? resolveDataDirs().sshKeys;
78871
78916
  var KEY_FILES = ["id_ed25519", "id_ed25519.pub"];
78872
78917
  function adoptLegacyKey(dir2) {
78873
78918
  if (effectiveStr("HANOMAN_SSH_KEY_DIR")) return;
78874
- const legacy = join41(homedir8(), ".hanoman");
78919
+ const legacy = join42(homedir8(), ".hanoman");
78875
78920
  if (legacy === dir2) return;
78876
- if (!KEY_FILES.every((f) => existsSync21(join41(legacy, f)))) return;
78921
+ if (!KEY_FILES.every((f) => existsSync21(join42(legacy, f)))) return;
78877
78922
  mkdirSync19(dir2, { recursive: true, mode: 448 });
78878
78923
  for (const f of KEY_FILES) {
78879
- copyFileSync(join41(legacy, f), join41(dir2, f));
78880
- chmodSync9(join41(dir2, f), 384);
78924
+ copyFileSync(join42(legacy, f), join42(dir2, f));
78925
+ chmodSync9(join42(dir2, f), 384);
78881
78926
  }
78882
- for (const f of KEY_FILES) unlinkSync5(join41(legacy, f));
78927
+ for (const f of KEY_FILES) unlinkSync5(join42(legacy, f));
78883
78928
  console.log(`vps: key SSH dipindah dari ${legacy} ke ${dir2} (SPEC-846 \u2014 satu batas backup)`);
78884
78929
  }
78885
78930
  function ensureHanomanKey() {
78886
78931
  const dir2 = keyDir();
78887
- const privPath = join41(dir2, KEY_FILES[0]);
78932
+ const privPath = join42(dir2, KEY_FILES[0]);
78888
78933
  const pubPath = `${privPath}.pub`;
78889
78934
  if (!existsSync21(privPath)) adoptLegacyKey(dir2);
78890
78935
  if (!existsSync21(privPath)) {
@@ -79197,7 +79242,7 @@ init_config2();
79197
79242
  import { execFileSync as execFileSync8 } from "node:child_process";
79198
79243
  import { readFileSync as readFileSync24 } from "node:fs";
79199
79244
  import { homedir as homedir10, userInfo } from "node:os";
79200
- import { join as join42 } from "node:path";
79245
+ import { join as join43 } from "node:path";
79201
79246
  var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
79202
79247
  var TTL_MS2 = 3e4;
79203
79248
  var lastOk = null;
@@ -79210,7 +79255,7 @@ var LABELS = {
79210
79255
  var humanize = (k) => k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
79211
79256
  var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
79212
79257
  function credsFile() {
79213
- return join42(effectiveStr("CLAUDE_CONFIG_DIR") ?? join42(homedir10(), ".claude"), ".credentials.json");
79258
+ return join43(effectiveStr("CLAUDE_CONFIG_DIR") ?? join43(homedir10(), ".claude"), ".credentials.json");
79214
79259
  }
79215
79260
  var securityRun = (args) => execFileSync8("security", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
79216
79261
  var osAccount = () => {
@@ -79295,14 +79340,14 @@ async function getLimits() {
79295
79340
  // src/services/codex-limits.ts
79296
79341
  import { open as open4, readdir as readdir9, stat as stat7 } from "node:fs/promises";
79297
79342
  import { homedir as homedir11 } from "node:os";
79298
- import { join as join43 } from "node:path";
79343
+ import { join as join44 } from "node:path";
79299
79344
  var TTL_MS3 = 3e4;
79300
79345
  var STALE_AFTER_MS = 12 * 36e5;
79301
79346
  var TAIL_BYTES = 512 * 1024;
79302
79347
  var MAX_FILES = 8;
79303
79348
  var cache2 = null;
79304
79349
  var freshUntil2 = 0;
79305
- var codexSessionsDir = () => join43(process.env.CODEX_HOME ?? join43(homedir11(), ".codex"), "sessions");
79350
+ var codexSessionsDir = () => join44(process.env.CODEX_HOME ?? join44(homedir11(), ".codex"), "sessions");
79306
79351
  var UNAVAILABLE = { status: "unavailable", windows: [], fetchedAt: null, plan: null };
79307
79352
  function windowLabel(minutes) {
79308
79353
  if (minutes === 300) return "Sesi 5 jam";
@@ -79335,7 +79380,7 @@ async function recentRollouts(dir2) {
79335
79380
  return [];
79336
79381
  }
79337
79382
  const stamped = await Promise.all(entries4.map(async (rel) => {
79338
- const full = join43(dir2, rel);
79383
+ const full = join44(dir2, rel);
79339
79384
  try {
79340
79385
  return { full, mtime: (await stat7(full)).mtimeMs };
79341
79386
  } catch {
@@ -79411,7 +79456,7 @@ init_config2();
79411
79456
  import { execFileSync as execFileSync9 } from "node:child_process";
79412
79457
  import { readFileSync as readFileSync25 } from "node:fs";
79413
79458
  import { homedir as homedir12 } from "node:os";
79414
- import { join as join44 } from "node:path";
79459
+ import { join as join45 } from "node:path";
79415
79460
  var QUOTA_URL = "https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary";
79416
79461
  var USER_AGENT = "antigravity (hanoman)";
79417
79462
  var TTL_MS4 = 6e4;
@@ -79433,10 +79478,10 @@ function readAgyToken(run6 = securityRun2) {
79433
79478
  return null;
79434
79479
  }
79435
79480
  }
79436
- var agyHome = () => join44(homedir12(), ".gemini", "antigravity-cli");
79481
+ var agyHome = () => join45(homedir12(), ".gemini", "antigravity-cli");
79437
79482
  function defaultProject() {
79438
79483
  try {
79439
- return readFileSync25(join44(agyHome(), "cache", "default_project_id.txt"), "utf8").trim() || null;
79484
+ return readFileSync25(join45(agyHome(), "cache", "default_project_id.txt"), "utf8").trim() || null;
79440
79485
  } catch {
79441
79486
  return null;
79442
79487
  }
@@ -79624,7 +79669,7 @@ init_src2();
79624
79669
  init_src();
79625
79670
  import { randomUUID as randomUUID14 } from "node:crypto";
79626
79671
  import { readFile as readFile8, writeFile as writeFile6, rename as rename5, mkdir as mkdir9, rm as rm9 } from "node:fs/promises";
79627
- import { dirname as dirname18, join as join46 } from "node:path";
79672
+ import { dirname as dirname18, join as join47 } from "node:path";
79628
79673
 
79629
79674
  // src/services/model-catalog-parser.ts
79630
79675
  init_zod();
@@ -79728,10 +79773,10 @@ init_src2();
79728
79773
  init_antigravity_runtime();
79729
79774
  init_config2();
79730
79775
  init_session_sandbox();
79731
- import { spawn as spawn5 } from "node:child_process";
79776
+ import { spawn as spawn6 } from "node:child_process";
79732
79777
  import { mkdtemp as mkdtemp2, rm as rm8 } from "node:fs/promises";
79733
79778
  import { tmpdir as tmpdir9 } from "node:os";
79734
- import { join as join45 } from "node:path";
79779
+ import { join as join46 } from "node:path";
79735
79780
  var MAX_BYTES = 4 * 1024 * 1024;
79736
79781
  var TIMEOUT_MS = 2e4;
79737
79782
  var quote3 = (s2) => "'" + s2.replaceAll("'", "'\\''") + "'";
@@ -79768,7 +79813,7 @@ function catalogCommand(agent, cwd, env) {
79768
79813
  function runCatalogCommand(command, agent, cwd, env, timeoutMs = TIMEOUT_MS) {
79769
79814
  return new Promise((resolve25, reject2) => {
79770
79815
  assertAntigravityLaunch(agent, void 0, env);
79771
- const child = spawn5(command.bin, command.args, { cwd, env: agentEnvironment(agent, env), stdio: ["pipe", "pipe", "pipe"] });
79816
+ const child = spawn6(command.bin, command.args, { cwd, env: agentEnvironment(agent, env), stdio: ["pipe", "pipe", "pipe"] });
79772
79817
  let buffer = "", bytes = 0, settled = false;
79773
79818
  let killTimer;
79774
79819
  const finish = (error, result) => {
@@ -79830,7 +79875,7 @@ function runCatalogCommand(command, agent, cwd, env, timeoutMs = TIMEOUT_MS) {
79830
79875
  }
79831
79876
  async function probeModelCatalog(agent) {
79832
79877
  assertAntigravityLaunch(agent);
79833
- const cwd = await mkdtemp2(join45(tmpdir9(), "hanoman-models-"));
79878
+ const cwd = await mkdtemp2(join46(tmpdir9(), "hanoman-models-"));
79834
79879
  const env = agentEnvironment(agent, process.env);
79835
79880
  for (const key of ["HANOMAN_CLAUDE_BIN", "HANOMAN_CODEX_BIN", "HANOMAN_PODMAN_BIN", "HANOMAN_ANTIGRAVITY_BIN"]) {
79836
79881
  const value = effectiveStr(key);
@@ -79941,7 +79986,7 @@ async function writeModelCatalogCache(file, catalog, current = () => true) {
79941
79986
  await rm9(temp, { force: true });
79942
79987
  }
79943
79988
  }
79944
- var cacheFile = () => join46(resolveHome(), "model-catalog.json");
79989
+ var cacheFile = () => join47(resolveHome(), "model-catalog.json");
79945
79990
  var modelCatalogService = createModelCatalogService({
79946
79991
  probe: probeModelCatalog,
79947
79992
  key: catalogProbeKey,
@@ -79980,8 +80025,9 @@ init_pty();
79980
80025
 
79981
80026
  // src/services/restart.ts
79982
80027
  init_src();
80028
+ init_exit_guard();
79983
80029
  function requestConfigRestart(delayMs = 50) {
79984
- setTimeout(() => process.exit(CONFIG_RESTART_EXIT), delayMs);
80030
+ setTimeout(() => exitGuarded(CONFIG_RESTART_EXIT), delayMs);
79985
80031
  }
79986
80032
  function afterReplyFlushed(raw, fn, maxWaitMs = 5e3) {
79987
80033
  let done = false;
@@ -79999,7 +80045,7 @@ function afterReplyFlushed(raw, fn, maxWaitMs = 5e3) {
79999
80045
  timer11 = setTimeout(run6, maxWaitMs);
80000
80046
  }
80001
80047
  function requestManualRestart(delayMs = 250) {
80002
- setTimeout(() => process.exit(MANUAL_RESTART_EXIT), delayMs);
80048
+ setTimeout(() => exitGuarded(MANUAL_RESTART_EXIT), delayMs);
80003
80049
  }
80004
80050
 
80005
80051
  // src/routes/update.ts
@@ -81868,7 +81914,7 @@ init_src();
81868
81914
  init_db();
81869
81915
  import { gunzipSync, gzipSync } from "node:zlib";
81870
81916
  import { mkdir as mkdir10, rename as rename6, writeFile as writeFile7 } from "node:fs/promises";
81871
- import { join as join47 } from "node:path";
81917
+ import { join as join48 } from "node:path";
81872
81918
 
81873
81919
  // src/services/device-auth.ts
81874
81920
  function bearerToken2(req) {
@@ -82124,9 +82170,9 @@ async function sync_default(app3) {
82124
82170
  const deviceId = req.device.id;
82125
82171
  const withTranscriptKey = await Promise.all(parsed.data.entries.map(async (e) => {
82126
82172
  if (!e.transcript) return e;
82127
- const dir2 = join47(resolveDataDirs().home, "remote-transcripts", deviceId);
82173
+ const dir2 = join48(resolveDataDirs().home, "remote-transcripts", deviceId);
82128
82174
  await mkdir10(dir2, { recursive: true, mode: 448 });
82129
- const final = join47(dir2, `${e.seq}.txt`);
82175
+ const final = join48(dir2, `${e.seq}.txt`);
82130
82176
  const tmp = `${final}.tmp`;
82131
82177
  await writeFile7(tmp, e.transcript, { encoding: "utf8", mode: 384 });
82132
82178
  await rename6(tmp, final);
@@ -85413,7 +85459,7 @@ init_qa_attachment_transfer();
85413
85459
  init_uploads();
85414
85460
  import { createHash as createHash21 } from "node:crypto";
85415
85461
  import { execFileSync as execFileSync10 } from "node:child_process";
85416
- import { join as join49 } from "node:path";
85462
+ import { join as join50 } from "node:path";
85417
85463
  var QaSessionError = class extends Error {
85418
85464
  constructor(code, message, needsBind = false) {
85419
85465
  super(message);
@@ -85440,7 +85486,7 @@ async function startQaFindingSession(d, fid) {
85440
85486
  const saved = await saveSessionUpload(id2, bytes, a.mimeType);
85441
85487
  files2.push(`${JSON.stringify(a.filename)} (${a.mimeType}): ${saved.path}`);
85442
85488
  }
85443
- const wt = join49(repoDir, ".worktrees", id2);
85489
+ const wt = join50(repoDir, ".worktrees", id2);
85444
85490
  const branch = `qa/${id2.slice(3)}`;
85445
85491
  const reused = realGit.worktreeAlive(wt);
85446
85492
  try {
@@ -86979,9 +87025,9 @@ ${baru}`;
86979
87025
  // src/services/portal-chat/workspace.ts
86980
87026
  init_src();
86981
87027
  init_db();
86982
- import { mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync5, rmSync as rmSync14, writeFileSync as writeFileSync19 } from "node:fs";
87028
+ import { mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync5, rmSync as rmSync14, writeFileSync as writeFileSync20 } from "node:fs";
86983
87029
  import { tmpdir as tmpdir10 } from "node:os";
86984
- import { join as join50 } from "node:path";
87030
+ import { join as join51 } from "node:path";
86985
87031
  var STAGE_LABEL = {
86986
87032
  brainstorming: "Dirumuskan",
86987
87033
  objective: "Dirumuskan",
@@ -87024,7 +87070,7 @@ function renderTicketDoc(tickets2) {
87024
87070
  ${baris.join("\n")}`;
87025
87071
  }
87026
87072
  async function buildChatWorkspace(projectId) {
87027
- const dir2 = mkdtempSync5(join50(tmpdir10(), "hanoman-portal-chat-"));
87073
+ const dir2 = mkdtempSync5(join51(tmpdir10(), "hanoman-portal-chat-"));
87028
87074
  try {
87029
87075
  const project = await prisma.project.findUnique({
87030
87076
  where: { id: projectId },
@@ -87039,7 +87085,7 @@ async function buildChatWorkspace(projectId) {
87039
87085
  });
87040
87086
  const files2 = [];
87041
87087
  const tulis = (rel, isi) => {
87042
- writeFileSync19(join50(dir2, rel), isi, { mode: 384 });
87088
+ writeFileSync20(join51(dir2, rel), isi, { mode: 384 });
87043
87089
  files2.push(rel);
87044
87090
  };
87045
87091
  tulis("project.md", renderProjectDoc(project));
@@ -87047,11 +87093,11 @@ async function buildChatWorkspace(projectId) {
87047
87093
  tulis("laporan.md", renderTicketDoc(tickets2.map((t) => toPortalTicket(t, null))));
87048
87094
  const prds = await listPrds(projectId);
87049
87095
  if (prds.length) {
87050
- mkdirSync20(join50(dir2, "dokumen"), { mode: 448 });
87096
+ mkdirSync20(join51(dir2, "dokumen"), { mode: 448 });
87051
87097
  for (const prd of prds) {
87052
87098
  const isi = await readPrd(projectId, prd.path);
87053
87099
  if (!isi) continue;
87054
- tulis(join50("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
87100
+ tulis(join51("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
87055
87101
  }
87056
87102
  }
87057
87103
  return { dir: dir2, files: files2, cleanup: () => rmSync14(dir2, { recursive: true, force: true }) };
@@ -87578,13 +87624,13 @@ init_db();
87578
87624
  // src/services/bootstrap.ts
87579
87625
  import { createHash as createHash22, randomBytes as randomBytes11, timingSafeEqual as timingSafeEqual5 } from "node:crypto";
87580
87626
  import { chmod as chmod2, lstat as lstat3, mkdir as mkdir11, readFile as readFile9, unlink as unlink5, writeFile as writeFile8 } from "node:fs/promises";
87581
- import { join as join51 } from "node:path";
87627
+ import { join as join52 } from "node:path";
87582
87628
  var TTL_MS7 = 15 * 6e4;
87583
87629
  var SETUP_TOKEN_FILE = "setup.token";
87584
87630
  var BootstrapError = class extends Error {
87585
87631
  code = "BOOTSTRAP_PROOF";
87586
87632
  };
87587
- var tokenPath = (home3) => join51(home3, SETUP_TOKEN_FILE);
87633
+ var tokenPath = (home3) => join52(home3, SETUP_TOKEN_FILE);
87588
87634
  async function readStored(home3) {
87589
87635
  try {
87590
87636
  const info = await lstat3(tokenPath(home3));
@@ -88422,7 +88468,7 @@ init_event_log();
88422
88468
  init_transcript_store();
88423
88469
  init_src2();
88424
88470
  import { readFile as readFile10 } from "node:fs/promises";
88425
- import { join as join52 } from "node:path";
88471
+ import { join as join53 } from "node:path";
88426
88472
  function decodeCursor(c) {
88427
88473
  if (!c) return null;
88428
88474
  try {
@@ -88460,7 +88506,7 @@ async function readRemoteTranscript(id2) {
88460
88506
  const row = await prisma.logEntry.findUnique({ where: { id: id2 } });
88461
88507
  if (!row || row.lane !== "transcript" || !row.transcriptKey) return null;
88462
88508
  if (row.deviceId === "local") return readTranscript(row.transcriptKey);
88463
- const path = join52(resolveDataDirs().home, "remote-transcripts", row.transcriptKey);
88509
+ const path = join53(resolveDataDirs().home, "remote-transcripts", row.transcriptKey);
88464
88510
  try {
88465
88511
  return await readFile10(path, "utf8");
88466
88512
  } catch {
@@ -89727,9 +89773,9 @@ async function pruneLogs(now, retention, opts2 = {}) {
89727
89773
  }
89728
89774
  async function reconcileRemoteTranscripts() {
89729
89775
  const { readdir: readdir10, unlink: unlink6, stat: stat8 } = await import("node:fs/promises");
89730
- const { join: join53 } = await import("node:path");
89776
+ const { join: join54 } = await import("node:path");
89731
89777
  const { resolveDataDirs: resolveDataDirs2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
89732
- const root = join53(resolveDataDirs2().home, "remote-transcripts");
89778
+ const root = join54(resolveDataDirs2().home, "remote-transcripts");
89733
89779
  let deviceDirs;
89734
89780
  try {
89735
89781
  deviceDirs = await readdir10(root);
@@ -89743,16 +89789,16 @@ async function reconcileRemoteTranscripts() {
89743
89789
  for (const dev of deviceDirs) {
89744
89790
  let files2;
89745
89791
  try {
89746
- files2 = await readdir10(join53(root, dev));
89792
+ files2 = await readdir10(join54(root, dev));
89747
89793
  } catch {
89748
89794
  continue;
89749
89795
  }
89750
89796
  for (const f of files2) {
89751
89797
  const key = `${dev}/${f}`;
89752
89798
  if (referenced.has(key)) continue;
89753
- const st = await stat8(join53(root, dev, f)).catch(() => null);
89799
+ const st = await stat8(join54(root, dev, f)).catch(() => null);
89754
89800
  if (!st || Date.now() - st.mtimeMs < gracePeriodMs) continue;
89755
- await unlink6(join53(root, dev, f)).catch(() => {
89801
+ await unlink6(join54(root, dev, f)).catch(() => {
89756
89802
  });
89757
89803
  orphans++;
89758
89804
  }
@@ -89939,6 +89985,7 @@ init_transcript_store();
89939
89985
  init_client2();
89940
89986
  init_dispatcher();
89941
89987
  init_safe_outbound_request();
89988
+ init_exit_guard();
89942
89989
  installSafeGlobalFetch();
89943
89990
  var app2 = buildApp();
89944
89991
  var port = Number(process.env.PORT ?? 8787);
@@ -89957,7 +90004,7 @@ var shutdown = createShutdown({
89957
90004
  await app2.close();
89958
90005
  await prisma.$disconnect();
89959
90006
  },
89960
- exit: (code) => process.exit(code)
90007
+ exit: (code) => exitGuarded(code)
89961
90008
  });
89962
90009
  process.on("SIGTERM", () => void shutdown("SIGTERM"));
89963
90010
  process.on("SIGINT", () => void shutdown("SIGINT"));
@@ -90033,7 +90080,7 @@ bootstrapReady.then(async () => {
90033
90080
  startOverviewRecorder();
90034
90081
  }).catch((err) => {
90035
90082
  console.error("listen gagal:", err);
90036
- process.exit(1);
90083
+ exitGuarded(1);
90037
90084
  });
90038
90085
  /*! Bundled license information:
90039
90086
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hanoman",
3
- "version": "0.9.31",
3
+ "version": "0.9.32",
4
4
  "description": "Orchestrator + dashboard workflow docs-driven untuk sesi Claude Code / Codex",
5
5
  "type": "module",
6
6
  "repository": {