hanoman 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -85,6 +85,17 @@ punya riwayat migrasi hanoman — biasanya bukan DB hanoman versi ini (sisa prot
85
85
  berkas tool lain yang kebetulan bernama sama). hanoman **tidak** mengubah isinya. Pindahkan berkas
86
86
  itu lalu jalankan ulang, atau tunjuk berkas lain dengan `hanoman --db /path/baru.db`.
87
87
 
88
+ ## Kalau terminal sesi terbuka tapi kosong
89
+
90
+ Sesi hanoman hidup di dalam **tmux**; layarnya dialirkan ke browser lewat node-pty. Dua sebab:
91
+
92
+ - **tmux belum terpasang** — `brew install tmux` (macOS) atau paket distro Anda.
93
+ - **`spawn-helper` node-pty tak executable** — node-pty menerbitkan biner pendampingnya dengan mode
94
+ `0644`, jadi `posix_spawnp` gagal dan tak satu byte pun mengalir ke terminal walau sesinya hidup.
95
+ Sejak `0.1.3` `hanoman` memperbaikinya sendiri saat start (sekali per instalasi). Bila instalasi
96
+ global itu milik root dan hanoman dijalankan sebagai pengguna biasa, chmod-nya ditolak — perbaiki
97
+ manual: `sudo chmod +x "$(npm root -g)"/hanoman/node_modules/node-pty/prebuilds/*/spawn-helper`.
98
+
88
99
  ## Lisensi
89
100
 
90
101
  MIT — lihat `LICENSE`.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.2",
3
- "sha": "f55334d",
4
- "builtAt": "2026-07-30T07:38:46.598Z"
2
+ "version": "0.1.3",
3
+ "sha": "c5266ce",
4
+ "builtAt": "2026-07-30T08:59:12.271Z"
5
5
  }
package/dist/cli.js CHANGED
@@ -256,14 +256,17 @@ __export(start_exports, {
256
256
  default: () => start,
257
257
  distDir: () => distDir,
258
258
  ensurePrismaClient: () => ensurePrismaClient,
259
+ ensureSpawnHelpersExecutable: () => ensureSpawnHelpersExecutable,
259
260
  migrateFailureHint: () => migrateFailureHint,
260
261
  parseStartArgs: () => parseStartArgs,
261
- prismaClientUsable: () => prismaClientUsable
262
+ prismaClientUsable: () => prismaClientUsable,
263
+ repairSpawnHelper: () => repairSpawnHelper,
264
+ spawnHelperPaths: () => spawnHelperPaths
262
265
  });
263
266
  import { spawn, execFileSync } from "node:child_process";
264
267
  import { createRequire } from "node:module";
265
- import { existsSync, mkdirSync } from "node:fs";
266
- import { dirname as dirname2, resolve as resolvePath } from "node:path";
268
+ import { existsSync, mkdirSync, readdirSync, statSync, chmodSync } from "node:fs";
269
+ import { dirname as dirname2, join as join3, resolve as resolvePath } from "node:path";
267
270
  import { fileURLToPath } from "node:url";
268
271
  function parseStartArgs(argv) {
269
272
  const out = { port: null, host: null, db: null, migrate: true };
@@ -355,6 +358,44 @@ async function ensurePrismaClient(schema, dbUrl, ctx) {
355
358
  return false;
356
359
  }
357
360
  }
