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 +11 -0
- package/dist/build-info.json +3 -3
- package/dist/cli.js +80 -38
- package/package.json +8 -6
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`.
|
package/dist/build-info.json
CHANGED
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
|
|
5963
|
+
import { join as join4 } from "node:path";
|
|
5922
5964
|
function loadConfig(repoRoot) {
|
|
5923
|
-
const p =
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
6009
|
+
for (const name of readdirSync2(dir)) {
|
|
5968
6010
|
if (name.startsWith(".")) continue;
|
|
5969
|
-
const abs =
|
|
5970
|
-
if (
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
6132
|
+
const docsRoot = join8(root, docsDir);
|
|
6091
6133
|
const corpus = walkDocs(docsRoot);
|
|
6092
6134
|
const read = (rel) => {
|
|
6093
6135
|
try {
|
|
6094
|
-
return readFileSync5(
|
|
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(
|
|
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
|
|
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:
|
|
6179
|
-
{ from:
|
|
6180
|
-
{ from:
|
|
6181
|
-
{ from:
|
|
6182
|
-
{ from:
|
|
6183
|
-
{ from:
|
|
6184
|
-
{ from:
|
|
6185
|
-
{ from:
|
|
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
|
|
6270
|
+
import { dirname as dirname5, join as join10 } from "node:path";
|
|
6229
6271
|
function depVersions(repo) {
|
|
6230
|
-
const read = (p) => JSON.parse(readFileSync6(
|
|
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(
|
|
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 ?
|
|
6256
|
-
const version2 = JSON.parse(readFileSync6(
|
|
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(
|
|
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 =
|
|
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(
|
|
6282
|
-
writeFileSync2(
|
|
6283
|
-
const missing = REQUIRED_ARTIFACTS.filter((a) => !existsSync7(
|
|
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.
|
|
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
|
+
"@fastify/cookie": "^9.4.0",
|
|
29
29
|
"@fastify/static": "^7.0.0",
|
|
30
30
|
"@fastify/websocket": "^10.0.1",
|
|
31
|
-
"@
|
|
32
|
-
"
|
|
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
|
}
|