hanoman 0.9.29 → 0.9.30
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 +23 -0
- package/dist/server.js +266 -68
- package/package.json +1 -1
- package/web/assets/{DalangHanomanScreen-BMrikKnt.js → DalangHanomanScreen-Fd7_DcSP.js} +1 -1
- package/web/assets/{DocPreviewModal-5j31WUwI.js → DocPreviewModal-SMRN2K98.js} +1 -1
- package/web/assets/{DocsWorkspace-GkTrniF3.js → DocsWorkspace-pxj0N6QW.js} +1 -1
- package/web/assets/{IdeScreen-CAQfv1_e.js → IdeScreen-Cr9Vqdrc.js} +1 -1
- package/web/assets/{LeadScreen-Da5-1FLe.js → LeadScreen-CQXpJy0y.js} +1 -1
- package/web/assets/{ReviewScreen-B8KHzQpd.js → ReviewScreen-B7NNEjxk.js} +1 -1
- package/web/assets/SchedulerScreen-D3rPdS-F.js +1 -0
- package/web/assets/{SettingsScreen-BxQ3CH2D.js → SettingsScreen-Bx13PWKQ.js} +1 -1
- package/web/assets/{TeamScreen-P6vz88Pp.js → TeamScreen-BBAsYLpT.js} +1 -1
- package/web/assets/{TerminalScreen-B_cagBZd.js → TerminalScreen-DpVp4h1n.js} +1 -1
- package/web/assets/{TriageScreen-IYkxyhdm.js → TriageScreen-wAPnttRe.js} +1 -1
- package/web/assets/{VpsScreen-DFGT5S8Y.js → VpsScreen-DeQVx9Jy.js} +1 -1
- package/web/assets/{fields-CmVwp6QK.js → fields-qnBoIMR_.js} +1 -1
- package/web/assets/{index-C-iSWHn3.js → index-BQw6xQI4.js} +207 -207
- package/web/assets/{index-0T3s-I82.css → index-C3KwOJxI.css} +1 -1
- package/web/assets/{live-lSo-u7M6.js → live-CONO0GTu.js} +1 -1
- package/web/assets/{sched-lead-shared-BTz6aILx.js → sched-lead-shared-CDR0pOqs.js} +1 -1
- package/web/assets/{settings-rebase-CPawFVpp.js → settings-rebase-CpaFw0Tm.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/SchedulerScreen-CBd4O5kw.js +0 -1
package/dist/server.js
CHANGED
|
@@ -5575,7 +5575,7 @@ function resolvePhasePlan(input) {
|
|
|
5575
5575
|
if (!input.nativeAgents || cfg?.enabled === false) return null;
|
|
5576
5576
|
const cells = cfg?.[input.runtime] ?? {};
|
|
5577
5577
|
const coerce3 = input.runtime === "codex" ? coerceCodexEffort : coerceClaudeEffort;
|
|
5578
|
-
const resolve25 = (key,
|
|
5578
|
+
const resolve25 = (key, fallback3) => {
|
|
5579
5579
|
const cell4 = cells[key];
|
|
5580
5580
|
const override = input.phaseOverrides?.[key];
|
|
5581
5581
|
if (input.runtime === "antigravity") {
|
|
@@ -5587,9 +5587,9 @@ function resolvePhasePlan(input) {
|
|
|
5587
5587
|
}
|
|
5588
5588
|
return { model: model2, effort: null };
|
|
5589
5589
|
}
|
|
5590
|
-
const picked = override?.model ?? cell4?.model ??
|
|
5590
|
+
const picked = override?.model ?? cell4?.model ?? fallback3.model;
|
|
5591
5591
|
const model = input.runtime === "claude" && picked === CLAUDE_DEFAULT_ALIAS ? CLAUDE_SUBAGENT_INHERIT : picked;
|
|
5592
|
-
return { model, effort: coerce3(model, override?.effort ?? cell4?.effort ??
|
|
5592
|
+
return { model, effort: coerce3(model, override?.effort ?? cell4?.effort ?? fallback3.effort ?? "medium") };
|
|
5593
5593
|
};
|
|
5594
5594
|
const reviewer = REVIEWER_FLOWS.has(input.flow) ? {
|
|
5595
5595
|
agentName: phaseAgentName(REVIEWER_CELL),
|
|
@@ -9122,6 +9122,17 @@ var init_config_registry = __esm({
|
|
|
9122
9122
|
// runtime
|
|
9123
9123
|
{ key: "HANOMAN_EVENTS_TICK_MS", group: "runtime", label: "Interval events (ms)", kind: "int", apply: "live", category: "knob", example: "1000", default: "1000", min: 100 },
|
|
9124
9124
|
{ key: "HANOMAN_UPDATE_FETCH", group: "runtime", label: "Deteksi update saat boot", kind: "bool", apply: "live", category: "knob", default: "1" },
|
|
9125
|
+
// ADR-0224 · limit Antigravity membaca token agy di Keychain dan memanggil Cloud Code API Google.
|
|
9126
|
+
{
|
|
9127
|
+
key: "HANOMAN_ANTIGRAVITY_LIMITS_FETCH",
|
|
9128
|
+
group: "runtime",
|
|
9129
|
+
label: "Baca limit Antigravity",
|
|
9130
|
+
kind: "bool",
|
|
9131
|
+
apply: "live",
|
|
9132
|
+
category: "knob",
|
|
9133
|
+
default: "1",
|
|
9134
|
+
help: "Membaca token agy di Keychain macOS lalu memanggil API kuota Google (endpoint internal, tak terdokumentasi). Matikan bila tak ingin hanoman menyentuh kredensial agy."
|
|
9135
|
+
},
|
|
9125
9136
|
// SPEC-398 · ADR-0087 · deteksi update kini membandingkan semver dengan registry npm, bukan
|
|
9126
9137
|
// menghitung commit di repo git (HANOMAN_REPO_ROOT dicabut bersama jalur git itu).
|
|
9127
9138
|
{
|
|
@@ -14235,6 +14246,18 @@ var init_system = __esm({
|
|
|
14235
14246
|
build: () => ({ method: "GET", path: "/limits/codex" }),
|
|
14236
14247
|
shape: (raw) => raw
|
|
14237
14248
|
},
|
|
14249
|
+
{
|
|
14250
|
+
name: "hanoman_limits_antigravity",
|
|
14251
|
+
title: "Sisa kuota Antigravity",
|
|
14252
|
+
description: "Sisa kuota Antigravity CLI (agy) per grup model (Gemini; Claude/GPT) untuk jendela mingguan dan 5 jam, berikut kapan resetnya. Dibaca live dari Cloud Code API Google memakai token agy yang sedang hidup dan di-cache 60 detik; token yang kedaluwarsa (agy tak berjalan) memberi `stale` atau `unavailable`, bukan angka karangan. Endpoint terpisah dari hanoman_limits karena sumbernya beda. Tak menuntut capability apa pun.",
|
|
14253
|
+
inputSchema: obj({ properties: {} }),
|
|
14254
|
+
mode: "read",
|
|
14255
|
+
capability: null,
|
|
14256
|
+
samplePath: "/limits/antigravity",
|
|
14257
|
+
sampleMethod: "GET",
|
|
14258
|
+
build: () => ({ method: "GET", path: "/limits/antigravity" }),
|
|
14259
|
+
shape: (raw) => raw
|
|
14260
|
+
},
|
|
14238
14261
|
{
|
|
14239
14262
|
name: "hanoman_overview_get",
|
|
14240
14263
|
title: "Ringkasan kinerja hari ini (Overview)",
|
|
@@ -63404,7 +63427,7 @@ function parseCodexVersion(out4) {
|
|
|
63404
63427
|
return m ? `${m[1]}.${m[2]}.${m[3]}` : null;
|
|
63405
63428
|
}
|
|
63406
63429
|
async function getCodexVersion(now = Date.now()) {
|
|
63407
|
-
if (cache3 && now - cache3.at <
|
|
63430
|
+
if (cache3 && now - cache3.at < TTL_MS5) return cache3.version;
|
|
63408
63431
|
const version = await probeCodexVersion();
|
|
63409
63432
|
cache3 = { at: now, version };
|
|
63410
63433
|
return version;
|
|
@@ -63420,7 +63443,7 @@ async function codexVersionInfo() {
|
|
|
63420
63443
|
function _resetCodexVersionCache() {
|
|
63421
63444
|
cache3 = null;
|
|
63422
63445
|
}
|
|
63423
|
-
var run3, CODEX_MIN_CLIENT,
|
|
63446
|
+
var run3, CODEX_MIN_CLIENT, TTL_MS5, cache3, codexBin2, runVersion;
|
|
63424
63447
|
var init_codex_version = __esm({
|
|
63425
63448
|
"src/services/codex-version.ts"() {
|
|
63426
63449
|
"use strict";
|
|
@@ -63429,7 +63452,7 @@ var init_codex_version = __esm({
|
|
|
63429
63452
|
init_config2();
|
|
63430
63453
|
run3 = promisify16(execFile17);
|
|
63431
63454
|
CODEX_MIN_CLIENT = "0.144.0";
|
|
63432
|
-
|
|
63455
|
+
TTL_MS5 = 5 * 6e4;
|
|
63433
63456
|
cache3 = null;
|
|
63434
63457
|
codexBin2 = () => effectiveStr("HANOMAN_CODEX_BIN") ?? "codex";
|
|
63435
63458
|
runVersion = async (bin, args) => {
|
|
@@ -63440,17 +63463,17 @@ var init_codex_version = __esm({
|
|
|
63440
63463
|
});
|
|
63441
63464
|
|
|
63442
63465
|
// src/services/agent-tool-catalog.ts
|
|
63443
|
-
import { readFileSync as
|
|
63444
|
-
import { homedir as
|
|
63445
|
-
import { join as
|
|
63466
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
63467
|
+
import { homedir as homedir13 } from "node:os";
|
|
63468
|
+
import { join as join48 } from "node:path";
|
|
63446
63469
|
function mcpServerNames(repoDir) {
|
|
63447
63470
|
const names = [];
|
|
63448
|
-
const claudeJson = readJson2(
|
|
63471
|
+
const claudeJson = readJson2(join48(home(), ".claude.json"));
|
|
63449
63472
|
names.push(...serversOf(claudeJson));
|
|
63450
63473
|
if (repoDir) {
|
|
63451
63474
|
const projects = claudeJson?.projects;
|
|
63452
63475
|
if (projects && typeof projects === "object") names.push(...serversOf(projects[repoDir]));
|
|
63453
|
-
names.push(...serversOf(readJson2(
|
|
63476
|
+
names.push(...serversOf(readJson2(join48(repoDir, ".mcp.json"))));
|
|
63454
63477
|
}
|
|
63455
63478
|
names.push(...codexServers());
|
|
63456
63479
|
return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
@@ -63463,10 +63486,10 @@ var init_agent_tool_catalog = __esm({
|
|
|
63463
63486
|
"src/services/agent-tool-catalog.ts"() {
|
|
63464
63487
|
"use strict";
|
|
63465
63488
|
init_src();
|
|
63466
|
-
home = () => process.env.HOME ||
|
|
63489
|
+
home = () => process.env.HOME || homedir13();
|
|
63467
63490
|
readJson2 = (path) => {
|
|
63468
63491
|
try {
|
|
63469
|
-
return JSON.parse(
|
|
63492
|
+
return JSON.parse(readFileSync26(path, "utf8"));
|
|
63470
63493
|
} catch {
|
|
63471
63494
|
return null;
|
|
63472
63495
|
}
|
|
@@ -63479,7 +63502,7 @@ var init_agent_tool_catalog = __esm({
|
|
|
63479
63502
|
codexServers = () => {
|
|
63480
63503
|
let text2;
|
|
63481
63504
|
try {
|
|
63482
|
-
text2 =
|
|
63505
|
+
text2 = readFileSync26(join48(home(), ".codex", "config.toml"), "utf8");
|
|
63483
63506
|
} catch {
|
|
63484
63507
|
return [];
|
|
63485
63508
|
}
|
|
@@ -76879,6 +76902,10 @@ async function fail(req, deps, reason, flowId = null, step = null) {
|
|
|
76879
76902
|
// src/services/lead/detect.ts
|
|
76880
76903
|
var answers = /* @__PURE__ */ new Map();
|
|
76881
76904
|
var capped = /* @__PURE__ */ new Set();
|
|
76905
|
+
function askSignature(a) {
|
|
76906
|
+
const raw = a.questions.length ? a.questions.map((q2) => q2.question).join("\n") : a.message;
|
|
76907
|
+
return raw.toLowerCase().replace(/\s+/g, " ").trim().slice(0, 2e3);
|
|
76908
|
+
}
|
|
76882
76909
|
var failures = /* @__PURE__ */ new Map();
|
|
76883
76910
|
var failCapped = /* @__PURE__ */ new Set();
|
|
76884
76911
|
var failedAt = /* @__PURE__ */ new Map();
|
|
@@ -76950,7 +76977,9 @@ async function admitAsk(s2, deps = prodDetectDeps) {
|
|
|
76950
76977
|
if (!(await deps.optIn()).includes(s2.projectId)) return no("project tak opt-in lead");
|
|
76951
76978
|
if (!leadActive(cfg, s2.projectId)) return no("lead dijeda untuk project ini");
|
|
76952
76979
|
if (deps.exited(s2.id)) return no("pane mati");
|
|
76953
|
-
|
|
76980
|
+
const run6 = answers.get(s2.id);
|
|
76981
|
+
const looping = !!run6 && (s2.sig === void 0 || s2.sig === run6.sig);
|
|
76982
|
+
if (run6 && looping && run6.n >= cfg.maxAutoAnswers) {
|
|
76954
76983
|
if (!capped.has(s2.id)) {
|
|
76955
76984
|
capped.add(s2.id);
|
|
76956
76985
|
const row = await recordDecision({
|
|
@@ -76959,9 +76988,9 @@ async function admitAsk(s2, deps = prodDetectDeps) {
|
|
|
76959
76988
|
sessionId: s2.id,
|
|
76960
76989
|
gate: "detected",
|
|
76961
76990
|
kind: "quality",
|
|
76962
|
-
question: `Sesi ${s2.id} sudah dijawab otomatis ${cfg.maxAutoAnswers}\xD7 berturut-turut.`,
|
|
76963
|
-
answer: "Berhenti menjawab
|
|
76964
|
-
reason: "
|
|
76991
|
+
question: `Sesi ${s2.id} sudah dijawab otomatis ${cfg.maxAutoAnswers}\xD7 berturut-turut untuk pertanyaan yang sama.`,
|
|
76992
|
+
answer: "Berhenti menjawab pertanyaan ini; serahkan ke operator.",
|
|
76993
|
+
reason: "Pertanyaan yang sama terus kembali sesudah dijawab \u2014 pengulangan menandakan lead tak benar-benar membuka jalan buntunya (AC-11, ADR-0223).",
|
|
76965
76994
|
refs: [],
|
|
76966
76995
|
confidence: "tinggi",
|
|
76967
76996
|
action: "none",
|
|
@@ -77017,7 +77046,13 @@ async function answerAsk(ask, s2, deps = prodDetectDeps) {
|
|
|
77017
77046
|
const out4 = { at: chain.at, flowId: chain.flowId, step: chain.step };
|
|
77018
77047
|
if (chain.acted && chain.done) {
|
|
77019
77048
|
if (s2.decisionFile) deps.clearMarker(s2.decisionFile);
|
|
77020
|
-
|
|
77049
|
+
const sig = askSignature(ask);
|
|
77050
|
+
const prev = answers.get(ask.sessionId);
|
|
77051
|
+
if (prev?.sig === sig) prev.n++;
|
|
77052
|
+
else {
|
|
77053
|
+
answers.set(ask.sessionId, { sig, n: 1 });
|
|
77054
|
+
capped.delete(ask.sessionId);
|
|
77055
|
+
}
|
|
77021
77056
|
failures.delete(ask.sessionId);
|
|
77022
77057
|
return { answered: true, reason: "", ...out4 };
|
|
77023
77058
|
}
|
|
@@ -77025,7 +77060,7 @@ async function answerAsk(ask, s2, deps = prodDetectDeps) {
|
|
|
77025
77060
|
failures.set(ask.sessionId, (failures.get(ask.sessionId) ?? 0) + 1);
|
|
77026
77061
|
failedAt.set(ask.sessionId, deps.now());
|
|
77027
77062
|
}
|
|
77028
|
-
return { answered: false, reason: chain.reason, ...out4 };
|
|
77063
|
+
return { answered: false, reason: chain.reason, ...out4, ...chain.mismatch ? { mismatch: true } : {} };
|
|
77029
77064
|
}
|
|
77030
77065
|
async function runChain(ask, s2, deps) {
|
|
77031
77066
|
const sid = ask.sessionId;
|
|
@@ -77043,6 +77078,7 @@ async function runChain(ask, s2, deps) {
|
|
|
77043
77078
|
return { ...r, flowId, step };
|
|
77044
77079
|
};
|
|
77045
77080
|
const stop = (reason, failed = false) => ({ acted, done: false, failed, reason, at, flowId, step });
|
|
77081
|
+
const asked = ask.source === "ask-tool";
|
|
77046
77082
|
if (ask.source === "turn-end") {
|
|
77047
77083
|
const read2 = readCodexTurn(ask.message);
|
|
77048
77084
|
if (!read2.asking) return stop(read2.reason);
|
|
@@ -77079,6 +77115,13 @@ async function runChain(ask, s2, deps) {
|
|
|
77079
77115
|
// `tunggal` di tiap langkah — `chainSteps` kosong tepat di tempat yang paling membutuhkannya.
|
|
77080
77116
|
chain: true
|
|
77081
77117
|
};
|
|
77118
|
+
if (asked && q2) {
|
|
77119
|
+
const seen2 = await waitDialogFor(sid, q2, ask.questions, deps);
|
|
77120
|
+
if (seen2 === "none") return stop("dialog tak muncul di pane \u2014 tak ada yang diketik", true);
|
|
77121
|
+
if (seen2 === "other") {
|
|
77122
|
+
return i === 0 ? { ...stop("dialog di pane adalah pertanyaan lain \u2014 tak ada yang diketik"), mismatch: true } : stop("dialog di pane bukan pertanyaan berikutnya rantai ini \u2014 tak ada yang diketik");
|
|
77123
|
+
}
|
|
77124
|
+
}
|
|
77082
77125
|
let row;
|
|
77083
77126
|
try {
|
|
77084
77127
|
row = await deps.decide({ ...req, flowId }, deps.decideDeps);
|
|
@@ -77102,9 +77145,11 @@ async function runChain(ask, s2, deps) {
|
|
|
77102
77145
|
if (!row) return stop("lead tak menghasilkan keputusan yang berlaku");
|
|
77103
77146
|
if (row.status !== "berlaku") return stop("lead tak menghasilkan keputusan yang berlaku", true);
|
|
77104
77147
|
let before2 = "";
|
|
77105
|
-
if (
|
|
77106
|
-
const text2 =
|
|
77107
|
-
|
|
77148
|
+
if (asked && q2) {
|
|
77149
|
+
const text2 = deps.pane(sid);
|
|
77150
|
+
const screen = readDialogScreen(text2);
|
|
77151
|
+
if (!screen || !dialogFits(screen, q2, ask.questions))
|
|
77152
|
+
return stop("layar dialog berubah selagi lead memutuskan \u2014 tak ada yang diketik");
|
|
77108
77153
|
before2 = dialogKey(text2);
|
|
77109
77154
|
}
|
|
77110
77155
|
if (isTakenOver(sid)) return stop("diambil alih operator");
|
|
@@ -77136,13 +77181,29 @@ async function afterLastAnswer(id2, deps) {
|
|
|
77136
77181
|
}
|
|
77137
77182
|
return "pending";
|
|
77138
77183
|
}
|
|
77139
|
-
async function
|
|
77184
|
+
async function waitDialogFor(id2, q2, all, deps) {
|
|
77185
|
+
let sawDialog = false;
|
|
77140
77186
|
for (let i = 0; i < DIALOG_WAIT_TRIES; i++) {
|
|
77141
|
-
const
|
|
77142
|
-
if (
|
|
77187
|
+
const screen = readDialogScreen(deps.pane(id2));
|
|
77188
|
+
if (screen?.kind === "question") {
|
|
77189
|
+
if (dialogFits(screen, q2, all)) return "fit";
|
|
77190
|
+
sawDialog = true;
|
|
77191
|
+
}
|
|
77143
77192
|
await deps.sleep(CHAIN_POLL_MS);
|
|
77144
77193
|
}
|
|
77145
|
-
return
|
|
77194
|
+
return sawDialog ? "other" : "none";
|
|
77195
|
+
}
|
|
77196
|
+
var bare = (s2) => s2.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "");
|
|
77197
|
+
function dialogFits(screen, q2, all) {
|
|
77198
|
+
if (screen.kind !== "question") return false;
|
|
77199
|
+
if (screen.tabs.length && screen.tabs.length !== all.length) return false;
|
|
77200
|
+
const title = bare(screen.title);
|
|
77201
|
+
const text2 = bare(q2.question);
|
|
77202
|
+
if (title.length >= 6 && text2 && !text2.startsWith(title) && !title.startsWith(text2)) return false;
|
|
77203
|
+
const labels = q2.options.map((o) => bare(o.label)).filter(Boolean);
|
|
77204
|
+
const shown = screen.options.map(bare).filter(Boolean);
|
|
77205
|
+
if (labels.length && shown.length && !shown.some((s2) => labels.some((l) => l.startsWith(s2) || s2.startsWith(l)))) return false;
|
|
77206
|
+
return true;
|
|
77146
77207
|
}
|
|
77147
77208
|
async function waitScreenChange(id2, before2, deps) {
|
|
77148
77209
|
for (let i = 0; i < CHAIN_POLL_TRIES; i++) {
|
|
@@ -77157,6 +77218,8 @@ init_pty();
|
|
|
77157
77218
|
var ASK_BUCKET = { capacity: 5, refillMs: 1e4 };
|
|
77158
77219
|
var GLOBAL_BUCKET = { capacity: 20, refillMs: 500 };
|
|
77159
77220
|
var SEEN_MAX = 512;
|
|
77221
|
+
var ASK_QUEUE_MAX = 8;
|
|
77222
|
+
var ASK_MISMATCH_MAX = 2;
|
|
77160
77223
|
var entries = /* @__PURE__ */ new Map();
|
|
77161
77224
|
var seen = [];
|
|
77162
77225
|
var seenSet = /* @__PURE__ */ new Set();
|
|
@@ -77236,7 +77299,8 @@ async function intakeAsk(input, deps = prodAskDeps) {
|
|
|
77236
77299
|
const verdict = await deps.admit({
|
|
77237
77300
|
id: input.sessionId,
|
|
77238
77301
|
specId: input.specId,
|
|
77239
|
-
projectId: input.projectId
|
|
77302
|
+
projectId: input.projectId,
|
|
77303
|
+
sig: askSignature(input.event)
|
|
77240
77304
|
});
|
|
77241
77305
|
if (!verdict.ok) return { status: "rejected", reason: verdict.reason };
|
|
77242
77306
|
seenSet.add(key);
|
|
@@ -77252,12 +77316,16 @@ async function intakeAsk(input, deps = prodAskDeps) {
|
|
|
77252
77316
|
};
|
|
77253
77317
|
const ask = toAsk({ sessionId: input.sessionId, agent: input.agent, event: input.event, now });
|
|
77254
77318
|
const cur = entries.get(input.sessionId);
|
|
77255
|
-
if (cur?.
|
|
77256
|
-
cur.
|
|
77319
|
+
if (cur?.busy) {
|
|
77320
|
+
cur.queue.push({ ask, ctx, skips: 0 });
|
|
77321
|
+
if (cur.queue.length > ASK_QUEUE_MAX) {
|
|
77322
|
+
const dropped = cur.queue.shift();
|
|
77323
|
+
console.log(`lead ask ${input.sessionId}: antrean penuh \u2014 tanya ${dropped.ask.askId} dibuang`);
|
|
77324
|
+
}
|
|
77257
77325
|
return { status: "accepted" };
|
|
77258
77326
|
}
|
|
77259
77327
|
clearTakeover(input.sessionId);
|
|
77260
|
-
entries.set(input.sessionId, { ask, ctx,
|
|
77328
|
+
entries.set(input.sessionId, { ask, ctx, skips: 0, queue: [], busy: true, running: false });
|
|
77261
77329
|
void prune(deps);
|
|
77262
77330
|
void run2(input.sessionId, deps);
|
|
77263
77331
|
return { status: "accepted" };
|
|
@@ -77311,16 +77379,19 @@ async function run2(sessionId2, deps) {
|
|
|
77311
77379
|
step: r.step,
|
|
77312
77380
|
state: r.answered ? "answered" : "failed"
|
|
77313
77381
|
};
|
|
77314
|
-
if (
|
|
77315
|
-
|
|
77316
|
-
|
|
77382
|
+
if (r.mismatch && e.queue.length && e.skips < ASK_MISMATCH_MAX)
|
|
77383
|
+
e.queue.push({ ask: { ...e.ask, state: "queued" }, ctx: e.ctx, skips: e.skips + 1 });
|
|
77384
|
+
const next = e.queue.shift();
|
|
77385
|
+
if (!next) break;
|
|
77317
77386
|
e.ask = next.ask;
|
|
77318
77387
|
e.ctx = next.ctx;
|
|
77388
|
+
e.skips = next.skips;
|
|
77319
77389
|
}
|
|
77320
77390
|
} finally {
|
|
77321
77391
|
clearQueued(sessionId2);
|
|
77322
77392
|
clearDeciding(sessionId2);
|
|
77323
77393
|
e.running = false;
|
|
77394
|
+
e.busy = false;
|
|
77324
77395
|
inFlight3--;
|
|
77325
77396
|
const nextId = waiting.shift();
|
|
77326
77397
|
if (nextId) void run2(nextId, deps);
|
|
@@ -79247,10 +79318,131 @@ async function getCodexLimits(dir2) {
|
|
|
79247
79318
|
return dto;
|
|
79248
79319
|
}
|
|
79249
79320
|
|
|
79321
|
+
// src/services/antigravity-limits.ts
|
|
79322
|
+
init_config2();
|
|
79323
|
+
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
79324
|
+
import { readFileSync as readFileSync25 } from "node:fs";
|
|
79325
|
+
import { homedir as homedir12 } from "node:os";
|
|
79326
|
+
import { join as join44 } from "node:path";
|
|
79327
|
+
var QUOTA_URL = "https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuotaSummary";
|
|
79328
|
+
var USER_AGENT = "antigravity (hanoman)";
|
|
79329
|
+
var TTL_MS4 = 6e4;
|
|
79330
|
+
var EXPIRY_SKEW_MS = 3e4;
|
|
79331
|
+
var lastOk2 = null;
|
|
79332
|
+
var freshUntil3 = 0;
|
|
79333
|
+
var UNAVAILABLE2 = { status: "unavailable", windows: [], fetchedAt: null };
|
|
79334
|
+
var securityRun2 = (args) => execFileSync9("security", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
79335
|
+
function readAgyToken(run6 = securityRun2) {
|
|
79336
|
+
try {
|
|
79337
|
+
let raw = run6(["find-generic-password", "-s", "gemini", "-a", "antigravity", "-w"]).trim();
|
|
79338
|
+
const prefix = "go-keyring-base64:";
|
|
79339
|
+
if (raw.startsWith(prefix)) raw = Buffer.from(raw.slice(prefix.length), "base64").toString("utf8");
|
|
79340
|
+
const tok = JSON.parse(raw)?.token;
|
|
79341
|
+
if (typeof tok?.access_token !== "string" || !tok.access_token) return null;
|
|
79342
|
+
const expiry = typeof tok.expiry === "string" ? Date.parse(tok.expiry) : NaN;
|
|
79343
|
+
return { accessToken: tok.access_token, expiry: Number.isFinite(expiry) ? expiry : null };
|
|
79344
|
+
} catch {
|
|
79345
|
+
return null;
|
|
79346
|
+
}
|
|
79347
|
+
}
|
|
79348
|
+
var agyHome = () => join44(homedir12(), ".gemini", "antigravity-cli");
|
|
79349
|
+
function defaultProject() {
|
|
79350
|
+
try {
|
|
79351
|
+
return readFileSync25(join44(agyHome(), "cache", "default_project_id.txt"), "utf8").trim() || null;
|
|
79352
|
+
} catch {
|
|
79353
|
+
return null;
|
|
79354
|
+
}
|
|
79355
|
+
}
|
|
79356
|
+
var severityOf2 = (pct3) => pct3 >= 90 ? "critical" : pct3 >= 70 ? "warning" : "normal";
|
|
79357
|
+
function windowLabel2(window2) {
|
|
79358
|
+
if (window2 === "5h") return "Sesi 5 jam";
|
|
79359
|
+
if (window2 === "weekly") return "Mingguan";
|
|
79360
|
+
return window2;
|
|
79361
|
+
}
|
|
79362
|
+
function groupLabel(name2) {
|
|
79363
|
+
if (/^gemini/i.test(name2)) return "Gemini";
|
|
79364
|
+
if (/claude/i.test(name2) && /gpt/i.test(name2)) return "Claude/GPT";
|
|
79365
|
+
return name2;
|
|
79366
|
+
}
|
|
79367
|
+
function mapQuota(json) {
|
|
79368
|
+
const groups = json?.groups;
|
|
79369
|
+
if (!Array.isArray(groups)) return [];
|
|
79370
|
+
return groups.flatMap((g) => {
|
|
79371
|
+
const group2 = groupLabel(g.displayName ?? "");
|
|
79372
|
+
return (Array.isArray(g.buckets) ? g.buckets : []).flatMap((b) => {
|
|
79373
|
+
if (typeof b.remainingFraction !== "number" || !b.bucketId) return [];
|
|
79374
|
+
const remaining = Math.max(0, Math.min(1, b.remainingFraction));
|
|
79375
|
+
const usedPct = Math.round((1 - remaining) * 100);
|
|
79376
|
+
const label2 = windowLabel2(b.window ?? "");
|
|
79377
|
+
return [{
|
|
79378
|
+
key: `antigravity:${b.bucketId}`,
|
|
79379
|
+
label: group2 ? `${group2} \xB7 ${label2}` : label2,
|
|
79380
|
+
usedPct,
|
|
79381
|
+
resetsAt: b.resetTime ?? null,
|
|
79382
|
+
severity: severityOf2(usedPct),
|
|
79383
|
+
// API tak punya padanan `is_active` claude. Yang paling dekat: bucket yang HABIS.
|
|
79384
|
+
isActive: remaining === 0
|
|
79385
|
+
}];
|
|
79386
|
+
});
|
|
79387
|
+
});
|
|
79388
|
+
}
|
|
79389
|
+
function fallback2() {
|
|
79390
|
+
return lastOk2 ? { ...lastOk2, status: "stale" } : UNAVAILABLE2;
|
|
79391
|
+
}
|
|
79392
|
+
var prodDeps = {
|
|
79393
|
+
platform: process.platform,
|
|
79394
|
+
// `HANOMAN_ANTIGRAVITY_LIMITS_FETCH=0` mematikan sumbernya sama sekali (cermin
|
|
79395
|
+
// `HANOMAN_UPDATE_FETCH`): suite test server berjalan di macOS operator, dan route ini tak boleh
|
|
79396
|
+
// membaca Keychain asli lalu memanggil Google dari dalam test.
|
|
79397
|
+
token: () => effectiveStr("HANOMAN_ANTIGRAVITY_LIMITS_FETCH") === "0" ? null : readAgyToken(),
|
|
79398
|
+
project: defaultProject,
|
|
79399
|
+
fetch: (...a) => fetch(...a),
|
|
79400
|
+
now: Date.now
|
|
79401
|
+
};
|
|
79402
|
+
async function fetchQuota(deps) {
|
|
79403
|
+
if (deps.platform !== "darwin") return fallback2();
|
|
79404
|
+
const tok = deps.token();
|
|
79405
|
+
if (!tok) return fallback2();
|
|
79406
|
+
if (tok.expiry !== null && tok.expiry - EXPIRY_SKEW_MS <= deps.now()) return fallback2();
|
|
79407
|
+
const project = deps.project();
|
|
79408
|
+
try {
|
|
79409
|
+
const res = await deps.fetch(QUOTA_URL, {
|
|
79410
|
+
method: "POST",
|
|
79411
|
+
headers: {
|
|
79412
|
+
authorization: `Bearer ${tok.accessToken}`,
|
|
79413
|
+
"content-type": "application/json",
|
|
79414
|
+
"user-agent": USER_AGENT
|
|
79415
|
+
},
|
|
79416
|
+
body: JSON.stringify(project ? { project } : {}),
|
|
79417
|
+
// Endpoint yang menggantung tak boleh menumpuk koneksi di bawah siar 30 dtk (SPEC-197).
|
|
79418
|
+
signal: AbortSignal.timeout(5e3)
|
|
79419
|
+
});
|
|
79420
|
+
if (!res.ok) return fallback2();
|
|
79421
|
+
const windows = mapQuota(await res.json());
|
|
79422
|
+
if (!windows.length) return fallback2();
|
|
79423
|
+
const dto = {
|
|
79424
|
+
status: "ok",
|
|
79425
|
+
windows,
|
|
79426
|
+
fetchedAt: new Date(deps.now()).toISOString()
|
|
79427
|
+
};
|
|
79428
|
+
lastOk2 = dto;
|
|
79429
|
+
return dto;
|
|
79430
|
+
} catch {
|
|
79431
|
+
return fallback2();
|
|
79432
|
+
}
|
|
79433
|
+
}
|
|
79434
|
+
async function getAntigravityLimits(deps = prodDeps) {
|
|
79435
|
+
if (lastOk2 && deps.now() < freshUntil3) return lastOk2;
|
|
79436
|
+
const dto = await fetchQuota(deps);
|
|
79437
|
+
if (dto.status === "ok") freshUntil3 = deps.now() + TTL_MS4;
|
|
79438
|
+
return dto;
|
|
79439
|
+
}
|
|
79440
|
+
|
|
79250
79441
|
// src/routes/limits.ts
|
|
79251
79442
|
async function limits(app3) {
|
|
79252
79443
|
app3.get("/limits", async () => getLimits());
|
|
79253
79444
|
app3.get("/limits/codex", async () => getCodexLimits());
|
|
79445
|
+
app3.get("/limits/antigravity", async () => getAntigravityLimits());
|
|
79254
79446
|
}
|
|
79255
79447
|
|
|
79256
79448
|
// src/routes/codex.ts
|
|
@@ -79266,7 +79458,7 @@ init_session_sandbox();
|
|
|
79266
79458
|
import { execFile as execFile18 } from "node:child_process";
|
|
79267
79459
|
import { promisify as promisify17 } from "node:util";
|
|
79268
79460
|
var run4 = promisify17(execFile18);
|
|
79269
|
-
var
|
|
79461
|
+
var TTL_MS6 = 5 * 6e4;
|
|
79270
79462
|
var unavailable = (error) => ({ version: null, error });
|
|
79271
79463
|
var CHANGED2 = "Konfigurasi runtime berubah; periksa versi kembali.";
|
|
79272
79464
|
function parseAntigravityVersion(output) {
|
|
@@ -79318,7 +79510,7 @@ function createAntigravityVersionService(deps = {}) {
|
|
|
79318
79510
|
cache6 = void 0;
|
|
79319
79511
|
pending2 = void 0;
|
|
79320
79512
|
}
|
|
79321
|
-
if (cache6 && now() - cache6.at <
|
|
79513
|
+
if (cache6 && now() - cache6.at < TTL_MS6) return Promise.resolve(cache6.info);
|
|
79322
79514
|
if (pending2) return pending2;
|
|
79323
79515
|
const startedGeneration = generation2;
|
|
79324
79516
|
const request = probe().catch(() => unavailable("Versi Antigravity tidak dapat diperiksa.")).then((info) => {
|
|
@@ -79344,7 +79536,7 @@ init_src2();
|
|
|
79344
79536
|
init_src();
|
|
79345
79537
|
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
79346
79538
|
import { readFile as readFile8, writeFile as writeFile6, rename as rename5, mkdir as mkdir9, rm as rm9 } from "node:fs/promises";
|
|
79347
|
-
import { dirname as dirname18, join as
|
|
79539
|
+
import { dirname as dirname18, join as join46 } from "node:path";
|
|
79348
79540
|
|
|
79349
79541
|
// src/services/model-catalog-parser.ts
|
|
79350
79542
|
init_zod();
|
|
@@ -79451,7 +79643,7 @@ init_session_sandbox();
|
|
|
79451
79643
|
import { spawn as spawn5 } from "node:child_process";
|
|
79452
79644
|
import { mkdtemp as mkdtemp2, rm as rm8 } from "node:fs/promises";
|
|
79453
79645
|
import { tmpdir as tmpdir9 } from "node:os";
|
|
79454
|
-
import { join as
|
|
79646
|
+
import { join as join45 } from "node:path";
|
|
79455
79647
|
var MAX_BYTES = 4 * 1024 * 1024;
|
|
79456
79648
|
var TIMEOUT_MS = 2e4;
|
|
79457
79649
|
var quote3 = (s2) => "'" + s2.replaceAll("'", "'\\''") + "'";
|
|
@@ -79550,7 +79742,7 @@ function runCatalogCommand(command, agent, cwd, env, timeoutMs = TIMEOUT_MS) {
|
|
|
79550
79742
|
}
|
|
79551
79743
|
async function probeModelCatalog(agent) {
|
|
79552
79744
|
assertAntigravityLaunch(agent);
|
|
79553
|
-
const cwd = await mkdtemp2(
|
|
79745
|
+
const cwd = await mkdtemp2(join45(tmpdir9(), "hanoman-models-"));
|
|
79554
79746
|
const env = agentEnvironment(agent, process.env);
|
|
79555
79747
|
for (const key of ["HANOMAN_CLAUDE_BIN", "HANOMAN_CODEX_BIN", "HANOMAN_PODMAN_BIN", "HANOMAN_ANTIGRAVITY_BIN"]) {
|
|
79556
79748
|
const value = effectiveStr(key);
|
|
@@ -79661,7 +79853,7 @@ async function writeModelCatalogCache(file, catalog, current = () => true) {
|
|
|
79661
79853
|
await rm9(temp, { force: true });
|
|
79662
79854
|
}
|
|
79663
79855
|
}
|
|
79664
|
-
var cacheFile = () =>
|
|
79856
|
+
var cacheFile = () => join46(resolveHome(), "model-catalog.json");
|
|
79665
79857
|
var modelCatalogService = createModelCatalogService({
|
|
79666
79858
|
probe: probeModelCatalog,
|
|
79667
79859
|
key: catalogProbeKey,
|
|
@@ -81158,6 +81350,9 @@ var GROUPS = [
|
|
|
81158
81350
|
// ekor + cache 30s, jadi biayanya jauh di bawah panggilan jaringan claude. Frame lahir hanya saat
|
|
81159
81351
|
// isinya berubah (dedup signature), dan snapshot codex baru bergerak saat ada sesi codex jalan.
|
|
81160
81352
|
{ everyTicks: 30, last: "", build: async () => ({ t: "codexLimits", limits: await getCodexLimits() }) },
|
|
81353
|
+
// ADR-0224 · limit Antigravity — grup TERPISAH. Panggilan jaringan ber-cache 60 dtk di service, dan
|
|
81354
|
+
// nol panggilan saat token agy kedaluwarsa; dedup signature membuat frame lahir saat isinya berubah.
|
|
81355
|
+
{ everyTicks: 30, last: "", build: async () => ({ t: "antigravityLimits", limits: await getAntigravityLimits() }) },
|
|
81161
81356
|
// SPEC-214 · deteksi update jarang berubah; recompute tiap 300 dtk, dedup signature → siar hanya
|
|
81162
81357
|
// saat status berubah. getUpdateStatus cache 15s + fetch ter-gate (server.ts) → attach tak menahan.
|
|
81163
81358
|
{ everyTicks: 300, last: "", build: async () => ({ t: "update", update: await getUpdateStatus() }) },
|
|
@@ -81585,7 +81780,7 @@ init_src();
|
|
|
81585
81780
|
init_db();
|
|
81586
81781
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
81587
81782
|
import { mkdir as mkdir10, rename as rename6, writeFile as writeFile7 } from "node:fs/promises";
|
|
81588
|
-
import { join as
|
|
81783
|
+
import { join as join47 } from "node:path";
|
|
81589
81784
|
|
|
81590
81785
|
// src/services/device-auth.ts
|
|
81591
81786
|
function bearerToken2(req) {
|
|
@@ -81841,9 +82036,9 @@ async function sync_default(app3) {
|
|
|
81841
82036
|
const deviceId = req.device.id;
|
|
81842
82037
|
const withTranscriptKey = await Promise.all(parsed.data.entries.map(async (e) => {
|
|
81843
82038
|
if (!e.transcript) return e;
|
|
81844
|
-
const dir2 =
|
|
82039
|
+
const dir2 = join47(resolveDataDirs().home, "remote-transcripts", deviceId);
|
|
81845
82040
|
await mkdir10(dir2, { recursive: true, mode: 448 });
|
|
81846
|
-
const final =
|
|
82041
|
+
const final = join47(dir2, `${e.seq}.txt`);
|
|
81847
82042
|
const tmp = `${final}.tmp`;
|
|
81848
82043
|
await writeFile7(tmp, e.transcript, { encoding: "utf8", mode: 384 });
|
|
81849
82044
|
await rename6(tmp, final);
|
|
@@ -82238,7 +82433,10 @@ async function session_events_default(app3) {
|
|
|
82238
82433
|
event
|
|
82239
82434
|
});
|
|
82240
82435
|
if (r.status === "duplicate") return reply.code(202).send({ duplicate: true });
|
|
82241
|
-
if (r.status === "rate-limited")
|
|
82436
|
+
if (r.status === "rate-limited") {
|
|
82437
|
+
console.log(`lead ask ${sessionId2}: tanya ${event.askId} ditolak batas laju \u2014 menunggu manusia`);
|
|
82438
|
+
return reply.code(429).send({ error: "terlalu banyak event" });
|
|
82439
|
+
}
|
|
82242
82440
|
if (r.status === "rejected") return reply.code(202).send({ rejected: true, reason: r.reason });
|
|
82243
82441
|
return reply.code(202).send({ accepted: true });
|
|
82244
82442
|
});
|
|
@@ -85126,8 +85324,8 @@ init_codex_trust();
|
|
|
85126
85324
|
init_qa_attachment_transfer();
|
|
85127
85325
|
init_uploads();
|
|
85128
85326
|
import { createHash as createHash21 } from "node:crypto";
|
|
85129
|
-
import { execFileSync as
|
|
85130
|
-
import { join as
|
|
85327
|
+
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
85328
|
+
import { join as join49 } from "node:path";
|
|
85131
85329
|
var QaSessionError = class extends Error {
|
|
85132
85330
|
constructor(code, message, needsBind = false) {
|
|
85133
85331
|
super(message);
|
|
@@ -85154,14 +85352,14 @@ async function startQaFindingSession(d, fid) {
|
|
|
85154
85352
|
const saved = await saveSessionUpload(id2, bytes, a.mimeType);
|
|
85155
85353
|
files2.push(`${JSON.stringify(a.filename)} (${a.mimeType}): ${saved.path}`);
|
|
85156
85354
|
}
|
|
85157
|
-
const wt =
|
|
85355
|
+
const wt = join49(repoDir, ".worktrees", id2);
|
|
85158
85356
|
const branch = `qa/${id2.slice(3)}`;
|
|
85159
85357
|
const reused = realGit.worktreeAlive(wt);
|
|
85160
85358
|
try {
|
|
85161
85359
|
if (!reused) {
|
|
85162
85360
|
const base3 = realGit.revParse(repoDir, branch) ? branch : "HEAD";
|
|
85163
85361
|
realGit.addWorktree(repoDir, wt, base3);
|
|
85164
|
-
|
|
85362
|
+
execFileSync10("git", ["checkout", "-B", branch], { cwd: wt, encoding: "utf8" });
|
|
85165
85363
|
}
|
|
85166
85364
|
} catch (e) {
|
|
85167
85365
|
throw new QaSessionError(422, `Gagal membuat worktree QA: ${e.message}`);
|
|
@@ -86695,7 +86893,7 @@ init_src();
|
|
|
86695
86893
|
init_db();
|
|
86696
86894
|
import { mkdirSync as mkdirSync20, mkdtempSync as mkdtempSync5, rmSync as rmSync14, writeFileSync as writeFileSync19 } from "node:fs";
|
|
86697
86895
|
import { tmpdir as tmpdir10 } from "node:os";
|
|
86698
|
-
import { join as
|
|
86896
|
+
import { join as join50 } from "node:path";
|
|
86699
86897
|
var STAGE_LABEL = {
|
|
86700
86898
|
brainstorming: "Dirumuskan",
|
|
86701
86899
|
objective: "Dirumuskan",
|
|
@@ -86738,7 +86936,7 @@ function renderTicketDoc(tickets2) {
|
|
|
86738
86936
|
${baris.join("\n")}`;
|
|
86739
86937
|
}
|
|
86740
86938
|
async function buildChatWorkspace(projectId) {
|
|
86741
|
-
const dir2 = mkdtempSync5(
|
|
86939
|
+
const dir2 = mkdtempSync5(join50(tmpdir10(), "hanoman-portal-chat-"));
|
|
86742
86940
|
try {
|
|
86743
86941
|
const project = await prisma.project.findUnique({
|
|
86744
86942
|
where: { id: projectId },
|
|
@@ -86753,7 +86951,7 @@ async function buildChatWorkspace(projectId) {
|
|
|
86753
86951
|
});
|
|
86754
86952
|
const files2 = [];
|
|
86755
86953
|
const tulis = (rel, isi) => {
|
|
86756
|
-
writeFileSync19(
|
|
86954
|
+
writeFileSync19(join50(dir2, rel), isi, { mode: 384 });
|
|
86757
86955
|
files2.push(rel);
|
|
86758
86956
|
};
|
|
86759
86957
|
tulis("project.md", renderProjectDoc(project));
|
|
@@ -86761,11 +86959,11 @@ async function buildChatWorkspace(projectId) {
|
|
|
86761
86959
|
tulis("laporan.md", renderTicketDoc(tickets2.map((t) => toPortalTicket(t, null))));
|
|
86762
86960
|
const prds = await listPrds(projectId);
|
|
86763
86961
|
if (prds.length) {
|
|
86764
|
-
mkdirSync20(
|
|
86962
|
+
mkdirSync20(join50(dir2, "dokumen"), { mode: 448 });
|
|
86765
86963
|
for (const prd of prds) {
|
|
86766
86964
|
const isi = await readPrd(projectId, prd.path);
|
|
86767
86965
|
if (!isi) continue;
|
|
86768
|
-
tulis(
|
|
86966
|
+
tulis(join50("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
|
|
86769
86967
|
}
|
|
86770
86968
|
}
|
|
86771
86969
|
return { dir: dir2, files: files2, cleanup: () => rmSync14(dir2, { recursive: true, force: true }) };
|
|
@@ -87292,13 +87490,13 @@ init_db();
|
|
|
87292
87490
|
// src/services/bootstrap.ts
|
|
87293
87491
|
import { createHash as createHash22, randomBytes as randomBytes11, timingSafeEqual as timingSafeEqual5 } from "node:crypto";
|
|
87294
87492
|
import { chmod as chmod2, lstat as lstat3, mkdir as mkdir11, readFile as readFile9, unlink as unlink5, writeFile as writeFile8 } from "node:fs/promises";
|
|
87295
|
-
import { join as
|
|
87296
|
-
var
|
|
87493
|
+
import { join as join51 } from "node:path";
|
|
87494
|
+
var TTL_MS7 = 15 * 6e4;
|
|
87297
87495
|
var SETUP_TOKEN_FILE = "setup.token";
|
|
87298
87496
|
var BootstrapError = class extends Error {
|
|
87299
87497
|
code = "BOOTSTRAP_PROOF";
|
|
87300
87498
|
};
|
|
87301
|
-
var tokenPath = (home3) =>
|
|
87499
|
+
var tokenPath = (home3) => join51(home3, SETUP_TOKEN_FILE);
|
|
87302
87500
|
async function readStored(home3) {
|
|
87303
87501
|
try {
|
|
87304
87502
|
const info = await lstat3(tokenPath(home3));
|
|
@@ -87330,7 +87528,7 @@ async function ensureSetupToken(home3, now = Date.now()) {
|
|
|
87330
87528
|
});
|
|
87331
87529
|
}
|
|
87332
87530
|
const token = randomBytes11(32).toString("base64url");
|
|
87333
|
-
const expiresAt = now +
|
|
87531
|
+
const expiresAt = now + TTL_MS7;
|
|
87334
87532
|
try {
|
|
87335
87533
|
await writeFile8(tokenPath(home3), `${token}
|
|
87336
87534
|
${new Date(expiresAt).toISOString()}
|
|
@@ -88136,7 +88334,7 @@ init_event_log();
|
|
|
88136
88334
|
init_transcript_store();
|
|
88137
88335
|
init_src2();
|
|
88138
88336
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
88139
|
-
import { join as
|
|
88337
|
+
import { join as join52 } from "node:path";
|
|
88140
88338
|
function decodeCursor(c) {
|
|
88141
88339
|
if (!c) return null;
|
|
88142
88340
|
try {
|
|
@@ -88174,7 +88372,7 @@ async function readRemoteTranscript(id2) {
|
|
|
88174
88372
|
const row = await prisma.logEntry.findUnique({ where: { id: id2 } });
|
|
88175
88373
|
if (!row || row.lane !== "transcript" || !row.transcriptKey) return null;
|
|
88176
88374
|
if (row.deviceId === "local") return readTranscript(row.transcriptKey);
|
|
88177
|
-
const path =
|
|
88375
|
+
const path = join52(resolveDataDirs().home, "remote-transcripts", row.transcriptKey);
|
|
88178
88376
|
try {
|
|
88179
88377
|
return await readFile10(path, "utf8");
|
|
88180
88378
|
} catch {
|
|
@@ -88841,7 +89039,7 @@ async function tick4(now, deps, end2 = prodEnd) {
|
|
|
88841
89039
|
console.error("scheduler drain:", e);
|
|
88842
89040
|
}
|
|
88843
89041
|
}
|
|
88844
|
-
var
|
|
89042
|
+
var prodDeps2 = {
|
|
88845
89043
|
liveCount: async () => (await listSessionsAsync()).filter((s2) => !s2.exited).length,
|
|
88846
89044
|
isLive: async (specId) => {
|
|
88847
89045
|
const s2 = await getSessionAsync(sessionIdForSpec(specId));
|
|
@@ -88875,7 +89073,7 @@ var prodDeps = {
|
|
|
88875
89073
|
};
|
|
88876
89074
|
var TICK_MS3 = 1e4;
|
|
88877
89075
|
var timer7;
|
|
88878
|
-
function startScheduler(deps =
|
|
89076
|
+
function startScheduler(deps = prodDeps2) {
|
|
88879
89077
|
if (timer7) return;
|
|
88880
89078
|
timer7 = setInterval(() => void tick4(Date.now(), deps), TICK_MS3);
|
|
88881
89079
|
timer7.unref();
|
|
@@ -88910,7 +89108,7 @@ init_src();
|
|
|
88910
89108
|
init_db();
|
|
88911
89109
|
init_session_admission2();
|
|
88912
89110
|
init_notifications2();
|
|
88913
|
-
var
|
|
89111
|
+
var prodDeps3 = {
|
|
88914
89112
|
// ADR-0170 · mengamandemen ADR-0169 keputusan #4: `bypassCapacity` DICABUT. Auto-resume kini
|
|
88915
89113
|
// tunduk cap/beban host seperti "Lanjutkan" manual — mesin 8 GB operator sudah dua kali kernel
|
|
88916
89114
|
// panic akibat batch sesi paralel tepat SAAT boot (memori mac-mini-8gb-panic-agen-paralel),
|
|
@@ -88919,7 +89117,7 @@ var prodDeps2 = {
|
|
|
88919
89117
|
recordFail: recordFailure,
|
|
88920
89118
|
recordDeferred: recordFailure
|
|
88921
89119
|
};
|
|
88922
|
-
async function resumeReconciledSessions(cutoff, deps =
|
|
89120
|
+
async function resumeReconciledSessions(cutoff, deps = prodDeps3) {
|
|
88923
89121
|
const report = { resumed: [], failed: [], deferred: [] };
|
|
88924
89122
|
const specIds = await reconciledSpecIdsSince(cutoff);
|
|
88925
89123
|
if (!specIds.length) return report;
|
|
@@ -89441,9 +89639,9 @@ async function pruneLogs(now, retention, opts2 = {}) {
|
|
|
89441
89639
|
}
|
|
89442
89640
|
async function reconcileRemoteTranscripts() {
|
|
89443
89641
|
const { readdir: readdir10, unlink: unlink6, stat: stat8 } = await import("node:fs/promises");
|
|
89444
|
-
const { join:
|
|
89642
|
+
const { join: join53 } = await import("node:path");
|
|
89445
89643
|
const { resolveDataDirs: resolveDataDirs2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
89446
|
-
const root =
|
|
89644
|
+
const root = join53(resolveDataDirs2().home, "remote-transcripts");
|
|
89447
89645
|
let deviceDirs;
|
|
89448
89646
|
try {
|
|
89449
89647
|
deviceDirs = await readdir10(root);
|
|
@@ -89457,16 +89655,16 @@ async function reconcileRemoteTranscripts() {
|
|
|
89457
89655
|
for (const dev of deviceDirs) {
|
|
89458
89656
|
let files2;
|
|
89459
89657
|
try {
|
|
89460
|
-
files2 = await readdir10(
|
|
89658
|
+
files2 = await readdir10(join53(root, dev));
|
|
89461
89659
|
} catch {
|
|
89462
89660
|
continue;
|
|
89463
89661
|
}
|
|
89464
89662
|
for (const f of files2) {
|
|
89465
89663
|
const key = `${dev}/${f}`;
|
|
89466
89664
|
if (referenced.has(key)) continue;
|
|
89467
|
-
const st = await stat8(
|
|
89665
|
+
const st = await stat8(join53(root, dev, f)).catch(() => null);
|
|
89468
89666
|
if (!st || Date.now() - st.mtimeMs < gracePeriodMs) continue;
|
|
89469
|
-
await unlink6(
|
|
89667
|
+
await unlink6(join53(root, dev, f)).catch(() => {
|
|
89470
89668
|
});
|
|
89471
89669
|
orphans++;
|
|
89472
89670
|
}
|