hanoman 0.1.18 → 0.1.19
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 +58 -8
- package/dist/server.js +310 -60
- package/docs/agent-integration.md +5 -2
- package/package.json +1 -1
- package/prisma/schema.prisma +1 -1
- package/web/assets/{index-BrlNu5Qe.js → index-BJTj--gG.js} +1546 -1546
- package/web/index.html +1 -1
package/dist/server.js
CHANGED
|
@@ -4341,7 +4341,7 @@ function cmpVersion(a, b) {
|
|
|
4341
4341
|
}
|
|
4342
4342
|
return 0;
|
|
4343
4343
|
}
|
|
4344
|
-
var zProject, zBriefPayload, zQaPayload, zGoalPayload, zSpecBlocker, zSpec, NOTIFY_SOUNDS, MODELS, EFFORTS, E_5_6, E_LUNA, E_BASE, CODEX_MODELS, RETIRED_CODEX_MODELS, zCodex, CODEX_DEFAULTS, zSourceCommon, zScheduler, SCHEDULER_DEFAULTS, zGoal, GOAL_DEFAULTS, zConflict, CONFLICT_DEFAULTS, zLeadEngine, zLead, LEAD_DEFAULTS, zSetting, zNotification, zDocFile, zDeviceTokenView;
|
|
4344
|
+
var zProject, zBriefPayload, zQaPayload, zGoalPayload, zSpecBlocker, zSpec, NOTIFY_SOUNDS, MODELS, EFFORTS, E_5_6, E_LUNA, E_BASE, CODEX_MODELS, RETIRED_CODEX_MODELS, zCodex, CODEX_DEFAULTS, zSourceCommon, zScheduler, SCHEDULER_DEFAULTS, zGoal, GOAL_DEFAULTS, zConflict, CONFLICT_DEFAULTS, CHANGELOG_ENGINE_DEFAULTS, zLeadEngine, zLead, LEAD_DEFAULTS, zSetting, zNotification, zDocFile, zDeviceTokenView;
|
|
4345
4345
|
var init_entities = __esm({
|
|
4346
4346
|
"../shared/src/entities.ts"() {
|
|
4347
4347
|
"use strict";
|
|
@@ -4491,6 +4491,7 @@ var init_entities = __esm({
|
|
|
4491
4491
|
effort: external_exports.string().default("xhigh")
|
|
4492
4492
|
});
|
|
4493
4493
|
CONFLICT_DEFAULTS = zConflict.parse({});
|
|
4494
|
+
CHANGELOG_ENGINE_DEFAULTS = zAgentEngine.parse({});
|
|
4494
4495
|
zLeadEngine = zAgentEngine;
|
|
4495
4496
|
zLead = external_exports.object({
|
|
4496
4497
|
enabled: external_exports.boolean().default(false),
|
|
@@ -4562,8 +4563,10 @@ var init_entities = __esm({
|
|
|
4562
4563
|
// SPEC-383 · ADR-0081 · default sesi konflik rebase/merge
|
|
4563
4564
|
lead: zLead.default(LEAD_DEFAULTS),
|
|
4564
4565
|
// SPEC-409 · ADR-0091 · hanoman-lead (default mati)
|
|
4565
|
-
telegram: zTelegramSettings.default(TELEGRAM_DEFAULTS)
|
|
4566
|
+
telegram: zTelegramSettings.default(TELEGRAM_DEFAULTS),
|
|
4566
4567
|
// SPEC-476 · ADR-0096 · gateway Telegram (default mati)
|
|
4568
|
+
changelog: zAgentEngine.default(CHANGELOG_ENGINE_DEFAULTS)
|
|
4569
|
+
// SPEC-518 · agen pembuat changelog (opt-in, mati)
|
|
4567
4570
|
});
|
|
4568
4571
|
zNotification = external_exports.object({
|
|
4569
4572
|
id: external_exports.string(),
|
|
@@ -5092,11 +5095,46 @@ var init_lead = __esm({
|
|
|
5092
5095
|
}
|
|
5093
5096
|
});
|
|
5094
5097
|
|
|
5098
|
+
// ../shared/src/prd-status.ts
|
|
5099
|
+
function prdBranchFor(prdPath) {
|
|
5100
|
+
if (!prdPath.startsWith(PRD_DIR) || !prdPath.endsWith(".md")) return null;
|
|
5101
|
+
const slug = prdPath.slice(PRD_DIR.length, -3);
|
|
5102
|
+
return slug ? `prd/${slug}` : null;
|
|
5103
|
+
}
|
|
5104
|
+
function payloadText(payload) {
|
|
5105
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return "";
|
|
5106
|
+
const p = payload;
|
|
5107
|
+
return [p.context, p.goal].filter((v) => typeof v === "string").join("\n");
|
|
5108
|
+
}
|
|
5109
|
+
function specDerivesFromPrd(spec, prdPath) {
|
|
5110
|
+
const branch = prdBranchFor(prdPath);
|
|
5111
|
+
if (!branch) return false;
|
|
5112
|
+
if (spec.branchFrom === branch) return true;
|
|
5113
|
+
return payloadText(spec.payload).includes(prdPath);
|
|
5114
|
+
}
|
|
5115
|
+
function prdStatusOf(prdPath, specs) {
|
|
5116
|
+
const derived = specs.filter((s2) => specDerivesFromPrd(s2, prdPath));
|
|
5117
|
+
const specCount = derived.length;
|
|
5118
|
+
const doneCount = derived.filter((s2) => s2.stage === "done").length;
|
|
5119
|
+
const status = specCount === 0 ? "draft" : doneCount === specCount ? "terwujud" : "dieskalasi";
|
|
5120
|
+
return { status, specCount, doneCount };
|
|
5121
|
+
}
|
|
5122
|
+
var PRD_STATUSES, zPrdStatus, PRD_DIR;
|
|
5123
|
+
var init_prd_status = __esm({
|
|
5124
|
+
"../shared/src/prd-status.ts"() {
|
|
5125
|
+
"use strict";
|
|
5126
|
+
init_zod();
|
|
5127
|
+
PRD_STATUSES = ["draft", "dieskalasi", "terwujud"];
|
|
5128
|
+
zPrdStatus = external_exports.enum(PRD_STATUSES);
|
|
5129
|
+
PRD_DIR = "docs/prd/";
|
|
5130
|
+
}
|
|
5131
|
+
});
|
|
5132
|
+
|
|
5095
5133
|
// ../shared/src/dto.ts
|
|
5096
5134
|
function flowForSource(source) {
|
|
5097
5135
|
return source === "qa" ? "qa" : source === "audit" ? "audit" : source === "goal" ? "goal" : "feature";
|
|
5098
5136
|
}
|
|
5099
|
-
var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, zLogin, zSignup, zChangePassword, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, UPDATE_RESTART_EXIT, zUpdateApplyBody, zTicketView, zTicketAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus;
|
|
5137
|
+
var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, zLogin, zSignup, zChangePassword, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, UPDATE_RESTART_EXIT, zUpdateApplyBody, zTicketView, zTicketAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus;
|
|
5100
5138
|
var init_dto = __esm({
|
|
5101
5139
|
"../shared/src/dto.ts"() {
|
|
5102
5140
|
"use strict";
|
|
@@ -5104,6 +5142,7 @@ var init_dto = __esm({
|
|
|
5104
5142
|
init_entities();
|
|
5105
5143
|
init_lead();
|
|
5106
5144
|
init_auto_merge();
|
|
5145
|
+
init_prd_status();
|
|
5107
5146
|
init_enums();
|
|
5108
5147
|
zIssueDeviceToken = external_exports.object({ name: external_exports.string().min(1) });
|
|
5109
5148
|
zSessionResult = external_exports.object({
|
|
@@ -5248,12 +5287,21 @@ var init_dto = __esm({
|
|
|
5248
5287
|
decision: external_exports.boolean(),
|
|
5249
5288
|
exited: external_exports.boolean()
|
|
5250
5289
|
});
|
|
5290
|
+
zSchedulerQueueCounts = external_exports.object({
|
|
5291
|
+
queued: external_exports.number().int(),
|
|
5292
|
+
launched: external_exports.number().int(),
|
|
5293
|
+
done: external_exports.number().int(),
|
|
5294
|
+
failed: external_exports.number().int(),
|
|
5295
|
+
// SPEC-522 · `canceled` adalah nilai kelima `status`; hitungannya ikut supaya panel bisa
|
|
5296
|
+
// menampilkan seksi "Dibatalkan" tanpa memuat daftarnya.
|
|
5297
|
+
canceled: external_exports.number().int()
|
|
5298
|
+
});
|
|
5251
5299
|
zSchedulerState = external_exports.object({
|
|
5252
5300
|
config: zScheduler,
|
|
5253
5301
|
cap: external_exports.number(),
|
|
5254
5302
|
liveCount: external_exports.number(),
|
|
5255
5303
|
sources: external_exports.array(zSchedulerSourceView),
|
|
5256
|
-
|
|
5304
|
+
queueCounts: zSchedulerQueueCounts,
|
|
5257
5305
|
sessions: external_exports.array(zSchedulerSessionView)
|
|
5258
5306
|
});
|
|
5259
5307
|
zLeadDecisionView = external_exports.object({
|
|
@@ -5361,7 +5409,13 @@ var init_dto = __esm({
|
|
|
5361
5409
|
title: external_exports.string(),
|
|
5362
5410
|
live: external_exports.boolean(),
|
|
5363
5411
|
projectId: external_exports.string(),
|
|
5364
|
-
projectName: external_exports.string()
|
|
5412
|
+
projectName: external_exports.string(),
|
|
5413
|
+
// SPEC-520 · status TURUNAN dari backlog yang lahir dari PRD ini (ADR-0018/0019) — bukan
|
|
5414
|
+
// kolom, bukan prosa di dalam dokumennya. `live` di atas menjawab pertanyaan LAIN
|
|
5415
|
+
// (freshest-wins dari worktree sesi prd hidup) dan sengaja tetap ortogonal.
|
|
5416
|
+
status: zPrdStatus,
|
|
5417
|
+
specCount: external_exports.number().int().nonnegative(),
|
|
5418
|
+
doneCount: external_exports.number().int().nonnegative()
|
|
5365
5419
|
});
|
|
5366
5420
|
zBreakdownItem = external_exports.object({
|
|
5367
5421
|
title: external_exports.string().min(1),
|
|
@@ -5406,9 +5460,10 @@ var init_dto = __esm({
|
|
|
5406
5460
|
// pipeline claude). DIDAHULUKAN: z.object non-strict membuang key asing, jadi bila varian
|
|
5407
5461
|
// longgar {project,flow?} lebih dulu, {project,shell:true} akan lolos sbg plain (shell dibuang).
|
|
5408
5462
|
external_exports.object({ project: external_exports.string(), shell: external_exports.literal(true) }),
|
|
5409
|
-
//
|
|
5410
|
-
//
|
|
5411
|
-
|
|
5463
|
+
// SPEC-166 · "reverse" = sesi project-level di worktree-nya sendiri, menyusun Source of Truth
|
|
5464
|
+
// dari kode. TANPA override runtime: sesi project-level mengikuti Setting.agent (ADR-0074).
|
|
5465
|
+
// Terminal biasa (tanpa flow) kini punya variannya SENDIRI di bawah — lihat SPEC-517.
|
|
5466
|
+
external_exports.object({ project: external_exports.string(), flow: external_exports.literal("reverse") }),
|
|
5412
5467
|
// SPEC-210 · sesi prd project-level di worktree sendiri; menghasilkan dokumen PRD dari brief.
|
|
5413
5468
|
// SPEC-340 · ADR-0076 · eskalasi audit → PRD: branchFrom = branch audit (worktree lahir dari sana,
|
|
5414
5469
|
// resolveCommit + fallback origin/<rev>), fromAudit = id spec audit (isi dokumennya disematkan ke
|
|
@@ -5425,6 +5480,18 @@ var init_dto = __esm({
|
|
|
5425
5480
|
// SPEC-222 · scaffold: sesi project-level from-scratch, menyusun SoT dari ide. Tanpa brief
|
|
5426
5481
|
// (diseed dari Project.desc), tanpa Spec — cermin reverse.
|
|
5427
5482
|
external_exports.object({ project: external_exports.string(), flow: external_exports.literal("scaffold") }),
|
|
5483
|
+
// SPEC-517 · terminal agen biasa: agen (claude|codex) + model + effort boleh dipilih PER SESI,
|
|
5484
|
+
// seperti picker Start backlog (ADR-0061/0074). Kosong → default global (Setting).
|
|
5485
|
+
// `flow: z.undefined()` BUKAN hiasan: varian ini permisif dan diletakkan SESUDAH semua varian
|
|
5486
|
+
// ber-flow, jadi tanpa gerbang itu body flow yang CACAT ({project, flow:"prd"} tanpa brief)
|
|
5487
|
+
// akan lolos ke sini dan melahirkan terminal biasa secara senyap alih-alih dijawab 400.
|
|
5488
|
+
external_exports.object({
|
|
5489
|
+
project: external_exports.string(),
|
|
5490
|
+
flow: external_exports.undefined(),
|
|
5491
|
+
agent: zAgent.optional(),
|
|
5492
|
+
model: external_exports.string().optional(),
|
|
5493
|
+
effort: external_exports.string().optional()
|
|
5494
|
+
}),
|
|
5428
5495
|
// SPEC-252 · ADR-0061 — model & effort per SESI: override opsional saat Start; kosong → global.
|
|
5429
5496
|
// SPEC-332 · ADR-0073 — mode goal per SESI: `goal` undefined → ikut Setting.goal.enabled,
|
|
5430
5497
|
// false → mati walau global nyala; `goalCondition` kosong → template global → default bawaan.
|
|
@@ -5674,6 +5741,12 @@ var init_api = __esm({
|
|
|
5674
5741
|
// SPEC-299 · panel scheduler (daun #6) — konsumen read-only fondasi SPEC-294/ADR-0072.
|
|
5675
5742
|
schedulerConfig: `${API}/scheduler/config`,
|
|
5676
5743
|
schedulerState: `${API}/scheduler/state`,
|
|
5744
|
+
// SPEC-522 · ADR-0106 · batalkan / antre lagi SATU baris antrean. Di bawah prefix `scheduler`
|
|
5745
|
+
// supaya capability-nya turunan peta yang sudah ada (settings, MENURUT METHOD) — tanpa baris peta baru.
|
|
5746
|
+
schedulerQueueCancel: (id) => `${API}/scheduler/queue/${encodeURIComponent(id)}/cancel`,
|
|
5747
|
+
schedulerQueueRequeue: (id) => `${API}/scheduler/queue/${encodeURIComponent(id)}/requeue`,
|
|
5748
|
+
// SPEC-523 · antrean sebagai daftar berhalaman (page/limit + status), lepas dari `state`.
|
|
5749
|
+
schedulerQueue: `${API}/scheduler/queue`,
|
|
5677
5750
|
// SPEC-409 · ADR-0091 · hanoman-lead. Semua HTTP (polling) — tak ada kanal WS baru (AC-26).
|
|
5678
5751
|
leadConfig: `${API}/lead/config`,
|
|
5679
5752
|
leadStatus: `${API}/lead/status`,
|
|
@@ -7131,6 +7204,11 @@ function defaultRange(today) {
|
|
|
7131
7204
|
const from = new Date(today.getFullYear(), today.getMonth(), today.getDate() - (DEFAULT_RANGE_DAYS - 1));
|
|
7132
7205
|
return { from: dayString(from), to: dayString(today) };
|
|
7133
7206
|
}
|
|
7207
|
+
function changelogMatches(row, q) {
|
|
7208
|
+
const needle = (q ?? "").trim().toLowerCase();
|
|
7209
|
+
if (!needle) return true;
|
|
7210
|
+
return row.title.toLowerCase().includes(needle) || row.body.toLowerCase().includes(needle) || row.mode.toLowerCase().includes(needle);
|
|
7211
|
+
}
|
|
7134
7212
|
var CHANGELOG_MODES, zChangelogMode, zDay, zChangelogRequest, zChangelogView, DEFAULT_RANGE_DAYS;
|
|
7135
7213
|
var init_changelog = __esm({
|
|
7136
7214
|
"../shared/src/changelog.ts"() {
|
|
@@ -7183,6 +7261,7 @@ var init_src = __esm({
|
|
|
7183
7261
|
init_api();
|
|
7184
7262
|
init_coverage();
|
|
7185
7263
|
init_ticket_status();
|
|
7264
|
+
init_prd_status();
|
|
7186
7265
|
init_session_kind();
|
|
7187
7266
|
init_config();
|
|
7188
7267
|
init_config_registry();
|
|
@@ -9724,6 +9803,13 @@ async function conflictSessionDefaults() {
|
|
|
9724
9803
|
if (!c.enabled) return agentDefaultsOf(s2);
|
|
9725
9804
|
return c.agent === "codex" ? { agent: "codex", model: c.model, effort: coerceCodexEffort(c.model, c.effort) } : { agent: "claude", model: c.model, effort: c.effort };
|
|
9726
9805
|
}
|
|
9806
|
+
async function terminalAgentDefaults(o) {
|
|
9807
|
+
const s2 = await getSetting();
|
|
9808
|
+
const base2 = o.agent ? o.agent === "codex" ? { agent: "codex", model: s2.codex.model, effort: s2.codex.effort } : { agent: "claude", model: s2.model, effort: s2.effort } : agentDefaultsOf(s2);
|
|
9809
|
+
const model = o.model ?? base2.model;
|
|
9810
|
+
const effort = o.effort ?? base2.effort;
|
|
9811
|
+
return base2.agent === "codex" ? { agent: "codex", model, effort: coerceCodexEffort(model, effort) } : { agent: "claude", model, effort };
|
|
9812
|
+
}
|
|
9727
9813
|
var STEP, DEFAULT_SETTING, RETIRED_MODELS;
|
|
9728
9814
|
var init_settings2 = __esm({
|
|
9729
9815
|
"src/services/settings.ts"() {
|
|
@@ -9756,8 +9842,10 @@ var init_settings2 = __esm({
|
|
|
9756
9842
|
// SPEC-383 · ADR-0081 · default sesi konflik (opt-in, mati)
|
|
9757
9843
|
lead: LEAD_DEFAULTS,
|
|
9758
9844
|
// SPEC-409 · ADR-0091 · hanoman-lead (master switch mati)
|
|
9759
|
-
telegram: TELEGRAM_DEFAULTS
|
|
9845
|
+
telegram: TELEGRAM_DEFAULTS,
|
|
9760
9846
|
// SPEC-476 · ADR-0096 · gateway Telegram opt-in
|
|
9847
|
+
changelog: CHANGELOG_ENGINE_DEFAULTS
|
|
9848
|
+
// SPEC-518 · agen pembuat changelog (opt-in, mati)
|
|
9761
9849
|
};
|
|
9762
9850
|
RETIRED_MODELS = { "claude-opus-4-8": "claude-opus-5" };
|
|
9763
9851
|
}
|
|
@@ -21923,11 +22011,19 @@ async function scanDecisions(read = liveDecisions) {
|
|
|
21923
22011
|
});
|
|
21924
22012
|
}
|
|
21925
22013
|
}
|
|
21926
|
-
|
|
22014
|
+
var DEFAULT_FEED_TAKE = 50;
|
|
22015
|
+
async function notificationsFeed(p = {}) {
|
|
21927
22016
|
await scanDecisions();
|
|
21928
|
-
const
|
|
22017
|
+
const pageSize = p.limit ? Math.max(1, Math.floor(+p.limit) || 1) : DEFAULT_FEED_TAKE;
|
|
22018
|
+
const page = p.page ? Math.max(1, Math.floor(+p.page) || 1) : 1;
|
|
22019
|
+
const total = await prisma.notification.count();
|
|
22020
|
+
const items = await prisma.notification.findMany({
|
|
22021
|
+
orderBy: { createdAt: "desc" },
|
|
22022
|
+
skip: (page - 1) * pageSize,
|
|
22023
|
+
take: pageSize
|
|
22024
|
+
});
|
|
21929
22025
|
const unread = await prisma.notification.count({ where: { readAt: null } });
|
|
21930
|
-
return { items, unread };
|
|
22026
|
+
return { items, unread, total, page, pageSize };
|
|
21931
22027
|
}
|
|
21932
22028
|
|
|
21933
22029
|
// src/services/spec-head.ts
|
|
@@ -22249,7 +22345,7 @@ ${item.context}` : item.context;
|
|
|
22249
22345
|
// src/routes/notifications.ts
|
|
22250
22346
|
init_db();
|
|
22251
22347
|
async function notifications_default(app2) {
|
|
22252
|
-
app2.get("/notifications", async () => notificationsFeed());
|
|
22348
|
+
app2.get("/notifications", async (req) => notificationsFeed(req.query));
|
|
22253
22349
|
app2.post("/notifications/read", async (_req, reply) => {
|
|
22254
22350
|
await prisma.notification.updateMany({ where: { readAt: null }, data: { readAt: /* @__PURE__ */ new Date() } });
|
|
22255
22351
|
return reply.code(204).send();
|
|
@@ -22288,11 +22384,12 @@ async function settings_default(app2) {
|
|
|
22288
22384
|
init_src();
|
|
22289
22385
|
|
|
22290
22386
|
// src/services/project-prds.ts
|
|
22387
|
+
init_src();
|
|
22291
22388
|
init_db();
|
|
22292
22389
|
init_pty();
|
|
22293
|
-
var
|
|
22294
|
-
var isPrd = (rel) => rel.startsWith(
|
|
22295
|
-
var slugOf = (rel) => rel.slice(
|
|
22390
|
+
var PRD_DIR2 = "docs/prd/";
|
|
22391
|
+
var isPrd = (rel) => rel.startsWith(PRD_DIR2) && rel.endsWith(".md") && !rel.endsWith(".breakdown.md");
|
|
22392
|
+
var slugOf = (rel) => rel.slice(PRD_DIR2.length, -3);
|
|
22296
22393
|
var titleOf = (content, slug) => {
|
|
22297
22394
|
const m = content?.match(/^#\s+(.+)$/m);
|
|
22298
22395
|
return m ? m[1].trim() : slug;
|
|
@@ -22302,13 +22399,26 @@ async function resolveDir2(projectId, sessions = listSessions()) {
|
|
|
22302
22399
|
if (live) return { dir: live.cwd, live: true };
|
|
22303
22400
|
return { dir: await resolveRepoDir(projectId), live: false };
|
|
22304
22401
|
}
|
|
22305
|
-
async function
|
|
22402
|
+
async function loadTraces(projectIds) {
|
|
22403
|
+
const map = new Map(projectIds.map((id) => [id, []]));
|
|
22404
|
+
if (!projectIds.length) return map;
|
|
22405
|
+
const rows = await prisma.spec.findMany({
|
|
22406
|
+
where: { projectId: { in: projectIds } },
|
|
22407
|
+
select: { projectId: true, stage: true, payload: true, branchFrom: true }
|
|
22408
|
+
});
|
|
22409
|
+
for (const r of rows)
|
|
22410
|
+
map.get(r.projectId)?.push({ stage: r.stage, payload: r.payload, branchFrom: r.branchFrom });
|
|
22411
|
+
return map;
|
|
22412
|
+
}
|
|
22413
|
+
async function listPrds(projectId, sessions = listSessions(), traces) {
|
|
22306
22414
|
const { dir: dir2, live } = await resolveDir2(projectId, sessions);
|
|
22307
22415
|
if (!dir2) return [];
|
|
22308
22416
|
const project = await prisma.project.findUnique({ where: { id: projectId }, select: { name: true } });
|
|
22309
22417
|
const projectName = project?.name ?? projectId;
|
|
22418
|
+
const specs = traces ?? (await loadTraces([projectId])).get(projectId) ?? [];
|
|
22310
22419
|
return (await listRepoDocs(dir2)).filter(isPrd).map((rel) => {
|
|
22311
22420
|
const slug = slugOf(rel);
|
|
22421
|
+
const { status, specCount, doneCount } = prdStatusOf(rel, specs);
|
|
22312
22422
|
return {
|
|
22313
22423
|
slug,
|
|
22314
22424
|
name: rel.slice(rel.lastIndexOf("/") + 1),
|
|
@@ -22316,7 +22426,10 @@ async function listPrds(projectId, sessions = listSessions()) {
|
|
|
22316
22426
|
title: titleOf(readDocFile(dir2, rel), slug),
|
|
22317
22427
|
live,
|
|
22318
22428
|
projectId,
|
|
22319
|
-
projectName
|
|
22429
|
+
projectName,
|
|
22430
|
+
status,
|
|
22431
|
+
specCount,
|
|
22432
|
+
doneCount
|
|
22320
22433
|
};
|
|
22321
22434
|
}).sort((a, b) => a.slug.localeCompare(b.slug));
|
|
22322
22435
|
}
|
|
@@ -22325,7 +22438,10 @@ async function listAllPrds(sessions = listSessions()) {
|
|
|
22325
22438
|
orderBy: { createdAt: "desc" },
|
|
22326
22439
|
select: { id: true }
|
|
22327
22440
|
});
|
|
22328
|
-
const
|
|
22441
|
+
const traces = await loadTraces(projects.map((p) => p.id));
|
|
22442
|
+
const nested = await Promise.all(
|
|
22443
|
+
projects.map((p) => listPrds(p.id, sessions, traces.get(p.id) ?? []))
|
|
22444
|
+
);
|
|
22329
22445
|
return nested.flat();
|
|
22330
22446
|
}
|
|
22331
22447
|
async function readPrd(projectId, path, sessions = listSessions()) {
|
|
@@ -22337,8 +22453,8 @@ async function readPrd(projectId, path, sessions = listSessions()) {
|
|
|
22337
22453
|
// src/services/project-breakdowns.ts
|
|
22338
22454
|
init_src();
|
|
22339
22455
|
init_pty();
|
|
22340
|
-
var
|
|
22341
|
-
var isPrd2 = (rel) => rel.startsWith(
|
|
22456
|
+
var PRD_DIR3 = "docs/prd/";
|
|
22457
|
+
var isPrd2 = (rel) => rel.startsWith(PRD_DIR3) && rel.endsWith(".md");
|
|
22342
22458
|
function breakdownPathFor(prdPath) {
|
|
22343
22459
|
if (!isPrd2(prdPath) || prdPath.endsWith(".breakdown.md")) return null;
|
|
22344
22460
|
return prdPath.slice(0, -3) + ".breakdown.md";
|
|
@@ -22578,7 +22694,7 @@ async function listStashes(repoDir) {
|
|
|
22578
22694
|
}
|
|
22579
22695
|
}
|
|
22580
22696
|
async function listGraph(repoDir, limit = 200, opts = {}) {
|
|
22581
|
-
if (!repoDir || !existsSync6(repoDir)) return { commits: [], current: "" };
|
|
22697
|
+
if (!repoDir || !existsSync6(repoDir)) return { commits: [], current: "", total: 0 };
|
|
22582
22698
|
try {
|
|
22583
22699
|
const fmt = ["%H", "%P", "%an", "%aI", "%s", "%D"].join(US);
|
|
22584
22700
|
const refArgs = opts.branches?.length ? ["--end-of-options", ...opts.branches] : [
|
|
@@ -22610,9 +22726,16 @@ async function listGraph(repoDir, limit = 200, opts = {}) {
|
|
|
22610
22726
|
tags
|
|
22611
22727
|
};
|
|
22612
22728
|
});
|
|
22613
|
-
|
|
22729
|
+
let total = commits.length;
|
|
22730
|
+
try {
|
|
22731
|
+
const c = await exec6("git", ["rev-list", "--count", ...refArgs], { cwd: repoDir, ...GIT5 });
|
|
22732
|
+
const n2 = Number(c.stdout.trim());
|
|
22733
|
+
if (Number.isFinite(n2)) total = n2;
|
|
22734
|
+
} catch {
|
|
22735
|
+
}
|
|
22736
|
+
return { commits, current: await currentBranch(repoDir), total };
|
|
22614
22737
|
} catch {
|
|
22615
|
-
return { commits: [], current: "" };
|
|
22738
|
+
return { commits: [], current: "", total: 0 };
|
|
22616
22739
|
}
|
|
22617
22740
|
}
|
|
22618
22741
|
function mergeNumName(numOut, nameOut) {
|
|
@@ -23773,7 +23896,7 @@ async function terminal_default(app2) {
|
|
|
23773
23896
|
});
|
|
23774
23897
|
return reply.code(201).send({ id: s3.id });
|
|
23775
23898
|
}
|
|
23776
|
-
const { agent, model, effort } = await
|
|
23899
|
+
const { agent, model, effort } = await terminalAgentDefaults(parsed.data);
|
|
23777
23900
|
if (agent === "codex") ensureCodexTrust(repoDir);
|
|
23778
23901
|
const s2 = createSession(project.id, repoDir, { agent, model, effort });
|
|
23779
23902
|
return reply.code(201).send({ id: s2.id });
|
|
@@ -25984,7 +26107,9 @@ async function tickets_default(app2) {
|
|
|
25984
26107
|
}
|
|
25985
26108
|
|
|
25986
26109
|
// src/routes/scheduler.ts
|
|
26110
|
+
init_zod();
|
|
25987
26111
|
init_src();
|
|
26112
|
+
init_db();
|
|
25988
26113
|
|
|
25989
26114
|
// src/services/scheduler/config.ts
|
|
25990
26115
|
init_db();
|
|
@@ -26013,12 +26138,39 @@ async function enqueue(i) {
|
|
|
26013
26138
|
function listQueue(status) {
|
|
26014
26139
|
return prisma.schedulerQueueItem.findMany({ where: status ? { status } : void 0 });
|
|
26015
26140
|
}
|
|
26141
|
+
async function listQueuePage(f = {}) {
|
|
26142
|
+
const where = f.status ? { status: f.status } : void 0;
|
|
26143
|
+
const total = await prisma.schedulerQueueItem.count({ where });
|
|
26144
|
+
const pageSize = f.limit ? Math.max(1, Math.floor(+f.limit) || 1) : total || 1;
|
|
26145
|
+
const page = f.page ? Math.max(1, Math.floor(+f.page) || 1) : 1;
|
|
26146
|
+
const items = await prisma.schedulerQueueItem.findMany({
|
|
26147
|
+
where,
|
|
26148
|
+
orderBy: { enqueuedAt: "desc" },
|
|
26149
|
+
skip: (page - 1) * pageSize,
|
|
26150
|
+
take: pageSize
|
|
26151
|
+
});
|
|
26152
|
+
return { items, total, page, pageSize };
|
|
26153
|
+
}
|
|
26154
|
+
async function queueCounts() {
|
|
26155
|
+
const [queued3, launched, done, failed, canceled] = await Promise.all([
|
|
26156
|
+
prisma.schedulerQueueItem.count({ where: { status: "queued" } }),
|
|
26157
|
+
prisma.schedulerQueueItem.count({ where: { status: "launched" } }),
|
|
26158
|
+
prisma.schedulerQueueItem.count({ where: { status: "done" } }),
|
|
26159
|
+
prisma.schedulerQueueItem.count({ where: { status: "failed" } }),
|
|
26160
|
+
prisma.schedulerQueueItem.count({ where: { status: "canceled" } })
|
|
26161
|
+
]);
|
|
26162
|
+
return { queued: queued3, launched, done, failed, canceled };
|
|
26163
|
+
}
|
|
26016
26164
|
async function queued2() {
|
|
26017
26165
|
const items = await prisma.schedulerQueueItem.findMany({ where: { status: "queued" } });
|
|
26018
26166
|
return items.sort((a, b) => (RANK[a.priority] ?? 1) - (RANK[b.priority] ?? 1) || a.enqueuedAt.getTime() - b.enqueuedAt.getTime());
|
|
26019
26167
|
}
|
|
26020
26168
|
async function markLaunched(id, sessionId2) {
|
|
26021
|
-
|
|
26169
|
+
const { count } = await prisma.schedulerQueueItem.updateMany({
|
|
26170
|
+
where: { id, status: "queued" },
|
|
26171
|
+
data: { status: "launched", sessionId: sessionId2, launchedAt: /* @__PURE__ */ new Date() }
|
|
26172
|
+
});
|
|
26173
|
+
return count > 0;
|
|
26022
26174
|
}
|
|
26023
26175
|
async function markFailed(id, note) {
|
|
26024
26176
|
await prisma.schedulerQueueItem.update({ where: { id }, data: { status: "failed", note: note ?? null } });
|
|
@@ -26029,11 +26181,29 @@ async function markDone(id, note) {
|
|
|
26029
26181
|
data: { status: "done", ...note ? { note } : {} }
|
|
26030
26182
|
});
|
|
26031
26183
|
}
|
|
26032
|
-
async function
|
|
26184
|
+
async function noteRow(id, note) {
|
|
26033
26185
|
const row = await prisma.schedulerQueueItem.findUnique({ where: { id }, select: { note: true } });
|
|
26034
26186
|
if (row?.note === note) return;
|
|
26035
26187
|
await prisma.schedulerQueueItem.update({ where: { id }, data: { note } });
|
|
26036
26188
|
}
|
|
26189
|
+
async function markCanceled(id, note) {
|
|
26190
|
+
const { count } = await prisma.schedulerQueueItem.updateMany({
|
|
26191
|
+
where: { id, status: "queued" },
|
|
26192
|
+
data: { status: "canceled", note }
|
|
26193
|
+
});
|
|
26194
|
+
return count > 0;
|
|
26195
|
+
}
|
|
26196
|
+
async function markRequeued(id) {
|
|
26197
|
+
const { count } = await prisma.schedulerQueueItem.updateMany({
|
|
26198
|
+
where: { id, status: "canceled" },
|
|
26199
|
+
data: { status: "queued", note: null }
|
|
26200
|
+
});
|
|
26201
|
+
return count > 0;
|
|
26202
|
+
}
|
|
26203
|
+
async function isQueued(id) {
|
|
26204
|
+
const row = await prisma.schedulerQueueItem.findUnique({ where: { id }, select: { status: true } });
|
|
26205
|
+
return row?.status === "queued";
|
|
26206
|
+
}
|
|
26037
26207
|
|
|
26038
26208
|
// src/services/scheduler/registry.ts
|
|
26039
26209
|
var sources = /* @__PURE__ */ new Map();
|
|
@@ -26057,6 +26227,14 @@ function isDue(id, everyMin, now) {
|
|
|
26057
26227
|
|
|
26058
26228
|
// src/routes/scheduler.ts
|
|
26059
26229
|
init_pty();
|
|
26230
|
+
var CANCEL_NOTE = "dibatalkan operator";
|
|
26231
|
+
var zCancelBody = external_exports.object({ reason: external_exports.string().trim().max(200).optional() });
|
|
26232
|
+
async function refuse(reply, id, verb) {
|
|
26233
|
+
const row = await prisma.schedulerQueueItem.findUnique({ where: { id }, select: { status: true } });
|
|
26234
|
+
if (!row) return reply.code(404).send({ error: "item antrean tak ada" });
|
|
26235
|
+
const why = row.status === "launched" ? "sesinya sudah berjalan \u2014 tutup dari Terminal bila memang tak diperlukan" : `statusnya sudah ${row.status}`;
|
|
26236
|
+
return reply.code(409).send({ error: `tak bisa ${verb}: ${why}`, status: row.status });
|
|
26237
|
+
}
|
|
26060
26238
|
async function scheduler_default(app2) {
|
|
26061
26239
|
app2.get("/scheduler/config", async () => getScheduler());
|
|
26062
26240
|
app2.put("/scheduler/config", async (req, reply) => {
|
|
@@ -26064,10 +26242,22 @@ async function scheduler_default(app2) {
|
|
|
26064
26242
|
if (!parsed.success) return reply.code(400).send({ error: parsed.error.flatten() });
|
|
26065
26243
|
return setScheduler(parsed.data);
|
|
26066
26244
|
});
|
|
26245
|
+
app2.post("/scheduler/queue/:id/cancel", async (req, reply) => {
|
|
26246
|
+
const { id } = req.params;
|
|
26247
|
+
const parsed = zCancelBody.safeParse(req.body ?? {});
|
|
26248
|
+
if (!parsed.success) return reply.code(400).send({ error: parsed.error.flatten() });
|
|
26249
|
+
const note = parsed.data.reason ? `${CANCEL_NOTE}: ${parsed.data.reason}` : CANCEL_NOTE;
|
|
26250
|
+
if (!await markCanceled(id, note)) return refuse(reply, id, "membatalkan");
|
|
26251
|
+
return prisma.schedulerQueueItem.findUnique({ where: { id } });
|
|
26252
|
+
});
|
|
26253
|
+
app2.post("/scheduler/queue/:id/requeue", async (req, reply) => {
|
|
26254
|
+
const { id } = req.params;
|
|
26255
|
+
if (!await markRequeued(id)) return refuse(reply, id, "mengembalikan ke antrean");
|
|
26256
|
+
return prisma.schedulerQueueItem.findUnique({ where: { id } });
|
|
26257
|
+
});
|
|
26067
26258
|
app2.get("/scheduler/state", async () => {
|
|
26068
26259
|
const cfg = await getScheduler();
|
|
26069
26260
|
const live = listSessions().filter((s2) => !s2.exited);
|
|
26070
|
-
const queue2 = await listQueue();
|
|
26071
26261
|
const srcView = (id, sc, minCount) => {
|
|
26072
26262
|
const last = getLastRun(id);
|
|
26073
26263
|
return {
|
|
@@ -26083,9 +26273,31 @@ async function scheduler_default(app2) {
|
|
|
26083
26273
|
srcView("backlog", cfg.sources.backlog),
|
|
26084
26274
|
srcView("triase", cfg.sources.triase)
|
|
26085
26275
|
];
|
|
26086
|
-
const
|
|
26276
|
+
const counts = await queueCounts();
|
|
26277
|
+
const launchedSpecs = new Set((await listQueue("launched")).map((q) => q.specId));
|
|
26087
26278
|
const sessions = live.filter((s2) => s2.specId && launchedSpecs.has(s2.specId));
|
|
26088
|
-
return { config: cfg, cap: cfg.maxConcurrent, liveCount: live.length, sources: sources2,
|
|
26279
|
+
return { config: cfg, cap: cfg.maxConcurrent, liveCount: live.length, sources: sources2, queueCounts: counts, sessions };
|
|
26280
|
+
});
|
|
26281
|
+
app2.get("/scheduler/queue", async (req) => {
|
|
26282
|
+
const { status, page, limit } = req.query;
|
|
26283
|
+
const r = await listQueuePage({ status, page, limit });
|
|
26284
|
+
return {
|
|
26285
|
+
items: r.items.map((q) => ({
|
|
26286
|
+
id: q.id,
|
|
26287
|
+
specId: q.specId,
|
|
26288
|
+
projectId: q.projectId,
|
|
26289
|
+
source: q.source,
|
|
26290
|
+
priority: q.priority,
|
|
26291
|
+
status: q.status,
|
|
26292
|
+
sessionId: q.sessionId,
|
|
26293
|
+
note: q.note,
|
|
26294
|
+
enqueuedAt: q.enqueuedAt.toISOString(),
|
|
26295
|
+
launchedAt: q.launchedAt ? q.launchedAt.toISOString() : null
|
|
26296
|
+
})),
|
|
26297
|
+
total: r.total,
|
|
26298
|
+
page: r.page,
|
|
26299
|
+
pageSize: r.pageSize
|
|
26300
|
+
};
|
|
26089
26301
|
});
|
|
26090
26302
|
}
|
|
26091
26303
|
|
|
@@ -26264,6 +26476,22 @@ function keepExistingRefs(refs2, repoDir) {
|
|
|
26264
26476
|
// src/services/lead/trail.ts
|
|
26265
26477
|
init_db();
|
|
26266
26478
|
init_src();
|
|
26479
|
+
|
|
26480
|
+
// src/services/lead/page.ts
|
|
26481
|
+
var LEAD_MAX_TAKE = 200;
|
|
26482
|
+
var LEAD_DEFAULT_TAKE = 50;
|
|
26483
|
+
function leadWindow(f) {
|
|
26484
|
+
if (f.limit !== void 0 || f.page !== void 0) {
|
|
26485
|
+
const pageSize = Math.min(Math.max(1, Math.floor(f.limit ?? LEAD_DEFAULT_TAKE) || 1), LEAD_MAX_TAKE);
|
|
26486
|
+
const page = Math.max(1, Math.floor(f.page ?? 1) || 1);
|
|
26487
|
+
return { skip: (page - 1) * pageSize, take: pageSize, page, pageSize };
|
|
26488
|
+
}
|
|
26489
|
+
const take2 = Math.min(f.take ?? LEAD_DEFAULT_TAKE, LEAD_MAX_TAKE);
|
|
26490
|
+
const skip = f.skip ?? 0;
|
|
26491
|
+
return { skip, take: take2, page: Math.floor(skip / Math.max(1, take2)) + 1, pageSize: take2 };
|
|
26492
|
+
}
|
|
26493
|
+
|
|
26494
|
+
// src/services/lead/trail.ts
|
|
26267
26495
|
async function recordDecision(i) {
|
|
26268
26496
|
return prisma.leadDecision.create({
|
|
26269
26497
|
data: {
|
|
@@ -26298,20 +26526,24 @@ async function recordDecision(i) {
|
|
|
26298
26526
|
});
|
|
26299
26527
|
}
|
|
26300
26528
|
async function listDecisions(f = {}) {
|
|
26301
|
-
|
|
26302
|
-
|
|
26303
|
-
|
|
26304
|
-
|
|
26305
|
-
|
|
26306
|
-
|
|
26307
|
-
|
|
26308
|
-
|
|
26529
|
+
const where = {
|
|
26530
|
+
...f.projectId ? { projectId: f.projectId } : {},
|
|
26531
|
+
...f.specId ? { specId: f.specId } : {},
|
|
26532
|
+
...f.sessionId ? { sessionId: f.sessionId } : {},
|
|
26533
|
+
...f.flowId ? { flowId: f.flowId } : {},
|
|
26534
|
+
...f.status ? { status: f.status } : {}
|
|
26535
|
+
};
|
|
26536
|
+
const w = leadWindow(f);
|
|
26537
|
+
const total = await prisma.leadDecision.count({ where });
|
|
26538
|
+
const rows = await prisma.leadDecision.findMany({
|
|
26539
|
+
where,
|
|
26309
26540
|
// SPEC-485 · satu RANTAI dibaca dari awal: urutan pertanyaannya adalah isi jejaknya. Daftar
|
|
26310
26541
|
// umum tetap terbaru-dulu (AC-24) — dua pertanyaan yang berbeda, dua urutan yang berbeda.
|
|
26311
26542
|
orderBy: { createdAt: f.flowId ? "asc" : "desc" },
|
|
26312
|
-
take:
|
|
26313
|
-
skip:
|
|
26543
|
+
take: w.take,
|
|
26544
|
+
skip: w.skip
|
|
26314
26545
|
});
|
|
26546
|
+
return { rows, total, page: w.page, pageSize: w.pageSize };
|
|
26315
26547
|
}
|
|
26316
26548
|
async function overrideDecision(id, answer, reason, rawChoices = []) {
|
|
26317
26549
|
const old = await prisma.leadDecision.findUnique({ where: { id } });
|
|
@@ -26555,15 +26787,19 @@ async function closeFlow(id, reason) {
|
|
|
26555
26787
|
});
|
|
26556
26788
|
}
|
|
26557
26789
|
async function listFlows(f = {}) {
|
|
26558
|
-
|
|
26559
|
-
|
|
26560
|
-
|
|
26561
|
-
|
|
26562
|
-
|
|
26790
|
+
const where = {
|
|
26791
|
+
...f.projectId ? { projectId: f.projectId } : {},
|
|
26792
|
+
...f.status ? { status: f.status } : {}
|
|
26793
|
+
};
|
|
26794
|
+
const w = leadWindow(f);
|
|
26795
|
+
const total = await prisma.leadFlow.count({ where });
|
|
26796
|
+
const rows = await prisma.leadFlow.findMany({
|
|
26797
|
+
where,
|
|
26563
26798
|
orderBy: { createdAt: "desc" },
|
|
26564
|
-
take:
|
|
26565
|
-
skip:
|
|
26799
|
+
take: w.take,
|
|
26800
|
+
skip: w.skip
|
|
26566
26801
|
});
|
|
26802
|
+
return { rows, total, page: w.page, pageSize: w.pageSize };
|
|
26567
26803
|
}
|
|
26568
26804
|
async function expireFlows(now) {
|
|
26569
26805
|
const due = await prisma.leadFlow.findMany({
|
|
@@ -27691,7 +27927,7 @@ async function lead_default(app2) {
|
|
|
27691
27927
|
});
|
|
27692
27928
|
app2.get("/lead/decisions", async (req) => {
|
|
27693
27929
|
const q = req.query;
|
|
27694
|
-
const
|
|
27930
|
+
const r = await listDecisions({
|
|
27695
27931
|
projectId: q.projectId,
|
|
27696
27932
|
specId: q.specId,
|
|
27697
27933
|
sessionId: q.sessionId,
|
|
@@ -27699,19 +27935,23 @@ async function lead_default(app2) {
|
|
|
27699
27935
|
// SPEC-485 · satu rantai dibaca lewat filter ini, urut NAIK (lihat `listDecisions`).
|
|
27700
27936
|
flowId: q.flowId,
|
|
27701
27937
|
take: q.take ? Number(q.take) : void 0,
|
|
27702
|
-
skip: q.skip ? Number(q.skip) : void 0
|
|
27938
|
+
skip: q.skip ? Number(q.skip) : void 0,
|
|
27939
|
+
page: q.page ? Number(q.page) : void 0,
|
|
27940
|
+
limit: q.limit ? Number(q.limit) : void 0
|
|
27703
27941
|
});
|
|
27704
|
-
return { items: rows.map(toDecisionView) };
|
|
27942
|
+
return { items: r.rows.map(toDecisionView), total: r.total, page: r.page, pageSize: r.pageSize };
|
|
27705
27943
|
});
|
|
27706
27944
|
app2.get("/lead/flows", async (req) => {
|
|
27707
27945
|
const q = req.query;
|
|
27708
|
-
const
|
|
27946
|
+
const r = await listFlows({
|
|
27709
27947
|
projectId: q.projectId,
|
|
27710
27948
|
status: q.status,
|
|
27711
27949
|
take: q.take ? Number(q.take) : void 0,
|
|
27712
|
-
skip: q.skip ? Number(q.skip) : void 0
|
|
27950
|
+
skip: q.skip ? Number(q.skip) : void 0,
|
|
27951
|
+
page: q.page ? Number(q.page) : void 0,
|
|
27952
|
+
limit: q.limit ? Number(q.limit) : void 0
|
|
27713
27953
|
});
|
|
27714
|
-
return { items: rows.map(toFlowView) };
|
|
27954
|
+
return { items: r.rows.map(toFlowView), total: r.total, page: r.page, pageSize: r.pageSize };
|
|
27715
27955
|
});
|
|
27716
27956
|
app2.post("/lead/flows/:id/submit", async (req, reply) => {
|
|
27717
27957
|
const { id } = req.params;
|
|
@@ -27955,7 +28195,15 @@ async function collectVersions(repoDir, fromTag, toTag) {
|
|
|
27955
28195
|
// src/services/changelog/generate.ts
|
|
27956
28196
|
init_src();
|
|
27957
28197
|
init_db();
|
|
28198
|
+
|
|
28199
|
+
// src/services/changelog/config.ts
|
|
28200
|
+
init_src();
|
|
27958
28201
|
init_settings2();
|
|
28202
|
+
async function changelogAgentDefaults() {
|
|
28203
|
+
const e = (await getSetting()).changelog;
|
|
28204
|
+
if (!e.enabled) return sessionAgentDefaults();
|
|
28205
|
+
return e.agent === "codex" ? { agent: "codex", model: e.model, effort: coerceCodexEffort(e.model, e.effort) } : { agent: "claude", model: e.model, effort: e.effort };
|
|
28206
|
+
}
|
|
27959
28207
|
|
|
27960
28208
|
// src/services/changelog/render.ts
|
|
27961
28209
|
var MODE_LABEL = {
|
|
@@ -28033,7 +28281,7 @@ async function generateChangelog(projectId, req, deps = {}) {
|
|
|
28033
28281
|
if (!got.ok) return got;
|
|
28034
28282
|
const input = got.input;
|
|
28035
28283
|
const prompt = changelogPrompt(input, CHANGELOG_TIMEOUT_MS);
|
|
28036
|
-
const { agent, model, effort } = await
|
|
28284
|
+
const { agent, model, effort } = await changelogAgentDefaults();
|
|
28037
28285
|
const run2 = deps.think ?? think;
|
|
28038
28286
|
let body = "";
|
|
28039
28287
|
let generator = "agent";
|
|
@@ -28101,9 +28349,9 @@ async function changelog_default(app2) {
|
|
|
28101
28349
|
app2.get("/projects/:id/changelog", async (req, reply) => {
|
|
28102
28350
|
const { id } = req.params;
|
|
28103
28351
|
if (!await prisma.project.findUnique({ where: { id } })) return reply.code(404).send({ error: "not found" });
|
|
28104
|
-
const { page, limit } = req.query;
|
|
28352
|
+
const { page, limit, q } = req.query;
|
|
28105
28353
|
const rows = await prisma.changelog.findMany({ where: { projectId: id }, orderBy: { createdAt: "desc" } });
|
|
28106
|
-
return paginate(rows.map(view6), page, limit);
|
|
28354
|
+
return paginate(rows.filter((r) => changelogMatches(r, q ?? "")).map(view6), page, limit);
|
|
28107
28355
|
});
|
|
28108
28356
|
app2.get("/projects/:id/changelog/:cid", async (req, reply) => {
|
|
28109
28357
|
const { id, cid } = req.params;
|
|
@@ -28773,14 +29021,14 @@ async function githubIssues(app2) {
|
|
|
28773
29021
|
});
|
|
28774
29022
|
app2.get("/projects/:id/github/issues", async (req, reply) => {
|
|
28775
29023
|
const { id } = req.params;
|
|
28776
|
-
const { status } = req.query;
|
|
29024
|
+
const { status, page, limit } = req.query;
|
|
28777
29025
|
const project = await prisma.project.findUnique({ where: { id }, select: { id: true } });
|
|
28778
29026
|
if (!project) return reply.code(404).send({ error: "not found" });
|
|
28779
29027
|
const items = await prisma.githubIssue.findMany({
|
|
28780
29028
|
where: { projectId: id, ...status ? { status } : {} },
|
|
28781
29029
|
orderBy: [{ number: "desc" }]
|
|
28782
29030
|
});
|
|
28783
|
-
return reply.send(
|
|
29031
|
+
return reply.send(paginate(items.map(view8), page, limit));
|
|
28784
29032
|
});
|
|
28785
29033
|
app2.post("/github-issues/accept", async (req, reply) => {
|
|
28786
29034
|
const parsed = zAcceptMany.safeParse(req.body ?? {});
|
|
@@ -30004,6 +30252,7 @@ init_src();
|
|
|
30004
30252
|
|
|
30005
30253
|
// src/services/scheduler/governor.ts
|
|
30006
30254
|
var ALREADY_DONE_NOTE = "spec sudah selesai \u2014 tak diluncurkan";
|
|
30255
|
+
var canceledRaceNote = (sessionId2) => `dibatalkan saat sesi ${sessionId2} sudah terlanjur lahir \u2014 sesi dibiarkan hidup, tutup dari Terminal bila tak diperlukan`;
|
|
30007
30256
|
var draining = false;
|
|
30008
30257
|
async function drain(cfg, deps) {
|
|
30009
30258
|
if (draining) return;
|
|
@@ -30013,13 +30262,14 @@ async function drain(cfg, deps) {
|
|
|
30013
30262
|
if (slots <= 0) return;
|
|
30014
30263
|
for (const item of await queued2()) {
|
|
30015
30264
|
if (slots <= 0) break;
|
|
30265
|
+
if (!await isQueued(item.id)) continue;
|
|
30016
30266
|
if (await deps.isDone(item.specId)) {
|
|
30017
30267
|
await markDone(item.id, ALREADY_DONE_NOTE);
|
|
30018
30268
|
continue;
|
|
30019
30269
|
}
|
|
30020
30270
|
const blocked = await deps.blockers(item.specId);
|
|
30021
30271
|
if (blocked.length) {
|
|
30022
|
-
await
|
|
30272
|
+
await noteRow(item.id, blockedNote(blocked));
|
|
30023
30273
|
continue;
|
|
30024
30274
|
}
|
|
30025
30275
|
const liveId = deps.isLive(item.specId);
|
|
@@ -30029,7 +30279,7 @@ async function drain(cfg, deps) {
|
|
|
30029
30279
|
}
|
|
30030
30280
|
try {
|
|
30031
30281
|
const sessionId2 = await deps.launch(item, cfg.autonomy);
|
|
30032
|
-
await markLaunched(item.id, sessionId2);
|
|
30282
|
+
if (!await markLaunched(item.id, sessionId2)) await noteRow(item.id, canceledRaceNote(sessionId2));
|
|
30033
30283
|
slots--;
|
|
30034
30284
|
} catch (e) {
|
|
30035
30285
|
await markFailed(item.id, e.message);
|