hanoman 0.9.27 → 0.9.28
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 +86 -0
- package/dist/server.js +667 -202
- package/docs/agent-integration.md +1 -0
- package/package.json +1 -1
- package/web/assets/{DalangHanomanScreen-wJskhX1_.js → DalangHanomanScreen-C3UMPHFg.js} +1 -1
- package/web/assets/{DocPreviewModal-ClHbRVmQ.js → DocPreviewModal-Bp5stb4M.js} +1 -1
- package/web/assets/{DocsWorkspace-Db_g219X.js → DocsWorkspace-C9yFxK4D.js} +1 -1
- package/web/assets/{IdeScreen-CfX1HEtj.js → IdeScreen-CRFRAP6E.js} +1 -1
- package/web/assets/{LeadScreen-BIpisTdG.js → LeadScreen-DfVUawoK.js} +1 -1
- package/web/assets/{ReviewScreen-DMGTLCki.js → ReviewScreen-CMHOlmgX.js} +1 -1
- package/web/assets/{SchedulerScreen-CSaFiXc9.js → SchedulerScreen-CK5QvX5c.js} +1 -1
- package/web/assets/{SettingsScreen-C4chALvS.js → SettingsScreen-D7P5JgjU.js} +1 -1
- package/web/assets/{TeamScreen-CS9osC8I.js → TeamScreen-D4tA34Mj.js} +1 -1
- package/web/assets/TerminalScreen-BjFMJnq6.css +1 -0
- package/web/assets/TerminalScreen-CKySL63X.js +2 -0
- package/web/assets/{TriageScreen-Eh8BoPTm.js → TriageScreen-B8ZCexaX.js} +1 -1
- package/web/assets/{VpsScreen-CweCi934.js → VpsScreen-BCrLfZh7.js} +1 -1
- package/web/assets/{index-Av-RfHXX.js → index-BRR3JL_o.js} +210 -210
- package/web/assets/{index-B8xChq6y.css → index-BqAP-VDq.css} +1 -1
- package/web/assets/{live-Bfx0Oehi.js → live-DuBdI0MZ.js} +1 -1
- package/web/assets/{sched-lead-shared-DJn2D3VE.js → sched-lead-shared-QqbtjoVv.js} +1 -1
- package/web/assets/{settings-rebase-Bbz53ceL.js → settings-rebase-Cbh-JGJ-.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/TerminalScreen-C0fHHsUa.js +0 -2
- package/web/assets/TerminalScreen-D0NdFmR-.css +0 -1
package/dist/server.js
CHANGED
|
@@ -8381,6 +8381,8 @@ var init_api = __esm({
|
|
|
8381
8381
|
// SPEC-199 · WebSocket siar dashboard (global, bukan per-sesi)
|
|
8382
8382
|
// SPEC-919 · ADR-0147 · muat awal + fallback halaman Klien. Selama WS sehat tak ada yang men-poll-nya.
|
|
8383
8383
|
presence: `${API}/presence`,
|
|
8384
|
+
// ADR-0220 · muat awal agen/MCP untuk agregat Overview; dashboard menerimanya lewat grup siar `overview`.
|
|
8385
|
+
overview: `${API}/overview`,
|
|
8384
8386
|
wsTickets: `${API}/ws-tickets`,
|
|
8385
8387
|
vps: `${API}/vps`,
|
|
8386
8388
|
vpsOne: (id2) => `${API}/vps/${id2}`,
|
|
@@ -8721,12 +8723,88 @@ var init_session_kind = __esm({
|
|
|
8721
8723
|
});
|
|
8722
8724
|
|
|
8723
8725
|
// ../shared/src/session-end.ts
|
|
8726
|
+
function sessionOutcome(r) {
|
|
8727
|
+
if (!r.endedAt) return "running";
|
|
8728
|
+
if (r.endedReason === "reconciled") return "interrupted";
|
|
8729
|
+
return r.exitCode === null || r.exitCode === 0 ? "completed" : "failed";
|
|
8730
|
+
}
|
|
8724
8731
|
var init_session_end = __esm({
|
|
8725
8732
|
"../shared/src/session-end.ts"() {
|
|
8726
8733
|
"use strict";
|
|
8727
8734
|
}
|
|
8728
8735
|
});
|
|
8729
8736
|
|
|
8737
|
+
// ../shared/src/overview.ts
|
|
8738
|
+
var zHeadlinePart, zOverviewDay, zOverviewRiver, zOverviewTokens, zOverviewWait, BENSIN_VERDICTS, zOverviewBensin, zOverviewHeatmap, zOverviewRecords, zOverview;
|
|
8739
|
+
var init_overview = __esm({
|
|
8740
|
+
"../shared/src/overview.ts"() {
|
|
8741
|
+
"use strict";
|
|
8742
|
+
init_zod();
|
|
8743
|
+
zHeadlinePart = external_exports.object({ text: external_exports.string(), tone: external_exports.enum(["bad", "good"]).optional() });
|
|
8744
|
+
zOverviewDay = external_exports.object({ landed: external_exports.number().int(), failed: external_exports.number().int() });
|
|
8745
|
+
zOverviewRiver = external_exports.object({
|
|
8746
|
+
created: external_exports.number().int(),
|
|
8747
|
+
started: external_exports.number().int(),
|
|
8748
|
+
landed: external_exports.number().int(),
|
|
8749
|
+
// Median 7 hari terakhir atas spec yang selesai; null bila tak ada.
|
|
8750
|
+
leadTimeMedMs: external_exports.number().nullable(),
|
|
8751
|
+
cycleTimeMedMs: external_exports.number().nullable()
|
|
8752
|
+
});
|
|
8753
|
+
zOverviewTokens = external_exports.object({
|
|
8754
|
+
input: external_exports.number(),
|
|
8755
|
+
output: external_exports.number(),
|
|
8756
|
+
cached: external_exports.number(),
|
|
8757
|
+
total: external_exports.number(),
|
|
8758
|
+
cacheHitPct: external_exports.number().nullable(),
|
|
8759
|
+
perLanded: external_exports.number().nullable(),
|
|
8760
|
+
topAgent: external_exports.object({ name: external_exports.string(), total: external_exports.number() }).nullable(),
|
|
8761
|
+
yesterdayTotal: external_exports.number()
|
|
8762
|
+
});
|
|
8763
|
+
zOverviewWait = external_exports.object({
|
|
8764
|
+
todayMin: external_exports.number(),
|
|
8765
|
+
yesterdayMin: external_exports.number(),
|
|
8766
|
+
pctOfLive: external_exports.number().nullable()
|
|
8767
|
+
});
|
|
8768
|
+
BENSIN_VERDICTS = ["habis", "aman", "stabil", "kurang-data"];
|
|
8769
|
+
zOverviewBensin = external_exports.object({
|
|
8770
|
+
source: external_exports.enum(["claude", "codex"]),
|
|
8771
|
+
key: external_exports.string(),
|
|
8772
|
+
label: external_exports.string(),
|
|
8773
|
+
usedPct: external_exports.number(),
|
|
8774
|
+
resetsAt: external_exports.string().nullable(),
|
|
8775
|
+
exhaustAt: external_exports.string().nullable(),
|
|
8776
|
+
verdict: external_exports.enum(BENSIN_VERDICTS),
|
|
8777
|
+
samples: external_exports.array(external_exports.object({ at: external_exports.string(), pct: external_exports.number() }))
|
|
8778
|
+
});
|
|
8779
|
+
zOverviewHeatmap = external_exports.object({
|
|
8780
|
+
days: external_exports.array(external_exports.string()),
|
|
8781
|
+
// YYYY-MM-DD lokal, terlama dulu
|
|
8782
|
+
minutes: external_exports.array(external_exports.array(external_exports.number()))
|
|
8783
|
+
// [hari][jam 0..23] = menit sesi agen hidup
|
|
8784
|
+
});
|
|
8785
|
+
zOverviewRecords = external_exports.object({
|
|
8786
|
+
streakDays: external_exports.number().int(),
|
|
8787
|
+
bestStreak: external_exports.number().int(),
|
|
8788
|
+
bestLandedDay: external_exports.object({ date: external_exports.string(), count: external_exports.number().int() }).nullable(),
|
|
8789
|
+
landedRecordToday: external_exports.boolean()
|
|
8790
|
+
});
|
|
8791
|
+
zOverview = external_exports.object({
|
|
8792
|
+
generatedAt: external_exports.string(),
|
|
8793
|
+
recordingSince: external_exports.string(),
|
|
8794
|
+
// awal perekam di memori (menunggu & bensin); restart mengulangnya
|
|
8795
|
+
today: zOverviewDay.nullable(),
|
|
8796
|
+
yesterday: zOverviewDay.nullable(),
|
|
8797
|
+
river: zOverviewRiver.nullable(),
|
|
8798
|
+
tokens: zOverviewTokens.nullable(),
|
|
8799
|
+
wait: zOverviewWait.nullable(),
|
|
8800
|
+
bensin: external_exports.array(zOverviewBensin).nullable(),
|
|
8801
|
+
heatmap: zOverviewHeatmap.nullable(),
|
|
8802
|
+
records: zOverviewRecords.nullable(),
|
|
8803
|
+
headline: external_exports.object({ text: external_exports.string(), parts: external_exports.array(zHeadlinePart) })
|
|
8804
|
+
});
|
|
8805
|
+
}
|
|
8806
|
+
});
|
|
8807
|
+
|
|
8730
8808
|
// ../shared/src/config.ts
|
|
8731
8809
|
var zHanomanConfig;
|
|
8732
8810
|
var init_config = __esm({
|
|
@@ -14003,6 +14081,18 @@ var init_system = __esm({
|
|
|
14003
14081
|
build: () => ({ method: "GET", path: "/limits/codex" }),
|
|
14004
14082
|
shape: (raw) => raw
|
|
14005
14083
|
},
|
|
14084
|
+
{
|
|
14085
|
+
name: "hanoman_overview_get",
|
|
14086
|
+
title: "Ringkasan kinerja hari ini (Overview)",
|
|
14087
|
+
description: 'Agregat kinerja workspace hari ini vs kemarin, persis yang tampil di layar Overview "Koran Pagi": spec mendarat, sesi agen tumbang, menit agen menunggu jawaban manusia, token agen tercatat (hanya subagen/agen fase), prakiraan kapan limit habis dibanding reset, peta panas jam kerja agen 7\xD724, streak/rekor, dan headline-nya. Menit menunggu & prakiraan limit direkam di memori server dan hilang saat restart (`recordingSince`). Panel `null` = sumbernya gagal dibaca. Tak menuntut capability apa pun.',
|
|
14088
|
+
inputSchema: obj({ properties: {} }),
|
|
14089
|
+
mode: "read",
|
|
14090
|
+
capability: null,
|
|
14091
|
+
samplePath: "/overview",
|
|
14092
|
+
sampleMethod: "GET",
|
|
14093
|
+
build: () => ({ method: "GET", path: "/overview" }),
|
|
14094
|
+
shape: (raw) => raw
|
|
14095
|
+
},
|
|
14006
14096
|
{
|
|
14007
14097
|
name: "hanoman_update_status",
|
|
14008
14098
|
title: "Status versi & pembaruan",
|
|
@@ -16543,6 +16633,7 @@ var init_src = __esm({
|
|
|
16543
16633
|
init_session_kind();
|
|
16544
16634
|
init_session_admission();
|
|
16545
16635
|
init_session_end();
|
|
16636
|
+
init_overview();
|
|
16546
16637
|
init_config();
|
|
16547
16638
|
init_config_registry();
|
|
16548
16639
|
init_semver();
|
|
@@ -20996,7 +21087,7 @@ var require_timers = __commonJS({
|
|
|
20996
21087
|
"use strict";
|
|
20997
21088
|
var fastNow = 0;
|
|
20998
21089
|
var RESOLUTION_MS = 1e3;
|
|
20999
|
-
var
|
|
21090
|
+
var TICK_MS6 = (RESOLUTION_MS >> 1) - 1;
|
|
21000
21091
|
var fastNowTimeout;
|
|
21001
21092
|
var kFastTimer = Symbol("kFastTimer");
|
|
21002
21093
|
var fastTimers = [];
|
|
@@ -21005,21 +21096,21 @@ var require_timers = __commonJS({
|
|
|
21005
21096
|
var PENDING = 0;
|
|
21006
21097
|
var ACTIVE = 1;
|
|
21007
21098
|
function onTick() {
|
|
21008
|
-
fastNow +=
|
|
21099
|
+
fastNow += TICK_MS6;
|
|
21009
21100
|
let idx = 0;
|
|
21010
21101
|
let len = fastTimers.length;
|
|
21011
21102
|
while (idx < len) {
|
|
21012
|
-
const
|
|
21013
|
-
if (
|
|
21014
|
-
|
|
21015
|
-
|
|
21016
|
-
} else if (
|
|
21017
|
-
|
|
21018
|
-
|
|
21019
|
-
|
|
21020
|
-
}
|
|
21021
|
-
if (
|
|
21022
|
-
|
|
21103
|
+
const timer11 = fastTimers[idx];
|
|
21104
|
+
if (timer11._state === PENDING) {
|
|
21105
|
+
timer11._idleStart = fastNow - TICK_MS6;
|
|
21106
|
+
timer11._state = ACTIVE;
|
|
21107
|
+
} else if (timer11._state === ACTIVE && fastNow >= timer11._idleStart + timer11._idleTimeout) {
|
|
21108
|
+
timer11._state = TO_BE_CLEARED;
|
|
21109
|
+
timer11._idleStart = -1;
|
|
21110
|
+
timer11._onTimeout(timer11._timerArg);
|
|
21111
|
+
}
|
|
21112
|
+
if (timer11._state === TO_BE_CLEARED) {
|
|
21113
|
+
timer11._state = NOT_IN_LIST;
|
|
21023
21114
|
if (--len !== 0) {
|
|
21024
21115
|
fastTimers[idx] = fastTimers[len];
|
|
21025
21116
|
}
|
|
@@ -21037,7 +21128,7 @@ var require_timers = __commonJS({
|
|
|
21037
21128
|
fastNowTimeout.refresh();
|
|
21038
21129
|
} else {
|
|
21039
21130
|
clearTimeout(fastNowTimeout);
|
|
21040
|
-
fastNowTimeout = setTimeout(onTick,
|
|
21131
|
+
fastNowTimeout = setTimeout(onTick, TICK_MS6);
|
|
21041
21132
|
fastNowTimeout?.unref();
|
|
21042
21133
|
}
|
|
21043
21134
|
}
|
|
@@ -33764,22 +33855,22 @@ var require_mock_utils = __commonJS({
|
|
|
33764
33855
|
return true;
|
|
33765
33856
|
}
|
|
33766
33857
|
let aborted = false;
|
|
33767
|
-
let
|
|
33858
|
+
let timer11 = null;
|
|
33768
33859
|
function abort(err) {
|
|
33769
33860
|
if (aborted) {
|
|
33770
33861
|
return;
|
|
33771
33862
|
}
|
|
33772
33863
|
aborted = true;
|
|
33773
|
-
if (
|
|
33774
|
-
clearTimeout(
|
|
33775
|
-
|
|
33864
|
+
if (timer11 !== null) {
|
|
33865
|
+
clearTimeout(timer11);
|
|
33866
|
+
timer11 = null;
|
|
33776
33867
|
}
|
|
33777
33868
|
handler.onError(err);
|
|
33778
33869
|
}
|
|
33779
33870
|
handler.onConnect?.(abort, null);
|
|
33780
33871
|
if (typeof delay2 === "number" && delay2 > 0) {
|
|
33781
|
-
|
|
33782
|
-
|
|
33872
|
+
timer11 = setTimeout(() => {
|
|
33873
|
+
timer11 = null;
|
|
33783
33874
|
handleReply(this[kDispatches]);
|
|
33784
33875
|
}, delay2);
|
|
33785
33876
|
} else {
|
|
@@ -46913,9 +47004,9 @@ async function pinnedRequest(input) {
|
|
|
46913
47004
|
});
|
|
46914
47005
|
response.on("error", reject2);
|
|
46915
47006
|
});
|
|
46916
|
-
const
|
|
46917
|
-
|
|
46918
|
-
req.once("close", () => clearTimeout(
|
|
47007
|
+
const timer11 = setTimeout(() => req.destroy(Object.assign(new Error("outbound timeout"), { name: "AbortError" })), input.totalMs);
|
|
47008
|
+
timer11.unref?.();
|
|
47009
|
+
req.once("close", () => clearTimeout(timer11));
|
|
46919
47010
|
req.once("error", reject2);
|
|
46920
47011
|
if (input.body) req.write(input.body);
|
|
46921
47012
|
req.end();
|
|
@@ -46923,13 +47014,13 @@ async function pinnedRequest(input) {
|
|
|
46923
47014
|
}
|
|
46924
47015
|
function withLookupTimeout(p3, ms) {
|
|
46925
47016
|
return new Promise((resolve24, reject2) => {
|
|
46926
|
-
const
|
|
46927
|
-
|
|
47017
|
+
const timer11 = setTimeout(() => reject2(Object.assign(new Error("outbound timeout"), { name: "AbortError" })), ms);
|
|
47018
|
+
timer11.unref?.();
|
|
46928
47019
|
p3.then((v) => {
|
|
46929
|
-
clearTimeout(
|
|
47020
|
+
clearTimeout(timer11);
|
|
46930
47021
|
resolve24(v);
|
|
46931
47022
|
}, (e) => {
|
|
46932
|
-
clearTimeout(
|
|
47023
|
+
clearTimeout(timer11);
|
|
46933
47024
|
reject2(e);
|
|
46934
47025
|
});
|
|
46935
47026
|
});
|
|
@@ -47041,19 +47132,19 @@ async function dropSessionUploads(sessionId2) {
|
|
|
47041
47132
|
});
|
|
47042
47133
|
}
|
|
47043
47134
|
async function readUpload(storageKey) {
|
|
47044
|
-
const
|
|
47045
|
-
return readFile2(join15(uploadDir(),
|
|
47135
|
+
const safe3 = storageKey.replace(/[/\\]/g, "");
|
|
47136
|
+
return readFile2(join15(uploadDir(), safe3));
|
|
47046
47137
|
}
|
|
47047
47138
|
async function readUploadOrFetch(storageKey) {
|
|
47048
|
-
const
|
|
47049
|
-
const target3 = join15(uploadDir(),
|
|
47139
|
+
const safe3 = storageKey.replace(/[/\\]/g, "");
|
|
47140
|
+
const target3 = join15(uploadDir(), safe3);
|
|
47050
47141
|
try {
|
|
47051
47142
|
return await readFile2(target3);
|
|
47052
47143
|
} catch {
|
|
47053
47144
|
const base3 = effectiveStr("SYNC_SERVER_URL");
|
|
47054
47145
|
const token = effectiveStr("SYNC_DEVICE_TOKEN");
|
|
47055
|
-
if (!base3 || !token) throw new Error(`lampiran ${
|
|
47056
|
-
const url2 = new URL(`/api/sync/attachments/${encodeURIComponent(
|
|
47146
|
+
if (!base3 || !token) throw new Error(`lampiran ${safe3} tak ada lokal & bukan client sync`);
|
|
47147
|
+
const url2 = new URL(`/api/sync/attachments/${encodeURIComponent(safe3)}`, `${base3.replace(/\/$/, "")}/`);
|
|
47057
47148
|
const loopback = ["localhost", "127.0.0.1", "::1"].includes(url2.hostname);
|
|
47058
47149
|
const res = await safeRequest({
|
|
47059
47150
|
url: url2,
|
|
@@ -47072,8 +47163,8 @@ async function readUploadOrFetch(storageKey) {
|
|
|
47072
47163
|
}
|
|
47073
47164
|
}
|
|
47074
47165
|
async function deleteUpload(storageKey) {
|
|
47075
|
-
const
|
|
47076
|
-
await unlink(join15(uploadDir(),
|
|
47166
|
+
const safe3 = storageKey.replace(/[/\\]/g, "");
|
|
47167
|
+
await unlink(join15(uploadDir(), safe3)).catch(() => {
|
|
47077
47168
|
});
|
|
47078
47169
|
}
|
|
47079
47170
|
var EXT, SESSION_ID;
|
|
@@ -54173,9 +54264,9 @@ function recordBytes(rec) {
|
|
|
54173
54264
|
return Buffer.byteLength(JSON.stringify(rec));
|
|
54174
54265
|
}
|
|
54175
54266
|
async function pull(sinceCursor, limit = 500, maxBytes = PULL_MAX_BYTES, opts2 = {}) {
|
|
54176
|
-
const
|
|
54267
|
+
const since2 = Number(sinceCursor || "0");
|
|
54177
54268
|
const rows = await prisma.syncLog.findMany({
|
|
54178
|
-
where: { seq: { gt:
|
|
54269
|
+
where: { seq: { gt: since2 } },
|
|
54179
54270
|
orderBy: { seq: "asc" },
|
|
54180
54271
|
take: limit
|
|
54181
54272
|
});
|
|
@@ -55366,12 +55457,12 @@ function requestRelay(deviceId, r, opts2 = {}) {
|
|
|
55366
55457
|
return Promise.reject(new RelayError("too-large"));
|
|
55367
55458
|
const id2 = `r${++counter}`;
|
|
55368
55459
|
return new Promise((resolve24, reject2) => {
|
|
55369
|
-
const
|
|
55460
|
+
const timer11 = setTimeout(() => {
|
|
55370
55461
|
sendCancel(link2, id2);
|
|
55371
55462
|
settle(link2, id2, new RelayError("timeout"));
|
|
55372
55463
|
}, opts2.timeoutMs ?? RELAY_REQ_TIMEOUT_MS);
|
|
55373
|
-
|
|
55374
|
-
link2.pending.set(id2, { resolve: resolve24, reject: reject2, timer:
|
|
55464
|
+
timer11.unref?.();
|
|
55465
|
+
link2.pending.set(id2, { resolve: resolve24, reject: reject2, timer: timer11, contentType: null, chunks: [], bytes: 0 });
|
|
55375
55466
|
try {
|
|
55376
55467
|
link2.socket.send(JSON.stringify({
|
|
55377
55468
|
t: "req",
|
|
@@ -56694,8 +56785,8 @@ var init_store = __esm({
|
|
|
56694
56785
|
async bindSession(chatId, sessionId2) {
|
|
56695
56786
|
await this.db.telegramChat.update({ where: { chatId }, data: { sessionId: sessionId2 } });
|
|
56696
56787
|
}
|
|
56697
|
-
async rateLimitExceeded(userId,
|
|
56698
|
-
return await this.db.telegramUpdate.count({ where: { userId, receivedAt: { gte:
|
|
56788
|
+
async rateLimitExceeded(userId, since2, limit) {
|
|
56789
|
+
return await this.db.telegramUpdate.count({ where: { userId, receivedAt: { gte: since2 } } }) > limit;
|
|
56699
56790
|
}
|
|
56700
56791
|
async markUpdateUncertain(updateId, reason) {
|
|
56701
56792
|
await this.db.telegramUpdate.updateMany({
|
|
@@ -56714,9 +56805,9 @@ var init_store = __esm({
|
|
|
56714
56805
|
* SELAMANYA, dan tanpa pagar ini gateway akan mengetik selamanya sekaligus mengunci long-poll
|
|
56715
56806
|
* di 4 detik selamanya.
|
|
56716
56807
|
*/
|
|
56717
|
-
async chatsAwaitingReply(
|
|
56808
|
+
async chatsAwaitingReply(since2) {
|
|
56718
56809
|
const pending2 = await this.db.telegramUpdate.findMany({
|
|
56719
|
-
where: { state: "dispatched", chatId: { not: null }, dispatchedAt: { gte:
|
|
56810
|
+
where: { state: "dispatched", chatId: { not: null }, dispatchedAt: { gte: since2 } },
|
|
56720
56811
|
select: { updateId: true, chatId: true },
|
|
56721
56812
|
orderBy: { updateId: "asc" }
|
|
56722
56813
|
});
|
|
@@ -57323,12 +57414,12 @@ function startPresenceSender(o) {
|
|
|
57323
57414
|
heartbeatMs: o.heartbeatMs
|
|
57324
57415
|
});
|
|
57325
57416
|
void sender.tick(Date.now());
|
|
57326
|
-
const
|
|
57417
|
+
const timer11 = setInterval(() => {
|
|
57327
57418
|
void sender.tick(Date.now());
|
|
57328
57419
|
}, o.tickMs ?? PRESENCE_TICK_MS);
|
|
57329
|
-
|
|
57420
|
+
timer11.unref?.();
|
|
57330
57421
|
return { stop() {
|
|
57331
|
-
clearInterval(
|
|
57422
|
+
clearInterval(timer11);
|
|
57332
57423
|
} };
|
|
57333
57424
|
}
|
|
57334
57425
|
var init_sender = __esm({
|
|
@@ -57379,7 +57470,7 @@ function capabilityForRoute(method, path) {
|
|
|
57379
57470
|
if (top === "devices") return "relay:exec";
|
|
57380
57471
|
if (top === "logs") return rw("logs");
|
|
57381
57472
|
if (top === "presence") return read2 ? "presence:read" : "COOKIE_ONLY";
|
|
57382
|
-
if (top === "limits" || top === "update" || top === "events" || top === "fs" || top === "health" || top === "models" || top === "methods" || top === "codex")
|
|
57473
|
+
if (top === "limits" || top === "update" || top === "events" || top === "fs" || top === "health" || top === "models" || top === "methods" || top === "codex" || top === "overview")
|
|
57383
57474
|
return read2 ? "GLOBAL_READ" : "COOKIE_ONLY";
|
|
57384
57475
|
if (top === "session-results") return rw("activity");
|
|
57385
57476
|
if (top === "portal-chat") return rw("portal-chat");
|
|
@@ -60996,7 +61087,7 @@ var require_stream = __commonJS({
|
|
|
60996
61087
|
};
|
|
60997
61088
|
duplex._final = function(callback) {
|
|
60998
61089
|
if (ws2.readyState === ws2.CONNECTING) {
|
|
60999
|
-
ws2.once("open", function
|
|
61090
|
+
ws2.once("open", function open6() {
|
|
61000
61091
|
duplex._final(callback);
|
|
61001
61092
|
});
|
|
61002
61093
|
return;
|
|
@@ -61017,7 +61108,7 @@ var require_stream = __commonJS({
|
|
|
61017
61108
|
};
|
|
61018
61109
|
duplex._write = function(chunk, encoding, callback) {
|
|
61019
61110
|
if (ws2.readyState === ws2.CONNECTING) {
|
|
61020
|
-
ws2.once("open", function
|
|
61111
|
+
ws2.once("open", function open6() {
|
|
61021
61112
|
duplex._write(chunk, encoding, callback);
|
|
61022
61113
|
});
|
|
61023
61114
|
return;
|
|
@@ -61749,14 +61840,14 @@ async function loadCursor(lane) {
|
|
|
61749
61840
|
const c = await prisma.logCursor.findUnique({ where: { deviceId_lane: { deviceId: LOCAL_DEVICE_ID, lane } } });
|
|
61750
61841
|
return c?.seq ?? 0n;
|
|
61751
61842
|
}
|
|
61752
|
-
async function nextBatch(lane,
|
|
61843
|
+
async function nextBatch(lane, since2) {
|
|
61753
61844
|
if (lane === "server") {
|
|
61754
61845
|
const segs = await readSpoolSegments("server");
|
|
61755
61846
|
const flat = segs.flatMap((s2) => s2.entries);
|
|
61756
61847
|
return flat.slice(0, LOG_BATCH_MAX_ENTRIES);
|
|
61757
61848
|
}
|
|
61758
61849
|
const rows = await prisma.logEntry.findMany({
|
|
61759
|
-
where: { deviceId: LOCAL_DEVICE_ID, lane, seq: { gt:
|
|
61850
|
+
where: { deviceId: LOCAL_DEVICE_ID, lane, seq: { gt: since2 } },
|
|
61760
61851
|
orderBy: { seq: "asc" },
|
|
61761
61852
|
take: LOG_BATCH_MAX_ENTRIES
|
|
61762
61853
|
});
|
|
@@ -61775,9 +61866,9 @@ async function nextBatch(lane, since) {
|
|
|
61775
61866
|
async function shipLane(lane, transport) {
|
|
61776
61867
|
const now = Date.now();
|
|
61777
61868
|
if ((pausedUntil.get(lane) ?? 0) > now) return;
|
|
61778
|
-
let
|
|
61869
|
+
let since2 = await loadCursor(lane);
|
|
61779
61870
|
for (let i = 0; i < LOG_SHIP_MAX_BATCHES_PER_TICK; i++) {
|
|
61780
|
-
const entries4 = await nextBatch(lane,
|
|
61871
|
+
const entries4 = await nextBatch(lane, since2);
|
|
61781
61872
|
if (entries4.length === 0) return;
|
|
61782
61873
|
const res = await transport("POST", "/api/sync/logs", { lane, entries: entries4 });
|
|
61783
61874
|
if (res.status === 200) {
|
|
@@ -61788,7 +61879,7 @@ async function shipLane(lane, transport) {
|
|
|
61788
61879
|
update: { seq: last2 },
|
|
61789
61880
|
create: { deviceId: LOCAL_DEVICE_ID, lane, seq: last2 }
|
|
61790
61881
|
});
|
|
61791
|
-
|
|
61882
|
+
since2 = last2;
|
|
61792
61883
|
if (lane === "server") {
|
|
61793
61884
|
const segs = await readSpoolSegments("server");
|
|
61794
61885
|
for (const s2 of segs) if (s2.entries.every((e) => BigInt(e.seq) <= last2)) await removeSpoolSegment(s2.file);
|
|
@@ -61797,7 +61888,7 @@ async function shipLane(lane, transport) {
|
|
|
61797
61888
|
}
|
|
61798
61889
|
if (res.status === 400) {
|
|
61799
61890
|
await appendGap("rejected");
|
|
61800
|
-
|
|
61891
|
+
since2 = BigInt(entries4[entries4.length - 1].seq);
|
|
61801
61892
|
continue;
|
|
61802
61893
|
}
|
|
61803
61894
|
if (res.status === 404) {
|
|
@@ -62246,7 +62337,7 @@ async function startSyncClient(base3, token, tickMs2) {
|
|
|
62246
62337
|
started = true;
|
|
62247
62338
|
startRelayClient(base3, token);
|
|
62248
62339
|
const transport = fetchTransport(base3, token);
|
|
62249
|
-
const
|
|
62340
|
+
const tick7 = () => syncTick(transport);
|
|
62250
62341
|
const connectWs = async () => {
|
|
62251
62342
|
const { WebSocket: WebSocket2 } = await Promise.resolve().then(() => (init_wrapper(), wrapper_exports));
|
|
62252
62343
|
const wsUrl = base3.replace(/^http/, "ws").replace(/\/$/, "") + "/api/sync/ws?" + OPTIONAL_QUERY;
|
|
@@ -62254,14 +62345,14 @@ async function startSyncClient(base3, token, tickMs2) {
|
|
|
62254
62345
|
ws = sock2;
|
|
62255
62346
|
sock2.on("open", () => {
|
|
62256
62347
|
reconnectDelay = 0;
|
|
62257
|
-
void
|
|
62348
|
+
void tick7();
|
|
62258
62349
|
presence = startPresenceSender({ send: (json) => sock2.send(json) });
|
|
62259
62350
|
});
|
|
62260
62351
|
sock2.on("message", async (raw) => {
|
|
62261
62352
|
try {
|
|
62262
62353
|
const msg = JSON.parse(raw.toString());
|
|
62263
62354
|
if (msg.t !== "sync") return;
|
|
62264
|
-
if (!await applyFeedFrame(msg)) void
|
|
62355
|
+
if (!await applyFeedFrame(msg)) void tick7();
|
|
62265
62356
|
} catch {
|
|
62266
62357
|
}
|
|
62267
62358
|
});
|
|
@@ -62283,11 +62374,11 @@ async function startSyncClient(base3, token, tickMs2) {
|
|
|
62283
62374
|
});
|
|
62284
62375
|
};
|
|
62285
62376
|
await bootstrapOnce(transport).catch(() => null);
|
|
62286
|
-
await
|
|
62377
|
+
await tick7();
|
|
62287
62378
|
void connectWs();
|
|
62288
62379
|
const ms = tickMs2 && tickMs2 > 0 ? tickMs2 : 15e3;
|
|
62289
62380
|
timer3 = setInterval(() => {
|
|
62290
|
-
void
|
|
62381
|
+
void tick7();
|
|
62291
62382
|
}, ms);
|
|
62292
62383
|
timer3.unref?.();
|
|
62293
62384
|
}
|
|
@@ -65497,14 +65588,14 @@ var require_secure_json_parse = __commonJS({
|
|
|
65497
65588
|
}
|
|
65498
65589
|
return filter(obj2, { protoAction, constructorAction, safe: options2 && options2.safe });
|
|
65499
65590
|
}
|
|
65500
|
-
function filter(obj2, { protoAction = "error", constructorAction = "error", safe:
|
|
65591
|
+
function filter(obj2, { protoAction = "error", constructorAction = "error", safe: safe3 } = {}) {
|
|
65501
65592
|
let next = [obj2];
|
|
65502
65593
|
while (next.length) {
|
|
65503
65594
|
const nodes = next;
|
|
65504
65595
|
next = [];
|
|
65505
65596
|
for (const node of nodes) {
|
|
65506
65597
|
if (protoAction !== "ignore" && Object.prototype.hasOwnProperty.call(node, "__proto__")) {
|
|
65507
|
-
if (
|
|
65598
|
+
if (safe3 === true) {
|
|
65508
65599
|
return null;
|
|
65509
65600
|
} else if (protoAction === "error") {
|
|
65510
65601
|
throw new SyntaxError("Object contains forbidden prototype property");
|
|
@@ -65512,7 +65603,7 @@ var require_secure_json_parse = __commonJS({
|
|
|
65512
65603
|
delete node.__proto__;
|
|
65513
65604
|
}
|
|
65514
65605
|
if (constructorAction !== "ignore" && Object.prototype.hasOwnProperty.call(node, "constructor") && node.constructor !== null && typeof node.constructor === "object" && Object.prototype.hasOwnProperty.call(node.constructor, "prototype")) {
|
|
65515
|
-
if (
|
|
65606
|
+
if (safe3 === true) {
|
|
65516
65607
|
return null;
|
|
65517
65608
|
} else if (constructorAction === "error") {
|
|
65518
65609
|
throw new SyntaxError("Object contains forbidden prototype property");
|
|
@@ -66147,12 +66238,12 @@ var UploadError = class extends Error {
|
|
|
66147
66238
|
};
|
|
66148
66239
|
function timeout(promise, ms, code) {
|
|
66149
66240
|
return new Promise((resolve24, reject2) => {
|
|
66150
|
-
const
|
|
66241
|
+
const timer11 = setTimeout(() => reject2(new UploadError(code, "upload operation timed out")), ms);
|
|
66151
66242
|
promise.then((value) => {
|
|
66152
|
-
clearTimeout(
|
|
66243
|
+
clearTimeout(timer11);
|
|
66153
66244
|
resolve24(value);
|
|
66154
66245
|
}, (error) => {
|
|
66155
|
-
clearTimeout(
|
|
66246
|
+
clearTimeout(timer11);
|
|
66156
66247
|
reject2(error);
|
|
66157
66248
|
});
|
|
66158
66249
|
});
|
|
@@ -66175,16 +66266,16 @@ function scannerFromEnv(path) {
|
|
|
66175
66266
|
if (!isAbsolute8(command)) return Promise.reject(new UploadError("UPLOAD_SCAN", "scanner path must be absolute"));
|
|
66176
66267
|
return new Promise((resolve24, reject2) => {
|
|
66177
66268
|
const child = spawn2(command, [path], { shell: false, stdio: "ignore" });
|
|
66178
|
-
const
|
|
66269
|
+
const timer11 = setTimeout(() => {
|
|
66179
66270
|
child.kill("SIGKILL");
|
|
66180
66271
|
reject2(new UploadError("UPLOAD_SCAN", "scanner timeout"));
|
|
66181
66272
|
}, UPLOAD_LIMITS.scanMs);
|
|
66182
66273
|
child.once("error", (error) => {
|
|
66183
|
-
clearTimeout(
|
|
66274
|
+
clearTimeout(timer11);
|
|
66184
66275
|
reject2(error);
|
|
66185
66276
|
});
|
|
66186
66277
|
child.once("exit", (code) => {
|
|
66187
|
-
clearTimeout(
|
|
66278
|
+
clearTimeout(timer11);
|
|
66188
66279
|
if (code === 0) resolve24();
|
|
66189
66280
|
else reject2(new UploadError("UPLOAD_SCAN", `scanner exit ${code}`));
|
|
66190
66281
|
});
|
|
@@ -66340,12 +66431,12 @@ async function addSpecAttachments(spec, files2) {
|
|
|
66340
66431
|
continue;
|
|
66341
66432
|
}
|
|
66342
66433
|
const ext = attachmentExt(name2);
|
|
66343
|
-
let
|
|
66434
|
+
let safe3;
|
|
66344
66435
|
try {
|
|
66345
66436
|
const image = IMAGE_TYPES[f.mime];
|
|
66346
66437
|
if (image) {
|
|
66347
66438
|
if (!image.includes(ext)) throw new UploadError("UPLOAD_TYPE", "extension mismatch");
|
|
66348
|
-
|
|
66439
|
+
safe3 = await processUpload({
|
|
66349
66440
|
buffer: f.buf,
|
|
66350
66441
|
clientName: name2,
|
|
66351
66442
|
clientMime: f.mime,
|
|
@@ -66353,7 +66444,7 @@ async function addSpecAttachments(spec, files2) {
|
|
|
66353
66444
|
parentBytes: bytes
|
|
66354
66445
|
});
|
|
66355
66446
|
} else if (DOCUMENT_TYPES[f.mime]) {
|
|
66356
|
-
|
|
66447
|
+
safe3 = await processDocumentUpload({
|
|
66357
66448
|
buffer: f.buf,
|
|
66358
66449
|
clientName: name2,
|
|
66359
66450
|
clientMime: f.mime,
|
|
@@ -66372,17 +66463,17 @@ async function addSpecAttachments(spec, files2) {
|
|
|
66372
66463
|
row = await prisma.specAttachment.create({ data: {
|
|
66373
66464
|
specId: spec.id,
|
|
66374
66465
|
projectId: spec.projectId,
|
|
66375
|
-
filename:
|
|
66376
|
-
mimeType:
|
|
66377
|
-
size:
|
|
66378
|
-
storageKey:
|
|
66466
|
+
filename: safe3.filename,
|
|
66467
|
+
mimeType: safe3.mimeType,
|
|
66468
|
+
size: safe3.size,
|
|
66469
|
+
storageKey: safe3.storageKey
|
|
66379
66470
|
} });
|
|
66380
66471
|
} catch (error) {
|
|
66381
|
-
await deleteUpload(
|
|
66472
|
+
await deleteUpload(safe3.storageKey);
|
|
66382
66473
|
throw error;
|
|
66383
66474
|
}
|
|
66384
66475
|
count += 1;
|
|
66385
|
-
bytes +=
|
|
66476
|
+
bytes += safe3.size;
|
|
66386
66477
|
saved.push(view2(row));
|
|
66387
66478
|
}
|
|
66388
66479
|
return { saved, rejected };
|
|
@@ -66590,14 +66681,14 @@ async function addQaAttachments(report, owner, files2) {
|
|
|
66590
66681
|
continue;
|
|
66591
66682
|
}
|
|
66592
66683
|
const ext = attachmentExt(name2);
|
|
66593
|
-
let
|
|
66684
|
+
let safe3;
|
|
66594
66685
|
try {
|
|
66595
66686
|
const image = IMAGE_TYPES[f.mime];
|
|
66596
66687
|
if (image) {
|
|
66597
66688
|
if (!image.includes(ext)) throw new UploadError("UPLOAD_TYPE", "extension mismatch");
|
|
66598
|
-
|
|
66689
|
+
safe3 = await processUpload({ buffer: f.buf, clientName: name2, clientMime: f.mime, projectId: report.projectId, parentBytes: bytes });
|
|
66599
66690
|
} else if (DOCUMENT_TYPES[f.mime]) {
|
|
66600
|
-
|
|
66691
|
+
safe3 = await processDocumentUpload({ buffer: f.buf, clientName: name2, clientMime: f.mime, clientExt: ext });
|
|
66601
66692
|
} else {
|
|
66602
66693
|
throw new UploadError("UPLOAD_TYPE", "unsupported type");
|
|
66603
66694
|
}
|
|
@@ -66607,28 +66698,28 @@ async function addQaAttachments(report, owner, files2) {
|
|
|
66607
66698
|
continue;
|
|
66608
66699
|
}
|
|
66609
66700
|
try {
|
|
66610
|
-
const sha256 = createHash6("sha256").update(await readUpload(
|
|
66701
|
+
const sha256 = createHash6("sha256").update(await readUpload(safe3.storageKey)).digest("hex");
|
|
66611
66702
|
const row = await prisma.qaAttachment.create({ data: {
|
|
66612
66703
|
reportId: report.id,
|
|
66613
66704
|
projectId: report.projectId,
|
|
66614
66705
|
ownerType: owner.ownerType,
|
|
66615
66706
|
ownerId: owner.ownerId,
|
|
66616
|
-
filename:
|
|
66617
|
-
mimeType:
|
|
66618
|
-
size:
|
|
66707
|
+
filename: safe3.filename,
|
|
66708
|
+
mimeType: safe3.mimeType,
|
|
66709
|
+
size: safe3.size,
|
|
66619
66710
|
sha256,
|
|
66620
|
-
storageKey:
|
|
66711
|
+
storageKey: safe3.storageKey,
|
|
66621
66712
|
// Client: byte baru ada di sini saja → menunggu unggah ke hub. Hub/standalone: tak ada atasan → langsung tersedia.
|
|
66622
66713
|
syncState: effectiveStr("SYNC_SERVER_URL") ? "local-only" : "available"
|
|
66623
66714
|
} });
|
|
66624
66715
|
await notifySynced("qaAttachment", row.id);
|
|
66625
66716
|
saved.push(attachmentView(row));
|
|
66626
66717
|
} catch (error) {
|
|
66627
|
-
await deleteUpload(
|
|
66718
|
+
await deleteUpload(safe3.storageKey);
|
|
66628
66719
|
throw error;
|
|
66629
66720
|
}
|
|
66630
66721
|
count += 1;
|
|
66631
|
-
bytes +=
|
|
66722
|
+
bytes += safe3.size;
|
|
66632
66723
|
}
|
|
66633
66724
|
return { saved, rejected };
|
|
66634
66725
|
}
|
|
@@ -66964,9 +67055,9 @@ async function toProjectView(p3, sessions, devices2) {
|
|
|
66964
67055
|
const specs = await prisma.spec.findMany({ where: { projectId: p3.id } });
|
|
66965
67056
|
const { coverage } = await scanRepoDocs(await resolveRepoDir(p3.id));
|
|
66966
67057
|
const binding = await getBinding(p3.id);
|
|
66967
|
-
const
|
|
67058
|
+
const open6 = specs.filter((s2) => s2.stage !== "done");
|
|
66968
67059
|
const { session, commit } = sessionOf(p3.id, sessions);
|
|
66969
|
-
const topStage =
|
|
67060
|
+
const topStage = open6.length ? open6.map((s2) => s2.stage).sort((a, b) => STAGES.indexOf(b) - STAGES.indexOf(a))[0] : "spec";
|
|
66970
67061
|
return {
|
|
66971
67062
|
id: p3.id,
|
|
66972
67063
|
name: p3.name,
|
|
@@ -66979,7 +67070,7 @@ async function toProjectView(p3, sessions, devices2) {
|
|
|
66979
67070
|
docStatus: docStatusFor(coverage),
|
|
66980
67071
|
coverage,
|
|
66981
67072
|
createdAt: p3.createdAt.toISOString(),
|
|
66982
|
-
backlog:
|
|
67073
|
+
backlog: open6.length,
|
|
66983
67074
|
topStage,
|
|
66984
67075
|
session,
|
|
66985
67076
|
activity: session.status === "running" ? `running \xB7 ${session.flow ?? "sesi"}` : "idle",
|
|
@@ -68448,9 +68539,9 @@ async function deleteWorktrees(repoDir, names, opts2) {
|
|
|
68448
68539
|
try {
|
|
68449
68540
|
if (w.locked) await opts2.unlock(report.repoDir, w.path);
|
|
68450
68541
|
if (w.session) {
|
|
68451
|
-
const
|
|
68542
|
+
const closed2 = await opts2.closeSession(w.session.id);
|
|
68452
68543
|
row.closedSession = w.session.id;
|
|
68453
|
-
row.cleanup =
|
|
68544
|
+
row.cleanup = closed2?.cleanup ?? null;
|
|
68454
68545
|
}
|
|
68455
68546
|
if (!row.cleanup) row.cleanup = opts2.release(report.repoDir, w.path);
|
|
68456
68547
|
await opts2.prune(report.repoDir);
|
|
@@ -68757,11 +68848,11 @@ async function beginSession(b) {
|
|
|
68757
68848
|
});
|
|
68758
68849
|
}
|
|
68759
68850
|
async function finishSession(d) {
|
|
68760
|
-
const
|
|
68851
|
+
const open6 = await prisma.sessionHistory.findFirst({
|
|
68761
68852
|
where: { sessionId: d.sessionId, endedAt: null },
|
|
68762
68853
|
orderBy: { startedAt: "desc" }
|
|
68763
68854
|
});
|
|
68764
|
-
if (!
|
|
68855
|
+
if (!open6) return;
|
|
68765
68856
|
let t = { key: "", bytes: 0 };
|
|
68766
68857
|
if (d.transcript) {
|
|
68767
68858
|
try {
|
|
@@ -68771,7 +68862,7 @@ async function finishSession(d) {
|
|
|
68771
68862
|
}
|
|
68772
68863
|
}
|
|
68773
68864
|
await prisma.sessionHistory.update({
|
|
68774
|
-
where: { id:
|
|
68865
|
+
where: { id: open6.id },
|
|
68775
68866
|
data: {
|
|
68776
68867
|
endedAt: /* @__PURE__ */ new Date(),
|
|
68777
68868
|
endedReason: CLOSED,
|
|
@@ -68886,22 +68977,22 @@ async function reconcileTranscripts(opts2 = {}) {
|
|
|
68886
68977
|
return report;
|
|
68887
68978
|
}
|
|
68888
68979
|
async function reconcileHistory(liveSessionIds) {
|
|
68889
|
-
const
|
|
68980
|
+
const open6 = await prisma.sessionHistory.findMany({
|
|
68890
68981
|
where: { endedAt: null },
|
|
68891
68982
|
select: { id: true, sessionId: true, updatedAt: true }
|
|
68892
68983
|
});
|
|
68893
68984
|
const live = new Set(liveSessionIds);
|
|
68894
68985
|
const at = /* @__PURE__ */ new Date();
|
|
68895
|
-
let
|
|
68896
|
-
for (const r of
|
|
68986
|
+
let closed2 = 0;
|
|
68987
|
+
for (const r of open6) {
|
|
68897
68988
|
if (live.has(r.sessionId)) continue;
|
|
68898
68989
|
await prisma.sessionHistory.update({
|
|
68899
68990
|
where: { id: r.id },
|
|
68900
68991
|
data: { endedAt: r.updatedAt, endedReason: RECONCILED, reconciledAt: at }
|
|
68901
68992
|
});
|
|
68902
|
-
|
|
68993
|
+
closed2++;
|
|
68903
68994
|
}
|
|
68904
|
-
return
|
|
68995
|
+
return closed2;
|
|
68905
68996
|
}
|
|
68906
68997
|
async function reconciledSpecIdsSince(cutoff) {
|
|
68907
68998
|
const rows = await prisma.sessionHistory.findMany({
|
|
@@ -68973,13 +69064,13 @@ async function detectOrphanWorktrees(deps = { repos: prodReaperDeps.repos, input
|
|
|
68973
69064
|
// src/services/worktree-delete.ts
|
|
68974
69065
|
var execAsync = promisify11(execFile12);
|
|
68975
69066
|
async function lockInputs(id2) {
|
|
68976
|
-
const
|
|
69067
|
+
const open6 = await prisma.spec.findMany({
|
|
68977
69068
|
where: { projectId: id2, stage: { not: "done" } },
|
|
68978
69069
|
select: { id: true }
|
|
68979
69070
|
});
|
|
68980
69071
|
const sessions = listSessions().filter((s2) => s2.projectId === id2 && !s2.exited).map((s2) => s2.branch || (s2.specId ? `hanoman/${s2.id}` : "")).filter(Boolean);
|
|
68981
69072
|
return {
|
|
68982
|
-
openSpecBranches: new Set(
|
|
69073
|
+
openSpecBranches: new Set(open6.map((s2) => sourceBranch(s2.id))),
|
|
68983
69074
|
sessionBranches: new Set(sessions)
|
|
68984
69075
|
};
|
|
68985
69076
|
}
|
|
@@ -74246,7 +74337,7 @@ function createBoundedSender(ws2, o = {}) {
|
|
|
74246
74337
|
const drainMs = o.drainMs ?? DRAIN_MS;
|
|
74247
74338
|
let pending2 = [];
|
|
74248
74339
|
let pendingBytes = 0;
|
|
74249
|
-
let
|
|
74340
|
+
let timer11;
|
|
74250
74341
|
let resyncNeeded = false;
|
|
74251
74342
|
const trim = () => {
|
|
74252
74343
|
for (let i = 0; i < pending2.length && pendingBytes > cap; ) {
|
|
@@ -74260,10 +74351,10 @@ function createBoundedSender(ws2, o = {}) {
|
|
|
74260
74351
|
}
|
|
74261
74352
|
};
|
|
74262
74353
|
const drain2 = () => {
|
|
74263
|
-
|
|
74354
|
+
timer11 = void 0;
|
|
74264
74355
|
if (ws2.bufferedAmount > high) {
|
|
74265
|
-
|
|
74266
|
-
|
|
74356
|
+
timer11 = setTimeout(drain2, drainMs);
|
|
74357
|
+
timer11.unref?.();
|
|
74267
74358
|
return;
|
|
74268
74359
|
}
|
|
74269
74360
|
const out4 = pending2;
|
|
@@ -74284,14 +74375,14 @@ function createBoundedSender(ws2, o = {}) {
|
|
|
74284
74375
|
pending2.push(frame2);
|
|
74285
74376
|
pendingBytes += frame2.length;
|
|
74286
74377
|
trim();
|
|
74287
|
-
if (!
|
|
74288
|
-
|
|
74289
|
-
|
|
74378
|
+
if (!timer11) {
|
|
74379
|
+
timer11 = setTimeout(drain2, drainMs);
|
|
74380
|
+
timer11.unref?.();
|
|
74290
74381
|
}
|
|
74291
74382
|
},
|
|
74292
74383
|
dispose() {
|
|
74293
|
-
if (
|
|
74294
|
-
|
|
74384
|
+
if (timer11) clearTimeout(timer11);
|
|
74385
|
+
timer11 = void 0;
|
|
74295
74386
|
pending2 = [];
|
|
74296
74387
|
pendingBytes = 0;
|
|
74297
74388
|
}
|
|
@@ -74439,7 +74530,7 @@ function startSessionEventRelay(app3, options2 = {}) {
|
|
|
74439
74530
|
const root = options2.root ?? sessionEventSpoolRoot();
|
|
74440
74531
|
const host2 = controlHost(loadIngressPolicy(options2.env ?? process.env));
|
|
74441
74532
|
let running = false;
|
|
74442
|
-
const
|
|
74533
|
+
const tick7 = async () => {
|
|
74443
74534
|
if (running) return;
|
|
74444
74535
|
running = true;
|
|
74445
74536
|
try {
|
|
@@ -74450,14 +74541,14 @@ function startSessionEventRelay(app3, options2 = {}) {
|
|
|
74450
74541
|
running = false;
|
|
74451
74542
|
}
|
|
74452
74543
|
};
|
|
74453
|
-
const
|
|
74454
|
-
void
|
|
74544
|
+
const timer11 = setInterval(() => {
|
|
74545
|
+
void tick7();
|
|
74455
74546
|
}, options2.intervalMs ?? 250);
|
|
74456
|
-
|
|
74547
|
+
timer11.unref();
|
|
74457
74548
|
app3.addHook("onClose", async () => {
|
|
74458
|
-
clearInterval(
|
|
74549
|
+
clearInterval(timer11);
|
|
74459
74550
|
});
|
|
74460
|
-
void
|
|
74551
|
+
void tick7();
|
|
74461
74552
|
}
|
|
74462
74553
|
|
|
74463
74554
|
// src/services/agent-invocations.ts
|
|
@@ -74681,10 +74772,10 @@ async function stopAgentInvocation(input, io = {}) {
|
|
|
74681
74772
|
}
|
|
74682
74773
|
async function reconcileAgentInvocations(liveSessionIds) {
|
|
74683
74774
|
const live = new Set(liveSessionIds);
|
|
74684
|
-
const
|
|
74775
|
+
const open6 = await prisma.agentInvocation.findMany({ where: { status: "running" } });
|
|
74685
74776
|
let changed = 0;
|
|
74686
74777
|
const endedAt = /* @__PURE__ */ new Date();
|
|
74687
|
-
for (const row of
|
|
74778
|
+
for (const row of open6) {
|
|
74688
74779
|
if (live.has(row.sessionId)) continue;
|
|
74689
74780
|
await prisma.agentInvocation.update({
|
|
74690
74781
|
where: { id: row.id },
|
|
@@ -75080,8 +75171,8 @@ async function expireFlows(now) {
|
|
|
75080
75171
|
});
|
|
75081
75172
|
const out4 = [];
|
|
75082
75173
|
for (const f of due) {
|
|
75083
|
-
const
|
|
75084
|
-
if (
|
|
75174
|
+
const closed2 = await closeFlow(f.id, "kedaluwarsa");
|
|
75175
|
+
if (closed2) out4.push(closed2);
|
|
75085
75176
|
}
|
|
75086
75177
|
return out4;
|
|
75087
75178
|
}
|
|
@@ -76768,7 +76859,7 @@ async function terminal_default(app3, opts2) {
|
|
|
76768
76859
|
});
|
|
76769
76860
|
const client = { send: (m) => bounded.send(m), close: () => socket2.close() };
|
|
76770
76861
|
let ready = false;
|
|
76771
|
-
let
|
|
76862
|
+
let closed2 = false;
|
|
76772
76863
|
const early = [];
|
|
76773
76864
|
const attachReady = (async () => {
|
|
76774
76865
|
const found = await getSessionAsync(id2);
|
|
@@ -76776,9 +76867,9 @@ async function terminal_default(app3, opts2) {
|
|
|
76776
76867
|
socket2.close(4004, "not found");
|
|
76777
76868
|
return;
|
|
76778
76869
|
}
|
|
76779
|
-
if (
|
|
76870
|
+
if (closed2) return;
|
|
76780
76871
|
await attachAsync(id2, client);
|
|
76781
|
-
if (
|
|
76872
|
+
if (closed2) {
|
|
76782
76873
|
detach(id2, client);
|
|
76783
76874
|
return;
|
|
76784
76875
|
}
|
|
@@ -76826,7 +76917,7 @@ async function terminal_default(app3, opts2) {
|
|
|
76826
76917
|
const revalidate = watch ? setInterval(() => watch.refresh(), 6e4) : void 0;
|
|
76827
76918
|
revalidate?.unref?.();
|
|
76828
76919
|
socket2.on("close", () => {
|
|
76829
|
-
|
|
76920
|
+
closed2 = true;
|
|
76830
76921
|
bounded.dispose();
|
|
76831
76922
|
if (revalidate) clearInterval(revalidate);
|
|
76832
76923
|
watch?.dispose();
|
|
@@ -77184,9 +77275,9 @@ function sshExec(t, remoteCmd, opts2 = {}) {
|
|
|
77184
77275
|
return new Promise((resolve24) => {
|
|
77185
77276
|
const p3 = spawn4(sshBin(), args, { stdio: ["pipe", "pipe", "pipe"], env });
|
|
77186
77277
|
let out4 = "";
|
|
77187
|
-
const
|
|
77278
|
+
const timer11 = setTimeout(() => p3.kill("SIGKILL"), opts2.timeoutMs ?? 6e4);
|
|
77188
77279
|
const done = (r) => {
|
|
77189
|
-
clearTimeout(
|
|
77280
|
+
clearTimeout(timer11);
|
|
77190
77281
|
if (askpass) rmSync12(dirname15(askpass), { recursive: true, force: true });
|
|
77191
77282
|
resolve24(r);
|
|
77192
77283
|
};
|
|
@@ -78258,7 +78349,7 @@ function runCatalogCommand(command, agent, cwd, env, timeoutMs = TIMEOUT_MS) {
|
|
|
78258
78349
|
const finish = (error, result) => {
|
|
78259
78350
|
if (settled) return;
|
|
78260
78351
|
settled = true;
|
|
78261
|
-
clearTimeout(
|
|
78352
|
+
clearTimeout(timer11);
|
|
78262
78353
|
child.stdin.end();
|
|
78263
78354
|
if (child.exitCode === null && child.signalCode === null) {
|
|
78264
78355
|
child.kill("SIGTERM");
|
|
@@ -78268,7 +78359,7 @@ function runCatalogCommand(command, agent, cwd, env, timeoutMs = TIMEOUT_MS) {
|
|
|
78268
78359
|
if (error) reject2(error);
|
|
78269
78360
|
else resolve24(result);
|
|
78270
78361
|
};
|
|
78271
|
-
const
|
|
78362
|
+
const timer11 = setTimeout(() => finish(new Error("Pemeriksaan model melewati batas waktu")), timeoutMs);
|
|
78272
78363
|
child.on("error", () => finish(new Error("CLI model tidak dapat dijalankan")));
|
|
78273
78364
|
child.stdin.on("error", () => finish(new Error("Kanal katalog CLI terputus")));
|
|
78274
78365
|
child.stderr.on("data", () => {
|
|
@@ -78409,11 +78500,11 @@ var modelCatalogService = createModelCatalogService({
|
|
|
78409
78500
|
});
|
|
78410
78501
|
function startModelDiscovery() {
|
|
78411
78502
|
void modelCatalogService.refresh();
|
|
78412
|
-
const
|
|
78503
|
+
const timer11 = setInterval(() => {
|
|
78413
78504
|
void modelCatalogService.refresh();
|
|
78414
78505
|
}, 5 * 6e4);
|
|
78415
|
-
|
|
78416
|
-
return () => clearInterval(
|
|
78506
|
+
timer11.unref();
|
|
78507
|
+
return () => clearInterval(timer11);
|
|
78417
78508
|
}
|
|
78418
78509
|
|
|
78419
78510
|
// src/routes/models.ts
|
|
@@ -78438,18 +78529,18 @@ function requestConfigRestart(delayMs = 50) {
|
|
|
78438
78529
|
}
|
|
78439
78530
|
function afterReplyFlushed(raw, fn, maxWaitMs = 5e3) {
|
|
78440
78531
|
let done = false;
|
|
78441
|
-
let
|
|
78532
|
+
let timer11;
|
|
78442
78533
|
const run5 = () => {
|
|
78443
78534
|
if (done) return;
|
|
78444
78535
|
done = true;
|
|
78445
|
-
if (
|
|
78536
|
+
if (timer11) clearTimeout(timer11);
|
|
78446
78537
|
raw.removeListener("finish", run5);
|
|
78447
78538
|
raw.removeListener("close", run5);
|
|
78448
78539
|
fn();
|
|
78449
78540
|
};
|
|
78450
78541
|
raw.once("finish", run5);
|
|
78451
78542
|
raw.once("close", run5);
|
|
78452
|
-
|
|
78543
|
+
timer11 = setTimeout(run5, maxWaitMs);
|
|
78453
78544
|
}
|
|
78454
78545
|
function requestManualRestart(delayMs = 250) {
|
|
78455
78546
|
setTimeout(() => process.exit(MANUAL_RESTART_EXIT), delayMs);
|
|
@@ -78516,6 +78607,364 @@ async function pendingCounts(now = Date.now(), prds = listAllPrds) {
|
|
|
78516
78607
|
return { ...EMPTY_PENDING, triage: tickets2 + issues, backlog, prd, lead };
|
|
78517
78608
|
}
|
|
78518
78609
|
|
|
78610
|
+
// src/services/overview-source.ts
|
|
78611
|
+
init_db();
|
|
78612
|
+
|
|
78613
|
+
// src/services/overview-metrics.ts
|
|
78614
|
+
init_src();
|
|
78615
|
+
var AGENT_KINDS = /* @__PURE__ */ new Set(["spec", "reverse", "prd", "scaffold", "breakdown", "telegram"]);
|
|
78616
|
+
var MIN_MS = 6e4;
|
|
78617
|
+
var HOUR_MS2 = 36e5;
|
|
78618
|
+
var startOfDay = (t) => {
|
|
78619
|
+
const d = new Date(t);
|
|
78620
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
78621
|
+
};
|
|
78622
|
+
var addDays = (t, n2) => {
|
|
78623
|
+
const d = new Date(t);
|
|
78624
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate() + n2, d.getHours(), d.getMinutes()).getTime();
|
|
78625
|
+
};
|
|
78626
|
+
var dayKey = (t) => {
|
|
78627
|
+
const d = new Date(t);
|
|
78628
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
78629
|
+
};
|
|
78630
|
+
var overlapMs = (a0, a1, b0, b1) => Math.max(0, Math.min(a1, b1) - Math.max(a0, b0));
|
|
78631
|
+
function median2(xs) {
|
|
78632
|
+
if (xs.length === 0) return null;
|
|
78633
|
+
const s2 = [...xs].sort((a, b) => a - b);
|
|
78634
|
+
const m = s2.length >> 1;
|
|
78635
|
+
return s2.length % 2 ? s2[m] : (s2[m - 1] + s2[m]) / 2;
|
|
78636
|
+
}
|
|
78637
|
+
var inRange = (d, t0, t1) => d !== null && d.getTime() >= t0 && d.getTime() < t1;
|
|
78638
|
+
var isFailed = (s2) => AGENT_KINDS.has(s2.kind) && s2.endedAt !== null && sessionOutcome({ endedAt: s2.endedAt.toISOString(), endedReason: s2.endedReason, exitCode: s2.exitCode }) === "failed";
|
|
78639
|
+
function dayStats(dayStart2, specs, sessions) {
|
|
78640
|
+
const dayEnd2 = addDays(dayStart2, 1);
|
|
78641
|
+
return {
|
|
78642
|
+
landed: specs.filter((s2) => inRange(s2.doneAt, dayStart2, dayEnd2)).length,
|
|
78643
|
+
failed: sessions.filter((s2) => isFailed(s2) && inRange(s2.endedAt, dayStart2, dayEnd2)).length
|
|
78644
|
+
};
|
|
78645
|
+
}
|
|
78646
|
+
function riverStats(now, specs) {
|
|
78647
|
+
const t0 = startOfDay(now);
|
|
78648
|
+
const t1 = addDays(t0, 1);
|
|
78649
|
+
const week0 = addDays(t0, -6);
|
|
78650
|
+
const done7 = specs.filter((s2) => inRange(s2.doneAt, week0, t1));
|
|
78651
|
+
return {
|
|
78652
|
+
created: specs.filter((s2) => inRange(s2.createdAt, t0, t1)).length,
|
|
78653
|
+
started: specs.filter((s2) => inRange(s2.startedAt, t0, t1)).length,
|
|
78654
|
+
landed: specs.filter((s2) => inRange(s2.doneAt, t0, t1)).length,
|
|
78655
|
+
leadTimeMedMs: median2(done7.map((s2) => s2.doneAt.getTime() - s2.createdAt.getTime())),
|
|
78656
|
+
cycleTimeMedMs: median2(done7.filter((s2) => s2.startedAt).map((s2) => s2.doneAt.getTime() - s2.startedAt.getTime()))
|
|
78657
|
+
};
|
|
78658
|
+
}
|
|
78659
|
+
var invTotal = (i) => (i.inputTokens ?? 0) + (i.outputTokens ?? 0) + (i.cachedTokens ?? 0);
|
|
78660
|
+
function tokenStats(now, invs, landedToday) {
|
|
78661
|
+
const t0 = startOfDay(now);
|
|
78662
|
+
const y0 = addDays(t0, -1);
|
|
78663
|
+
const today = invs.filter((i) => i.startedAt.getTime() >= t0);
|
|
78664
|
+
const input = today.reduce((n2, i) => n2 + (i.inputTokens ?? 0), 0);
|
|
78665
|
+
const output = today.reduce((n2, i) => n2 + (i.outputTokens ?? 0), 0);
|
|
78666
|
+
const cached3 = today.reduce((n2, i) => n2 + (i.cachedTokens ?? 0), 0);
|
|
78667
|
+
const total = input + output + cached3;
|
|
78668
|
+
const byAgent = /* @__PURE__ */ new Map();
|
|
78669
|
+
for (const i of today) byAgent.set(i.agentName, (byAgent.get(i.agentName) ?? 0) + invTotal(i));
|
|
78670
|
+
let topAgent = null;
|
|
78671
|
+
for (const [name2, n2] of byAgent) if (!topAgent || n2 > topAgent.total) topAgent = { name: name2, total: n2 };
|
|
78672
|
+
return {
|
|
78673
|
+
input,
|
|
78674
|
+
output,
|
|
78675
|
+
cached: cached3,
|
|
78676
|
+
total,
|
|
78677
|
+
cacheHitPct: input + cached3 > 0 ? Math.round(cached3 / (input + cached3) * 100) : null,
|
|
78678
|
+
perLanded: landedToday && landedToday > 0 ? Math.round(total / landedToday) : null,
|
|
78679
|
+
topAgent,
|
|
78680
|
+
yesterdayTotal: invs.filter((i) => i.startedAt.getTime() >= y0 && i.startedAt.getTime() < t0).reduce((n2, i) => n2 + invTotal(i), 0)
|
|
78681
|
+
};
|
|
78682
|
+
}
|
|
78683
|
+
function liveMs(sessions, t0, t1, now) {
|
|
78684
|
+
let ms = 0;
|
|
78685
|
+
for (const s2 of sessions) {
|
|
78686
|
+
if (!AGENT_KINDS.has(s2.kind)) continue;
|
|
78687
|
+
ms += overlapMs(s2.startedAt.getTime(), s2.endedAt ? s2.endedAt.getTime() : now, t0, t1);
|
|
78688
|
+
}
|
|
78689
|
+
return ms;
|
|
78690
|
+
}
|
|
78691
|
+
var spanMs = (spans, t0, t1, now) => spans.reduce((n2, w) => n2 + overlapMs(w.start, w.end ?? now, t0, t1), 0);
|
|
78692
|
+
function waitStats(now, waits, sessions) {
|
|
78693
|
+
const t0 = startOfDay(now);
|
|
78694
|
+
const t1 = addDays(t0, 1);
|
|
78695
|
+
const y0 = addDays(t0, -1);
|
|
78696
|
+
const todayMs = spanMs(waits, t0, t1, now);
|
|
78697
|
+
const live = sessions ? liveMs(sessions, t0, t1, now) : 0;
|
|
78698
|
+
return {
|
|
78699
|
+
todayMin: Math.round(todayMs / MIN_MS),
|
|
78700
|
+
yesterdayMin: Math.round(spanMs(waits, y0, t0, now) / MIN_MS),
|
|
78701
|
+
pctOfLive: sessions && live > 0 ? Math.min(100, Math.round(todayMs / live * 100)) : null
|
|
78702
|
+
};
|
|
78703
|
+
}
|
|
78704
|
+
function heatmapStats(now, sessions) {
|
|
78705
|
+
const t0 = startOfDay(now);
|
|
78706
|
+
const days2 = [];
|
|
78707
|
+
const minutes = [];
|
|
78708
|
+
for (let i = 6; i >= 0; i--) {
|
|
78709
|
+
const d0 = addDays(t0, -i);
|
|
78710
|
+
const base3 = new Date(d0);
|
|
78711
|
+
days2.push(dayKey(d0));
|
|
78712
|
+
const row = [];
|
|
78713
|
+
for (let h2 = 0; h2 < 24; h2++) {
|
|
78714
|
+
const h0 = new Date(base3.getFullYear(), base3.getMonth(), base3.getDate(), h2).getTime();
|
|
78715
|
+
const h1 = new Date(base3.getFullYear(), base3.getMonth(), base3.getDate(), h2 + 1).getTime();
|
|
78716
|
+
row.push(Math.round(liveMs(sessions, h0, h1, now) / MIN_MS));
|
|
78717
|
+
}
|
|
78718
|
+
minutes.push(row);
|
|
78719
|
+
}
|
|
78720
|
+
return { days: days2, minutes };
|
|
78721
|
+
}
|
|
78722
|
+
var RECORD_WINDOW_DAYS = 90;
|
|
78723
|
+
function recordStats(now, specs, sessions) {
|
|
78724
|
+
const t0 = startOfDay(now);
|
|
78725
|
+
const windowStart = addDays(t0, -(RECORD_WINDOW_DAYS - 1));
|
|
78726
|
+
const agent = sessions.filter((s2) => AGENT_KINDS.has(s2.kind));
|
|
78727
|
+
const failedDays = new Set(agent.filter(isFailed).map((s2) => dayKey(s2.endedAt.getTime())));
|
|
78728
|
+
let streakDays = 0;
|
|
78729
|
+
let bestStreak = 0;
|
|
78730
|
+
if (agent.length > 0) {
|
|
78731
|
+
const first = Math.max(windowStart, startOfDay(Math.min(...agent.map((s2) => s2.startedAt.getTime()))));
|
|
78732
|
+
let run5 = 0;
|
|
78733
|
+
for (let d = first; d <= t0; d = addDays(d, 1)) {
|
|
78734
|
+
run5 = failedDays.has(dayKey(d)) ? 0 : run5 + 1;
|
|
78735
|
+
bestStreak = Math.max(bestStreak, run5);
|
|
78736
|
+
}
|
|
78737
|
+
streakDays = run5;
|
|
78738
|
+
}
|
|
78739
|
+
const landedByDay = /* @__PURE__ */ new Map();
|
|
78740
|
+
for (const s2 of specs) {
|
|
78741
|
+
if (!s2.doneAt || s2.doneAt.getTime() < windowStart) continue;
|
|
78742
|
+
const k = dayKey(s2.doneAt.getTime());
|
|
78743
|
+
landedByDay.set(k, (landedByDay.get(k) ?? 0) + 1);
|
|
78744
|
+
}
|
|
78745
|
+
let bestLandedDay = null;
|
|
78746
|
+
for (const [date, count] of [...landedByDay].sort(([a], [b]) => a.localeCompare(b)))
|
|
78747
|
+
if (!bestLandedDay || count > bestLandedDay.count) bestLandedDay = { date, count };
|
|
78748
|
+
const todayKey = dayKey(t0);
|
|
78749
|
+
const todayCount = landedByDay.get(todayKey) ?? 0;
|
|
78750
|
+
const prevMax = Math.max(0, ...[...landedByDay].filter(([k]) => k !== todayKey).map(([, n2]) => n2));
|
|
78751
|
+
return { streakDays, bestStreak, bestLandedDay, landedRecordToday: prevMax > 0 && todayCount > prevMax };
|
|
78752
|
+
}
|
|
78753
|
+
var BENSIN_LOOKBACK_MS = 60 * MIN_MS;
|
|
78754
|
+
var BENSIN_MIN_SAMPLES = 4;
|
|
78755
|
+
var BENSIN_MIN_SPAN_MS = 15 * MIN_MS;
|
|
78756
|
+
function slopeOf(pts) {
|
|
78757
|
+
const n2 = pts.length;
|
|
78758
|
+
const mx = pts.reduce((s2, p3) => s2 + p3.at, 0) / n2;
|
|
78759
|
+
const my = pts.reduce((s2, p3) => s2 + p3.pct, 0) / n2;
|
|
78760
|
+
let num = 0;
|
|
78761
|
+
let den = 0;
|
|
78762
|
+
for (const p3 of pts) {
|
|
78763
|
+
num += (p3.at - mx) * (p3.pct - my);
|
|
78764
|
+
den += (p3.at - mx) ** 2;
|
|
78765
|
+
}
|
|
78766
|
+
return den === 0 ? 0 : num / den;
|
|
78767
|
+
}
|
|
78768
|
+
function bensinStats(now, series2) {
|
|
78769
|
+
const out4 = [];
|
|
78770
|
+
for (const s2 of series2) {
|
|
78771
|
+
const last2 = s2.samples.at(-1);
|
|
78772
|
+
if (!last2) continue;
|
|
78773
|
+
const recent = s2.samples.filter((p3) => p3.at >= now - BENSIN_LOOKBACK_MS);
|
|
78774
|
+
const base3 = {
|
|
78775
|
+
source: s2.source,
|
|
78776
|
+
key: s2.key,
|
|
78777
|
+
label: s2.label,
|
|
78778
|
+
usedPct: last2.pct,
|
|
78779
|
+
resetsAt: s2.resetsAt,
|
|
78780
|
+
samples: recent.map((p3) => ({ at: new Date(p3.at).toISOString(), pct: p3.pct }))
|
|
78781
|
+
};
|
|
78782
|
+
if (recent.length < BENSIN_MIN_SAMPLES || last2.at - recent[0].at < BENSIN_MIN_SPAN_MS) {
|
|
78783
|
+
out4.push({ ...base3, exhaustAt: null, verdict: "kurang-data" });
|
|
78784
|
+
continue;
|
|
78785
|
+
}
|
|
78786
|
+
const slope = slopeOf(recent);
|
|
78787
|
+
if (slope <= 0) {
|
|
78788
|
+
out4.push({ ...base3, exhaustAt: null, verdict: "stabil" });
|
|
78789
|
+
continue;
|
|
78790
|
+
}
|
|
78791
|
+
const exhaust = Math.round(last2.at + (100 - last2.pct) / slope);
|
|
78792
|
+
const reset = s2.resetsAt ? Date.parse(s2.resetsAt) : Number.NaN;
|
|
78793
|
+
if (Number.isFinite(reset) && exhaust >= reset) out4.push({ ...base3, exhaustAt: null, verdict: "aman" });
|
|
78794
|
+
else out4.push({ ...base3, exhaustAt: new Date(exhaust).toISOString(), verdict: "habis" });
|
|
78795
|
+
}
|
|
78796
|
+
return out4;
|
|
78797
|
+
}
|
|
78798
|
+
function fmtMinutes(min) {
|
|
78799
|
+
if (min < 60) return `${min} menit`;
|
|
78800
|
+
const h2 = Math.floor(min / 60);
|
|
78801
|
+
const m = min % 60;
|
|
78802
|
+
return m ? `${h2} jam ${m} menit` : `${h2} jam`;
|
|
78803
|
+
}
|
|
78804
|
+
function fmtClock(t) {
|
|
78805
|
+
const d = new Date(t);
|
|
78806
|
+
return `${String(d.getHours()).padStart(2, "0")}.${String(d.getMinutes()).padStart(2, "0")}`;
|
|
78807
|
+
}
|
|
78808
|
+
var WAIT_HEADLINE_MIN = 30;
|
|
78809
|
+
function headline(o) {
|
|
78810
|
+
const facts = [];
|
|
78811
|
+
if (o.today && o.today.failed > 0) facts.push({ text: `${o.today.failed} sesi tumbang`, tone: "bad" });
|
|
78812
|
+
if (o.wait && o.wait.todayMin >= WAIT_HEADLINE_MIN)
|
|
78813
|
+
facts.push({ text: `agen menunggumu ${fmtMinutes(o.wait.todayMin)}`, tone: "bad" });
|
|
78814
|
+
const record2 = !!o.records?.landedRecordToday && !!o.today && o.today.landed > 0;
|
|
78815
|
+
if (record2) facts.push({ text: `rekor baru: ${o.today.landed} spec mendarat`, tone: "good" });
|
|
78816
|
+
const empty = o.bensin?.find((b) => b.verdict === "habis" && b.exhaustAt);
|
|
78817
|
+
if (empty) facts.push({ text: `limit ${empty.label} habis \xB1${fmtClock(Date.parse(empty.exhaustAt))}`, tone: "bad" });
|
|
78818
|
+
if (!record2 && o.today && o.today.landed > 0) facts.push({ text: `${o.today.landed} spec mendarat`, tone: "good" });
|
|
78819
|
+
const top = facts.slice(0, 3);
|
|
78820
|
+
if (top.length === 0) return { text: "Hari yang tenang.", parts: [{ text: "Hari yang tenang." }] };
|
|
78821
|
+
top[0] = { ...top[0], text: top[0].text.charAt(0).toUpperCase() + top[0].text.slice(1) };
|
|
78822
|
+
const parts = [];
|
|
78823
|
+
top.forEach((f, i) => {
|
|
78824
|
+
if (i > 0) parts.push({ text: i === top.length - 1 ? top.length === 2 ? " dan " : ", dan " : ", " });
|
|
78825
|
+
parts.push(f);
|
|
78826
|
+
});
|
|
78827
|
+
parts.push({ text: "." });
|
|
78828
|
+
return { text: parts.map((p3) => p3.text).join(""), parts };
|
|
78829
|
+
}
|
|
78830
|
+
function safe(label2, f) {
|
|
78831
|
+
try {
|
|
78832
|
+
return f();
|
|
78833
|
+
} catch (err) {
|
|
78834
|
+
console.warn(`overview: panel ${label2} gagal dihitung:`, err);
|
|
78835
|
+
return null;
|
|
78836
|
+
}
|
|
78837
|
+
}
|
|
78838
|
+
function computeOverview(now, src) {
|
|
78839
|
+
const { specs, sessions, invocations } = src;
|
|
78840
|
+
const t0 = startOfDay(now);
|
|
78841
|
+
const today = specs && sessions ? safe("today", () => dayStats(t0, specs, sessions)) : null;
|
|
78842
|
+
const yesterday = specs && sessions ? safe("yesterday", () => dayStats(addDays(t0, -1), specs, sessions)) : null;
|
|
78843
|
+
const wait = safe("wait", () => waitStats(now, src.waits, sessions));
|
|
78844
|
+
const bensin = safe("bensin", () => bensinStats(now, src.limits));
|
|
78845
|
+
const records = specs && sessions ? safe("records", () => recordStats(now, specs, sessions)) : null;
|
|
78846
|
+
return {
|
|
78847
|
+
generatedAt: new Date(now).toISOString(),
|
|
78848
|
+
recordingSince: new Date(src.recordingSince).toISOString(),
|
|
78849
|
+
today,
|
|
78850
|
+
yesterday,
|
|
78851
|
+
wait,
|
|
78852
|
+
bensin,
|
|
78853
|
+
records,
|
|
78854
|
+
river: specs ? safe("river", () => riverStats(now, specs)) : null,
|
|
78855
|
+
tokens: invocations ? safe("tokens", () => tokenStats(now, invocations, today?.landed ?? null)) : null,
|
|
78856
|
+
heatmap: sessions ? safe("heatmap", () => heatmapStats(now, sessions)) : null,
|
|
78857
|
+
headline: safe("headline", () => headline({ today, wait, records, bensin })) ?? { text: "Hari yang tenang.", parts: [{ text: "Hari yang tenang." }] }
|
|
78858
|
+
};
|
|
78859
|
+
}
|
|
78860
|
+
|
|
78861
|
+
// src/services/overview-recorder.ts
|
|
78862
|
+
init_pty();
|
|
78863
|
+
var MAX_EPISODES = 2e3;
|
|
78864
|
+
var KEEP_MS = 2 * HOUR_MS2;
|
|
78865
|
+
var MIN_GAP_MS = 25e3;
|
|
78866
|
+
var TICK_MS2 = 1e4;
|
|
78867
|
+
var LIMIT_EVERY_TICKS = 3;
|
|
78868
|
+
var since = Date.now();
|
|
78869
|
+
var open5 = /* @__PURE__ */ new Map();
|
|
78870
|
+
var closed = [];
|
|
78871
|
+
var series = /* @__PURE__ */ new Map();
|
|
78872
|
+
var timer4;
|
|
78873
|
+
var ticks = 0;
|
|
78874
|
+
function observeSessions(list2, now = Date.now()) {
|
|
78875
|
+
const waiting2 = /* @__PURE__ */ new Set();
|
|
78876
|
+
for (const s2 of list2) {
|
|
78877
|
+
if (s2.exited || !s2.decision || s2.deciding) continue;
|
|
78878
|
+
waiting2.add(s2.id);
|
|
78879
|
+
if (open5.has(s2.id)) continue;
|
|
78880
|
+
const at = s2.decisionAt ? Date.parse(s2.decisionAt) : Number.NaN;
|
|
78881
|
+
open5.set(s2.id, Number.isFinite(at) ? Math.min(at, now) : now);
|
|
78882
|
+
}
|
|
78883
|
+
for (const [id2, start] of open5) {
|
|
78884
|
+
if (waiting2.has(id2)) continue;
|
|
78885
|
+
open5.delete(id2);
|
|
78886
|
+
closed.push({ start, end: now });
|
|
78887
|
+
}
|
|
78888
|
+
if (closed.length > MAX_EPISODES) closed = closed.slice(-MAX_EPISODES);
|
|
78889
|
+
}
|
|
78890
|
+
function observeLimits(source, windows, now = Date.now()) {
|
|
78891
|
+
for (const w of windows) {
|
|
78892
|
+
const k = `${source}:${w.key}`;
|
|
78893
|
+
let s2 = series.get(k);
|
|
78894
|
+
if (!s2) {
|
|
78895
|
+
s2 = { source, key: w.key, label: w.label, resetsAt: w.resetsAt, samples: [] };
|
|
78896
|
+
series.set(k, s2);
|
|
78897
|
+
}
|
|
78898
|
+
s2.label = w.label;
|
|
78899
|
+
s2.resetsAt = w.resetsAt;
|
|
78900
|
+
const last2 = s2.samples.at(-1);
|
|
78901
|
+
if (last2 && w.usedPct < last2.pct) s2.samples = [];
|
|
78902
|
+
else if (last2 && now - last2.at < MIN_GAP_MS) continue;
|
|
78903
|
+
s2.samples.push({ at: now, pct: w.usedPct });
|
|
78904
|
+
s2.samples = s2.samples.filter((p3) => p3.at >= now - KEEP_MS);
|
|
78905
|
+
}
|
|
78906
|
+
}
|
|
78907
|
+
var waitSpans = () => [...closed, ...[...open5.values()].map((start) => ({ start, end: null }))];
|
|
78908
|
+
var limitSeries = () => [...series.values()].map((s2) => ({ ...s2, samples: [...s2.samples] }));
|
|
78909
|
+
var recordingSince = () => since;
|
|
78910
|
+
async function tick() {
|
|
78911
|
+
const list2 = await listSessionsAsync();
|
|
78912
|
+
observeSessions(list2.map((s2) => ({ ...s2, deciding: isDeciding(s2.id) })));
|
|
78913
|
+
if (ticks++ % LIMIT_EVERY_TICKS !== 0) return;
|
|
78914
|
+
const [claude, codex2] = await Promise.all([getLimits(), getCodexLimits()]);
|
|
78915
|
+
if (claude.status !== "unavailable") observeLimits("claude", claude.windows);
|
|
78916
|
+
if (codex2.status !== "unavailable") observeLimits("codex", codex2.windows);
|
|
78917
|
+
}
|
|
78918
|
+
function startOverviewRecorder() {
|
|
78919
|
+
if (timer4) return;
|
|
78920
|
+
void tick().catch((e) => console.warn("perekam overview:", e));
|
|
78921
|
+
timer4 = setInterval(() => {
|
|
78922
|
+
void tick().catch(() => {
|
|
78923
|
+
});
|
|
78924
|
+
}, TICK_MS2);
|
|
78925
|
+
timer4.unref();
|
|
78926
|
+
}
|
|
78927
|
+
|
|
78928
|
+
// src/services/overview-source.ts
|
|
78929
|
+
async function settle2(label2, p3) {
|
|
78930
|
+
try {
|
|
78931
|
+
return await p3;
|
|
78932
|
+
} catch (err) {
|
|
78933
|
+
console.warn(`overview: sumber ${label2} gagal:`, err);
|
|
78934
|
+
return null;
|
|
78935
|
+
}
|
|
78936
|
+
}
|
|
78937
|
+
var RECORD_WINDOW_DAYS2 = 90;
|
|
78938
|
+
async function getOverview(now = Date.now()) {
|
|
78939
|
+
const since2 = new Date(addDays(startOfDay(now), -(RECORD_WINDOW_DAYS2 - 1)));
|
|
78940
|
+
const sinceYesterday = new Date(addDays(startOfDay(now), -1));
|
|
78941
|
+
const [specs, sessions, invocations] = await Promise.all([
|
|
78942
|
+
settle2("spec", prisma.spec.findMany({
|
|
78943
|
+
where: { OR: [{ createdAt: { gte: since2 } }, { startedAt: { gte: since2 } }, { doneAt: { gte: since2 } }] },
|
|
78944
|
+
select: { createdAt: true, startedAt: true, doneAt: true }
|
|
78945
|
+
})),
|
|
78946
|
+
settle2("sessionHistory", prisma.sessionHistory.findMany({
|
|
78947
|
+
where: {
|
|
78948
|
+
kind: { in: [...AGENT_KINDS] },
|
|
78949
|
+
OR: [{ startedAt: { gte: since2 } }, { endedAt: null }, { endedAt: { gte: since2 } }]
|
|
78950
|
+
},
|
|
78951
|
+
select: { kind: true, startedAt: true, endedAt: true, exitCode: true, endedReason: true }
|
|
78952
|
+
})),
|
|
78953
|
+
settle2("agentInvocation", prisma.agentInvocation.findMany({
|
|
78954
|
+
where: { startedAt: { gte: sinceYesterday } },
|
|
78955
|
+
select: { agentName: true, startedAt: true, inputTokens: true, outputTokens: true, cachedTokens: true }
|
|
78956
|
+
}))
|
|
78957
|
+
]);
|
|
78958
|
+
return computeOverview(now, {
|
|
78959
|
+
specs,
|
|
78960
|
+
sessions,
|
|
78961
|
+
invocations,
|
|
78962
|
+
waits: waitSpans(),
|
|
78963
|
+
limits: limitSeries(),
|
|
78964
|
+
recordingSince: recordingSince()
|
|
78965
|
+
});
|
|
78966
|
+
}
|
|
78967
|
+
|
|
78519
78968
|
// src/services/events.ts
|
|
78520
78969
|
init_db();
|
|
78521
78970
|
init_config2();
|
|
@@ -79298,11 +79747,11 @@ var HOUSEKEEPING_MS = 6e4;
|
|
|
79298
79747
|
var busyPulse = false;
|
|
79299
79748
|
var lastPulseAt = 0;
|
|
79300
79749
|
var pulseEndedAt = 0;
|
|
79301
|
-
var
|
|
79750
|
+
var timer5;
|
|
79302
79751
|
function lastPulse() {
|
|
79303
79752
|
return lastPulseAt;
|
|
79304
79753
|
}
|
|
79305
|
-
async function
|
|
79754
|
+
async function tick2(now, deps = {}) {
|
|
79306
79755
|
let cfg;
|
|
79307
79756
|
try {
|
|
79308
79757
|
cfg = await (deps.cfg ?? getLead)();
|
|
@@ -79353,10 +79802,10 @@ async function tick(now, deps = {}) {
|
|
|
79353
79802
|
await Promise.all(jobs);
|
|
79354
79803
|
}
|
|
79355
79804
|
function startLead(deps = {}) {
|
|
79356
|
-
if (
|
|
79357
|
-
|
|
79358
|
-
|
|
79359
|
-
void
|
|
79805
|
+
if (timer5) return;
|
|
79806
|
+
timer5 = setInterval(() => void tick2(Date.now(), deps), HOUSEKEEPING_MS);
|
|
79807
|
+
timer5.unref();
|
|
79808
|
+
void tick2(Date.now(), deps);
|
|
79360
79809
|
}
|
|
79361
79810
|
|
|
79362
79811
|
// src/services/lead/views.ts
|
|
@@ -79367,7 +79816,7 @@ async function buildLeadStatus() {
|
|
|
79367
79816
|
select: { id: true, name: true },
|
|
79368
79817
|
orderBy: { id: "asc" }
|
|
79369
79818
|
});
|
|
79370
|
-
const
|
|
79819
|
+
const since2 = new Date(Date.now() - 24 * 60 * 60 * 1e3);
|
|
79371
79820
|
let live = [];
|
|
79372
79821
|
try {
|
|
79373
79822
|
live = (await listSessionsAsync()).filter((s2) => !s2.exited);
|
|
@@ -79383,7 +79832,7 @@ async function buildLeadStatus() {
|
|
|
79383
79832
|
name: p3.name,
|
|
79384
79833
|
optIn: true,
|
|
79385
79834
|
paused: !leadActive(cfg, p3.id),
|
|
79386
|
-
decisions24h: await prisma.leadDecision.count({ where: { projectId: p3.id, createdAt: { gte:
|
|
79835
|
+
decisions24h: await prisma.leadDecision.count({ where: { projectId: p3.id, createdAt: { gte: since2 } } }),
|
|
79387
79836
|
openSessions: live.filter((s2) => s2.projectId === p3.id).length
|
|
79388
79837
|
})));
|
|
79389
79838
|
const last2 = lastPulse();
|
|
@@ -79555,7 +80004,16 @@ var GROUPS = [
|
|
|
79555
80004
|
// bukan empat grup: keempatnya berubah dari peristiwa yang sama (item diajukan/diputuskan) dan
|
|
79556
80005
|
// dibaca satu komponen yang sama. `cookieOnly` TIDAK dipasang — muatannya jumlah agregat tanpa
|
|
79557
80006
|
// id/judul, dan agent token sudah boleh membaca daftar sumbernya lewat capability masing-masing.
|
|
79558
|
-
{ everyTicks: 5, last: "", build: async () => ({ t: "pending", counts: await pendingCounts() }) }
|
|
80007
|
+
{ everyTicks: 5, last: "", build: async () => ({ t: "pending", counts: await pendingCounts() }) },
|
|
80008
|
+
// ADR-0220 · grup GLOBAL ke-12 · agregat Overview "Koran Pagi". 30 dtk: angkanya harian, bukan
|
|
80009
|
+
// detik-an. `generatedAt` berubah tiap build, jadi kunci dedup mengosongkannya; frame lahir hanya
|
|
80010
|
+
// saat isi agregat benar-benar berubah. Tidak `cookieOnly`: angka agregat tanpa id/judul.
|
|
80011
|
+
{
|
|
80012
|
+
everyTicks: 30,
|
|
80013
|
+
last: "",
|
|
80014
|
+
build: async () => ({ t: "overview", overview: await getOverview() }),
|
|
80015
|
+
sig: (m) => JSON.stringify({ ...m.overview, generatedAt: null })
|
|
80016
|
+
}
|
|
79559
80017
|
];
|
|
79560
80018
|
function broadcast2(msg, s2, cookieOnly = false) {
|
|
79561
80019
|
for (const c of clients) {
|
|
@@ -79642,9 +80100,9 @@ function subscribeClient(c, subs) {
|
|
|
79642
80100
|
if (e.subscribers.size === 0) entries2.delete(key);
|
|
79643
80101
|
}
|
|
79644
80102
|
}
|
|
79645
|
-
var
|
|
80103
|
+
var tick3 = 0;
|
|
79646
80104
|
var busy = false;
|
|
79647
|
-
var
|
|
80105
|
+
var timer6;
|
|
79648
80106
|
async function runGroup(g) {
|
|
79649
80107
|
const t0 = profStart();
|
|
79650
80108
|
let msg;
|
|
@@ -79684,9 +80142,9 @@ async function runGroup(g) {
|
|
|
79684
80142
|
async function __tick() {
|
|
79685
80143
|
if (busy) return;
|
|
79686
80144
|
busy = true;
|
|
79687
|
-
|
|
80145
|
+
tick3++;
|
|
79688
80146
|
try {
|
|
79689
|
-
const results = await Promise.all(GROUPS.filter((g) =>
|
|
80147
|
+
const results = await Promise.all(GROUPS.filter((g) => tick3 % g.everyTicks === 0).map(runGroup));
|
|
79690
80148
|
for (const r of results) {
|
|
79691
80149
|
if (!r) continue;
|
|
79692
80150
|
r.g.last = r.json;
|
|
@@ -79704,19 +80162,19 @@ async function __tick() {
|
|
|
79704
80162
|
}
|
|
79705
80163
|
}
|
|
79706
80164
|
function startLoop() {
|
|
79707
|
-
if (
|
|
80165
|
+
if (timer6) return;
|
|
79708
80166
|
startLagMonitor();
|
|
79709
|
-
|
|
80167
|
+
timer6 = setInterval(() => {
|
|
79710
80168
|
void __tick();
|
|
79711
80169
|
}, tickMs());
|
|
79712
|
-
|
|
80170
|
+
timer6.unref();
|
|
79713
80171
|
}
|
|
79714
80172
|
function stopLoop() {
|
|
79715
|
-
if (
|
|
79716
|
-
clearInterval(
|
|
79717
|
-
|
|
80173
|
+
if (timer6) {
|
|
80174
|
+
clearInterval(timer6);
|
|
80175
|
+
timer6 = void 0;
|
|
79718
80176
|
}
|
|
79719
|
-
|
|
80177
|
+
tick3 = 0;
|
|
79720
80178
|
for (const g of GROUPS) {
|
|
79721
80179
|
g.last = "";
|
|
79722
80180
|
g.lastSig = void 0;
|
|
@@ -80361,6 +80819,11 @@ async function presence_default(app3) {
|
|
|
80361
80819
|
app3.get("/presence", async () => presenceView());
|
|
80362
80820
|
}
|
|
80363
80821
|
|
|
80822
|
+
// src/routes/overview.ts
|
|
80823
|
+
async function overview_default(app3) {
|
|
80824
|
+
app3.get("/overview", async () => getOverview());
|
|
80825
|
+
}
|
|
80826
|
+
|
|
80364
80827
|
// src/routes/session-results.ts
|
|
80365
80828
|
init_db();
|
|
80366
80829
|
var view6 = (r) => ({
|
|
@@ -80671,7 +81134,7 @@ async function intakeTicket(input) {
|
|
|
80671
81134
|
let ticketBytes = 0;
|
|
80672
81135
|
for (const f of input.files) {
|
|
80673
81136
|
try {
|
|
80674
|
-
const
|
|
81137
|
+
const safe3 = await processUpload({
|
|
80675
81138
|
buffer: f.buf,
|
|
80676
81139
|
clientName: f.name,
|
|
80677
81140
|
clientMime: f.mime,
|
|
@@ -80684,17 +81147,17 @@ async function intakeTicket(input) {
|
|
|
80684
81147
|
data: {
|
|
80685
81148
|
ticketId: ticket.id,
|
|
80686
81149
|
projectId: input.projectId,
|
|
80687
|
-
filename:
|
|
80688
|
-
mimeType:
|
|
80689
|
-
size:
|
|
80690
|
-
storageKey:
|
|
81150
|
+
filename: safe3.filename,
|
|
81151
|
+
mimeType: safe3.mimeType,
|
|
81152
|
+
size: safe3.size,
|
|
81153
|
+
storageKey: safe3.storageKey
|
|
80691
81154
|
}
|
|
80692
81155
|
});
|
|
80693
81156
|
} catch (error) {
|
|
80694
|
-
await deleteUpload(
|
|
81157
|
+
await deleteUpload(safe3.storageKey);
|
|
80695
81158
|
throw error;
|
|
80696
81159
|
}
|
|
80697
|
-
ticketBytes +=
|
|
81160
|
+
ticketBytes += safe3.size;
|
|
80698
81161
|
await notifySynced("ticketAttachment", att.id);
|
|
80699
81162
|
} catch (error) {
|
|
80700
81163
|
if (!(error instanceof UploadError)) throw error;
|
|
@@ -82714,7 +83177,7 @@ var QaImportError = class extends Error {
|
|
|
82714
83177
|
this.status = status2;
|
|
82715
83178
|
}
|
|
82716
83179
|
};
|
|
82717
|
-
var
|
|
83180
|
+
var safe2 = (s2) => s2.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
82718
83181
|
var MIME_BY_EXT = {
|
|
82719
83182
|
png: "image/png",
|
|
82720
83183
|
jpg: "image/jpeg",
|
|
@@ -82742,7 +83205,7 @@ async function exportReport(projectId, reportId) {
|
|
|
82742
83205
|
const owner = codeOf(a.ownerType, a.ownerId);
|
|
82743
83206
|
const n2 = (seq.get(owner) ?? 0) + 1;
|
|
82744
83207
|
seq.set(owner, n2);
|
|
82745
|
-
paths2[a.id] = `attachments/${owner}-${n2}-${
|
|
83208
|
+
paths2[a.id] = `attachments/${owner}-${n2}-${safe2(a.filename)}`;
|
|
82746
83209
|
files2.push({ name: paths2[a.id], data });
|
|
82747
83210
|
present.push(a);
|
|
82748
83211
|
}
|
|
@@ -84168,7 +84631,7 @@ async function testTelegramConnection(deps = {}) {
|
|
|
84168
84631
|
}
|
|
84169
84632
|
const timeoutMs = deps.timeoutMs ?? 1e4;
|
|
84170
84633
|
const controller = new AbortController();
|
|
84171
|
-
const
|
|
84634
|
+
const timer11 = setTimeout(() => controller.abort(), timeoutMs);
|
|
84172
84635
|
const send = deps.transport ?? ((url2, init) => fetch(url2, init));
|
|
84173
84636
|
const transport = (url2, init) => send(url2, { ...init, signal: controller.signal });
|
|
84174
84637
|
const client = new TelegramApiClient(botToken, transport);
|
|
@@ -84182,7 +84645,7 @@ async function testTelegramConnection(deps = {}) {
|
|
|
84182
84645
|
const message = controller.signal.aborted ? `Timeout ${timeoutMs} ms \u2014 Telegram tidak menjawab.` : raw;
|
|
84183
84646
|
return { ok: false, inbound, error: sanitizeTelegramOutput(message, [botToken]).slice(0, 500) };
|
|
84184
84647
|
} finally {
|
|
84185
|
-
clearTimeout(
|
|
84648
|
+
clearTimeout(timer11);
|
|
84186
84649
|
}
|
|
84187
84650
|
}
|
|
84188
84651
|
function resolveTestChatId() {
|
|
@@ -84416,7 +84879,7 @@ async function sendOnce(o, deps = {}) {
|
|
|
84416
84879
|
nowSec: o.nowSec ?? Math.floor(Date.now() / 1e3)
|
|
84417
84880
|
});
|
|
84418
84881
|
const ac = new AbortController();
|
|
84419
|
-
const
|
|
84882
|
+
const timer11 = setTimeout(() => ac.abort(), WEBHOOK_TIMEOUT_MS);
|
|
84420
84883
|
try {
|
|
84421
84884
|
const res = deps.fetcher ? await deps.fetcher(parsed.url.toString(), {
|
|
84422
84885
|
method: "POST",
|
|
@@ -84450,7 +84913,7 @@ async function sendOnce(o, deps = {}) {
|
|
|
84450
84913
|
const msg = e.name === "AbortError" ? `timeout ${WEBHOOK_TIMEOUT_MS} ms` : e.message;
|
|
84451
84914
|
return fail4(msg);
|
|
84452
84915
|
} finally {
|
|
84453
|
-
clearTimeout(
|
|
84916
|
+
clearTimeout(timer11);
|
|
84454
84917
|
}
|
|
84455
84918
|
}
|
|
84456
84919
|
|
|
@@ -84845,10 +85308,10 @@ function sanitizeClientText(text2) {
|
|
|
84845
85308
|
return text2.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "").slice(0, MAX_PESAN);
|
|
84846
85309
|
}
|
|
84847
85310
|
function wrapClientMessage(text2, nonce) {
|
|
84848
|
-
const
|
|
85311
|
+
const open6 = `<pesan-klien-${nonce}>`;
|
|
84849
85312
|
const close = `</pesan-klien-${nonce}>`;
|
|
84850
85313
|
const jinak = sanitizeClientText(text2).replaceAll("</pesan-klien", "<\u200B/pesan-klien").replaceAll("<pesan-klien", "<\u200Bpesan-klien");
|
|
84851
|
-
return `${
|
|
85314
|
+
return `${open6}
|
|
84852
85315
|
${jinak}
|
|
84853
85316
|
${close}`;
|
|
84854
85317
|
}
|
|
@@ -86736,6 +87199,7 @@ function buildApp({ requireAuth = true, agentDocFile, env = process.env } = {})
|
|
|
86736
87199
|
await api.register(bindings_default);
|
|
86737
87200
|
await api.register(sync_default);
|
|
86738
87201
|
await api.register(presence_default);
|
|
87202
|
+
await api.register(overview_default);
|
|
86739
87203
|
await api.register(session_results_default);
|
|
86740
87204
|
await api.register(session_history_default);
|
|
86741
87205
|
await api.register(session_events_default);
|
|
@@ -86781,17 +87245,17 @@ function createShutdown(o) {
|
|
|
86781
87245
|
let running;
|
|
86782
87246
|
return (sig) => running ??= (async () => {
|
|
86783
87247
|
log(`${sig} \u2014 menutup`);
|
|
86784
|
-
const
|
|
87248
|
+
const timer11 = setTimeout(() => {
|
|
86785
87249
|
log(`penutupan melewati ${o.timeoutMs ?? DEFAULT_TIMEOUT_MS} ms \u2014 keluar paksa`);
|
|
86786
87250
|
o.exit(1);
|
|
86787
87251
|
}, o.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
86788
|
-
|
|
87252
|
+
timer11.unref?.();
|
|
86789
87253
|
try {
|
|
86790
87254
|
await o.close();
|
|
86791
87255
|
} catch (e) {
|
|
86792
87256
|
log(`penutupan gagal: ${e.message}`);
|
|
86793
87257
|
}
|
|
86794
|
-
clearTimeout(
|
|
87258
|
+
clearTimeout(timer11);
|
|
86795
87259
|
o.exit(0);
|
|
86796
87260
|
})();
|
|
86797
87261
|
}
|
|
@@ -87056,7 +87520,7 @@ var prodEnd = {
|
|
|
87056
87520
|
reconcile: () => reconcile(reconcileProdDeps),
|
|
87057
87521
|
scanDecisions: () => scanDecisions()
|
|
87058
87522
|
};
|
|
87059
|
-
async function
|
|
87523
|
+
async function tick4(now, deps, end2 = prodEnd) {
|
|
87060
87524
|
const cfg = await getScheduler();
|
|
87061
87525
|
if (!cfg.enabled) return;
|
|
87062
87526
|
for (const src of listSources()) {
|
|
@@ -87121,13 +87585,13 @@ var prodDeps = {
|
|
|
87121
87585
|
launchCron: async (cron) => (await startCronSession(cron)).id
|
|
87122
87586
|
})
|
|
87123
87587
|
};
|
|
87124
|
-
var
|
|
87125
|
-
var
|
|
87588
|
+
var TICK_MS3 = 1e4;
|
|
87589
|
+
var timer7;
|
|
87126
87590
|
function startScheduler(deps = prodDeps) {
|
|
87127
|
-
if (
|
|
87128
|
-
|
|
87129
|
-
|
|
87130
|
-
void
|
|
87591
|
+
if (timer7) return;
|
|
87592
|
+
timer7 = setInterval(() => void tick4(Date.now(), deps), TICK_MS3);
|
|
87593
|
+
timer7.unref();
|
|
87594
|
+
void tick4(Date.now(), deps);
|
|
87131
87595
|
}
|
|
87132
87596
|
|
|
87133
87597
|
// src/services/scheduler/sources/backlog.ts
|
|
@@ -87299,7 +87763,7 @@ async function installWebhooks() {
|
|
|
87299
87763
|
// src/services/webhooks/engine.ts
|
|
87300
87764
|
init_src();
|
|
87301
87765
|
init_db();
|
|
87302
|
-
var
|
|
87766
|
+
var TICK_MS4 = 2e3;
|
|
87303
87767
|
var MAX_IN_FLIGHT = 4;
|
|
87304
87768
|
var PRUNE_EVERY_TICKS = 30;
|
|
87305
87769
|
function backoffAt(from, attempt) {
|
|
@@ -87442,15 +87906,15 @@ async function pruneHistory() {
|
|
|
87442
87906
|
}
|
|
87443
87907
|
return removed;
|
|
87444
87908
|
}
|
|
87445
|
-
var
|
|
87446
|
-
var
|
|
87909
|
+
var timer8;
|
|
87910
|
+
var ticks2 = 0;
|
|
87447
87911
|
var busy2 = false;
|
|
87448
|
-
async function
|
|
87912
|
+
async function tick5() {
|
|
87449
87913
|
if (busy2) return;
|
|
87450
87914
|
busy2 = true;
|
|
87451
87915
|
try {
|
|
87452
87916
|
await deliverDue(/* @__PURE__ */ new Date());
|
|
87453
|
-
if (++
|
|
87917
|
+
if (++ticks2 % PRUNE_EVERY_TICKS === 0) await pruneHistory();
|
|
87454
87918
|
} catch (e) {
|
|
87455
87919
|
console.error("webhook engine:", e);
|
|
87456
87920
|
} finally {
|
|
@@ -87458,9 +87922,9 @@ async function tick4() {
|
|
|
87458
87922
|
}
|
|
87459
87923
|
}
|
|
87460
87924
|
function startWebhookEngine() {
|
|
87461
|
-
if (
|
|
87462
|
-
|
|
87463
|
-
|
|
87925
|
+
if (timer8) return;
|
|
87926
|
+
timer8 = setInterval(() => void tick5(), TICK_MS4);
|
|
87927
|
+
timer8.unref();
|
|
87464
87928
|
void resetStuckDeliveries().then((n2) => {
|
|
87465
87929
|
if (n2) console.log(`webhook: ${n2} pengiriman tertinggal dikembalikan ke antrean`);
|
|
87466
87930
|
}).catch((e) => console.error("webhook reset:", e));
|
|
@@ -87475,7 +87939,7 @@ init_local_binding();
|
|
|
87475
87939
|
init_notifications2();
|
|
87476
87940
|
var AUTO_MERGE_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
87477
87941
|
var AUTO_MERGE_GRACE_MS = 15 * 60 * 1e3;
|
|
87478
|
-
var
|
|
87942
|
+
var TICK_MS5 = 6e4;
|
|
87479
87943
|
var exec11 = promisify17(execFile20);
|
|
87480
87944
|
var GIT8 = { timeout: 3e4, maxBuffer: 1 << 24, encoding: "utf8" };
|
|
87481
87945
|
var gitOut = async (cwd, args) => {
|
|
@@ -87498,9 +87962,9 @@ var prodAutoMergeDeps = {
|
|
|
87498
87962
|
deleteBranch: deleteMergedBranch
|
|
87499
87963
|
};
|
|
87500
87964
|
async function candidates(now) {
|
|
87501
|
-
const
|
|
87965
|
+
const since2 = new Date(now.getTime() - AUTO_MERGE_WINDOW_MS);
|
|
87502
87966
|
const done = await prisma.notification.findMany({
|
|
87503
|
-
where: { type: "done", createdAt: { gte:
|
|
87967
|
+
where: { type: "done", createdAt: { gte: since2 }, specId: { not: null } },
|
|
87504
87968
|
select: { specId: true, projectId: true, createdAt: true }
|
|
87505
87969
|
});
|
|
87506
87970
|
if (!done.length) return [];
|
|
@@ -87564,9 +88028,9 @@ async function settleOne(c, deps, now) {
|
|
|
87564
88028
|
}
|
|
87565
88029
|
return report(`Auto-merge ${spec.id} \u2192 ${target3} GAGAL: ${res.error}`);
|
|
87566
88030
|
}
|
|
87567
|
-
var
|
|
88031
|
+
var timer9;
|
|
87568
88032
|
var busy3 = false;
|
|
87569
|
-
async function
|
|
88033
|
+
async function tick6() {
|
|
87570
88034
|
if (busy3) return;
|
|
87571
88035
|
busy3 = true;
|
|
87572
88036
|
try {
|
|
@@ -87578,9 +88042,9 @@ async function tick5() {
|
|
|
87578
88042
|
}
|
|
87579
88043
|
}
|
|
87580
88044
|
function startAutoMerge() {
|
|
87581
|
-
if (
|
|
87582
|
-
|
|
87583
|
-
|
|
88045
|
+
if (timer9) return;
|
|
88046
|
+
timer9 = setInterval(() => void tick6(), TICK_MS5);
|
|
88047
|
+
timer9.unref();
|
|
87584
88048
|
}
|
|
87585
88049
|
|
|
87586
88050
|
// src/server.ts
|
|
@@ -87879,9 +88343,9 @@ async function pruneSyncFeed(now, dryRun = false) {
|
|
|
87879
88343
|
}
|
|
87880
88344
|
return pruned;
|
|
87881
88345
|
}
|
|
87882
|
-
var
|
|
88346
|
+
var timer10;
|
|
87883
88347
|
function startRetentionSweep() {
|
|
87884
|
-
if (
|
|
88348
|
+
if (timer10) return;
|
|
87885
88349
|
const sweep = () => {
|
|
87886
88350
|
const holds = new Set((process.env.HANOMAN_RETENTION_HOLDS ?? "").split(",").map((v) => v.trim()).filter(Boolean));
|
|
87887
88351
|
void runRetention({ holds }).then((report) => {
|
|
@@ -87891,8 +88355,8 @@ function startRetentionSweep() {
|
|
|
87891
88355
|
}).catch((error) => console.error("retention sweep:", error));
|
|
87892
88356
|
};
|
|
87893
88357
|
sweep();
|
|
87894
|
-
|
|
87895
|
-
|
|
88358
|
+
timer10 = setInterval(sweep, 24 * 60 * 6e4);
|
|
88359
|
+
timer10.unref?.();
|
|
87896
88360
|
}
|
|
87897
88361
|
|
|
87898
88362
|
// src/server.ts
|
|
@@ -87992,6 +88456,7 @@ bootstrapReady.then(async () => {
|
|
|
87992
88456
|
startAutoMerge();
|
|
87993
88457
|
startWorktreeReaper();
|
|
87994
88458
|
startRetentionSweep();
|
|
88459
|
+
startOverviewRecorder();
|
|
87995
88460
|
}).catch((err) => {
|
|
87996
88461
|
console.error("listen gagal:", err);
|
|
87997
88462
|
process.exit(1);
|