hanoman 0.1.52 → 0.1.53
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/build-info.json +3 -3
- package/dist/cli.js +13 -2
- package/dist/server.js +957 -590
- package/package.json +1 -1
- package/web/assets/{index-VxebVskK.js → index-fCgQsVLZ.js} +1518 -1518
- package/web/index.html +1 -1
package/dist/server.js
CHANGED
|
@@ -4189,10 +4189,10 @@ function convertPayload(to, payload) {
|
|
|
4189
4189
|
const fromShape = shapeOfPayload(payload);
|
|
4190
4190
|
const toShape = payloadShapeFor(to);
|
|
4191
4191
|
const fromAudit = str3("fromAudit");
|
|
4192
|
-
const done = (
|
|
4193
|
-
payload:
|
|
4192
|
+
const done = (out4, dropped) => ({
|
|
4193
|
+
payload: out4,
|
|
4194
4194
|
dropped,
|
|
4195
|
-
missing: SHAPE_REQUIRED[toShape].filter((f) => typeof
|
|
4195
|
+
missing: SHAPE_REQUIRED[toShape].filter((f) => typeof out4[f] !== "string" || out4[f] === "")
|
|
4196
4196
|
});
|
|
4197
4197
|
if (fromShape === toShape) return { payload: { ...p }, dropped: [], missing: [] };
|
|
4198
4198
|
if (toShape === "qa") {
|
|
@@ -4967,25 +4967,25 @@ var init_agent = __esm({
|
|
|
4967
4967
|
// ../shared/src/custom-agent.ts
|
|
4968
4968
|
function mentionsOf(v) {
|
|
4969
4969
|
if (!Array.isArray(v)) return [];
|
|
4970
|
-
const
|
|
4971
|
-
for (const x of v) if (typeof x === "string" && x && !
|
|
4972
|
-
return
|
|
4970
|
+
const out4 = [];
|
|
4971
|
+
for (const x of v) if (typeof x === "string" && x && !out4.includes(x)) out4.push(x);
|
|
4972
|
+
return out4;
|
|
4973
4973
|
}
|
|
4974
4974
|
function runtimeOf(v) {
|
|
4975
4975
|
return typeof v === "string" && AGENT_RUNTIMES.includes(v) ? v : null;
|
|
4976
4976
|
}
|
|
4977
4977
|
function toolsOf(v) {
|
|
4978
4978
|
if (!Array.isArray(v)) return null;
|
|
4979
|
-
const
|
|
4980
|
-
for (const x of v) if (typeof x === "string" && x && !
|
|
4981
|
-
return
|
|
4979
|
+
const out4 = [];
|
|
4980
|
+
for (const x of v) if (typeof x === "string" && x && !out4.includes(x)) out4.push(x);
|
|
4981
|
+
return out4;
|
|
4982
4982
|
}
|
|
4983
4983
|
function resolveTools(a) {
|
|
4984
4984
|
const base2 = a.tools ?? [...DEFAULT_AGENT_TOOLS];
|
|
4985
4985
|
const canMention = (a.mentions ?? []).length > 0;
|
|
4986
|
-
const
|
|
4987
|
-
if (canMention)
|
|
4988
|
-
return
|
|
4986
|
+
const out4 = base2.filter((t) => t !== MENTION_TOOL);
|
|
4987
|
+
if (canMention) out4.push(MENTION_TOOL);
|
|
4988
|
+
return out4;
|
|
4989
4989
|
}
|
|
4990
4990
|
function detectCycle(nodes) {
|
|
4991
4991
|
const edges = new Map(nodes.map((n2) => [n2.name, n2.mentions]));
|
|
@@ -5392,7 +5392,7 @@ var init_prd_status = __esm({
|
|
|
5392
5392
|
|
|
5393
5393
|
// ../shared/src/cron-expr.ts
|
|
5394
5394
|
function parseField(raw2, lo, hi) {
|
|
5395
|
-
const
|
|
5395
|
+
const out4 = /* @__PURE__ */ new Set();
|
|
5396
5396
|
for (const part of raw2.split(",")) {
|
|
5397
5397
|
const slash = part.indexOf("/");
|
|
5398
5398
|
const rangePart = slash === -1 ? part : part.slice(0, slash);
|
|
@@ -5418,9 +5418,9 @@ function parseField(raw2, lo, hi) {
|
|
|
5418
5418
|
to = Number(m[2]);
|
|
5419
5419
|
}
|
|
5420
5420
|
if (from < lo || to > hi || from > to) return null;
|
|
5421
|
-
for (let v = from; v <= to; v += step)
|
|
5421
|
+
for (let v = from; v <= to; v += step) out4.add(v);
|
|
5422
5422
|
}
|
|
5423
|
-
return
|
|
5423
|
+
return out4.size ? out4 : null;
|
|
5424
5424
|
}
|
|
5425
5425
|
function parseCron(expr) {
|
|
5426
5426
|
const parts = expr.trim().split(/\s+/);
|
|
@@ -6055,9 +6055,20 @@ var init_api = __esm({
|
|
|
6055
6055
|
// SPEC-255 · ADR-0064 · rename slug project (efek: DSN, Help Center, sync).
|
|
6056
6056
|
projectRename: (id) => `${API}/projects/${encodeURIComponent(id)}/rename`,
|
|
6057
6057
|
branches: (id) => `${API}/projects/${id}/branches`,
|
|
6058
|
-
// SPEC-360 · ADR-0077 · branch
|
|
6059
|
-
|
|
6058
|
+
// SPEC-360 · ADR-0077 · daftar branch (nilai turunan git) + hapus batch local/origin.
|
|
6059
|
+
// SPEC-859 · `include=all` memuat branch yang belum ter-merge; tanpa itu hanya yang ter-merge.
|
|
6060
|
+
branchesUnused: (id, base2, include) => {
|
|
6061
|
+
const q = new URLSearchParams();
|
|
6062
|
+
if (base2) q.set("base", base2);
|
|
6063
|
+
if (include) q.set("include", include);
|
|
6064
|
+
const s2 = q.toString();
|
|
6065
|
+
return `${API}/projects/${id}/branches/unused${s2 ? `?${s2}` : ""}`;
|
|
6066
|
+
},
|
|
6060
6067
|
branchesDelete: (id) => `${API}/projects/${id}/branches/delete`,
|
|
6068
|
+
// SPEC-861 · ADR-0132 · worktree HIDUP (nilai turunan git) + sinyal mahal per baris + hapus batch.
|
|
6069
|
+
worktrees: (id) => `${API}/projects/${id}/worktrees`,
|
|
6070
|
+
worktreeStats: (id, name2) => `${API}/projects/${id}/worktrees/stats?name=${encodeURIComponent(name2)}`,
|
|
6071
|
+
worktreesDelete: (id) => `${API}/projects/${id}/worktrees/delete`,
|
|
6061
6072
|
// SPEC-217 · path per-mesin (LocalBinding, tak disync) + clone dari gitRemote
|
|
6062
6073
|
binding: (id) => `${API}/projects/${id}/binding`,
|
|
6063
6074
|
clone: (id) => `${API}/projects/${id}/clone`,
|
|
@@ -6307,13 +6318,13 @@ function resolveLink(fromRel, target2) {
|
|
|
6307
6318
|
if (clean2.startsWith("/")) return clean2.slice(1).split("/").filter((p) => p && p !== ".").join("/");
|
|
6308
6319
|
const dir2 = fromRel.includes("/") ? fromRel.slice(0, fromRel.lastIndexOf("/")) : "";
|
|
6309
6320
|
const parts = (dir2 ? dir2.split("/") : []).concat(clean2.replace(/^\.\//, "").split("/"));
|
|
6310
|
-
const
|
|
6321
|
+
const out4 = [];
|
|
6311
6322
|
for (const p of parts) {
|
|
6312
6323
|
if (p === "" || p === ".") continue;
|
|
6313
|
-
if (p === "..")
|
|
6314
|
-
else
|
|
6324
|
+
if (p === "..") out4.pop();
|
|
6325
|
+
else out4.push(p);
|
|
6315
6326
|
}
|
|
6316
|
-
return
|
|
6327
|
+
return out4.join("/");
|
|
6317
6328
|
}
|
|
6318
6329
|
function linkedSetFrom(indexRel, docs, read2) {
|
|
6319
6330
|
const inCorpus = new Set(docs);
|
|
@@ -6755,17 +6766,17 @@ function matchesEvent(subscribed, type) {
|
|
|
6755
6766
|
return false;
|
|
6756
6767
|
}
|
|
6757
6768
|
function projectRow(def2, row) {
|
|
6758
|
-
const
|
|
6759
|
-
for (const f of def2.fields) if (f in row)
|
|
6760
|
-
return
|
|
6769
|
+
const out4 = {};
|
|
6770
|
+
for (const f of def2.fields) if (f in row) out4[f] = iso2(row[f]);
|
|
6771
|
+
return out4;
|
|
6761
6772
|
}
|
|
6762
6773
|
function diffFields(def2, before2, after) {
|
|
6763
|
-
const
|
|
6774
|
+
const out4 = [];
|
|
6764
6775
|
for (const f of def2.fields) {
|
|
6765
6776
|
if (IGNORED_FIELDS.has(f)) continue;
|
|
6766
|
-
if (JSON.stringify(iso2(before2[f]) ?? null) !== JSON.stringify(iso2(after[f]) ?? null))
|
|
6777
|
+
if (JSON.stringify(iso2(before2[f]) ?? null) !== JSON.stringify(iso2(after[f]) ?? null)) out4.push(f);
|
|
6767
6778
|
}
|
|
6768
|
-
return
|
|
6779
|
+
return out4;
|
|
6769
6780
|
}
|
|
6770
6781
|
function eventTypeFor(def2, action, changed) {
|
|
6771
6782
|
if (action === "updated") {
|
|
@@ -7298,9 +7309,9 @@ var init_mcp_shape = __esm({
|
|
|
7298
7309
|
OBJECTIVE_CLIP = 200;
|
|
7299
7310
|
MARK = "\u2026 (dipotong)";
|
|
7300
7311
|
pick = (row, keys) => {
|
|
7301
|
-
const
|
|
7302
|
-
for (const k of keys) if (row[k] !== void 0)
|
|
7303
|
-
return
|
|
7312
|
+
const out4 = {};
|
|
7313
|
+
for (const k of keys) if (row[k] !== void 0) out4[k] = row[k];
|
|
7314
|
+
return out4;
|
|
7304
7315
|
};
|
|
7305
7316
|
shapeProject = (row) => pick(row, ["id", "name", "kind", "desc", "backlog", "topStage", "coverage", "schedulerOptIn", "leadOptIn"]);
|
|
7306
7317
|
shapeProjectDetail = (row) => pick(row, [
|
|
@@ -7361,9 +7372,9 @@ var init_mcp_shape = __esm({
|
|
|
7361
7372
|
|
|
7362
7373
|
// ../shared/src/mcp-catalog.ts
|
|
7363
7374
|
function query(pairs) {
|
|
7364
|
-
const
|
|
7365
|
-
for (const [k, v] of Object.entries(pairs)) if (v !== void 0)
|
|
7366
|
-
return
|
|
7375
|
+
const out4 = {};
|
|
7376
|
+
for (const [k, v] of Object.entries(pairs)) if (v !== void 0) out4[k] = v;
|
|
7377
|
+
return out4;
|
|
7367
7378
|
}
|
|
7368
7379
|
function reshapePage(raw2, fn2) {
|
|
7369
7380
|
const p = raw2;
|
|
@@ -8634,9 +8645,9 @@ function resolveGoalCondition(a, override, template) {
|
|
|
8634
8645
|
return (picked ? picked.trim() : defaultGoalCondition(a)).slice(0, GOAL_MAX);
|
|
8635
8646
|
}
|
|
8636
8647
|
function goalChunks(line, size = GOAL_CHUNK) {
|
|
8637
|
-
const
|
|
8638
|
-
for (let i = 0; i < line.length; i += size)
|
|
8639
|
-
return
|
|
8648
|
+
const out4 = [];
|
|
8649
|
+
for (let i = 0; i < line.length; i += size) out4.push(line.slice(i, i + size));
|
|
8650
|
+
return out4;
|
|
8640
8651
|
}
|
|
8641
8652
|
var GOAL_MAX, goalOneLine, GOAL_CHUNK;
|
|
8642
8653
|
var init_goal = __esm({
|
|
@@ -8782,16 +8793,16 @@ function agentPromptOf(def2, roster) {
|
|
|
8782
8793
|
}
|
|
8783
8794
|
function renderAgentsJson(defs) {
|
|
8784
8795
|
if (defs.length === 0) return "";
|
|
8785
|
-
const
|
|
8796
|
+
const out4 = {};
|
|
8786
8797
|
for (const d of defs) {
|
|
8787
|
-
|
|
8798
|
+
out4[d.name] = {
|
|
8788
8799
|
description: d.description,
|
|
8789
8800
|
prompt: agentPromptOf(d, defs),
|
|
8790
8801
|
tools: resolveTools({ tools: d.tools, mentions: d.mentions }),
|
|
8791
8802
|
...d.model ? { model: d.model } : {}
|
|
8792
8803
|
};
|
|
8793
8804
|
}
|
|
8794
|
-
return JSON.stringify(
|
|
8805
|
+
return JSON.stringify(out4);
|
|
8795
8806
|
}
|
|
8796
8807
|
function agentRosterBlock(defs) {
|
|
8797
8808
|
if (defs.length === 0) return "";
|
|
@@ -8924,27 +8935,27 @@ function asSkill(dir2, pkg, name2 = basename2(dir2)) {
|
|
|
8924
8935
|
return { id: pkg ? `${pkg}:${name2}` : name2, name: name2, pkg, dir: dir2 };
|
|
8925
8936
|
}
|
|
8926
8937
|
function skillsUnder(dir2, pkg, depth = 2) {
|
|
8927
|
-
const
|
|
8938
|
+
const out4 = [];
|
|
8928
8939
|
for (const name2 of dirsIn(dir2)) {
|
|
8929
8940
|
const sub = join2(dir2, name2);
|
|
8930
|
-
if (isSkillDir(sub))
|
|
8931
|
-
else if (depth > 1)
|
|
8941
|
+
if (isSkillDir(sub)) out4.push(asSkill(sub, pkg, name2));
|
|
8942
|
+
else if (depth > 1) out4.push(...skillsUnder(sub, pkg, depth - 1));
|
|
8932
8943
|
}
|
|
8933
|
-
return
|
|
8944
|
+
return out4;
|
|
8934
8945
|
}
|
|
8935
8946
|
function manifestSkills(installPath, pkg) {
|
|
8936
8947
|
for (const marker of [".claude-plugin", ".codex-plugin"]) {
|
|
8937
8948
|
const j = readJson(join2(installPath, marker, "plugin.json"));
|
|
8938
8949
|
const declared = j?.skills;
|
|
8939
8950
|
if (!Array.isArray(declared)) continue;
|
|
8940
|
-
const
|
|
8951
|
+
const out4 = [];
|
|
8941
8952
|
for (const rel of declared) {
|
|
8942
8953
|
if (typeof rel !== "string" || !rel) continue;
|
|
8943
8954
|
const p = resolve6(installPath, rel);
|
|
8944
8955
|
const dir2 = basename2(p) === "SKILL.md" ? dirname3(p) : p;
|
|
8945
|
-
if (isSkillDir(dir2))
|
|
8956
|
+
if (isSkillDir(dir2)) out4.push(asSkill(dir2, pkg));
|
|
8946
8957
|
}
|
|
8947
|
-
if (
|
|
8958
|
+
if (out4.length) return out4;
|
|
8948
8959
|
}
|
|
8949
8960
|
return [];
|
|
8950
8961
|
}
|
|
@@ -8953,15 +8964,15 @@ function pluginSkills(installPath, pkg) {
|
|
|
8953
8964
|
return declared.length ? declared : skillsUnder(join2(installPath, "skills"), pkg);
|
|
8954
8965
|
}
|
|
8955
8966
|
function lockPluginNames(agentsHome) {
|
|
8956
|
-
const
|
|
8967
|
+
const out4 = /* @__PURE__ */ new Map();
|
|
8957
8968
|
const j = readJson(join2(agentsHome, ".skill-lock.json"));
|
|
8958
8969
|
const skills = j?.skills;
|
|
8959
|
-
if (!skills || typeof skills !== "object") return
|
|
8970
|
+
if (!skills || typeof skills !== "object") return out4;
|
|
8960
8971
|
for (const [name2, entry] of Object.entries(skills)) {
|
|
8961
8972
|
const pkg = entry?.pluginName;
|
|
8962
|
-
if (typeof pkg === "string" && pkg.trim())
|
|
8973
|
+
if (typeof pkg === "string" && pkg.trim()) out4.set(name2, pkg.trim());
|
|
8963
8974
|
}
|
|
8964
|
-
return
|
|
8975
|
+
return out4;
|
|
8965
8976
|
}
|
|
8966
8977
|
function splitPluginKey(key) {
|
|
8967
8978
|
const at = key.lastIndexOf("@");
|
|
@@ -8971,39 +8982,39 @@ function manifestRoots(home3) {
|
|
|
8971
8982
|
const j = readJson(join2(home3, "plugins", "installed_plugins.json"));
|
|
8972
8983
|
const plugins = j?.plugins;
|
|
8973
8984
|
if (!plugins || typeof plugins !== "object") return [];
|
|
8974
|
-
const
|
|
8985
|
+
const out4 = [];
|
|
8975
8986
|
for (const [key, entries] of Object.entries(plugins)) {
|
|
8976
8987
|
const { pkg, marketplace } = splitPluginKey(key);
|
|
8977
8988
|
for (const e of Array.isArray(entries) ? entries : []) {
|
|
8978
8989
|
const p = e?.installPath;
|
|
8979
|
-
if (typeof p === "string" && p)
|
|
8990
|
+
if (typeof p === "string" && p) out4.push({ pkg, marketplace, dir: p });
|
|
8980
8991
|
}
|
|
8981
8992
|
}
|
|
8982
|
-
return
|
|
8993
|
+
return out4;
|
|
8983
8994
|
}
|
|
8984
8995
|
function cacheRoots(home3) {
|
|
8985
8996
|
const cache7 = join2(home3, "plugins", "cache");
|
|
8986
|
-
const
|
|
8997
|
+
const out4 = [];
|
|
8987
8998
|
for (const marketplace of dirsIn(cache7))
|
|
8988
8999
|
for (const pkg of dirsIn(join2(cache7, marketplace)))
|
|
8989
9000
|
for (const version2 of dirsIn(join2(cache7, marketplace, pkg)))
|
|
8990
|
-
|
|
8991
|
-
return
|
|
9001
|
+
out4.push({ pkg, marketplace, dir: join2(cache7, marketplace, pkg, version2) });
|
|
9002
|
+
return out4;
|
|
8992
9003
|
}
|
|
8993
9004
|
function disabledPlugins(agent, home3) {
|
|
8994
|
-
const
|
|
9005
|
+
const out4 = /* @__PURE__ */ new Set();
|
|
8995
9006
|
if (agent === "claude") {
|
|
8996
9007
|
const en = readJson(join2(home3, "settings.json"))?.enabledPlugins;
|
|
8997
9008
|
if (en && typeof en === "object") {
|
|
8998
|
-
for (const [k, v] of Object.entries(en)) if (v === false)
|
|
9009
|
+
for (const [k, v] of Object.entries(en)) if (v === false) out4.add(k);
|
|
8999
9010
|
}
|
|
9000
|
-
return
|
|
9011
|
+
return out4;
|
|
9001
9012
|
}
|
|
9002
9013
|
let toml;
|
|
9003
9014
|
try {
|
|
9004
9015
|
toml = readFileSync2(join2(home3, "config.toml"), "utf8");
|
|
9005
9016
|
} catch {
|
|
9006
|
-
return
|
|
9017
|
+
return out4;
|
|
9007
9018
|
}
|
|
9008
9019
|
let current = null;
|
|
9009
9020
|
for (const raw2 of toml.split("\n")) {
|
|
@@ -9014,9 +9025,9 @@ function disabledPlugins(agent, home3) {
|
|
|
9014
9025
|
current = m ? m[1] : null;
|
|
9015
9026
|
continue;
|
|
9016
9027
|
}
|
|
9017
|
-
if (current && /^enabled\s*=\s*false\b/.test(line))
|
|
9028
|
+
if (current && /^enabled\s*=\s*false\b/.test(line)) out4.add(current);
|
|
9018
9029
|
}
|
|
9019
|
-
return
|
|
9030
|
+
return out4;
|
|
9020
9031
|
}
|
|
9021
9032
|
function scanAgentSkills(agent, env = process.env, osHome = homedir2()) {
|
|
9022
9033
|
const home3 = agentSkillHome(agent, env, osHome);
|
|
@@ -9159,10 +9170,10 @@ function ensureSpawnHelpersExecutable(paths2, ops) {
|
|
|
9159
9170
|
}
|
|
9160
9171
|
return fixed;
|
|
9161
9172
|
}
|
|
9162
|
-
function repairSpawnHelper(
|
|
9173
|
+
function repairSpawnHelper(resolve21, notify) {
|
|
9163
9174
|
let ptyDir;
|
|
9164
9175
|
try {
|
|
9165
|
-
ptyDir = dirname4(
|
|
9176
|
+
ptyDir = dirname4(resolve21("node-pty/package.json"));
|
|
9166
9177
|
} catch {
|
|
9167
9178
|
return [];
|
|
9168
9179
|
}
|
|
@@ -9180,10 +9191,10 @@ function repairSpawnHelper(resolve19, notify) {
|
|
|
9180
9191
|
if (fixed.length) notify?.("hanoman \xB7 memperbaiki izin `spawn-helper` node-pty (sekali per instalasi)\n");
|
|
9181
9192
|
return fixed;
|
|
9182
9193
|
}
|
|
9183
|
-
function ensureSpawnHelperOnce(
|
|
9194
|
+
function ensureSpawnHelperOnce(resolve21, notify) {
|
|
9184
9195
|
if (repaired) return [];
|
|
9185
9196
|
repaired = true;
|
|
9186
|
-
return repairSpawnHelper(
|
|
9197
|
+
return repairSpawnHelper(resolve21, notify);
|
|
9187
9198
|
}
|
|
9188
9199
|
var repaired;
|
|
9189
9200
|
var init_spawn_helper = __esm({
|
|
@@ -9222,14 +9233,14 @@ function registerWebhookTap(s2) {
|
|
|
9222
9233
|
}
|
|
9223
9234
|
async function cascadeCounts(base2, def2, id) {
|
|
9224
9235
|
if (!def2.cascade?.length) return void 0;
|
|
9225
|
-
const
|
|
9236
|
+
const out4 = {};
|
|
9226
9237
|
for (const child of def2.cascade) {
|
|
9227
9238
|
try {
|
|
9228
|
-
|
|
9239
|
+
out4[child] = await base2[child].count({ where: { projectId: id } });
|
|
9229
9240
|
} catch {
|
|
9230
9241
|
}
|
|
9231
9242
|
}
|
|
9232
|
-
return
|
|
9243
|
+
return out4;
|
|
9233
9244
|
}
|
|
9234
9245
|
function webhookTap(base2) {
|
|
9235
9246
|
const pre = async (model, where) => {
|
|
@@ -9276,9 +9287,9 @@ function webhookTap(base2) {
|
|
|
9276
9287
|
const def2 = on(model);
|
|
9277
9288
|
const before2 = def2 ? await pre(model, args.where) : null;
|
|
9278
9289
|
const cascade = def2 && before2 ? await cascadeCounts(base2, def2, String(before2.id)) : void 0;
|
|
9279
|
-
const
|
|
9290
|
+
const out4 = await query2(args);
|
|
9280
9291
|
if (def2 && before2) fire({ def: def2, action: "deleted", before: before2, after: null, changed: [], cascade });
|
|
9281
|
-
return
|
|
9292
|
+
return out4;
|
|
9282
9293
|
},
|
|
9283
9294
|
// `liveSpecs` memajukan stage lewat CAS `updateMany` — jalur perubahan stage yang PALING
|
|
9284
9295
|
// sering dipakai. Melewatkannya berarti melewatkan peristiwa yang paling diminta.
|
|
@@ -9290,7 +9301,7 @@ function webhookTap(base2) {
|
|
|
9290
9301
|
before2 = await del(base2, model).findMany({ where: args.where });
|
|
9291
9302
|
} catch {
|
|
9292
9303
|
}
|
|
9293
|
-
const
|
|
9304
|
+
const out4 = await query2(args);
|
|
9294
9305
|
if (before2.length) {
|
|
9295
9306
|
const ids = before2.map((r) => r.id);
|
|
9296
9307
|
let after = [];
|
|
@@ -9306,7 +9317,7 @@ function webhookTap(base2) {
|
|
|
9306
9317
|
if (changed.length) fire({ def: def2, action: "updated", before: b, after: aft, changed });
|
|
9307
9318
|
}
|
|
9308
9319
|
}
|
|
9309
|
-
return
|
|
9320
|
+
return out4;
|
|
9310
9321
|
},
|
|
9311
9322
|
async deleteMany({ model, args, query: query2 }) {
|
|
9312
9323
|
const def2 = on(model);
|
|
@@ -9316,10 +9327,10 @@ function webhookTap(base2) {
|
|
|
9316
9327
|
before2 = await del(base2, model).findMany({ where: args.where });
|
|
9317
9328
|
} catch {
|
|
9318
9329
|
}
|
|
9319
|
-
const
|
|
9330
|
+
const out4 = await query2(args);
|
|
9320
9331
|
for (const b of before2)
|
|
9321
9332
|
fire({ def: def2, action: "deleted", before: b, after: null, changed: [] });
|
|
9322
|
-
return
|
|
9333
|
+
return out4;
|
|
9323
9334
|
}
|
|
9324
9335
|
}
|
|
9325
9336
|
}
|
|
@@ -9366,6 +9377,7 @@ var init_db = __esm({
|
|
|
9366
9377
|
process.env.DATABASE_URL = url;
|
|
9367
9378
|
mkdirSync2(dirname5(dbFilePath(url)), { recursive: true, mode: 448 });
|
|
9368
9379
|
base = new PrismaClient();
|
|
9380
|
+
void base.$queryRawUnsafe("PRAGMA journal_mode=WAL").catch((e) => console.warn("tak bisa menyetel journal_mode=WAL:", e));
|
|
9369
9381
|
prisma = base.$extends(webhookTap(base));
|
|
9370
9382
|
}
|
|
9371
9383
|
});
|
|
@@ -9382,12 +9394,12 @@ var init_stage_machine = __esm({
|
|
|
9382
9394
|
// src/services/session-phases.ts
|
|
9383
9395
|
import { readFileSync as readFileSync3, readdirSync as readdirSync3 } from "node:fs";
|
|
9384
9396
|
function recorded(file) {
|
|
9385
|
-
const
|
|
9397
|
+
const out4 = /* @__PURE__ */ new Map();
|
|
9386
9398
|
let raw2;
|
|
9387
9399
|
try {
|
|
9388
9400
|
raw2 = readFileSync3(file, "utf8");
|
|
9389
9401
|
} catch {
|
|
9390
|
-
return
|
|
9402
|
+
return out4;
|
|
9391
9403
|
}
|
|
9392
9404
|
for (const line of raw2.split("\n")) {
|
|
9393
9405
|
const trimmed = line.trimEnd();
|
|
@@ -9395,9 +9407,9 @@ function recorded(file) {
|
|
|
9395
9407
|
if (i < 1) continue;
|
|
9396
9408
|
const state = trimmed.slice(i + 1);
|
|
9397
9409
|
if (state !== "done" && state !== "skipped") continue;
|
|
9398
|
-
|
|
9410
|
+
out4.set(trimmed.slice(0, i).trim(), state);
|
|
9399
9411
|
}
|
|
9400
|
-
return
|
|
9412
|
+
return out4;
|
|
9401
9413
|
}
|
|
9402
9414
|
function readPhases(file, flow) {
|
|
9403
9415
|
const seen = recorded(file);
|
|
@@ -9736,7 +9748,7 @@ import { lookup as dnsLookup2 } from "node:dns/promises";
|
|
|
9736
9748
|
import { request as httpRequest } from "node:http";
|
|
9737
9749
|
import { request as httpsRequest } from "node:https";
|
|
9738
9750
|
async function pinnedRequest(input) {
|
|
9739
|
-
return new Promise((
|
|
9751
|
+
return new Promise((resolve21, reject2) => {
|
|
9740
9752
|
const transport = input.url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
9741
9753
|
const req = transport({
|
|
9742
9754
|
protocol: input.url.protocol,
|
|
@@ -9764,7 +9776,7 @@ async function pinnedRequest(input) {
|
|
|
9764
9776
|
}
|
|
9765
9777
|
chunks.push(chunk);
|
|
9766
9778
|
});
|
|
9767
|
-
response.on("end", () =>
|
|
9779
|
+
response.on("end", () => resolve21({
|
|
9768
9780
|
status: response.statusCode ?? 0,
|
|
9769
9781
|
headers: response.headers,
|
|
9770
9782
|
body: Buffer.concat(chunks)
|
|
@@ -10206,6 +10218,12 @@ import { randomUUID as randomUUID2 } from "node:crypto";
|
|
|
10206
10218
|
import { mkdirSync as mkdirSync4, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
10207
10219
|
import { dirname as dirname7 } from "node:path";
|
|
10208
10220
|
import { tmpdir } from "node:os";
|
|
10221
|
+
function noTtyPromptEnv() {
|
|
10222
|
+
const path2 = askpassDenyPath();
|
|
10223
|
+
mkdirSync4(dirname7(path2), { recursive: true, mode: 448 });
|
|
10224
|
+
writeFileSync2(path2, ASKPASS_DENY, { mode: 448 });
|
|
10225
|
+
return { SSH_ASKPASS: path2, SSH_ASKPASS_REQUIRE: "force", GIT_TERMINAL_PROMPT: "0" };
|
|
10226
|
+
}
|
|
10209
10227
|
function tmux(...args) {
|
|
10210
10228
|
try {
|
|
10211
10229
|
return execFileSync("tmux", ["-L", socket(), "-f", "/dev/null", ...args], {
|
|
@@ -10222,15 +10240,28 @@ function tmux(...args) {
|
|
|
10222
10240
|
}
|
|
10223
10241
|
}
|
|
10224
10242
|
function listPanes() {
|
|
10225
|
-
let
|
|
10243
|
+
let out4;
|
|
10226
10244
|
try {
|
|
10227
|
-
|
|
10245
|
+
out4 = tmux("list-panes", "-a", "-F", FMT);
|
|
10228
10246
|
} catch (e) {
|
|
10229
10247
|
if (e instanceof TmuxError && e.noServer) return [];
|
|
10230
10248
|
throw e;
|
|
10231
10249
|
}
|
|
10232
|
-
return
|
|
10233
|
-
const [
|
|
10250
|
+
return out4.split("\n").filter(Boolean).flatMap((line) => {
|
|
10251
|
+
const [
|
|
10252
|
+
n2,
|
|
10253
|
+
projectId,
|
|
10254
|
+
specId,
|
|
10255
|
+
flow,
|
|
10256
|
+
phaseFile,
|
|
10257
|
+
cwd,
|
|
10258
|
+
dead,
|
|
10259
|
+
code,
|
|
10260
|
+
decisionFile,
|
|
10261
|
+
branch,
|
|
10262
|
+
agent,
|
|
10263
|
+
alternate
|
|
10264
|
+
] = line.split(" ");
|
|
10234
10265
|
if (!n2?.startsWith(PREFIX)) return [];
|
|
10235
10266
|
const exited = dead === "1";
|
|
10236
10267
|
return [{
|
|
@@ -10248,7 +10279,8 @@ function listPanes() {
|
|
|
10248
10279
|
// SPEC-196 · sesi hidup dengan marker keputusan terisi = menunggu manusia.
|
|
10249
10280
|
decision: !exited && !!decisionFile && markerFilled(decisionFile),
|
|
10250
10281
|
// SPEC-338 · sesi yang lahir sebelum ADR-0074 tak punya opsi ini → claude.
|
|
10251
|
-
agent: agent === "codex" ? "codex" : "claude"
|
|
10282
|
+
agent: agent === "codex" ? "codex" : "claude",
|
|
10283
|
+
altScreen: alternate === "1"
|
|
10252
10284
|
}];
|
|
10253
10285
|
});
|
|
10254
10286
|
}
|
|
@@ -10269,8 +10301,8 @@ function sessionKind(o, projectId, cwd) {
|
|
|
10269
10301
|
}
|
|
10270
10302
|
function captureTranscript(id) {
|
|
10271
10303
|
try {
|
|
10272
|
-
const
|
|
10273
|
-
return
|
|
10304
|
+
const out4 = tmux("capture-pane", "-p", "-J", "-S", "-50000", "-t", name(id));
|
|
10305
|
+
return out4.trim() ? out4 : null;
|
|
10274
10306
|
} catch {
|
|
10275
10307
|
return null;
|
|
10276
10308
|
}
|
|
@@ -10334,6 +10366,9 @@ function createSession(projectId, cwd, opts = {}) {
|
|
|
10334
10366
|
if (!opts.command && agent === "claude") {
|
|
10335
10367
|
for (const [k, v] of Object.entries(rootBypassEnv())) envPairs.push(`${k}=${sq(v)}`);
|
|
10336
10368
|
}
|
|
10369
|
+
if (!opts.command) {
|
|
10370
|
+
for (const [k, v] of Object.entries(noTtyPromptEnv())) envPairs.push(`${k}=${sq(v)}`);
|
|
10371
|
+
}
|
|
10337
10372
|
if (opts.phaseFile) {
|
|
10338
10373
|
mkdirSync4(dirname7(opts.phaseFile), { recursive: true });
|
|
10339
10374
|
envPairs.push(`HANOMAN_PHASE_FILE=${sq(opts.phaseFile)}`);
|
|
@@ -10534,18 +10569,18 @@ function sessionPhases(id) {
|
|
|
10534
10569
|
return readPhases(p.phaseFile, p.flow);
|
|
10535
10570
|
}
|
|
10536
10571
|
function sessionPhasesBySpec() {
|
|
10537
|
-
const
|
|
10572
|
+
const out4 = /* @__PURE__ */ new Map();
|
|
10538
10573
|
let panes;
|
|
10539
10574
|
try {
|
|
10540
10575
|
panes = listPanes();
|
|
10541
10576
|
} catch {
|
|
10542
|
-
return
|
|
10577
|
+
return out4;
|
|
10543
10578
|
}
|
|
10544
10579
|
for (const p of panes) {
|
|
10545
10580
|
if (!p.specId || !p.flow || !p.phaseFile) continue;
|
|
10546
|
-
|
|
10581
|
+
out4.set(p.specId, { phases: readPhases(p.phaseFile, p.flow), cwd: p.cwd });
|
|
10547
10582
|
}
|
|
10548
|
-
return
|
|
10583
|
+
return out4;
|
|
10549
10584
|
}
|
|
10550
10585
|
function broadcast(a, f) {
|
|
10551
10586
|
const msg = frame(f);
|
|
@@ -10602,6 +10637,11 @@ function pollPhases(p, a) {
|
|
|
10602
10637
|
a.lastPhases = json;
|
|
10603
10638
|
broadcast(a, { t: "phase", phases, complete });
|
|
10604
10639
|
}
|
|
10640
|
+
function pollAlt(p, a) {
|
|
10641
|
+
if (p.altScreen === a.lastAlt) return;
|
|
10642
|
+
a.lastAlt = p.altScreen;
|
|
10643
|
+
broadcast(a, { t: "alt", on: p.altScreen });
|
|
10644
|
+
}
|
|
10605
10645
|
function startPoll() {
|
|
10606
10646
|
if (poll) return;
|
|
10607
10647
|
poll = setInterval(() => {
|
|
@@ -10616,7 +10656,11 @@ function startPoll() {
|
|
|
10616
10656
|
const p = live.get(id);
|
|
10617
10657
|
if (!p) end(id, 0);
|
|
10618
10658
|
else if (p.exited) end(id, p.code);
|
|
10619
|
-
else
|
|
10659
|
+
else {
|
|
10660
|
+
const a = attached.get(id);
|
|
10661
|
+
pollPhases(p, a);
|
|
10662
|
+
pollAlt(p, a);
|
|
10663
|
+
}
|
|
10620
10664
|
}
|
|
10621
10665
|
if (attached.size === 0 && poll) {
|
|
10622
10666
|
clearInterval(poll);
|
|
@@ -10633,14 +10677,18 @@ function attach(id, c) {
|
|
|
10633
10677
|
}
|
|
10634
10678
|
if (p.exited) {
|
|
10635
10679
|
const screen = tmux("capture-pane", "-p", "-e", "-J", "-S", "-2000", "-t", name(id));
|
|
10636
|
-
if (screen.trim())
|
|
10680
|
+
if (screen.trim()) {
|
|
10681
|
+
c.send(frame({ t: "data", d: stripTerminalQueries(screen.replace(/\n/g, "\r\n")) }));
|
|
10682
|
+
}
|
|
10637
10683
|
c.send(frame({ t: "exit", code: p.code }));
|
|
10638
10684
|
c.close();
|
|
10639
10685
|
return;
|
|
10640
10686
|
}
|
|
10641
10687
|
const a = attached.get(id) ?? open(id);
|
|
10642
10688
|
a.clients.add(c);
|
|
10643
|
-
if (a.scrollback) c.send(frame({ t: "data", d: a.scrollback }));
|
|
10689
|
+
if (a.scrollback) c.send(frame({ t: "data", d: stripTerminalQueries(a.scrollback) }));
|
|
10690
|
+
a.lastAlt = p.altScreen;
|
|
10691
|
+
c.send(frame({ t: "alt", on: p.altScreen }));
|
|
10644
10692
|
if (p.flow && p.phaseFile) {
|
|
10645
10693
|
const phases = readPhases(p.phaseFile, p.flow);
|
|
10646
10694
|
const complete = paneComplete(p);
|
|
@@ -10648,8 +10696,11 @@ function attach(id, c) {
|
|
|
10648
10696
|
c.send(frame({ t: "phase", phases, complete }));
|
|
10649
10697
|
}
|
|
10650
10698
|
}
|
|
10651
|
-
function writeTo(id, d) {
|
|
10652
|
-
attached.get(id)
|
|
10699
|
+
function writeTo(id, d, from) {
|
|
10700
|
+
const a = attached.get(id);
|
|
10701
|
+
if (!a) return;
|
|
10702
|
+
if (from && isTerminalResponse(d) && a.clients.values().next().value !== from) return;
|
|
10703
|
+
a.pty.write(d);
|
|
10653
10704
|
}
|
|
10654
10705
|
function resize(id, cols, rows) {
|
|
10655
10706
|
attached.get(id)?.pty.resize(cols, rows);
|
|
@@ -10685,7 +10736,7 @@ function spawnPty(...args) {
|
|
|
10685
10736
|
);
|
|
10686
10737
|
}
|
|
10687
10738
|
}
|
|
10688
|
-
var socket, PREFIX, MAX_SCROLLBACK, SCROLLBACK_SLACK, POLL_MS, markerFilled, attached, claudeBin, shellBin, codexBin, agentBin, rootBypassEnv, frame, name, promptFilePath, goalGatePath, goalStatePath, agentsFilePath, NO_SERVER, TmuxError, sq, idFor, FMT, listSessions, liveDecisions, getSession, hooks, emitBirth, emitDeath, customAgentSource, customAgentsFor, sleep, paneText, dialogIO, DIALOG_CAPTURE_LINES, GOAL_ARMED_MARKERS, goalArmed, COALESCE_MS, COALESCE_MAX_BYTES, trimScrollback, phaseKey, paneComplete, sessionFinished, poll, detach;
|
|
10739
|
+
var socket, PREFIX, MAX_SCROLLBACK, SCROLLBACK_SLACK, POLL_MS, markerFilled, attached, claudeBin, shellBin, codexBin, agentBin, rootBypassEnv, frame, name, promptFilePath, goalGatePath, goalStatePath, agentsFilePath, askpassDenyPath, ASKPASS_DENY, NO_SERVER, TmuxError, sq, idFor, FMT, listSessions, liveDecisions, getSession, hooks, emitBirth, emitDeath, customAgentSource, customAgentsFor, sleep, paneText, dialogIO, DIALOG_CAPTURE_LINES, GOAL_ARMED_MARKERS, goalArmed, COALESCE_MS, COALESCE_MAX_BYTES, trimScrollback, phaseKey, paneComplete, sessionFinished, poll, TERMINAL_QUERY, stripTerminalQueries, TERMINAL_RESPONSE, isTerminalResponse, detach;
|
|
10689
10740
|
var init_pty = __esm({
|
|
10690
10741
|
"src/services/pty.ts"() {
|
|
10691
10742
|
"use strict";
|
|
@@ -10721,6 +10772,12 @@ var init_pty = __esm({
|
|
|
10721
10772
|
goalGatePath = (id) => `${tmpdir()}/hanoman-goal-gates/${id}.sh`;
|
|
10722
10773
|
goalStatePath = (id) => `${tmpdir()}/hanoman-goal-gates/${id}.count`;
|
|
10723
10774
|
agentsFilePath = (id) => `${tmpdir()}/hanoman-agents/${id}.json`;
|
|
10775
|
+
askpassDenyPath = () => `${tmpdir()}/hanoman-askpass/deny.sh`;
|
|
10776
|
+
ASKPASS_DENY = `#!/bin/sh
|
|
10777
|
+
echo "hanoman: tak ada manusia di pane ini \u2014 permintaan ketikan ditolak: $1" >&2
|
|
10778
|
+
echo "hanoman: buka kuncinya di luar sesi (mis. ssh-add ~/.ssh/id_rsa), lalu ulangi." >&2
|
|
10779
|
+
exit 1
|
|
10780
|
+
`;
|
|
10724
10781
|
NO_SERVER = /no server running|error connecting to/i;
|
|
10725
10782
|
TmuxError = class extends Error {
|
|
10726
10783
|
constructor(message, noServer) {
|
|
@@ -10741,7 +10798,8 @@ var init_pty = __esm({
|
|
|
10741
10798
|
"#{pane_dead_status}",
|
|
10742
10799
|
"#{@hanoman_decision_file}",
|
|
10743
10800
|
"#{@hanoman_branch}",
|
|
10744
|
-
"#{@hanoman_agent}"
|
|
10801
|
+
"#{@hanoman_agent}",
|
|
10802
|
+
"#{alternate_on}"
|
|
10745
10803
|
].join(" ");
|
|
10746
10804
|
listSessions = () => listPanes().map(({ id, projectId, specId, flow, cwd, exited, code, branch, decision, agent }) => ({
|
|
10747
10805
|
id,
|
|
@@ -10823,6 +10881,38 @@ var init_pty = __esm({
|
|
|
10823
10881
|
const p = getSession(id);
|
|
10824
10882
|
return !!p && paneComplete(p);
|
|
10825
10883
|
};
|
|
10884
|
+
TERMINAL_QUERY = new RegExp([
|
|
10885
|
+
"\\x1b\\[[?>=]?[0-9;]*c",
|
|
10886
|
+
// Device Attributes 1/2/3
|
|
10887
|
+
"\\x1b\\[>[0-9;]*q",
|
|
10888
|
+
// XTVERSION — `>` wajib, DECSCUSR `\\x1b[2 q` bukan ini
|
|
10889
|
+
"\\x1b\\[\\??[0-9;]*n",
|
|
10890
|
+
// DSR/DECDSR, termasuk `?996n` (skema warna)
|
|
10891
|
+
"\\x1b\\[\\??[0-9;]*\\$[py]",
|
|
10892
|
+
// DECRQM & balasannya
|
|
10893
|
+
"\\x1b\\][0-9][0-9;]*\\?(?:\\x07|\\x1b\\\\)",
|
|
10894
|
+
// OSC 4/10/11/12 `…;?`
|
|
10895
|
+
"\\x1bP[0-9]*[$+][a-z][^\\x1b]*\\x1b\\\\",
|
|
10896
|
+
// DECRQSS & XTGETTCAP
|
|
10897
|
+
"\\x1b\\[(?:11|13|14|15|16|18|19|20|21)t"
|
|
10898
|
+
// XTWINOPS laporan — berparameter TUNGGAL
|
|
10899
|
+
].join("|"), "g");
|
|
10900
|
+
stripTerminalQueries = (s2) => s2.replace(TERMINAL_QUERY, "");
|
|
10901
|
+
TERMINAL_RESPONSE = new RegExp([
|
|
10902
|
+
"\\x1b\\[[?>][0-9;]*c",
|
|
10903
|
+
// balasan DA
|
|
10904
|
+
"\\x1b\\[\\??[0-9]+;[0-9]+R",
|
|
10905
|
+
// CPR / DECXCPR
|
|
10906
|
+
"\\x1b\\[\\??[0-9;]*n",
|
|
10907
|
+
// balasan DSR
|
|
10908
|
+
"\\x1b\\[\\??[0-9;]*\\$y",
|
|
10909
|
+
// DECRPM
|
|
10910
|
+
"\\x1b\\][0-9][0-9;]*;[^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)",
|
|
10911
|
+
// balasan warna OSC
|
|
10912
|
+
"\\x1bP[0-9]*[$+>][a-z|][^\\x1b]*\\x1b\\\\"
|
|
10913
|
+
// DECRPSS / XTGETTCAP / XTVERSION
|
|
10914
|
+
].join("|"), "g");
|
|
10915
|
+
isTerminalResponse = (d) => d.length > 0 && d.replace(TERMINAL_RESPONSE, "") === "";
|
|
10826
10916
|
detach = (id, c) => {
|
|
10827
10917
|
attached.get(id)?.clients.delete(c);
|
|
10828
10918
|
};
|
|
@@ -11003,14 +11093,14 @@ function jsonSafe(v) {
|
|
|
11003
11093
|
return v === void 0 ? null : JSON.parse(JSON.stringify(v));
|
|
11004
11094
|
}
|
|
11005
11095
|
function coerce2(entity, data) {
|
|
11006
|
-
const
|
|
11096
|
+
const out4 = {};
|
|
11007
11097
|
for (const f of FIELDS[entity]) {
|
|
11008
11098
|
if (!(f in data)) continue;
|
|
11009
11099
|
let v = data[f];
|
|
11010
11100
|
if (DATE_FIELDS[entity].includes(f) && typeof v === "string") v = new Date(v);
|
|
11011
|
-
|
|
11101
|
+
out4[f] = v;
|
|
11012
11102
|
}
|
|
11013
|
-
return
|
|
11103
|
+
return out4;
|
|
11014
11104
|
}
|
|
11015
11105
|
async function snapshot(entity, id) {
|
|
11016
11106
|
const row = await DELEGATE[entity].findUnique({ where: { id } });
|
|
@@ -12326,14 +12416,14 @@ var require_filesystem = __commonJS({
|
|
|
12326
12416
|
});
|
|
12327
12417
|
return buffer2.subarray(0, bytesRead);
|
|
12328
12418
|
};
|
|
12329
|
-
var readFile6 = (path2) => new Promise((
|
|
12419
|
+
var readFile6 = (path2) => new Promise((resolve21, reject2) => {
|
|
12330
12420
|
fs.open(path2, "r", (err, fd) => {
|
|
12331
12421
|
if (err) {
|
|
12332
12422
|
reject2(err);
|
|
12333
12423
|
} else {
|
|
12334
12424
|
const buffer2 = Buffer.alloc(MAX_LENGTH);
|
|
12335
12425
|
fs.read(fd, buffer2, 0, MAX_LENGTH, 0, (_, bytesRead) => {
|
|
12336
|
-
|
|
12426
|
+
resolve21(buffer2.subarray(0, bytesRead));
|
|
12337
12427
|
fs.close(fd, () => {
|
|
12338
12428
|
});
|
|
12339
12429
|
});
|
|
@@ -12401,10 +12491,10 @@ var require_detect_libc = __commonJS({
|
|
|
12401
12491
|
var commandOut = "";
|
|
12402
12492
|
var safeCommand = () => {
|
|
12403
12493
|
if (!commandOut) {
|
|
12404
|
-
return new Promise((
|
|
12405
|
-
childProcess.exec(command, (err,
|
|
12406
|
-
commandOut = err ? " " :
|
|
12407
|
-
|
|
12494
|
+
return new Promise((resolve21) => {
|
|
12495
|
+
childProcess.exec(command, (err, out4) => {
|
|
12496
|
+
commandOut = err ? " " : out4;
|
|
12497
|
+
resolve21(commandOut);
|
|
12408
12498
|
});
|
|
12409
12499
|
});
|
|
12410
12500
|
}
|
|
@@ -12436,8 +12526,8 @@ var require_detect_libc = __commonJS({
|
|
|
12436
12526
|
}
|
|
12437
12527
|
return null;
|
|
12438
12528
|
};
|
|
12439
|
-
var familyFromCommand = (
|
|
12440
|
-
const [getconf, ldd1] =
|
|
12529
|
+
var familyFromCommand = (out4) => {
|
|
12530
|
+
const [getconf, ldd1] = out4.split(/[\r\n]+/);
|
|
12441
12531
|
if (getconf && getconf.includes(GLIBC)) {
|
|
12442
12532
|
return GLIBC;
|
|
12443
12533
|
}
|
|
@@ -12526,8 +12616,8 @@ var require_detect_libc = __commonJS({
|
|
|
12526
12616
|
family2 = familyFromReport();
|
|
12527
12617
|
}
|
|
12528
12618
|
if (!family2) {
|
|
12529
|
-
const
|
|
12530
|
-
family2 = familyFromCommand(
|
|
12619
|
+
const out4 = await safeCommand();
|
|
12620
|
+
family2 = familyFromCommand(out4);
|
|
12531
12621
|
}
|
|
12532
12622
|
}
|
|
12533
12623
|
}
|
|
@@ -12543,8 +12633,8 @@ var require_detect_libc = __commonJS({
|
|
|
12543
12633
|
family2 = familyFromReport();
|
|
12544
12634
|
}
|
|
12545
12635
|
if (!family2) {
|
|
12546
|
-
const
|
|
12547
|
-
family2 = familyFromCommand(
|
|
12636
|
+
const out4 = safeCommandSync();
|
|
12637
|
+
family2 = familyFromCommand(out4);
|
|
12548
12638
|
}
|
|
12549
12639
|
}
|
|
12550
12640
|
}
|
|
@@ -12590,8 +12680,8 @@ var require_detect_libc = __commonJS({
|
|
|
12590
12680
|
return null;
|
|
12591
12681
|
};
|
|
12592
12682
|
var versionSuffix = (s2) => s2.trim().split(/\s+/)[1];
|
|
12593
|
-
var versionFromCommand = (
|
|
12594
|
-
const [getconf, ldd1, ldd2] =
|
|
12683
|
+
var versionFromCommand = (out4) => {
|
|
12684
|
+
const [getconf, ldd1, ldd2] = out4.split(/[\r\n]+/);
|
|
12595
12685
|
if (getconf && getconf.includes(GLIBC)) {
|
|
12596
12686
|
return versionSuffix(getconf);
|
|
12597
12687
|
}
|
|
@@ -12608,8 +12698,8 @@ var require_detect_libc = __commonJS({
|
|
|
12608
12698
|
version3 = versionFromReport();
|
|
12609
12699
|
}
|
|
12610
12700
|
if (!version3) {
|
|
12611
|
-
const
|
|
12612
|
-
version3 = versionFromCommand(
|
|
12701
|
+
const out4 = await safeCommand();
|
|
12702
|
+
version3 = versionFromCommand(out4);
|
|
12613
12703
|
}
|
|
12614
12704
|
}
|
|
12615
12705
|
return version3;
|
|
@@ -12622,8 +12712,8 @@ var require_detect_libc = __commonJS({
|
|
|
12622
12712
|
version3 = versionFromReport();
|
|
12623
12713
|
}
|
|
12624
12714
|
if (!version3) {
|
|
12625
|
-
const
|
|
12626
|
-
version3 = versionFromCommand(
|
|
12715
|
+
const out4 = safeCommandSync();
|
|
12716
|
+
version3 = versionFromCommand(out4);
|
|
12627
12717
|
}
|
|
12628
12718
|
}
|
|
12629
12719
|
return version3;
|
|
@@ -16868,7 +16958,7 @@ var init_gateway = __esm({
|
|
|
16868
16958
|
const message = sanitizeTelegramOutput(error.message, this.deps.exactSecrets).slice(0, 500);
|
|
16869
16959
|
updateTelegramRuntimeStatus({ running: false, readiness: "error", lastError: message });
|
|
16870
16960
|
if (error instanceof TelegramApiError && error.code === 409) return;
|
|
16871
|
-
await new Promise((
|
|
16961
|
+
await new Promise((resolve21) => setTimeout(resolve21, 1e3));
|
|
16872
16962
|
}
|
|
16873
16963
|
}
|
|
16874
16964
|
}
|
|
@@ -22999,7 +23089,7 @@ var require_parseParams = __commonJS({
|
|
|
22999
23089
|
var require_basename = __commonJS({
|
|
23000
23090
|
"../node_modules/.pnpm/@fastify+busboy@3.2.0/node_modules/@fastify/busboy/lib/utils/basename.js"(exports, module) {
|
|
23001
23091
|
"use strict";
|
|
23002
|
-
module.exports = function
|
|
23092
|
+
module.exports = function basename7(path2) {
|
|
23003
23093
|
if (typeof path2 !== "string") {
|
|
23004
23094
|
return "";
|
|
23005
23095
|
}
|
|
@@ -23026,7 +23116,7 @@ var require_multipart = __commonJS({
|
|
|
23026
23116
|
var Dicer = require_Dicer();
|
|
23027
23117
|
var parseParams = require_parseParams();
|
|
23028
23118
|
var decodeText = require_decodeText();
|
|
23029
|
-
var
|
|
23119
|
+
var basename7 = require_basename();
|
|
23030
23120
|
var getLimit = require_getLimit();
|
|
23031
23121
|
var RE_BOUNDARY = /^boundary$/i;
|
|
23032
23122
|
var RE_FIELD = /^form-data$/i;
|
|
@@ -23143,7 +23233,7 @@ var require_multipart = __commonJS({
|
|
|
23143
23233
|
} else if (RE_FILENAME.test(parsed[i][0])) {
|
|
23144
23234
|
filename = parsed[i][1];
|
|
23145
23235
|
if (!preservePath) {
|
|
23146
|
-
filename =
|
|
23236
|
+
filename = basename7(filename);
|
|
23147
23237
|
}
|
|
23148
23238
|
}
|
|
23149
23239
|
}
|
|
@@ -24226,14 +24316,14 @@ var require_stream_consumer = __commonJS({
|
|
|
24226
24316
|
"../node_modules/.pnpm/@fastify+multipart@10.1.0/node_modules/@fastify/multipart/lib/stream-consumer.js"(exports, module) {
|
|
24227
24317
|
"use strict";
|
|
24228
24318
|
module.exports = function streamToNull(stream2) {
|
|
24229
|
-
return new Promise((
|
|
24319
|
+
return new Promise((resolve21, reject2) => {
|
|
24230
24320
|
stream2.on("data", () => {
|
|
24231
24321
|
});
|
|
24232
24322
|
stream2.on("close", () => {
|
|
24233
|
-
|
|
24323
|
+
resolve21();
|
|
24234
24324
|
});
|
|
24235
24325
|
stream2.on("end", () => {
|
|
24236
|
-
|
|
24326
|
+
resolve21();
|
|
24237
24327
|
});
|
|
24238
24328
|
stream2.on("error", (error) => {
|
|
24239
24329
|
reject2(error);
|
|
@@ -24682,10 +24772,10 @@ var require_multipart2 = __commonJS({
|
|
|
24682
24772
|
}
|
|
24683
24773
|
};
|
|
24684
24774
|
const parts = () => {
|
|
24685
|
-
return new Promise((
|
|
24775
|
+
return new Promise((resolve21, reject2) => {
|
|
24686
24776
|
handle((val) => {
|
|
24687
24777
|
if (val instanceof Error) return reject2(val);
|
|
24688
|
-
|
|
24778
|
+
resolve21(val);
|
|
24689
24779
|
});
|
|
24690
24780
|
});
|
|
24691
24781
|
};
|
|
@@ -25073,10 +25163,10 @@ function components(rel) {
|
|
|
25073
25163
|
return parts;
|
|
25074
25164
|
}
|
|
25075
25165
|
async function canonicalRoot(root) {
|
|
25076
|
-
const
|
|
25077
|
-
const
|
|
25078
|
-
if (!
|
|
25079
|
-
return
|
|
25166
|
+
const out4 = await realpath(root).catch(() => denied("root tidak ada"));
|
|
25167
|
+
const stat4 = await lstat(out4);
|
|
25168
|
+
if (!stat4.isDirectory() || stat4.isSymbolicLink()) denied("root bukan direktori regular");
|
|
25169
|
+
return out4;
|
|
25080
25170
|
}
|
|
25081
25171
|
function beneath(root, candidate) {
|
|
25082
25172
|
const rel = relative(root, candidate);
|
|
@@ -25093,8 +25183,8 @@ async function ensureRepoParents(root, rel) {
|
|
|
25093
25183
|
} catch (error) {
|
|
25094
25184
|
if (error.code !== "EEXIST") throw error;
|
|
25095
25185
|
}
|
|
25096
|
-
const
|
|
25097
|
-
if (
|
|
25186
|
+
const stat4 = await lstat(current);
|
|
25187
|
+
if (stat4.isSymbolicLink() || !stat4.isDirectory()) denied("parent tidak aman");
|
|
25098
25188
|
const canonical = await realpath(current);
|
|
25099
25189
|
if (!beneath(base2, canonical) || canonical !== current) denied("parent keluar root");
|
|
25100
25190
|
}
|
|
@@ -25110,8 +25200,8 @@ function ensureRepoParentsSync(root, rel) {
|
|
|
25110
25200
|
} catch (error) {
|
|
25111
25201
|
if (error.code !== "EEXIST") throw error;
|
|
25112
25202
|
}
|
|
25113
|
-
const
|
|
25114
|
-
if (
|
|
25203
|
+
const stat4 = lstatSync(current);
|
|
25204
|
+
if (stat4.isSymbolicLink() || !stat4.isDirectory()) denied("parent tidak aman");
|
|
25115
25205
|
if (realpathSync2(current) !== current || !beneath(base2, current)) denied("parent keluar root");
|
|
25116
25206
|
}
|
|
25117
25207
|
}
|
|
@@ -25121,14 +25211,14 @@ async function resolveRepoEntry(root, rel, opts = {}) {
|
|
|
25121
25211
|
let current = base2;
|
|
25122
25212
|
for (let i = 0; i < parts.length; i++) {
|
|
25123
25213
|
current = join6(current, parts[i]);
|
|
25124
|
-
const
|
|
25214
|
+
const stat4 = await lstat(current).catch((error) => {
|
|
25125
25215
|
if (error.code === "ENOENT" && opts.allowMissingTail) return null;
|
|
25126
25216
|
if (error.code === "ENOENT" && opts.allowMissingFinal && i === parts.length - 1) return null;
|
|
25127
25217
|
return denied("komponen tidak ada");
|
|
25128
25218
|
});
|
|
25129
|
-
if (!
|
|
25130
|
-
if (
|
|
25131
|
-
if (i < parts.length - 1 && !
|
|
25219
|
+
if (!stat4) break;
|
|
25220
|
+
if (stat4.isSymbolicLink()) denied("symlink");
|
|
25221
|
+
if (i < parts.length - 1 && !stat4.isDirectory()) denied("parent bukan direktori");
|
|
25132
25222
|
}
|
|
25133
25223
|
if (!beneath(base2, resolve9(current))) denied("keluar root");
|
|
25134
25224
|
const parent = resolve9(current, "..");
|
|
@@ -25143,9 +25233,9 @@ function assertSafeRepoPathSync(root, rel, allowMissingFinal = false, allowMissi
|
|
|
25143
25233
|
for (let i = 0; i < parts.length; i++) {
|
|
25144
25234
|
current = join6(current, parts[i]);
|
|
25145
25235
|
try {
|
|
25146
|
-
const
|
|
25147
|
-
if (
|
|
25148
|
-
if (i < parts.length - 1 && !
|
|
25236
|
+
const stat4 = lstatSync(current);
|
|
25237
|
+
if (stat4.isSymbolicLink()) denied("symlink");
|
|
25238
|
+
if (i < parts.length - 1 && !stat4.isDirectory()) denied("parent bukan direktori");
|
|
25149
25239
|
} catch (error) {
|
|
25150
25240
|
if (allowMissingTail && error.code === "ENOENT") break;
|
|
25151
25241
|
if (allowMissingFinal && error.code === "ENOENT" && i === parts.length - 1) break;
|
|
@@ -25200,8 +25290,8 @@ async function readRepoFile(root, rel) {
|
|
|
25200
25290
|
const entry = await resolveRepoEntry(root, rel);
|
|
25201
25291
|
const handle = await open2(entry.path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0)).catch(() => denied("final component tidak aman"));
|
|
25202
25292
|
try {
|
|
25203
|
-
const
|
|
25204
|
-
if (!
|
|
25293
|
+
const stat4 = await handle.stat();
|
|
25294
|
+
if (!stat4.isFile()) denied("bukan file regular");
|
|
25205
25295
|
return await handle.readFile();
|
|
25206
25296
|
} finally {
|
|
25207
25297
|
await handle.close();
|
|
@@ -25430,13 +25520,13 @@ async function deleteSynced(entity, id, deviceId) {
|
|
|
25430
25520
|
return true;
|
|
25431
25521
|
}
|
|
25432
25522
|
async function listPendingDeletes() {
|
|
25433
|
-
const
|
|
25523
|
+
const out4 = [];
|
|
25434
25524
|
for (const item of await listOutbox()) {
|
|
25435
25525
|
const tomb = await findTombstone(item.entity, item.recordId);
|
|
25436
25526
|
if (!tomb) continue;
|
|
25437
|
-
|
|
25527
|
+
out4.push({ entity: item.entity, recordId: item.recordId, deletedAt: tomb.deletedAt.toISOString() });
|
|
25438
25528
|
}
|
|
25439
|
-
return
|
|
25529
|
+
return out4;
|
|
25440
25530
|
}
|
|
25441
25531
|
|
|
25442
25532
|
// src/services/branches.ts
|
|
@@ -26651,8 +26741,8 @@ var FileTokenizer = class _FileTokenizer extends AbstractTokenizer {
|
|
|
26651
26741
|
*/
|
|
26652
26742
|
static async fromFile(sourceFilePath) {
|
|
26653
26743
|
const fileHandle = await fsOpen(sourceFilePath, "r");
|
|
26654
|
-
const
|
|
26655
|
-
return new _FileTokenizer(fileHandle, { fileInfo: { path: sourceFilePath, size:
|
|
26744
|
+
const stat4 = await fileHandle.stat();
|
|
26745
|
+
return new _FileTokenizer(fileHandle, { fileInfo: { path: sourceFilePath, size: stat4.size } });
|
|
26656
26746
|
}
|
|
26657
26747
|
constructor(fileHandle, options2) {
|
|
26658
26748
|
super(options2);
|
|
@@ -26931,18 +27021,18 @@ function decodeLatin1(bytes) {
|
|
|
26931
27021
|
}
|
|
26932
27022
|
function decodeWindows1252(bytes) {
|
|
26933
27023
|
const parts = [];
|
|
26934
|
-
let
|
|
27024
|
+
let out4 = "";
|
|
26935
27025
|
for (let i = 0; i < bytes.length; i++) {
|
|
26936
27026
|
const b = bytes[i];
|
|
26937
27027
|
const extra = b >= 128 && b <= 159 ? WINDOWS_1252_EXTRA[b] : void 0;
|
|
26938
|
-
|
|
26939
|
-
if (
|
|
26940
|
-
parts.push(
|
|
26941
|
-
|
|
27028
|
+
out4 += extra !== null && extra !== void 0 ? extra : String.fromCharCode(b);
|
|
27029
|
+
if (out4.length >= CHUNK) {
|
|
27030
|
+
parts.push(out4);
|
|
27031
|
+
out4 = "";
|
|
26942
27032
|
}
|
|
26943
27033
|
}
|
|
26944
|
-
if (
|
|
26945
|
-
parts.push(
|
|
27034
|
+
if (out4)
|
|
27035
|
+
parts.push(out4);
|
|
26946
27036
|
return parts.join("");
|
|
26947
27037
|
}
|
|
26948
27038
|
|
|
@@ -28224,7 +28314,7 @@ function readByobReaderWithSignal(reader, buffer2, signal) {
|
|
|
28224
28314
|
return reader.read(buffer2);
|
|
28225
28315
|
}
|
|
28226
28316
|
signal.throwIfAborted();
|
|
28227
|
-
return new Promise((
|
|
28317
|
+
return new Promise((resolve21, reject2) => {
|
|
28228
28318
|
const cleanup = () => {
|
|
28229
28319
|
signal.removeEventListener("abort", onAbort);
|
|
28230
28320
|
};
|
|
@@ -28244,7 +28334,7 @@ function readByobReaderWithSignal(reader, buffer2, signal) {
|
|
|
28244
28334
|
try {
|
|
28245
28335
|
const result = await reader.read(buffer2);
|
|
28246
28336
|
cleanup();
|
|
28247
|
-
|
|
28337
|
+
resolve21(result);
|
|
28248
28338
|
} catch (error) {
|
|
28249
28339
|
cleanup();
|
|
28250
28340
|
reject2(error);
|
|
@@ -31364,14 +31454,14 @@ function metadata(callback) {
|
|
|
31364
31454
|
return this;
|
|
31365
31455
|
} else {
|
|
31366
31456
|
if (this._isStreamInput()) {
|
|
31367
|
-
return new Promise((
|
|
31457
|
+
return new Promise((resolve21, reject2) => {
|
|
31368
31458
|
const finished = () => {
|
|
31369
31459
|
this._flattenBufferIn();
|
|
31370
31460
|
sharp_default.metadata(this.options, (err, metadata2) => {
|
|
31371
31461
|
if (err) {
|
|
31372
31462
|
reject2(is_default.nativeError(err, stack));
|
|
31373
31463
|
} else {
|
|
31374
|
-
|
|
31464
|
+
resolve21(metadata2);
|
|
31375
31465
|
}
|
|
31376
31466
|
});
|
|
31377
31467
|
};
|
|
@@ -31382,12 +31472,12 @@ function metadata(callback) {
|
|
|
31382
31472
|
}
|
|
31383
31473
|
});
|
|
31384
31474
|
} else {
|
|
31385
|
-
return new Promise((
|
|
31475
|
+
return new Promise((resolve21, reject2) => {
|
|
31386
31476
|
sharp_default.metadata(this.options, (err, metadata2) => {
|
|
31387
31477
|
if (err) {
|
|
31388
31478
|
reject2(is_default.nativeError(err, stack));
|
|
31389
31479
|
} else {
|
|
31390
|
-
|
|
31480
|
+
resolve21(metadata2);
|
|
31391
31481
|
}
|
|
31392
31482
|
});
|
|
31393
31483
|
});
|
|
@@ -31420,25 +31510,25 @@ function stats(callback) {
|
|
|
31420
31510
|
return this;
|
|
31421
31511
|
} else {
|
|
31422
31512
|
if (this._isStreamInput()) {
|
|
31423
|
-
return new Promise((
|
|
31513
|
+
return new Promise((resolve21, reject2) => {
|
|
31424
31514
|
this.on("finish", function() {
|
|
31425
31515
|
this._flattenBufferIn();
|
|
31426
31516
|
sharp_default.stats(this.options, (err, stats2) => {
|
|
31427
31517
|
if (err) {
|
|
31428
31518
|
reject2(is_default.nativeError(err, stack));
|
|
31429
31519
|
} else {
|
|
31430
|
-
|
|
31520
|
+
resolve21(stats2);
|
|
31431
31521
|
}
|
|
31432
31522
|
});
|
|
31433
31523
|
});
|
|
31434
31524
|
});
|
|
31435
31525
|
} else {
|
|
31436
|
-
return new Promise((
|
|
31526
|
+
return new Promise((resolve21, reject2) => {
|
|
31437
31527
|
sharp_default.stats(this.options, (err, stats2) => {
|
|
31438
31528
|
if (err) {
|
|
31439
31529
|
reject2(is_default.nativeError(err, stack));
|
|
31440
31530
|
} else {
|
|
31441
|
-
|
|
31531
|
+
resolve21(stats2);
|
|
31442
31532
|
}
|
|
31443
31533
|
});
|
|
31444
31534
|
});
|
|
@@ -33267,7 +33357,7 @@ function _pipeline(callback, stack) {
|
|
|
33267
33357
|
return this;
|
|
33268
33358
|
} else {
|
|
33269
33359
|
if (this._isStreamInput()) {
|
|
33270
|
-
return new Promise((
|
|
33360
|
+
return new Promise((resolve21, reject2) => {
|
|
33271
33361
|
this.once("finish", () => {
|
|
33272
33362
|
this._flattenBufferIn();
|
|
33273
33363
|
sharp_default.pipeline(this.options, (err, data, info) => {
|
|
@@ -33275,24 +33365,24 @@ function _pipeline(callback, stack) {
|
|
|
33275
33365
|
reject2(is_default.nativeError(err, stack));
|
|
33276
33366
|
} else {
|
|
33277
33367
|
if (this.options.resolveWithObject) {
|
|
33278
|
-
|
|
33368
|
+
resolve21({ data, info });
|
|
33279
33369
|
} else {
|
|
33280
|
-
|
|
33370
|
+
resolve21(data);
|
|
33281
33371
|
}
|
|
33282
33372
|
}
|
|
33283
33373
|
});
|
|
33284
33374
|
});
|
|
33285
33375
|
});
|
|
33286
33376
|
} else {
|
|
33287
|
-
return new Promise((
|
|
33377
|
+
return new Promise((resolve21, reject2) => {
|
|
33288
33378
|
sharp_default.pipeline(this.options, (err, data, info) => {
|
|
33289
33379
|
if (err) {
|
|
33290
33380
|
reject2(is_default.nativeError(err, stack));
|
|
33291
33381
|
} else {
|
|
33292
33382
|
if (this.options.resolveWithObject) {
|
|
33293
|
-
|
|
33383
|
+
resolve21({ data, info });
|
|
33294
33384
|
} else {
|
|
33295
|
-
|
|
33385
|
+
resolve21(data);
|
|
33296
33386
|
}
|
|
33297
33387
|
}
|
|
33298
33388
|
});
|
|
@@ -33497,11 +33587,11 @@ var UploadError = class extends Error {
|
|
|
33497
33587
|
}
|
|
33498
33588
|
};
|
|
33499
33589
|
function timeout2(promise, ms, code) {
|
|
33500
|
-
return new Promise((
|
|
33590
|
+
return new Promise((resolve21, reject2) => {
|
|
33501
33591
|
const timer9 = setTimeout(() => reject2(new UploadError(code, "upload operation timed out")), ms);
|
|
33502
33592
|
promise.then((value) => {
|
|
33503
33593
|
clearTimeout(timer9);
|
|
33504
|
-
|
|
33594
|
+
resolve21(value);
|
|
33505
33595
|
}, (error) => {
|
|
33506
33596
|
clearTimeout(timer9);
|
|
33507
33597
|
reject2(error);
|
|
@@ -33522,7 +33612,7 @@ function scannerFromEnv(path2) {
|
|
|
33522
33612
|
return Promise.resolve();
|
|
33523
33613
|
}
|
|
33524
33614
|
if (!isAbsolute4(command)) return Promise.reject(new UploadError("UPLOAD_SCAN", "scanner path must be absolute"));
|
|
33525
|
-
return new Promise((
|
|
33615
|
+
return new Promise((resolve21, reject2) => {
|
|
33526
33616
|
const child = spawn2(command, [path2], { shell: false, stdio: "ignore" });
|
|
33527
33617
|
const timer9 = setTimeout(() => {
|
|
33528
33618
|
child.kill("SIGKILL");
|
|
@@ -33534,7 +33624,7 @@ function scannerFromEnv(path2) {
|
|
|
33534
33624
|
});
|
|
33535
33625
|
child.once("exit", (code) => {
|
|
33536
33626
|
clearTimeout(timer9);
|
|
33537
|
-
if (code === 0)
|
|
33627
|
+
if (code === 0) resolve21();
|
|
33538
33628
|
else reject2(new UploadError("UPLOAD_SCAN", `scanner exit ${code}`));
|
|
33539
33629
|
});
|
|
33540
33630
|
});
|
|
@@ -33961,29 +34051,29 @@ var REASON_LABEL = {
|
|
|
33961
34051
|
function dependsOnOf(spec) {
|
|
33962
34052
|
const v = spec.dependsOn;
|
|
33963
34053
|
if (!Array.isArray(v)) return [];
|
|
33964
|
-
const
|
|
33965
|
-
for (const x of v) if (typeof x === "string" && x !== "" && !
|
|
33966
|
-
return
|
|
34054
|
+
const out4 = [];
|
|
34055
|
+
for (const x of v) if (typeof x === "string" && x !== "" && !out4.includes(x)) out4.push(x);
|
|
34056
|
+
return out4;
|
|
33967
34057
|
}
|
|
33968
34058
|
function blockersFor(spec, deps, tipOf, isMerged) {
|
|
33969
34059
|
const ids = dependsOnOf(spec);
|
|
33970
34060
|
if (ids.length === 0) return [];
|
|
33971
34061
|
const base2 = spec.branchFrom ?? "HEAD";
|
|
33972
|
-
const
|
|
34062
|
+
const out4 = [];
|
|
33973
34063
|
for (const id of ids) {
|
|
33974
34064
|
const d = deps.get(id);
|
|
33975
34065
|
if (!d) {
|
|
33976
|
-
|
|
34066
|
+
out4.push({ id, reason: "missing" });
|
|
33977
34067
|
continue;
|
|
33978
34068
|
}
|
|
33979
34069
|
if (d.stage !== "done") {
|
|
33980
|
-
|
|
34070
|
+
out4.push({ id, reason: "unfinished" });
|
|
33981
34071
|
continue;
|
|
33982
34072
|
}
|
|
33983
34073
|
const tip = tipOf(d);
|
|
33984
|
-
if (tip && !isMerged(tip, base2))
|
|
34074
|
+
if (tip && !isMerged(tip, base2)) out4.push({ id, reason: "unmerged" });
|
|
33985
34075
|
}
|
|
33986
|
-
return
|
|
34076
|
+
return out4;
|
|
33987
34077
|
}
|
|
33988
34078
|
function blockedNote(bl) {
|
|
33989
34079
|
return `menunggu ${bl.map((b) => `${b.id} (${REASON_LABEL[b.reason]})`).join(", ")}`;
|
|
@@ -34051,18 +34141,18 @@ async function decorateBlocked(specs) {
|
|
|
34051
34141
|
const rows = await depRows(ids);
|
|
34052
34142
|
const deps = new Map(rows.map((r) => [r.id, r]));
|
|
34053
34143
|
const repos = /* @__PURE__ */ new Map();
|
|
34054
|
-
const
|
|
34144
|
+
const out4 = [];
|
|
34055
34145
|
for (const s2 of specs) {
|
|
34056
34146
|
const own = dependsOnOf(s2);
|
|
34057
34147
|
if (own.length === 0) {
|
|
34058
|
-
|
|
34148
|
+
out4.push({ ...s2, dependsOn: [], blockedBy: [] });
|
|
34059
34149
|
continue;
|
|
34060
34150
|
}
|
|
34061
34151
|
if (!repos.has(s2.projectId)) repos.set(s2.projectId, await resolveRepoDir(s2.projectId));
|
|
34062
34152
|
const repo = repos.get(s2.projectId);
|
|
34063
|
-
|
|
34153
|
+
out4.push({ ...s2, dependsOn: own, blockedBy: blockersFor(s2, deps, tipper(repo), merger(repo)) });
|
|
34064
34154
|
}
|
|
34065
|
-
return
|
|
34155
|
+
return out4;
|
|
34066
34156
|
}
|
|
34067
34157
|
async function validateDependsOn(specId, projectId, raw2) {
|
|
34068
34158
|
const ids = dependsOnOf({ dependsOn: raw2 });
|
|
@@ -35479,12 +35569,12 @@ ${content}</tr>
|
|
|
35479
35569
|
return text;
|
|
35480
35570
|
}
|
|
35481
35571
|
href = cleanHref;
|
|
35482
|
-
let
|
|
35572
|
+
let out4 = '<a href="' + href + '"';
|
|
35483
35573
|
if (title) {
|
|
35484
|
-
|
|
35574
|
+
out4 += ' title="' + title + '"';
|
|
35485
35575
|
}
|
|
35486
|
-
|
|
35487
|
-
return
|
|
35576
|
+
out4 += ">" + text + "</a>";
|
|
35577
|
+
return out4;
|
|
35488
35578
|
}
|
|
35489
35579
|
image(href, title, text) {
|
|
35490
35580
|
const cleanHref = cleanUrl(href);
|
|
@@ -35492,12 +35582,12 @@ ${content}</tr>
|
|
|
35492
35582
|
return text;
|
|
35493
35583
|
}
|
|
35494
35584
|
href = cleanHref;
|
|
35495
|
-
let
|
|
35585
|
+
let out4 = `<img src="${href}" alt="${text}"`;
|
|
35496
35586
|
if (title) {
|
|
35497
|
-
|
|
35587
|
+
out4 += ` title="${title}"`;
|
|
35498
35588
|
}
|
|
35499
|
-
|
|
35500
|
-
return
|
|
35589
|
+
out4 += ">";
|
|
35590
|
+
return out4;
|
|
35501
35591
|
}
|
|
35502
35592
|
text(text) {
|
|
35503
35593
|
return text;
|
|
@@ -35562,14 +35652,14 @@ var _Parser = class __Parser {
|
|
|
35562
35652
|
* Parse Loop
|
|
35563
35653
|
*/
|
|
35564
35654
|
parse(tokens, top = true) {
|
|
35565
|
-
let
|
|
35655
|
+
let out4 = "";
|
|
35566
35656
|
for (let i = 0; i < tokens.length; i++) {
|
|
35567
35657
|
const token = tokens[i];
|
|
35568
35658
|
if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
|
|
35569
35659
|
const genericToken = token;
|
|
35570
35660
|
const ret = this.options.extensions.renderers[genericToken.type].call({ parser: this }, genericToken);
|
|
35571
35661
|
if (ret !== false || !["space", "hr", "heading", "code", "table", "blockquote", "list", "html", "paragraph", "text"].includes(genericToken.type)) {
|
|
35572
|
-
|
|
35662
|
+
out4 += ret || "";
|
|
35573
35663
|
continue;
|
|
35574
35664
|
}
|
|
35575
35665
|
}
|
|
@@ -35578,17 +35668,17 @@ var _Parser = class __Parser {
|
|
|
35578
35668
|
continue;
|
|
35579
35669
|
}
|
|
35580
35670
|
case "hr": {
|
|
35581
|
-
|
|
35671
|
+
out4 += this.renderer.hr();
|
|
35582
35672
|
continue;
|
|
35583
35673
|
}
|
|
35584
35674
|
case "heading": {
|
|
35585
35675
|
const headingToken = token;
|
|
35586
|
-
|
|
35676
|
+
out4 += this.renderer.heading(this.parseInline(headingToken.tokens), headingToken.depth, unescape(this.parseInline(headingToken.tokens, this.textRenderer)));
|
|
35587
35677
|
continue;
|
|
35588
35678
|
}
|
|
35589
35679
|
case "code": {
|
|
35590
35680
|
const codeToken = token;
|
|
35591
|
-
|
|
35681
|
+
out4 += this.renderer.code(codeToken.text, codeToken.lang, !!codeToken.escaped);
|
|
35592
35682
|
continue;
|
|
35593
35683
|
}
|
|
35594
35684
|
case "table": {
|
|
@@ -35608,13 +35698,13 @@ var _Parser = class __Parser {
|
|
|
35608
35698
|
}
|
|
35609
35699
|
body += this.renderer.tablerow(cell);
|
|
35610
35700
|
}
|
|
35611
|
-
|
|
35701
|
+
out4 += this.renderer.table(header2, body);
|
|
35612
35702
|
continue;
|
|
35613
35703
|
}
|
|
35614
35704
|
case "blockquote": {
|
|
35615
35705
|
const blockquoteToken = token;
|
|
35616
35706
|
const body = this.parse(blockquoteToken.tokens);
|
|
35617
|
-
|
|
35707
|
+
out4 += this.renderer.blockquote(body);
|
|
35618
35708
|
continue;
|
|
35619
35709
|
}
|
|
35620
35710
|
case "list": {
|
|
@@ -35649,17 +35739,17 @@ var _Parser = class __Parser {
|
|
|
35649
35739
|
itemBody += this.parse(item.tokens, loose);
|
|
35650
35740
|
body += this.renderer.listitem(itemBody, task, !!checked);
|
|
35651
35741
|
}
|
|
35652
|
-
|
|
35742
|
+
out4 += this.renderer.list(body, ordered, start);
|
|
35653
35743
|
continue;
|
|
35654
35744
|
}
|
|
35655
35745
|
case "html": {
|
|
35656
35746
|
const htmlToken = token;
|
|
35657
|
-
|
|
35747
|
+
out4 += this.renderer.html(htmlToken.text, htmlToken.block);
|
|
35658
35748
|
continue;
|
|
35659
35749
|
}
|
|
35660
35750
|
case "paragraph": {
|
|
35661
35751
|
const paragraphToken = token;
|
|
35662
|
-
|
|
35752
|
+
out4 += this.renderer.paragraph(this.parseInline(paragraphToken.tokens));
|
|
35663
35753
|
continue;
|
|
35664
35754
|
}
|
|
35665
35755
|
case "text": {
|
|
@@ -35669,7 +35759,7 @@ var _Parser = class __Parser {
|
|
|
35669
35759
|
textToken = tokens[++i];
|
|
35670
35760
|
body += "\n" + (textToken.tokens ? this.parseInline(textToken.tokens) : textToken.text);
|
|
35671
35761
|
}
|
|
35672
|
-
|
|
35762
|
+
out4 += top ? this.renderer.paragraph(body) : body;
|
|
35673
35763
|
continue;
|
|
35674
35764
|
}
|
|
35675
35765
|
default: {
|
|
@@ -35683,71 +35773,71 @@ var _Parser = class __Parser {
|
|
|
35683
35773
|
}
|
|
35684
35774
|
}
|
|
35685
35775
|
}
|
|
35686
|
-
return
|
|
35776
|
+
return out4;
|
|
35687
35777
|
}
|
|
35688
35778
|
/**
|
|
35689
35779
|
* Parse Inline Tokens
|
|
35690
35780
|
*/
|
|
35691
35781
|
parseInline(tokens, renderer) {
|
|
35692
35782
|
renderer = renderer || this.renderer;
|
|
35693
|
-
let
|
|
35783
|
+
let out4 = "";
|
|
35694
35784
|
for (let i = 0; i < tokens.length; i++) {
|
|
35695
35785
|
const token = tokens[i];
|
|
35696
35786
|
if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
|
|
35697
35787
|
const ret = this.options.extensions.renderers[token.type].call({ parser: this }, token);
|
|
35698
35788
|
if (ret !== false || !["escape", "html", "link", "image", "strong", "em", "codespan", "br", "del", "text"].includes(token.type)) {
|
|
35699
|
-
|
|
35789
|
+
out4 += ret || "";
|
|
35700
35790
|
continue;
|
|
35701
35791
|
}
|
|
35702
35792
|
}
|
|
35703
35793
|
switch (token.type) {
|
|
35704
35794
|
case "escape": {
|
|
35705
35795
|
const escapeToken = token;
|
|
35706
|
-
|
|
35796
|
+
out4 += renderer.text(escapeToken.text);
|
|
35707
35797
|
break;
|
|
35708
35798
|
}
|
|
35709
35799
|
case "html": {
|
|
35710
35800
|
const tagToken = token;
|
|
35711
|
-
|
|
35801
|
+
out4 += renderer.html(tagToken.text);
|
|
35712
35802
|
break;
|
|
35713
35803
|
}
|
|
35714
35804
|
case "link": {
|
|
35715
35805
|
const linkToken = token;
|
|
35716
|
-
|
|
35806
|
+
out4 += renderer.link(linkToken.href, linkToken.title, this.parseInline(linkToken.tokens, renderer));
|
|
35717
35807
|
break;
|
|
35718
35808
|
}
|
|
35719
35809
|
case "image": {
|
|
35720
35810
|
const imageToken = token;
|
|
35721
|
-
|
|
35811
|
+
out4 += renderer.image(imageToken.href, imageToken.title, imageToken.text);
|
|
35722
35812
|
break;
|
|
35723
35813
|
}
|
|
35724
35814
|
case "strong": {
|
|
35725
35815
|
const strongToken = token;
|
|
35726
|
-
|
|
35816
|
+
out4 += renderer.strong(this.parseInline(strongToken.tokens, renderer));
|
|
35727
35817
|
break;
|
|
35728
35818
|
}
|
|
35729
35819
|
case "em": {
|
|
35730
35820
|
const emToken = token;
|
|
35731
|
-
|
|
35821
|
+
out4 += renderer.em(this.parseInline(emToken.tokens, renderer));
|
|
35732
35822
|
break;
|
|
35733
35823
|
}
|
|
35734
35824
|
case "codespan": {
|
|
35735
35825
|
const codespanToken = token;
|
|
35736
|
-
|
|
35826
|
+
out4 += renderer.codespan(codespanToken.text);
|
|
35737
35827
|
break;
|
|
35738
35828
|
}
|
|
35739
35829
|
case "br": {
|
|
35740
|
-
|
|
35830
|
+
out4 += renderer.br();
|
|
35741
35831
|
break;
|
|
35742
35832
|
}
|
|
35743
35833
|
case "del": {
|
|
35744
35834
|
const delToken = token;
|
|
35745
|
-
|
|
35835
|
+
out4 += renderer.del(this.parseInline(delToken.tokens, renderer));
|
|
35746
35836
|
break;
|
|
35747
35837
|
}
|
|
35748
35838
|
case "text": {
|
|
35749
35839
|
const textToken = token;
|
|
35750
|
-
|
|
35840
|
+
out4 += renderer.text(textToken.text);
|
|
35751
35841
|
break;
|
|
35752
35842
|
}
|
|
35753
35843
|
default: {
|
|
@@ -35761,7 +35851,7 @@ var _Parser = class __Parser {
|
|
|
35761
35851
|
}
|
|
35762
35852
|
}
|
|
35763
35853
|
}
|
|
35764
|
-
return
|
|
35854
|
+
return out4;
|
|
35765
35855
|
}
|
|
35766
35856
|
};
|
|
35767
35857
|
var _Hooks = class {
|
|
@@ -36155,26 +36245,26 @@ function isDropped(cp) {
|
|
|
36155
36245
|
return cp >= 126976 && cp <= 131071 || cp >= 9728 && cp <= 10175 || cp >= 65024 && cp <= 65039 || cp === 8205;
|
|
36156
36246
|
}
|
|
36157
36247
|
function toWinAnsi(s2) {
|
|
36158
|
-
let
|
|
36248
|
+
let out4 = "";
|
|
36159
36249
|
for (const ch of s2 || "") {
|
|
36160
36250
|
const t = TRANSLIT[ch];
|
|
36161
36251
|
if (t !== void 0) {
|
|
36162
|
-
|
|
36252
|
+
out4 += t;
|
|
36163
36253
|
continue;
|
|
36164
36254
|
}
|
|
36165
36255
|
const cp = ch.codePointAt(0);
|
|
36166
36256
|
if (cp <= 255) {
|
|
36167
|
-
|
|
36257
|
+
out4 += ch;
|
|
36168
36258
|
continue;
|
|
36169
36259
|
}
|
|
36170
36260
|
if (WINANSI_EXTRA.has(ch)) {
|
|
36171
|
-
|
|
36261
|
+
out4 += ch;
|
|
36172
36262
|
continue;
|
|
36173
36263
|
}
|
|
36174
36264
|
if (isDropped(cp)) continue;
|
|
36175
|
-
|
|
36265
|
+
out4 += "?";
|
|
36176
36266
|
}
|
|
36177
|
-
return
|
|
36267
|
+
return out4;
|
|
36178
36268
|
}
|
|
36179
36269
|
function downloadFilename(prefix, path2, ext) {
|
|
36180
36270
|
const base2 = (path2.split("/").pop() ?? "").replace(/\.[^.]+$/, "");
|
|
@@ -36214,40 +36304,40 @@ function decodeEntities(s2) {
|
|
|
36214
36304
|
}
|
|
36215
36305
|
var inlineText2 = (s2) => decodeEntities(s2).replace(/\s*\n\s*/g, " ");
|
|
36216
36306
|
function flatten2(tokens, base2 = { text: "" }) {
|
|
36217
|
-
const
|
|
36307
|
+
const out4 = [];
|
|
36218
36308
|
for (const t of tokens ?? []) {
|
|
36219
36309
|
switch (t.type) {
|
|
36220
36310
|
case "strong":
|
|
36221
|
-
|
|
36311
|
+
out4.push(...flatten2(t.tokens, { ...base2, bold: true }));
|
|
36222
36312
|
break;
|
|
36223
36313
|
case "em":
|
|
36224
|
-
|
|
36314
|
+
out4.push(...flatten2(t.tokens, { ...base2, italic: true }));
|
|
36225
36315
|
break;
|
|
36226
36316
|
case "del":
|
|
36227
|
-
|
|
36317
|
+
out4.push(...flatten2(t.tokens, { ...base2, strike: true }));
|
|
36228
36318
|
break;
|
|
36229
36319
|
case "codespan":
|
|
36230
|
-
|
|
36320
|
+
out4.push({ ...base2, text: inlineText2(t.text ?? ""), mono: true });
|
|
36231
36321
|
break;
|
|
36232
36322
|
case "link":
|
|
36233
|
-
|
|
36323
|
+
out4.push(...flatten2(t.tokens, { ...base2, link: t.href }));
|
|
36234
36324
|
break;
|
|
36235
36325
|
case "br":
|
|
36236
|
-
|
|
36326
|
+
out4.push({ ...base2, text: "\n" });
|
|
36237
36327
|
break;
|
|
36238
36328
|
case "image":
|
|
36239
|
-
|
|
36329
|
+
out4.push({ ...base2, text: `[gambar: ${t.href ?? ""}]`, italic: true });
|
|
36240
36330
|
break;
|
|
36241
36331
|
case "escape":
|
|
36242
36332
|
case "text":
|
|
36243
|
-
if (t.tokens?.length)
|
|
36244
|
-
else
|
|
36333
|
+
if (t.tokens?.length) out4.push(...flatten2(t.tokens, base2));
|
|
36334
|
+
else out4.push({ ...base2, text: inlineText2(t.text ?? "") });
|
|
36245
36335
|
break;
|
|
36246
36336
|
default:
|
|
36247
|
-
|
|
36337
|
+
out4.push({ ...base2, text: inlineText2(t.text ?? "") });
|
|
36248
36338
|
}
|
|
36249
36339
|
}
|
|
36250
|
-
return
|
|
36340
|
+
return out4;
|
|
36251
36341
|
}
|
|
36252
36342
|
function drawRuns(doc, runs, size, color2, opts = {}) {
|
|
36253
36343
|
const items = runs.filter((r) => r.text !== "");
|
|
@@ -36417,7 +36507,7 @@ function renderDocPdf(text, name2, meta) {
|
|
|
36417
36507
|
});
|
|
36418
36508
|
const chunks = [];
|
|
36419
36509
|
doc.on("data", (c) => chunks.push(c));
|
|
36420
|
-
const done = new Promise((
|
|
36510
|
+
const done = new Promise((resolve21) => doc.on("end", () => resolve21(Buffer.concat(chunks))));
|
|
36421
36511
|
doc.font("Helvetica-Bold").fontSize(8).fillColor(BRASS).text(toWinAnsi(meta.eyebrow.toUpperCase()), { characterSpacing: 0.8 });
|
|
36422
36512
|
doc.moveDown(0.25);
|
|
36423
36513
|
doc.font("Helvetica-Bold").fontSize(17).fillColor(STRONG).text(toWinAnsi(name2.split("/").pop() ?? name2));
|
|
@@ -36521,7 +36611,7 @@ async function liveSpecs(filter = {}) {
|
|
|
36521
36611
|
if (live.size === 0) return decorateBlocked(specs);
|
|
36522
36612
|
const advanced = [];
|
|
36523
36613
|
const doneNow = [];
|
|
36524
|
-
const
|
|
36614
|
+
const out4 = specs.map((s2) => {
|
|
36525
36615
|
const entry = live.get(s2.id);
|
|
36526
36616
|
if (!entry) return s2;
|
|
36527
36617
|
const next = stageForRun(entry.phases, entry.cwd, s2.id);
|
|
@@ -36538,7 +36628,7 @@ async function liveSpecs(filter = {}) {
|
|
|
36538
36628
|
await notifySynced("spec", a.id);
|
|
36539
36629
|
}));
|
|
36540
36630
|
await Promise.all(doneNow.map((d) => recordCompletion(d.specId, d.title, d.projectId)));
|
|
36541
|
-
return decorateBlocked(
|
|
36631
|
+
return decorateBlocked(out4);
|
|
36542
36632
|
}
|
|
36543
36633
|
|
|
36544
36634
|
// src/services/launch-authority.ts
|
|
@@ -37060,12 +37150,12 @@ function parseBreakdown(md) {
|
|
|
37060
37150
|
return [];
|
|
37061
37151
|
}
|
|
37062
37152
|
const arr = data && typeof data === "object" && Array.isArray(data.items) ? data.items : [];
|
|
37063
|
-
const
|
|
37153
|
+
const out4 = [];
|
|
37064
37154
|
for (const it of arr) {
|
|
37065
37155
|
const p = zBreakdownItem.safeParse(it);
|
|
37066
|
-
if (p.success)
|
|
37156
|
+
if (p.success) out4.push(p.data);
|
|
37067
37157
|
}
|
|
37068
|
-
return
|
|
37158
|
+
return out4;
|
|
37069
37159
|
}
|
|
37070
37160
|
async function resolveDir3(projectId, sessions) {
|
|
37071
37161
|
const live = sessions.find((s2) => s2.projectId === projectId && s2.flow === "breakdown" && !s2.exited && s2.cwd);
|
|
@@ -37135,8 +37225,9 @@ async function docs_default(app2) {
|
|
|
37135
37225
|
|
|
37136
37226
|
// src/routes/ide.ts
|
|
37137
37227
|
init_src2();
|
|
37138
|
-
import { basename as
|
|
37139
|
-
import { spawn as spawn3 } from "node:child_process";
|
|
37228
|
+
import { basename as basename5, resolve as resolve16 } from "node:path";
|
|
37229
|
+
import { execFile as execFile10, spawn as spawn3 } from "node:child_process";
|
|
37230
|
+
import { promisify as promisify10 } from "node:util";
|
|
37140
37231
|
|
|
37141
37232
|
// src/services/git-remotes.ts
|
|
37142
37233
|
import { execFile as execFile5 } from "node:child_process";
|
|
@@ -37550,53 +37641,53 @@ function gitArgs(op) {
|
|
|
37550
37641
|
}
|
|
37551
37642
|
}
|
|
37552
37643
|
async function afterMergeDelete(repoDir, branch, mergeOut, mergeErr) {
|
|
37553
|
-
const
|
|
37644
|
+
const out4 = [mergeOut], err = [mergeErr];
|
|
37554
37645
|
const step = async (args) => {
|
|
37555
37646
|
const r = await exec6("git", args, { cwd: repoDir, ...GIT5 });
|
|
37556
|
-
|
|
37647
|
+
out4.push(r.stdout);
|
|
37557
37648
|
err.push(r.stderr);
|
|
37558
37649
|
};
|
|
37559
37650
|
try {
|
|
37560
37651
|
await step(["branch", "-D", branch]);
|
|
37561
37652
|
const hasOrigin = await exec6("git", ["rev-parse", "--verify", "--quiet", `refs/remotes/origin/${branch}`], { cwd: repoDir, ...GIT5 }).then(() => true).catch(() => false);
|
|
37562
37653
|
if (hasOrigin) await step(["push", "origin", "--delete", branch]);
|
|
37563
|
-
return { ok: true, stdout:
|
|
37654
|
+
return { ok: true, stdout: out4.join("\n").trim(), stderr: err.join("\n").trim(), current: await currentBranch(repoDir) };
|
|
37564
37655
|
} catch (e) {
|
|
37565
37656
|
const ee = e;
|
|
37566
|
-
return { ok: false, stdout: [...
|
|
37657
|
+
return { ok: false, stdout: [...out4, ee.stdout ?? ""].join("\n").trim(), stderr: [...err, ee.stderr ?? String(e)].join("\n").trim(), current: await currentBranch(repoDir) };
|
|
37567
37658
|
}
|
|
37568
37659
|
}
|
|
37569
37660
|
async function runDeleteBranch(repoDir, op) {
|
|
37570
|
-
const
|
|
37661
|
+
const out4 = [], err = [];
|
|
37571
37662
|
const step = async (args) => {
|
|
37572
37663
|
const r = await exec6("git", args, { cwd: repoDir, ...GIT5 });
|
|
37573
|
-
|
|
37664
|
+
out4.push(r.stdout);
|
|
37574
37665
|
err.push(r.stderr);
|
|
37575
37666
|
};
|
|
37576
37667
|
try {
|
|
37577
37668
|
if (op.local !== false) await step(gitArgs({ op: "delete-branch", name: op.name, force: op.force }));
|
|
37578
37669
|
if (op.remote) await step(["push", "origin", "--delete", "--end-of-options", op.name]);
|
|
37579
|
-
return { ok: true, stdout:
|
|
37670
|
+
return { ok: true, stdout: out4.join("\n").trim(), stderr: err.join("\n").trim(), current: await currentBranch(repoDir) };
|
|
37580
37671
|
} catch (e) {
|
|
37581
37672
|
const ee = e;
|
|
37582
|
-
return { ok: false, stdout: [...
|
|
37673
|
+
return { ok: false, stdout: [...out4, ee.stdout ?? ""].join("\n").trim(), stderr: [...err, ee.stderr ?? String(e)].join("\n").trim(), current: await currentBranch(repoDir) };
|
|
37583
37674
|
}
|
|
37584
37675
|
}
|
|
37585
37676
|
async function runTagOp(repoDir, op) {
|
|
37586
|
-
const
|
|
37677
|
+
const out4 = [], err = [];
|
|
37587
37678
|
const step = async (args) => {
|
|
37588
37679
|
const r = await exec6("git", args, { cwd: repoDir, ...GIT5 });
|
|
37589
|
-
|
|
37680
|
+
out4.push(r.stdout);
|
|
37590
37681
|
err.push(r.stderr);
|
|
37591
37682
|
};
|
|
37592
37683
|
try {
|
|
37593
37684
|
await step(gitArgs(op));
|
|
37594
37685
|
if (op.op === "tag" && op.push) await step(["push", "origin", "--end-of-options", op.name]);
|
|
37595
37686
|
if (op.op === "delete-tag" && op.remote) await step(["push", "origin", "--delete", "--end-of-options", op.name]);
|
|
37596
|
-
return { ok: true, stdout:
|
|
37687
|
+
return { ok: true, stdout: out4.join("\n").trim(), stderr: err.join("\n").trim(), current: await currentBranch(repoDir) };
|
|
37597
37688
|
} catch (e) {
|
|
37598
37689
|
const ee = e;
|
|
37599
|
-
return { ok: false, stdout: [...
|
|
37690
|
+
return { ok: false, stdout: [...out4, ee.stdout ?? ""].join("\n").trim(), stderr: [...err, ee.stderr ?? String(e)].join("\n").trim(), current: await currentBranch(repoDir) };
|
|
37600
37691
|
}
|
|
37601
37692
|
}
|
|
37602
37693
|
async function runGitOp(repoDir, op) {
|
|
@@ -37679,22 +37770,32 @@ async function resolveBase(repoDir, want, current) {
|
|
|
37679
37770
|
return current && current !== "HEAD" ? current : "HEAD";
|
|
37680
37771
|
}
|
|
37681
37772
|
async function worktreeBranches(repoDir) {
|
|
37682
|
-
const
|
|
37683
|
-
|
|
37684
|
-
|
|
37685
|
-
|
|
37773
|
+
const m = /* @__PURE__ */ new Map();
|
|
37774
|
+
let path2 = "";
|
|
37775
|
+
for (const l of lines(await out2(repoDir, ["worktree", "list", "--porcelain"]))) {
|
|
37776
|
+
if (l.startsWith("worktree ")) path2 = l.slice("worktree ".length);
|
|
37777
|
+
else if (l.startsWith("branch refs/heads/")) m.set(l.slice("branch refs/heads/".length), path2);
|
|
37778
|
+
}
|
|
37779
|
+
return m;
|
|
37686
37780
|
}
|
|
37687
37781
|
var SEP = "";
|
|
37688
|
-
async function
|
|
37689
|
-
const fmt = [
|
|
37690
|
-
|
|
37782
|
+
async function refIndex(repoDir) {
|
|
37783
|
+
const fmt = [
|
|
37784
|
+
"%(refname)",
|
|
37785
|
+
"%(refname:short)",
|
|
37786
|
+
"%(objectname)",
|
|
37787
|
+
"%(committerdate:iso-strict)",
|
|
37788
|
+
"%(contents:subject)"
|
|
37789
|
+
].join(SEP);
|
|
37790
|
+
const idx = { locals: /* @__PURE__ */ new Set(), remotes: /* @__PURE__ */ new Set(), meta: /* @__PURE__ */ new Map() };
|
|
37691
37791
|
for (const l of lines(await out2(repoDir, ["for-each-ref", `--format=${fmt}`, "refs/heads", "refs/remotes/origin"]))) {
|
|
37692
|
-
const [ref, sha, at, ...rest] = l.split(SEP);
|
|
37792
|
+
const [full, ref, sha, at, ...rest] = l.split(SEP);
|
|
37693
37793
|
const name2 = shortName(ref ?? "");
|
|
37694
37794
|
if (!name2 || !sha) continue;
|
|
37695
|
-
|
|
37795
|
+
(full?.startsWith("refs/heads/") ? idx.locals : idx.remotes).add(name2);
|
|
37796
|
+
if (!idx.meta.has(name2)) idx.meta.set(name2, { sha, at: at ?? "", subject: rest.join(SEP) });
|
|
37696
37797
|
}
|
|
37697
|
-
return
|
|
37798
|
+
return idx;
|
|
37698
37799
|
}
|
|
37699
37800
|
async function listUnusedBranches(repoDir, opts) {
|
|
37700
37801
|
if (!repoDir) return EMPTY;
|
|
@@ -37705,26 +37806,45 @@ async function listUnusedBranches(repoDir, opts) {
|
|
|
37705
37806
|
if (!baseSha) return { ...EMPTY, current };
|
|
37706
37807
|
const baseRemote = await revSha(repoDir, `origin/${base2}`) ? `origin/${base2}` : null;
|
|
37707
37808
|
const baseRemoteSha = baseRemote ? await revSha(repoDir, baseRemote) : baseSha;
|
|
37708
|
-
const [localMerged, remoteMerged, wt,
|
|
37809
|
+
const [localMerged, remoteMerged, wt, refs2] = await Promise.all([
|
|
37709
37810
|
out2(repoDir, ["branch", "--merged", baseSha, "--format=%(refname:short)"]),
|
|
37710
37811
|
out2(repoDir, ["branch", "-r", "--merged", baseRemoteSha, "--format=%(refname:short)"]),
|
|
37711
37812
|
worktreeBranches(repoDir),
|
|
37712
|
-
|
|
37813
|
+
refIndex(repoDir)
|
|
37713
37814
|
]);
|
|
37714
|
-
const
|
|
37715
|
-
const
|
|
37815
|
+
const mergedLocals = new Set(lines(localMerged).map(shortName).filter(Boolean));
|
|
37816
|
+
const mergedRemotes = new Set(
|
|
37716
37817
|
lines(remoteMerged).filter((r) => r.startsWith("origin/")).map(shortName).filter(Boolean)
|
|
37717
37818
|
);
|
|
37718
|
-
const
|
|
37719
|
-
const
|
|
37819
|
+
const all = opts.include === "all";
|
|
37820
|
+
const names = [...new Set(all ? [...refs2.locals, ...refs2.remotes] : [...mergedLocals, ...mergedRemotes])].sort();
|
|
37821
|
+
const branches = [];
|
|
37822
|
+
for (const name2 of names) {
|
|
37823
|
+
const local = refs2.locals.has(name2);
|
|
37824
|
+
const remote = refs2.remotes.has(name2);
|
|
37825
|
+
const mergedLocal = local && mergedLocals.has(name2);
|
|
37826
|
+
const mergedRemote = remote && mergedRemotes.has(name2);
|
|
37827
|
+
const merged = (!local || mergedLocal) && (!remote || mergedRemote);
|
|
37828
|
+
if (!all && !merged) continue;
|
|
37720
37829
|
const locks = [];
|
|
37721
37830
|
if (name2 === current) locks.push("current");
|
|
37722
37831
|
if (name2 === base2) locks.push("base");
|
|
37723
37832
|
if (wt.has(name2)) locks.push("worktree");
|
|
37724
37833
|
if (opts.openSpecBranches.has(name2)) locks.push("spec-open");
|
|
37725
37834
|
if (opts.sessionBranches.has(name2)) locks.push("session");
|
|
37726
|
-
|
|
37727
|
-
|
|
37835
|
+
const wtPath = wt.get(name2);
|
|
37836
|
+
branches.push({
|
|
37837
|
+
name: name2,
|
|
37838
|
+
local,
|
|
37839
|
+
remote,
|
|
37840
|
+
mergedLocal,
|
|
37841
|
+
mergedRemote,
|
|
37842
|
+
merged,
|
|
37843
|
+
lastCommit: refs2.meta.get(name2) ?? null,
|
|
37844
|
+
locks,
|
|
37845
|
+
...wtPath ? { worktree: wtPath } : {}
|
|
37846
|
+
});
|
|
37847
|
+
}
|
|
37728
37848
|
return { base: base2, baseRemote, current, branches };
|
|
37729
37849
|
}
|
|
37730
37850
|
function effectiveScope(want, b) {
|
|
@@ -37736,26 +37856,30 @@ function effectiveScope(want, b) {
|
|
|
37736
37856
|
return "none";
|
|
37737
37857
|
}
|
|
37738
37858
|
async function deleteBranches(repoDir, names, opts) {
|
|
37739
|
-
const report = await listUnusedBranches(repoDir, opts);
|
|
37859
|
+
const report = await listUnusedBranches(repoDir, { ...opts, include: "all" });
|
|
37740
37860
|
const byName = new Map(report.branches.map((b) => [b.name, b]));
|
|
37741
37861
|
const results = [];
|
|
37742
37862
|
for (const name2 of names) {
|
|
37743
37863
|
const b = byName.get(name2);
|
|
37744
37864
|
if (!b) {
|
|
37865
|
+
results.push({ name: name2, ok: false, scope: "none", error: "branch tak ditemukan di repo" });
|
|
37866
|
+
continue;
|
|
37867
|
+
}
|
|
37868
|
+
if (b.locks.length) {
|
|
37745
37869
|
results.push({
|
|
37746
37870
|
name: name2,
|
|
37747
37871
|
ok: false,
|
|
37748
37872
|
scope: "none",
|
|
37749
|
-
error: `
|
|
37873
|
+
error: `terkunci: ${b.locks.map((l) => LOCK_REASON[l]).join(", ")}`
|
|
37750
37874
|
});
|
|
37751
37875
|
continue;
|
|
37752
37876
|
}
|
|
37753
|
-
if (b.
|
|
37877
|
+
if (!b.merged && !opts.allowUnmerged) {
|
|
37754
37878
|
results.push({
|
|
37755
37879
|
name: name2,
|
|
37756
37880
|
ok: false,
|
|
37757
37881
|
scope: "none",
|
|
37758
|
-
error: `
|
|
37882
|
+
error: `belum ter-merge ke ${report.base} \u2014 commit-nya bisa hilang; butuh konfirmasi terpisah`
|
|
37759
37883
|
});
|
|
37760
37884
|
continue;
|
|
37761
37885
|
}
|
|
@@ -37769,21 +37893,353 @@ async function deleteBranches(repoDir, names, opts) {
|
|
|
37769
37893
|
});
|
|
37770
37894
|
continue;
|
|
37771
37895
|
}
|
|
37896
|
+
const forced = scope !== "remote" && !b.mergedLocal;
|
|
37772
37897
|
const r = await runGitOp(repoDir, {
|
|
37773
37898
|
op: "delete-branch",
|
|
37774
37899
|
name: name2,
|
|
37775
37900
|
local: scope !== "remote",
|
|
37776
|
-
remote: scope !== "local"
|
|
37901
|
+
remote: scope !== "local",
|
|
37902
|
+
force: forced
|
|
37777
37903
|
});
|
|
37778
|
-
results.push(r.ok ? { name: name2, ok: true, scope } : { name: name2, ok: false, scope, error: r.stderr || "hapus branch gagal" });
|
|
37904
|
+
results.push(r.ok ? { name: name2, ok: true, scope, ...forced ? { forced: true } : {} } : { name: name2, ok: false, scope, error: r.stderr || "hapus branch gagal" });
|
|
37779
37905
|
}
|
|
37780
37906
|
return { base: report.base, results };
|
|
37781
37907
|
}
|
|
37782
37908
|
|
|
37909
|
+
// src/services/worktree-list.ts
|
|
37910
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
37911
|
+
import { realpathSync as realpathSync4 } from "node:fs";
|
|
37912
|
+
import { stat } from "node:fs/promises";
|
|
37913
|
+
import { basename as basename4, join as join11, resolve as resolve14, sep as sep4 } from "node:path";
|
|
37914
|
+
import { promisify as promisify8 } from "node:util";
|
|
37915
|
+
|
|
37916
|
+
// src/services/session-worktree.ts
|
|
37917
|
+
import { resolve as resolve13, sep as sep3 } from "node:path";
|
|
37918
|
+
function ownsWorktree(repoDir, cwd) {
|
|
37919
|
+
const base2 = resolve13(repoDir);
|
|
37920
|
+
const dir2 = resolve13(cwd);
|
|
37921
|
+
const container = resolve13(base2, ".worktrees");
|
|
37922
|
+
return dir2 !== base2 && dir2 !== container && dir2.startsWith(container + sep3);
|
|
37923
|
+
}
|
|
37924
|
+
|
|
37925
|
+
// src/services/worktree-list.ts
|
|
37926
|
+
var exec8 = promisify8(execFile8);
|
|
37927
|
+
var GIT7 = { timeout: 6e4, maxBuffer: 1 << 24, encoding: "utf8" };
|
|
37928
|
+
async function out3(cwd, args) {
|
|
37929
|
+
try {
|
|
37930
|
+
return (await exec8("git", args, { cwd, ...GIT7 })).stdout;
|
|
37931
|
+
} catch {
|
|
37932
|
+
return "";
|
|
37933
|
+
}
|
|
37934
|
+
}
|
|
37935
|
+
function parseWorktreePorcelain(text) {
|
|
37936
|
+
const rows = [];
|
|
37937
|
+
let cur = null;
|
|
37938
|
+
for (const raw2 of text.split("\n")) {
|
|
37939
|
+
const line = raw2.replace(/\r$/, "");
|
|
37940
|
+
if (line.startsWith("worktree ")) {
|
|
37941
|
+
cur = {
|
|
37942
|
+
path: line.slice("worktree ".length),
|
|
37943
|
+
head: "",
|
|
37944
|
+
branch: null,
|
|
37945
|
+
prunable: false,
|
|
37946
|
+
locked: false,
|
|
37947
|
+
bare: false
|
|
37948
|
+
};
|
|
37949
|
+
rows.push(cur);
|
|
37950
|
+
} else if (!cur) continue;
|
|
37951
|
+
else if (line.startsWith("HEAD ")) cur.head = line.slice("HEAD ".length);
|
|
37952
|
+
else if (line.startsWith("branch refs/heads/")) cur.branch = line.slice("branch refs/heads/".length);
|
|
37953
|
+
else if (line === "bare") cur.bare = true;
|
|
37954
|
+
else if (line === "locked" || line.startsWith("locked ")) cur.locked = true;
|
|
37955
|
+
else if (line === "prunable" || line.startsWith("prunable ")) cur.prunable = true;
|
|
37956
|
+
}
|
|
37957
|
+
return rows;
|
|
37958
|
+
}
|
|
37959
|
+
var EMPTY2 = { repoDir: "", worktrees: [] };
|
|
37960
|
+
var real = (p) => {
|
|
37961
|
+
try {
|
|
37962
|
+
return realpathSync4(p);
|
|
37963
|
+
} catch {
|
|
37964
|
+
return resolve14(p);
|
|
37965
|
+
}
|
|
37966
|
+
};
|
|
37967
|
+
async function bornAt(path2) {
|
|
37968
|
+
try {
|
|
37969
|
+
const st = await stat(join11(path2, ".git"));
|
|
37970
|
+
const ms = st.birthtimeMs > 0 ? st.birthtimeMs : st.mtimeMs;
|
|
37971
|
+
return new Date(ms).toISOString();
|
|
37972
|
+
} catch {
|
|
37973
|
+
return null;
|
|
37974
|
+
}
|
|
37975
|
+
}
|
|
37976
|
+
async function listWorktrees(repoDir, inputs) {
|
|
37977
|
+
if (!repoDir) return EMPTY2;
|
|
37978
|
+
const base2 = resolve14(repoDir);
|
|
37979
|
+
const baseReal = real(base2);
|
|
37980
|
+
const text = await out3(base2, ["worktree", "list", "--porcelain"]);
|
|
37981
|
+
const trash = resolve14(baseReal, ".worktrees", ".trash");
|
|
37982
|
+
const sessions = new Map([...inputs.sessions].map(([cwd, s2]) => [real(cwd), s2]));
|
|
37983
|
+
const rows = [];
|
|
37984
|
+
for (const w of parseWorktreePorcelain(text)) {
|
|
37985
|
+
const path2 = resolve14(w.path);
|
|
37986
|
+
if (path2 === trash || path2.startsWith(trash + sep4)) continue;
|
|
37987
|
+
const name2 = basename4(path2);
|
|
37988
|
+
const deletable = ownsWorktree(baseReal, path2);
|
|
37989
|
+
const spec = inputs.specs.get(name2);
|
|
37990
|
+
rows.push({
|
|
37991
|
+
path: path2,
|
|
37992
|
+
name: name2,
|
|
37993
|
+
head: w.head,
|
|
37994
|
+
branch: w.branch,
|
|
37995
|
+
prunable: w.prunable,
|
|
37996
|
+
locked: w.locked,
|
|
37997
|
+
deletable,
|
|
37998
|
+
blocked: deletable ? null : path2 === baseReal ? "checkout project" : "di luar .worktrees project ini",
|
|
37999
|
+
spec: spec ? { id: spec.id, stage: spec.stage } : null,
|
|
38000
|
+
session: sessions.get(path2) ?? null,
|
|
38001
|
+
createdAt: await bornAt(path2)
|
|
38002
|
+
});
|
|
38003
|
+
}
|
|
38004
|
+
rows.sort((a, b) => Number(a.deletable) - Number(b.deletable) || a.name.localeCompare(b.name));
|
|
38005
|
+
return { repoDir: base2, worktrees: rows };
|
|
38006
|
+
}
|
|
38007
|
+
async function worktreeStats(repoDir, w) {
|
|
38008
|
+
const [sizeBytes, dirtyFiles, orphanCommits] = await Promise.all([
|
|
38009
|
+
diskBytes(w),
|
|
38010
|
+
dirtyCount(w),
|
|
38011
|
+
orphanCount(resolve14(repoDir), w)
|
|
38012
|
+
]);
|
|
38013
|
+
return { name: w.name, sizeBytes, dirtyFiles, orphanCommits };
|
|
38014
|
+
}
|
|
38015
|
+
async function diskBytes(w) {
|
|
38016
|
+
if (w.prunable) return null;
|
|
38017
|
+
try {
|
|
38018
|
+
const { stdout } = await exec8(
|
|
38019
|
+
"du",
|
|
38020
|
+
["-sk", w.path],
|
|
38021
|
+
{ timeout: 3e4, maxBuffer: 1 << 20, encoding: "utf8" }
|
|
38022
|
+
);
|
|
38023
|
+
const kb = Number.parseInt(stdout.trim().split(/\s+/)[0] ?? "", 10);
|
|
38024
|
+
return Number.isFinite(kb) ? kb * 1024 : null;
|
|
38025
|
+
} catch {
|
|
38026
|
+
return null;
|
|
38027
|
+
}
|
|
38028
|
+
}
|
|
38029
|
+
async function dirtyCount(w) {
|
|
38030
|
+
if (w.prunable) return 0;
|
|
38031
|
+
const s2 = await out3(w.path, ["status", "--porcelain"]);
|
|
38032
|
+
return s2.split("\n").filter((l) => l.trim()).length;
|
|
38033
|
+
}
|
|
38034
|
+
async function orphanCount(repoDir, w) {
|
|
38035
|
+
if (!w.head) return 0;
|
|
38036
|
+
const args = ["rev-list", "--count", w.head, "--not"];
|
|
38037
|
+
if (w.branch) args.push(`--exclude=${w.branch}`, "--branches", `--exclude=*/${w.branch}`, "--remotes", "--tags");
|
|
38038
|
+
else args.push("--branches", "--remotes", "--tags");
|
|
38039
|
+
const n2 = Number.parseInt((await out3(repoDir, args)).trim(), 10);
|
|
38040
|
+
return Number.isFinite(n2) ? n2 : 0;
|
|
38041
|
+
}
|
|
38042
|
+
async function deleteWorktrees(repoDir, names, opts) {
|
|
38043
|
+
const report = await listWorktrees(repoDir, opts);
|
|
38044
|
+
const byName = new Map(report.worktrees.map((w) => [w.name, w]));
|
|
38045
|
+
const results = [];
|
|
38046
|
+
for (const name2 of names) {
|
|
38047
|
+
const w = byName.get(name2);
|
|
38048
|
+
if (!w) {
|
|
38049
|
+
results.push({ name: name2, ok: false, cleanup: null, error: "worktree tak ditemukan" });
|
|
38050
|
+
continue;
|
|
38051
|
+
}
|
|
38052
|
+
if (!w.deletable) {
|
|
38053
|
+
results.push({ name: name2, ok: false, cleanup: null, error: `tak bisa dihapus: ${w.blocked}` });
|
|
38054
|
+
continue;
|
|
38055
|
+
}
|
|
38056
|
+
const row = { name: name2, ok: true, cleanup: null };
|
|
38057
|
+
try {
|
|
38058
|
+
if (w.session) {
|
|
38059
|
+
const closed = await opts.closeSession(w.session.id);
|
|
38060
|
+
row.closedSession = w.session.id;
|
|
38061
|
+
row.cleanup = closed?.cleanup ?? null;
|
|
38062
|
+
}
|
|
38063
|
+
if (!row.cleanup) row.cleanup = opts.release(report.repoDir, w.path);
|
|
38064
|
+
await opts.prune(report.repoDir);
|
|
38065
|
+
if (opts.withBranch && w.branch) {
|
|
38066
|
+
const b = await opts.deleteBranch(report.repoDir, w.branch);
|
|
38067
|
+
row.branch = { name: w.branch, ok: b.ok, ...b.error ? { error: b.error } : {} };
|
|
38068
|
+
}
|
|
38069
|
+
} catch (e) {
|
|
38070
|
+
row.ok = false;
|
|
38071
|
+
row.error = e.message;
|
|
38072
|
+
}
|
|
38073
|
+
results.push(row);
|
|
38074
|
+
}
|
|
38075
|
+
return { results };
|
|
38076
|
+
}
|
|
38077
|
+
|
|
38078
|
+
// src/services/session-close.ts
|
|
38079
|
+
init_src2();
|
|
38080
|
+
init_db();
|
|
38081
|
+
init_pty();
|
|
38082
|
+
init_session_phases();
|
|
38083
|
+
init_notifications();
|
|
38084
|
+
init_stage_machine();
|
|
38085
|
+
|
|
38086
|
+
// src/services/worktree-reaper.ts
|
|
38087
|
+
init_src2();
|
|
38088
|
+
init_db();
|
|
38089
|
+
import { execFile as execFile9 } from "node:child_process";
|
|
38090
|
+
import { readdir as readdir2, rm as rm4 } from "node:fs/promises";
|
|
38091
|
+
import { promisify as promisify9 } from "node:util";
|
|
38092
|
+
import { join as join12, resolve as resolve15 } from "node:path";
|
|
38093
|
+
init_notifications();
|
|
38094
|
+
var TICK_MS = 6e4;
|
|
38095
|
+
var trashDirOf = (repoDir) => resolve15(repoDir, ".worktrees", ".trash");
|
|
38096
|
+
var exec9 = promisify9(execFile9);
|
|
38097
|
+
var prodReaperDeps = {
|
|
38098
|
+
rm: (path2) => rm4(path2, { recursive: true, force: true }),
|
|
38099
|
+
prune: async (repoDir) => {
|
|
38100
|
+
try {
|
|
38101
|
+
await exec9("git", ["worktree", "prune"], { cwd: repoDir, timeout: 3e4 });
|
|
38102
|
+
} catch {
|
|
38103
|
+
}
|
|
38104
|
+
},
|
|
38105
|
+
repos: async () => {
|
|
38106
|
+
const projects = await prisma.project.findMany({ select: { id: true } });
|
|
38107
|
+
const seen = /* @__PURE__ */ new Set();
|
|
38108
|
+
const out4 = [];
|
|
38109
|
+
for (const p of projects) {
|
|
38110
|
+
const repoDir = await resolveRepoDir(p.id);
|
|
38111
|
+
if (!repoDir || seen.has(repoDir)) continue;
|
|
38112
|
+
seen.add(repoDir);
|
|
38113
|
+
out4.push({ projectId: p.id, repoDir });
|
|
38114
|
+
}
|
|
38115
|
+
return out4;
|
|
38116
|
+
}
|
|
38117
|
+
};
|
|
38118
|
+
var pending = /* @__PURE__ */ new Map();
|
|
38119
|
+
var sessionIdOf = (entry) => entry.split(".")[0] ?? entry;
|
|
38120
|
+
function releaseWorktree(repoDir, cwd, projectId, deps = prodReaperDeps) {
|
|
38121
|
+
let path2;
|
|
38122
|
+
try {
|
|
38123
|
+
path2 = realGit.trashWorktree(repoDir, cwd);
|
|
38124
|
+
} catch {
|
|
38125
|
+
realGit.removeWorktree(repoDir, cwd);
|
|
38126
|
+
return null;
|
|
38127
|
+
}
|
|
38128
|
+
if (!path2) return null;
|
|
38129
|
+
const entry = path2.slice(trashDirOf(repoDir).length + 1);
|
|
38130
|
+
pending.set(path2, { path: path2, repoDir, projectId, entry, sessionId: sessionIdOf(entry), since: Date.now() });
|
|
38131
|
+
reapSoon(repoDir, projectId, deps);
|
|
38132
|
+
return entry;
|
|
38133
|
+
}
|
|
38134
|
+
var listCleanups = () => [...pending.values()].sort((a, b) => a.since - b.since).map((p) => ({
|
|
38135
|
+
sessionId: p.sessionId,
|
|
38136
|
+
projectId: p.projectId,
|
|
38137
|
+
entry: p.entry,
|
|
38138
|
+
since: new Date(p.since).toISOString(),
|
|
38139
|
+
state: p.error ? "failed" : "closing",
|
|
38140
|
+
...p.error ? { error: p.error } : {}
|
|
38141
|
+
}));
|
|
38142
|
+
async function sweepRepo(repoDir, projectId, deps = prodReaperDeps) {
|
|
38143
|
+
const dir2 = trashDirOf(repoDir);
|
|
38144
|
+
let entries;
|
|
38145
|
+
try {
|
|
38146
|
+
entries = await readdir2(dir2);
|
|
38147
|
+
} catch {
|
|
38148
|
+
return 0;
|
|
38149
|
+
}
|
|
38150
|
+
let removed = 0;
|
|
38151
|
+
for (const entry of entries) {
|
|
38152
|
+
const path2 = join12(dir2, entry);
|
|
38153
|
+
const known = pending.get(path2);
|
|
38154
|
+
const row = known ?? { path: path2, repoDir, projectId, entry, sessionId: sessionIdOf(entry), since: Date.now() };
|
|
38155
|
+
pending.set(path2, row);
|
|
38156
|
+
try {
|
|
38157
|
+
await deps.rm(path2);
|
|
38158
|
+
pending.delete(path2);
|
|
38159
|
+
removed++;
|
|
38160
|
+
} catch (e) {
|
|
38161
|
+
row.error = e.message;
|
|
38162
|
+
await recordCleanupFailure(row.sessionId, row.projectId, entry, row.error);
|
|
38163
|
+
}
|
|
38164
|
+
}
|
|
38165
|
+
if (removed) await deps.prune(repoDir);
|
|
38166
|
+
return removed;
|
|
38167
|
+
}
|
|
38168
|
+
async function sweepAll(deps = prodReaperDeps) {
|
|
38169
|
+
let removed = 0;
|
|
38170
|
+
for (const { projectId, repoDir } of await deps.repos()) {
|
|
38171
|
+
removed += await sweepRepo(repoDir, projectId, deps).catch(() => 0);
|
|
38172
|
+
}
|
|
38173
|
+
return removed;
|
|
38174
|
+
}
|
|
38175
|
+
var inFlight = /* @__PURE__ */ new Set();
|
|
38176
|
+
function reapSoon(repoDir, projectId, deps = prodReaperDeps) {
|
|
38177
|
+
if (inFlight.has(repoDir)) return;
|
|
38178
|
+
inFlight.add(repoDir);
|
|
38179
|
+
void sweepRepo(repoDir, projectId, deps).catch((e) => console.error("sapuan worktree:", e)).finally(() => inFlight.delete(repoDir));
|
|
38180
|
+
}
|
|
38181
|
+
var timer;
|
|
38182
|
+
function startWorktreeReaper(deps = prodReaperDeps) {
|
|
38183
|
+
if (timer) return;
|
|
38184
|
+
void sweepAll(deps).catch((e) => console.error("sapuan worktree saat boot:", e));
|
|
38185
|
+
timer = setInterval(() => {
|
|
38186
|
+
void sweepAll(deps).catch(() => {
|
|
38187
|
+
});
|
|
38188
|
+
}, TICK_MS);
|
|
38189
|
+
timer.unref();
|
|
38190
|
+
}
|
|
38191
|
+
|
|
38192
|
+
// src/services/session-close.ts
|
|
38193
|
+
async function advanceStage(specId, repoDir, sessionId2, flow, worktree) {
|
|
38194
|
+
const next = stageForRun(readPhases(phaseFilePath(repoDir, sessionId2), flow), worktree, specId);
|
|
38195
|
+
if (!next) return;
|
|
38196
|
+
const spec = await prisma.spec.findUnique({ where: { id: specId }, select: { stage: true, title: true, projectId: true } });
|
|
38197
|
+
if (!spec || STAGES.indexOf(next) <= STAGES.indexOf(spec.stage)) return;
|
|
38198
|
+
const { count } = await prisma.spec.updateMany({ where: { id: specId, stage: spec.stage }, data: { stage: next } });
|
|
38199
|
+
if (count === 0) return;
|
|
38200
|
+
let commitSha = null;
|
|
38201
|
+
try {
|
|
38202
|
+
commitSha = realGit.headSha(worktree);
|
|
38203
|
+
} catch {
|
|
38204
|
+
}
|
|
38205
|
+
await recordSessionResult({
|
|
38206
|
+
projectId: spec.projectId,
|
|
38207
|
+
specId,
|
|
38208
|
+
oldStage: spec.stage,
|
|
38209
|
+
newStage: next,
|
|
38210
|
+
commitSha,
|
|
38211
|
+
branch: `hanoman/${sessionId2}`,
|
|
38212
|
+
status: next === "done" ? "done" : "progress"
|
|
38213
|
+
}).catch(() => {
|
|
38214
|
+
});
|
|
38215
|
+
if (next === "done") await recordCompletion(specId, spec.title, spec.projectId);
|
|
38216
|
+
}
|
|
38217
|
+
async function closeSession(id) {
|
|
38218
|
+
const s2 = getSession(id);
|
|
38219
|
+
if (!s2) return null;
|
|
38220
|
+
if (s2.flow || s2.cwd.includes("/.worktrees/")) {
|
|
38221
|
+
const repoDir = await resolveRepoDir(s2.projectId);
|
|
38222
|
+
if (repoDir) {
|
|
38223
|
+
if (s2.specId) {
|
|
38224
|
+
if (s2.flow) await advanceStage(s2.specId, repoDir, id, s2.flow, s2.cwd);
|
|
38225
|
+
await recordHeadSha(s2.specId, s2.cwd);
|
|
38226
|
+
}
|
|
38227
|
+
killSession(id);
|
|
38228
|
+
const cleanup = ownsWorktree(repoDir, s2.cwd) ? releaseWorktree(repoDir, s2.cwd, s2.projectId) : null;
|
|
38229
|
+
return { cleanup };
|
|
38230
|
+
}
|
|
38231
|
+
}
|
|
38232
|
+
killSession(id);
|
|
38233
|
+
return { cleanup: null };
|
|
38234
|
+
}
|
|
38235
|
+
|
|
38236
|
+
// src/routes/ide.ts
|
|
38237
|
+
init_session_id();
|
|
38238
|
+
|
|
37783
38239
|
// src/services/repo-fs.ts
|
|
37784
|
-
import { lstat as lstat2, rename as rename3, rm as
|
|
38240
|
+
import { lstat as lstat2, rename as rename3, rm as rm5 } from "node:fs/promises";
|
|
37785
38241
|
import { createWriteStream } from "node:fs";
|
|
37786
|
-
import { join as
|
|
38242
|
+
import { join as join13 } from "node:path";
|
|
37787
38243
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
37788
38244
|
import { pipeline } from "node:stream/promises";
|
|
37789
38245
|
var EntryExistsError = class extends Error {
|
|
@@ -37833,7 +38289,7 @@ async function deleteEntry(repoDir, rel) {
|
|
|
37833
38289
|
const kind = await entryKind(repoDir, rel);
|
|
37834
38290
|
if (!kind) throw new EntryMissingError(`tidak ada: ${rel}`);
|
|
37835
38291
|
const { path: path2 } = await resolveRepoEntry(repoDir, rel);
|
|
37836
|
-
await
|
|
38292
|
+
await rm5(path2, { recursive: kind === "dir", force: false });
|
|
37837
38293
|
return { path: rel, kind };
|
|
37838
38294
|
}
|
|
37839
38295
|
async function saveUpload(repoDir, rel, source, opts = {}) {
|
|
@@ -37844,17 +38300,17 @@ async function saveUpload(repoDir, rel, source, opts = {}) {
|
|
|
37844
38300
|
if (current && !opts.overwrite) return { status: "exists" };
|
|
37845
38301
|
if (current && !current.isFile())
|
|
37846
38302
|
throw new PathContainmentError("repository path ditolak: target bukan file regular");
|
|
37847
|
-
const temp =
|
|
38303
|
+
const temp = join13(entry.parent, `.hanoman-${randomUUID6()}.tmp`);
|
|
37848
38304
|
try {
|
|
37849
38305
|
await pipeline(source, createWriteStream(temp, { flags: "wx", mode: 384 }));
|
|
37850
38306
|
if (opts.isTruncated?.()) {
|
|
37851
|
-
await
|
|
38307
|
+
await rm5(temp, { force: true });
|
|
37852
38308
|
return { status: "too-large" };
|
|
37853
38309
|
}
|
|
37854
38310
|
await rename3(temp, entry.path);
|
|
37855
38311
|
return { status: "written" };
|
|
37856
38312
|
} catch (error) {
|
|
37857
|
-
await
|
|
38313
|
+
await rm5(temp, { force: true });
|
|
37858
38314
|
throw error;
|
|
37859
38315
|
}
|
|
37860
38316
|
}
|
|
@@ -37877,6 +38333,14 @@ async function lockInputs(id) {
|
|
|
37877
38333
|
sessionBranches: new Set(sessions)
|
|
37878
38334
|
};
|
|
37879
38335
|
}
|
|
38336
|
+
async function worktreeInputs(id) {
|
|
38337
|
+
const specs = await prisma.spec.findMany({ where: { projectId: id }, select: { id: true, stage: true } });
|
|
38338
|
+
return {
|
|
38339
|
+
specs: new Map(specs.map((s2) => [sessionIdForSpec(s2.id), { id: s2.id, stage: s2.stage }])),
|
|
38340
|
+
sessions: new Map(listSessions().filter((s2) => s2.projectId === id && !s2.exited).map((s2) => [resolve16(s2.cwd), { id: s2.id, specId: s2.specId ?? null }]))
|
|
38341
|
+
};
|
|
38342
|
+
}
|
|
38343
|
+
var execAsync = promisify10(execFile10);
|
|
37880
38344
|
function entryError(reply, e) {
|
|
37881
38345
|
if (e instanceof EntryMissingError) return reply.code(404).send({ error: "not found" });
|
|
37882
38346
|
if (e instanceof EntryExistsError) return reply.code(409).send({ error: "sudah ada" });
|
|
@@ -38129,7 +38593,7 @@ async function ide_default(app2) {
|
|
|
38129
38593
|
const fmt = q.format === "tar" ? "tar" : "zip";
|
|
38130
38594
|
const child = spawn3("git", ["archive", `--format=${fmt}`, "--end-of-options", ref], { cwd: repoDir });
|
|
38131
38595
|
reply.header("content-type", fmt === "zip" ? "application/zip" : "application/x-tar");
|
|
38132
|
-
reply.header("content-disposition", `attachment; filename="${
|
|
38596
|
+
reply.header("content-disposition", `attachment; filename="${basename5(repoDir)}-${ref.replace(/[^\w.-]/g, "_")}.${fmt}"`);
|
|
38133
38597
|
return reply.send(child.stdout);
|
|
38134
38598
|
});
|
|
38135
38599
|
app2.get("/projects/:id/commit/:sha", async (req, reply) => {
|
|
@@ -38248,8 +38712,12 @@ async function ide_default(app2) {
|
|
|
38248
38712
|
const { id } = req.params;
|
|
38249
38713
|
const repoDir = await repoOf(id);
|
|
38250
38714
|
if (repoDir === void 0) return reply.code(404).send({ error: "not found" });
|
|
38251
|
-
const { base: base2 } = req.query;
|
|
38252
|
-
return listUnusedBranches(repoDir, {
|
|
38715
|
+
const { base: base2, include } = req.query;
|
|
38716
|
+
return listUnusedBranches(repoDir, {
|
|
38717
|
+
base: base2,
|
|
38718
|
+
include: include === "all" ? "all" : "merged",
|
|
38719
|
+
...await lockInputs(id)
|
|
38720
|
+
});
|
|
38253
38721
|
});
|
|
38254
38722
|
app2.post("/projects/:id/branches/delete", async (req, reply) => {
|
|
38255
38723
|
const { id } = req.params;
|
|
@@ -38261,12 +38729,60 @@ async function ide_default(app2) {
|
|
|
38261
38729
|
return reply.code(400).send({ error: "names wajib berisi nama branch" });
|
|
38262
38730
|
if (b.scope !== void 0 && b.scope !== "local" && b.scope !== "remote" && b.scope !== "both")
|
|
38263
38731
|
return reply.code(400).send({ error: "scope harus local, remote, atau both" });
|
|
38732
|
+
if (b.allowUnmerged !== void 0 && typeof b.allowUnmerged !== "boolean")
|
|
38733
|
+
return reply.code(400).send({ error: "allowUnmerged harus boolean" });
|
|
38264
38734
|
return deleteBranches(repoDir, b.names, {
|
|
38265
38735
|
scope: b.scope ?? "both",
|
|
38266
38736
|
base: typeof b.base === "string" && b.base ? b.base : void 0,
|
|
38737
|
+
allowUnmerged: b.allowUnmerged === true,
|
|
38267
38738
|
...await lockInputs(id)
|
|
38268
38739
|
});
|
|
38269
38740
|
});
|
|
38741
|
+
app2.get("/projects/:id/worktrees", async (req, reply) => {
|
|
38742
|
+
const { id } = req.params;
|
|
38743
|
+
const repoDir = await repoOf(id);
|
|
38744
|
+
if (repoDir === void 0) return reply.code(404).send({ error: "not found" });
|
|
38745
|
+
return listWorktrees(repoDir, await worktreeInputs(id));
|
|
38746
|
+
});
|
|
38747
|
+
app2.get("/projects/:id/worktrees/stats", async (req, reply) => {
|
|
38748
|
+
const { id } = req.params;
|
|
38749
|
+
const repoDir = await repoOf(id);
|
|
38750
|
+
if (repoDir === void 0) return reply.code(404).send({ error: "not found" });
|
|
38751
|
+
if (!repoDir) return reply.code(400).send({ error: "project tidak punya repoDir" });
|
|
38752
|
+
const { name: name2 } = req.query;
|
|
38753
|
+
const report = await listWorktrees(repoDir, await worktreeInputs(id));
|
|
38754
|
+
const w = report.worktrees.find((x) => x.name === name2);
|
|
38755
|
+
if (!w) return reply.code(404).send({ error: "not found" });
|
|
38756
|
+
return worktreeStats(report.repoDir, w);
|
|
38757
|
+
});
|
|
38758
|
+
app2.post("/projects/:id/worktrees/delete", async (req, reply) => {
|
|
38759
|
+
const { id } = req.params;
|
|
38760
|
+
const repoDir = await repoOf(id);
|
|
38761
|
+
if (repoDir === void 0) return reply.code(404).send({ error: "not found" });
|
|
38762
|
+
if (!repoDir) return reply.code(400).send({ error: "project tidak punya repoDir" });
|
|
38763
|
+
const b = req.body;
|
|
38764
|
+
if (!Array.isArray(b?.names) || b.names.some((n2) => typeof n2 !== "string" || !n2))
|
|
38765
|
+
return reply.code(400).send({ error: "names wajib berisi nama worktree" });
|
|
38766
|
+
const locks = await lockInputs(id);
|
|
38767
|
+
return deleteWorktrees(repoDir, b.names, {
|
|
38768
|
+
withBranch: b.deleteBranch === true,
|
|
38769
|
+
...await worktreeInputs(id),
|
|
38770
|
+
closeSession,
|
|
38771
|
+
release: (repo, path2) => releaseWorktree(repo, path2, id),
|
|
38772
|
+
prune: async (repo) => {
|
|
38773
|
+
try {
|
|
38774
|
+
await execAsync("git", ["worktree", "prune"], { cwd: repo, timeout: 3e4 });
|
|
38775
|
+
} catch {
|
|
38776
|
+
}
|
|
38777
|
+
},
|
|
38778
|
+
// Pagar kunci ADR-0077 ikut apa adanya — satu-satunya jalur hapus branch di codebase.
|
|
38779
|
+
deleteBranch: async (repo, name2) => {
|
|
38780
|
+
const r = await deleteBranches(repo, [name2], { scope: "both", ...locks });
|
|
38781
|
+
const first = r.results[0];
|
|
38782
|
+
return { ok: !!first?.ok, ...first?.error ? { error: first.error } : {} };
|
|
38783
|
+
}
|
|
38784
|
+
});
|
|
38785
|
+
});
|
|
38270
38786
|
}
|
|
38271
38787
|
async function finishGraphOp(reply, id, repoDir, r, verb) {
|
|
38272
38788
|
if (r.status === "error") return reply.code(r.code).send({ error: r.error });
|
|
@@ -38281,21 +38797,21 @@ async function finishGraphOp(reply, id, repoDir, r, verb) {
|
|
|
38281
38797
|
CODE_STYLE_CLAUSE,
|
|
38282
38798
|
`${verb} via git graph project ${id}.`
|
|
38283
38799
|
].join("\n\n");
|
|
38284
|
-
const s2 = createSession(id, r.worktree, { id:
|
|
38800
|
+
const s2 = createSession(id, r.worktree, { id: basename5(r.worktree), model, effort, agent, prompt });
|
|
38285
38801
|
return { status: "conflict", sessionId: s2.id };
|
|
38286
38802
|
}
|
|
38287
38803
|
|
|
38288
38804
|
// src/routes/fs.ts
|
|
38289
|
-
import { readdir as
|
|
38805
|
+
import { readdir as readdir3 } from "node:fs/promises";
|
|
38290
38806
|
import { homedir as homedir4 } from "node:os";
|
|
38291
|
-
import { resolve as
|
|
38807
|
+
import { resolve as resolve17, dirname as dirname9, join as join14 } from "node:path";
|
|
38292
38808
|
async function fs_default(app2) {
|
|
38293
38809
|
app2.get("/fs/browse", async (req, reply) => {
|
|
38294
38810
|
const q = req.query.path;
|
|
38295
|
-
const dir2 = q && q.trim() ?
|
|
38811
|
+
const dir2 = q && q.trim() ? resolve17(q.trim()) : homedir4();
|
|
38296
38812
|
try {
|
|
38297
|
-
const ents = await
|
|
38298
|
-
const entries = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path:
|
|
38813
|
+
const ents = await readdir3(dir2, { withFileTypes: true });
|
|
38814
|
+
const entries = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join14(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
|
|
38299
38815
|
const parent = dirname9(dir2);
|
|
38300
38816
|
return { path: dir2, parent: parent === dir2 ? null : parent, entries };
|
|
38301
38817
|
} catch {
|
|
@@ -38498,8 +39014,8 @@ function setBounded(map, key, value, maxKeys) {
|
|
|
38498
39014
|
// src/services/auth.ts
|
|
38499
39015
|
init_db();
|
|
38500
39016
|
import { randomBytes as randomBytes3, scrypt as scryptCb, timingSafeEqual as timingSafeEqual2, createHash as createHash6 } from "node:crypto";
|
|
38501
|
-
import { promisify as
|
|
38502
|
-
var scrypt =
|
|
39017
|
+
import { promisify as promisify11 } from "node:util";
|
|
39018
|
+
var scrypt = promisify11(scryptCb);
|
|
38503
39019
|
var COOKIE_NAME = "hn_session";
|
|
38504
39020
|
var SESSION_TTL_MS = 7 * 24 * 3600 * 1e3;
|
|
38505
39021
|
async function hashPassword(pw) {
|
|
@@ -38734,121 +39250,6 @@ function installCommand(m, agent, shell = shellBin()) {
|
|
|
38734
39250
|
return [shell, "-lc", `${m.install[agent].join(" && ")}; exec '${shell}' -l`];
|
|
38735
39251
|
}
|
|
38736
39252
|
|
|
38737
|
-
// src/services/session-worktree.ts
|
|
38738
|
-
import { resolve as resolve14, sep as sep3 } from "node:path";
|
|
38739
|
-
function ownsWorktree(repoDir, cwd) {
|
|
38740
|
-
const base2 = resolve14(repoDir);
|
|
38741
|
-
const dir2 = resolve14(cwd);
|
|
38742
|
-
const container = resolve14(base2, ".worktrees");
|
|
38743
|
-
return dir2 !== base2 && dir2 !== container && dir2.startsWith(container + sep3);
|
|
38744
|
-
}
|
|
38745
|
-
|
|
38746
|
-
// src/services/worktree-reaper.ts
|
|
38747
|
-
init_src2();
|
|
38748
|
-
init_db();
|
|
38749
|
-
import { execFile as execFile8 } from "node:child_process";
|
|
38750
|
-
import { readdir as readdir3, rm as rm5 } from "node:fs/promises";
|
|
38751
|
-
import { promisify as promisify9 } from "node:util";
|
|
38752
|
-
import { join as join13, resolve as resolve15 } from "node:path";
|
|
38753
|
-
init_notifications();
|
|
38754
|
-
var TICK_MS = 6e4;
|
|
38755
|
-
var trashDirOf = (repoDir) => resolve15(repoDir, ".worktrees", ".trash");
|
|
38756
|
-
var exec8 = promisify9(execFile8);
|
|
38757
|
-
var prodReaperDeps = {
|
|
38758
|
-
rm: (path2) => rm5(path2, { recursive: true, force: true }),
|
|
38759
|
-
prune: async (repoDir) => {
|
|
38760
|
-
try {
|
|
38761
|
-
await exec8("git", ["worktree", "prune"], { cwd: repoDir, timeout: 3e4 });
|
|
38762
|
-
} catch {
|
|
38763
|
-
}
|
|
38764
|
-
},
|
|
38765
|
-
repos: async () => {
|
|
38766
|
-
const projects = await prisma.project.findMany({ select: { id: true } });
|
|
38767
|
-
const seen = /* @__PURE__ */ new Set();
|
|
38768
|
-
const out3 = [];
|
|
38769
|
-
for (const p of projects) {
|
|
38770
|
-
const repoDir = await resolveRepoDir(p.id);
|
|
38771
|
-
if (!repoDir || seen.has(repoDir)) continue;
|
|
38772
|
-
seen.add(repoDir);
|
|
38773
|
-
out3.push({ projectId: p.id, repoDir });
|
|
38774
|
-
}
|
|
38775
|
-
return out3;
|
|
38776
|
-
}
|
|
38777
|
-
};
|
|
38778
|
-
var pending = /* @__PURE__ */ new Map();
|
|
38779
|
-
var sessionIdOf = (entry) => entry.split(".")[0] ?? entry;
|
|
38780
|
-
function releaseWorktree(repoDir, cwd, projectId, deps = prodReaperDeps) {
|
|
38781
|
-
let path2;
|
|
38782
|
-
try {
|
|
38783
|
-
path2 = realGit.trashWorktree(repoDir, cwd);
|
|
38784
|
-
} catch {
|
|
38785
|
-
realGit.removeWorktree(repoDir, cwd);
|
|
38786
|
-
return null;
|
|
38787
|
-
}
|
|
38788
|
-
if (!path2) return null;
|
|
38789
|
-
const entry = path2.slice(trashDirOf(repoDir).length + 1);
|
|
38790
|
-
pending.set(path2, { path: path2, repoDir, projectId, entry, sessionId: sessionIdOf(entry), since: Date.now() });
|
|
38791
|
-
reapSoon(repoDir, projectId, deps);
|
|
38792
|
-
return entry;
|
|
38793
|
-
}
|
|
38794
|
-
var listCleanups = () => [...pending.values()].sort((a, b) => a.since - b.since).map((p) => ({
|
|
38795
|
-
sessionId: p.sessionId,
|
|
38796
|
-
projectId: p.projectId,
|
|
38797
|
-
entry: p.entry,
|
|
38798
|
-
since: new Date(p.since).toISOString(),
|
|
38799
|
-
state: p.error ? "failed" : "closing",
|
|
38800
|
-
...p.error ? { error: p.error } : {}
|
|
38801
|
-
}));
|
|
38802
|
-
async function sweepRepo(repoDir, projectId, deps = prodReaperDeps) {
|
|
38803
|
-
const dir2 = trashDirOf(repoDir);
|
|
38804
|
-
let entries;
|
|
38805
|
-
try {
|
|
38806
|
-
entries = await readdir3(dir2);
|
|
38807
|
-
} catch {
|
|
38808
|
-
return 0;
|
|
38809
|
-
}
|
|
38810
|
-
let removed = 0;
|
|
38811
|
-
for (const entry of entries) {
|
|
38812
|
-
const path2 = join13(dir2, entry);
|
|
38813
|
-
const known = pending.get(path2);
|
|
38814
|
-
const row = known ?? { path: path2, repoDir, projectId, entry, sessionId: sessionIdOf(entry), since: Date.now() };
|
|
38815
|
-
pending.set(path2, row);
|
|
38816
|
-
try {
|
|
38817
|
-
await deps.rm(path2);
|
|
38818
|
-
pending.delete(path2);
|
|
38819
|
-
removed++;
|
|
38820
|
-
} catch (e) {
|
|
38821
|
-
row.error = e.message;
|
|
38822
|
-
await recordCleanupFailure(row.sessionId, row.projectId, entry, row.error);
|
|
38823
|
-
}
|
|
38824
|
-
}
|
|
38825
|
-
if (removed) await deps.prune(repoDir);
|
|
38826
|
-
return removed;
|
|
38827
|
-
}
|
|
38828
|
-
async function sweepAll(deps = prodReaperDeps) {
|
|
38829
|
-
let removed = 0;
|
|
38830
|
-
for (const { projectId, repoDir } of await deps.repos()) {
|
|
38831
|
-
removed += await sweepRepo(repoDir, projectId, deps).catch(() => 0);
|
|
38832
|
-
}
|
|
38833
|
-
return removed;
|
|
38834
|
-
}
|
|
38835
|
-
var inFlight = /* @__PURE__ */ new Set();
|
|
38836
|
-
function reapSoon(repoDir, projectId, deps = prodReaperDeps) {
|
|
38837
|
-
if (inFlight.has(repoDir)) return;
|
|
38838
|
-
inFlight.add(repoDir);
|
|
38839
|
-
void sweepRepo(repoDir, projectId, deps).catch((e) => console.error("sapuan worktree:", e)).finally(() => inFlight.delete(repoDir));
|
|
38840
|
-
}
|
|
38841
|
-
var timer;
|
|
38842
|
-
function startWorktreeReaper(deps = prodReaperDeps) {
|
|
38843
|
-
if (timer) return;
|
|
38844
|
-
void sweepAll(deps).catch((e) => console.error("sapuan worktree saat boot:", e));
|
|
38845
|
-
timer = setInterval(() => {
|
|
38846
|
-
void sweepAll(deps).catch(() => {
|
|
38847
|
-
});
|
|
38848
|
-
}, TICK_MS);
|
|
38849
|
-
timer.unref();
|
|
38850
|
-
}
|
|
38851
|
-
|
|
38852
39253
|
// src/routes/terminal.ts
|
|
38853
39254
|
init_notifications();
|
|
38854
39255
|
init_stage_machine();
|
|
@@ -38856,30 +39257,6 @@ init_pty();
|
|
|
38856
39257
|
init_uploads();
|
|
38857
39258
|
var ATTACHMENT_MIME = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
|
|
38858
39259
|
var TERMINAL_WS_MESSAGES_PER_MINUTE = 6e3;
|
|
38859
|
-
async function advanceStage(specId, repoDir, sessionId2, flow, worktree) {
|
|
38860
|
-
const next = stageForRun(readPhases(phaseFilePath(repoDir, sessionId2), flow), worktree, specId);
|
|
38861
|
-
if (!next) return;
|
|
38862
|
-
const spec = await prisma.spec.findUnique({ where: { id: specId }, select: { stage: true, title: true, projectId: true } });
|
|
38863
|
-
if (!spec || STAGES.indexOf(next) <= STAGES.indexOf(spec.stage)) return;
|
|
38864
|
-
const { count } = await prisma.spec.updateMany({ where: { id: specId, stage: spec.stage }, data: { stage: next } });
|
|
38865
|
-
if (count === 0) return;
|
|
38866
|
-
let commitSha = null;
|
|
38867
|
-
try {
|
|
38868
|
-
commitSha = realGit.headSha(worktree);
|
|
38869
|
-
} catch {
|
|
38870
|
-
}
|
|
38871
|
-
await recordSessionResult({
|
|
38872
|
-
projectId: spec.projectId,
|
|
38873
|
-
specId,
|
|
38874
|
-
oldStage: spec.stage,
|
|
38875
|
-
newStage: next,
|
|
38876
|
-
commitSha,
|
|
38877
|
-
branch: `hanoman/${sessionId2}`,
|
|
38878
|
-
status: next === "done" ? "done" : "progress"
|
|
38879
|
-
}).catch(() => {
|
|
38880
|
-
});
|
|
38881
|
-
if (next === "done") await recordCompletion(specId, spec.title, spec.projectId);
|
|
38882
|
-
}
|
|
38883
39260
|
var ensureWorktree = (repoDir, wt, branchFrom) => {
|
|
38884
39261
|
if (realGit.worktreeAlive(wt)) return true;
|
|
38885
39262
|
realGit.addWorktree(repoDir, wt, branchFrom);
|
|
@@ -39161,22 +39538,9 @@ async function terminal_default(app2, opts) {
|
|
|
39161
39538
|
app2.get("/terminal/cleanups", async () => ({ items: listCleanups() }));
|
|
39162
39539
|
app2.delete("/terminal/sessions/:id", async (req, reply) => {
|
|
39163
39540
|
const { id } = req.params;
|
|
39164
|
-
const
|
|
39165
|
-
if (!
|
|
39166
|
-
|
|
39167
|
-
const repoDir = await resolveRepoDir(s2.projectId);
|
|
39168
|
-
if (repoDir) {
|
|
39169
|
-
if (s2.specId) {
|
|
39170
|
-
if (s2.flow) await advanceStage(s2.specId, repoDir, id, s2.flow, s2.cwd);
|
|
39171
|
-
await recordHeadSha(s2.specId, s2.cwd);
|
|
39172
|
-
}
|
|
39173
|
-
killSession(id);
|
|
39174
|
-
const cleanup = ownsWorktree(repoDir, s2.cwd) ? releaseWorktree(repoDir, s2.cwd, s2.projectId) : null;
|
|
39175
|
-
return reply.code(202).send({ cleanup });
|
|
39176
|
-
}
|
|
39177
|
-
}
|
|
39178
|
-
killSession(id);
|
|
39179
|
-
return reply.code(202).send({ cleanup: null });
|
|
39541
|
+
const r = await closeSession(id);
|
|
39542
|
+
if (!r) return reply.code(404).send({ error: "not found" });
|
|
39543
|
+
return reply.code(202).send(r);
|
|
39180
39544
|
});
|
|
39181
39545
|
app2.post("/terminal/sessions/:id/attachments", async (req, reply) => {
|
|
39182
39546
|
const { id } = req.params;
|
|
@@ -39231,7 +39595,7 @@ async function terminal_default(app2, opts) {
|
|
|
39231
39595
|
} catch {
|
|
39232
39596
|
return;
|
|
39233
39597
|
}
|
|
39234
|
-
if (m.t === "in" && typeof m.d === "string") writeTo(id, m.d);
|
|
39598
|
+
if (m.t === "in" && typeof m.d === "string") writeTo(id, m.d, client);
|
|
39235
39599
|
else if (m.t === "resize" && m.cols && m.rows) resize(id, m.cols, m.rows);
|
|
39236
39600
|
});
|
|
39237
39601
|
const revalidate = setInterval(() => {
|
|
@@ -39428,7 +39792,7 @@ init_config2();
|
|
|
39428
39792
|
import { spawn as spawn4 } from "node:child_process";
|
|
39429
39793
|
import { mkdtempSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
39430
39794
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
39431
|
-
import { dirname as dirname10, join as
|
|
39795
|
+
import { dirname as dirname10, join as join15 } from "node:path";
|
|
39432
39796
|
var sshBin = () => effectiveStr("HANOMAN_SSH_BIN") ?? "ssh";
|
|
39433
39797
|
function consoleArgv(t) {
|
|
39434
39798
|
return [
|
|
@@ -39443,8 +39807,8 @@ function consoleArgv(t) {
|
|
|
39443
39807
|
];
|
|
39444
39808
|
}
|
|
39445
39809
|
function askpassScript() {
|
|
39446
|
-
const dir2 = mkdtempSync(
|
|
39447
|
-
const path2 =
|
|
39810
|
+
const dir2 = mkdtempSync(join15(tmpdir3(), "hanoman-askpass-"));
|
|
39811
|
+
const path2 = join15(dir2, "askpass.sh");
|
|
39448
39812
|
writeFileSync4(path2, `#!/bin/sh
|
|
39449
39813
|
printf '%s' "$HANOMAN_SSH_PASSWORD"
|
|
39450
39814
|
`, { mode: 448 });
|
|
@@ -39478,24 +39842,24 @@ function sshExec(t, remoteCmd, opts = {}) {
|
|
|
39478
39842
|
SSH_ASKPASS_REQUIRE: "force",
|
|
39479
39843
|
HANOMAN_SSH_PASSWORD: opts.password
|
|
39480
39844
|
} : process.env;
|
|
39481
|
-
return new Promise((
|
|
39845
|
+
return new Promise((resolve21) => {
|
|
39482
39846
|
const p = spawn4(sshBin(), args, { stdio: ["pipe", "pipe", "pipe"], env });
|
|
39483
|
-
let
|
|
39847
|
+
let out4 = "";
|
|
39484
39848
|
const timer9 = setTimeout(() => p.kill("SIGKILL"), opts.timeoutMs ?? 6e4);
|
|
39485
39849
|
const done = (r) => {
|
|
39486
39850
|
clearTimeout(timer9);
|
|
39487
39851
|
if (askpass) rmSync4(dirname10(askpass), { recursive: true, force: true });
|
|
39488
|
-
|
|
39852
|
+
resolve21(r);
|
|
39489
39853
|
};
|
|
39490
39854
|
p.stdout.on("data", (d) => {
|
|
39491
|
-
|
|
39855
|
+
out4 += d;
|
|
39492
39856
|
});
|
|
39493
39857
|
p.stderr.on("data", (d) => {
|
|
39494
|
-
|
|
39858
|
+
out4 += d;
|
|
39495
39859
|
});
|
|
39496
39860
|
p.stdin.on("error", () => {
|
|
39497
39861
|
});
|
|
39498
|
-
p.on("close", (code) => done({ code: code ?? 1, out:
|
|
39862
|
+
p.on("close", (code) => done({ code: code ?? 1, out: out4 }));
|
|
39499
39863
|
p.on("error", (e) => done({ code: 127, out: String(e) }));
|
|
39500
39864
|
if (opts.stdin) p.stdin.write(opts.stdin);
|
|
39501
39865
|
p.stdin.end();
|
|
@@ -39505,15 +39869,15 @@ function sshExec(t, remoteCmd, opts = {}) {
|
|
|
39505
39869
|
// src/services/vps-audit.ts
|
|
39506
39870
|
init_db();
|
|
39507
39871
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
39508
|
-
import { join as
|
|
39872
|
+
import { join as join17 } from "node:path";
|
|
39509
39873
|
|
|
39510
39874
|
// src/runner/deps.ts
|
|
39511
39875
|
import { existsSync as existsSync11 } from "node:fs";
|
|
39512
|
-
import { dirname as dirname11, join as
|
|
39876
|
+
import { dirname as dirname11, join as join16 } from "node:path";
|
|
39513
39877
|
function repoRootFrom(startDir) {
|
|
39514
39878
|
let dir2 = startDir;
|
|
39515
39879
|
for (let i = 0; i < 8; i++) {
|
|
39516
|
-
if (existsSync11(
|
|
39880
|
+
if (existsSync11(join16(dir2, "pnpm-workspace.yaml"))) return dir2;
|
|
39517
39881
|
const parent = dirname11(dir2);
|
|
39518
39882
|
if (parent === dir2) break;
|
|
39519
39883
|
dir2 = parent;
|
|
@@ -39564,14 +39928,14 @@ function scoreCompliance(probeStatus, states, items = CATALOG) {
|
|
|
39564
39928
|
// src/vps/drift.ts
|
|
39565
39929
|
var REGRESS = /* @__PURE__ */ new Set(["fail", "warn"]);
|
|
39566
39930
|
function computeDrift(prev, curr) {
|
|
39567
|
-
const
|
|
39931
|
+
const out4 = [];
|
|
39568
39932
|
for (const [id, c] of Object.entries(curr)) {
|
|
39569
39933
|
const p = prev[id];
|
|
39570
39934
|
if (p && p.status === "pass" && REGRESS.has(c.status)) {
|
|
39571
|
-
|
|
39935
|
+
out4.push({ itemId: id, from: p.status, to: c.status });
|
|
39572
39936
|
}
|
|
39573
39937
|
}
|
|
39574
|
-
return
|
|
39938
|
+
return out4;
|
|
39575
39939
|
}
|
|
39576
39940
|
|
|
39577
39941
|
// src/services/vps-audit.ts
|
|
@@ -39585,33 +39949,33 @@ var CRITICAL = [
|
|
|
39585
39949
|
"fail2ban",
|
|
39586
39950
|
"auto_updates"
|
|
39587
39951
|
];
|
|
39588
|
-
function parseAudit(
|
|
39589
|
-
return
|
|
39952
|
+
function parseAudit(out4) {
|
|
39953
|
+
return out4.split("\n").flatMap((line) => {
|
|
39590
39954
|
const m = line.match(/^CHECK (\S+) (pass|fail|warn|na)(?: (.*))?$/);
|
|
39591
39955
|
return m ? [{ check: m[1], status: m[2], detail: (m[3] ?? "").trim() }] : [];
|
|
39592
39956
|
});
|
|
39593
39957
|
}
|
|
39594
|
-
function parseStack(
|
|
39958
|
+
function parseStack(out4) {
|
|
39595
39959
|
const d = {};
|
|
39596
|
-
for (const line of
|
|
39960
|
+
for (const line of out4.split("\n")) {
|
|
39597
39961
|
const m = line.match(/^STACK (\S+) (present|absent)(?: (.*))?$/);
|
|
39598
39962
|
if (m) d[m[1]] = { present: m[2] === "present", detail: (m[3] ?? "").trim() };
|
|
39599
39963
|
}
|
|
39600
39964
|
return d;
|
|
39601
39965
|
}
|
|
39602
39966
|
function mapToCatalog(checks) {
|
|
39603
|
-
const
|
|
39967
|
+
const out4 = {};
|
|
39604
39968
|
for (const c of checks) {
|
|
39605
39969
|
if (!byId(c.check)) continue;
|
|
39606
|
-
|
|
39970
|
+
out4[c.check] = c.status === "na" ? "unknown" : c.status;
|
|
39607
39971
|
}
|
|
39608
|
-
return
|
|
39972
|
+
return out4;
|
|
39609
39973
|
}
|
|
39610
39974
|
var isHardened = (checks) => CRITICAL.every((c) => checks.find((x) => x.check === c)?.status === "pass");
|
|
39611
39975
|
var HEALTH_CMD = `echo HEALTH uptime $(uptime -p 2>/dev/null || uptime); echo HEALTH disk $(df -P / | awk 'NR==2{print $5}'); echo HEALTH mem $(free -m 2>/dev/null | awk 'NR==2{printf "%d/%dMB", $3, $2}'); echo HEALTH load $(cut -d" " -f1-3 /proc/loadavg)`;
|
|
39612
|
-
function parseHealth(
|
|
39976
|
+
function parseHealth(out4) {
|
|
39613
39977
|
const h = {};
|
|
39614
|
-
for (const line of
|
|
39978
|
+
for (const line of out4.split("\n")) {
|
|
39615
39979
|
const m = line.match(/^HEALTH (\S+) ?(.*)$/);
|
|
39616
39980
|
if (m) h[m[1]] = m[2].trim();
|
|
39617
39981
|
}
|
|
@@ -39619,7 +39983,7 @@ function parseHealth(out3) {
|
|
|
39619
39983
|
return { uptime: h.uptime ?? "", disk: h.disk ?? "", mem: h.mem ?? "", load: h.load ?? "" };
|
|
39620
39984
|
}
|
|
39621
39985
|
var target = (v) => ({ host: v.host, port: v.port, user: v.user, keyPath: v.keyPath });
|
|
39622
|
-
var scriptPath = (f) =>
|
|
39986
|
+
var scriptPath = (f) => join17(repoRoot(), "server", "scripts", "vps", f);
|
|
39623
39987
|
async function itemStatesOf(vpsId) {
|
|
39624
39988
|
const rows = await prisma.vpsItemState.findMany({ where: { vpsId } });
|
|
39625
39989
|
return Object.fromEntries(rows.map((s2) => [s2.itemId, { na: s2.na, attested: s2.attested }]));
|
|
@@ -39677,8 +40041,8 @@ async function runHealth(v) {
|
|
|
39677
40041
|
}
|
|
39678
40042
|
|
|
39679
40043
|
// src/services/vps-remediate.ts
|
|
39680
|
-
function parseSteps(
|
|
39681
|
-
return
|
|
40044
|
+
function parseSteps(out4) {
|
|
40045
|
+
return out4.split("\n").flatMap((line) => {
|
|
39682
40046
|
const m = line.match(/^STEP (\S+) (would|ok|fail)(?: (.*))?$/);
|
|
39683
40047
|
return m ? [{ item: m[1], status: m[2], detail: (m[3] ?? "").trim() }] : [];
|
|
39684
40048
|
});
|
|
@@ -39769,25 +40133,25 @@ init_config2();
|
|
|
39769
40133
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
39770
40134
|
import { chmodSync as chmodSync3, copyFileSync, existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync9, unlinkSync as unlinkSync2 } from "node:fs";
|
|
39771
40135
|
import { homedir as homedir5 } from "node:os";
|
|
39772
|
-
import { join as
|
|
40136
|
+
import { join as join18 } from "node:path";
|
|
39773
40137
|
var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? resolveDataDirs().sshKeys;
|
|
39774
40138
|
var KEY_FILES = ["id_ed25519", "id_ed25519.pub"];
|
|
39775
40139
|
function adoptLegacyKey(dir2) {
|
|
39776
40140
|
if (effectiveStr("HANOMAN_SSH_KEY_DIR")) return;
|
|
39777
|
-
const legacy =
|
|
40141
|
+
const legacy = join18(homedir5(), ".hanoman");
|
|
39778
40142
|
if (legacy === dir2) return;
|
|
39779
|
-
if (!KEY_FILES.every((f) => existsSync12(
|
|
40143
|
+
if (!KEY_FILES.every((f) => existsSync12(join18(legacy, f)))) return;
|
|
39780
40144
|
mkdirSync8(dir2, { recursive: true, mode: 448 });
|
|
39781
40145
|
for (const f of KEY_FILES) {
|
|
39782
|
-
copyFileSync(
|
|
39783
|
-
chmodSync3(
|
|
40146
|
+
copyFileSync(join18(legacy, f), join18(dir2, f));
|
|
40147
|
+
chmodSync3(join18(dir2, f), 384);
|
|
39784
40148
|
}
|
|
39785
|
-
for (const f of KEY_FILES) unlinkSync2(
|
|
40149
|
+
for (const f of KEY_FILES) unlinkSync2(join18(legacy, f));
|
|
39786
40150
|
console.log(`vps: key SSH dipindah dari ${legacy} ke ${dir2} (SPEC-846 \u2014 satu batas backup)`);
|
|
39787
40151
|
}
|
|
39788
40152
|
function ensureHanomanKey() {
|
|
39789
40153
|
const dir2 = keyDir();
|
|
39790
|
-
const privPath =
|
|
40154
|
+
const privPath = join18(dir2, KEY_FILES[0]);
|
|
39791
40155
|
const pubPath = `${privPath}.pub`;
|
|
39792
40156
|
if (!existsSync12(privPath)) adoptLegacyKey(dir2);
|
|
39793
40157
|
if (!existsSync12(privPath)) {
|
|
@@ -40026,7 +40390,7 @@ init_config2();
|
|
|
40026
40390
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
40027
40391
|
import { readFileSync as readFileSync11 } from "node:fs";
|
|
40028
40392
|
import { homedir as homedir7 } from "node:os";
|
|
40029
|
-
import { join as
|
|
40393
|
+
import { join as join19 } from "node:path";
|
|
40030
40394
|
var USAGE_URL = "https://api.anthropic.com/api/oauth/usage";
|
|
40031
40395
|
var TTL_MS2 = 3e4;
|
|
40032
40396
|
var lastOk = null;
|
|
@@ -40039,7 +40403,7 @@ var LABELS = {
|
|
|
40039
40403
|
var humanize = (k) => k.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
40040
40404
|
var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
40041
40405
|
function credsFile() {
|
|
40042
|
-
return
|
|
40406
|
+
return join19(effectiveStr("CLAUDE_CONFIG_DIR") ?? join19(homedir7(), ".claude"), ".credentials.json");
|
|
40043
40407
|
}
|
|
40044
40408
|
function readAccessToken() {
|
|
40045
40409
|
if (process.platform === "darwin" && !effectiveStr("CLAUDE_CONFIG_DIR")) {
|
|
@@ -40111,16 +40475,16 @@ async function getLimits() {
|
|
|
40111
40475
|
}
|
|
40112
40476
|
|
|
40113
40477
|
// src/services/codex-limits.ts
|
|
40114
|
-
import { open as open3, readdir as readdir4, stat } from "node:fs/promises";
|
|
40478
|
+
import { open as open3, readdir as readdir4, stat as stat2 } from "node:fs/promises";
|
|
40115
40479
|
import { homedir as homedir8 } from "node:os";
|
|
40116
|
-
import { join as
|
|
40480
|
+
import { join as join20 } from "node:path";
|
|
40117
40481
|
var TTL_MS3 = 3e4;
|
|
40118
40482
|
var STALE_AFTER_MS = 12 * 36e5;
|
|
40119
40483
|
var TAIL_BYTES = 512 * 1024;
|
|
40120
40484
|
var MAX_FILES = 8;
|
|
40121
40485
|
var cache3 = null;
|
|
40122
40486
|
var freshUntil2 = 0;
|
|
40123
|
-
var codexSessionsDir = () =>
|
|
40487
|
+
var codexSessionsDir = () => join20(process.env.CODEX_HOME ?? join20(homedir8(), ".codex"), "sessions");
|
|
40124
40488
|
var UNAVAILABLE = { status: "unavailable", windows: [], fetchedAt: null, plan: null };
|
|
40125
40489
|
function windowLabel(minutes) {
|
|
40126
40490
|
if (minutes === 300) return "Sesi 5 jam";
|
|
@@ -40153,9 +40517,9 @@ async function recentRollouts(dir2) {
|
|
|
40153
40517
|
return [];
|
|
40154
40518
|
}
|
|
40155
40519
|
const stamped = await Promise.all(entries.map(async (rel) => {
|
|
40156
|
-
const full =
|
|
40520
|
+
const full = join20(dir2, rel);
|
|
40157
40521
|
try {
|
|
40158
|
-
return { full, mtime: (await
|
|
40522
|
+
return { full, mtime: (await stat2(full)).mtimeMs };
|
|
40159
40523
|
} catch {
|
|
40160
40524
|
return null;
|
|
40161
40525
|
}
|
|
@@ -40233,15 +40597,15 @@ async function limits(app2) {
|
|
|
40233
40597
|
// src/services/codex-version.ts
|
|
40234
40598
|
init_src();
|
|
40235
40599
|
init_config2();
|
|
40236
|
-
import { execFile as
|
|
40237
|
-
import { promisify as
|
|
40238
|
-
var run =
|
|
40600
|
+
import { execFile as execFile11 } from "node:child_process";
|
|
40601
|
+
import { promisify as promisify12 } from "node:util";
|
|
40602
|
+
var run = promisify12(execFile11);
|
|
40239
40603
|
var CODEX_MIN_CLIENT = "0.144.0";
|
|
40240
40604
|
var TTL_MS4 = 5 * 6e4;
|
|
40241
40605
|
var cache4 = null;
|
|
40242
40606
|
var codexBin2 = () => effectiveStr("HANOMAN_CODEX_BIN") ?? "codex";
|
|
40243
|
-
function parseCodexVersion(
|
|
40244
|
-
const m = /(\d+)\.(\d+)\.(\d+)/.exec(
|
|
40607
|
+
function parseCodexVersion(out4) {
|
|
40608
|
+
const m = /(\d+)\.(\d+)\.(\d+)/.exec(out4);
|
|
40245
40609
|
return m ? `${m[1]}.${m[2]}.${m[3]}` : null;
|
|
40246
40610
|
}
|
|
40247
40611
|
async function getCodexVersion(now = Date.now()) {
|
|
@@ -40281,7 +40645,7 @@ init_src();
|
|
|
40281
40645
|
init_src();
|
|
40282
40646
|
init_config2();
|
|
40283
40647
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
40284
|
-
import { resolve as
|
|
40648
|
+
import { resolve as resolve18, dirname as dirname12 } from "node:path";
|
|
40285
40649
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
40286
40650
|
var UPDATE_COMMAND = "npm i -g hanoman@latest --prefer-online";
|
|
40287
40651
|
function composeUpdate(x) {
|
|
@@ -40307,7 +40671,7 @@ var lastLatest = null;
|
|
|
40307
40671
|
var lastStatus = "unavailable";
|
|
40308
40672
|
function runningVersion() {
|
|
40309
40673
|
const here = dirname12(fileURLToPath3(import.meta.url));
|
|
40310
|
-
for (const p of [
|
|
40674
|
+
for (const p of [resolve18(here, "build-info.json"), resolve18(here, "../package.json")]) {
|
|
40311
40675
|
try {
|
|
40312
40676
|
const v = JSON.parse(readFileSync12(p, "utf8")).version;
|
|
40313
40677
|
if (typeof v === "string" && v) return v;
|
|
@@ -40823,11 +41187,11 @@ import { randomUUID as randomUUID8 } from "node:crypto";
|
|
|
40823
41187
|
init_config2();
|
|
40824
41188
|
init_src2();
|
|
40825
41189
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
40826
|
-
import { mkdir as mkdir5, writeFile as writeFile4, readFile as readFile4, unlink as unlink4, readdir as readdir5, stat as
|
|
40827
|
-
import { join as
|
|
41190
|
+
import { mkdir as mkdir5, writeFile as writeFile4, readFile as readFile4, unlink as unlink4, readdir as readdir5, stat as stat3 } from "node:fs/promises";
|
|
41191
|
+
import { join as join21, resolve as resolve19, basename as basename6 } from "node:path";
|
|
40828
41192
|
var MAX_TRANSCRIPT_BYTES = 1024 * 1024;
|
|
40829
41193
|
function transcriptDir() {
|
|
40830
|
-
return
|
|
41194
|
+
return resolve19(effectiveStr("HANOMAN_TRANSCRIPT_DIR")?.trim() || resolveDataDirs().transcripts);
|
|
40831
41195
|
}
|
|
40832
41196
|
function clamp(text) {
|
|
40833
41197
|
const buf = Buffer.from(text, "utf8");
|
|
@@ -40845,13 +41209,13 @@ async function saveTranscript(text) {
|
|
|
40845
41209
|
const dir2 = transcriptDir();
|
|
40846
41210
|
await mkdir5(dir2, { recursive: true, mode: 448 });
|
|
40847
41211
|
const key = `${randomUUID7()}.log`;
|
|
40848
|
-
await writeFile4(
|
|
41212
|
+
await writeFile4(join21(dir2, key), body, { encoding: "utf8", mode: 384 });
|
|
40849
41213
|
return { key, bytes: Buffer.byteLength(body, "utf8"), truncated };
|
|
40850
41214
|
}
|
|
40851
41215
|
async function readTranscript(key) {
|
|
40852
41216
|
if (!key) return null;
|
|
40853
41217
|
try {
|
|
40854
|
-
return await readFile4(
|
|
41218
|
+
return await readFile4(join21(transcriptDir(), basename6(key)), "utf8");
|
|
40855
41219
|
} catch {
|
|
40856
41220
|
return null;
|
|
40857
41221
|
}
|
|
@@ -40859,7 +41223,7 @@ async function readTranscript(key) {
|
|
|
40859
41223
|
async function deleteTranscript(key) {
|
|
40860
41224
|
if (!key) return;
|
|
40861
41225
|
try {
|
|
40862
|
-
await unlink4(
|
|
41226
|
+
await unlink4(join21(transcriptDir(), basename6(key)));
|
|
40863
41227
|
} catch (e) {
|
|
40864
41228
|
if (e.code !== "ENOENT") throw e;
|
|
40865
41229
|
}
|
|
@@ -40877,7 +41241,7 @@ async function listTranscripts() {
|
|
|
40877
41241
|
for (const name2 of names) {
|
|
40878
41242
|
if (!name2.endsWith(".log")) continue;
|
|
40879
41243
|
try {
|
|
40880
|
-
rows.push({ key: name2, mtimeMs: (await
|
|
41244
|
+
rows.push({ key: name2, mtimeMs: (await stat3(join21(dir2, name2))).mtimeMs });
|
|
40881
41245
|
} catch {
|
|
40882
41246
|
}
|
|
40883
41247
|
}
|
|
@@ -42171,12 +42535,12 @@ function parseLeadVerdict(raw2) {
|
|
|
42171
42535
|
var SHA = /^[0-9a-f]{7,40}$/i;
|
|
42172
42536
|
var isNonFileRef = (ref) => SHA.test(ref) || /^ADR-\d{4}$/i.test(ref);
|
|
42173
42537
|
function keepExistingRefs(refs2, repoDir) {
|
|
42174
|
-
const
|
|
42538
|
+
const out4 = [];
|
|
42175
42539
|
for (const raw2 of refs2) {
|
|
42176
42540
|
const ref = raw2.trim();
|
|
42177
42541
|
if (!ref) continue;
|
|
42178
42542
|
if (isNonFileRef(ref)) {
|
|
42179
|
-
|
|
42543
|
+
out4.push(ref);
|
|
42180
42544
|
continue;
|
|
42181
42545
|
}
|
|
42182
42546
|
if (!repoDir) continue;
|
|
@@ -42184,9 +42548,9 @@ function keepExistingRefs(refs2, repoDir) {
|
|
|
42184
42548
|
const full = resolvePath(repoDir, ref);
|
|
42185
42549
|
const rel = relative2(repoDir, full);
|
|
42186
42550
|
if (rel.startsWith("..")) continue;
|
|
42187
|
-
if (existsSync14(full))
|
|
42551
|
+
if (existsSync14(full)) out4.push(ref);
|
|
42188
42552
|
}
|
|
42189
|
-
return [...new Set(
|
|
42553
|
+
return [...new Set(out4)];
|
|
42190
42554
|
}
|
|
42191
42555
|
|
|
42192
42556
|
// src/services/lead/trail.ts
|
|
@@ -42335,11 +42699,11 @@ function toDecisionView(r) {
|
|
|
42335
42699
|
init_config2();
|
|
42336
42700
|
init_pty();
|
|
42337
42701
|
init_session_sandbox();
|
|
42338
|
-
import { execFile as
|
|
42702
|
+
import { execFile as execFile12 } from "node:child_process";
|
|
42339
42703
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
42340
42704
|
import { mkdirSync as mkdirSync9, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
42341
42705
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
42342
|
-
import { join as
|
|
42706
|
+
import { join as join22 } from "node:path";
|
|
42343
42707
|
var binFor = (agent) => agent === "codex" ? effectiveStr("HANOMAN_CODEX_BIN") ?? "codex" : effectiveStr("HANOMAN_CLAUDE_BIN") ?? "claude";
|
|
42344
42708
|
function leadArgv(o) {
|
|
42345
42709
|
if (o.agent === "codex") {
|
|
@@ -42367,11 +42731,11 @@ function leadProcess(prompt, o, env = process.env) {
|
|
|
42367
42731
|
const mode = env.HANOMAN_SESSION_SANDBOX ?? (env.NODE_ENV === "production" ? "required" : "off");
|
|
42368
42732
|
if (mode === "off") return { file, args: directArgs, cwd: o.cwd, cleanup: () => {
|
|
42369
42733
|
} };
|
|
42370
|
-
const promptDir =
|
|
42734
|
+
const promptDir = join22(tmpdir4(), "hanoman-prompts");
|
|
42371
42735
|
mkdirSync9(promptDir, { recursive: true, mode: 448 });
|
|
42372
|
-
const promptFile =
|
|
42736
|
+
const promptFile = join22(promptDir, `oneshot-${randomUUID9()}`);
|
|
42373
42737
|
writeFileSync5(promptFile, prompt, { flag: "wx", mode: 384 });
|
|
42374
|
-
const workspace = o.cwd ??
|
|
42738
|
+
const workspace = o.cwd ?? join22(tmpdir4(), `hanoman-oneshot-${randomUUID9()}`);
|
|
42375
42739
|
if (!o.cwd) mkdirSync9(workspace, { recursive: false, mode: 448 });
|
|
42376
42740
|
try {
|
|
42377
42741
|
const argsWithoutPrompt = directArgs.slice(0, -1);
|
|
@@ -42413,8 +42777,8 @@ function leadFailureReason(agent, timeoutMs, err, stdout, stderr) {
|
|
|
42413
42777
|
}
|
|
42414
42778
|
function think(prompt, o) {
|
|
42415
42779
|
const process2 = leadProcess(prompt, o);
|
|
42416
|
-
return new Promise((
|
|
42417
|
-
const child =
|
|
42780
|
+
return new Promise((resolve21, reject2) => {
|
|
42781
|
+
const child = execFile12(process2.file, process2.args, {
|
|
42418
42782
|
cwd: process2.cwd,
|
|
42419
42783
|
timeout: o.timeoutMs,
|
|
42420
42784
|
maxBuffer: 16 * 1024 * 1024,
|
|
@@ -42427,7 +42791,7 @@ function think(prompt, o) {
|
|
|
42427
42791
|
reject2(new Error(leadFailureReason(o.agent, o.timeoutMs, err, stdout, stderr)));
|
|
42428
42792
|
return;
|
|
42429
42793
|
}
|
|
42430
|
-
|
|
42794
|
+
resolve21(stdout);
|
|
42431
42795
|
});
|
|
42432
42796
|
child.stdin?.end();
|
|
42433
42797
|
});
|
|
@@ -42464,9 +42828,9 @@ function acquire(cap, waitMs) {
|
|
|
42464
42828
|
return Promise.resolve();
|
|
42465
42829
|
}
|
|
42466
42830
|
const startedAt = Date.now();
|
|
42467
|
-
return new Promise((
|
|
42831
|
+
return new Promise((resolve21, reject2) => {
|
|
42468
42832
|
const w = {
|
|
42469
|
-
grant:
|
|
42833
|
+
grant: resolve21,
|
|
42470
42834
|
deny: reject2,
|
|
42471
42835
|
timer: setTimeout(() => {
|
|
42472
42836
|
const i = queue2.indexOf(w);
|
|
@@ -42567,12 +42931,12 @@ async function expireFlows(now) {
|
|
|
42567
42931
|
orderBy: { createdAt: "asc" },
|
|
42568
42932
|
take: 100
|
|
42569
42933
|
});
|
|
42570
|
-
const
|
|
42934
|
+
const out4 = [];
|
|
42571
42935
|
for (const f of due) {
|
|
42572
42936
|
const closed = await closeFlow(f.id, "kedaluwarsa");
|
|
42573
|
-
if (closed)
|
|
42937
|
+
if (closed) out4.push(closed);
|
|
42574
42938
|
}
|
|
42575
|
-
return
|
|
42939
|
+
return out4;
|
|
42576
42940
|
}
|
|
42577
42941
|
function toFlowView(r) {
|
|
42578
42942
|
return {
|
|
@@ -43083,15 +43447,15 @@ var prodDetectDeps = {
|
|
|
43083
43447
|
cfg: getLead
|
|
43084
43448
|
};
|
|
43085
43449
|
async function scanAndAnswer(deps = prodDetectDeps) {
|
|
43086
|
-
const
|
|
43450
|
+
const out4 = { answered: [], skipped: [] };
|
|
43087
43451
|
const cfg = await deps.cfg();
|
|
43088
|
-
if (!cfg.enabled || cfg.paused) return
|
|
43452
|
+
if (!cfg.enabled || cfg.paused) return out4;
|
|
43089
43453
|
const optIn = new Set(await deps.optIn());
|
|
43090
43454
|
const sessions = deps.live();
|
|
43091
43455
|
sweep(sessions.map((s2) => s2.id));
|
|
43092
43456
|
const ready = [];
|
|
43093
43457
|
for (const s2 of sessions) {
|
|
43094
|
-
const skip = (reason) =>
|
|
43458
|
+
const skip = (reason) => out4.skipped.push({ id: s2.id, reason });
|
|
43095
43459
|
if (!optIn.has(s2.projectId)) {
|
|
43096
43460
|
skip("project tak opt-in lead");
|
|
43097
43461
|
continue;
|
|
@@ -43181,16 +43545,16 @@ async function scanAndAnswer(deps = prodDetectDeps) {
|
|
|
43181
43545
|
deps.clearMarker(s2.decisionFile);
|
|
43182
43546
|
answers.set(s2.id, (answers.get(s2.id) ?? 0) + 1);
|
|
43183
43547
|
failures.delete(s2.id);
|
|
43184
|
-
|
|
43548
|
+
out4.answered.push(s2.id);
|
|
43185
43549
|
return;
|
|
43186
43550
|
}
|
|
43187
43551
|
if (chain.failed) {
|
|
43188
43552
|
failures.set(s2.id, (failures.get(s2.id) ?? 0) + 1);
|
|
43189
43553
|
failedAt.set(s2.id, deps.now());
|
|
43190
43554
|
}
|
|
43191
|
-
|
|
43555
|
+
out4.skipped.push({ id: s2.id, reason: chain.reason });
|
|
43192
43556
|
});
|
|
43193
|
-
return
|
|
43557
|
+
return out4;
|
|
43194
43558
|
}
|
|
43195
43559
|
async function runChain(s2, agent, deps) {
|
|
43196
43560
|
let acted = false;
|
|
@@ -43300,7 +43664,7 @@ init_session_phases();
|
|
|
43300
43664
|
init_notifications();
|
|
43301
43665
|
var moduleOf = (p) => p.split("/").slice(0, 2).join("/");
|
|
43302
43666
|
function findCollisions(areas) {
|
|
43303
|
-
const
|
|
43667
|
+
const out4 = [];
|
|
43304
43668
|
for (let i = 0; i < areas.length; i++) {
|
|
43305
43669
|
for (let j = i + 1; j < areas.length; j++) {
|
|
43306
43670
|
const a = areas[i], b = areas[j];
|
|
@@ -43309,10 +43673,10 @@ function findCollisions(areas) {
|
|
|
43309
43673
|
const shared = a.paths.filter((p) => bp.has(p));
|
|
43310
43674
|
const bm = new Set(b.paths.map(moduleOf));
|
|
43311
43675
|
const nearby = [...new Set(a.paths.map(moduleOf))].filter((m) => bm.has(m) && !shared.some((s2) => moduleOf(s2) === m));
|
|
43312
|
-
if (shared.length || nearby.length)
|
|
43676
|
+
if (shared.length || nearby.length) out4.push({ a, b, shared, nearby });
|
|
43313
43677
|
}
|
|
43314
43678
|
}
|
|
43315
|
-
return
|
|
43679
|
+
return out4;
|
|
43316
43680
|
}
|
|
43317
43681
|
var prodPulseDeps = {
|
|
43318
43682
|
sessions: () => {
|
|
@@ -43828,8 +44192,8 @@ init_db();
|
|
|
43828
44192
|
|
|
43829
44193
|
// src/services/changelog/collect.ts
|
|
43830
44194
|
init_db();
|
|
43831
|
-
import { execFile as
|
|
43832
|
-
import { promisify as
|
|
44195
|
+
import { execFile as execFile13 } from "node:child_process";
|
|
44196
|
+
import { promisify as promisify13 } from "node:util";
|
|
43833
44197
|
import { existsSync as existsSync15 } from "node:fs";
|
|
43834
44198
|
|
|
43835
44199
|
// src/services/changelog/scrub.ts
|
|
@@ -43891,8 +44255,8 @@ async function collectBacklog(projectId, from, to) {
|
|
|
43891
44255
|
notes.push(`${stampless} item selesai tanpa stempel waktu (selesai sebelum stempel ini ada) dan tak ikut dihitung.`);
|
|
43892
44256
|
return { ok: true, input: { mode: "backlog", title: `${from} \u2013 ${to}`, items, notes } };
|
|
43893
44257
|
}
|
|
43894
|
-
var
|
|
43895
|
-
var
|
|
44258
|
+
var exec10 = promisify13(execFile13);
|
|
44259
|
+
var GIT8 = { maxBuffer: 1 << 24, encoding: "utf8" };
|
|
43896
44260
|
var US2 = "";
|
|
43897
44261
|
var RS = "";
|
|
43898
44262
|
var NO_REPO = "project ini belum ditautkan ke repo di mesin ini";
|
|
@@ -43901,25 +44265,25 @@ var usable = (repoDir) => !!repoDir && existsSync15(repoDir);
|
|
|
43901
44265
|
async function listTags(repoDir) {
|
|
43902
44266
|
if (!usable(repoDir)) return { tags: [], head: null, reason: NO_REPO };
|
|
43903
44267
|
try {
|
|
43904
|
-
const { stdout } = await
|
|
44268
|
+
const { stdout } = await exec10("git", ["tag", "--list", "--sort=-creatordate"], { cwd: repoDir, ...GIT8 });
|
|
43905
44269
|
const tags = stdout.split("\n").map((s2) => s2.trim()).filter(Boolean);
|
|
43906
|
-
const head = await
|
|
44270
|
+
const head = await exec10("git", ["rev-parse", "--short", "HEAD"], { cwd: repoDir, ...GIT8 }).then((r) => r.stdout.trim()).catch(() => null);
|
|
43907
44271
|
return { tags, head, reason: tags.length ? null : NO_TAG };
|
|
43908
44272
|
} catch {
|
|
43909
44273
|
return { tags: [], head: null, reason: NO_TAG };
|
|
43910
44274
|
}
|
|
43911
44275
|
}
|
|
43912
44276
|
async function previousTag(repoDir, tag2) {
|
|
43913
|
-
return
|
|
44277
|
+
return exec10("git", ["describe", "--tags", "--abbrev=0", "--end-of-options", `${tag2}^`], { cwd: repoDir, ...GIT8 }).then((r) => r.stdout.trim() || null).catch(() => null);
|
|
43914
44278
|
}
|
|
43915
44279
|
async function revExists(repoDir, rev) {
|
|
43916
|
-
return
|
|
44280
|
+
return exec10("git", ["rev-parse", "--verify", "--quiet", "--end-of-options", `${rev}^{commit}`], { cwd: repoDir, ...GIT8 }).then(() => true).catch(() => false);
|
|
43917
44281
|
}
|
|
43918
44282
|
async function logRange(repoDir, range) {
|
|
43919
|
-
const { stdout } = await
|
|
44283
|
+
const { stdout } = await exec10(
|
|
43920
44284
|
"git",
|
|
43921
44285
|
["log", "--no-merges", `--pretty=format:%s${US2}%b${RS}`, ...range],
|
|
43922
|
-
{ cwd: repoDir, ...
|
|
44286
|
+
{ cwd: repoDir, ...GIT8 }
|
|
43923
44287
|
);
|
|
43924
44288
|
return stdout.split(RS).map((rec) => rec.replace(/^\n+/, "")).filter((rec) => rec.trim()).map((rec) => {
|
|
43925
44289
|
const [subject, body] = rec.split(US2);
|
|
@@ -44160,7 +44524,7 @@ init_db();
|
|
|
44160
44524
|
init_src();
|
|
44161
44525
|
import { readFileSync as readFileSync13 } from "node:fs";
|
|
44162
44526
|
import { homedir as homedir9 } from "node:os";
|
|
44163
|
-
import { join as
|
|
44527
|
+
import { join as join23 } from "node:path";
|
|
44164
44528
|
var home = () => process.env.HOME || homedir9();
|
|
44165
44529
|
var readJson2 = (path2) => {
|
|
44166
44530
|
try {
|
|
@@ -44177,25 +44541,25 @@ var serversOf = (node) => {
|
|
|
44177
44541
|
var codexServers = () => {
|
|
44178
44542
|
let text;
|
|
44179
44543
|
try {
|
|
44180
|
-
text = readFileSync13(
|
|
44544
|
+
text = readFileSync13(join23(home(), ".codex", "config.toml"), "utf8");
|
|
44181
44545
|
} catch {
|
|
44182
44546
|
return [];
|
|
44183
44547
|
}
|
|
44184
|
-
const
|
|
44548
|
+
const out4 = [];
|
|
44185
44549
|
for (const m of text.matchAll(/^\s*\[mcp_servers\.(?:"([^"]+)"|([A-Za-z0-9_-]+))(?:\.[^\]]*)?\]/gm)) {
|
|
44186
44550
|
const name2 = m[1] ?? m[2];
|
|
44187
|
-
if (name2)
|
|
44551
|
+
if (name2) out4.push(name2);
|
|
44188
44552
|
}
|
|
44189
|
-
return
|
|
44553
|
+
return out4;
|
|
44190
44554
|
};
|
|
44191
44555
|
function mcpServerNames(repoDir) {
|
|
44192
44556
|
const names = [];
|
|
44193
|
-
const claudeJson = readJson2(
|
|
44557
|
+
const claudeJson = readJson2(join23(home(), ".claude.json"));
|
|
44194
44558
|
names.push(...serversOf(claudeJson));
|
|
44195
44559
|
if (repoDir) {
|
|
44196
44560
|
const projects = claudeJson?.projects;
|
|
44197
44561
|
if (projects && typeof projects === "object") names.push(...serversOf(projects[repoDir]));
|
|
44198
|
-
names.push(...serversOf(readJson2(
|
|
44562
|
+
names.push(...serversOf(readJson2(join23(repoDir, ".mcp.json"))));
|
|
44199
44563
|
}
|
|
44200
44564
|
names.push(...codexServers());
|
|
44201
44565
|
return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
@@ -44494,7 +44858,7 @@ async function resolveGithubRepo(projectId) {
|
|
|
44494
44858
|
|
|
44495
44859
|
// src/services/github-fetch.ts
|
|
44496
44860
|
init_config2();
|
|
44497
|
-
import { execFile as
|
|
44861
|
+
import { execFile as execFile14 } from "node:child_process";
|
|
44498
44862
|
var labelNames = (l) => (l ?? []).map((x) => typeof x === "string" ? x : x.name ?? "").filter(Boolean);
|
|
44499
44863
|
var norm2 = (s2) => s2.toLowerCase() === "closed" ? "closed" : "open";
|
|
44500
44864
|
function issueFromGh(raw2) {
|
|
@@ -44534,15 +44898,15 @@ function issuesFromRest(raw2) {
|
|
|
44534
44898
|
}
|
|
44535
44899
|
var GH_FIELDS = "number,title,body,author,labels,url,state,createdAt,updatedAt";
|
|
44536
44900
|
var API2 = "https://api.github.com";
|
|
44537
|
-
var defaultRunGh = (args, env) => new Promise((
|
|
44538
|
-
|
|
44901
|
+
var defaultRunGh = (args, env) => new Promise((resolve21, reject2) => {
|
|
44902
|
+
execFile14(
|
|
44539
44903
|
args[0],
|
|
44540
44904
|
args.slice(1),
|
|
44541
44905
|
{ env, maxBuffer: 1 << 26, encoding: "utf8", timeout: 6e4 },
|
|
44542
44906
|
(err, stdout, stderr) => {
|
|
44543
44907
|
const e = err;
|
|
44544
44908
|
if (e && (e.code === "ENOENT" || e.code === "EACCES")) return reject2(e);
|
|
44545
|
-
|
|
44909
|
+
resolve21({ code: err ? Number(err.code ?? 1) : 0, stdout, stderr });
|
|
44546
44910
|
}
|
|
44547
44911
|
);
|
|
44548
44912
|
});
|
|
@@ -44577,20 +44941,20 @@ async function viaGh(repo, opts, deps) {
|
|
|
44577
44941
|
];
|
|
44578
44942
|
const env = { ...process.env };
|
|
44579
44943
|
if (deps.token) env.GH_TOKEN = deps.token;
|
|
44580
|
-
let
|
|
44944
|
+
let out4;
|
|
44581
44945
|
try {
|
|
44582
|
-
|
|
44946
|
+
out4 = await (deps.runGh ?? defaultRunGh)(args, env);
|
|
44583
44947
|
} catch {
|
|
44584
44948
|
return { fallback: true, reason: "gh tak terpasang" };
|
|
44585
44949
|
}
|
|
44586
|
-
if (
|
|
44587
|
-
const kind = classifyGhStderr(
|
|
44950
|
+
if (out4.code !== 0) {
|
|
44951
|
+
const kind = classifyGhStderr(out4.stderr);
|
|
44588
44952
|
if (kind === "unauth") return { fallback: true, reason: "gh tak terautentikasi" };
|
|
44589
|
-
return { ok: false, kind, error:
|
|
44953
|
+
return { ok: false, kind, error: out4.stderr.trim() || `gh keluar dengan kode ${out4.code}` };
|
|
44590
44954
|
}
|
|
44591
44955
|
let raw2;
|
|
44592
44956
|
try {
|
|
44593
|
-
raw2 = JSON.parse(
|
|
44957
|
+
raw2 = JSON.parse(out4.stdout || "[]");
|
|
44594
44958
|
} catch {
|
|
44595
44959
|
return { ok: false, kind: "other", error: "keluaran gh bukan JSON" };
|
|
44596
44960
|
}
|
|
@@ -45402,13 +45766,13 @@ async function webhooks_default(app2) {
|
|
|
45402
45766
|
sentAt: res.ok ? /* @__PURE__ */ new Date() : null
|
|
45403
45767
|
}
|
|
45404
45768
|
});
|
|
45405
|
-
const
|
|
45769
|
+
const out4 = {
|
|
45406
45770
|
ok: res.ok,
|
|
45407
45771
|
httpStatus: res.httpStatus,
|
|
45408
45772
|
durationMs: res.durationMs,
|
|
45409
45773
|
error: res.error
|
|
45410
45774
|
};
|
|
45411
|
-
return
|
|
45775
|
+
return out4;
|
|
45412
45776
|
});
|
|
45413
45777
|
app2.get("/webhooks/:id/deliveries", async (req, reply) => {
|
|
45414
45778
|
const { id } = req.params;
|
|
@@ -45567,7 +45931,7 @@ init_settings2();
|
|
|
45567
45931
|
// src/services/portal-chat/turn.ts
|
|
45568
45932
|
init_src();
|
|
45569
45933
|
init_db();
|
|
45570
|
-
import { execFile as
|
|
45934
|
+
import { execFile as execFile15 } from "node:child_process";
|
|
45571
45935
|
|
|
45572
45936
|
// src/services/portal-chat/guard-input.ts
|
|
45573
45937
|
import { randomBytes as randomBytes9 } from "node:crypto";
|
|
@@ -45681,7 +46045,7 @@ init_src();
|
|
|
45681
46045
|
init_db();
|
|
45682
46046
|
import { mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
45683
46047
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
45684
|
-
import { join as
|
|
46048
|
+
import { join as join24 } from "node:path";
|
|
45685
46049
|
var STAGE_LABEL = {
|
|
45686
46050
|
brainstorming: "Dirumuskan",
|
|
45687
46051
|
objective: "Dirumuskan",
|
|
@@ -45736,7 +46100,7 @@ ${c.body}
|
|
|
45736
46100
|
${baris.join("\n")}`;
|
|
45737
46101
|
}
|
|
45738
46102
|
async function buildChatWorkspace(projectId) {
|
|
45739
|
-
const dir2 = mkdtempSync2(
|
|
46103
|
+
const dir2 = mkdtempSync2(join24(tmpdir5(), "hanoman-portal-chat-"));
|
|
45740
46104
|
try {
|
|
45741
46105
|
const project = await prisma.project.findUnique({
|
|
45742
46106
|
where: { id: projectId },
|
|
@@ -45757,7 +46121,7 @@ async function buildChatWorkspace(projectId) {
|
|
|
45757
46121
|
});
|
|
45758
46122
|
const files = [];
|
|
45759
46123
|
const tulis = (rel, isi) => {
|
|
45760
|
-
writeFileSync7(
|
|
46124
|
+
writeFileSync7(join24(dir2, rel), isi, { mode: 384 });
|
|
45761
46125
|
files.push(rel);
|
|
45762
46126
|
};
|
|
45763
46127
|
tulis("project.md", renderProjectDoc(project));
|
|
@@ -45766,11 +46130,11 @@ async function buildChatWorkspace(projectId) {
|
|
|
45766
46130
|
tulis("catatan-rilis.md", renderChangelogDoc(changelogs));
|
|
45767
46131
|
const prds = await listPrds(projectId);
|
|
45768
46132
|
if (prds.length) {
|
|
45769
|
-
mkdirSync10(
|
|
46133
|
+
mkdirSync10(join24(dir2, "dokumen"), { mode: 448 });
|
|
45770
46134
|
for (const prd of prds) {
|
|
45771
46135
|
const isi = await readPrd(projectId, prd.path);
|
|
45772
46136
|
if (!isi) continue;
|
|
45773
|
-
tulis(
|
|
46137
|
+
tulis(join24("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
|
|
45774
46138
|
}
|
|
45775
46139
|
}
|
|
45776
46140
|
return { dir: dir2, files, cleanup: () => rmSync6(dir2, { recursive: true, force: true }) };
|
|
@@ -45852,8 +46216,8 @@ function chatFailureReason(err, stdout, stderr, timeoutMs) {
|
|
|
45852
46216
|
return `chat portal gagal (${bagaimana}): ${detail}`;
|
|
45853
46217
|
}
|
|
45854
46218
|
function runProcess(p, timeoutMs) {
|
|
45855
|
-
return new Promise((
|
|
45856
|
-
const child =
|
|
46219
|
+
return new Promise((resolve21, reject2) => {
|
|
46220
|
+
const child = execFile15(p.file, p.args, {
|
|
45857
46221
|
cwd: p.cwd,
|
|
45858
46222
|
timeout: timeoutMs,
|
|
45859
46223
|
maxBuffer: 16 * 1024 * 1024,
|
|
@@ -45864,7 +46228,7 @@ function runProcess(p, timeoutMs) {
|
|
|
45864
46228
|
stdout,
|
|
45865
46229
|
stderr,
|
|
45866
46230
|
timeoutMs
|
|
45867
|
-
))) :
|
|
46231
|
+
))) : resolve21(stdout));
|
|
45868
46232
|
child.stdin?.end();
|
|
45869
46233
|
});
|
|
45870
46234
|
}
|
|
@@ -46282,13 +46646,13 @@ init_db();
|
|
|
46282
46646
|
// src/services/bootstrap.ts
|
|
46283
46647
|
import { createHash as createHash9, randomBytes as randomBytes10, timingSafeEqual as timingSafeEqual3 } from "node:crypto";
|
|
46284
46648
|
import { chmod, lstat as lstat3, mkdir as mkdir6, readFile as readFile5, unlink as unlink5, writeFile as writeFile5 } from "node:fs/promises";
|
|
46285
|
-
import { join as
|
|
46649
|
+
import { join as join25 } from "node:path";
|
|
46286
46650
|
var TTL_MS5 = 15 * 6e4;
|
|
46287
46651
|
var SETUP_TOKEN_FILE = "setup.token";
|
|
46288
46652
|
var BootstrapError = class extends Error {
|
|
46289
46653
|
code = "BOOTSTRAP_PROOF";
|
|
46290
46654
|
};
|
|
46291
|
-
var tokenPath = (home3) =>
|
|
46655
|
+
var tokenPath = (home3) => join25(home3, SETUP_TOKEN_FILE);
|
|
46292
46656
|
async function readStored(home3) {
|
|
46293
46657
|
try {
|
|
46294
46658
|
const info = await lstat3(tokenPath(home3));
|
|
@@ -46503,7 +46867,10 @@ var IDE_SUBS = /* @__PURE__ */ new Set([
|
|
|
46503
46867
|
// POST/PATCH/DELETE menuntut ide:write — capability yang sudah memberi hak menimpa isi
|
|
46504
46868
|
// berkas apa pun lewat PUT /file (hindari kelas bug SPEC-405: prefix tanpa lihat method).
|
|
46505
46869
|
"entry",
|
|
46506
|
-
"upload"
|
|
46870
|
+
"upload",
|
|
46871
|
+
// SPEC-861 · ADR-0132 · daftar & hapus worktree hidup. `rw()` menurunkan read/write DARI METHOD,
|
|
46872
|
+
// jadi POST /worktrees/delete menuntut ide:write (hindari kelas bug SPEC-405).
|
|
46873
|
+
"worktrees"
|
|
46507
46874
|
]);
|
|
46508
46875
|
function capabilityForRoute(method, path2) {
|
|
46509
46876
|
const read2 = method === "GET" || method === "HEAD";
|
|
@@ -46675,16 +47042,16 @@ init_runtime();
|
|
|
46675
47042
|
|
|
46676
47043
|
// src/services/ingress-policy.ts
|
|
46677
47044
|
function origins(value, key) {
|
|
46678
|
-
const
|
|
47045
|
+
const out4 = [];
|
|
46679
47046
|
for (const raw2 of value?.split(",") ?? []) {
|
|
46680
47047
|
const input = raw2.trim();
|
|
46681
47048
|
if (!input) continue;
|
|
46682
47049
|
const parsed = new URL(input);
|
|
46683
47050
|
if (!/^https?:$/.test(parsed.protocol) || parsed.username || parsed.password || parsed.pathname !== "/")
|
|
46684
47051
|
throw new Error(`${key} harus berisi exact http(s) origin`);
|
|
46685
|
-
|
|
47052
|
+
out4.push({ host: parsed.host.toLowerCase(), base: `${parsed.protocol}//${parsed.host}` });
|
|
46686
47053
|
}
|
|
46687
|
-
return
|
|
47054
|
+
return out4;
|
|
46688
47055
|
}
|
|
46689
47056
|
function loadIngressPolicy(env) {
|
|
46690
47057
|
const pub = origins(env.HANOMAN_PUBLIC_ORIGINS, "HANOMAN_PUBLIC_ORIGINS");
|
|
@@ -47470,17 +47837,17 @@ function startWebhookEngine() {
|
|
|
47470
47837
|
// src/services/auto-merge.ts
|
|
47471
47838
|
init_src();
|
|
47472
47839
|
init_db();
|
|
47473
|
-
import { execFile as
|
|
47474
|
-
import { promisify as
|
|
47840
|
+
import { execFile as execFile16 } from "node:child_process";
|
|
47841
|
+
import { promisify as promisify14 } from "node:util";
|
|
47475
47842
|
init_notifications();
|
|
47476
47843
|
var AUTO_MERGE_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
47477
47844
|
var AUTO_MERGE_GRACE_MS = 15 * 60 * 1e3;
|
|
47478
47845
|
var TICK_MS5 = 6e4;
|
|
47479
|
-
var
|
|
47480
|
-
var
|
|
47846
|
+
var exec11 = promisify14(execFile16);
|
|
47847
|
+
var GIT9 = { timeout: 3e4, maxBuffer: 1 << 24, encoding: "utf8" };
|
|
47481
47848
|
var gitOut = async (cwd, args) => {
|
|
47482
47849
|
try {
|
|
47483
|
-
return (await
|
|
47850
|
+
return (await exec11("git", args, { cwd, ...GIT9 })).stdout.trim();
|
|
47484
47851
|
} catch {
|
|
47485
47852
|
return null;
|
|
47486
47853
|
}
|
|
@@ -47589,13 +47956,13 @@ init_src2();
|
|
|
47589
47956
|
|
|
47590
47957
|
// src/services/secure-home.ts
|
|
47591
47958
|
import { chmod as chmod2, lstat as lstat4, mkdir as mkdir7 } from "node:fs/promises";
|
|
47592
|
-
import { isAbsolute as isAbsolute6, resolve as
|
|
47959
|
+
import { isAbsolute as isAbsolute6, resolve as resolve20 } from "node:path";
|
|
47593
47960
|
var HomePermissionError = class extends Error {
|
|
47594
47961
|
code = "HOME_SYMLINK";
|
|
47595
47962
|
};
|
|
47596
47963
|
async function assertNoSymlink(path2, allowMissing) {
|
|
47597
47964
|
try {
|
|
47598
|
-
const info = await lstat4(
|
|
47965
|
+
const info = await lstat4(resolve20(path2));
|
|
47599
47966
|
if (info.isSymbolicLink()) throw new HomePermissionError(`symlink ditolak: ${path2}`);
|
|
47600
47967
|
} catch (error) {
|
|
47601
47968
|
if (error.code === "ENOENT" && allowMissing) return;
|