361
+ function spawnHelperPaths(ptyDir, listDir, exists) {
362
+ const dirs = [join3(ptyDir, "build", "Release")];
363
+ for (const name of listDir(join3(ptyDir, "prebuilds"))) dirs.push(join3(ptyDir, "prebuilds", name));
364
+ return dirs.map((d) => join3(d, "spawn-helper")).filter(exists);
365
+ }
366
+ function ensureSpawnHelpersExecutable(paths2, ops) {
367
+ const fixed = [];
368
+ for (const p of paths2) {
369
+ try {
370
+ const mode = ops.mode(p) & 4095;
371
+ if ((mode & 73) === 73) continue;
372
+ ops.chmod(p, mode | 73);
373
+ fixed.push(p);
374
+ } catch {
375
+ }
376
+ }
377
+ return fixed;
378
+ }
379
+ function repairSpawnHelper(ctx) {
380
+ let ptyDir;
381
+ try {
382
+ ptyDir = dirname2(createRequire(import.meta.url).resolve("node-pty/package.json"));
383
+ } catch {
384
+ return;
385
+ }
386
+ const paths2 = spawnHelperPaths(ptyDir, (d) => {
387
+ try {
388
+ return readdirSync(d);
389
+ } catch {
390
+ return [];
391
+ }
392
+ }, existsSync);
393
+ const fixed = ensureSpawnHelpersExecutable(paths2, {
394
+ mode: (p) => statSync(p).mode,
395
+ chmod: (p, m) => chmodSync(p, m)
396
+ });
397
+ if (fixed.length) ctx.stdout("hanoman \xB7 memperbaiki izin `spawn-helper` node-pty (sekali per instalasi)\n");
398
+ }
358
399
  async function start(argv, ctx) {
359
400
  let opts;
360
401
  try {
@@ -386,6 +427,7 @@ async function start(argv, ctx) {
386
427
  return 1;
387
428
  }
388
429
  if (!await ensurePrismaClient(layout.schema, dbUrl, ctx)) return 1;
430
+ repairSpawnHelper(ctx);
389
431
  if (opts.migrate) {
390
432
  ctx.stdout(`hanoman \xB7 menerapkan migrasi ke ${dbFilePath(dbUrl)}
391
433
  `);
@@ -5918,9 +5960,9 @@ var init_migrate_pg = __esm({
5918
5960
 
5919
5961
  // src/config.ts
5920
5962
  import { readFileSync, existsSync as existsSync4 } from "node:fs";
5921
- import { join as join3 } from "node:path";
5963
+ import { join as join4 } from "node:path";
5922
5964
  function loadConfig(repoRoot) {
5923
- const p = join3(repoRoot, "hanoman.config.json");
5965
+ const p = join4(repoRoot, "hanoman.config.json");
5924
5966
  const raw = existsSync4(p) ? JSON.parse(readFileSync(p, "utf8")) : {};
5925
5967
  return zHanomanConfig.parse(raw);
5926
5968
  }
@@ -5933,12 +5975,12 @@ var init_config2 = __esm({
5933
5975
 
5934
5976
  // src/repo.ts
5935
5977
  import { spawnSync } from "node:child_process";
5936
- import { join as join4 } from "node:path";
5978
+ import { join as join5 } from "node:path";
5937
5979
  function resolveRepo(cwd) {
5938
5980
  const r = spawnSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8" });
5939
5981
  const root = r.status === 0 ? r.stdout.trim() : cwd;
5940
5982
  const { docsDir } = loadConfig(root);
5941
- return { root, docsDir, indexPath: join4(root, docsDir, "README.md") };
5983
+ return { root, docsDir, indexPath: join5(root, docsDir, "README.md") };
5942
5984
  }
5943
5985
  var init_repo = __esm({
5944
5986
  "src/repo.ts"() {
@@ -5948,8 +5990,8 @@ var init_repo = __esm({
5948
5990
  });
5949
5991
 
5950
5992
  // src/docs-model.ts
5951
- import { readFileSync as readFileSync2, readdirSync, statSync } from "node:fs";
5952
- import { join as join5, relative } from "node:path";
5993
+ import { readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
5994
+ import { join as join6, relative } from "node:path";
5953
5995
  function parseIndex(indexPath) {
5954
5996
  const md = readFileSync2(indexPath, "utf8");
5955
5997
  const out = /* @__PURE__ */ new Set();
@@ -5964,10 +6006,10 @@ function parseIndex(indexPath) {
5964
6006
  function walkDocs(docsRoot) {
5965
6007
  const out = [];
5966
6008
  const walk = (dir) => {
5967
- for (const name of readdirSync(dir)) {
6009
+ for (const name of readdirSync2(dir)) {
5968
6010
  if (name.startsWith(".")) continue;
5969
- const abs = join5(dir, name);
5970
- if (statSync(abs).isDirectory()) walk(abs);
6011
+ const abs = join6(dir, name);
6012
+ if (statSync2(abs).isDirectory()) walk(abs);
5971
6013
  else out.push(relative(docsRoot, abs).split("\\").join("/"));
5972
6014
  }
5973
6015
  };
@@ -5998,17 +6040,17 @@ var init_docs_model = __esm({
5998
6040
  });
5999
6041
 
6000
6042
  // src/verify.ts
6001
- import { join as join6 } from "node:path";
6043
+ import { join as join7 } from "node:path";
6002
6044
  import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
6003
6045
  function scanCoverage(cwd) {
6004
6046
  const { root, docsDir, indexPath } = resolveRepo(cwd);
6005
- const docsRoot = join6(root, docsDir);
6047
+ const docsRoot = join7(root, docsDir);
6006
6048
  if (!existsSync5(docsRoot)) return { coverage: 100, cats: [] };
6007
6049
  if (!existsSync5(indexPath)) throw new Error(`index Source of Truth tidak ada: ${indexPath}`);
6008
6050
  const corpus = walkDocs(docsRoot);
6009
6051
  const read = (rel) => {
6010
6052
  try {
6011
- return readFileSync3(join6(docsRoot, rel), "utf8");
6053
+ return readFileSync3(join7(docsRoot, rel), "utf8");
6012
6054
  } catch {
6013
6055
  return null;
6014
6056
  }
@@ -6082,23 +6124,23 @@ __export(docs_index_exports, {
6082
6124
  default: () => docs_index_default
6083
6125
  });
6084
6126
  import { parseArgs as parseArgs2 } from "node:util";
6085
- import { join as join7 } from "node:path";
6127
+ import { join as join8 } from "node:path";
6086
6128
  import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
6087
6129
  async function docs_index_default(args, ctx) {
6088
6130
  const { values } = parseArgs2({ args, options: { check: { type: "boolean" }, fix: { type: "boolean" } }, allowPositionals: true });
6089
6131
  const { root, docsDir, indexPath } = resolveRepo(ctx.cwd);
6090
- const docsRoot = join7(root, docsDir);
6132
+ const docsRoot = join8(root, docsDir);
6091
6133
  const corpus = walkDocs(docsRoot);
6092
6134
  const read = (rel) => {
6093
6135
  try {
6094
- return readFileSync5(join7(docsRoot, rel), "utf8");
6136
+ return readFileSync5(join8(docsRoot, rel), "utf8");
6095
6137
  } catch {
6096
6138
  return null;
6097
6139
  }
6098
6140
  };
6099
6141
  const linked = linkedSetFrom(INDEX_NAME, corpus, read);
6100
6142
  const unlinked = corpus.filter((f) => f !== INDEX_NAME && !linked.has(f));
6101
- const dangling = [...parseIndex(indexPath)].filter((p) => !existsSync6(join7(docsRoot, p)));
6143
+ const dangling = [...parseIndex(indexPath)].filter((p) => !existsSync6(join8(docsRoot, p)));
6102
6144
  if (values.fix) {
6103
6145
  for (const f of unlinked) addLink(indexPath, f, f.split("/")[0]);
6104
6146
  ctx.stdout(`linked ${unlinked.length} doc(s)
@@ -6151,7 +6193,7 @@ var init_docs_link = __esm({
6151
6193
  });
6152
6194
 
6153
6195
  // src/release/pack.ts
6154
- import { join as join8 } from "node:path";
6196
+ import { join as join9 } from "node:path";
6155
6197
  function packageJsonFor(version2, deps) {
6156
6198
  return {
6157
6199
  name: PKG_NAME,
@@ -6175,14 +6217,14 @@ function packageJsonFor(version2, deps) {
6175
6217
  }
6176
6218
  function copyPlan(repo) {
6177
6219
  return [
6178
- { from: join8(repo, "server/dist/server.js"), to: "dist/server.js" },
6179
- { from: join8(repo, "server/dist/build-info.json"), to: "dist/build-info.json" },
6180
- { from: join8(repo, "cli/dist/hanoman.js"), to: "dist/cli.js" },
6181
- { from: join8(repo, "src/dist"), to: "web", dir: true },
6182
- { from: join8(repo, "server/prisma/schema.prisma"), to: "prisma/schema.prisma" },
6183
- { from: join8(repo, "server/prisma/migrations"), to: "prisma/migrations", dir: true },
6184
- { from: join8(repo, "internal/docs/operations/npm-readme.md"), to: "README.md" },
6185
- { from: join8(repo, "LICENSE"), to: "LICENSE" }
6220
+ { from: join9(repo, "server/dist/server.js"), to: "dist/server.js" },
6221
+ { from: join9(repo, "server/dist/build-info.json"), to: "dist/build-info.json" },
6222
+ { from: join9(repo, "cli/dist/hanoman.js"), to: "dist/cli.js" },
6223
+ { from: join9(repo, "src/dist"), to: "web", dir: true },
6224
+ { from: join9(repo, "server/prisma/schema.prisma"), to: "prisma/schema.prisma" },
6225
+ { from: join9(repo, "server/prisma/migrations"), to: "prisma/migrations", dir: true },
6226
+ { from: join9(repo, "internal/docs/operations/npm-readme.md"), to: "README.md" },
6227
+ { from: join9(repo, "LICENSE"), to: "LICENSE" }
6186
6228
  ];
6187
6229
  }
6188
6230
  var PKG_NAME, REPO_URL, RUNTIME_DEPS, REQUIRED_ARTIFACTS, BIN_SHIM;
@@ -6225,9 +6267,9 @@ __export(pack_exports, {
6225
6267
  default: () => pack
6226
6268
  });
6227
6269
  import { cpSync, existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
6228
- import { dirname as dirname5, join as join9 } from "node:path";
6270
+ import { dirname as dirname5, join as join10 } from "node:path";
6229
6271
  function depVersions(repo) {
6230
- const read = (p) => JSON.parse(readFileSync6(join9(repo, p), "utf8"));
6272
+ const read = (p) => JSON.parse(readFileSync6(join10(repo, p), "utf8"));
6231
6273
  const server = read("server/package.json"), cli = read("cli/package.json");
6232
6274
  const pools = [server.dependencies, server.devDependencies, cli.dependencies, cli.devDependencies];
6233
6275
  const out = {};
@@ -6247,13 +6289,13 @@ async function pack(argv, ctx) {
6247
6289
  `);
6248
6290
  return 1;
6249
6291
  }
6250
- if (!existsSync7(join9(repo, "server/prisma/schema.prisma"))) {
6292
+ if (!existsSync7(join10(repo, "server/prisma/schema.prisma"))) {
6251
6293
  ctx.stderr("pack: hanya bisa dijalankan dari checkout repo hanoman\n");
6252
6294
  return 1;
6253
6295
  }
6254
6296
  const outIdx = argv.indexOf("--out");
6255
- const out = outIdx === -1 ? join9(repo, "dist-npm") : join9(repo, argv[outIdx + 1] ?? "dist-npm");
6256
- const version2 = JSON.parse(readFileSync6(join9(repo, "package.json"), "utf8")).version;
6297
+ const out = outIdx === -1 ? join10(repo, "dist-npm") : join10(repo, argv[outIdx + 1] ?? "dist-npm");
6298
+ const version2 = JSON.parse(readFileSync6(join10(repo, "package.json"), "utf8")).version;
6257
6299
  if (!version2) {
6258
6300
  ctx.stderr("pack: root package.json tanpa field version\n");
6259
6301
  return 1;
@@ -6267,20 +6309,20 @@ async function pack(argv, ctx) {
6267
6309
  return 1;
6268
6310
  }
6269
6311
  rmSync(out, { recursive: true, force: true });
6270
- mkdirSync2(join9(out, "bin"), { recursive: true });
6312
+ mkdirSync2(join10(out, "bin"), { recursive: true });
6271
6313
  for (const item of copyPlan(repo)) {
6272
6314
  if (!existsSync7(item.from)) {
6273
6315
  ctx.stderr(`pack: artefak hilang \u2014 ${item.from} (sudah \`pnpm build\`?)
6274
6316
  `);
6275
6317
  return 1;
6276
6318
  }
6277
- const dest = join9(out, item.to);
6319
+ const dest = join10(out, item.to);
6278
6320
  mkdirSync2(dirname5(dest), { recursive: true });
6279
6321
  cpSync(item.from, dest, item.dir ? { recursive: true } : {});
6280
6322
  }
6281
- writeFileSync2(join9(out, "bin/hanoman.mjs"), BIN_SHIM);
6282
- writeFileSync2(join9(out, "package.json"), JSON.stringify(packageJsonFor(version2, deps), null, 2) + "\n");
6283
- const missing = REQUIRED_ARTIFACTS.filter((a) => !existsSync7(join9(out, a)));
6323
+ writeFileSync2(join10(out, "bin/hanoman.mjs"), BIN_SHIM);
6324
+ writeFileSync2(join10(out, "package.json"), JSON.stringify(packageJsonFor(version2, deps), null, 2) + "\n");
6325
+ const missing = REQUIRED_ARTIFACTS.filter((a) => !existsSync7(join10(out, a)));
6284
6326
  if (missing.length) {
6285
6327
  ctx.stderr(`pack: artefak wajib hilang: ${missing.join(", ")}
6286
6328
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hanoman",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Orchestrator + dashboard workflow docs-driven untuk sesi Claude Code / Codex",
5
5
  "type": "module",
6
6
  "repository": {
@@ -25,15 +25,17 @@
25
25
  "LICENSE"
26
26
  ],
27
27
  "dependencies": {
28
- "fastify": "^4.28.0",
28
+ "@fastify/cookie": "^9.4.0",
29
29
  "@fastify/static": "^7.0.0",
30
30
  "@fastify/websocket": "^10.0.1",
31
- "@fastify/cookie": "^9.4.0",
32
- "@prisma/client": "^6.19.0",
31
+ "@prisma/client": "^6.19.3",
32
+ "fastify": "^4.28.0",
33
33
  "node-pty": "^1.1.0",
34
34
  "pdfkit": "^0.19.1",
35
- "prisma": "^6.19.0",
36
35
  "pg": "^8.13.1"
37
36
  },
38
- "license": "MIT"
37
+ "license": "MIT",
38
+ "devDependencies": {
39
+ "prisma": "^6.19.3"
40
+ }
39
41
  }