hanoman 0.9.40 → 0.9.41
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 +3 -0
- package/dist/server.js +413 -273
- package/package.json +1 -1
- package/prisma/migrations/20261108090000_session_agent_session_id/migration.sql +2 -0
- package/prisma/schema.prisma +3 -0
- package/web/assets/{DalangHanomanScreen-WQ2B2G0F.js → DalangHanomanScreen-BBByhG99.js} +1 -1
- package/web/assets/{DocPreviewModal-DkuSjeL9.js → DocPreviewModal-CQY4THbu.js} +1 -1
- package/web/assets/{DocsWorkspace-BJ71xmQ_.js → DocsWorkspace-C6pT1E7m.js} +1 -1
- package/web/assets/{IdeScreen-Bc4gR6md.js → IdeScreen-BLFtTk_L.js} +1 -1
- package/web/assets/{LeadScreen-BmRdOMoJ.js → LeadScreen-jkt1heEV.js} +1 -1
- package/web/assets/{ReviewScreen-C83FuzRB.js → ReviewScreen-wUPVe5KU.js} +1 -1
- package/web/assets/{SchedulerScreen-BKnqWV7j.js → SchedulerScreen-DWz8dSHR.js} +1 -1
- package/web/assets/SettingsScreen-CK252KAu.js +62 -0
- package/web/assets/{TeamScreen-Dgrvomt0.js → TeamScreen-PNeLa4KQ.js} +1 -1
- package/web/assets/{TerminalScreen-D2KSQ13m.js → TerminalScreen-D_nXSBk6.js} +1 -1
- package/web/assets/{TriageScreen-DrPERvDs.js → TriageScreen-Bntfin0b.js} +1 -1
- package/web/assets/{VpsScreen-7sDyg5MI.js → VpsScreen-DugFaqzS.js} +1 -1
- package/web/assets/fields-ADsZ83ci.js +1 -0
- package/web/assets/{index-NoTUDPfb.js → index-BIwgmLXG.js} +3 -3
- package/web/assets/{live-B4ZwQ4kB.js → live-BIDo2oKi.js} +1 -1
- package/web/assets/{sched-lead-shared-CZ0_L34F.js → sched-lead-shared-BGQiQkHX.js} +1 -1
- package/web/assets/{settings-rebase-DwTIHvhF.js → settings-rebase-CF06reBv.js} +1 -1
- package/web/index.html +1 -1
- package/web/assets/SettingsScreen-FrgI5pzO.js +0 -62
- package/web/assets/fields-BNhNDKOP.js +0 -1
package/dist/server.js
CHANGED
|
@@ -5798,6 +5798,9 @@ var init_entities = __esm({
|
|
|
5798
5798
|
// SPEC-184
|
|
5799
5799
|
notifyDecisionTelegram: external_exports.boolean().default(true),
|
|
5800
5800
|
// ADR-0226 · dorong "menunggu keputusan" ke Telegram
|
|
5801
|
+
// ADR-0235 · saklar tunggal auto-resume SEMUA sesi (backlog + bebas) yang terputus paksa (reboot/crash).
|
|
5802
|
+
// Default MATI — mengamandemen ADR-0169 (dulu selalu nyala untuk backlog).
|
|
5803
|
+
autoResumeSessions: external_exports.boolean().default(false),
|
|
5801
5804
|
agentAccessEnabled: external_exports.boolean().default(false),
|
|
5802
5805
|
// SPEC-257 · master switch akses AI agent
|
|
5803
5806
|
scheduler: zScheduler.default(SCHEDULER_DEFAULTS),
|
|
@@ -21749,10 +21752,29 @@ var init_session_id = __esm({
|
|
|
21749
21752
|
}
|
|
21750
21753
|
});
|
|
21751
21754
|
|
|
21755
|
+
// src/services/claude-conversation.ts
|
|
21756
|
+
import { existsSync as existsSync8, readdirSync as readdirSync8 } from "node:fs";
|
|
21757
|
+
import { homedir as homedir4 } from "node:os";
|
|
21758
|
+
import { join as join13 } from "node:path";
|
|
21759
|
+
function claudeConversationExists(uuid, configDir) {
|
|
21760
|
+
if (!/^[0-9a-f-]{36}$/i.test(uuid)) return false;
|
|
21761
|
+
const projects = join13(configDir ?? process.env.CLAUDE_CONFIG_DIR ?? join13(homedir4(), ".claude"), "projects");
|
|
21762
|
+
try {
|
|
21763
|
+
return readdirSync8(projects).some((slug2) => existsSync8(join13(projects, slug2, `${uuid}.jsonl`)));
|
|
21764
|
+
} catch {
|
|
21765
|
+
return false;
|
|
21766
|
+
}
|
|
21767
|
+
}
|
|
21768
|
+
var init_claude_conversation = __esm({
|
|
21769
|
+
"src/services/claude-conversation.ts"() {
|
|
21770
|
+
"use strict";
|
|
21771
|
+
}
|
|
21772
|
+
});
|
|
21773
|
+
|
|
21752
21774
|
// src/services/antigravity-env-bootstrap.ts
|
|
21753
21775
|
import * as fs from "node:fs";
|
|
21754
21776
|
import { createHash as createHash6 } from "node:crypto";
|
|
21755
|
-
import { join as
|
|
21777
|
+
import { join as join14, dirname as dirname8, basename as basename5 } from "node:path";
|
|
21756
21778
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
21757
21779
|
function consume(receipt, io, hashOf2, parent, paths3) {
|
|
21758
21780
|
if (!receipt || typeof receipt.directory !== "string" || paths3.dirname(receipt.directory) !== parent || !paths3.basename(receipt.directory).startsWith("hanoman-ag-env-")) return;
|
|
@@ -21768,12 +21790,12 @@ function consume(receipt, io, hashOf2, parent, paths3) {
|
|
|
21768
21790
|
}
|
|
21769
21791
|
function cleanupAntigravityEnvBootstrap(receipt) {
|
|
21770
21792
|
const parent = receipt && typeof receipt === "object" && "parent" in receipt && typeof receipt.parent === "string" ? receipt.parent : tmpdir3();
|
|
21771
|
-
consume(receipt, fs, hashOf, parent, { join:
|
|
21793
|
+
consume(receipt, fs, hashOf, parent, { join: join14, dirname: dirname8, basename: basename5 });
|
|
21772
21794
|
}
|
|
21773
21795
|
function createAntigravityEnvBootstrap(command, env) {
|
|
21774
|
-
const directory = fs.mkdtempSync(
|
|
21796
|
+
const directory = fs.mkdtempSync(join14(tmpdir3(), "hanoman-ag-env-"));
|
|
21775
21797
|
fs.chmodSync(directory, 448);
|
|
21776
|
-
const path =
|
|
21798
|
+
const path = join14(directory, "launch.sh"), metadata = join14(directory, "receipt.json");
|
|
21777
21799
|
const cleaner = `const fs=require('node:fs'),{createHash}=require('node:crypto'),{join,dirname,basename}=require('node:path'); (${consume.toString()})(JSON.parse(fs.readFileSync(process.argv[1],'utf8')),fs,b=>createHash('sha256').update(b).digest('hex'),${JSON.stringify(dirname8(directory))},require('node:path'));`;
|
|
21778
21800
|
const exports = ["GEMINI_API_KEY", "GOOGLE_GEMINI_BASE_URL"].map((key) => env[key] === void 0 ? `unset ${key}` : `export ${key}=${quote(env[key])}`);
|
|
21779
21801
|
const content = `unset GEMINI_API_KEY GOOGLE_GEMINI_BASE_URL CLAUDE_CODE_OAUTH_TOKEN ANTHROPIC_API_KEY
|
|
@@ -21802,9 +21824,9 @@ var init_antigravity_env_bootstrap = __esm({
|
|
|
21802
21824
|
});
|
|
21803
21825
|
|
|
21804
21826
|
// src/services/secret-box.ts
|
|
21805
|
-
import { chmodSync as chmodSync6, existsSync as
|
|
21827
|
+
import { chmodSync as chmodSync6, existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
21806
21828
|
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
21807
|
-
import { dirname as dirname9, join as
|
|
21829
|
+
import { dirname as dirname9, join as join15 } from "node:path";
|
|
21808
21830
|
function isEncrypted(value) {
|
|
21809
21831
|
return value.startsWith(ENC_PREFIX);
|
|
21810
21832
|
}
|
|
@@ -21836,7 +21858,7 @@ function fromEnv(raw) {
|
|
|
21836
21858
|
return null;
|
|
21837
21859
|
}
|
|
21838
21860
|
function secretKeyPath() {
|
|
21839
|
-
return
|
|
21861
|
+
return join15(resolveHome(), "secret.key");
|
|
21840
21862
|
}
|
|
21841
21863
|
function secretKey() {
|
|
21842
21864
|
if (cached) return cached;
|
|
@@ -21848,7 +21870,7 @@ function secretKey() {
|
|
|
21848
21870
|
return key2;
|
|
21849
21871
|
}
|
|
21850
21872
|
const path = secretKeyPath();
|
|
21851
|
-
if (
|
|
21873
|
+
if (existsSync9(path)) {
|
|
21852
21874
|
const key2 = Buffer.from(readFileSync10(path, "utf8").trim(), "base64url");
|
|
21853
21875
|
if (key2.length !== KEY_BYTES) throw new Error(`kunci di ${path} rusak \u2014 panjangnya bukan 32 byte`);
|
|
21854
21876
|
cached = key2;
|
|
@@ -21972,12 +21994,12 @@ var init_antigravity_runtime = __esm({
|
|
|
21972
21994
|
});
|
|
21973
21995
|
|
|
21974
21996
|
// src/runner/deps.ts
|
|
21975
|
-
import { existsSync as
|
|
21976
|
-
import { dirname as dirname10, join as
|
|
21997
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
21998
|
+
import { dirname as dirname10, join as join16 } from "node:path";
|
|
21977
21999
|
function repoRootFrom(startDir) {
|
|
21978
22000
|
let dir2 = startDir;
|
|
21979
22001
|
for (let i = 0; i < 8; i++) {
|
|
21980
|
-
if (
|
|
22002
|
+
if (existsSync10(join16(dir2, "pnpm-workspace.yaml"))) return dir2;
|
|
21981
22003
|
const parent = dirname10(dir2);
|
|
21982
22004
|
if (parent === dir2) break;
|
|
21983
22005
|
dir2 = parent;
|
|
@@ -21994,19 +22016,19 @@ var init_deps = __esm({
|
|
|
21994
22016
|
|
|
21995
22017
|
// src/services/bundled-skills.ts
|
|
21996
22018
|
import { createHash as createHash7 } from "node:crypto";
|
|
21997
|
-
import { chmodSync as chmodSync7, cpSync as cpSync3, existsSync as
|
|
21998
|
-
import { join as
|
|
22019
|
+
import { chmodSync as chmodSync7, cpSync as cpSync3, existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync11, readdirSync as readdirSync9, renameSync as renameSync4, rmSync as rmSync6, statSync as statSync3, writeFileSync as writeFileSync11 } from "node:fs";
|
|
22020
|
+
import { join as join17, relative as relative5, sep as sep6 } from "node:path";
|
|
21999
22021
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
22000
22022
|
function bundledSkillsDir() {
|
|
22001
22023
|
const packed = packagedDir();
|
|
22002
|
-
return
|
|
22024
|
+
return existsSync11(packed) ? packed : join17(repoRoot(moduleDir()), "server", "skills");
|
|
22003
22025
|
}
|
|
22004
22026
|
function filesUnder(dir2) {
|
|
22005
22027
|
const out4 = [];
|
|
22006
22028
|
const walk = (d) => {
|
|
22007
|
-
for (const n2 of
|
|
22029
|
+
for (const n2 of readdirSync9(d).sort()) {
|
|
22008
22030
|
if (n2 === BUNDLED_MARKER || n2 === ".DS_Store") continue;
|
|
22009
|
-
const full =
|
|
22031
|
+
const full = join17(d, n2);
|
|
22010
22032
|
if (statSync3(full).isDirectory()) walk(full);
|
|
22011
22033
|
else out4.push(full);
|
|
22012
22034
|
}
|
|
@@ -22026,7 +22048,7 @@ function contentSha(dir2) {
|
|
|
22026
22048
|
}
|
|
22027
22049
|
function markerSha(dir2) {
|
|
22028
22050
|
try {
|
|
22029
|
-
const parsed = JSON.parse(readFileSync11(
|
|
22051
|
+
const parsed = JSON.parse(readFileSync11(join17(dir2, BUNDLED_MARKER), "utf8"));
|
|
22030
22052
|
const sha2 = parsed?.sha;
|
|
22031
22053
|
return typeof sha2 === "string" ? sha2 : null;
|
|
22032
22054
|
} catch {
|
|
@@ -22039,7 +22061,7 @@ function install(src, dest, sha2) {
|
|
|
22039
22061
|
cpSync3(src, tmp, { recursive: true });
|
|
22040
22062
|
for (const f of filesUnder(tmp))
|
|
22041
22063
|
if (relative5(tmp, f).split(sep6)[0] === "scripts") chmodSync7(f, statSync3(f).mode | 493);
|
|
22042
|
-
writeFileSync11(
|
|
22064
|
+
writeFileSync11(join17(tmp, BUNDLED_MARKER), `${JSON.stringify({ sha: sha2, seededAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
22043
22065
|
`);
|
|
22044
22066
|
rmSync6(dest, { recursive: true, force: true });
|
|
22045
22067
|
renameSync4(tmp, dest);
|
|
@@ -22053,10 +22075,10 @@ function seedBundledSkills(hanomanHome, o = {}) {
|
|
|
22053
22075
|
const root = hanomanSkillsDir(hanomanHome);
|
|
22054
22076
|
for (const name2 of names) {
|
|
22055
22077
|
try {
|
|
22056
|
-
const src =
|
|
22057
|
-
const dest =
|
|
22078
|
+
const src = join17(source, name2);
|
|
22079
|
+
const dest = join17(root, name2);
|
|
22058
22080
|
const bundleSha = contentSha(src);
|
|
22059
|
-
if (!
|
|
22081
|
+
if (!existsSync11(dest)) {
|
|
22060
22082
|
mkdirSync9(root, { recursive: true });
|
|
22061
22083
|
install(src, dest, bundleSha);
|
|
22062
22084
|
out4.seeded.push(name2);
|
|
@@ -22089,24 +22111,24 @@ var init_bundled_skills = __esm({
|
|
|
22089
22111
|
skillDirsIn = (source) => {
|
|
22090
22112
|
let names;
|
|
22091
22113
|
try {
|
|
22092
|
-
names =
|
|
22114
|
+
names = readdirSync9(source);
|
|
22093
22115
|
} catch {
|
|
22094
22116
|
return [];
|
|
22095
22117
|
}
|
|
22096
|
-
return names.filter((n2) => !n2.startsWith(".") &&
|
|
22118
|
+
return names.filter((n2) => !n2.startsWith(".") && existsSync11(join17(source, n2, "SKILL.md"))).sort();
|
|
22097
22119
|
};
|
|
22098
|
-
isBundledCopy = (dir2) =>
|
|
22120
|
+
isBundledCopy = (dir2) => existsSync11(join17(dir2, BUNDLED_MARKER));
|
|
22099
22121
|
}
|
|
22100
22122
|
});
|
|
22101
22123
|
|
|
22102
22124
|
// src/services/skill-inject.ts
|
|
22103
22125
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
22104
22126
|
import { createHash as createHash8 } from "node:crypto";
|
|
22105
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
22106
|
-
import { isAbsolute as isAbsolute8, join as
|
|
22127
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync12, lstatSync as lstatSync7, mkdirSync as mkdirSync10, readFileSync as readFileSync12, readlinkSync as readlinkSync2, symlinkSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "node:fs";
|
|
22128
|
+
import { isAbsolute as isAbsolute8, join as join18 } from "node:path";
|
|
22107
22129
|
function excludeFile(repoDir) {
|
|
22108
22130
|
const common = execFileSync4("git", ["rev-parse", "--git-common-dir"], { cwd: repoDir, env: agentEnvironment(null, process.env), encoding: "utf8" }).trim();
|
|
22109
|
-
return
|
|
22131
|
+
return join18(isAbsolute8(common) ? common : join18(repoDir, common), "info", "exclude");
|
|
22110
22132
|
}
|
|
22111
22133
|
function ownedSkills(tempDir) {
|
|
22112
22134
|
try {
|
|
@@ -22141,11 +22163,11 @@ function injectGlobalSkills(o) {
|
|
|
22141
22163
|
const todo = global2.filter((s2) => !taken.has(s2.name) && !(userOwned.has(s2.name) && isBundledCopy(s2.dir)));
|
|
22142
22164
|
if (!todo.length) return out4;
|
|
22143
22165
|
if (o.agent === "claude") {
|
|
22144
|
-
const root =
|
|
22166
|
+
const root = join18(o.tempDir, "skills-root");
|
|
22145
22167
|
try {
|
|
22146
|
-
mkdirSync10(
|
|
22168
|
+
mkdirSync10(join18(root, ".claude", "skills"), { recursive: true });
|
|
22147
22169
|
for (const s2 of todo) {
|
|
22148
|
-
const link2 =
|
|
22170
|
+
const link2 = join18(root, ".claude", "skills", s2.name);
|
|
22149
22171
|
if (!lstatSync7(link2, { throwIfNoEntry: false })) symlinkSync(s2.dir, link2);
|
|
22150
22172
|
out4.injected.push(s2.name);
|
|
22151
22173
|
}
|
|
@@ -22166,19 +22188,19 @@ function injectGlobalSkills(o) {
|
|
|
22166
22188
|
let ownedLink;
|
|
22167
22189
|
let ownedPair = "";
|
|
22168
22190
|
try {
|
|
22169
|
-
const link2 =
|
|
22191
|
+
const link2 = join18(o.cwd, ".agents", "skills", s2.name);
|
|
22170
22192
|
if (o.agent === "antigravity") {
|
|
22171
|
-
for (const dir2 of [
|
|
22193
|
+
for (const dir2 of [join18(o.cwd, ".agents"), join18(o.cwd, ".agents", "skills")]) {
|
|
22172
22194
|
const st2 = lstatSync7(dir2, { throwIfNoEntry: false });
|
|
22173
22195
|
if (st2 && (!st2.isDirectory() || st2.isSymbolicLink())) throw new Error("akar skill bukan direktori aman");
|
|
22174
22196
|
}
|
|
22175
22197
|
}
|
|
22176
22198
|
if (lstatSync7(link2, { throwIfNoEntry: false })) continue;
|
|
22177
|
-
mkdirSync10(
|
|
22199
|
+
mkdirSync10(join18(o.cwd, ".agents", "skills"), { recursive: true });
|
|
22178
22200
|
symlinkSync(s2.dir, link2);
|
|
22179
22201
|
ownedLink = link2;
|
|
22180
|
-
mkdirSync10(
|
|
22181
|
-
const cur =
|
|
22202
|
+
mkdirSync10(join18(exclude, ".."), { recursive: true });
|
|
22203
|
+
const cur = existsSync12(exclude) ? readFileSync12(exclude, "utf8") : "";
|
|
22182
22204
|
const line = excludeLine(s2.name);
|
|
22183
22205
|
const lines2 = cur.split("\n");
|
|
22184
22206
|
const hasOperatorLine = lines2.some((entry2, index) => entry2 === line && !lines2[index - 1]?.startsWith(`${INJECT_MARKER}:antigravity:`));
|
|
@@ -22221,7 +22243,7 @@ ${line}
|
|
|
22221
22243
|
function unexcludeInjected(repoDir, name2) {
|
|
22222
22244
|
try {
|
|
22223
22245
|
const f = excludeFile(repoDir);
|
|
22224
|
-
if (!
|
|
22246
|
+
if (!existsSync12(f)) return;
|
|
22225
22247
|
const line = excludeLine(name2);
|
|
22226
22248
|
writeFileSync12(f, readFileSync12(f, "utf8").split("\n").filter((l) => l !== line).join("\n"));
|
|
22227
22249
|
} catch {
|
|
@@ -22235,31 +22257,31 @@ var init_skill_inject = __esm({
|
|
|
22235
22257
|
init_bundled_skills();
|
|
22236
22258
|
INJECT_MARKER = "# hanoman:skill-inject";
|
|
22237
22259
|
excludeLine = (name2) => `/.agents/skills/${name2}`;
|
|
22238
|
-
manifestPath = (tempDir) =>
|
|
22260
|
+
manifestPath = (tempDir) => join18(tempDir, "antigravity-skills.json");
|
|
22239
22261
|
}
|
|
22240
22262
|
});
|
|
22241
22263
|
|
|
22242
22264
|
// src/services/antigravity-trust.ts
|
|
22243
22265
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
22244
22266
|
import { closeSync as closeSync2, lstatSync as lstatSync8, mkdirSync as mkdirSync11, openSync as openSync2, readFileSync as readFileSync13, realpathSync as realpathSync6, renameSync as renameSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync13 } from "node:fs";
|
|
22245
|
-
import { homedir as
|
|
22246
|
-
import { join as
|
|
22267
|
+
import { homedir as homedir5 } from "node:os";
|
|
22268
|
+
import { join as join19 } from "node:path";
|
|
22247
22269
|
function safeOwned(path, directory) {
|
|
22248
22270
|
const st2 = lstatSync8(path);
|
|
22249
22271
|
if (st2.isSymbolicLink() || (directory ? !st2.isDirectory() : !st2.isFile()) || process.getuid && st2.uid !== process.getuid()) throw new Error("unsafe path");
|
|
22250
22272
|
}
|
|
22251
|
-
function ensureAntigravityTrust(cwd, home3 =
|
|
22273
|
+
function ensureAntigravityTrust(cwd, home3 = homedir5()) {
|
|
22252
22274
|
let temp;
|
|
22253
22275
|
let fd;
|
|
22254
22276
|
try {
|
|
22255
22277
|
const canonical = realpathSync6(cwd);
|
|
22256
22278
|
mkdirSync11(home3, { recursive: true });
|
|
22257
22279
|
const root = realpathSync6(home3);
|
|
22258
|
-
for (const dir2 of [
|
|
22280
|
+
for (const dir2 of [join19(root, ".gemini"), join19(root, ".gemini", "antigravity-cli")]) {
|
|
22259
22281
|
if (!lstatSync8(dir2, { throwIfNoEntry: false })) mkdirSync11(dir2, { mode: 448 });
|
|
22260
22282
|
safeOwned(dir2, true);
|
|
22261
22283
|
}
|
|
22262
|
-
const file =
|
|
22284
|
+
const file = join19(root, ".gemini", "antigravity-cli", "settings.json");
|
|
22263
22285
|
const original = lstatSync8(file, { throwIfNoEntry: false });
|
|
22264
22286
|
let settings = {};
|
|
22265
22287
|
if (original) {
|
|
@@ -22333,14 +22355,14 @@ var init_local_binding = __esm({
|
|
|
22333
22355
|
});
|
|
22334
22356
|
|
|
22335
22357
|
// src/services/skill-library.ts
|
|
22336
|
-
import { join as
|
|
22337
|
-
import { existsSync as
|
|
22358
|
+
import { join as join20 } from "node:path";
|
|
22359
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
22338
22360
|
async function libraryAll() {
|
|
22339
22361
|
const global2 = scanGlobalSkills(opts());
|
|
22340
22362
|
const projects = await prisma.project.findMany({ orderBy: { name: "asc" }, select: { id: true, name: true } });
|
|
22341
22363
|
const groups = await Promise.all(projects.map(async (p3) => {
|
|
22342
22364
|
const repo = await resolveRepoDir(p3.id);
|
|
22343
|
-
if (!repo || !
|
|
22365
|
+
if (!repo || !existsSync13(repo)) return { projectId: p3.id, name: p3.name, skills: [], error: "repo tidak ditemukan" };
|
|
22344
22366
|
return { projectId: p3.id, name: p3.name, skills: scanProjectSkills(p3.id, repo, opts()) };
|
|
22345
22367
|
}));
|
|
22346
22368
|
return { global: global2, projects: groups };
|
|
@@ -22363,17 +22385,17 @@ async function targetParent(layer, source, projectId) {
|
|
|
22363
22385
|
if (layer === "user") {
|
|
22364
22386
|
const s2 = source ?? ".claude";
|
|
22365
22387
|
if (!USER_SOURCES.includes(s2)) throw new SkillError(400, "source user harus .claude/.codex/.agents/.gemini/antigravity-cli");
|
|
22366
|
-
if (s2 === ".agents") return
|
|
22367
|
-
if (s2 === ".gemini/antigravity-cli") return
|
|
22368
|
-
return
|
|
22388
|
+
if (s2 === ".agents") return join20(agentsSkillHome(process.env), "skills");
|
|
22389
|
+
if (s2 === ".gemini/antigravity-cli") return join20(agentSkillHome("antigravity", process.env), "skills");
|
|
22390
|
+
return join20(agentSkillHome(s2 === ".codex" ? "codex" : "claude", process.env), "skills");
|
|
22369
22391
|
}
|
|
22370
22392
|
if (layer === "project") {
|
|
22371
22393
|
if (!projectId) throw new SkillError(400, "projectId wajib untuk lapis project");
|
|
22372
22394
|
const s2 = source ?? ".claude";
|
|
22373
22395
|
if (!PROJECT_SOURCES.includes(s2)) throw new SkillError(400, "source project harus .claude/.agents/.codex");
|
|
22374
22396
|
const repo = await resolveRepoDir(projectId);
|
|
22375
|
-
if (!repo || !
|
|
22376
|
-
return
|
|
22397
|
+
if (!repo || !existsSync13(repo)) throw new SkillError(404, "repo project tidak ditemukan");
|
|
22398
|
+
return join20(repo, s2, "skills");
|
|
22377
22399
|
}
|
|
22378
22400
|
throw new SkillError(400, "layer harus hanoman, user, atau project");
|
|
22379
22401
|
}
|
|
@@ -48487,7 +48509,7 @@ var init_safe_outbound_request = __esm({
|
|
|
48487
48509
|
// src/services/uploads.ts
|
|
48488
48510
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
48489
48511
|
import { mkdir, writeFile, readFile as readFile2, unlink, rm } from "node:fs/promises";
|
|
48490
|
-
import { join as
|
|
48512
|
+
import { join as join21, resolve as resolve9 } from "node:path";
|
|
48491
48513
|
function extFor(mimeType) {
|
|
48492
48514
|
return EXT[mimeType] ?? ".bin";
|
|
48493
48515
|
}
|
|
@@ -48498,17 +48520,17 @@ async function saveUpload(buf, mimeType) {
|
|
|
48498
48520
|
const dir2 = uploadDir();
|
|
48499
48521
|
await mkdir(dir2, { recursive: true, mode: 448 });
|
|
48500
48522
|
const storageKey = `${randomUUID3()}${extFor(mimeType)}`;
|
|
48501
|
-
await writeFile(
|
|
48523
|
+
await writeFile(join21(dir2, storageKey), buf, { mode: 384 });
|
|
48502
48524
|
return { storageKey, size: buf.length };
|
|
48503
48525
|
}
|
|
48504
48526
|
function sessionUploadDir(sessionId2) {
|
|
48505
48527
|
if (!SESSION_ID.test(sessionId2)) throw new Error(`sessionId tak sah: ${sessionId2}`);
|
|
48506
|
-
return
|
|
48528
|
+
return join21(uploadDir(), "terminal", sessionId2);
|
|
48507
48529
|
}
|
|
48508
48530
|
async function saveSessionUpload(sessionId2, buf, mimeType) {
|
|
48509
48531
|
const dir2 = sessionUploadDir(sessionId2);
|
|
48510
48532
|
await mkdir(dir2, { recursive: true, mode: 448 });
|
|
48511
|
-
const path =
|
|
48533
|
+
const path = join21(dir2, `${randomUUID3()}${extFor(mimeType)}`);
|
|
48512
48534
|
await writeFile(path, buf, { mode: 384 });
|
|
48513
48535
|
return { path, size: buf.length };
|
|
48514
48536
|
}
|
|
@@ -48524,11 +48546,11 @@ async function dropSessionUploads(sessionId2) {
|
|
|
48524
48546
|
}
|
|
48525
48547
|
async function readUpload(storageKey) {
|
|
48526
48548
|
const safe3 = storageKey.replace(/[/\\]/g, "");
|
|
48527
|
-
return readFile2(
|
|
48549
|
+
return readFile2(join21(uploadDir(), safe3));
|
|
48528
48550
|
}
|
|
48529
48551
|
async function readUploadOrFetch(storageKey) {
|
|
48530
48552
|
const safe3 = storageKey.replace(/[/\\]/g, "");
|
|
48531
|
-
const target3 =
|
|
48553
|
+
const target3 = join21(uploadDir(), safe3);
|
|
48532
48554
|
try {
|
|
48533
48555
|
return await readFile2(target3);
|
|
48534
48556
|
} catch {
|
|
@@ -48555,7 +48577,7 @@ async function readUploadOrFetch(storageKey) {
|
|
|
48555
48577
|
}
|
|
48556
48578
|
async function deleteUpload(storageKey) {
|
|
48557
48579
|
const safe3 = storageKey.replace(/[/\\]/g, "");
|
|
48558
|
-
await unlink(
|
|
48580
|
+
await unlink(join21(uploadDir(), safe3)).catch(() => {
|
|
48559
48581
|
});
|
|
48560
48582
|
}
|
|
48561
48583
|
var EXT, SESSION_ID;
|
|
@@ -49001,14 +49023,14 @@ var init_session_sandbox = __esm({
|
|
|
49001
49023
|
});
|
|
49002
49024
|
|
|
49003
49025
|
// src/services/session-event-spool.ts
|
|
49004
|
-
import { join as
|
|
49026
|
+
import { join as join22 } from "node:path";
|
|
49005
49027
|
var sessionEventSpoolRoot, sessionEventDir;
|
|
49006
49028
|
var init_session_event_spool = __esm({
|
|
49007
49029
|
"src/services/session-event-spool.ts"() {
|
|
49008
49030
|
"use strict";
|
|
49009
49031
|
init_src2();
|
|
49010
|
-
sessionEventSpoolRoot = (env = process.env) =>
|
|
49011
|
-
sessionEventDir = (sessionId2, env = process.env) =>
|
|
49032
|
+
sessionEventSpoolRoot = (env = process.env) => join22(resolveHome(env), "session-events");
|
|
49033
|
+
sessionEventDir = (sessionId2, env = process.env) => join22(sessionEventSpoolRoot(env), sessionId2);
|
|
49012
49034
|
}
|
|
49013
49035
|
});
|
|
49014
49036
|
|
|
@@ -49535,6 +49557,9 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49535
49557
|
}
|
|
49536
49558
|
const agent = opts2.agent ?? "claude";
|
|
49537
49559
|
const account = opts2.command ? null : accountResolver(agent, opts2.accountId);
|
|
49560
|
+
const convId = !opts2.command && agent === "claude" && !opts2.specId ? opts2.agentSessionId ?? randomUUID4() : void 0;
|
|
49561
|
+
const canResumeConv = !!convId && !!opts2.agentSessionId && !!opts2.resumeAgentSession && claudeConversationExists(convId, account?.env?.CLAUDE_CONFIG_DIR);
|
|
49562
|
+
const convArg = convId ? `${canResumeConv ? "--resume" : "--session-id"} ${sq(convId)}` : "";
|
|
49538
49563
|
let argv;
|
|
49539
49564
|
if (opts2.command) {
|
|
49540
49565
|
argv = opts2.command.map(sq).join(" ");
|
|
@@ -49577,7 +49602,7 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49577
49602
|
process.stderr.write(`hanoman: penyuntikan skill global gagal: ${e.message}
|
|
49578
49603
|
`);
|
|
49579
49604
|
}
|
|
49580
|
-
argv = (agent === "antigravity" ? [sq(agentBin(agent)), flags, ...promptArg ? ["--prompt-interactive", promptArg] : []] : [sq(agentBin(agent)), promptArg, flags, agentsArg, nativeAgentArgs, skillsArg]).filter(Boolean).join(" ");
|
|
49605
|
+
argv = (agent === "antigravity" ? [sq(agentBin(agent)), flags, ...promptArg ? ["--prompt-interactive", promptArg] : []] : [sq(agentBin(agent)), promptArg, flags, convArg, agentsArg, nativeAgentArgs, skillsArg]).filter(Boolean).join(" ");
|
|
49581
49606
|
}
|
|
49582
49607
|
const envPairs = [];
|
|
49583
49608
|
if (!opts2.command && agent === "claude") {
|
|
@@ -49750,7 +49775,8 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49750
49775
|
effort: opts2.effort,
|
|
49751
49776
|
branch: opts2.branch,
|
|
49752
49777
|
cwd,
|
|
49753
|
-
accountId: account?.id
|
|
49778
|
+
accountId: account?.id,
|
|
49779
|
+
...convId ? { agentSessionId: convId } : {}
|
|
49754
49780
|
});
|
|
49755
49781
|
return {
|
|
49756
49782
|
id: id2,
|
|
@@ -50083,6 +50109,7 @@ var init_pty = __esm({
|
|
|
50083
50109
|
init_src();
|
|
50084
50110
|
init_session_phases();
|
|
50085
50111
|
init_session_id();
|
|
50112
|
+
init_claude_conversation();
|
|
50086
50113
|
init_antigravity_env_bootstrap();
|
|
50087
50114
|
init_antigravity_runtime();
|
|
50088
50115
|
init_skill_inject();
|
|
@@ -50388,7 +50415,7 @@ var init_redact_known = __esm({
|
|
|
50388
50415
|
// src/services/transcript-store.ts
|
|
50389
50416
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
50390
50417
|
import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile3, unlink as unlink2, readdir as readdir2, stat as stat3 } from "node:fs/promises";
|
|
50391
|
-
import { join as
|
|
50418
|
+
import { join as join23, resolve as resolve10, basename as basename6 } from "node:path";
|
|
50392
50419
|
function transcriptDir() {
|
|
50393
50420
|
return resolve10(effectiveStr("HANOMAN_TRANSCRIPT_DIR")?.trim() || resolveDataDirs().transcripts);
|
|
50394
50421
|
}
|
|
@@ -50408,13 +50435,13 @@ async function saveTranscript(text2) {
|
|
|
50408
50435
|
const dir2 = transcriptDir();
|
|
50409
50436
|
await mkdir2(dir2, { recursive: true, mode: 448 });
|
|
50410
50437
|
const key = `${randomUUID5()}.log`;
|
|
50411
|
-
await writeFile2(
|
|
50438
|
+
await writeFile2(join23(dir2, key), body, { encoding: "utf8", mode: 384 });
|
|
50412
50439
|
return { key, bytes: Buffer.byteLength(body, "utf8"), truncated };
|
|
50413
50440
|
}
|
|
50414
50441
|
async function readTranscript(key) {
|
|
50415
50442
|
if (!key) return null;
|
|
50416
50443
|
try {
|
|
50417
|
-
return await readFile3(
|
|
50444
|
+
return await readFile3(join23(transcriptDir(), basename6(key)), "utf8");
|
|
50418
50445
|
} catch {
|
|
50419
50446
|
return null;
|
|
50420
50447
|
}
|
|
@@ -50422,7 +50449,7 @@ async function readTranscript(key) {
|
|
|
50422
50449
|
async function deleteTranscript(key) {
|
|
50423
50450
|
if (!key) return;
|
|
50424
50451
|
try {
|
|
50425
|
-
await unlink2(
|
|
50452
|
+
await unlink2(join23(transcriptDir(), basename6(key)));
|
|
50426
50453
|
} catch (e) {
|
|
50427
50454
|
if (e.code !== "ENOENT") throw e;
|
|
50428
50455
|
}
|
|
@@ -50440,7 +50467,7 @@ async function listTranscripts() {
|
|
|
50440
50467
|
for (const name2 of names) {
|
|
50441
50468
|
if (!name2.endsWith(".log")) continue;
|
|
50442
50469
|
try {
|
|
50443
|
-
rows.push({ key: name2, mtimeMs: (await stat3(
|
|
50470
|
+
rows.push({ key: name2, mtimeMs: (await stat3(join23(dir2, name2))).mtimeMs });
|
|
50444
50471
|
} catch {
|
|
50445
50472
|
}
|
|
50446
50473
|
}
|
|
@@ -50771,6 +50798,8 @@ var init_settings3 = __esm({
|
|
|
50771
50798
|
notifyDecisionSound: "alert",
|
|
50772
50799
|
notifyDecisionTelegram: true,
|
|
50773
50800
|
// ADR-0226 · tak berefek tanpa bot token + allowlist
|
|
50801
|
+
autoResumeSessions: false,
|
|
50802
|
+
// ADR-0235 · auto-resume semua sesi setelah reboot/crash (opt-in)
|
|
50774
50803
|
agentAccessEnabled: false,
|
|
50775
50804
|
// SPEC-257 · akses AI agent off sampai dibuka manusia
|
|
50776
50805
|
scheduler: SCHEDULER_DEFAULTS,
|
|
@@ -55737,11 +55766,11 @@ var init_tombstone = __esm({
|
|
|
55737
55766
|
// src/services/qa-attachment-sync.ts
|
|
55738
55767
|
import { createHash as createHash9, randomUUID as randomUUID7 } from "node:crypto";
|
|
55739
55768
|
import { chmod, mkdir as mkdir4, rename as rename2, stat as stat4, writeFile as writeFile4 } from "node:fs/promises";
|
|
55740
|
-
import { join as
|
|
55769
|
+
import { join as join25 } from "node:path";
|
|
55741
55770
|
async function qaBytesPresent(storageKey) {
|
|
55742
55771
|
if (!QA_STORAGE_KEY.test(storageKey)) return false;
|
|
55743
55772
|
try {
|
|
55744
|
-
return (await stat4(
|
|
55773
|
+
return (await stat4(join25(uploadDir(), storageKey))).isFile();
|
|
55745
55774
|
} catch {
|
|
55746
55775
|
return false;
|
|
55747
55776
|
}
|
|
@@ -55766,10 +55795,10 @@ async function storeQaBytes(storageKey, buf) {
|
|
|
55766
55795
|
if (!QA_STORAGE_KEY.test(storageKey)) throw new Error("storageKey tak sah");
|
|
55767
55796
|
const dir2 = uploadDir();
|
|
55768
55797
|
await mkdir4(dir2, { recursive: true, mode: 448 });
|
|
55769
|
-
const tmp =
|
|
55798
|
+
const tmp = join25(dir2, `${storageKey}.tmp-${randomUUID7()}`);
|
|
55770
55799
|
await writeFile4(tmp, buf, { mode: 384 });
|
|
55771
|
-
await rename2(tmp,
|
|
55772
|
-
await chmod(
|
|
55800
|
+
await rename2(tmp, join25(dir2, storageKey));
|
|
55801
|
+
await chmod(join25(dir2, storageKey), 384).catch(() => {
|
|
55773
55802
|
});
|
|
55774
55803
|
}
|
|
55775
55804
|
var QA_STORAGE_KEY, QA_SYNC_MAX_BYTES, SNIFFABLE2, isUtf8Text2;
|
|
@@ -56420,7 +56449,7 @@ var init_sync_notify = __esm({
|
|
|
56420
56449
|
// src/services/safe-repo-path.ts
|
|
56421
56450
|
import { constants as constants3 } from "node:fs";
|
|
56422
56451
|
import { lstat, mkdir as mkdir5, open as open2, realpath, rename as rename3, unlink as unlink4 } from "node:fs/promises";
|
|
56423
|
-
import { isAbsolute as isAbsolute10, join as
|
|
56452
|
+
import { isAbsolute as isAbsolute10, join as join26, relative as relative6, resolve as resolve13, sep as sep7 } from "node:path";
|
|
56424
56453
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
56425
56454
|
import { closeSync as closeSync3, fstatSync as fstatSync2, lstatSync as lstatSync9, mkdirSync as mkdirSync13, openSync as openSync3, readFileSync as readFileSync15, realpathSync as realpathSync7, renameSync as renameSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync15 } from "node:fs";
|
|
56426
56455
|
function components(rel) {
|
|
@@ -56444,7 +56473,7 @@ async function ensureRepoParents(root, rel) {
|
|
|
56444
56473
|
const parts = components(rel).slice(0, -1);
|
|
56445
56474
|
let current = base3;
|
|
56446
56475
|
for (const part of parts) {
|
|
56447
|
-
current =
|
|
56476
|
+
current = join26(current, part);
|
|
56448
56477
|
try {
|
|
56449
56478
|
await mkdir5(current, { mode: 448 });
|
|
56450
56479
|
} catch (error) {
|
|
@@ -56461,7 +56490,7 @@ function ensureRepoParentsSync(root, rel) {
|
|
|
56461
56490
|
const parts = components(rel).slice(0, -1);
|
|
56462
56491
|
let current = base3;
|
|
56463
56492
|
for (const part of parts) {
|
|
56464
|
-
current =
|
|
56493
|
+
current = join26(current, part);
|
|
56465
56494
|
try {
|
|
56466
56495
|
mkdirSync13(current, { mode: 448 });
|
|
56467
56496
|
} catch (error) {
|
|
@@ -56477,7 +56506,7 @@ async function resolveRepoEntry(root, rel, opts2 = {}) {
|
|
|
56477
56506
|
const parts = components(rel);
|
|
56478
56507
|
let current = base3;
|
|
56479
56508
|
for (let i = 0; i < parts.length; i++) {
|
|
56480
|
-
current =
|
|
56509
|
+
current = join26(current, parts[i]);
|
|
56481
56510
|
const stat8 = await lstat(current).catch((error) => {
|
|
56482
56511
|
if (error.code === "ENOENT" && opts2.allowMissingTail) return null;
|
|
56483
56512
|
if (error.code === "ENOENT" && opts2.allowMissingFinal && i === parts.length - 1) return null;
|
|
@@ -56498,7 +56527,7 @@ function assertSafeRepoPathSync(root, rel, allowMissingFinal = false, allowMissi
|
|
|
56498
56527
|
const parts = components(rel);
|
|
56499
56528
|
let current = base3;
|
|
56500
56529
|
for (let i = 0; i < parts.length; i++) {
|
|
56501
|
-
current =
|
|
56530
|
+
current = join26(current, parts[i]);
|
|
56502
56531
|
try {
|
|
56503
56532
|
const stat8 = lstatSync9(current);
|
|
56504
56533
|
if (stat8.isSymbolicLink()) denied("symlink");
|
|
@@ -56528,7 +56557,7 @@ function writeRepoFileAtomicSync(root, rel, data) {
|
|
|
56528
56557
|
const parent = resolve13(path, "..");
|
|
56529
56558
|
const parentRel = relative6(realpathSync7(root), parent);
|
|
56530
56559
|
if (parentRel) assertSafeRepoPathSync(root, parentRel);
|
|
56531
|
-
const temp =
|
|
56560
|
+
const temp = join26(parent, `.hanoman-${randomUUID8()}.tmp`);
|
|
56532
56561
|
const fd = openSync3(temp, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW ?? 0), 384);
|
|
56533
56562
|
try {
|
|
56534
56563
|
writeFileSync15(fd, data);
|
|
@@ -56575,7 +56604,7 @@ async function writeRepoFileAtomic(root, rel, data) {
|
|
|
56575
56604
|
await mkdir5(entry2.parent, { recursive: false, mode: 448 }).catch((error) => {
|
|
56576
56605
|
if (error.code !== "EEXIST") throw error;
|
|
56577
56606
|
});
|
|
56578
|
-
const temp =
|
|
56607
|
+
const temp = join26(entry2.parent, `.hanoman-${randomUUID8()}.tmp`);
|
|
56579
56608
|
const handle = await open2(temp, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW ?? 0), 384);
|
|
56580
56609
|
try {
|
|
56581
56610
|
await handle.writeFile(data);
|
|
@@ -56612,7 +56641,7 @@ var init_safe_repo_path = __esm({
|
|
|
56612
56641
|
import { execFile as execFile5 } from "node:child_process";
|
|
56613
56642
|
import { promisify as promisify4 } from "node:util";
|
|
56614
56643
|
import { rmSync as rmSync9 } from "node:fs";
|
|
56615
|
-
import { join as
|
|
56644
|
+
import { join as join27 } from "node:path";
|
|
56616
56645
|
async function worktreeForBranch(repoDir, branch) {
|
|
56617
56646
|
let path = null;
|
|
56618
56647
|
for (const line of (await out(repoDir, ["worktree", "list", "--porcelain"])).split("\n")) {
|
|
@@ -56645,7 +56674,7 @@ async function integrateBranch(repoDir, src, op, target3) {
|
|
|
56645
56674
|
const tgt = await resolveTarget(repoDir, target3);
|
|
56646
56675
|
if (!tgt) return { status: "error", code: 400, error: `target "${target3}" tidak dikenal` };
|
|
56647
56676
|
await sh(repoDir, ["fetch", "origin"]);
|
|
56648
|
-
const wt =
|
|
56677
|
+
const wt = join27(repoDir, ".worktrees", `merge-${sanitize(src.mergeId)}`);
|
|
56649
56678
|
await reclaim(repoDir, wt);
|
|
56650
56679
|
const baseRef = op === "merge" ? tgt.ref : source;
|
|
56651
56680
|
const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `${baseRef}^{commit}`]);
|
|
@@ -56706,7 +56735,7 @@ async function mergeIntoCurrent(repoDir, source, opts2 = {}) {
|
|
|
56706
56735
|
const src = await resolveGraphSource(repoDir, source);
|
|
56707
56736
|
if (!src) return { status: "error", code: 400, error: `source "${source}" tak dikenal` };
|
|
56708
56737
|
await sh(repoDir, ["fetch", "origin"]);
|
|
56709
|
-
const wt =
|
|
56738
|
+
const wt = join27(repoDir, ".worktrees", `merge-${sanitize(current)}`);
|
|
56710
56739
|
await reclaim(repoDir, wt);
|
|
56711
56740
|
const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `refs/heads/${current}^{commit}`]);
|
|
56712
56741
|
if (!await ok(repoDir, ["worktree", "add", "--detach", "-q", wt, baseSha]))
|
|
@@ -56744,7 +56773,7 @@ async function replayCurrent(repoDir, source, cmd) {
|
|
|
56744
56773
|
const current = await out(repoDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
56745
56774
|
if (!current || current === "HEAD")
|
|
56746
56775
|
return { status: "error", code: 409, error: "HEAD detached \u2014 checkout sebuah branch dulu" };
|
|
56747
|
-
const wt =
|
|
56776
|
+
const wt = join27(repoDir, ".worktrees", `merge-${sanitize(current)}`);
|
|
56748
56777
|
await reclaim(repoDir, wt);
|
|
56749
56778
|
const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `refs/heads/${current}^{commit}`]);
|
|
56750
56779
|
if (!await ok(repoDir, ["worktree", "add", "--detach", "-q", wt, baseSha]))
|
|
@@ -56804,11 +56833,11 @@ import { execFile as execFile6 } from "node:child_process";
|
|
|
56804
56833
|
import { promisify as promisify5 } from "node:util";
|
|
56805
56834
|
import { mkdtemp, copyFile, rm as rm2 } from "node:fs/promises";
|
|
56806
56835
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
56807
|
-
import { join as
|
|
56836
|
+
import { join as join28, resolve as resolve15 } from "node:path";
|
|
56808
56837
|
async function withTempIndex(wt, fn) {
|
|
56809
56838
|
const idx = (await exec4("git", ["rev-parse", "--git-path", "index"], { cwd: wt, ...GIT3 })).stdout.trim();
|
|
56810
|
-
const dir2 = await mkdtemp(
|
|
56811
|
-
const tmp =
|
|
56839
|
+
const dir2 = await mkdtemp(join28(tmpdir5(), "hanoman-idx-"));
|
|
56840
|
+
const tmp = join28(dir2, "index");
|
|
56812
56841
|
await copyFile(resolve15(wt, idx), tmp);
|
|
56813
56842
|
const env = { ...process.env, GIT_INDEX_FILE: tmp };
|
|
56814
56843
|
try {
|
|
@@ -56954,7 +56983,7 @@ var init_spec_review2 = __esm({
|
|
|
56954
56983
|
GIT3 = { maxBuffer: 1 << 24 };
|
|
56955
56984
|
MAX = 256 * 1024;
|
|
56956
56985
|
emptySpecReview = (reason) => ({ base: "", files: [], changed: [], empty: true, reason });
|
|
56957
|
-
worktreeDir = (repoDir, specId) =>
|
|
56986
|
+
worktreeDir = (repoDir, specId) => join28(repoDir, ".worktrees", specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
|
|
56958
56987
|
splitZ = (s2) => s2.split("\0").filter(Boolean);
|
|
56959
56988
|
EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
56960
56989
|
}
|
|
@@ -57531,6 +57560,12 @@ async function recordFailure(specId, title, projectId, reason) {
|
|
|
57531
57560
|
}).catch(() => {
|
|
57532
57561
|
});
|
|
57533
57562
|
}
|
|
57563
|
+
async function recordFreeResumeIssue(sessionId2, title, projectId, reason) {
|
|
57564
|
+
await prisma.notification.create({
|
|
57565
|
+
data: { type: "fail", key: `fail:resume:${sessionId2}:${Date.now()}`, specId: null, sessionId: sessionId2, title: `Gagal: ${title} \u2014 ${reason}`, projectId }
|
|
57566
|
+
}).catch(() => {
|
|
57567
|
+
});
|
|
57568
|
+
}
|
|
57534
57569
|
async function recordAutoMerge(specId, projectId, title) {
|
|
57535
57570
|
const sessionId2 = specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_");
|
|
57536
57571
|
await prisma.notification.create({
|
|
@@ -57655,7 +57690,7 @@ var init_notifications2 = __esm({
|
|
|
57655
57690
|
|
|
57656
57691
|
// src/services/codex-trust.ts
|
|
57657
57692
|
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync14, readFileSync as readFileSync16, realpathSync as realpathSync9 } from "node:fs";
|
|
57658
|
-
import { homedir as
|
|
57693
|
+
import { homedir as homedir6 } from "node:os";
|
|
57659
57694
|
function ensureCodexTrust(repoDir, home3) {
|
|
57660
57695
|
const path = codexConfigPath(home3);
|
|
57661
57696
|
let canonical = repoDir;
|
|
@@ -57684,7 +57719,7 @@ var codexHome, codexConfigPath;
|
|
|
57684
57719
|
var init_codex_trust = __esm({
|
|
57685
57720
|
"src/services/codex-trust.ts"() {
|
|
57686
57721
|
"use strict";
|
|
57687
|
-
codexHome = (home3) => home3 ?? process.env.CODEX_HOME ?? `${
|
|
57722
|
+
codexHome = (home3) => home3 ?? process.env.CODEX_HOME ?? `${homedir6()}/.codex`;
|
|
57688
57723
|
codexConfigPath = (home3) => `${codexHome(home3)}/config.toml`;
|
|
57689
57724
|
}
|
|
57690
57725
|
});
|
|
@@ -57812,7 +57847,7 @@ var init_device_sockets = __esm({
|
|
|
57812
57847
|
// src/services/exit-guard.ts
|
|
57813
57848
|
import { spawn as spawn3 } from "node:child_process";
|
|
57814
57849
|
import { writeFileSync as writeFileSync16 } from "node:fs";
|
|
57815
|
-
import { join as
|
|
57850
|
+
import { join as join32 } from "node:path";
|
|
57816
57851
|
function watchdogScript(pid, graceMs) {
|
|
57817
57852
|
const secs = Math.max(1, Math.ceil(graceMs / 1e3));
|
|
57818
57853
|
return `s=$(ps -o lstart= -p ${pid}); sleep ${secs}; [ -n "$s" ] && [ "$(ps -o lstart= -p ${pid})" = "$s" ] && kill -9 ${pid}`;
|
|
@@ -57829,7 +57864,7 @@ function exitGuarded(code, d = {}) {
|
|
|
57829
57864
|
const pid = d.pid ?? process.pid;
|
|
57830
57865
|
try {
|
|
57831
57866
|
const write2 = d.writeIntent ?? ((p3, t) => writeFileSync16(p3, t, { mode: 384 }));
|
|
57832
|
-
write2(
|
|
57867
|
+
write2(join32(d.home ?? resolveHome(), EXIT_INTENT_FILE), formatExitIntent(pid, code));
|
|
57833
57868
|
} catch {
|
|
57834
57869
|
}
|
|
57835
57870
|
try {
|
|
@@ -58306,7 +58341,7 @@ var init_remote_session = __esm({
|
|
|
58306
58341
|
|
|
58307
58342
|
// src/services/spec-attachment-dir.ts
|
|
58308
58343
|
import { mkdir as mkdir6, readdir as readdir5, readFile as readFile6, rm as rm4, writeFile as writeFile5 } from "node:fs/promises";
|
|
58309
|
-
import { join as
|
|
58344
|
+
import { join as join33 } from "node:path";
|
|
58310
58345
|
function uniqueName(taken, filename) {
|
|
58311
58346
|
if (!taken.has(filename)) {
|
|
58312
58347
|
taken.add(filename);
|
|
@@ -58354,19 +58389,19 @@ async function syncSpecAttachmentsDir(specId, projectId) {
|
|
|
58354
58389
|
for (const a of rows) {
|
|
58355
58390
|
let bytes;
|
|
58356
58391
|
try {
|
|
58357
|
-
bytes = await readFile6(
|
|
58392
|
+
bytes = await readFile6(join33(uploadDir(), a.storageKey));
|
|
58358
58393
|
} catch {
|
|
58359
58394
|
continue;
|
|
58360
58395
|
}
|
|
58361
58396
|
const filename = uniqueName(taken, a.filename);
|
|
58362
|
-
const path =
|
|
58397
|
+
const path = join33(dir2, filename);
|
|
58363
58398
|
await writeFile5(path, bytes, { mode: 384 });
|
|
58364
58399
|
items.push({ filename, mimeType: a.mimeType, size: a.size, path });
|
|
58365
58400
|
}
|
|
58366
|
-
await writeFile5(
|
|
58401
|
+
await writeFile5(join33(dir2, INDEX), renderIndex(specId, items), { mode: 384 });
|
|
58367
58402
|
const keep = /* @__PURE__ */ new Set([INDEX, ...items.map((a) => a.filename)]);
|
|
58368
58403
|
for (const name2 of await readdir5(dir2)) {
|
|
58369
|
-
if (!keep.has(name2)) await rm4(
|
|
58404
|
+
if (!keep.has(name2)) await rm4(join33(dir2, name2), { recursive: true, force: true }).catch(() => {
|
|
58370
58405
|
});
|
|
58371
58406
|
}
|
|
58372
58407
|
return items;
|
|
@@ -58385,7 +58420,7 @@ var init_spec_attachment_dir = __esm({
|
|
|
58385
58420
|
init_local_binding();
|
|
58386
58421
|
init_session_id();
|
|
58387
58422
|
init_uploads();
|
|
58388
|
-
specAttachmentsDir = (repoDir, sessionId2) =>
|
|
58423
|
+
specAttachmentsDir = (repoDir, sessionId2) => join33(repoDir, ".worktrees", ".attachments", sessionId2);
|
|
58389
58424
|
INDEX = "INDEX.md";
|
|
58390
58425
|
humanSize = (n2) => n2 >= 1024 * 1024 ? `${(n2 / 1024 / 1024).toFixed(1)} MB` : n2 >= 1024 ? `${Math.round(n2 / 1024)} KB` : `${n2} B`;
|
|
58391
58426
|
}
|
|
@@ -59243,10 +59278,10 @@ var init_keychain = __esm({
|
|
|
59243
59278
|
// src/services/limits.ts
|
|
59244
59279
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
59245
59280
|
import { readFileSync as readFileSync18 } from "node:fs";
|
|
59246
|
-
import { homedir as
|
|
59247
|
-
import { join as
|
|
59281
|
+
import { homedir as homedir7, userInfo as userInfo2 } from "node:os";
|
|
59282
|
+
import { join as join35 } from "node:path";
|
|
59248
59283
|
function credsFile() {
|
|
59249
|
-
return
|
|
59284
|
+
return join35(effectiveStr("CLAUDE_CONFIG_DIR") ?? join35(homedir7(), ".claude"), ".credentials.json");
|
|
59250
59285
|
}
|
|
59251
59286
|
function keychainAccessToken(run6 = securityRun2, account = osAccount2()) {
|
|
59252
59287
|
return readClaudeKeychainToken("Claude Code-credentials", run6, account);
|
|
@@ -59345,8 +59380,8 @@ var init_limits = __esm({
|
|
|
59345
59380
|
|
|
59346
59381
|
// src/services/codex-limits.ts
|
|
59347
59382
|
import { open as open3, readdir as readdir6, stat as stat6 } from "node:fs/promises";
|
|
59348
|
-
import { homedir as
|
|
59349
|
-
import { join as
|
|
59383
|
+
import { homedir as homedir8 } from "node:os";
|
|
59384
|
+
import { join as join36 } from "node:path";
|
|
59350
59385
|
function windowLabel(minutes) {
|
|
59351
59386
|
if (minutes === 300) return "Sesi 5 jam";
|
|
59352
59387
|
if (minutes === 10080) return "Mingguan";
|
|
@@ -59377,7 +59412,7 @@ async function recentRollouts(dir2) {
|
|
|
59377
59412
|
return [];
|
|
59378
59413
|
}
|
|
59379
59414
|
const stamped = await Promise.all(entries4.map(async (rel) => {
|
|
59380
|
-
const full =
|
|
59415
|
+
const full = join36(dir2, rel);
|
|
59381
59416
|
try {
|
|
59382
59417
|
return { full, mtime: (await stat6(full)).mtimeMs };
|
|
59383
59418
|
} catch {
|
|
@@ -59457,7 +59492,7 @@ var init_codex_limits = __esm({
|
|
|
59457
59492
|
MAX_FILES = 8;
|
|
59458
59493
|
cache2 = null;
|
|
59459
59494
|
freshUntil2 = 0;
|
|
59460
|
-
codexSessionsDir = () =>
|
|
59495
|
+
codexSessionsDir = () => join36(process.env.CODEX_HOME ?? join36(homedir8(), ".codex"), "sessions");
|
|
59461
59496
|
UNAVAILABLE = { status: "unavailable", windows: [], fetchedAt: null, plan: null };
|
|
59462
59497
|
severityOf = (pct3) => pct3 >= 90 ? "critical" : pct3 >= 70 ? "warning" : "normal";
|
|
59463
59498
|
}
|
|
@@ -59466,8 +59501,8 @@ var init_codex_limits = __esm({
|
|
|
59466
59501
|
// src/services/antigravity-limits.ts
|
|
59467
59502
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
59468
59503
|
import { readFileSync as readFileSync19 } from "node:fs";
|
|
59469
|
-
import { homedir as
|
|
59470
|
-
import { join as
|
|
59504
|
+
import { homedir as homedir9 } from "node:os";
|
|
59505
|
+
import { join as join37 } from "node:path";
|
|
59471
59506
|
function parseAgyToken(raw) {
|
|
59472
59507
|
try {
|
|
59473
59508
|
let s2 = raw.trim();
|
|
@@ -59490,7 +59525,7 @@ function readAgyToken(run6 = securityRun3) {
|
|
|
59490
59525
|
}
|
|
59491
59526
|
function defaultProject() {
|
|
59492
59527
|
try {
|
|
59493
|
-
return readFileSync19(
|
|
59528
|
+
return readFileSync19(join37(agyHome(), "cache", "default_project_id.txt"), "utf8").trim() || null;
|
|
59494
59529
|
} catch {
|
|
59495
59530
|
return null;
|
|
59496
59531
|
}
|
|
@@ -59580,7 +59615,7 @@ var init_antigravity_limits = __esm({
|
|
|
59580
59615
|
freshUntil3 = 0;
|
|
59581
59616
|
UNAVAILABLE2 = { status: "unavailable", windows: [], fetchedAt: null };
|
|
59582
59617
|
securityRun3 = (args) => execFileSync8("security", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
59583
|
-
agyHome = () =>
|
|
59618
|
+
agyHome = () => join37(homedir9(), ".gemini", "antigravity-cli");
|
|
59584
59619
|
severityOf2 = (pct3) => pct3 >= 90 ? "critical" : pct3 >= 70 ? "warning" : "normal";
|
|
59585
59620
|
prodDeps = {
|
|
59586
59621
|
platform: process.platform,
|
|
@@ -60038,7 +60073,7 @@ var init_prompt2 = __esm({
|
|
|
60038
60073
|
});
|
|
60039
60074
|
|
|
60040
60075
|
// src/services/lead/verdict.ts
|
|
60041
|
-
import { existsSync as
|
|
60076
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
60042
60077
|
import { isAbsolute as isAbsolute11, resolve as resolvePath2, relative as relative7 } from "node:path";
|
|
60043
60078
|
function extractJsonBlock(raw) {
|
|
60044
60079
|
const re = /```json\s*\n([\s\S]*?)```/g;
|
|
@@ -60074,7 +60109,7 @@ function keepExistingRefs(refs2, repoDir) {
|
|
|
60074
60109
|
const full = resolvePath2(repoDir, ref);
|
|
60075
60110
|
const rel = relative7(repoDir, full);
|
|
60076
60111
|
if (rel.startsWith("..")) continue;
|
|
60077
|
-
if (
|
|
60112
|
+
if (existsSync19(full)) out4.push(ref);
|
|
60078
60113
|
}
|
|
60079
60114
|
return [...new Set(out4)];
|
|
60080
60115
|
}
|
|
@@ -65924,15 +65959,15 @@ var init_client2 = __esm({
|
|
|
65924
65959
|
|
|
65925
65960
|
// src/services/logs/spool.ts
|
|
65926
65961
|
import { appendFile, mkdir as mkdir7, readFile as readFile8, readdir as readdir7, rm as rm5, stat as stat7 } from "node:fs/promises";
|
|
65927
|
-
import { join as
|
|
65962
|
+
import { join as join38 } from "node:path";
|
|
65928
65963
|
function spoolDir(lane) {
|
|
65929
|
-
return
|
|
65964
|
+
return join38(resolveDataDirs().home, "log-spool", lane);
|
|
65930
65965
|
}
|
|
65931
65966
|
async function currentSegment(lane) {
|
|
65932
65967
|
const dir2 = spoolDir(lane);
|
|
65933
65968
|
await mkdir7(dir2, { recursive: true, mode: 448 });
|
|
65934
65969
|
if (currentFile && currentBytes < LOG_SPOOL_SEGMENT_BYTES) return currentFile;
|
|
65935
|
-
currentFile =
|
|
65970
|
+
currentFile = join38(dir2, `${Date.now()}-${++segCounter}.ndjson`);
|
|
65936
65971
|
currentBytes = 0;
|
|
65937
65972
|
return currentFile;
|
|
65938
65973
|
}
|
|
@@ -65954,7 +65989,7 @@ async function readSpoolSegments(lane) {
|
|
|
65954
65989
|
const files2 = names.filter((n2) => n2.endsWith(".ndjson")).sort();
|
|
65955
65990
|
const out4 = [];
|
|
65956
65991
|
for (const name2 of files2) {
|
|
65957
|
-
const file =
|
|
65992
|
+
const file = join38(dir2, name2);
|
|
65958
65993
|
const raw = await readFile8(file, "utf8").catch(() => "");
|
|
65959
65994
|
const entries4 = raw.split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
65960
65995
|
out4.push({ file, entries: entries4 });
|
|
@@ -66619,8 +66654,8 @@ var init_git_token = __esm({
|
|
|
66619
66654
|
|
|
66620
66655
|
// src/services/project-checkout.ts
|
|
66621
66656
|
import { execFile as execFile14, spawnSync as spawnSync4 } from "node:child_process";
|
|
66622
|
-
import { existsSync as
|
|
66623
|
-
import { join as
|
|
66657
|
+
import { existsSync as existsSync20, readdirSync as readdirSync11, rmSync as rmSync10 } from "node:fs";
|
|
66658
|
+
import { join as join39 } from "node:path";
|
|
66624
66659
|
import { promisify as promisify13 } from "node:util";
|
|
66625
66660
|
async function runGit(args, o) {
|
|
66626
66661
|
const full = o.token ? ["-c", "credential.helper=", ...args] : args;
|
|
@@ -66642,8 +66677,8 @@ async function doClone(projectId, opts2) {
|
|
|
66642
66677
|
if (!project) throw new CheckoutError(`project "${projectId}" tidak ada`, "not-found");
|
|
66643
66678
|
if (!project.gitRemote) throw new CheckoutError(`project "${projectId}" tidak punya gitRemote untuk di-clone`, "no-remote");
|
|
66644
66679
|
const remote = project.gitRemote;
|
|
66645
|
-
const dest = opts2.dir ??
|
|
66646
|
-
if (
|
|
66680
|
+
const dest = opts2.dir ?? join39(checkoutRoot(), slug(project.id));
|
|
66681
|
+
if (existsSync20(dest) && !isEmptyDir(dest)) {
|
|
66647
66682
|
const origin = originOf2(dest);
|
|
66648
66683
|
if (origin && sameRemote(origin, remote)) {
|
|
66649
66684
|
await setBinding(projectId, dest);
|
|
@@ -66706,7 +66741,7 @@ var init_project_checkout = __esm({
|
|
|
66706
66741
|
CLONE_TIMEOUT_MS = 10 * 6e4;
|
|
66707
66742
|
FETCH_TIMEOUT_MS = 2 * 6e4;
|
|
66708
66743
|
AUTH_RE = /Authentication failed|could not read (Username|Password)|Invalid username or password|HTTP 40[13]|returned error: 40[13]|Repository not found|Permission denied \(publickey\)/i;
|
|
66709
|
-
checkoutRoot = () =>
|
|
66744
|
+
checkoutRoot = () => join39(resolveHome(), "projects");
|
|
66710
66745
|
slug = (id2) => id2.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
66711
66746
|
sameRemote = (a, b) => {
|
|
66712
66747
|
const norm5 = (u) => httpsRemote(u).replace(/\.git$/, "").replace(/\/+$/, "").toLowerCase();
|
|
@@ -66715,7 +66750,7 @@ var init_project_checkout = __esm({
|
|
|
66715
66750
|
redact = (text2, token) => token ? text2.split(token).join("***") : text2;
|
|
66716
66751
|
isEmptyDir = (dir2) => {
|
|
66717
66752
|
try {
|
|
66718
|
-
return
|
|
66753
|
+
return readdirSync11(dir2).length === 0;
|
|
66719
66754
|
} catch {
|
|
66720
66755
|
return false;
|
|
66721
66756
|
}
|
|
@@ -68470,16 +68505,16 @@ var init_codex_version = __esm({
|
|
|
68470
68505
|
|
|
68471
68506
|
// src/services/agent-tool-catalog.ts
|
|
68472
68507
|
import { readFileSync as readFileSync31 } from "node:fs";
|
|
68473
|
-
import { homedir as
|
|
68474
|
-
import { join as
|
|
68508
|
+
import { homedir as homedir17 } from "node:os";
|
|
68509
|
+
import { join as join57 } from "node:path";
|
|
68475
68510
|
function mcpServerNames(repoDir) {
|
|
68476
68511
|
const names = [];
|
|
68477
|
-
const claudeJson = readJson2(
|
|
68512
|
+
const claudeJson = readJson2(join57(home(), ".claude.json"));
|
|
68478
68513
|
names.push(...serversOf(claudeJson));
|
|
68479
68514
|
if (repoDir) {
|
|
68480
68515
|
const projects = claudeJson?.projects;
|
|
68481
68516
|
if (projects && typeof projects === "object") names.push(...serversOf(projects[repoDir]));
|
|
68482
|
-
names.push(...serversOf(readJson2(
|
|
68517
|
+
names.push(...serversOf(readJson2(join57(repoDir, ".mcp.json"))));
|
|
68483
68518
|
}
|
|
68484
68519
|
names.push(...codexServers());
|
|
68485
68520
|
return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
@@ -68492,7 +68527,7 @@ var init_agent_tool_catalog = __esm({
|
|
|
68492
68527
|
"src/services/agent-tool-catalog.ts"() {
|
|
68493
68528
|
"use strict";
|
|
68494
68529
|
init_src();
|
|
68495
|
-
home = () => process.env.HOME ||
|
|
68530
|
+
home = () => process.env.HOME || homedir17();
|
|
68496
68531
|
readJson2 = (path) => {
|
|
68497
68532
|
try {
|
|
68498
68533
|
return JSON.parse(readFileSync31(path, "utf8"));
|
|
@@ -68508,7 +68543,7 @@ var init_agent_tool_catalog = __esm({
|
|
|
68508
68543
|
codexServers = () => {
|
|
68509
68544
|
let text2;
|
|
68510
68545
|
try {
|
|
68511
|
-
text2 = readFileSync31(
|
|
68546
|
+
text2 = readFileSync31(join57(home(), ".codex", "config.toml"), "utf8");
|
|
68512
68547
|
} catch {
|
|
68513
68548
|
return [];
|
|
68514
68549
|
}
|
|
@@ -72128,7 +72163,7 @@ import websocket from "@fastify/websocket";
|
|
|
72128
72163
|
import cookie from "@fastify/cookie";
|
|
72129
72164
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
72130
72165
|
import { dirname as dirname19 } from "node:path";
|
|
72131
|
-
import { existsSync as
|
|
72166
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
72132
72167
|
|
|
72133
72168
|
// src/web-dir.ts
|
|
72134
72169
|
import { resolve as resolve11 } from "node:path";
|
|
@@ -72192,7 +72227,7 @@ init_uploads();
|
|
|
72192
72227
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
72193
72228
|
import { spawn as spawn2 } from "node:child_process";
|
|
72194
72229
|
import { mkdir as mkdir3, rename, unlink as unlink3, writeFile as writeFile3 } from "node:fs/promises";
|
|
72195
|
-
import { basename as basename7, isAbsolute as isAbsolute9, join as
|
|
72230
|
+
import { basename as basename7, isAbsolute as isAbsolute9, join as join24 } from "node:path";
|
|
72196
72231
|
import sharp from "sharp";
|
|
72197
72232
|
var UPLOAD_LIMITS = {
|
|
72198
72233
|
fileBytes: 5 * 1024 * 1024,
|
|
@@ -72266,16 +72301,16 @@ function safeFilename2(input, extension2) {
|
|
|
72266
72301
|
}
|
|
72267
72302
|
async function commitToStorage(buffer, extension2, deps, beforePromote) {
|
|
72268
72303
|
const storageDir = deps.storageDir ?? uploadDir();
|
|
72269
|
-
const quarantineDir =
|
|
72304
|
+
const quarantineDir = join24(storageDir, ".quarantine");
|
|
72270
72305
|
await mkdir3(quarantineDir, { recursive: true, mode: 448 });
|
|
72271
72306
|
await mkdir3(storageDir, { recursive: true, mode: 448 });
|
|
72272
|
-
const quarantine =
|
|
72307
|
+
const quarantine = join24(quarantineDir, `${randomUUID6()}.upload`);
|
|
72273
72308
|
const storageKey = `${randomUUID6()}${extension2}`;
|
|
72274
72309
|
await writeFile3(quarantine, buffer, { mode: 384, flag: "wx" });
|
|
72275
72310
|
try {
|
|
72276
72311
|
await timeout((deps.scanner ?? scannerFromEnv)(quarantine), UPLOAD_LIMITS.scanMs, "UPLOAD_SCAN");
|
|
72277
72312
|
await beforePromote?.();
|
|
72278
|
-
await rename(quarantine,
|
|
72313
|
+
await rename(quarantine, join24(storageDir, storageKey));
|
|
72279
72314
|
} catch (error) {
|
|
72280
72315
|
await unlink3(quarantine).catch(() => {
|
|
72281
72316
|
});
|
|
@@ -72731,7 +72766,7 @@ init_src();
|
|
|
72731
72766
|
init_safe_repo_path();
|
|
72732
72767
|
import { execFile as execFile3 } from "node:child_process";
|
|
72733
72768
|
import { promisify as promisify2 } from "node:util";
|
|
72734
|
-
import { existsSync as
|
|
72769
|
+
import { existsSync as existsSync14, rmSync as rmSync8 } from "node:fs";
|
|
72735
72770
|
import { resolve as resolve14 } from "node:path";
|
|
72736
72771
|
var exec = promisify2(execFile3);
|
|
72737
72772
|
async function listRepoDocs(repoDir) {
|
|
@@ -72756,12 +72791,12 @@ function docsDirOf(repoDir) {
|
|
|
72756
72791
|
}
|
|
72757
72792
|
function resolveIndex(repoDir, docsDir) {
|
|
72758
72793
|
const rel = `${docsDir}/README.md`;
|
|
72759
|
-
return
|
|
72794
|
+
return existsSync14(resolve14(repoDir, rel)) ? rel : "";
|
|
72760
72795
|
}
|
|
72761
72796
|
var catOf = (rel) => rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : ".";
|
|
72762
72797
|
var nameOf = (rel) => rel.includes("/") ? rel.slice(rel.lastIndexOf("/") + 1) : rel;
|
|
72763
72798
|
async function scanRepoDocs(repoDir) {
|
|
72764
|
-
if (!repoDir || !
|
|
72799
|
+
if (!repoDir || !existsSync14(repoDir)) return { coverage: 0, tree: [] };
|
|
72765
72800
|
const files2 = await listRepoDocs(repoDir);
|
|
72766
72801
|
const docsDir = docsDirOf(repoDir);
|
|
72767
72802
|
const index = resolveIndex(repoDir, docsDir);
|
|
@@ -72809,7 +72844,7 @@ function writeDocFile(repoDir, rel, content) {
|
|
|
72809
72844
|
}
|
|
72810
72845
|
function deleteDocFile(repoDir, rel) {
|
|
72811
72846
|
const abs = docAbsPath(repoDir, rel);
|
|
72812
|
-
if (!
|
|
72847
|
+
if (!existsSync14(abs)) return false;
|
|
72813
72848
|
rmSync8(abs);
|
|
72814
72849
|
return true;
|
|
72815
72850
|
}
|
|
@@ -73128,7 +73163,7 @@ async function projects_default(app3) {
|
|
|
73128
73163
|
init_src();
|
|
73129
73164
|
init_src2();
|
|
73130
73165
|
init_integrate();
|
|
73131
|
-
import { existsSync as
|
|
73166
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
73132
73167
|
import { basename as basename9 } from "node:path";
|
|
73133
73168
|
|
|
73134
73169
|
// src/services/worktree-delete.ts
|
|
@@ -73147,7 +73182,7 @@ init_spec_review2();
|
|
|
73147
73182
|
init_safe_repo_path();
|
|
73148
73183
|
import { execFile as execFile7 } from "node:child_process";
|
|
73149
73184
|
import { promisify as promisify6 } from "node:util";
|
|
73150
|
-
import { existsSync as
|
|
73185
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
73151
73186
|
import { readdir as readdir3 } from "node:fs/promises";
|
|
73152
73187
|
import { resolve as resolve16 } from "node:path";
|
|
73153
73188
|
var exec5 = promisify6(execFile7);
|
|
@@ -73160,7 +73195,7 @@ function repoAbsPath(repoDir, rel, allowMissingFinal = false) {
|
|
|
73160
73195
|
return resolve16(repoDir, rel);
|
|
73161
73196
|
}
|
|
73162
73197
|
async function listRepoTree(repoDir, ref = "") {
|
|
73163
|
-
if (!repoDir || !
|
|
73198
|
+
if (!repoDir || !existsSync15(repoDir)) return [];
|
|
73164
73199
|
try {
|
|
73165
73200
|
const { stdout } = ref ? await exec5("git", ["ls-tree", "-r", "--name-only", "-z", ref], { cwd: repoDir, ...GIT4 }) : await exec5("git", ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], { cwd: repoDir, ...GIT4 });
|
|
73166
73201
|
return [...new Set(splitZ2(stdout))].sort();
|
|
@@ -73170,7 +73205,7 @@ async function listRepoTree(repoDir, ref = "") {
|
|
|
73170
73205
|
}
|
|
73171
73206
|
async function listIgnoredEntries(repoDir) {
|
|
73172
73207
|
const empty = { files: [], dirs: [] };
|
|
73173
|
-
if (!repoDir || !
|
|
73208
|
+
if (!repoDir || !existsSync15(repoDir)) return empty;
|
|
73174
73209
|
try {
|
|
73175
73210
|
const { stdout } = await exec5(
|
|
73176
73211
|
"git",
|
|
@@ -73223,7 +73258,7 @@ async function currentBranch(repoDir) {
|
|
|
73223
73258
|
}
|
|
73224
73259
|
async function repoStatus(repoDir) {
|
|
73225
73260
|
const empty = { branch: "", ahead: 0, behind: 0, staged: [], unstaged: [], untracked: [], clean: true };
|
|
73226
|
-
if (!repoDir || !
|
|
73261
|
+
if (!repoDir || !existsSync15(repoDir)) return empty;
|
|
73227
73262
|
try {
|
|
73228
73263
|
const { stdout } = await exec5("git", ["status", "--porcelain=v1", "-z", "-b"], { cwd: repoDir, ...GIT4 });
|
|
73229
73264
|
const parts = stdout.split("\0");
|
|
@@ -73256,7 +73291,7 @@ async function repoStatus(repoDir) {
|
|
|
73256
73291
|
}
|
|
73257
73292
|
}
|
|
73258
73293
|
async function listStashes(repoDir) {
|
|
73259
|
-
if (!repoDir || !
|
|
73294
|
+
if (!repoDir || !existsSync15(repoDir)) return [];
|
|
73260
73295
|
try {
|
|
73261
73296
|
const { stdout } = await exec5("git", ["stash", "list", `--format=%gd${US}%s${US}%aI`], { cwd: repoDir, ...GIT4 });
|
|
73262
73297
|
return stdout.split("\n").filter(Boolean).map((line) => {
|
|
@@ -73268,7 +73303,7 @@ async function listStashes(repoDir) {
|
|
|
73268
73303
|
}
|
|
73269
73304
|
}
|
|
73270
73305
|
async function listGraph(repoDir, limit = 200, opts2 = {}) {
|
|
73271
|
-
if (!repoDir || !
|
|
73306
|
+
if (!repoDir || !existsSync15(repoDir)) return { commits: [], current: "", total: 0 };
|
|
73272
73307
|
try {
|
|
73273
73308
|
const fmt = ["%H", "%P", "%an", "%aI", "%s", "%D"].join(US);
|
|
73274
73309
|
const refArgs = opts2.branches?.length ? ["--end-of-options", ...opts2.branches] : [
|
|
@@ -73338,7 +73373,7 @@ async function changedOf(repoDir, sha2) {
|
|
|
73338
73373
|
}
|
|
73339
73374
|
async function compareCommits(repoDir, from, to) {
|
|
73340
73375
|
const empty = { from, to, changed: [] };
|
|
73341
|
-
if (!repoDir || !
|
|
73376
|
+
if (!repoDir || !existsSync15(repoDir)) return empty;
|
|
73342
73377
|
try {
|
|
73343
73378
|
const [num, name2] = await Promise.all([
|
|
73344
73379
|
exec5("git", ["diff", "--numstat", "-z", "--no-renames", "--end-of-options", from, to], { cwd: repoDir, ...GIT4 }),
|
|
@@ -73350,7 +73385,7 @@ async function compareCommits(repoDir, from, to) {
|
|
|
73350
73385
|
}
|
|
73351
73386
|
}
|
|
73352
73387
|
async function searchCommits(repoDir, q2, by = "all") {
|
|
73353
|
-
if (!repoDir || !
|
|
73388
|
+
if (!repoDir || !existsSync15(repoDir) || !q2) return [];
|
|
73354
73389
|
const run6 = async (extra) => {
|
|
73355
73390
|
try {
|
|
73356
73391
|
return (await exec5("git", ["log", "--all", "--format=%H", ...extra], { cwd: repoDir, ...GIT4 })).stdout.split("\n").filter(Boolean);
|
|
@@ -73597,7 +73632,7 @@ async function runGitOp(repoDir, op) {
|
|
|
73597
73632
|
}
|
|
73598
73633
|
}
|
|
73599
73634
|
async function workingStatus(repoDir) {
|
|
73600
|
-
if (!repoDir || !
|
|
73635
|
+
if (!repoDir || !existsSync15(repoDir)) return { branch: "", staged: [], unstaged: [] };
|
|
73601
73636
|
const [branch, staged, unstaged] = await Promise.all([
|
|
73602
73637
|
currentBranch(repoDir),
|
|
73603
73638
|
changedFiles(repoDir, ["--cached"]),
|
|
@@ -73606,7 +73641,7 @@ async function workingStatus(repoDir) {
|
|
|
73606
73641
|
return { branch, staged, unstaged };
|
|
73607
73642
|
}
|
|
73608
73643
|
async function workingFileDiff(repoDir, path, staged) {
|
|
73609
|
-
if (!repoDir || !
|
|
73644
|
+
if (!repoDir || !existsSync15(repoDir)) return null;
|
|
73610
73645
|
repoAbsPath(repoDir, path, true);
|
|
73611
73646
|
const changed = staged ? await changedFiles(repoDir, ["--cached"]) : await withTempIndex(repoDir, (env) => changedFiles(repoDir, [], env));
|
|
73612
73647
|
const cf = changed.find((c) => c.path === path);
|
|
@@ -73815,7 +73850,7 @@ async function deleteBranches(repoDir, names, opts2) {
|
|
|
73815
73850
|
import { execFile as execFile9 } from "node:child_process";
|
|
73816
73851
|
import { realpathSync as realpathSync8 } from "node:fs";
|
|
73817
73852
|
import { stat as stat5 } from "node:fs/promises";
|
|
73818
|
-
import { basename as basename8, dirname as dirname13, join as
|
|
73853
|
+
import { basename as basename8, dirname as dirname13, join as join29, resolve as resolve17, sep as sep8 } from "node:path";
|
|
73819
73854
|
import { promisify as promisify8 } from "node:util";
|
|
73820
73855
|
var exec7 = promisify8(execFile9);
|
|
73821
73856
|
var GIT6 = { timeout: 6e4, maxBuffer: 1 << 24, encoding: "utf8" };
|
|
@@ -73857,7 +73892,7 @@ var real = (p3) => {
|
|
|
73857
73892
|
} catch {
|
|
73858
73893
|
const path = resolve17(p3);
|
|
73859
73894
|
const parent = dirname13(path);
|
|
73860
|
-
return parent === path ? path :
|
|
73895
|
+
return parent === path ? path : join29(real(parent), basename8(path));
|
|
73861
73896
|
}
|
|
73862
73897
|
};
|
|
73863
73898
|
function hasWorktreeSession(path, sessionId2, sessions) {
|
|
@@ -73869,7 +73904,7 @@ function hasWorktreeSession(path, sessionId2, sessions) {
|
|
|
73869
73904
|
}
|
|
73870
73905
|
async function bornAt(path) {
|
|
73871
73906
|
try {
|
|
73872
|
-
const st2 = await stat5(
|
|
73907
|
+
const st2 = await stat5(join29(path, ".git"));
|
|
73873
73908
|
const ms = st2.birthtimeMs > 0 ? st2.birthtimeMs : st2.mtimeMs;
|
|
73874
73909
|
return new Date(ms).toISOString();
|
|
73875
73910
|
} catch {
|
|
@@ -74097,7 +74132,7 @@ init_notifications2();
|
|
|
74097
74132
|
import { execFile as execFile11 } from "node:child_process";
|
|
74098
74133
|
import { readdir as readdir4, rm as rm3 } from "node:fs/promises";
|
|
74099
74134
|
import { promisify as promisify10 } from "node:util";
|
|
74100
|
-
import { join as
|
|
74135
|
+
import { join as join30, resolve as resolve19 } from "node:path";
|
|
74101
74136
|
var TICK_MS = 6e4;
|
|
74102
74137
|
var trashDirOf = (repoDir) => resolve19(repoDir, ".worktrees", ".trash");
|
|
74103
74138
|
var exec8 = promisify10(execFile11);
|
|
@@ -74158,7 +74193,7 @@ async function sweepRepo(repoDir, projectId, deps = prodReaperDeps) {
|
|
|
74158
74193
|
}
|
|
74159
74194
|
let removed = 0;
|
|
74160
74195
|
for (const entry2 of entries4) {
|
|
74161
|
-
const path =
|
|
74196
|
+
const path = join30(dir2, entry2);
|
|
74162
74197
|
const known2 = pending.get(path);
|
|
74163
74198
|
const row = known2 ?? { path, repoDir, projectId, entry: entry2, sessionId: sessionIdOf(entry2), since: Date.now() };
|
|
74164
74199
|
pending.set(path, row);
|
|
@@ -74301,8 +74336,10 @@ async function beginSession(b) {
|
|
|
74301
74336
|
effort: b.effort ?? null,
|
|
74302
74337
|
branch: b.branch ?? null,
|
|
74303
74338
|
cwd: b.cwd,
|
|
74304
|
-
accountId: b.accountId ?? null
|
|
74339
|
+
accountId: b.accountId ?? null,
|
|
74305
74340
|
// ADR-0234
|
|
74341
|
+
agentSessionId: b.agentSessionId ?? null
|
|
74342
|
+
// ADR-0235
|
|
74306
74343
|
}
|
|
74307
74344
|
});
|
|
74308
74345
|
}
|
|
@@ -74480,6 +74517,35 @@ async function reconciledRuntimesSince(cutoff) {
|
|
|
74480
74517
|
}
|
|
74481
74518
|
return out4;
|
|
74482
74519
|
}
|
|
74520
|
+
var FREE_FLOW_KINDS = /* @__PURE__ */ new Set(["reverse", "prd", "scaffold", "breakdown"]);
|
|
74521
|
+
async function reconciledFreeSessionsSince(cutoff) {
|
|
74522
|
+
const rows = await prisma.sessionHistory.findMany({
|
|
74523
|
+
where: { endedReason: RECONCILED, reconciledAt: { gte: cutoff }, specId: null },
|
|
74524
|
+
orderBy: { startedAt: "desc" }
|
|
74525
|
+
});
|
|
74526
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
74527
|
+
const out4 = [];
|
|
74528
|
+
for (const r of rows) {
|
|
74529
|
+
if (seen2.has(r.sessionId)) continue;
|
|
74530
|
+
seen2.add(r.sessionId);
|
|
74531
|
+
const eligible = r.kind === "terminal" || r.kind === "shell" || FREE_FLOW_KINDS.has(r.kind) || r.kind === "worktree" && r.sessionId.startsWith("term-");
|
|
74532
|
+
if (!eligible) continue;
|
|
74533
|
+
out4.push({
|
|
74534
|
+
sessionId: r.sessionId,
|
|
74535
|
+
projectId: r.projectId,
|
|
74536
|
+
kind: r.kind,
|
|
74537
|
+
flow: r.flow,
|
|
74538
|
+
agent: r.agent === "antigravity" ? "antigravity" : r.agent === "codex" ? "codex" : "claude",
|
|
74539
|
+
...r.model ? { model: r.model } : {},
|
|
74540
|
+
...r.effort ? { effort: r.effort } : {},
|
|
74541
|
+
...r.accountId ? { accountId: r.accountId } : {},
|
|
74542
|
+
...r.branch ? { branch: r.branch } : {},
|
|
74543
|
+
cwd: r.cwd,
|
|
74544
|
+
...r.agentSessionId ? { agentSessionId: r.agentSessionId } : {}
|
|
74545
|
+
});
|
|
74546
|
+
}
|
|
74547
|
+
return out4.reverse();
|
|
74548
|
+
}
|
|
74483
74549
|
function installSessionHistory() {
|
|
74484
74550
|
registerSessionHooks({
|
|
74485
74551
|
onBirth: (b) => {
|
|
@@ -74626,8 +74692,8 @@ function appendSourceHistory(current, entry2) {
|
|
|
74626
74692
|
|
|
74627
74693
|
// src/services/spec-reset.ts
|
|
74628
74694
|
import { execFile as execFile13 } from "node:child_process";
|
|
74629
|
-
import { existsSync as
|
|
74630
|
-
import { join as
|
|
74695
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
74696
|
+
import { join as join31 } from "node:path";
|
|
74631
74697
|
import { promisify as promisify12 } from "node:util";
|
|
74632
74698
|
|
|
74633
74699
|
// src/services/stage-artifacts.ts
|
|
@@ -74682,14 +74748,14 @@ async function planSpecReset(spec) {
|
|
|
74682
74748
|
const repoDir = await resolveRepoDir(spec.projectId);
|
|
74683
74749
|
if (!repoDir) return EMPTY3;
|
|
74684
74750
|
const sid = sessionIdForSpec(spec.id);
|
|
74685
|
-
const wt =
|
|
74751
|
+
const wt = join31(repoDir, ".worktrees", sid);
|
|
74686
74752
|
const branch = `hanoman/${sid}`;
|
|
74687
74753
|
const hasBranch = await shaResolvable(repoDir, `refs/heads/${branch}`);
|
|
74688
74754
|
return {
|
|
74689
74755
|
// Berkas fase seluruh stage yang ditinggalkan — rentang yang sama persis dengan revert stage
|
|
74690
74756
|
// SPEC-167, karena tujuan akhirnya memang sama: item berdiri lagi di `brainstorming`.
|
|
74691
74757
|
wouldDelete: await artifactsToRemove(spec.projectId, spec.id, "brainstorming", spec.stage),
|
|
74692
|
-
worktree:
|
|
74758
|
+
worktree: existsSync16(wt) ? wt : null,
|
|
74693
74759
|
branch: hasBranch ? branch : null
|
|
74694
74760
|
};
|
|
74695
74761
|
}
|
|
@@ -74770,9 +74836,9 @@ init_uploads();
|
|
|
74770
74836
|
// src/services/spec-mockups.ts
|
|
74771
74837
|
init_local_binding();
|
|
74772
74838
|
init_spec_review2();
|
|
74773
|
-
import { existsSync as
|
|
74839
|
+
import { existsSync as existsSync17, readdirSync as readdirSync10, realpathSync as realpathSync10, statSync as statSync5 } from "node:fs";
|
|
74774
74840
|
import { readFile as readFile7 } from "node:fs/promises";
|
|
74775
|
-
import { extname as extname2, join as
|
|
74841
|
+
import { extname as extname2, join as join34, sep as sep10 } from "node:path";
|
|
74776
74842
|
var MOCKUP_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*\.(png|jpe?g|webp)$/i;
|
|
74777
74843
|
var MIME = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp" };
|
|
74778
74844
|
var isDir = (p3) => {
|
|
@@ -74785,18 +74851,18 @@ var isDir = (p3) => {
|
|
|
74785
74851
|
async function resolveMockupDir(spec) {
|
|
74786
74852
|
const repoDir = await resolveRepoDir(spec.projectId);
|
|
74787
74853
|
if (!repoDir) return null;
|
|
74788
|
-
const rel =
|
|
74789
|
-
const worktree =
|
|
74854
|
+
const rel = join34("docs", "mockups", spec.id.toLowerCase());
|
|
74855
|
+
const worktree = join34(worktreeDir(repoDir, spec.id), rel);
|
|
74790
74856
|
if (isDir(worktree)) return { source: "worktree", dir: worktree };
|
|
74791
|
-
const repo =
|
|
74857
|
+
const repo = join34(repoDir, rel);
|
|
74792
74858
|
return isDir(repo) ? { source: "repo", dir: repo } : null;
|
|
74793
74859
|
}
|
|
74794
74860
|
async function listMockups(spec) {
|
|
74795
74861
|
const found = await resolveMockupDir(spec);
|
|
74796
74862
|
if (!found) return { source: null, dir: null, items: [] };
|
|
74797
|
-
const items =
|
|
74863
|
+
const items = readdirSync10(found.dir).filter((n2) => MOCKUP_NAME.test(n2)).sort().flatMap((name2) => {
|
|
74798
74864
|
try {
|
|
74799
|
-
const st2 = statSync5(
|
|
74865
|
+
const st2 = statSync5(join34(found.dir, name2));
|
|
74800
74866
|
return st2.isFile() ? [{ name: name2, size: st2.size, modifiedAt: st2.mtime.toISOString() }] : [];
|
|
74801
74867
|
} catch {
|
|
74802
74868
|
return [];
|
|
@@ -74808,8 +74874,8 @@ async function readMockup(spec, name2) {
|
|
|
74808
74874
|
if (!MOCKUP_NAME.test(name2)) return null;
|
|
74809
74875
|
const found = await resolveMockupDir(spec);
|
|
74810
74876
|
if (!found) return null;
|
|
74811
|
-
const file =
|
|
74812
|
-
if (!
|
|
74877
|
+
const file = join34(found.dir, name2);
|
|
74878
|
+
if (!existsSync17(file)) return null;
|
|
74813
74879
|
try {
|
|
74814
74880
|
const root = realpathSync10(found.dir);
|
|
74815
74881
|
const real2 = realpathSync10(file);
|
|
@@ -77835,7 +77901,7 @@ ${item.context}` : item.context;
|
|
|
77835
77901
|
});
|
|
77836
77902
|
const specWithProject = (id2) => prisma.spec.findUnique({ where: { id: id2 }, include: { project: true } });
|
|
77837
77903
|
const resolveReview = async (repoDir, spec) => {
|
|
77838
|
-
if (
|
|
77904
|
+
if (existsSync18(worktreeDir(repoDir, spec.id))) return { wt: true };
|
|
77839
77905
|
if (spec.baseSha && spec.headSha && await shaResolvable(repoDir, spec.baseSha) && await shaResolvable(repoDir, spec.headSha))
|
|
77840
77906
|
return { wt: false, base: spec.baseSha, head: spec.headSha };
|
|
77841
77907
|
const r = await specCommitRange(repoDir, spec.id);
|
|
@@ -78133,7 +78199,7 @@ import { spawn as spawn5 } from "node:child_process";
|
|
|
78133
78199
|
// src/services/git-remotes.ts
|
|
78134
78200
|
import { execFile as execFile16 } from "node:child_process";
|
|
78135
78201
|
import { promisify as promisify15 } from "node:util";
|
|
78136
|
-
import { existsSync as
|
|
78202
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
78137
78203
|
var exec10 = promisify15(execFile16);
|
|
78138
78204
|
var GIT7 = { maxBuffer: 1 << 24, encoding: "utf8" };
|
|
78139
78205
|
function parseRemote(url2) {
|
|
@@ -78152,7 +78218,7 @@ function prUrl(remoteUrl, branch, base3 = "main") {
|
|
|
78152
78218
|
return null;
|
|
78153
78219
|
}
|
|
78154
78220
|
async function listRemotes(repoDir) {
|
|
78155
|
-
if (!repoDir || !
|
|
78221
|
+
if (!repoDir || !existsSync21(repoDir)) return [];
|
|
78156
78222
|
try {
|
|
78157
78223
|
const { stdout } = await exec10("git", ["remote", "-v"], { cwd: repoDir, ...GIT7 });
|
|
78158
78224
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -78200,7 +78266,7 @@ init_integrate();
|
|
|
78200
78266
|
init_safe_repo_path();
|
|
78201
78267
|
import { lstat as lstat2, rename as rename4, rm as rm6 } from "node:fs/promises";
|
|
78202
78268
|
import { createWriteStream } from "node:fs";
|
|
78203
|
-
import { join as
|
|
78269
|
+
import { join as join40 } from "node:path";
|
|
78204
78270
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
78205
78271
|
import { pipeline } from "node:stream/promises";
|
|
78206
78272
|
var EntryExistsError = class extends Error {
|
|
@@ -78261,7 +78327,7 @@ async function saveUpload2(repoDir, rel, source, opts2 = {}) {
|
|
|
78261
78327
|
if (current && !opts2.overwrite) return { status: "exists" };
|
|
78262
78328
|
if (current && !current.isFile())
|
|
78263
78329
|
throw new PathContainmentError("repository path ditolak: target bukan file regular");
|
|
78264
|
-
const temp =
|
|
78330
|
+
const temp = join40(entry2.parent, `.hanoman-${randomUUID11()}.tmp`);
|
|
78265
78331
|
try {
|
|
78266
78332
|
await pipeline(source, createWriteStream(temp, { flags: "wx", mode: 384 }));
|
|
78267
78333
|
if (opts2.isTruncated?.()) {
|
|
@@ -78751,15 +78817,15 @@ async function finishGraphOp(reply, id2, repoDir, r, verb) {
|
|
|
78751
78817
|
|
|
78752
78818
|
// src/routes/fs.ts
|
|
78753
78819
|
import { readdir as readdir8 } from "node:fs/promises";
|
|
78754
|
-
import { homedir as
|
|
78755
|
-
import { resolve as resolve22, dirname as dirname16, join as
|
|
78820
|
+
import { homedir as homedir10 } from "node:os";
|
|
78821
|
+
import { resolve as resolve22, dirname as dirname16, join as join41 } from "node:path";
|
|
78756
78822
|
async function fs_default(app3) {
|
|
78757
78823
|
app3.get("/fs/browse", async (req, reply) => {
|
|
78758
78824
|
const q2 = req.query.path;
|
|
78759
|
-
const dir2 = q2 && q2.trim() ? resolve22(q2.trim()) :
|
|
78825
|
+
const dir2 = q2 && q2.trim() ? resolve22(q2.trim()) : homedir10();
|
|
78760
78826
|
try {
|
|
78761
78827
|
const ents = await readdir8(dir2, { withFileTypes: true });
|
|
78762
|
-
const entries4 = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path:
|
|
78828
|
+
const entries4 = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join41(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
|
|
78763
78829
|
const parent = dirname16(dir2);
|
|
78764
78830
|
return { path: dir2, parent: parent === dir2 ? null : parent, entries: entries4 };
|
|
78765
78831
|
} catch {
|
|
@@ -78774,7 +78840,7 @@ init_src();
|
|
|
78774
78840
|
init_src2();
|
|
78775
78841
|
init_session_phases();
|
|
78776
78842
|
init_spec_review2();
|
|
78777
|
-
import { existsSync as
|
|
78843
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
78778
78844
|
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
78779
78845
|
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
78780
78846
|
init_integrate();
|
|
@@ -79134,13 +79200,13 @@ init_local_binding();
|
|
|
79134
79200
|
|
|
79135
79201
|
// src/services/terminal-diag.ts
|
|
79136
79202
|
import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync17, statSync as statSync6, rmSync as rmSync11 } from "node:fs";
|
|
79137
|
-
import { join as
|
|
79203
|
+
import { join as join42 } from "node:path";
|
|
79138
79204
|
var DIAG_MAX_BYTES = 2 * 1024 * 1024;
|
|
79139
79205
|
var KINDS = /* @__PURE__ */ new Set(["key", "comp", "data", "ack", "pred"]);
|
|
79140
79206
|
var ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
79141
79207
|
function diagFile(home3, sessionId2) {
|
|
79142
79208
|
if (!ID.test(sessionId2)) throw new Error(`id sesi tak sah untuk diag: ${sessionId2}`);
|
|
79143
|
-
return
|
|
79209
|
+
return join42(home3, "diag", `${sessionId2}.jsonl`);
|
|
79144
79210
|
}
|
|
79145
79211
|
function usable(ev) {
|
|
79146
79212
|
if (!ev || typeof ev !== "object") return false;
|
|
@@ -79151,7 +79217,7 @@ function appendDiag(home3, sessionId2, events) {
|
|
|
79151
79217
|
const file = diagFile(home3, sessionId2);
|
|
79152
79218
|
const rows = events.filter(usable);
|
|
79153
79219
|
if (!rows.length) return;
|
|
79154
|
-
mkdirSync17(
|
|
79220
|
+
mkdirSync17(join42(home3, "diag"), { recursive: true });
|
|
79155
79221
|
try {
|
|
79156
79222
|
if (statSync6(file).size > DIAG_MAX_BYTES) rmSync11(file);
|
|
79157
79223
|
} catch {
|
|
@@ -79167,19 +79233,19 @@ import { randomUUID as randomUUID12 } from "node:crypto";
|
|
|
79167
79233
|
import { rmSync as rmSync12 } from "node:fs";
|
|
79168
79234
|
|
|
79169
79235
|
// src/services/runtime-accounts/home.ts
|
|
79170
|
-
import { chmodSync as chmodSync10, existsSync as
|
|
79171
|
-
import { join as
|
|
79236
|
+
import { chmodSync as chmodSync10, existsSync as existsSync22, lstatSync as lstatSync10, mkdirSync as mkdirSync18, readFileSync as readFileSync20, readlinkSync as readlinkSync3, renameSync as renameSync7, symlinkSync as symlinkSync2, writeFileSync as writeFileSync18 } from "node:fs";
|
|
79237
|
+
import { join as join44 } from "node:path";
|
|
79172
79238
|
|
|
79173
79239
|
// src/services/runtime-accounts/paths.ts
|
|
79174
79240
|
init_src2();
|
|
79175
|
-
import { homedir as
|
|
79176
|
-
import { join as
|
|
79177
|
-
var accountsRoot = (env = process.env) =>
|
|
79178
|
-
var accountHomeDir = (runtime, id2, env = process.env) =>
|
|
79241
|
+
import { homedir as homedir11 } from "node:os";
|
|
79242
|
+
import { join as join43, resolve as resolve23, sep as sep11 } from "node:path";
|
|
79243
|
+
var accountsRoot = (env = process.env) => join43(resolveHome(env), "accounts");
|
|
79244
|
+
var accountHomeDir = (runtime, id2, env = process.env) => join43(accountsRoot(env), runtime, id2);
|
|
79179
79245
|
var baseHome = (runtime, env = process.env) => agentSkillHome(runtime, env);
|
|
79180
|
-
function baseClaudeJson(env = process.env, osHome =
|
|
79246
|
+
function baseClaudeJson(env = process.env, osHome = homedir11()) {
|
|
79181
79247
|
const own = env.HANOMAN_CLAUDE_HOME?.trim() || env.CLAUDE_CONFIG_DIR?.trim();
|
|
79182
|
-
return own ?
|
|
79248
|
+
return own ? join43(own, ".claude.json") : join43(osHome, ".claude.json");
|
|
79183
79249
|
}
|
|
79184
79250
|
function insideAccountsRoot(path, env = process.env) {
|
|
79185
79251
|
return resolve23(path).startsWith(resolve23(accountsRoot(env)) + sep11);
|
|
@@ -79207,19 +79273,19 @@ function classify(target3, link2) {
|
|
|
79207
79273
|
} catch {
|
|
79208
79274
|
st2 = null;
|
|
79209
79275
|
}
|
|
79210
|
-
if (!st2) return
|
|
79276
|
+
if (!st2) return existsSync22(target3) ? "absent" : "no-target";
|
|
79211
79277
|
if (st2.isSymbolicLink()) return readlinkSync3(link2) === target3 ? "ok" : "drift";
|
|
79212
79278
|
return "drift";
|
|
79213
79279
|
}
|
|
79214
79280
|
function inspectAccountHome(runtime, homeDir, base3 = baseHome(runtime)) {
|
|
79215
|
-
return SHARED_ENTRIES[runtime].filter((e) => classify(
|
|
79281
|
+
return SHARED_ENTRIES[runtime].filter((e) => classify(join44(base3, e), join44(homeDir, e)) === "drift");
|
|
79216
79282
|
}
|
|
79217
79283
|
function ensureAccountHome(runtime, homeDir, base3 = baseHome(runtime)) {
|
|
79218
79284
|
mkdirSync18(homeDir, { recursive: true, mode: 448 });
|
|
79219
79285
|
chmodSync10(homeDir, 448);
|
|
79220
79286
|
const linked = [], drift = [];
|
|
79221
79287
|
for (const entry2 of SHARED_ENTRIES[runtime]) {
|
|
79222
|
-
const target3 =
|
|
79288
|
+
const target3 = join44(base3, entry2), link2 = join44(homeDir, entry2);
|
|
79223
79289
|
const state = classify(target3, link2);
|
|
79224
79290
|
if (state === "drift") drift.push(entry2);
|
|
79225
79291
|
else if (state === "absent") {
|
|
@@ -79236,7 +79302,7 @@ function mirrorClaudeJson(accountHome, baseJsonPath = baseClaudeJson()) {
|
|
|
79236
79302
|
} catch {
|
|
79237
79303
|
return;
|
|
79238
79304
|
}
|
|
79239
|
-
const path =
|
|
79305
|
+
const path = join44(accountHome, ".claude.json");
|
|
79240
79306
|
let acct = {};
|
|
79241
79307
|
try {
|
|
79242
79308
|
acct = JSON.parse(readFileSync20(path, "utf8"));
|
|
@@ -79267,7 +79333,7 @@ init_keychain();
|
|
|
79267
79333
|
init_pty();
|
|
79268
79334
|
import { execFile as execFile17 } from "node:child_process";
|
|
79269
79335
|
import { readFileSync as readFileSync21 } from "node:fs";
|
|
79270
|
-
import { join as
|
|
79336
|
+
import { join as join45 } from "node:path";
|
|
79271
79337
|
var NONE2 = { loggedIn: false, identity: null };
|
|
79272
79338
|
var execOut = (bin, args, env) => new Promise((res, rej) => {
|
|
79273
79339
|
execFile17(bin, args, { env, encoding: "utf8", timeout: 15e3 }, (err, stdout) => stdout ? res(stdout) : rej(err));
|
|
@@ -79303,7 +79369,7 @@ function decodeJwtClaims(token) {
|
|
|
79303
79369
|
function probeCodexLogin(codexHome2) {
|
|
79304
79370
|
let j;
|
|
79305
79371
|
try {
|
|
79306
|
-
j = JSON.parse(readFileSync21(
|
|
79372
|
+
j = JSON.parse(readFileSync21(join45(codexHome2, "auth.json"), "utf8"));
|
|
79307
79373
|
} catch {
|
|
79308
79374
|
return NONE2;
|
|
79309
79375
|
}
|
|
@@ -79523,8 +79589,8 @@ init_antigravity_limits();
|
|
|
79523
79589
|
init_pty();
|
|
79524
79590
|
init_keychain();
|
|
79525
79591
|
import { chmodSync as chmodSync11, mkdirSync as mkdirSync19, readFileSync as readFileSync22, rmSync as rmSync13, writeFileSync as writeFileSync19 } from "node:fs";
|
|
79526
|
-
import { homedir as
|
|
79527
|
-
import { join as
|
|
79592
|
+
import { homedir as homedir12 } from "node:os";
|
|
79593
|
+
import { join as join46 } from "node:path";
|
|
79528
79594
|
var BUILTIN = builtinAccountId("antigravity");
|
|
79529
79595
|
var isAgyLoginSession = (sessionId2) => sessionId2 === "acct-login-default-antigravity" || accountSnapshot().rows.some((r) => r.runtime === "antigravity" && sessionId2 === `acct-login-${r.id}`);
|
|
79530
79596
|
var liveAgySessions = () => listSessions().filter((s2) => !s2.exited && (s2.agent === "antigravity" || isAgyLoginSession(s2.id)));
|
|
@@ -79542,20 +79608,20 @@ var fsAgyIo = () => ({
|
|
|
79542
79608
|
chmodSync11(p3, 384);
|
|
79543
79609
|
},
|
|
79544
79610
|
removeFile: (p3) => rmSync13(p3, { force: true }),
|
|
79545
|
-
tokenFile:
|
|
79546
|
-
accountsDir:
|
|
79611
|
+
tokenFile: join46(homedir12(), ".gemini", "antigravity-cli", "antigravity-oauth-token"),
|
|
79612
|
+
accountsDir: join46(accountsRoot(), "antigravity"),
|
|
79547
79613
|
// Sesi agy hidup memakai akun yang AKTIF saat ini (satu Keychain), apa pun @hanoman_account-nya.
|
|
79548
79614
|
liveAgyAccountIds: () => liveAgySessions().map((s2) => s2.accountId ?? BUILTIN)
|
|
79549
79615
|
});
|
|
79550
|
-
var slotDir = (io, id2) =>
|
|
79551
|
-
var activeFile = (io) =>
|
|
79616
|
+
var slotDir = (io, id2) => join46(io.accountsDir, isBuiltinAccountId(id2) ? "default" : id2);
|
|
79617
|
+
var activeFile = (io) => join46(io.accountsDir, "active");
|
|
79552
79618
|
function activeAgyAccount(io = fsAgyIo()) {
|
|
79553
79619
|
return io.readFile(activeFile(io))?.trim() || BUILTIN;
|
|
79554
79620
|
}
|
|
79555
79621
|
function readSlot(accountId, io = fsAgyIo()) {
|
|
79556
79622
|
let raw;
|
|
79557
79623
|
try {
|
|
79558
|
-
raw = readFileSync22(
|
|
79624
|
+
raw = readFileSync22(join46(slotDir(io, accountId), "slot.enc"), "utf8");
|
|
79559
79625
|
} catch {
|
|
79560
79626
|
return null;
|
|
79561
79627
|
}
|
|
@@ -79570,7 +79636,7 @@ function readSlot(accountId, io = fsAgyIo()) {
|
|
|
79570
79636
|
function writeSlot(accountId, slot, io) {
|
|
79571
79637
|
const d = slotDir(io, accountId);
|
|
79572
79638
|
mkdirSync19(d, { recursive: true, mode: 448 });
|
|
79573
|
-
const p3 =
|
|
79639
|
+
const p3 = join46(d, "slot.enc");
|
|
79574
79640
|
writeFileSync19(p3, encryptSecret(JSON.stringify(slot)), { mode: 384 });
|
|
79575
79641
|
chmodSync11(p3, 384);
|
|
79576
79642
|
}
|
|
@@ -79783,7 +79849,7 @@ init_src();
|
|
|
79783
79849
|
init_db();
|
|
79784
79850
|
import { createHash as createHash20, randomUUID as randomUUID13 } from "node:crypto";
|
|
79785
79851
|
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
79786
|
-
import { homedir as
|
|
79852
|
+
import { homedir as homedir13 } from "node:os";
|
|
79787
79853
|
import { readFileSync as readFileSync23, realpathSync as realpathSync11, statSync as statSync7 } from "node:fs";
|
|
79788
79854
|
import { isAbsolute as isAbsolute12, relative as relative8, resolve as resolve24 } from "node:path";
|
|
79789
79855
|
|
|
@@ -79793,7 +79859,7 @@ init_session_event_spool();
|
|
|
79793
79859
|
init_ingress_policy();
|
|
79794
79860
|
import { constants as constants4 } from "node:fs";
|
|
79795
79861
|
import { mkdir as mkdir8, open as open4, readdir as readdir9, rm as rm7 } from "node:fs/promises";
|
|
79796
|
-
import { join as
|
|
79862
|
+
import { join as join47 } from "node:path";
|
|
79797
79863
|
var MAX_EVENT_BYTES = 1e6;
|
|
79798
79864
|
var MAX_FILES_PER_DRAIN = 1e3;
|
|
79799
79865
|
var SESSION_ID_RE = /^[a-z0-9_-]+$/;
|
|
@@ -79844,7 +79910,7 @@ async function drainSpool(app3, root, observation, host2) {
|
|
|
79844
79910
|
let readBuffer;
|
|
79845
79911
|
for (const session of await readdir9(root, { withFileTypes: true })) {
|
|
79846
79912
|
if (!session.isDirectory() || !SESSION_ID_RE.test(session.name)) continue;
|
|
79847
|
-
const dir2 =
|
|
79913
|
+
const dir2 = join47(root, session.name);
|
|
79848
79914
|
let entries4;
|
|
79849
79915
|
try {
|
|
79850
79916
|
entries4 = await readdir9(dir2, { withFileTypes: true });
|
|
@@ -79856,7 +79922,7 @@ async function drainSpool(app3, root, observation, host2) {
|
|
|
79856
79922
|
if (!entry2.isFile() || !entry2.name.endsWith(".json")) continue;
|
|
79857
79923
|
const eventAt = /^(\d{1,15})-/.exec(entry2.name)?.[1];
|
|
79858
79924
|
if (++examined > MAX_FILES_PER_DRAIN) return delivered;
|
|
79859
|
-
const path =
|
|
79925
|
+
const path = join47(dir2, entry2.name);
|
|
79860
79926
|
let payload;
|
|
79861
79927
|
let handle;
|
|
79862
79928
|
try {
|
|
@@ -79974,8 +80040,8 @@ var utf8Prefix = (value, maxBytes) => {
|
|
|
79974
80040
|
return out4;
|
|
79975
80041
|
};
|
|
79976
80042
|
var transcriptRoots = () => [
|
|
79977
|
-
resolve24(process.env.CLAUDE_CONFIG_DIR ?? `${
|
|
79978
|
-
resolve24(process.env.CODEX_HOME ?? `${
|
|
80043
|
+
resolve24(process.env.CLAUDE_CONFIG_DIR ?? `${homedir13()}/.claude`),
|
|
80044
|
+
resolve24(process.env.CODEX_HOME ?? `${homedir13()}/.codex`),
|
|
79979
80045
|
// ADR-0234 · home akun runtime (CLAUDE_CONFIG_DIR/CODEX_HOME per sesi). Transkrip claude akun lewat
|
|
79980
80046
|
// symlink `projects/` tetap berada di bawah akar ini.
|
|
79981
80047
|
resolve24(accountsRoot())
|
|
@@ -80344,13 +80410,13 @@ init_ask();
|
|
|
80344
80410
|
// src/services/decision-guard.ts
|
|
80345
80411
|
init_src2();
|
|
80346
80412
|
init_db();
|
|
80347
|
-
import { mkdirSync as mkdirSync20, readFileSync as readFileSync24, readdirSync as
|
|
80348
|
-
import { join as
|
|
80413
|
+
import { mkdirSync as mkdirSync20, readFileSync as readFileSync24, readdirSync as readdirSync12, rmSync as rmSync14, statSync as statSync8, writeFileSync as writeFileSync20 } from "node:fs";
|
|
80414
|
+
import { join as join48 } from "node:path";
|
|
80349
80415
|
var SESSION_HEADER = "x-hanoman-session";
|
|
80350
80416
|
var SAFE_ID = /^[A-Za-z0-9._-]{1,200}$/;
|
|
80351
80417
|
var launchers = /* @__PURE__ */ new Map();
|
|
80352
80418
|
var STALE_MS = 30 * 24 * 60 * 6e4;
|
|
80353
|
-
var launcherDir = (env = process.env) =>
|
|
80419
|
+
var launcherDir = (env = process.env) => join48(resolveHome(env), "session-launchers");
|
|
80354
80420
|
function recordLauncher(sessionId2, agentId, env = process.env) {
|
|
80355
80421
|
if (!SAFE_ID.test(sessionId2)) return;
|
|
80356
80422
|
const known2 = launchersFor(sessionId2, env);
|
|
@@ -80358,11 +80424,11 @@ function recordLauncher(sessionId2, agentId, env = process.env) {
|
|
|
80358
80424
|
try {
|
|
80359
80425
|
const dir2 = launcherDir(env);
|
|
80360
80426
|
mkdirSync20(dir2, { recursive: true, mode: 448 });
|
|
80361
|
-
writeFileSync20(
|
|
80427
|
+
writeFileSync20(join48(dir2, sessionId2), [...known2].join("\n"), { mode: 384 });
|
|
80362
80428
|
const now = Date.now();
|
|
80363
|
-
for (const f of
|
|
80429
|
+
for (const f of readdirSync12(dir2)) {
|
|
80364
80430
|
try {
|
|
80365
|
-
if (now - statSync8(
|
|
80431
|
+
if (now - statSync8(join48(dir2, f)).mtimeMs > STALE_MS) rmSync14(join48(dir2, f), { force: true });
|
|
80366
80432
|
} catch {
|
|
80367
80433
|
}
|
|
80368
80434
|
}
|
|
@@ -80374,7 +80440,7 @@ function launchersFor(sessionId2, env) {
|
|
|
80374
80440
|
if (hot) return hot;
|
|
80375
80441
|
const ids = /* @__PURE__ */ new Set();
|
|
80376
80442
|
try {
|
|
80377
|
-
for (const l of readFileSync24(
|
|
80443
|
+
for (const l of readFileSync24(join48(launcherDir(env), sessionId2), "utf8").split("\n")) if (l.trim()) ids.add(l.trim());
|
|
80378
80444
|
} catch {
|
|
80379
80445
|
}
|
|
80380
80446
|
launchers.set(sessionId2, ids);
|
|
@@ -80837,7 +80903,7 @@ async function terminal_default(app3, opts2) {
|
|
|
80837
80903
|
if (!repoDir) return { ok: false, code: 409, msg: "project belum punya repoDir" };
|
|
80838
80904
|
if (!s2.cwd.includes("/.worktrees/"))
|
|
80839
80905
|
return { ok: false, code: 409, msg: "belum ada worktree untuk di-review \u2014 jalankan/lanjutkan sesi dulu", empty: "no-worktree" };
|
|
80840
|
-
if (!
|
|
80906
|
+
if (!existsSync23(s2.cwd))
|
|
80841
80907
|
return { ok: false, code: 409, msg: "belum ada worktree untuk di-review \u2014 jalankan/lanjutkan sesi dulu", empty: "no-commit" };
|
|
80842
80908
|
return { ok: true, id: s2.id, repoDir };
|
|
80843
80909
|
};
|
|
@@ -81100,8 +81166,8 @@ async function terminalWorkspaceRoutes(app3) {
|
|
|
81100
81166
|
// src/routes/vps.ts
|
|
81101
81167
|
init_db();
|
|
81102
81168
|
init_src();
|
|
81103
|
-
import { readFileSync as readFileSync29, existsSync as
|
|
81104
|
-
import { homedir as
|
|
81169
|
+
import { readFileSync as readFileSync29, existsSync as existsSync26 } from "node:fs";
|
|
81170
|
+
import { homedir as homedir15 } from "node:os";
|
|
81105
81171
|
|
|
81106
81172
|
// src/vps/catalog/catalog.data.ts
|
|
81107
81173
|
var RAW_SECTIONS = [{ "id": "ssh", "title": "SSH Hardening", "icon": "\u{1F511}", "count": 14 }, { "id": "firewall", "title": "Firewall & Network", "icon": "\u{1F525}", "count": 14 }, { "id": "aapanel", "title": "aaPanel Security", "icon": "\u{1F5A5}\uFE0F", "count": 14 }, { "id": "user", "title": "User & Permission Management", "icon": "\u{1F464}", "count": 14 }, { "id": "system", "title": "System Hardening (OS Level)", "icon": "\u2699\uFE0F", "count": 16 }, { "id": "webserver", "title": "Web Server Hardening", "icon": "\u{1F310}", "count": 14 }, { "id": "database", "title": "Database Security", "icon": "\u{1F5C4}\uFE0F", "count": 15 }, { "id": "ssl", "title": "SSL/TLS & Enkripsi", "icon": "\u{1F512}", "count": 13 }, { "id": "ids", "title": "Intrusion Detection & Prevention", "icon": "\u{1F6A8}", "count": 9 }, { "id": "logging", "title": "Logging & Monitoring", "icon": "\u{1F4CA}", "count": 13 }, { "id": "backup", "title": "Backup & Disaster Recovery", "icon": "\u{1F4BE}", "count": 12 }, { "id": "ddos", "title": "DDoS Protection", "icon": "\u{1F30A}", "count": 11 }, { "id": "kernel", "title": "Kernel Hardening", "icon": "\u{1F9E0}", "count": 12 }, { "id": "malware", "title": "File Integrity & Malware Detection", "icon": "\u{1F9A0}", "count": 7 }, { "id": "enterprise", "title": "Advanced Enterprise & Government Level", "icon": "\u{1F3E2}", "count": 31 }, { "id": "checklist", "title": "Checklist Rutin", "icon": "\u2705", "count": 23 }];
|
|
@@ -81315,7 +81381,7 @@ init_config2();
|
|
|
81315
81381
|
import { spawn as spawn6 } from "node:child_process";
|
|
81316
81382
|
import { mkdtempSync as mkdtempSync4, rmSync as rmSync15, writeFileSync as writeFileSync21 } from "node:fs";
|
|
81317
81383
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
81318
|
-
import { dirname as dirname17, join as
|
|
81384
|
+
import { dirname as dirname17, join as join49 } from "node:path";
|
|
81319
81385
|
var sshBin = () => effectiveStr("HANOMAN_SSH_BIN") ?? "ssh";
|
|
81320
81386
|
function consoleArgv(t) {
|
|
81321
81387
|
return [
|
|
@@ -81330,8 +81396,8 @@ function consoleArgv(t) {
|
|
|
81330
81396
|
];
|
|
81331
81397
|
}
|
|
81332
81398
|
function askpassScript() {
|
|
81333
|
-
const dir2 = mkdtempSync4(
|
|
81334
|
-
const path =
|
|
81399
|
+
const dir2 = mkdtempSync4(join49(tmpdir7(), "hanoman-askpass-"));
|
|
81400
|
+
const path = join49(dir2, "askpass.sh");
|
|
81335
81401
|
writeFileSync21(path, `#!/bin/sh
|
|
81336
81402
|
printf '%s' "$HANOMAN_SSH_PASSWORD"
|
|
81337
81403
|
`, { mode: 448 });
|
|
@@ -81392,8 +81458,8 @@ function sshExec(t, remoteCmd, opts2 = {}) {
|
|
|
81392
81458
|
// src/services/vps-audit.ts
|
|
81393
81459
|
init_db();
|
|
81394
81460
|
init_deps();
|
|
81395
|
-
import { existsSync as
|
|
81396
|
-
import { join as
|
|
81461
|
+
import { existsSync as existsSync24, readFileSync as readFileSync25 } from "node:fs";
|
|
81462
|
+
import { join as join50 } from "node:path";
|
|
81397
81463
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
81398
81464
|
init_sync_notify();
|
|
81399
81465
|
|
|
@@ -81498,7 +81564,7 @@ var packagedScript = (f) => fileURLToPath5(new URL(`../scripts/vps/${f}`, import
|
|
|
81498
81564
|
var moduleDir2 = () => fileURLToPath5(new URL(".", import.meta.url));
|
|
81499
81565
|
var scriptPath = (f) => {
|
|
81500
81566
|
const packed = packagedScript(f);
|
|
81501
|
-
return
|
|
81567
|
+
return existsSync24(packed) ? packed : join50(repoRoot(moduleDir2()), "server", "scripts", "vps", f);
|
|
81502
81568
|
};
|
|
81503
81569
|
async function itemStatesOf(vpsId) {
|
|
81504
81570
|
const rows = await prisma.vpsItemState.findMany({ where: { vpsId } });
|
|
@@ -81743,30 +81809,30 @@ async function listVpsWithScore() {
|
|
|
81743
81809
|
init_src2();
|
|
81744
81810
|
init_config2();
|
|
81745
81811
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
81746
|
-
import { chmodSync as chmodSync12, copyFileSync, existsSync as
|
|
81747
|
-
import { homedir as
|
|
81748
|
-
import { join as
|
|
81812
|
+
import { chmodSync as chmodSync12, copyFileSync, existsSync as existsSync25, mkdirSync as mkdirSync21, readFileSync as readFileSync28, unlinkSync as unlinkSync5 } from "node:fs";
|
|
81813
|
+
import { homedir as homedir14 } from "node:os";
|
|
81814
|
+
import { join as join51 } from "node:path";
|
|
81749
81815
|
var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? resolveDataDirs().sshKeys;
|
|
81750
81816
|
var KEY_FILES = ["id_ed25519", "id_ed25519.pub"];
|
|
81751
81817
|
function adoptLegacyKey(dir2) {
|
|
81752
81818
|
if (effectiveStr("HANOMAN_SSH_KEY_DIR")) return;
|
|
81753
|
-
const legacy =
|
|
81819
|
+
const legacy = join51(homedir14(), ".hanoman");
|
|
81754
81820
|
if (legacy === dir2) return;
|
|
81755
|
-
if (!KEY_FILES.every((f) =>
|
|
81821
|
+
if (!KEY_FILES.every((f) => existsSync25(join51(legacy, f)))) return;
|
|
81756
81822
|
mkdirSync21(dir2, { recursive: true, mode: 448 });
|
|
81757
81823
|
for (const f of KEY_FILES) {
|
|
81758
|
-
copyFileSync(
|
|
81759
|
-
chmodSync12(
|
|
81824
|
+
copyFileSync(join51(legacy, f), join51(dir2, f));
|
|
81825
|
+
chmodSync12(join51(dir2, f), 384);
|
|
81760
81826
|
}
|
|
81761
|
-
for (const f of KEY_FILES) unlinkSync5(
|
|
81827
|
+
for (const f of KEY_FILES) unlinkSync5(join51(legacy, f));
|
|
81762
81828
|
console.log(`vps: key SSH dipindah dari ${legacy} ke ${dir2} (SPEC-846 \u2014 satu batas backup)`);
|
|
81763
81829
|
}
|
|
81764
81830
|
function ensureHanomanKey() {
|
|
81765
81831
|
const dir2 = keyDir();
|
|
81766
|
-
const privPath =
|
|
81832
|
+
const privPath = join51(dir2, KEY_FILES[0]);
|
|
81767
81833
|
const pubPath = `${privPath}.pub`;
|
|
81768
|
-
if (!
|
|
81769
|
-
if (!
|
|
81834
|
+
if (!existsSync25(privPath)) adoptLegacyKey(dir2);
|
|
81835
|
+
if (!existsSync25(privPath)) {
|
|
81770
81836
|
mkdirSync21(dir2, { recursive: true, mode: 448 });
|
|
81771
81837
|
execFileSync11(
|
|
81772
81838
|
"ssh-keygen",
|
|
@@ -81794,7 +81860,7 @@ init_session_launch_gate();
|
|
|
81794
81860
|
init_settings3();
|
|
81795
81861
|
init_sync_notify();
|
|
81796
81862
|
function keyMissing(v) {
|
|
81797
|
-
return !!v.keyPath && !
|
|
81863
|
+
return !!v.keyPath && !existsSync26(v.keyPath);
|
|
81798
81864
|
}
|
|
81799
81865
|
async function vps_default(app3) {
|
|
81800
81866
|
app3.get("/vps", async () => listVpsWithScore());
|
|
@@ -82047,7 +82113,7 @@ async function vps_default(app3) {
|
|
|
82047
82113
|
const v = await prisma.vps.findUnique({ where: { id: req.params.id } });
|
|
82048
82114
|
if (!v) return reply.code(404).send({ error: "not found" });
|
|
82049
82115
|
if (keyMissing(v)) return reply.code(409).send({ error: "key VPS tidak ada di mesin ini", keyMissing: true });
|
|
82050
|
-
const s2 = await createOperatorSession(`vps-console:${v.id}`,
|
|
82116
|
+
const s2 = await createOperatorSession(`vps-console:${v.id}`, homedir15(), { id: `vpsc-${v.id}`, command: consoleArgv(v) });
|
|
82051
82117
|
return reply.code(201).send({ id: s2.id });
|
|
82052
82118
|
});
|
|
82053
82119
|
app3.post("/vps/:id/session", async (req, reply) => {
|
|
@@ -82056,7 +82122,7 @@ async function vps_default(app3) {
|
|
|
82056
82122
|
if (keyMissing(v)) return reply.code(409).send({ error: "key VPS tidak ada di mesin ini", keyMissing: true });
|
|
82057
82123
|
const checks = v.audit ?? [];
|
|
82058
82124
|
const { model, effort: effort2 } = await sessionModel();
|
|
82059
|
-
const s2 = await createAgentSession(`vps:${v.id}`,
|
|
82125
|
+
const s2 = await createAgentSession(`vps:${v.id}`, homedir15(), {
|
|
82060
82126
|
model,
|
|
82061
82127
|
effort: effort2,
|
|
82062
82128
|
prompt: [
|
|
@@ -82083,7 +82149,7 @@ init_codex_limits();
|
|
|
82083
82149
|
init_antigravity_limits();
|
|
82084
82150
|
init_keychain();
|
|
82085
82151
|
import { readFileSync as readFileSync30 } from "node:fs";
|
|
82086
|
-
import { join as
|
|
82152
|
+
import { join as join52 } from "node:path";
|
|
82087
82153
|
var TTL_MS5 = 3e4;
|
|
82088
82154
|
var UNAVAILABLE4 = { status: "unavailable", windows: [], fetchedAt: null };
|
|
82089
82155
|
function readClaudeAccountToken(homeDir, platform2 = process.platform) {
|
|
@@ -82092,7 +82158,7 @@ function readClaudeAccountToken(homeDir, platform2 = process.platform) {
|
|
|
82092
82158
|
if (t) return t;
|
|
82093
82159
|
}
|
|
82094
82160
|
try {
|
|
82095
|
-
const t = JSON.parse(readFileSync30(
|
|
82161
|
+
const t = JSON.parse(readFileSync30(join52(homeDir, ".credentials.json"), "utf8"))?.claudeAiOauth?.accessToken;
|
|
82096
82162
|
return typeof t === "string" && t ? t : null;
|
|
82097
82163
|
} catch {
|
|
82098
82164
|
return null;
|
|
@@ -82132,7 +82198,7 @@ async function loadRow(row, d) {
|
|
|
82132
82198
|
return windows ? { status: "ok", windows, fetchedAt: new Date(d.now()).toISOString() } : UNAVAILABLE4;
|
|
82133
82199
|
}
|
|
82134
82200
|
if (row.runtime === "codex") {
|
|
82135
|
-
const c = await d.codexLimits(
|
|
82201
|
+
const c = await d.codexLimits(join52(row.homeDir, "sessions"));
|
|
82136
82202
|
return { status: c.status, windows: c.windows, fetchedAt: c.fetchedAt };
|
|
82137
82203
|
}
|
|
82138
82204
|
return d.antigravity(row);
|
|
@@ -82168,7 +82234,7 @@ init_session_sandbox();
|
|
|
82168
82234
|
init_src();
|
|
82169
82235
|
init_pty();
|
|
82170
82236
|
init_session_launch_gate();
|
|
82171
|
-
import { homedir as
|
|
82237
|
+
import { homedir as homedir16 } from "node:os";
|
|
82172
82238
|
var LOGIN_PREFIX = "acct-login-";
|
|
82173
82239
|
var loginSessionId = (accountId) => `${LOGIN_PREFIX}${accountId.replace(/[^a-z0-9]/gi, "-")}`;
|
|
82174
82240
|
function accountIdForLoginSession(sessionId2) {
|
|
@@ -82206,7 +82272,7 @@ async function openLoginSession(accountId, opts2) {
|
|
|
82206
82272
|
}
|
|
82207
82273
|
const argv = loginArgv(runtime, opts2);
|
|
82208
82274
|
beforeLogin(runtime, accountId);
|
|
82209
|
-
const s2 = await createOperatorSession(`account:${accountId}`, homeDir ??
|
|
82275
|
+
const s2 = await createOperatorSession(`account:${accountId}`, homeDir ?? homedir16(), {
|
|
82210
82276
|
id: loginSessionId(accountId),
|
|
82211
82277
|
command: argv,
|
|
82212
82278
|
env
|
|
@@ -82375,7 +82441,7 @@ init_src2();
|
|
|
82375
82441
|
init_src();
|
|
82376
82442
|
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
82377
82443
|
import { readFile as readFile9, writeFile as writeFile6, rename as rename5, mkdir as mkdir9, rm as rm9 } from "node:fs/promises";
|
|
82378
|
-
import { dirname as dirname18, join as
|
|
82444
|
+
import { dirname as dirname18, join as join54 } from "node:path";
|
|
82379
82445
|
|
|
82380
82446
|
// src/services/model-catalog-parser.ts
|
|
82381
82447
|
init_zod();
|
|
@@ -82482,7 +82548,7 @@ init_session_sandbox();
|
|
|
82482
82548
|
import { spawn as spawn7 } from "node:child_process";
|
|
82483
82549
|
import { mkdtemp as mkdtemp2, rm as rm8 } from "node:fs/promises";
|
|
82484
82550
|
import { tmpdir as tmpdir8 } from "node:os";
|
|
82485
|
-
import { join as
|
|
82551
|
+
import { join as join53 } from "node:path";
|
|
82486
82552
|
var MAX_BYTES = 4 * 1024 * 1024;
|
|
82487
82553
|
var TIMEOUT_MS = 2e4;
|
|
82488
82554
|
var quote3 = (s2) => "'" + s2.replaceAll("'", "'\\''") + "'";
|
|
@@ -82581,7 +82647,7 @@ function runCatalogCommand(command, agent, cwd, env, timeoutMs = TIMEOUT_MS) {
|
|
|
82581
82647
|
}
|
|
82582
82648
|
async function probeModelCatalog(agent) {
|
|
82583
82649
|
assertAntigravityLaunch(agent);
|
|
82584
|
-
const cwd = await mkdtemp2(
|
|
82650
|
+
const cwd = await mkdtemp2(join53(tmpdir8(), "hanoman-models-"));
|
|
82585
82651
|
const env = agentEnvironment(agent, process.env);
|
|
82586
82652
|
for (const key of ["HANOMAN_CLAUDE_BIN", "HANOMAN_CODEX_BIN", "HANOMAN_PODMAN_BIN", "HANOMAN_ANTIGRAVITY_BIN"]) {
|
|
82587
82653
|
const value = effectiveStr(key);
|
|
@@ -82692,7 +82758,7 @@ async function writeModelCatalogCache(file, catalog, current = () => true) {
|
|
|
82692
82758
|
await rm9(temp, { force: true });
|
|
82693
82759
|
}
|
|
82694
82760
|
}
|
|
82695
|
-
var cacheFile = () =>
|
|
82761
|
+
var cacheFile = () => join54(resolveHome(), "model-catalog.json");
|
|
82696
82762
|
var modelCatalogService = createModelCatalogService({
|
|
82697
82763
|
probe: probeModelCatalog,
|
|
82698
82764
|
key: catalogProbeKey,
|
|
@@ -83882,7 +83948,7 @@ init_pty();
|
|
|
83882
83948
|
init_config5();
|
|
83883
83949
|
init_inbox();
|
|
83884
83950
|
import { mkdirSync as mkdirSync22, writeFileSync as writeFileSync22 } from "node:fs";
|
|
83885
|
-
import { join as
|
|
83951
|
+
import { join as join55 } from "node:path";
|
|
83886
83952
|
init_human_input();
|
|
83887
83953
|
var idle = /* @__PURE__ */ new Set();
|
|
83888
83954
|
var sending = /* @__PURE__ */ new Set();
|
|
@@ -83893,7 +83959,7 @@ function isLeadIdle(id2) {
|
|
|
83893
83959
|
return idle.has(id2);
|
|
83894
83960
|
}
|
|
83895
83961
|
var HUMAN_QUIET_MS = 12e4;
|
|
83896
|
-
var batchDir = (projectId) =>
|
|
83962
|
+
var batchDir = (projectId) => join55(resolveHome(), "lead-batches", projectId);
|
|
83897
83963
|
var briefFn = async () => "(situasi tak tersedia \u2014 panggil /brief)";
|
|
83898
83964
|
function setBriefBuilder(fn) {
|
|
83899
83965
|
briefFn = fn;
|
|
@@ -83914,7 +83980,7 @@ var prodCourierDeps = {
|
|
|
83914
83980
|
write: (projectId, text2) => {
|
|
83915
83981
|
const dir2 = batchDir(projectId);
|
|
83916
83982
|
mkdirSync22(dir2, { recursive: true });
|
|
83917
|
-
const file =
|
|
83983
|
+
const file = join55(dir2, `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.md`);
|
|
83918
83984
|
writeFileSync22(file, text2);
|
|
83919
83985
|
return file;
|
|
83920
83986
|
},
|
|
@@ -84716,7 +84782,7 @@ init_src();
|
|
|
84716
84782
|
init_db();
|
|
84717
84783
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
84718
84784
|
import { mkdir as mkdir10, rename as rename6, writeFile as writeFile7 } from "node:fs/promises";
|
|
84719
|
-
import { join as
|
|
84785
|
+
import { join as join56 } from "node:path";
|
|
84720
84786
|
|
|
84721
84787
|
// src/services/device-auth.ts
|
|
84722
84788
|
function bearerToken2(req) {
|
|
@@ -84974,9 +85040,9 @@ async function sync_default(app3) {
|
|
|
84974
85040
|
const deviceId = req.device.id;
|
|
84975
85041
|
const withTranscriptKey = await Promise.all(parsed.data.entries.map(async (e) => {
|
|
84976
85042
|
if (!e.transcript) return e;
|
|
84977
|
-
const dir2 =
|
|
85043
|
+
const dir2 = join56(resolveDataDirs().home, "remote-transcripts", deviceId);
|
|
84978
85044
|
await mkdir10(dir2, { recursive: true, mode: 448 });
|
|
84979
|
-
const final =
|
|
85045
|
+
const final = join56(dir2, `${e.seq}.txt`);
|
|
84980
85046
|
const tmp = `${final}.tmp`;
|
|
84981
85047
|
await writeFile7(tmp, e.transcript, { encoding: "utf8", mode: 384 });
|
|
84982
85048
|
await rename6(tmp, final);
|
|
@@ -88381,7 +88447,7 @@ init_uploads();
|
|
|
88381
88447
|
init_ask_via2();
|
|
88382
88448
|
import { createHash as createHash23 } from "node:crypto";
|
|
88383
88449
|
import { execFileSync as execFileSync12 } from "node:child_process";
|
|
88384
|
-
import { join as
|
|
88450
|
+
import { join as join58 } from "node:path";
|
|
88385
88451
|
var QaSessionError = class extends Error {
|
|
88386
88452
|
constructor(code, message, needsBind = false) {
|
|
88387
88453
|
super(message);
|
|
@@ -88408,7 +88474,7 @@ async function startQaFindingSession(d, fid) {
|
|
|
88408
88474
|
const saved = await saveSessionUpload(id2, bytes, a.mimeType);
|
|
88409
88475
|
files2.push(`${JSON.stringify(a.filename)} (${a.mimeType}): ${saved.path}`);
|
|
88410
88476
|
}
|
|
88411
|
-
const wt =
|
|
88477
|
+
const wt = join58(repoDir, ".worktrees", id2);
|
|
88412
88478
|
const branch = `qa/${id2.slice(3)}`;
|
|
88413
88479
|
const reused = realGit.worktreeAlive(wt);
|
|
88414
88480
|
try {
|
|
@@ -89953,7 +90019,7 @@ init_src();
|
|
|
89953
90019
|
init_db();
|
|
89954
90020
|
import { mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync5, rmSync as rmSync16, writeFileSync as writeFileSync23 } from "node:fs";
|
|
89955
90021
|
import { tmpdir as tmpdir9 } from "node:os";
|
|
89956
|
-
import { join as
|
|
90022
|
+
import { join as join59 } from "node:path";
|
|
89957
90023
|
var STAGE_LABEL = {
|
|
89958
90024
|
brainstorming: "Dirumuskan",
|
|
89959
90025
|
objective: "Dirumuskan",
|
|
@@ -89996,7 +90062,7 @@ function renderTicketDoc(tickets2) {
|
|
|
89996
90062
|
${baris.join("\n")}`;
|
|
89997
90063
|
}
|
|
89998
90064
|
async function buildChatWorkspace(projectId) {
|
|
89999
|
-
const dir2 = mkdtempSync5(
|
|
90065
|
+
const dir2 = mkdtempSync5(join59(tmpdir9(), "hanoman-portal-chat-"));
|
|
90000
90066
|
try {
|
|
90001
90067
|
const project = await prisma.project.findUnique({
|
|
90002
90068
|
where: { id: projectId },
|
|
@@ -90011,7 +90077,7 @@ async function buildChatWorkspace(projectId) {
|
|
|
90011
90077
|
});
|
|
90012
90078
|
const files2 = [];
|
|
90013
90079
|
const tulis = (rel, isi) => {
|
|
90014
|
-
writeFileSync23(
|
|
90080
|
+
writeFileSync23(join59(dir2, rel), isi, { mode: 384 });
|
|
90015
90081
|
files2.push(rel);
|
|
90016
90082
|
};
|
|
90017
90083
|
tulis("project.md", renderProjectDoc(project));
|
|
@@ -90019,11 +90085,11 @@ async function buildChatWorkspace(projectId) {
|
|
|
90019
90085
|
tulis("laporan.md", renderTicketDoc(tickets2.map((t) => toPortalTicket(t, null))));
|
|
90020
90086
|
const prds = await listPrds(projectId);
|
|
90021
90087
|
if (prds.length) {
|
|
90022
|
-
mkdirSync23(
|
|
90088
|
+
mkdirSync23(join59(dir2, "dokumen"), { mode: 448 });
|
|
90023
90089
|
for (const prd of prds) {
|
|
90024
90090
|
const isi = await readPrd(projectId, prd.path);
|
|
90025
90091
|
if (!isi) continue;
|
|
90026
|
-
tulis(
|
|
90092
|
+
tulis(join59("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
|
|
90027
90093
|
}
|
|
90028
90094
|
}
|
|
90029
90095
|
return { dir: dir2, files: files2, cleanup: () => rmSync16(dir2, { recursive: true, force: true }) };
|
|
@@ -90550,13 +90616,13 @@ init_db();
|
|
|
90550
90616
|
// src/services/bootstrap.ts
|
|
90551
90617
|
import { createHash as createHash24, randomBytes as randomBytes11, timingSafeEqual as timingSafeEqual5 } from "node:crypto";
|
|
90552
90618
|
import { chmod as chmod2, lstat as lstat3, mkdir as mkdir11, readFile as readFile10, unlink as unlink5, writeFile as writeFile8 } from "node:fs/promises";
|
|
90553
|
-
import { join as
|
|
90619
|
+
import { join as join60 } from "node:path";
|
|
90554
90620
|
var TTL_MS8 = 15 * 6e4;
|
|
90555
90621
|
var SETUP_TOKEN_FILE = "setup.token";
|
|
90556
90622
|
var BootstrapError = class extends Error {
|
|
90557
90623
|
code = "BOOTSTRAP_PROOF";
|
|
90558
90624
|
};
|
|
90559
|
-
var tokenPath = (home3) =>
|
|
90625
|
+
var tokenPath = (home3) => join60(home3, SETUP_TOKEN_FILE);
|
|
90560
90626
|
async function readStored(home3) {
|
|
90561
90627
|
try {
|
|
90562
90628
|
const info = await lstat3(tokenPath(home3));
|
|
@@ -91394,7 +91460,7 @@ init_event_log();
|
|
|
91394
91460
|
init_transcript_store();
|
|
91395
91461
|
init_src2();
|
|
91396
91462
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
91397
|
-
import { join as
|
|
91463
|
+
import { join as join61 } from "node:path";
|
|
91398
91464
|
function decodeCursor(c) {
|
|
91399
91465
|
if (!c) return null;
|
|
91400
91466
|
try {
|
|
@@ -91432,7 +91498,7 @@ async function readRemoteTranscript(id2) {
|
|
|
91432
91498
|
const row = await prisma.logEntry.findUnique({ where: { id: id2 } });
|
|
91433
91499
|
if (!row || row.lane !== "transcript" || !row.transcriptKey) return null;
|
|
91434
91500
|
if (row.deviceId === "local") return readTranscript(row.transcriptKey);
|
|
91435
|
-
const path =
|
|
91501
|
+
const path = join61(resolveDataDirs().home, "remote-transcripts", row.transcriptKey);
|
|
91436
91502
|
try {
|
|
91437
91503
|
return await readFile11(path, "utf8");
|
|
91438
91504
|
} catch {
|
|
@@ -91629,7 +91695,7 @@ var SETUP_PUBLIC = /* @__PURE__ */ new Set([
|
|
|
91629
91695
|
"POST /api/setup"
|
|
91630
91696
|
]);
|
|
91631
91697
|
function buildApp({ requireAuth = true, agentDocFile, env = process.env } = {}) {
|
|
91632
|
-
const docFile = agentDocFile !== void 0 ? agentDocFile : pickGuideFile(dirname19(fileURLToPath6(import.meta.url)), env,
|
|
91698
|
+
const docFile = agentDocFile !== void 0 ? agentDocFile : pickGuideFile(dirname19(fileURLToPath6(import.meta.url)), env, existsSync27);
|
|
91633
91699
|
const ingress = loadIngressPolicy(env);
|
|
91634
91700
|
const app3 = Fastify({ logger: false, trustProxy: trustProxyFromEnv(env) });
|
|
91635
91701
|
app3.setErrorHandler((error, _req, reply) => {
|
|
@@ -91779,7 +91845,7 @@ function buildApp({ requireAuth = true, agentDocFile, env = process.env } = {})
|
|
|
91779
91845
|
await api.register(qaBacklog);
|
|
91780
91846
|
}, { prefix: "/api" });
|
|
91781
91847
|
if (shouldServeWeb(env)) {
|
|
91782
|
-
const dist = pickWebDir(dirname19(fileURLToPath6(import.meta.url)), env,
|
|
91848
|
+
const dist = pickWebDir(dirname19(fileURLToPath6(import.meta.url)), env, existsSync27);
|
|
91783
91849
|
if (dist) {
|
|
91784
91850
|
app3.register(fastifyStatic, { root: dist });
|
|
91785
91851
|
app3.setNotFoundHandler((req, reply) => req.url.startsWith("/api") ? reply.code(404).send({ error: "not found" }) : req.url.startsWith("/assets/") ? reply.code(404).type("text/plain").send("not found") : reply.sendFile("index.html"));
|
|
@@ -92179,9 +92245,46 @@ function registerTriaseSource() {
|
|
|
92179
92245
|
// src/services/session-boot-resume.ts
|
|
92180
92246
|
init_src();
|
|
92181
92247
|
init_db();
|
|
92248
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
92182
92249
|
init_session_launch();
|
|
92250
|
+
init_session_launch_gate();
|
|
92183
92251
|
init_session_admission2();
|
|
92184
92252
|
init_notifications2();
|
|
92253
|
+
init_local_binding();
|
|
92254
|
+
init_session_phases();
|
|
92255
|
+
init_pty();
|
|
92256
|
+
var prodFreeStart = async (c) => {
|
|
92257
|
+
if (!existsSync28(c.cwd)) throw new Error(`direktori ${c.cwd} sudah tidak ada`);
|
|
92258
|
+
if (c.kind === "shell") {
|
|
92259
|
+
await createOperatorSession(c.projectId, c.cwd, { id: c.sessionId, command: [shellBin()] });
|
|
92260
|
+
return;
|
|
92261
|
+
}
|
|
92262
|
+
if (c.accountId && !accountExists(c.accountId))
|
|
92263
|
+
throw new Error(`akun runtime ${c.accountId} sudah dihapus \u2014 buka sesi baru dan pilih akun lain`);
|
|
92264
|
+
const base3 = {
|
|
92265
|
+
id: c.sessionId,
|
|
92266
|
+
agent: c.agent,
|
|
92267
|
+
model: c.model,
|
|
92268
|
+
effort: c.effort,
|
|
92269
|
+
accountId: c.accountId,
|
|
92270
|
+
...c.branch ? { branch: c.branch } : {},
|
|
92271
|
+
...c.agentSessionId ? { agentSessionId: c.agentSessionId, resumeAgentSession: true } : {}
|
|
92272
|
+
};
|
|
92273
|
+
if (c.kind === "terminal" || c.kind === "worktree") {
|
|
92274
|
+
await createOperatorSession(c.projectId, c.cwd, base3, { exempt: false });
|
|
92275
|
+
return;
|
|
92276
|
+
}
|
|
92277
|
+
if (c.agent !== "claude" || !c.agentSessionId)
|
|
92278
|
+
throw new Error("percakapan agennya tak tercatat \u2014 mulai ulang dari layar project");
|
|
92279
|
+
const repoDir = await resolveRepoDir(c.projectId);
|
|
92280
|
+
if (!repoDir) throw new Error("project belum di-bind ke checkout lokal");
|
|
92281
|
+
await createOperatorSession(c.projectId, c.cwd, {
|
|
92282
|
+
...base3,
|
|
92283
|
+
flow: c.flow,
|
|
92284
|
+
phaseFile: phaseFilePath(repoDir, c.sessionId),
|
|
92285
|
+
decisionFile: decisionFilePath(repoDir, c.sessionId)
|
|
92286
|
+
}, { exempt: false });
|
|
92287
|
+
};
|
|
92185
92288
|
var prodResumeStart = async (spec, runtime) => {
|
|
92186
92289
|
if (runtime?.accountId && !accountExists(runtime.accountId))
|
|
92187
92290
|
throw new Error(`akun runtime ${runtime.accountId} sudah dihapus \u2014 lanjutkan manual dan pilih akun lain`);
|
|
@@ -92194,12 +92297,50 @@ var prodDeps6 = {
|
|
|
92194
92297
|
// titik waktu paling rawan menumpuk kandidat resume. Gerbang dependency ADR-0093 tak berubah.
|
|
92195
92298
|
startSpec: prodResumeStart,
|
|
92196
92299
|
recordFail: recordFailure,
|
|
92197
|
-
recordDeferred: recordFailure
|
|
92300
|
+
recordDeferred: recordFailure,
|
|
92301
|
+
startFree: prodFreeStart,
|
|
92302
|
+
recordFreeIssue: recordFreeResumeIssue
|
|
92198
92303
|
};
|
|
92199
92304
|
async function resumeReconciledSessions(cutoff, deps = prodDeps6) {
|
|
92200
92305
|
const report = { resumed: [], failed: [], deferred: [] };
|
|
92306
|
+
await resumeSpecs(cutoff, deps, report);
|
|
92307
|
+
await resumeFree(cutoff, deps, report);
|
|
92308
|
+
return report;
|
|
92309
|
+
}
|
|
92310
|
+
async function resumeFree(cutoff, deps, report) {
|
|
92311
|
+
if (!deps.startFree) return;
|
|
92312
|
+
const candidates2 = await reconciledFreeSessionsSince(cutoff);
|
|
92313
|
+
const note = deps.recordFreeIssue ?? (async () => {
|
|
92314
|
+
});
|
|
92315
|
+
for (const c of candidates2) {
|
|
92316
|
+
const title = `sesi ${c.sessionId}`;
|
|
92317
|
+
try {
|
|
92318
|
+
await deps.startFree(c);
|
|
92319
|
+
report.resumed.push(c.sessionId);
|
|
92320
|
+
} catch (e) {
|
|
92321
|
+
if (e instanceof LaunchAdmissionError) {
|
|
92322
|
+
await note(
|
|
92323
|
+
c.sessionId,
|
|
92324
|
+
title,
|
|
92325
|
+
c.projectId,
|
|
92326
|
+
`ditunda \u2014 cap/beban host penuh saat boot (${e.kind}). Buka lagi manual saat slot kosong.`
|
|
92327
|
+
);
|
|
92328
|
+
report.deferred.push(c.sessionId);
|
|
92329
|
+
continue;
|
|
92330
|
+
}
|
|
92331
|
+
await note(
|
|
92332
|
+
c.sessionId,
|
|
92333
|
+
title,
|
|
92334
|
+
c.projectId,
|
|
92335
|
+
`gagal dilanjutkan otomatis \u2014 ${e instanceof Error ? e.message : String(e)}`
|
|
92336
|
+
);
|
|
92337
|
+
report.failed.push(c.sessionId);
|
|
92338
|
+
}
|
|
92339
|
+
}
|
|
92340
|
+
}
|
|
92341
|
+
async function resumeSpecs(cutoff, deps, report) {
|
|
92201
92342
|
const specIds = await reconciledSpecIdsSince(cutoff);
|
|
92202
|
-
if (!specIds.length) return
|
|
92343
|
+
if (!specIds.length) return;
|
|
92203
92344
|
const specs = await prisma.spec.findMany({ where: { id: { in: specIds }, stage: { not: "done" } } });
|
|
92204
92345
|
const runtimes = await reconciledRuntimesSince(cutoff);
|
|
92205
92346
|
for (const spec of specs) {
|
|
@@ -92222,7 +92363,6 @@ async function resumeReconciledSessions(cutoff, deps = prodDeps6) {
|
|
|
92222
92363
|
report.failed.push(spec.id);
|
|
92223
92364
|
}
|
|
92224
92365
|
}
|
|
92225
|
-
return report;
|
|
92226
92366
|
}
|
|
92227
92367
|
|
|
92228
92368
|
// src/services/runtime-accounts/install.ts
|
|
@@ -92763,9 +92903,9 @@ async function pruneLogs(now, retention, opts2 = {}) {
|
|
|
92763
92903
|
}
|
|
92764
92904
|
async function reconcileRemoteTranscripts() {
|
|
92765
92905
|
const { readdir: readdir10, unlink: unlink6, stat: stat8 } = await import("node:fs/promises");
|
|
92766
|
-
const { join:
|
|
92906
|
+
const { join: join62 } = await import("node:path");
|
|
92767
92907
|
const { resolveDataDirs: resolveDataDirs2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
92768
|
-
const root =
|
|
92908
|
+
const root = join62(resolveDataDirs2().home, "remote-transcripts");
|
|
92769
92909
|
let deviceDirs;
|
|
92770
92910
|
try {
|
|
92771
92911
|
deviceDirs = await readdir10(root);
|
|
@@ -92779,16 +92919,16 @@ async function reconcileRemoteTranscripts() {
|
|
|
92779
92919
|
for (const dev of deviceDirs) {
|
|
92780
92920
|
let files2;
|
|
92781
92921
|
try {
|
|
92782
|
-
files2 = await readdir10(
|
|
92922
|
+
files2 = await readdir10(join62(root, dev));
|
|
92783
92923
|
} catch {
|
|
92784
92924
|
continue;
|
|
92785
92925
|
}
|
|
92786
92926
|
for (const f of files2) {
|
|
92787
92927
|
const key = `${dev}/${f}`;
|
|
92788
92928
|
if (referenced.has(key)) continue;
|
|
92789
|
-
const st2 = await stat8(
|
|
92929
|
+
const st2 = await stat8(join62(root, dev, f)).catch(() => null);
|
|
92790
92930
|
if (!st2 || Date.now() - st2.mtimeMs < gracePeriodMs) continue;
|
|
92791
|
-
await unlink6(
|
|
92931
|
+
await unlink6(join62(root, dev, f)).catch(() => {
|
|
92792
92932
|
});
|
|
92793
92933
|
orphans++;
|
|
92794
92934
|
}
|
|
@@ -93043,7 +93183,7 @@ bootstrapReady.then(async () => {
|
|
|
93043
93183
|
for (const row of await detectOrphanWorktrees()) {
|
|
93044
93184
|
console.log(`worktree yatim: ${row.projectId} \u2014 ${row.count} menunggu konfirmasi di tab Worktrees`);
|
|
93045
93185
|
}
|
|
93046
|
-
if (n2) {
|
|
93186
|
+
if (n2 && (await getSetting()).autoResumeSessions) {
|
|
93047
93187
|
const { resumed, failed, deferred } = await resumeReconciledSessions(reconcileCutoff);
|
|
93048
93188
|
if (resumed.length)
|
|
93049
93189
|
console.log(`auto-resume: ${resumed.length} sesi dilanjutkan otomatis (${resumed.join(", ")})`);
|