hanoman 0.1.27 → 0.1.28

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
@@ -8657,7 +8657,7 @@ var init_paths = __esm({
8657
8657
 
8658
8658
  // ../runner/src/skills.ts
8659
8659
  import { homedir as homedir2 } from "node:os";
8660
- import { join as join2 } from "node:path";
8660
+ import { basename, dirname as dirname3, join as join2, resolve as resolve6 } from "node:path";
8661
8661
  import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
8662
8662
  function agentSkillHome(agent, env = process.env, osHome = homedir2()) {
8663
8663
  const own = (agent === "codex" ? env.HANOMAN_CODEX_HOME : env.HANOMAN_CLAUDE_HOME)?.trim();
@@ -8668,6 +8668,9 @@ function agentSkillHome(agent, env = process.env, osHome = homedir2()) {
8668
8668
  }
8669
8669
  return join2(osHome, agent === "codex" ? ".codex" : ".claude");
8670
8670
  }
8671
+ function agentsSkillHome(env = process.env, osHome = homedir2()) {
8672
+ return env.HANOMAN_AGENTS_HOME?.trim() || join2(osHome, ".agents");
8673
+ }
8671
8674
  function dirsIn(dir2) {
8672
8675
  try {
8673
8676
  return readdirSync(dir2, { withFileTypes: true }).filter((e) => (e.isDirectory() || e.isSymbolicLink()) && !e.name.startsWith(".")).map((e) => e.name);
@@ -8682,8 +8685,51 @@ function readJson(file) {
8682
8685
  return null;
8683
8686
  }
8684
8687
  }
8685
- function skillsUnder(dir2, pkg) {
8686
- return dirsIn(dir2).filter((name2) => existsSync2(join2(dir2, name2, "SKILL.md"))).map((name2) => ({ id: pkg ? `${pkg}:${name2}` : name2, name: name2, pkg, dir: join2(dir2, name2) }));
8688
+ function isSkillDir(dir2) {
8689
+ return existsSync2(join2(dir2, "SKILL.md"));
8690
+ }
8691
+ function asSkill(dir2, pkg, name2 = basename(dir2)) {
8692
+ return { id: pkg ? `${pkg}:${name2}` : name2, name: name2, pkg, dir: dir2 };
8693
+ }
8694
+ function skillsUnder(dir2, pkg, depth = 2) {
8695
+ const out3 = [];
8696
+ for (const name2 of dirsIn(dir2)) {
8697
+ const sub = join2(dir2, name2);
8698
+ if (isSkillDir(sub)) out3.push(asSkill(sub, pkg, name2));
8699
+ else if (depth > 1) out3.push(...skillsUnder(sub, pkg, depth - 1));
8700
+ }
8701
+ return out3;
8702
+ }
8703
+ function manifestSkills(installPath, pkg) {
8704
+ for (const marker of [".claude-plugin", ".codex-plugin"]) {
8705
+ const j = readJson(join2(installPath, marker, "plugin.json"));
8706
+ const declared = j?.skills;
8707
+ if (!Array.isArray(declared)) continue;
8708
+ const out3 = [];
8709
+ for (const rel of declared) {
8710
+ if (typeof rel !== "string" || !rel) continue;
8711
+ const p = resolve6(installPath, rel);
8712
+ const dir2 = basename(p) === "SKILL.md" ? dirname3(p) : p;
8713
+ if (isSkillDir(dir2)) out3.push(asSkill(dir2, pkg));
8714
+ }
8715
+ if (out3.length) return out3;
8716
+ }
8717
+ return [];
8718
+ }
8719
+ function pluginSkills(installPath, pkg) {
8720
+ const declared = manifestSkills(installPath, pkg);
8721
+ return declared.length ? declared : skillsUnder(join2(installPath, "skills"), pkg);
8722
+ }
8723
+ function lockPluginNames(agentsHome) {
8724
+ const out3 = /* @__PURE__ */ new Map();
8725
+ const j = readJson(join2(agentsHome, ".skill-lock.json"));
8726
+ const skills = j?.skills;
8727
+ if (!skills || typeof skills !== "object") return out3;
8728
+ for (const [name2, entry] of Object.entries(skills)) {
8729
+ const pkg = entry?.pluginName;
8730
+ if (typeof pkg === "string" && pkg.trim()) out3.set(name2, pkg.trim());
8731
+ }
8732
+ return out3;
8687
8733
  }
8688
8734
  function splitPluginKey(key) {
8689
8735
  const at = key.lastIndexOf("@");
@@ -8763,11 +8809,23 @@ function scanAgentSkills(agent, env = process.env, osHome = homedir2()) {
8763
8809
  add(userDir, skillsUnder(userDir, null));
8764
8810
  for (const r of [...manifestRoots(home2), ...cacheRoots(home2)]) {
8765
8811
  if (disabled.has(`${r.pkg}@${r.marketplace}`)) continue;
8766
- const dir2 = join2(r.dir, "skills");
8767
- const found = skillsUnder(dir2, r.pkg);
8812
+ const found = pluginSkills(r.dir, r.pkg);
8768
8813
  if (!found.length) continue;
8769
8814
  packages.add(r.pkg);
8770
- add(dir2, found);
8815
+ add(join2(r.dir, "skills"), found);
8816
+ }
8817
+ if (agent === "codex") {
8818
+ const agentsHome = agentsSkillHome(env, osHome);
8819
+ const dir2 = join2(agentsHome, "skills");
8820
+ const names = lockPluginNames(agentsHome);
8821
+ const flat = skillsUnder(dir2, null);
8822
+ const withPkg = flat.flatMap((s2) => {
8823
+ const pkg = names.get(s2.name);
8824
+ if (!pkg) return [s2];
8825
+ packages.add(pkg);
8826
+ return [asSkill(s2.dir, pkg, s2.name), s2];
8827
+ });
8828
+ add(dir2, withPkg);
8771
8829
  }
8772
8830
  return { agent, home: home2, roots, skills, packages: [...packages] };
8773
8831
  }
@@ -9002,7 +9060,7 @@ var init_tap = __esm({
9002
9060
 
9003
9061
  // src/db.ts
9004
9062
  import { mkdirSync as mkdirSync2 } from "node:fs";
9005
- import { dirname as dirname3, resolve as resolve6 } from "node:path";
9063
+ import { dirname as dirname4, resolve as resolve7 } from "node:path";
9006
9064
  import { fileURLToPath as fileURLToPath2 } from "node:url";
9007
9065
  import { PrismaClient } from "@prisma/client";
9008
9066
  var schemaDir, notice, url, base, prisma;
@@ -9012,12 +9070,12 @@ var init_db = __esm({
9012
9070
  init_env();
9013
9071
  init_src2();
9014
9072
  init_tap();
9015
- schemaDir = resolve6(dirname3(fileURLToPath2(import.meta.url)), "../prisma");
9073
+ schemaDir = resolve7(dirname4(fileURLToPath2(import.meta.url)), "../prisma");
9016
9074
  notice = dbUrlNotice(process.env);
9017
9075
  if (notice) console.warn(notice);
9018
9076
  url = resolveDbUrl(process.env, schemaDir);
9019
9077
  process.env.DATABASE_URL = url;
9020
- mkdirSync2(dirname3(dbFilePath(url)), { recursive: true });
9078
+ mkdirSync2(dirname4(dbFilePath(url)), { recursive: true });
9021
9079
  base = new PrismaClient();
9022
9080
  prisma = base.$extends(webhookTap(base));
9023
9081
  }
@@ -9373,7 +9431,7 @@ var init_tui_dialog = __esm({
9373
9431
  // src/services/secret-box.ts
9374
9432
  import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync } from "node:fs";
9375
9433
  import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
9376
- import { dirname as dirname4, join as join3 } from "node:path";
9434
+ import { dirname as dirname5, join as join3 } from "node:path";
9377
9435
  function isEncrypted(value) {
9378
9436
  return value.startsWith(ENC_PREFIX);
9379
9437
  }
@@ -9424,7 +9482,7 @@ function secretKey() {
9424
9482
  return key2;
9425
9483
  }
9426
9484
  const key = randomBytes(KEY_BYTES);
9427
- mkdirSync3(dirname4(path), { recursive: true });
9485
+ mkdirSync3(dirname5(path), { recursive: true });
9428
9486
  writeFileSync(path, key.toString("base64url"), { mode: 384 });
9429
9487
  chmodSync(path, 384);
9430
9488
  cached = key;
@@ -9523,7 +9581,7 @@ import { spawn } from "node-pty";
9523
9581
  import { execFileSync } from "node:child_process";
9524
9582
  import { randomUUID } from "node:crypto";
9525
9583
  import { mkdirSync as mkdirSync4, statSync, writeFileSync as writeFileSync2 } from "node:fs";
9526
- import { dirname as dirname5 } from "node:path";
9584
+ import { dirname as dirname6 } from "node:path";
9527
9585
  import { tmpdir } from "node:os";
9528
9586
  function tmux(...args) {
9529
9587
  try {
@@ -9605,7 +9663,7 @@ function createSession(projectId, cwd, opts = {}) {
9605
9663
  let promptArg = "";
9606
9664
  if (!opts.command && opts.prompt) {
9607
9665
  const promptFile = promptFilePath(id);
9608
- mkdirSync4(dirname5(promptFile), { recursive: true });
9666
+ mkdirSync4(dirname6(promptFile), { recursive: true });
9609
9667
  writeFileSync2(promptFile, opts.prompt + rosterBlock);
9610
9668
  promptArg = `"$(cat ${sq(promptFile)})"`;
9611
9669
  }
@@ -9617,7 +9675,7 @@ function createSession(projectId, cwd, opts = {}) {
9617
9675
  let goalGate;
9618
9676
  if (agent === "codex" && opts.goal && opts.flow && opts.specId) {
9619
9677
  goalGate = goalGatePath(id);
9620
- mkdirSync4(dirname5(goalGate), { recursive: true });
9678
+ mkdirSync4(dirname6(goalGate), { recursive: true });
9621
9679
  writeFileSync2(goalGate, codexGoalScript({
9622
9680
  flow: opts.flow,
9623
9681
  specId: opts.specId,
@@ -9633,7 +9691,7 @@ function createSession(projectId, cwd, opts = {}) {
9633
9691
  const json = renderAgentsJson(customDefs);
9634
9692
  if (json) {
9635
9693
  agentsFile = agentsFilePath(id);
9636
- mkdirSync4(dirname5(agentsFile), { recursive: true });
9694
+ mkdirSync4(dirname6(agentsFile), { recursive: true });
9637
9695
  writeFileSync2(agentsFile, json);
9638
9696
  }
9639
9697
  }
@@ -9653,12 +9711,12 @@ function createSession(projectId, cwd, opts = {}) {
9653
9711
  for (const [k, v] of Object.entries(rootBypassEnv())) envPairs.push(`${k}=${sq(v)}`);
9654
9712
  }
9655
9713
  if (opts.phaseFile) {
9656
- mkdirSync4(dirname5(opts.phaseFile), { recursive: true });
9714
+ mkdirSync4(dirname6(opts.phaseFile), { recursive: true });
9657
9715
  envPairs.push(`HANOMAN_PHASE_FILE=${sq(opts.phaseFile)}`);
9658
9716
  }
9659
9717
  for (const [k, v] of Object.entries(opts.env ?? {})) envPairs.push(`${k}=${sq(v)}`);
9660
9718
  const cmd = envPairs.length ? `${envPairs.join(" ")} ${argv}` : argv;
9661
- if (opts.decisionFile) mkdirSync4(dirname5(opts.decisionFile), { recursive: true });
9719
+ if (opts.decisionFile) mkdirSync4(dirname6(opts.decisionFile), { recursive: true });
9662
9720
  tmux(
9663
9721
  "set-option",
9664
9722
  "-g",
@@ -11169,7 +11227,7 @@ var init_gateway = __esm({
11169
11227
  const message = sanitizeTelegramOutput(error.message, this.deps.exactSecrets).slice(0, 500);
11170
11228
  updateTelegramRuntimeStatus({ running: false, readiness: "error", lastError: message });
11171
11229
  if (error instanceof TelegramApiError && error.code === 409) return;
11172
- await new Promise((resolve15) => setTimeout(resolve15, 1e3));
11230
+ await new Promise((resolve16) => setTimeout(resolve16, 1e3));
11173
11231
  }
11174
11232
  }
11175
11233
  }
@@ -17179,7 +17237,7 @@ var require_parseParams = __commonJS({
17179
17237
  var require_basename = __commonJS({
17180
17238
  "../node_modules/.pnpm/@fastify+busboy@3.2.0/node_modules/@fastify/busboy/lib/utils/basename.js"(exports, module) {
17181
17239
  "use strict";
17182
- module.exports = function basename3(path) {
17240
+ module.exports = function basename4(path) {
17183
17241
  if (typeof path !== "string") {
17184
17242
  return "";
17185
17243
  }
@@ -17206,7 +17264,7 @@ var require_multipart = __commonJS({
17206
17264
  var Dicer = require_Dicer();
17207
17265
  var parseParams = require_parseParams();
17208
17266
  var decodeText = require_decodeText();
17209
- var basename3 = require_basename();
17267
+ var basename4 = require_basename();
17210
17268
  var getLimit = require_getLimit();
17211
17269
  var RE_BOUNDARY = /^boundary$/i;
17212
17270
  var RE_FIELD = /^form-data$/i;
@@ -17323,7 +17381,7 @@ var require_multipart = __commonJS({
17323
17381
  } else if (RE_FILENAME.test(parsed[i][0])) {
17324
17382
  filename = parsed[i][1];
17325
17383
  if (!preservePath) {
17326
- filename = basename3(filename);
17384
+ filename = basename4(filename);
17327
17385
  }
17328
17386
  }
17329
17387
  }
@@ -18407,17 +18465,17 @@ var require_stream_wormhole = __commonJS({
18407
18465
  "../node_modules/.pnpm/stream-wormhole@1.1.0/node_modules/stream-wormhole/index.js"(exports, module) {
18408
18466
  "use strict";
18409
18467
  module.exports = (stream, throwError) => {
18410
- return new Promise((resolve15, reject2) => {
18468
+ return new Promise((resolve16, reject2) => {
18411
18469
  if (typeof stream.resume !== "function") {
18412
- return resolve15();
18470
+ return resolve16();
18413
18471
  }
18414
18472
  stream.unpipe && stream.unpipe();
18415
18473
  stream.resume();
18416
18474
  if (stream._readableState && stream._readableState.ended) {
18417
- return resolve15();
18475
+ return resolve16();
18418
18476
  }
18419
18477
  if (!stream.readable || stream.destroyed) {
18420
- return resolve15();
18478
+ return resolve16();
18421
18479
  }
18422
18480
  function cleanup() {
18423
18481
  stream.removeListener("end", onEnd);
@@ -18426,14 +18484,14 @@ var require_stream_wormhole = __commonJS({
18426
18484
  }
18427
18485
  function onEnd() {
18428
18486
  cleanup();
18429
- resolve15();
18487
+ resolve16();
18430
18488
  }
18431
18489
  function onError(err) {
18432
18490
  cleanup();
18433
18491
  if (throwError) {
18434
18492
  reject2(err);
18435
18493
  } else {
18436
- resolve15();
18494
+ resolve16();
18437
18495
  }
18438
18496
  }
18439
18497
  stream.on("end", onEnd);
@@ -18868,16 +18926,16 @@ var require_multipart2 = __commonJS({
18868
18926
  }
18869
18927
  };
18870
18928
  const parts = () => {
18871
- return new Promise((resolve15, reject2) => {
18929
+ return new Promise((resolve16, reject2) => {
18872
18930
  handle((val) => {
18873
18931
  if (val instanceof Error) {
18874
18932
  if (val.message === "Unexpected end of multipart data") {
18875
- resolve15(null);
18933
+ resolve16(null);
18876
18934
  } else {
18877
18935
  reject2(val);
18878
18936
  }
18879
18937
  } else {
18880
- resolve15(val);
18938
+ resolve16(val);
18881
18939
  }
18882
18940
  });
18883
18941
  });
@@ -19143,7 +19201,7 @@ import fastifyStatic from "@fastify/static";
19143
19201
  import websocket from "@fastify/websocket";
19144
19202
  import cookie from "@fastify/cookie";
19145
19203
  import { fileURLToPath as fileURLToPath4 } from "node:url";
19146
- import { dirname as dirname13 } from "node:path";
19204
+ import { dirname as dirname14 } from "node:path";
19147
19205
  import { existsSync as existsSync14 } from "node:fs";
19148
19206
 
19149
19207
  // src/web-dir.ts
@@ -19207,7 +19265,7 @@ init_src();
19207
19265
  import { execFile } from "node:child_process";
19208
19266
  import { promisify } from "node:util";
19209
19267
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync4, rmSync as rmSync2, mkdirSync as mkdirSync5 } from "node:fs";
19210
- import { resolve as resolve7, dirname as dirname6, sep } from "node:path";
19268
+ import { resolve as resolve8, dirname as dirname7, sep } from "node:path";
19211
19269
  var exec = promisify(execFile);
19212
19270
  async function listRepoDocs(repoDir) {
19213
19271
  try {
@@ -19223,7 +19281,7 @@ async function listRepoDocs(repoDir) {
19223
19281
  }
19224
19282
  function docsDirOf(repoDir) {
19225
19283
  try {
19226
- const raw = readFileSync5(resolve7(repoDir, "hanoman.config.json"), "utf8");
19284
+ const raw = readFileSync5(resolve8(repoDir, "hanoman.config.json"), "utf8");
19227
19285
  return zHanomanConfig.parse(JSON.parse(raw)).docsDir;
19228
19286
  } catch {
19229
19287
  return zHanomanConfig.parse({}).docsDir;
@@ -19231,7 +19289,7 @@ function docsDirOf(repoDir) {
19231
19289
  }
19232
19290
  function resolveIndex(repoDir, docsDir) {
19233
19291
  const rel = `${docsDir}/README.md`;
19234
- return existsSync4(resolve7(repoDir, rel)) ? rel : "";
19292
+ return existsSync4(resolve8(repoDir, rel)) ? rel : "";
19235
19293
  }
19236
19294
  var catOf = (rel) => rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : ".";
19237
19295
  var nameOf = (rel) => rel.includes("/") ? rel.slice(rel.lastIndexOf("/") + 1) : rel;
@@ -19242,7 +19300,7 @@ async function scanRepoDocs(repoDir) {
19242
19300
  const index = resolveIndex(repoDir, docsDir);
19243
19301
  const read = (rel) => {
19244
19302
  try {
19245
- return readFileSync5(resolve7(repoDir, rel), "utf8");
19303
+ return readFileSync5(resolve8(repoDir, rel), "utf8");
19246
19304
  } catch {
19247
19305
  return null;
19248
19306
  }
@@ -19265,7 +19323,7 @@ async function scanRepoDocs(repoDir) {
19265
19323
  function docAbsPath(repoDir, rel) {
19266
19324
  if (!rel.endsWith(".md")) throw new Error("hanya file .md yang diizinkan");
19267
19325
  if (rel.split("/").includes(".git")) throw new Error("tidak boleh menyentuh .git");
19268
- const abs = resolve7(repoDir, rel);
19326
+ const abs = resolve8(repoDir, rel);
19269
19327
  if (abs !== repoDir && !abs.startsWith(repoDir + sep)) throw new Error("path keluar dari repo");
19270
19328
  return abs;
19271
19329
  }
@@ -19278,7 +19336,7 @@ function readDocFile(repoDir, rel) {
19278
19336
  }
19279
19337
  function writeDocFile(repoDir, rel, content) {
19280
19338
  const abs = docAbsPath(repoDir, rel);
19281
- mkdirSync5(dirname6(abs), { recursive: true });
19339
+ mkdirSync5(dirname7(abs), { recursive: true });
19282
19340
  writeFileSync3(abs, content);
19283
19341
  }
19284
19342
  function deleteDocFile(repoDir, rel) {
@@ -19768,7 +19826,7 @@ import { execFile as execFile4 } from "node:child_process";
19768
19826
  import { promisify as promisify4 } from "node:util";
19769
19827
  import { readFile as readFile2, mkdtemp, copyFile, rm } from "node:fs/promises";
19770
19828
  import { tmpdir as tmpdir2 } from "node:os";
19771
- import { join as join5, resolve as resolve8 } from "node:path";
19829
+ import { join as join5, resolve as resolve9 } from "node:path";
19772
19830
  var exec4 = promisify4(execFile4);
19773
19831
  var GIT3 = { maxBuffer: 1 << 24 };
19774
19832
  var MAX = 256 * 1024;
@@ -19777,7 +19835,7 @@ async function withTempIndex(wt, fn) {
19777
19835
  const idx = (await exec4("git", ["rev-parse", "--git-path", "index"], { cwd: wt, ...GIT3 })).stdout.trim();
19778
19836
  const dir2 = await mkdtemp(join5(tmpdir2(), "hanoman-idx-"));
19779
19837
  const tmp = join5(dir2, "index");
19780
- await copyFile(resolve8(wt, idx), tmp);
19838
+ await copyFile(resolve9(wt, idx), tmp);
19781
19839
  const env = { ...process.env, GIT_INDEX_FILE: tmp };
19782
19840
  try {
19783
19841
  await exec4("git", ["add", "-A", "-N"], { cwd: wt, env, ...GIT3 });
@@ -22666,7 +22724,7 @@ function renderDocPdf(text, name2, meta) {
22666
22724
  });
22667
22725
  const chunks = [];
22668
22726
  doc.on("data", (c) => chunks.push(c));
22669
- const done = new Promise((resolve15) => doc.on("end", () => resolve15(Buffer.concat(chunks))));
22727
+ const done = new Promise((resolve16) => doc.on("end", () => resolve16(Buffer.concat(chunks))));
22670
22728
  doc.font("Helvetica-Bold").fontSize(8).fillColor(BRASS).text(toWinAnsi(meta.eyebrow.toUpperCase()), { characterSpacing: 0.8 });
22671
22729
  doc.moveDown(0.25);
22672
22730
  doc.font("Helvetica-Bold").fontSize(17).fillColor(STRONG).text(toWinAnsi(name2.split("/").pop() ?? name2));
@@ -23283,7 +23341,7 @@ async function docs_default(app2) {
23283
23341
 
23284
23342
  // src/routes/ide.ts
23285
23343
  init_src2();
23286
- import { basename } from "node:path";
23344
+ import { basename as basename2 } from "node:path";
23287
23345
  import { spawn as spawn2 } from "node:child_process";
23288
23346
 
23289
23347
  // src/services/git-remotes.ts
@@ -23351,14 +23409,14 @@ import { execFile as execFile6 } from "node:child_process";
23351
23409
  import { promisify as promisify6 } from "node:util";
23352
23410
  import { readFile as readFile3, writeFile, mkdir } from "node:fs/promises";
23353
23411
  import { existsSync as existsSync7 } from "node:fs";
23354
- import { resolve as resolve9, sep as sep2, dirname as dirname8 } from "node:path";
23412
+ import { resolve as resolve10, sep as sep2, dirname as dirname9 } from "node:path";
23355
23413
  var exec6 = promisify6(execFile6);
23356
23414
  var GIT5 = { maxBuffer: 1 << 24 };
23357
23415
  var MAX2 = 256 * 1024;
23358
23416
  var splitZ2 = (s2) => s2.split("\0").filter(Boolean);
23359
23417
  function repoAbsPath(repoDir, rel) {
23360
23418
  if (rel.split(/[\\/]/).includes(".git")) throw new Error("tidak boleh menyentuh .git");
23361
- const abs = resolve9(repoDir, rel);
23419
+ const abs = resolve10(repoDir, rel);
23362
23420
  if (abs !== repoDir && !abs.startsWith(repoDir + sep2)) throw new Error("path keluar dari repo");
23363
23421
  return abs;
23364
23422
  }
@@ -23597,7 +23655,7 @@ async function commitFileDiff(repoDir, sha, rel) {
23597
23655
  async function writeRepoFile(repoDir, rel, content) {
23598
23656
  if (!repoDir) throw new Error("project tidak punya repoDir");
23599
23657
  const abs = repoAbsPath(repoDir, rel);
23600
- await mkdir(dirname8(abs), { recursive: true });
23658
+ await mkdir(dirname9(abs), { recursive: true });
23601
23659
  await writeFile(abs, content);
23602
23660
  }
23603
23661
  function validateGitOp(op) {
@@ -24093,7 +24151,7 @@ async function ide_default(app2) {
24093
24151
  const fmt = q.format === "tar" ? "tar" : "zip";
24094
24152
  const child = spawn2("git", ["archive", `--format=${fmt}`, "--end-of-options", ref], { cwd: repoDir });
24095
24153
  reply.header("content-type", fmt === "zip" ? "application/zip" : "application/x-tar");
24096
- reply.header("content-disposition", `attachment; filename="${basename(repoDir)}-${ref.replace(/[^\w.-]/g, "_")}.${fmt}"`);
24154
+ reply.header("content-disposition", `attachment; filename="${basename2(repoDir)}-${ref.replace(/[^\w.-]/g, "_")}.${fmt}"`);
24097
24155
  return reply.send(child.stdout);
24098
24156
  });
24099
24157
  app2.get("/projects/:id/commit/:sha", async (req, reply) => {
@@ -24245,22 +24303,22 @@ async function finishGraphOp(reply, id, repoDir, r, verb) {
24245
24303
  CODE_STYLE_CLAUSE,
24246
24304
  `${verb} via git graph project ${id}.`
24247
24305
  ].join("\n\n");
24248
- const s2 = createSession(id, r.worktree, { id: basename(r.worktree), model, effort, agent, prompt });
24306
+ const s2 = createSession(id, r.worktree, { id: basename2(r.worktree), model, effort, agent, prompt });
24249
24307
  return { status: "conflict", sessionId: s2.id };
24250
24308
  }
24251
24309
 
24252
24310
  // src/routes/fs.ts
24253
24311
  import { readdir } from "node:fs/promises";
24254
24312
  import { homedir as homedir4 } from "node:os";
24255
- import { resolve as resolve10, dirname as dirname9, join as join6 } from "node:path";
24313
+ import { resolve as resolve11, dirname as dirname10, join as join6 } from "node:path";
24256
24314
  async function fs_default(app2) {
24257
24315
  app2.get("/fs/browse", async (req, reply) => {
24258
24316
  const q = req.query.path;
24259
- const dir2 = q && q.trim() ? resolve10(q.trim()) : homedir4();
24317
+ const dir2 = q && q.trim() ? resolve11(q.trim()) : homedir4();
24260
24318
  try {
24261
24319
  const ents = await readdir(dir2, { withFileTypes: true });
24262
24320
  const entries = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join6(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
24263
- const parent = dirname9(dir2);
24321
+ const parent = dirname10(dir2);
24264
24322
  return { path: dir2, parent: parent === dir2 ? null : parent, entries };
24265
24323
  } catch {
24266
24324
  return reply.code(400).send({ error: `tak bisa membaca folder "${dir2}"` });
@@ -24418,11 +24476,11 @@ function installCommand(m, agent, shell = shellBin()) {
24418
24476
  }
24419
24477
 
24420
24478
  // src/services/session-worktree.ts
24421
- import { resolve as resolve11, sep as sep3 } from "node:path";
24479
+ import { resolve as resolve12, sep as sep3 } from "node:path";
24422
24480
  function ownsWorktree(repoDir, cwd) {
24423
- const base2 = resolve11(repoDir);
24424
- const dir2 = resolve11(cwd);
24425
- const container = resolve11(base2, ".worktrees");
24481
+ const base2 = resolve12(repoDir);
24482
+ const dir2 = resolve12(cwd);
24483
+ const container = resolve12(base2, ".worktrees");
24426
24484
  return dir2 !== base2 && dir2 !== container && dir2.startsWith(container + sep3);
24427
24485
  }
24428
24486
 
@@ -24884,7 +24942,7 @@ init_config2();
24884
24942
  import { spawn as spawn3 } from "node:child_process";
24885
24943
  import { mkdtempSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
24886
24944
  import { tmpdir as tmpdir3 } from "node:os";
24887
- import { dirname as dirname10, join as join7 } from "node:path";
24945
+ import { dirname as dirname11, join as join7 } from "node:path";
24888
24946
  var sshBin = () => effectiveStr("HANOMAN_SSH_BIN") ?? "ssh";
24889
24947
  function consoleArgv(t) {
24890
24948
  return [
@@ -24934,14 +24992,14 @@ function sshExec(t, remoteCmd, opts = {}) {
24934
24992
  SSH_ASKPASS_REQUIRE: "force",
24935
24993
  HANOMAN_SSH_PASSWORD: opts.password
24936
24994
  } : process.env;
24937
- return new Promise((resolve15) => {
24995
+ return new Promise((resolve16) => {
24938
24996
  const p = spawn3(sshBin(), args, { stdio: ["pipe", "pipe", "pipe"], env });
24939
24997
  let out3 = "";
24940
24998
  const timer7 = setTimeout(() => p.kill("SIGKILL"), opts.timeoutMs ?? 6e4);
24941
24999
  const done = (r) => {
24942
25000
  clearTimeout(timer7);
24943
- if (askpass) rmSync4(dirname10(askpass), { recursive: true, force: true });
24944
- resolve15(r);
25001
+ if (askpass) rmSync4(dirname11(askpass), { recursive: true, force: true });
25002
+ resolve16(r);
24945
25003
  };
24946
25004
  p.stdout.on("data", (d) => {
24947
25005
  out3 += d;
@@ -24965,12 +25023,12 @@ import { join as join9 } from "node:path";
24965
25023
 
24966
25024
  // src/runner/deps.ts
24967
25025
  import { existsSync as existsSync9 } from "node:fs";
24968
- import { dirname as dirname11, join as join8 } from "node:path";
25026
+ import { dirname as dirname12, join as join8 } from "node:path";
24969
25027
  function repoRootFrom(startDir) {
24970
25028
  let dir2 = startDir;
24971
25029
  for (let i = 0; i < 8; i++) {
24972
25030
  if (existsSync9(join8(dir2, "pnpm-workspace.yaml"))) return dir2;
24973
- const parent = dirname11(dir2);
25031
+ const parent = dirname12(dir2);
24974
25032
  if (parent === dir2) break;
24975
25033
  dir2 = parent;
24976
25034
  }
@@ -25723,7 +25781,7 @@ init_src();
25723
25781
  init_src();
25724
25782
  init_config2();
25725
25783
  import { readFileSync as readFileSync12 } from "node:fs";
25726
- import { resolve as resolve12, dirname as dirname12 } from "node:path";
25784
+ import { resolve as resolve13, dirname as dirname13 } from "node:path";
25727
25785
  import { fileURLToPath as fileURLToPath3 } from "node:url";
25728
25786
  var UPDATE_COMMAND = "npm i -g hanoman@latest";
25729
25787
  function composeUpdate(x) {
@@ -25748,8 +25806,8 @@ var lastFetchAt = 0;
25748
25806
  var lastLatest = null;
25749
25807
  var lastStatus = "unavailable";
25750
25808
  function runningVersion() {
25751
- const here = dirname12(fileURLToPath3(import.meta.url));
25752
- for (const p of [resolve12(here, "build-info.json"), resolve12(here, "../package.json")]) {
25809
+ const here = dirname13(fileURLToPath3(import.meta.url));
25810
+ for (const p of [resolve13(here, "build-info.json"), resolve13(here, "../package.json")]) {
25753
25811
  try {
25754
25812
  const v = JSON.parse(readFileSync12(p, "utf8")).version;
25755
25813
  if (typeof v === "string" && v) return v;
@@ -26099,7 +26157,7 @@ init_conflicts();
26099
26157
  init_config2();
26100
26158
  import { randomUUID as randomUUID3 } from "node:crypto";
26101
26159
  import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile4, unlink } from "node:fs/promises";
26102
- import { join as join13, resolve as resolve13 } from "node:path";
26160
+ import { join as join13, resolve as resolve14 } from "node:path";
26103
26161
  var EXT = {
26104
26162
  "image/png": ".png",
26105
26163
  "image/jpeg": ".jpg",
@@ -26109,7 +26167,7 @@ function extFor(mimeType) {
26109
26167
  return EXT[mimeType] ?? ".bin";
26110
26168
  }
26111
26169
  function uploadDir() {
26112
- return resolve13(effectiveStr("HANOMAN_UPLOAD_DIR") ?? join13(process.cwd(), "data", "uploads"));
26170
+ return resolve14(effectiveStr("HANOMAN_UPLOAD_DIR") ?? join13(process.cwd(), "data", "uploads"));
26113
26171
  }
26114
26172
  async function saveUpload(buf, mimeType) {
26115
26173
  const dir2 = uploadDir();
@@ -26274,10 +26332,10 @@ import { randomUUID as randomUUID5 } from "node:crypto";
26274
26332
  init_config2();
26275
26333
  import { randomUUID as randomUUID4 } from "node:crypto";
26276
26334
  import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile5, unlink as unlink2 } from "node:fs/promises";
26277
- import { join as join14, resolve as resolve14, basename as basename2 } from "node:path";
26335
+ import { join as join14, resolve as resolve15, basename as basename3 } from "node:path";
26278
26336
  var MAX_TRANSCRIPT_BYTES = 1024 * 1024;
26279
26337
  function transcriptDir() {
26280
- return resolve14(effectiveStr("HANOMAN_TRANSCRIPT_DIR") ?? join14(process.cwd(), "data", "transcripts"));
26338
+ return resolve15(effectiveStr("HANOMAN_TRANSCRIPT_DIR") ?? join14(process.cwd(), "data", "transcripts"));
26281
26339
  }
26282
26340
  function clamp(text) {
26283
26341
  const buf = Buffer.from(text, "utf8");
@@ -26301,14 +26359,14 @@ async function saveTranscript(text) {
26301
26359
  async function readTranscript(key) {
26302
26360
  if (!key) return null;
26303
26361
  try {
26304
- return await readFile5(join14(transcriptDir(), basename2(key)), "utf8");
26362
+ return await readFile5(join14(transcriptDir(), basename3(key)), "utf8");
26305
26363
  } catch {
26306
26364
  return null;
26307
26365
  }
26308
26366
  }
26309
26367
  async function deleteTranscript(key) {
26310
26368
  if (!key) return;
26311
- await unlink2(join14(transcriptDir(), basename2(key))).catch(() => {
26369
+ await unlink2(join14(transcriptDir(), basename3(key))).catch(() => {
26312
26370
  });
26313
26371
  }
26314
26372
 
@@ -27696,7 +27754,7 @@ function leadFailureReason(agent, timeoutMs, err, stdout, stderr) {
27696
27754
  function think(prompt, o) {
27697
27755
  const bin = binFor(o.agent);
27698
27756
  const args = leadArgv({ agent: o.agent, model: o.model, effort: o.effort, prompt });
27699
- return new Promise((resolve15, reject2) => {
27757
+ return new Promise((resolve16, reject2) => {
27700
27758
  const child = execFile9(bin, args, {
27701
27759
  cwd: o.cwd,
27702
27760
  timeout: o.timeoutMs,
@@ -27709,7 +27767,7 @@ function think(prompt, o) {
27709
27767
  reject2(new Error(leadFailureReason(o.agent, o.timeoutMs, err, stdout, stderr)));
27710
27768
  return;
27711
27769
  }
27712
- resolve15(stdout);
27770
+ resolve16(stdout);
27713
27771
  });
27714
27772
  child.stdin?.end();
27715
27773
  });
@@ -27746,9 +27804,9 @@ function acquire(cap, waitMs) {
27746
27804
  return Promise.resolve();
27747
27805
  }
27748
27806
  const startedAt = Date.now();
27749
- return new Promise((resolve15, reject2) => {
27807
+ return new Promise((resolve16, reject2) => {
27750
27808
  const w = {
27751
- grant: resolve15,
27809
+ grant: resolve16,
27752
27810
  deny: reject2,
27753
27811
  timer: setTimeout(() => {
27754
27812
  const i = queue.indexOf(w);
@@ -29812,7 +29870,7 @@ function issuesFromRest(raw) {
29812
29870
  }
29813
29871
  var GH_FIELDS = "number,title,body,author,labels,url,state,createdAt,updatedAt";
29814
29872
  var API2 = "https://api.github.com";
29815
- var defaultRunGh = (args, env) => new Promise((resolve15, reject2) => {
29873
+ var defaultRunGh = (args, env) => new Promise((resolve16, reject2) => {
29816
29874
  execFile11(
29817
29875
  args[0],
29818
29876
  args.slice(1),
@@ -29820,7 +29878,7 @@ var defaultRunGh = (args, env) => new Promise((resolve15, reject2) => {
29820
29878
  (err, stdout, stderr) => {
29821
29879
  const e = err;
29822
29880
  if (e && (e.code === "ENOENT" || e.code === "EACCES")) return reject2(e);
29823
- resolve15({ code: err ? Number(err.code ?? 1) : 0, stdout, stderr });
29881
+ resolve16({ code: err ? Number(err.code ?? 1) : 0, stdout, stderr });
29824
29882
  }
29825
29883
  );
29826
29884
  });
@@ -31359,7 +31417,7 @@ var PUBLIC = /* @__PURE__ */ new Set([
31359
31417
  "GET /api/agent-integration.md"
31360
31418
  ]);
31361
31419
  function buildApp({ requireAuth = true, agentDocFile } = {}) {
31362
- const docFile = agentDocFile !== void 0 ? agentDocFile : pickGuideFile(dirname13(fileURLToPath4(import.meta.url)), process.env, existsSync14);
31420
+ const docFile = agentDocFile !== void 0 ? agentDocFile : pickGuideFile(dirname14(fileURLToPath4(import.meta.url)), process.env, existsSync14);
31363
31421
  const app2 = Fastify({ logger: false, trustProxy: true });
31364
31422
  app2.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
31365
31423
  if (!body) return done(null, void 0);
@@ -31450,7 +31508,7 @@ function buildApp({ requireAuth = true, agentDocFile } = {}) {
31450
31508
  await api.register(client_accounts_default);
31451
31509
  }, { prefix: "/api" });
31452
31510
  if (process.env.NODE_ENV === "production") {
31453
- const dist = pickWebDir(dirname13(fileURLToPath4(import.meta.url)), process.env, existsSync14);
31511
+ const dist = pickWebDir(dirname14(fileURLToPath4(import.meta.url)), process.env, existsSync14);
31454
31512
  if (dist) {
31455
31513
  app2.register(fastifyStatic, { root: dist });
31456
31514
  app2.setNotFoundHandler((req, reply) => req.url.startsWith("/api") ? reply.code(404).send({ error: "not found" }) : reply.sendFile("index.html"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hanoman",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "Orchestrator + dashboard workflow docs-driven untuk sesi Claude Code / Codex",
5
5
  "type": "module",
6
6
  "repository": {