hanoman 0.9.37 → 0.9.39
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 +80 -10
- package/dist/server.js +669 -395
- package/docs/agent-integration.md +5 -2
- package/package.json +2 -1
- package/skills/codex-image/SKILL.md +225 -0
- package/skills/codex-image/references/sprite-sheets.md +60 -0
- package/skills/codex-image/scripts/codex-image.sh +253 -0
- package/skills/codex-image/scripts/normalize-set.py +104 -0
- package/web/assets/{DalangHanomanScreen-JE8gkztH.js → DalangHanomanScreen-wpAO0xvb.js} +1 -1
- package/web/assets/{DocPreviewModal-B23vI_It.js → DocPreviewModal-CbhkDUJ2.js} +1 -1
- package/web/assets/{DocsWorkspace-CbV27H6A.js → DocsWorkspace-CcB0LGFV.js} +1 -1
- package/web/assets/{IdeScreen-BY7sm97W.js → IdeScreen-DI4v3ZkU.js} +1 -1
- package/web/assets/{LeadScreen-BFwI29zS.js → LeadScreen-BdS_xX_f.js} +1 -1
- package/web/assets/{ReviewScreen-pM6zzGZl.js → ReviewScreen-Ddw5U2sM.js} +1 -1
- package/web/assets/{SchedulerScreen-BIhqn3X1.js → SchedulerScreen-D6LGVrhM.js} +1 -1
- package/web/assets/{SettingsScreen-C_TW3mbs.js → SettingsScreen-CFNcg1T8.js} +1 -1
- package/web/assets/{TeamScreen-RT926lDf.js → TeamScreen-BkxPDI5P.js} +1 -1
- package/web/assets/TerminalScreen-CEQFK9Rc.js +2 -0
- package/web/assets/{TriageScreen-Zp1bdRnl.js → TriageScreen-B-HdZsEt.js} +1 -1
- package/web/assets/{VpsScreen-Jnb8C1o2.js → VpsScreen-CWp29NL_.js} +2 -2
- package/web/assets/{fields-B_3dIGcG.js → fields-BY7SYerd.js} +1 -1
- package/web/assets/{index-C3KwOJxI.css → index-B4b6i2tH.css} +1 -1
- package/web/assets/index-C4Py9MJV.js +1032 -0
- package/web/assets/{live-CNp86nGW.js → live-CBa_7-hn.js} +1 -1
- package/web/assets/{sched-lead-shared-BUcJMfNH.js → sched-lead-shared-CQIoYIzG.js} +1 -1
- package/web/assets/{settings-rebase-CYr7W_rB.js → settings-rebase-C3611gs8.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/TerminalScreen-fpC5a2jS.js +0 -2
- package/web/assets/index-CbX7iwxN.js +0 -1012
package/dist/server.js
CHANGED
|
@@ -4273,12 +4273,13 @@ function stampSpecMethod(payload, methodId) {
|
|
|
4273
4273
|
if (typeof payload !== "object" || Array.isArray(payload)) return null;
|
|
4274
4274
|
return { ...payload, method: methodId };
|
|
4275
4275
|
}
|
|
4276
|
-
var DEFAULT_METHOD, VERIFICATION_GATE, MATT_CLAUSE, AGENT_SKILLS_CLAUSE, METHODS, METHOD_IDS, uniq, PLAN_DIRS, SPEC_DIRS;
|
|
4276
|
+
var DEFAULT_METHOD, VERIFICATION_GATE, MOCKUP_SKILL, MATT_CLAUSE, AGENT_SKILLS_CLAUSE, METHODS, METHOD_IDS, uniq, PLAN_DIRS, SPEC_DIRS;
|
|
4277
4277
|
var init_method_catalog = __esm({
|
|
4278
4278
|
"../shared/src/method-catalog.ts"() {
|
|
4279
4279
|
"use strict";
|
|
4280
4280
|
DEFAULT_METHOD = "superpowers";
|
|
4281
4281
|
VERIFICATION_GATE = "superpowers:verification-before-completion";
|
|
4282
|
+
MOCKUP_SKILL = "codex-image";
|
|
4282
4283
|
MATT_CLAUSE = "Skill mattpocock yang kontraknya mewawancarai manusia (`/grill-me`, `/to-spec`, `/triage`) JANGAN dipakai: pakai primitif model-invoked-nya untuk menguji rancanganmu sendiri, bukan untuk mewawancarai siapa pun. Hal yang masih ambigu dan akan mempengaruhi hasil tetap JANGAN diputuskan sendiri \u2014 bila kamu agen fase, daftarkan di `Keputusan terbuka:` laporanmu; bila tidak, ajukan lewat jalur tanya di klausa otonomi prompt ini. `to-tickets` di fase Plan dipakai HANYA sebagai penghasil berkas plan berkotak `- [ ]` di `docs/matt/plans/`, BUKAN penerbit tiket \u2014 hanoman sendiri adalah issue tracker-nya, jadi jangan menulis ke tracker eksternal mana pun.";
|
|
4283
4284
|
AGENT_SKILLS_CLAUSE = "Pakai skill Agent Skills langsung sesuai fase, bukan slash-command atau meta-router `using-agent-skills`. Kontrak fase, instruksi project/pengguna, dan jalur keputusan/review hanoman tetap berlaku: `spec-driven-development` hanya menyusun spec pada fase Spec, jangan menjalankan Plan atau Execute dari fase itu. Simpan spec di `docs/agent-skills/specs/` dan plan beserta SELURUH checklist task `- [ ]` di `docs/agent-skills/plans/`, dengan id backlog pada nama berkas; ini menggantikan default `SPEC.md`, `tasks/plan.md`, dan `tasks/todo.md` upstream. Jangan menimpa plan lain yang belum tuntas atau membuat tiket di tracker eksternal: hanoman adalah issue tracker-nya. Centang task di berkas plan yang sama setelah implementasi dan verifikasi. Hal ambigu yang mempengaruhi hasil mengikuti jalur tanya prompt ini; bila kamu agen fase, daftarkan di `Keputusan terbuka:` laporanmu.";
|
|
4284
4285
|
METHODS = {
|
|
@@ -4293,6 +4294,7 @@ var init_method_catalog = __esm({
|
|
|
4293
4294
|
phaseSkills: {
|
|
4294
4295
|
Brainstorm: ["superpowers:brainstorming"],
|
|
4295
4296
|
Audit: ["superpowers:systematic-debugging"],
|
|
4297
|
+
Mockup: [MOCKUP_SKILL],
|
|
4296
4298
|
Plan: ["superpowers:writing-plans"],
|
|
4297
4299
|
Execute: [
|
|
4298
4300
|
"superpowers:executing-plans",
|
|
@@ -4338,6 +4340,7 @@ var init_method_catalog = __esm({
|
|
|
4338
4340
|
phaseSkills: {
|
|
4339
4341
|
Brainstorm: ["mattpocock-skills:grilling"],
|
|
4340
4342
|
Audit: ["mattpocock-skills:diagnosing-bugs"],
|
|
4343
|
+
Mockup: [MOCKUP_SKILL],
|
|
4341
4344
|
Plan: ["mattpocock-skills:to-tickets"],
|
|
4342
4345
|
Execute: [
|
|
4343
4346
|
"mattpocock-skills:implement",
|
|
@@ -4376,6 +4379,7 @@ var init_method_catalog = __esm({
|
|
|
4376
4379
|
phaseSkills: {
|
|
4377
4380
|
Brainstorm: ["agent-skills:idea-refine"],
|
|
4378
4381
|
Spec: ["agent-skills:spec-driven-development"],
|
|
4382
|
+
Mockup: [MOCKUP_SKILL],
|
|
4379
4383
|
Audit: [
|
|
4380
4384
|
"agent-skills:debugging-and-error-recovery",
|
|
4381
4385
|
"agent-skills:code-review-and-quality"
|
|
@@ -4419,7 +4423,7 @@ var init_method_catalog = __esm({
|
|
|
4419
4423
|
|
|
4420
4424
|
// ../shared/src/method-status.ts
|
|
4421
4425
|
function methodSkills(m) {
|
|
4422
|
-
return [.../* @__PURE__ */ new Set([...Object.values(m.phaseSkills).flat(), ...m.exitSkills])];
|
|
4426
|
+
return [.../* @__PURE__ */ new Set([...Object.values(m.phaseSkills).flat(), ...m.exitSkills])].filter((id2) => !HANOMAN_PROVIDED_SKILLS.has(id2));
|
|
4423
4427
|
}
|
|
4424
4428
|
function methodStatus(m, agent, installed2) {
|
|
4425
4429
|
const haveSkills = new Set(installed2.skills);
|
|
@@ -4437,10 +4441,12 @@ function methodStatus(m, agent, installed2) {
|
|
|
4437
4441
|
install: [...m.install[agent]]
|
|
4438
4442
|
};
|
|
4439
4443
|
}
|
|
4440
|
-
var ANTIGRAVITY_METHOD_UNAVAILABLE_REASON;
|
|
4444
|
+
var HANOMAN_PROVIDED_SKILLS, ANTIGRAVITY_METHOD_UNAVAILABLE_REASON;
|
|
4441
4445
|
var init_method_status = __esm({
|
|
4442
4446
|
"../shared/src/method-status.ts"() {
|
|
4443
4447
|
"use strict";
|
|
4448
|
+
init_method_catalog();
|
|
4449
|
+
HANOMAN_PROVIDED_SKILLS = /* @__PURE__ */ new Set([MOCKUP_SKILL]);
|
|
4444
4450
|
ANTIGRAVITY_METHOD_UNAVAILABLE_REASON = "Installer metode Antigravity belum terverifikasi dari upstream";
|
|
4445
4451
|
}
|
|
4446
4452
|
});
|
|
@@ -5174,7 +5180,7 @@ var init_logs = __esm({
|
|
|
5174
5180
|
function phaseAgentName(phase) {
|
|
5175
5181
|
return PHASE_AGENT_PREFIX + phase.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5176
5182
|
}
|
|
5177
|
-
var ORCHESTRATION_FLOWS, FLOW_PHASES, PHASE_AGENT_PREFIX, isPhaseAgentName, PHASE_EVIDENCE_GRACE_MS, EXECUTE_MODE_FLOWS, REVIEWER_CELL, REVIEWER_FLOWS, REVIEWER_DEFAULTS, UNSKIPPABLE_PHASES;
|
|
5183
|
+
var ORCHESTRATION_FLOWS, FLOW_PHASES, PHASE_AGENT_PREFIX, isPhaseAgentName, PHASE_EVIDENCE_GRACE_MS, EXECUTE_MODE_FLOWS, REVIEWER_CELL, REVIEWER_FLOWS, REVIEWER_DEFAULTS, UNSKIPPABLE_PHASES, MOCKUP_PHASE, OPTIONAL_PHASES;
|
|
5178
5184
|
var init_orchestration = __esm({
|
|
5179
5185
|
"../shared/src/orchestration.ts"() {
|
|
5180
5186
|
"use strict";
|
|
@@ -5191,8 +5197,10 @@ var init_orchestration = __esm({
|
|
|
5191
5197
|
];
|
|
5192
5198
|
FLOW_PHASES = {
|
|
5193
5199
|
// ADR-0230 · `Review` = fase resmi sesudah Execute (dulu langkah R di dalam Execute, ADR-0170 #4).
|
|
5194
|
-
|
|
5195
|
-
|
|
5200
|
+
// ADR-0233 · `Mockup` = fase opsional sesudah Spec: gambar UI disetujui sebelum Plan, dilewati agen
|
|
5201
|
+
// bila backlog tak menyentuh UI.
|
|
5202
|
+
feature: ["Brainstorm", "Objective", "Spec", "Mockup", "Plan", "Execute", "Review"],
|
|
5203
|
+
qa: ["Audit", "Spec", "Mockup", "Plan", "Execute", "Review"],
|
|
5196
5204
|
scaffold: ["Brainstorm", "Objective", "Doc index"],
|
|
5197
5205
|
reverse: ["Scan", "Docs teknis", "Wawancara", "Konvensi & index", "Serah terima"],
|
|
5198
5206
|
prd: ["Brainstorm", "PRD"],
|
|
@@ -5224,6 +5232,8 @@ var init_orchestration = __esm({
|
|
|
5224
5232
|
antigravity: { model: "inherit", effort: null }
|
|
5225
5233
|
};
|
|
5226
5234
|
UNSKIPPABLE_PHASES = /* @__PURE__ */ new Set([REVIEWER_CELL]);
|
|
5235
|
+
MOCKUP_PHASE = "Mockup";
|
|
5236
|
+
OPTIONAL_PHASES = /* @__PURE__ */ new Set([MOCKUP_PHASE]);
|
|
5227
5237
|
}
|
|
5228
5238
|
});
|
|
5229
5239
|
|
|
@@ -5234,7 +5244,7 @@ var init_runtime_defaults = __esm({
|
|
|
5234
5244
|
"use strict";
|
|
5235
5245
|
init_orchestration();
|
|
5236
5246
|
init_zod();
|
|
5237
|
-
RUNTIME_DEFAULTS_VERSION = "2026-10-
|
|
5247
|
+
RUNTIME_DEFAULTS_VERSION = "2026-10-31-v1";
|
|
5238
5248
|
zRuntimeDefaultState = external_exports.enum(["seeded", "user"]);
|
|
5239
5249
|
zBuiltinRuntimeDefaults = external_exports.object({
|
|
5240
5250
|
version: external_exports.string().default(RUNTIME_DEFAULTS_VERSION),
|
|
@@ -5290,6 +5300,7 @@ var init_runtime_defaults = __esm({
|
|
|
5290
5300
|
Brainstorm: cell("sonnet", "medium"),
|
|
5291
5301
|
Objective: cell("sonnet", "medium"),
|
|
5292
5302
|
Spec: cell("opus", "high"),
|
|
5303
|
+
Mockup: cell("sonnet", "medium"),
|
|
5293
5304
|
Plan: cell("opus", "high"),
|
|
5294
5305
|
Execute: cell("sonnet", "high"),
|
|
5295
5306
|
Review: cell("opus", "high")
|
|
@@ -5298,6 +5309,7 @@ var init_runtime_defaults = __esm({
|
|
|
5298
5309
|
Brainstorm: cell("gpt-5.6-terra", "medium"),
|
|
5299
5310
|
Objective: cell("gpt-5.6-terra", "medium"),
|
|
5300
5311
|
Spec: cell("gpt-5.6-sol", "high"),
|
|
5312
|
+
Mockup: cell("gpt-5.6-terra", "medium"),
|
|
5301
5313
|
Plan: cell("gpt-5.6-sol", "high"),
|
|
5302
5314
|
Execute: cell("gpt-5.6-terra", "high"),
|
|
5303
5315
|
Review: cell("gpt-5.6-sol", "high")
|
|
@@ -5307,6 +5319,7 @@ var init_runtime_defaults = __esm({
|
|
|
5307
5319
|
{
|
|
5308
5320
|
Audit: cell("opus", "high"),
|
|
5309
5321
|
Spec: cell("opus", "high"),
|
|
5322
|
+
Mockup: cell("sonnet", "medium"),
|
|
5310
5323
|
Plan: cell("sonnet", "high"),
|
|
5311
5324
|
Execute: cell("sonnet", "high"),
|
|
5312
5325
|
Review: cell("opus", "high")
|
|
@@ -5314,6 +5327,7 @@ var init_runtime_defaults = __esm({
|
|
|
5314
5327
|
{
|
|
5315
5328
|
Audit: cell("gpt-5.6-sol", "high"),
|
|
5316
5329
|
Spec: cell("gpt-5.6-sol", "high"),
|
|
5330
|
+
Mockup: cell("gpt-5.6-terra", "medium"),
|
|
5317
5331
|
Plan: cell("gpt-5.6-terra", "high"),
|
|
5318
5332
|
Execute: cell("gpt-5.6-terra", "high"),
|
|
5319
5333
|
Review: cell("gpt-5.6-sol", "high")
|
|
@@ -7699,7 +7713,7 @@ function subKey(topic, params) {
|
|
|
7699
7713
|
for (const k of Object.keys(params).sort()) if (params[k] !== void 0) canon[k] = params[k];
|
|
7700
7714
|
return `${topic}|${JSON.stringify(canon)}`;
|
|
7701
7715
|
}
|
|
7702
|
-
var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zChangeSpecSource, zMarkSpecDone, zMarkSpecReviewed, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, isGoalShapedFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, USER_ROLES, zUserRole, zLogin, zSignup, zSetup, zSetupApply, zChangePassword, zAdminPasswordReset, zCreateClientAccount, zUpdateClientAccount, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, PROVISION_PROFILES, COMPONENT_IDS, zProvision, UPDATE_RESTART_EXIT, CONFIG_RESTART_EXIT, MANUAL_RESTART_EXIT, EXIT_INTENT_FILE, zUpdateApplyBody, MAX_SUBS, zSubPage, zSubLimit, zTopicParams, zEventsClientMsg, zTicketView, zTicketAttachmentView, zSpecAttachmentView, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus, zCronExpr, zCreateCron, zPatchCron, zSchedulerCron, zSchedulerCronRun;
|
|
7716
|
+
var zIssueDeviceToken, zSessionResult, zSessionHistory, zCreateProject, zProjectId, zRenameProject, zUpdateProject, zCreateSpec, zPatchSpec, zChangeSpecSource, zMarkSpecDone, zMarkSpecReviewed, zIntegrate, zSessionSummary, zProjectView, zSchedulerQueueItem, zSchedulerSourceView, zSchedulerSessionView, zSchedulerQueueCounts, zSchedulerState, zLeadDecisionView, zLeadFlowView, zLeadAnswer, zLeadProjectStatus, zLeadStatusView, zFlow, isGoalShapedFlow, zPrdBrief, zPrdDoc, zBreakdownItem, zBreakdownDoc, zBatchCreateSpec, zEscalationTarget, zEscalationPrefill, zAuditEscalation, zAuditEscalationView, zTerminalSession, zDocFileContent, zDocIndexCat, zDocIndex, HOST_RE, USER_RE, zCreateVps, zPatchVps, USER_ROLES, zUserRole, zLogin, zSignup, zSetup, zSetupApply, zChangePassword, zAdminPasswordReset, zCreateClientAccount, zUpdateClientAccount, zVpsCheck, zMarkNa, zMarkNaBulk, zAttest, zRemediate, PROVISION_PROFILES, COMPONENT_IDS, zProvision, UPDATE_RESTART_EXIT, CONFIG_RESTART_EXIT, MANUAL_RESTART_EXIT, EXIT_INTENT_FILE, zUpdateApplyBody, MAX_SUBS, zSubPage, zSubLimit, zTopicParams, zEventsClientMsg, zTicketView, zTicketAttachmentView, zSpecAttachmentView, zMockupItem, zSpecMockups, zTicketDetail, zTicketEditInput, zHelpInfo, zPublicTicketStatus, zCronExpr, zCreateCron, zPatchCron, zSchedulerCron, zSchedulerCronRun;
|
|
7703
7717
|
var init_dto = __esm({
|
|
7704
7718
|
"../shared/src/dto.ts"() {
|
|
7705
7719
|
"use strict";
|
|
@@ -8323,6 +8337,12 @@ var init_dto = __esm({
|
|
|
8323
8337
|
size: external_exports.number().int(),
|
|
8324
8338
|
createdAt: external_exports.string()
|
|
8325
8339
|
});
|
|
8340
|
+
zMockupItem = external_exports.object({ name: external_exports.string(), size: external_exports.number().int(), modifiedAt: external_exports.string() });
|
|
8341
|
+
zSpecMockups = external_exports.object({
|
|
8342
|
+
source: external_exports.enum(["worktree", "repo"]).nullable(),
|
|
8343
|
+
dir: external_exports.string().nullable(),
|
|
8344
|
+
items: external_exports.array(zMockupItem)
|
|
8345
|
+
});
|
|
8326
8346
|
zTicketDetail = zTicketView.extend({
|
|
8327
8347
|
detail: external_exports.string(),
|
|
8328
8348
|
attachments: external_exports.array(zTicketAttachmentView),
|
|
@@ -8473,6 +8493,9 @@ var init_api = __esm({
|
|
|
8473
8493
|
// SPEC-843 · ADR-0124 · lampiran backlog item (multi-berkas per request).
|
|
8474
8494
|
specAttachments: (id2) => `${API}/specs/${id2}/attachments`,
|
|
8475
8495
|
specAttachment: (id2, attId) => `${API}/specs/${id2}/attachments/${attId}`,
|
|
8496
|
+
// ADR-0233 · mockup UI backlog (docs/mockups/<spec-id>/).
|
|
8497
|
+
specMockups: (id2) => `${API}/specs/${id2}/mockups`,
|
|
8498
|
+
specMockup: (id2, name2) => `${API}/specs/${id2}/mockups/${encodeURIComponent(name2)}`,
|
|
8476
8499
|
specReview: (id2) => `${API}/specs/${id2}/review`,
|
|
8477
8500
|
specReviewFile: (id2, path) => `${API}/specs/${id2}/review/${path}`,
|
|
8478
8501
|
// ADR-0213 · hapus worktree satu backlog item (cabang & commit tetap).
|
|
@@ -11228,6 +11251,19 @@ var init_backlog = __esm({
|
|
|
11228
11251
|
build: (a) => ({ method: "GET", path: `/specs/${enc(String(a.spec))}/attachments` }),
|
|
11229
11252
|
shape: (raw) => raw
|
|
11230
11253
|
},
|
|
11254
|
+
// ADR-0233 · mockup UI hasil fase Mockup, dibaca dari worktree sesi atau checkout project.
|
|
11255
|
+
{
|
|
11256
|
+
name: "hanoman_backlog_mockups_list",
|
|
11257
|
+
title: "Daftar mockup backlog",
|
|
11258
|
+
description: "Daftar gambar mockup UI sebuah backlog item (hasil fase Mockup, `docs/mockups/<spec-id>/`): `source` (`worktree` sesi atau `repo` checkout project, null bila belum ada), `dir` (path absolut folder di MESIN SERVER hanoman \u2014 baca langsung bila kamu di mesin yang sama), dan `items` {name, size, modifiedAt}. Unduh satu gambar ke disk klien dengan hanoman_backlog_mockup_download.",
|
|
11259
|
+
inputSchema: obj({ properties: { spec: str(ID_HINT) }, required: ["spec"] }),
|
|
11260
|
+
mode: "read",
|
|
11261
|
+
capability: "backlog:read",
|
|
11262
|
+
samplePath: "/specs/SPEC-1/mockups",
|
|
11263
|
+
sampleMethod: "GET",
|
|
11264
|
+
build: (a) => ({ method: "GET", path: `/specs/${enc(String(a.spec))}/mockups` }),
|
|
11265
|
+
shape: (raw) => raw
|
|
11266
|
+
},
|
|
11231
11267
|
{
|
|
11232
11268
|
name: "hanoman_backlog_attachment_delete",
|
|
11233
11269
|
title: "Hapus lampiran backlog",
|
|
@@ -14846,6 +14882,26 @@ var init_transfer = __esm({
|
|
|
14846
14882
|
build: (a) => ({ method: "GET", path: `${specOne(a)}/${enc(String(a.attachment))}`, download: dlFile(a, "lampiran") }),
|
|
14847
14883
|
shape: (raw) => raw
|
|
14848
14884
|
},
|
|
14885
|
+
// ADR-0233 · mockup UI backlog (`docs/mockups/<spec-id>/`), biner → transfer tool (preseden ADR-0183).
|
|
14886
|
+
{
|
|
14887
|
+
name: "hanoman_backlog_mockup_download",
|
|
14888
|
+
title: "Unduh mockup backlog",
|
|
14889
|
+
description: `Mengunduh SATU gambar mockup UI backlog item ke disk lokal klien MCP. Nama berkas dari hanoman_backlog_mockups_list. ${DOWNLOAD_RESULT}`,
|
|
14890
|
+
inputSchema: obj({
|
|
14891
|
+
properties: { spec: SPEC, name: str("Nama berkas mockup, mis. `01-backlog-detail.png`."), savePath: SAVE_PATH, overwrite: OVERWRITE },
|
|
14892
|
+
required: ["spec", "name"]
|
|
14893
|
+
}),
|
|
14894
|
+
mode: "write",
|
|
14895
|
+
capability: "backlog:read",
|
|
14896
|
+
samplePath: "/specs/SPEC-1/mockups/01-x.png",
|
|
14897
|
+
sampleMethod: "GET",
|
|
14898
|
+
build: (a) => ({
|
|
14899
|
+
method: "GET",
|
|
14900
|
+
path: `/specs/${enc(String(a.spec))}/mockups/${enc(String(a.name))}`,
|
|
14901
|
+
download: dlFile(a, String(a.name ?? "mockup.png"))
|
|
14902
|
+
}),
|
|
14903
|
+
shape: (raw) => raw
|
|
14904
|
+
},
|
|
14849
14905
|
// ───────────────────────── lampiran tiket ─────────────────────────
|
|
14850
14906
|
{
|
|
14851
14907
|
name: "hanoman_ticket_attachment_download",
|
|
@@ -17395,6 +17451,7 @@ function orchestratorClause(plan, o = {}) {
|
|
|
17395
17451
|
const askEscalation = codex2 || antigravity2 ? "tanyakan di terminal ini lalu tunggu jawaban" : ASK_ESCALATION_TOOL;
|
|
17396
17452
|
const askDecisions = antigravity2 ? "di terminal ini sekaligus dalam satu pesan, tiap pertanyaan bernomor dan diakhiri `?` beserta opsi, rekomendasi dan alasannya, lalu tunggu jawaban" : codex2 ? "di terminal ini sekaligus dalam satu pesan, tiap pertanyaan bernomor dan diakhiri `?` beserta opsinya, lalu tunggu jawaban" : ASK_DECISIONS_TOOL;
|
|
17397
17453
|
const turnTaking = plan.flow === "reverse" || plan.flow === "scaffold" || plan.flow === "prd" ? " Fase yang bergiliran dengan manusia (Wawancara, Brainstorm prd/scaffold) melapor satu topik per putaran \u2014 relay tiap putaran dengan cara yang sama, bawa konteks & opsi dari laporannya, dan biarkan jawaban bebas." : "";
|
|
17454
|
+
const mockupStep = plan.phases.some((p3) => p3.phase === MOCKUP_PHASE) ? 'M. Fase Mockup: laporan `Status: selesai` dengan `Rekomendasi fase: lewati` \u2192 jalankan `echo "Mockup skipped" >> "$HANOMAN_PHASE_FILE"` (gerbang yang sama seperti langkah 2), BUKAN `Mockup done`. Permintaan persetujuan mockup datang sebagai `Keputusan terbuka:` \u2014 relay lewat langkah 4 apa adanya, termasuk path gambar dan baris `Lihat di hanoman:`; jangan menyetujuinya sendiri.' : "";
|
|
17398
17455
|
const handoff = plan.phases.some((p3) => p3.phase === "Serah terima") ? "Sesudah fase Serah terima tercatat, tampilkan bagian `Ringkasan serah terima:` dari laporannya APA ADANYA ke manusia di terminal ini (teks biasa \u2014 informasi, bukan pertanyaan) sebelum menutup pekerjaan." : "";
|
|
17399
17456
|
const runtimeLabel = (entry2) => antigravity2 ? `tier native ${entry2.model} \xB7 effort native unknown` : `${entry2.model} \xB7 ${entry2.effort}`;
|
|
17400
17457
|
const phaseList = plan.phases.map((p3, i) => `${i + 1}. ${p3.phase} \u2192 \`${p3.agentName}\` \xB7 ${runtimeLabel(p3)}`).join("\n");
|
|
@@ -17425,7 +17482,8 @@ ${artefactRule} ${describeRule}`,
|
|
|
17425
17482
|
'2. Baca laporannya. `Status: selesai` DENGAN bukti \u2192 SEBELUM hal lain (memanggil fase berikutnya, commit, atau push): jalankan persis `echo "<Nama Fase> done" >> "$HANOMAN_PHASE_FILE"`, lalu verifikasi dengan `tail -1 "$HANOMAN_PHASE_FILE"` bahwa barisnya benar tertulis. Kamu satu-satunya penulis berkas itu.' + (review ? " Khusus Review: marker hanya sesudah `Verdict: lulus` (langkah R)." : ""),
|
|
17426
17483
|
`3. \`Status: sebagian\`/\`terhalang\`, galat, atau laporan tanpa bukti \u2192 delegasikan ULANG SEKALI ke subagent fase yang sama${retry} dengan laporan gagalnya disertakan (\`Percobaan: 2/2\`). Gagal lagi \u2192 BERHENTI dan ${askEscalation} apa yang harus dilakukan. Aturan ini berlaku walau klausa otonomi di prompt ini menyuruhmu tak bertanya.`,
|
|
17427
17484
|
`4. \`Status: menunggu-keputusan\` atau \`Keputusan terbuka:\` yang bukan \`-\` \u2192 SEBELUM menulis marker fase atau memanggil fase berikutnya, ajukan SEMUA pertanyaannya ${askDecisions}, lalu LANJUTKAN subagent yang SAMA lewat ${resume} dengan seluruh jawabannya. Ulangi selama subagent masih melaporkan keputusan terbuka \u2014 tak ada batas jumlah pertanyaan maupun putaran, dan giliran relay ini bukan percobaan ulang. JANGAN pernah menjawab sendiri, termasuk di sesi scheduler: yang menjawab ${DECIDER} \u2014 hanoman yang merutekannya, kamu cukup bertanya.${turnTaking}`,
|
|
17428
|
-
o.fastPath ? '5. `Rekomendasi fase: jalur-cepat` sesudah Audit \u2192 SEBELUM lanjut ke Execute: jalankan `echo "Spec skipped" >> "$HANOMAN_PHASE_FILE"` lalu `echo "Plan skipped" >> "$HANOMAN_PHASE_FILE"` (gerbang yang sama seperti langkah 2), lalu lanjut ke Execute. `penuh` \u2192 Spec \u2192 Plan \u2192 Execute.' : "",
|
|
17485
|
+
o.fastPath ? '5. `Rekomendasi fase: jalur-cepat` sesudah Audit \u2192 SEBELUM lanjut ke Execute: jalankan `echo "Spec skipped" >> "$HANOMAN_PHASE_FILE"`, `echo "Mockup skipped" >> "$HANOMAN_PHASE_FILE"`, lalu `echo "Plan skipped" >> "$HANOMAN_PHASE_FILE"` (gerbang yang sama seperti langkah 2), lalu lanjut ke Execute. `penuh` \u2192 Spec \u2192 Mockup \u2192 Plan \u2192 Execute.' : "",
|
|
17486
|
+
mockupStep,
|
|
17429
17487
|
reviewStep,
|
|
17430
17488
|
handoff,
|
|
17431
17489
|
"DILARANG mengerjakan isi fase sendiri \u2014 termasuk saat subagent gagal. Menulis `skipped` untuk fase yang dilewati bukan mengerjakannya.",
|
|
@@ -17664,7 +17722,7 @@ Stack: ${project.stack || "\u2014"}`,
|
|
|
17664
17722
|
${cron.prompt}`
|
|
17665
17723
|
].join("\n\n");
|
|
17666
17724
|
}
|
|
17667
|
-
var PIPELINES, WORK_PHASES, DECIDER, ASK_ROUTE, ASK_ESCALATION_TOOL, ASK_DECISIONS_TOOL, AMBIGUOUS, AUTONOMY_CLAUSE, AUTONOMY_CLAUSE_FULL, autonomyClause, PLAN_SHAPE, phaseInstruction, SELF_REVIEW_CLAUSE, skillInstruction, auditDecisionInstruction, ESCALATION_CONTRACT, auditOnlyInstruction, auditContinuationInstruction, auditContinuationForOrchestrator, writesCode, scopeClause, codeStyleClause, methodClause, attachmentClause, orchestratorAttachmentClause, PROJECT_METHOD, specContext, goalDetail, goalBlock, goalContext, projectContext, scaffoldContext, prdBriefBlock, prdAuditBlock, prdContext, breakdownContext, prdPhaseLines, breakdownPhaseLines, resumeClause, RESUMED_WORKTREE_NOTE, REVERSE_PHASE_GUIDE, SCAFFOLD_PHASE_GUIDE;
|
|
17725
|
+
var PIPELINES, WORK_PHASES, DECIDER, ASK_ROUTE, ASK_ESCALATION_TOOL, ASK_DECISIONS_TOOL, AMBIGUOUS, AUTONOMY_CLAUSE, AUTONOMY_CLAUSE_FULL, autonomyClause, PLAN_SHAPE, phaseInstruction, SELF_REVIEW_CLAUSE, mockupGuide, MOCKUP_FOLLOW_UP, MOCKUP_SINGLE, skillInstruction, auditDecisionInstruction, ESCALATION_CONTRACT, auditOnlyInstruction, auditContinuationInstruction, auditContinuationForOrchestrator, writesCode, scopeClause, codeStyleClause, methodClause, attachmentClause, orchestratorAttachmentClause, PROJECT_METHOD, specContext, goalDetail, goalBlock, goalContext, projectContext, scaffoldContext, prdBriefBlock, prdAuditBlock, prdContext, breakdownContext, prdPhaseLines, breakdownPhaseLines, resumeClause, RESUMED_WORKTREE_NOTE, REVERSE_PHASE_GUIDE, SCAFFOLD_PHASE_GUIDE;
|
|
17668
17726
|
var init_prompt = __esm({
|
|
17669
17727
|
"../runner/src/prompt.ts"() {
|
|
17670
17728
|
"use strict";
|
|
@@ -17688,12 +17746,23 @@ var init_prompt = __esm({
|
|
|
17688
17746
|
const base3 = `Kerjakan fase berurutan: ${phases2.join(" \u2192 ")}.
|
|
17689
17747
|
Setiap kali sebuah fase selesai (atau kamu putuskan dilewati), append satu baris ke berkas di $HANOMAN_PHASE_FILE \u2014 persis: \`echo "<Nama Fase> done" >> "$HANOMAN_PHASE_FILE"\`, atau \`skipped\` sebagai ganti \`done\`. Nama fase ditulis apa adanya seperti di atas.`;
|
|
17690
17748
|
if (!phases2.includes("Plan") || !phases2.includes("Execute")) return base3;
|
|
17691
|
-
return base3 + `
|
|
17749
|
+
return base3 + (phases2.includes(MOCKUP_PHASE) ? `
|
|
17750
|
+
${MOCKUP_SINGLE}
|
|
17751
|
+
${MOCKUP_FOLLOW_UP.Plan} ${MOCKUP_FOLLOW_UP.Execute}` : "") + `
|
|
17692
17752
|
Execute BELUM selesai selama plan (\`${method.planDir}/**\`) masih punya task \`- [ ]\`: kerjakan SEMUA PR/task sampai tiap kotak jadi \`- [x]\` sebelum menulis \`Execute done\`. hanoman menahan backlog di \`executing\`, bukan \`done\`, selama masih ada \`- [ ]\`.
|
|
17693
17753
|
${PLAN_SHAPE}` + (phases2.includes(REVIEWER_CELL) ? `
|
|
17694
17754
|
${SELF_REVIEW_CLAUSE}` : "");
|
|
17695
17755
|
};
|
|
17696
|
-
SELF_REVIEW_CLAUSE = "Fase Review (sesudah `Execute done`): periksa ulang hasil Execute terhadap dokumen spec & plan backlog ini \u2014 baca seluruh perubahan sesi ini (diff dari commit awal worktree) dan jalankan test sesuai scope verifikasi. Tulis tabel `AC \u2192 putusan \u2192 bukti` (setiap acceptance criteria \u2192 perintah & hasil yang kamu baca, atau path:baris) lalu satu baris `Verdict: lulus` atau `Verdict: rework`. `rework` \u2192 perbaiki temuannya, lalu review ulang; maks 2 putaran rework, sisanya jadi keputusan terbuka. Tulis `Review done` HANYA sesudah `Verdict: lulus`; Review TIDAK PERNAH `skipped`.";
|
|
17756
|
+
SELF_REVIEW_CLAUSE = "Fase Review (sesudah `Execute done`): periksa ulang hasil Execute terhadap dokumen spec & plan backlog ini \u2014 baca seluruh perubahan sesi ini (diff dari commit awal worktree) dan jalankan test sesuai scope verifikasi. Tulis tabel `AC \u2192 putusan \u2192 bukti` (setiap acceptance criteria \u2192 perintah & hasil yang kamu baca, atau path:baris) lalu satu baris `Verdict: lulus` atau `Verdict: rework`. `rework` \u2192 perbaiki temuannya, lalu review ulang; maks 2 putaran rework, sisanya jadi keputusan terbuka. Tulis `Review done` HANYA sesudah `Verdict: lulus`; Review TIDAK PERNAH `skipped`. Bila `docs/mockups/<spec-id>/` ada, tabel itu juga memuat kesesuaian hasil dengan mockup yang disetujui.";
|
|
17757
|
+
mockupGuide = (ask, close) => `putuskan dulu dari dokumen spec (qa jalur cepat: dokumen audit) apakah backlog ini mengubah atau membuat UI \u2014 layar, komponen, atau state yang terlihat pengguna. Tidak ada (perbaikan backend, CLI, refactor, docs) \u2192 fase ini dilewati, sebutkan alasannya dalam satu baris; ragu \u2192 anggap ada UI. Ada \u2192 baca design system project bila ada (mis. \`internal/docs/design-system/**\`, \`DESIGN.md\`) dan kode layar yang akan berubah, muat skill \`codex-image\`, lalu hasilkan SATU gambar per layar/state yang berubah, maks 6: desktop \`1536x1024\`, tambah mobile \`1024x1536\` bila layout responsif tersentuh, ke \`docs/mockups/<spec-id>/<NN>-<slug>.png\` (spec-id huruf kecil, NN dua digit urut). Tambahkan bagian \`## Mockup\` di dokumen spec: tiap berkas, layar/state yang ditunjukkannya, dan apa yang berubah. Lalu minta persetujuan ${ask}: sebutkan path absolut setiap gambar dan baris \`Lihat di hanoman: Backlog <SPEC-ID> \u2192 Mockup\`, dengan opsi Setujui \xB7 Revisi (beserta catatan) \xB7 Lewati mockup. Revisi \u2192 buat ulang gambar yang diminta dengan spec gambar LENGKAP ditambah koreksinya (bukan koreksinya saja), timpa berkas yang sama, lalu minta persetujuan lagi. Lewati \u2192 hapus berkas mockup yang belum di-commit. Disetujui \u2192 commit \`docs/mockups/<spec-id>/\` beserta dokumen spec. Bila gambar tak bisa dihasilkan (Codex tak terpasang/belum login, image_gen gagal dua kali), JANGAN membuat pengganti (SVG/CSS/HTML yang dirasterisasi, emoji, atau placeholder bernama sama) \u2014 tanyakan dengan jalur yang sama: tunggu operator menjalankan \`codex login\` lalu lanjut, atau lewati mockup. ${close}`;
|
|
17758
|
+
MOCKUP_FOLLOW_UP = {
|
|
17759
|
+
Plan: "Bila `docs/mockups/<spec-id>/` ada, setiap task UI di plan merujuk berkas mockup yang diwujudkannya.",
|
|
17760
|
+
Execute: "Bila `docs/mockups/<spec-id>/` ada, bangun UI dengan kode sesuai mockup itu \u2014 PNG mockup bukan aset produk."
|
|
17761
|
+
};
|
|
17762
|
+
MOCKUP_SINGLE = "Fase Mockup: " + mockupGuide(
|
|
17763
|
+
"lewat jalur tanya di klausa otonomi prompt ini",
|
|
17764
|
+
"Tulis `Mockup done` sesudah disetujui & di-commit, atau `Mockup skipped` bila dilewati."
|
|
17765
|
+
);
|
|
17697
17766
|
skillInstruction = (flow2, phases2, method) => {
|
|
17698
17767
|
const lines2 = phases2.map((p3) => {
|
|
17699
17768
|
const skills2 = phaseSkillsFor(flow2, p3, method);
|
|
@@ -17702,7 +17771,7 @@ ${SELF_REVIEW_CLAUSE}` : "");
|
|
|
17702
17771
|
return lines2.length ? `Skills ${method.label} WAJIB: sebelum mengerjakan fase di bawah, muat & ikuti skill-nya dengan mekanisme yang tersedia di agenmu \u2014 bila skill relevan tersedia, pakai.
|
|
17703
17772
|
${lines2.join("\n")}` : "";
|
|
17704
17773
|
};
|
|
17705
|
-
auditDecisionInstruction = (flow2) => flow2 !== "qa" ? "" : 'Keputusan pasca-Audit (qa): bila temuan berconfidence tinggi dan perbaikannya bisa dikerjakan langsung (diff kecil, akar masalah jelas), LEWATI Spec dan Plan \u2014 tandai
|
|
17774
|
+
auditDecisionInstruction = (flow2) => flow2 !== "qa" ? "" : 'Keputusan pasca-Audit (qa): bila temuan berconfidence tinggi dan perbaikannya bisa dikerjakan langsung (diff kecil, akar masalah jelas), LEWATI Spec, Mockup, dan Plan \u2014 tandai ketiganya `skipped` (`echo "Spec skipped" >> "$HANOMAN_PHASE_FILE"`, `echo "Mockup skipped" >> "$HANOMAN_PHASE_FILE"`, lalu `echo "Plan skipped" >> "$HANOMAN_PHASE_FILE"`) dan langsung ke Execute; dokumen audit menjadi doc-of-record perbaikan itu. Bila temuan luas, berisiko, atau ambigu, jalankan Spec \u2192 Mockup \u2192 Plan \u2192 Execute penuh. Keputusan ini milikmu berdasarkan hasil Audit, bukan default \u2014 jangan bayar perencanaan yang tak perlu untuk perbaikan sepele.';
|
|
17706
17775
|
ESCALATION_CONTRACT = [
|
|
17707
17776
|
"REKOMENDASI ESKALASI (wajib, terbaca mesin). Di bagian akhir dokumen audit, tulis bagian",
|
|
17708
17777
|
"`## Rekomendasi eskalasi` berisi penjelasan singkat untuk manusia, LALU tepat SATU blok ```json",
|
|
@@ -17733,7 +17802,7 @@ ${lines2.join("\n")}` : "";
|
|
|
17733
17802
|
const doc = `internal/docs/research/audit-${fromAudit.toLowerCase()}-*.md`;
|
|
17734
17803
|
if (flow2 === "feature")
|
|
17735
17804
|
return `Backlog brief ini LANJUTAN dari audit ${fromAudit}. Worktree ini lahir dari branch audit itu, jadi dokumen audit sudah ada di ${doc}. BACA dokumen itu lebih dulu dan pakai sebagai bahan fase Brainstorm & Objective \u2014 temuannya sudah terbukti, jangan menginvestigasi ulang dari nol. Semua fase tetap dijalankan: dokumen audit memuat TEMUAN, bukan bentuk solusi, jadi perancangan fitur tetap pekerjaanmu.`;
|
|
17736
|
-
return `Backlog qa ini LANJUTAN dari audit ${fromAudit}. Worktree ini lahir dari branch audit itu, jadi dokumen audit sudah ada di ${doc}. JANGAN mengulang investigasi fase Audit dari nol \u2014 baca dokumen audit itu sebagai temuan, tandai fase Audit dilewati (\`echo "Audit skipped" >> "$HANOMAN_PHASE_FILE"\`), lalu ambil keputusan pasca-Audit: perbaikan jelas & kecil \u2192 langsung Execute (tandai \`Spec skipped\` dan \`Plan skipped\` bila sesuai); selain itu Spec \u2192 Plan \u2192 Execute penuh.`;
|
|
17805
|
+
return `Backlog qa ini LANJUTAN dari audit ${fromAudit}. Worktree ini lahir dari branch audit itu, jadi dokumen audit sudah ada di ${doc}. JANGAN mengulang investigasi fase Audit dari nol \u2014 baca dokumen audit itu sebagai temuan, tandai fase Audit dilewati (\`echo "Audit skipped" >> "$HANOMAN_PHASE_FILE"\`), lalu ambil keputusan pasca-Audit: perbaikan jelas & kecil \u2192 langsung Execute (tandai \`Spec skipped\`, \`Mockup skipped\`, dan \`Plan skipped\` bila sesuai); selain itu Spec \u2192 Mockup \u2192 Plan \u2192 Execute penuh.`;
|
|
17737
17806
|
};
|
|
17738
17807
|
auditContinuationForOrchestrator = (flow2, spec) => {
|
|
17739
17808
|
if (flow2 !== "qa" && flow2 !== "feature") return "";
|
|
@@ -17742,7 +17811,7 @@ ${lines2.join("\n")}` : "";
|
|
|
17742
17811
|
const doc = `internal/docs/research/audit-${fromAudit.toLowerCase()}-*.md`;
|
|
17743
17812
|
if (flow2 === "feature")
|
|
17744
17813
|
return `Backlog brief ini LANJUTAN dari audit ${fromAudit}. Worktree ini lahir dari branch audit itu, jadi dokumen audit sudah ada di ${doc}. Saat memanggil agen fase Brainstorm, cantumkan path itu di baris \`Artefak fase sebelumnya:\` blok serah-terima \u2014 JANGAN membacanya sendiri untuk merancang fitur ini. Saat memanggil agen fase Objective, baris itu biasanya sudah membawa path artefak Brainstorm (dokumen spec yang ditulis fase itu); tambahkan path dokumen audit di samping path itu, bukan menggantikannya. Temuannya sudah terbukti, tapi bentuk solusinya belum; itu tetap dikerjakan agen fasenya masing-masing, bukan olehmu.`;
|
|
17745
|
-
return `Backlog qa ini LANJUTAN dari audit ${fromAudit}. Temuannya sudah terbukti di ${doc} \u2014 JANGAN mendelegasikan ulang fase Audit. Sebelum fase lain: jalankan persis \`echo "Audit skipped" >> "$HANOMAN_PHASE_FILE"\`, verifikasi dengan \`tail -1 "$HANOMAN_PHASE_FILE"\` bahwa barisnya benar tertulis (gerbang yang sama seperti langkah 2), lalu cantumkan path dokumen audit itu di baris \`Artefak fase sebelumnya:\` saat memanggil agen fase Spec. Keputusan routing di sini menggantikan langkah 5 di atas \u2014 JANGAN menunggu frasa \`Rekomendasi fase: jalur-cepat\` dari agen Audit karena Audit tidak dijalankan pada kelanjutan ini. Kamu diizinkan mengambil SATU keputusan ROUTING dari isi dokumen audit itu SAJA, dibaca langsung dari kriterianya: temuan berconfidence tinggi dan perbaikan langsung berdiff kecil \u2192 tandai \`Spec skipped\` lalu \`Plan skipped\` (gerbang yang sama) lalu panggil agen fase Execute; selebihnya panggil Spec \u2192 Plan \u2192 Execute penuh lewat agen fasenya masing-masing. Kamu TIDAK menginvestigasi ulang maupun merancang perbaikannya sendiri \u2014 itu tetap pekerjaan agen fase.`;
|
|
17814
|
+
return `Backlog qa ini LANJUTAN dari audit ${fromAudit}. Temuannya sudah terbukti di ${doc} \u2014 JANGAN mendelegasikan ulang fase Audit. Sebelum fase lain: jalankan persis \`echo "Audit skipped" >> "$HANOMAN_PHASE_FILE"\`, verifikasi dengan \`tail -1 "$HANOMAN_PHASE_FILE"\` bahwa barisnya benar tertulis (gerbang yang sama seperti langkah 2), lalu cantumkan path dokumen audit itu di baris \`Artefak fase sebelumnya:\` saat memanggil agen fase Spec. Keputusan routing di sini menggantikan langkah 5 di atas \u2014 JANGAN menunggu frasa \`Rekomendasi fase: jalur-cepat\` dari agen Audit karena Audit tidak dijalankan pada kelanjutan ini. Kamu diizinkan mengambil SATU keputusan ROUTING dari isi dokumen audit itu SAJA, dibaca langsung dari kriterianya: temuan berconfidence tinggi dan perbaikan langsung berdiff kecil \u2192 tandai \`Spec skipped\`, \`Mockup skipped\`, lalu \`Plan skipped\` (gerbang yang sama) lalu panggil agen fase Execute; selebihnya panggil Spec \u2192 Mockup \u2192 Plan \u2192 Execute penuh lewat agen fasenya masing-masing. Kamu TIDAK menginvestigasi ulang maupun merancang perbaikannya sendiri \u2014 itu tetap pekerjaan agen fase.`;
|
|
17746
17815
|
};
|
|
17747
17816
|
writesCode = (flow2) => PIPELINES[flow2].some((p3) => WORK_PHASES.includes(p3));
|
|
17748
17817
|
scopeClause = (flow2, scope) => scope && writesCode(flow2) ? verifyScopeClause(scope) : "";
|
|
@@ -18062,7 +18131,7 @@ function defaultGoalCondition({ flow: flow2, specId, branchTo, spec }) {
|
|
|
18062
18131
|
const phases2 = PIPELINES[flow2];
|
|
18063
18132
|
const planGate = phases2.includes("Plan") && phases2.includes("Execute");
|
|
18064
18133
|
const clauses = [
|
|
18065
|
-
`1. output \`cat "$HANOMAN_PHASE_FILE"\` yang memuat satu baris untuk SETIAP fase ${phases2.join(" \u2192 ")}, masing-masing berakhiran \`done\` atau \`skipped\`` + phases2.filter((p3) => UNSKIPPABLE_PHASES.has(p3)).map((p3) => ` (\`${p3}\` wajib \`done\`)`).join("") + ";"
|
|
18134
|
+
`1. output \`cat "$HANOMAN_PHASE_FILE"\` yang memuat satu baris untuk SETIAP fase ${phases2.join(" \u2192 ")}, masing-masing berakhiran \`done\` atau \`skipped\`` + phases2.filter((p3) => UNSKIPPABLE_PHASES.has(p3)).map((p3) => ` (\`${p3}\` wajib \`done\`)`).join("") + phases2.filter((p3) => OPTIONAL_PHASES.has(p3)).map((p3) => ` (\`${p3}\` boleh tanpa baris bila fase sesudahnya sudah tercatat)`).join("") + ";"
|
|
18066
18135
|
];
|
|
18067
18136
|
if (planGate) {
|
|
18068
18137
|
const dirs = PLAN_DIRS.map((d) => `${d}/`).join(" ");
|
|
@@ -18133,12 +18202,13 @@ function codexGoalScript(o) {
|
|
|
18133
18202
|
`ST=${shq(o.stateFile)}`,
|
|
18134
18203
|
"missing=''"
|
|
18135
18204
|
];
|
|
18136
|
-
|
|
18205
|
+
phases2.forEach((p3, i) => {
|
|
18206
|
+
const name2 = OPTIONAL_PHASES.has(p3) ? `(${phases2.slice(i).join("|")})` : p3;
|
|
18137
18207
|
lines2.push(
|
|
18138
18208
|
// ADR-0230 · fase `UNSKIPPABLE_PHASES` (Review) hanya lolos dengan `done`.
|
|
18139
|
-
`grep -qE ${shq(`^${
|
|
18209
|
+
`grep -qE ${shq(`^${name2} ${UNSKIPPABLE_PHASES.has(p3) ? "done" : "(done|skipped)"}[[:space:]]*$`)} "$PF" 2>/dev/null || missing="$missing\\n- fase ${p3} belum tercatat di \\$HANOMAN_PHASE_FILE"`
|
|
18140
18210
|
);
|
|
18141
|
-
}
|
|
18211
|
+
});
|
|
18142
18212
|
if (planGate) {
|
|
18143
18213
|
for (const dir2 of PLAN_DIRS) {
|
|
18144
18214
|
lines2.push(
|
|
@@ -18891,9 +18961,16 @@ function backlogGuide(flow2, phase, ctx) {
|
|
|
18891
18961
|
// default skill) lolos hampa. Nama dikunci seperti Brainstorm, dan path persisnya diteruskan
|
|
18892
18962
|
// orchestrator ke Execute & reviewer — bukan pola glob yang bisa mencocoki plan backlog lain.
|
|
18893
18963
|
case "Plan":
|
|
18894
|
-
return `- Plan: tulis plan implementasi berkotak \`- [ ]\` untuk backlog ini dari dokumen spec-nya (path di serah-terima) ke SATU berkas \`${m.planDir}/<YYYY-MM-DD>-<spec-id>-<slug>.md\` (spec-id backlog ini huruf kecil, mis. \`2026-09-25-spec-123-ekspor-csv.md\` \u2014 nama tanpa spec-id membuat hanoman tak menemukan plan-nya). Laporkan path PERSIS berkas itu di \`Artefak:\`; orchestrator meneruskannya ke Execute. ${PLAN_SHAPE}`;
|
|
18964
|
+
return `- Plan: tulis plan implementasi berkotak \`- [ ]\` untuk backlog ini dari dokumen spec-nya (path di serah-terima) ke SATU berkas \`${m.planDir}/<YYYY-MM-DD>-<spec-id>-<slug>.md\` (spec-id backlog ini huruf kecil, mis. \`2026-09-25-spec-123-ekspor-csv.md\` \u2014 nama tanpa spec-id membuat hanoman tak menemukan plan-nya). Laporkan path PERSIS berkas itu di \`Artefak:\`; orchestrator meneruskannya ke Execute. ${PLAN_SHAPE} ${MOCKUP_FOLLOW_UP.Plan}`;
|
|
18895
18965
|
case "Execute":
|
|
18896
|
-
return `- Execute: kerjakan plan backlog ini \u2014 path persisnya di baris \`Artefak fase sebelumnya:\` serah-terima (bila tak ada: berkas di \`${m.planDir}/\` yang namanya memuat spec-id backlog ini). Execute BELUM selesai selama plan masih punya task \`- [ ]\`: kerjakan SEMUA task sampai tiap kotak jadi \`- [x]\` sebelum melapor \`Status: selesai\`. hanoman menahan backlog di \`executing\`, bukan \`done\`, selama masih ada \`- [ ]\`. Laporan \`Status: selesai\` wajib memuat tabel \`AC \u2192 bukti\`: setiap acceptance criteria dokumen spec (qa jalur-cepat: dokumen audit) \u2192 perintah yang kamu jalankan beserta hasil yang kamu baca, atau path:baris yang membuktikannya. AC tanpa bukti = belum selesai; laporanmu diperiksa ulang sebelum fase ditutup
|
|
18966
|
+
return `- Execute: kerjakan plan backlog ini \u2014 path persisnya di baris \`Artefak fase sebelumnya:\` serah-terima (bila tak ada: berkas di \`${m.planDir}/\` yang namanya memuat spec-id backlog ini). Execute BELUM selesai selama plan masih punya task \`- [ ]\`: kerjakan SEMUA task sampai tiap kotak jadi \`- [x]\` sebelum melapor \`Status: selesai\`. hanoman menahan backlog di \`executing\`, bukan \`done\`, selama masih ada \`- [ ]\`. Laporan \`Status: selesai\` wajib memuat tabel \`AC \u2192 bukti\`: setiap acceptance criteria dokumen spec (qa jalur-cepat: dokumen audit) \u2192 perintah yang kamu jalankan beserta hasil yang kamu baca, atau path:baris yang membuktikannya. AC tanpa bukti = belum selesai; laporanmu diperiksa ulang sebelum fase ditutup. ${MOCKUP_FOLLOW_UP.Execute}`;
|
|
18967
|
+
// ADR-0233 · panduan yang SAMA dengan sesi tunggal (`mockupGuide`); bedanya cuma jalur tanya & penutupan:
|
|
18968
|
+
// agen fase tak bisa bertanya langsung dan tak menulis marker sendiri.
|
|
18969
|
+
case "Mockup":
|
|
18970
|
+
return "- Mockup: " + mockupGuide(
|
|
18971
|
+
"sebagai butir `Keputusan terbuka:` laporanmu (`Status: menunggu-keputusan`) \u2014 orchestrator yang menanyakannya",
|
|
18972
|
+
"Disetujui & di-commit \u2192 `Status: selesai`. Dilewati (tanpa UI, atau dipilih Lewati) \u2192 `Status: selesai` dengan `Rekomendasi fase: lewati` beserta alasannya; orchestrator yang menulis marker-nya."
|
|
18973
|
+
);
|
|
18897
18974
|
case "Audit":
|
|
18898
18975
|
return flow2 === "audit" ? "- Audit: ini audit-only \u2014 investigasi SAJA, JANGAN menulis perbaikan kode apa pun. Telusuri akar masalah / log / jawaban dan nilai apakah issue terdefinisi dengan baik. Laporkan temuan beserta buktinya; dokumennya ditulis fase Laporan." : "- Audit: telusuri akar masalah dengan bukti (reproduksi, log, kode) dan tulis dokumen audit ke `internal/docs/research/audit-<spec-id>-<slug>.md`. Lalu putuskan jalurnya dari hasil Audit, bukan default: bila temuan berconfidence tinggi dan perbaikannya bisa dikerjakan langsung (diff kecil, akar masalah jelas), tulis `Rekomendasi fase: jalur-cepat` beserta alasannya \u2014 orchestrator akan melewati Spec & Plan dan dokumen audit menjadi doc-of-record perbaikan itu. Bila temuan luas, berisiko, atau ambigu, tulis `Rekomendasi fase: penuh`. " + FRONT_LOADED_DECISIONS("Spec, Plan, dan Execute") + " Catat jawabannya di dokumen audit.";
|
|
18899
18976
|
case "Laporan":
|
|
@@ -18971,7 +19048,7 @@ function reviewerInstructions(plan, ctx) {
|
|
|
18971
19048
|
"Prosedur:",
|
|
18972
19049
|
[
|
|
18973
19050
|
`1. Baca dokumen spec & plan dari baris \`Artefak:\` serah-terima (qa jalur-cepat: dokumen audit adalah doc-of-record; bila path tak ada, cari berkas ber-spec-id di \`${m.specDir}/\` & \`${m.planDir}/\`). Plan yang menyimpang dari spec adalah temuan.`,
|
|
18974
|
-
"2. Ubah acceptance criteria jadi daftar yang diperiksa SATU PER SATU. Kalimat tak terukur ditandai tak terukur, bukan ditafsirkan.",
|
|
19051
|
+
"2. Ubah acceptance criteria jadi daftar yang diperiksa SATU PER SATU. Kalimat tak terukur ditandai tak terukur, bukan ditafsirkan. Bila `docs/mockups/<spec-id>/` ada, kesesuaian UI hasil dengan mockup yang disetujui ikut jadi kriteria.",
|
|
18975
19052
|
'3. Baca `git diff "$HANOMAN_BASE_SHA"...HEAD` dan `git status --porcelain` (perubahan belum di-commit adalah temuan). Laporan Execute dan kotak `- [x]` hanyalah klaim.',
|
|
18976
19053
|
"4. Jalankan test yang relevan dengan berkas yang berubah, sesuai scope verifikasi di bawah, dan baca outputnya sendiri.",
|
|
18977
19054
|
`5. Per kriteria putuskan salah satu: ${SPEC_AUDIT_VERDICT_LIST}. Kriteria tanpa jangkar di diff bukan otomatis gagal \u2014 cari bukti keadaan akhir; tak bisa dibuktikan \u2192 belum terverifikasi.`
|
|
@@ -20971,6 +21048,7 @@ __export(src_exports, {
|
|
|
20971
21048
|
GOAL_MAX: () => GOAL_MAX,
|
|
20972
21049
|
GOAL_MAX_BLOCKS: () => GOAL_MAX_BLOCKS,
|
|
20973
21050
|
GOAL_TUI_PASTE_LIMIT: () => GOAL_TUI_PASTE_LIMIT,
|
|
21051
|
+
MOCKUP_FOLLOW_UP: () => MOCKUP_FOLLOW_UP,
|
|
20974
21052
|
PHASE_CONTEXT_HEADER: () => PHASE_CONTEXT_HEADER,
|
|
20975
21053
|
PIPELINES: () => PIPELINES,
|
|
20976
21054
|
PLAN_SHAPE: () => PLAN_SHAPE,
|
|
@@ -21037,6 +21115,7 @@ __export(src_exports, {
|
|
|
21037
21115
|
materializeAntigravityAgents: () => materializeAntigravityAgents,
|
|
21038
21116
|
materializeCodexAgents: () => materializeCodexAgents,
|
|
21039
21117
|
methodClause: () => methodClause,
|
|
21118
|
+
mockupGuide: () => mockupGuide,
|
|
21040
21119
|
orchestratorClause: () => orchestratorClause,
|
|
21041
21120
|
parseConfigEnv: () => parseConfigEnv,
|
|
21042
21121
|
phaseAgentInstructions: () => phaseAgentInstructions,
|
|
@@ -21509,10 +21588,13 @@ var init_session_phases = __esm({
|
|
|
21509
21588
|
decisionFilePath = (repoDir, sessionId2) => `${repoDir}/.worktrees/.decisions/${sessionId2}`;
|
|
21510
21589
|
phaseFileMemo = /* @__PURE__ */ new Map();
|
|
21511
21590
|
derivePhases = (seen2, flow2) => {
|
|
21591
|
+
const names = PIPELINES[flow2];
|
|
21512
21592
|
let activeTaken = false;
|
|
21513
|
-
return
|
|
21593
|
+
return names.map((name2, i) => {
|
|
21514
21594
|
const state = seen2.get(name2);
|
|
21515
21595
|
if (state) return { name: name2, state };
|
|
21596
|
+
if (OPTIONAL_PHASES.has(name2) && names.slice(i + 1).some((later) => seen2.has(later)))
|
|
21597
|
+
return { name: name2, state: "skipped" };
|
|
21516
21598
|
if (activeTaken) return { name: name2, state: "pending" };
|
|
21517
21599
|
activeTaken = true;
|
|
21518
21600
|
return { name: name2, state: "active" };
|
|
@@ -21793,18 +21875,146 @@ var init_antigravity_runtime = __esm({
|
|
|
21793
21875
|
}
|
|
21794
21876
|
});
|
|
21795
21877
|
|
|
21878
|
+
// src/runner/deps.ts
|
|
21879
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
21880
|
+
import { dirname as dirname10, join as join15 } from "node:path";
|
|
21881
|
+
function repoRootFrom(startDir) {
|
|
21882
|
+
let dir2 = startDir;
|
|
21883
|
+
for (let i = 0; i < 8; i++) {
|
|
21884
|
+
if (existsSync9(join15(dir2, "pnpm-workspace.yaml"))) return dir2;
|
|
21885
|
+
const parent = dirname10(dir2);
|
|
21886
|
+
if (parent === dir2) break;
|
|
21887
|
+
dir2 = parent;
|
|
21888
|
+
}
|
|
21889
|
+
return startDir;
|
|
21890
|
+
}
|
|
21891
|
+
var repoRoot;
|
|
21892
|
+
var init_deps = __esm({
|
|
21893
|
+
"src/runner/deps.ts"() {
|
|
21894
|
+
"use strict";
|
|
21895
|
+
repoRoot = (startDir = process.cwd()) => repoRootFrom(startDir);
|
|
21896
|
+
}
|
|
21897
|
+
});
|
|
21898
|
+
|
|
21899
|
+
// src/services/bundled-skills.ts
|
|
21900
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
21901
|
+
import { chmodSync as chmodSync7, cpSync as cpSync3, existsSync as existsSync10, mkdirSync as mkdirSync9, readFileSync as readFileSync11, readdirSync as readdirSync8, renameSync as renameSync4, rmSync as rmSync6, statSync as statSync3, writeFileSync as writeFileSync11 } from "node:fs";
|
|
21902
|
+
import { join as join16, relative as relative5, sep as sep6 } from "node:path";
|
|
21903
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
21904
|
+
function bundledSkillsDir() {
|
|
21905
|
+
const packed = packagedDir();
|
|
21906
|
+
return existsSync10(packed) ? packed : join16(repoRoot(moduleDir()), "server", "skills");
|
|
21907
|
+
}
|
|
21908
|
+
function filesUnder(dir2) {
|
|
21909
|
+
const out4 = [];
|
|
21910
|
+
const walk = (d) => {
|
|
21911
|
+
for (const n2 of readdirSync8(d).sort()) {
|
|
21912
|
+
if (n2 === BUNDLED_MARKER || n2 === ".DS_Store") continue;
|
|
21913
|
+
const full = join16(d, n2);
|
|
21914
|
+
if (statSync3(full).isDirectory()) walk(full);
|
|
21915
|
+
else out4.push(full);
|
|
21916
|
+
}
|
|
21917
|
+
};
|
|
21918
|
+
walk(dir2);
|
|
21919
|
+
return out4;
|
|
21920
|
+
}
|
|
21921
|
+
function contentSha(dir2) {
|
|
21922
|
+
const h2 = createHash7("sha256");
|
|
21923
|
+
for (const f of filesUnder(dir2)) {
|
|
21924
|
+
h2.update(relative5(dir2, f).split(sep6).join("/"));
|
|
21925
|
+
h2.update("\0");
|
|
21926
|
+
h2.update(readFileSync11(f));
|
|
21927
|
+
h2.update("\0");
|
|
21928
|
+
}
|
|
21929
|
+
return h2.digest("hex");
|
|
21930
|
+
}
|
|
21931
|
+
function markerSha(dir2) {
|
|
21932
|
+
try {
|
|
21933
|
+
const parsed = JSON.parse(readFileSync11(join16(dir2, BUNDLED_MARKER), "utf8"));
|
|
21934
|
+
const sha2 = parsed?.sha;
|
|
21935
|
+
return typeof sha2 === "string" ? sha2 : null;
|
|
21936
|
+
} catch {
|
|
21937
|
+
return null;
|
|
21938
|
+
}
|
|
21939
|
+
}
|
|
21940
|
+
function install(src, dest, sha2) {
|
|
21941
|
+
const tmp = `${dest}.tmp-${process.pid}`;
|
|
21942
|
+
rmSync6(tmp, { recursive: true, force: true });
|
|
21943
|
+
cpSync3(src, tmp, { recursive: true });
|
|
21944
|
+
for (const f of filesUnder(tmp))
|
|
21945
|
+
if (relative5(tmp, f).split(sep6)[0] === "scripts") chmodSync7(f, statSync3(f).mode | 493);
|
|
21946
|
+
writeFileSync11(join16(tmp, BUNDLED_MARKER), `${JSON.stringify({ sha: sha2, seededAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
21947
|
+
`);
|
|
21948
|
+
rmSync6(dest, { recursive: true, force: true });
|
|
21949
|
+
renameSync4(tmp, dest);
|
|
21950
|
+
}
|
|
21951
|
+
function seedBundledSkills(hanomanHome, o = {}) {
|
|
21952
|
+
const out4 = { seeded: [], updated: [], kept: [] };
|
|
21953
|
+
const source = o.source ?? bundledSkillsDir();
|
|
21954
|
+
const log = o.log ?? ((m) => console.warn(m));
|
|
21955
|
+
const names = skillDirsIn(source);
|
|
21956
|
+
if (!names.length) return out4;
|
|
21957
|
+
const root = hanomanSkillsDir(hanomanHome);
|
|
21958
|
+
for (const name2 of names) {
|
|
21959
|
+
try {
|
|
21960
|
+
const src = join16(source, name2);
|
|
21961
|
+
const dest = join16(root, name2);
|
|
21962
|
+
const bundleSha = contentSha(src);
|
|
21963
|
+
if (!existsSync10(dest)) {
|
|
21964
|
+
mkdirSync9(root, { recursive: true });
|
|
21965
|
+
install(src, dest, bundleSha);
|
|
21966
|
+
out4.seeded.push(name2);
|
|
21967
|
+
continue;
|
|
21968
|
+
}
|
|
21969
|
+
const recorded2 = markerSha(dest);
|
|
21970
|
+
if (recorded2 === bundleSha) continue;
|
|
21971
|
+
if (recorded2 === null || contentSha(dest) !== recorded2) {
|
|
21972
|
+
out4.kept.push(name2);
|
|
21973
|
+
log(`skill bundel ${name2} tak diperbarui: salinan di ${dest} milik operator (tanpa marker atau sudah disunting)`);
|
|
21974
|
+
continue;
|
|
21975
|
+
}
|
|
21976
|
+
install(src, dest, bundleSha);
|
|
21977
|
+
out4.updated.push(name2);
|
|
21978
|
+
} catch (e) {
|
|
21979
|
+
log(`skill bundel ${name2} gagal di-seed: ${e.message}`);
|
|
21980
|
+
}
|
|
21981
|
+
}
|
|
21982
|
+
return out4;
|
|
21983
|
+
}
|
|
21984
|
+
var BUNDLED_MARKER, packagedDir, moduleDir, skillDirsIn, isBundledCopy;
|
|
21985
|
+
var init_bundled_skills = __esm({
|
|
21986
|
+
"src/services/bundled-skills.ts"() {
|
|
21987
|
+
"use strict";
|
|
21988
|
+
init_src2();
|
|
21989
|
+
init_deps();
|
|
21990
|
+
BUNDLED_MARKER = ".hanoman-bundled.json";
|
|
21991
|
+
packagedDir = () => fileURLToPath3(new URL("../skills", import.meta.url));
|
|
21992
|
+
moduleDir = () => fileURLToPath3(new URL(".", import.meta.url));
|
|
21993
|
+
skillDirsIn = (source) => {
|
|
21994
|
+
let names;
|
|
21995
|
+
try {
|
|
21996
|
+
names = readdirSync8(source);
|
|
21997
|
+
} catch {
|
|
21998
|
+
return [];
|
|
21999
|
+
}
|
|
22000
|
+
return names.filter((n2) => !n2.startsWith(".") && existsSync10(join16(source, n2, "SKILL.md"))).sort();
|
|
22001
|
+
};
|
|
22002
|
+
isBundledCopy = (dir2) => existsSync10(join16(dir2, BUNDLED_MARKER));
|
|
22003
|
+
}
|
|
22004
|
+
});
|
|
22005
|
+
|
|
21796
22006
|
// src/services/skill-inject.ts
|
|
21797
22007
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
21798
|
-
import { createHash as
|
|
21799
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
21800
|
-
import { isAbsolute as isAbsolute8, join as
|
|
22008
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22009
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync11, lstatSync as lstatSync7, mkdirSync as mkdirSync10, readFileSync as readFileSync12, readlinkSync as readlinkSync2, symlinkSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "node:fs";
|
|
22010
|
+
import { isAbsolute as isAbsolute8, join as join17 } from "node:path";
|
|
21801
22011
|
function excludeFile(repoDir) {
|
|
21802
22012
|
const common = execFileSync4("git", ["rev-parse", "--git-common-dir"], { cwd: repoDir, env: agentEnvironment(null, process.env), encoding: "utf8" }).trim();
|
|
21803
|
-
return
|
|
22013
|
+
return join17(isAbsolute8(common) ? common : join17(repoDir, common), "info", "exclude");
|
|
21804
22014
|
}
|
|
21805
22015
|
function ownedSkills(tempDir) {
|
|
21806
22016
|
try {
|
|
21807
|
-
const parsed = JSON.parse(
|
|
22017
|
+
const parsed = JSON.parse(readFileSync12(manifestPath(tempDir), "utf8"));
|
|
21808
22018
|
return Array.isArray(parsed) ? parsed.filter((row) => row && typeof row.link === "string" && typeof row.target === "string" && typeof row.exclude === "string" && typeof row.pair === "string" && typeof row.ino === "number" && typeof row.dev === "number") : [];
|
|
21809
22019
|
} catch {
|
|
21810
22020
|
return [];
|
|
@@ -21817,8 +22027,8 @@ function cleanupInjectedSkills(tempDir) {
|
|
|
21817
22027
|
if (!st2.isSymbolicLink() || st2.ino !== own.ino || st2.dev !== own.dev || readlinkSync2(own.link) !== own.target) continue;
|
|
21818
22028
|
unlinkSync2(own.link);
|
|
21819
22029
|
if (own.pair) {
|
|
21820
|
-
const bytes =
|
|
21821
|
-
if (bytes.includes(own.pair))
|
|
22030
|
+
const bytes = readFileSync12(own.exclude, "utf8");
|
|
22031
|
+
if (bytes.includes(own.pair)) writeFileSync12(own.exclude, bytes.replace(own.pair, ""));
|
|
21822
22032
|
}
|
|
21823
22033
|
} catch {
|
|
21824
22034
|
}
|
|
@@ -21826,18 +22036,20 @@ function cleanupInjectedSkills(tempDir) {
|
|
|
21826
22036
|
}
|
|
21827
22037
|
function injectGlobalSkills(o) {
|
|
21828
22038
|
const out4 = { injected: [], warnings: [] };
|
|
21829
|
-
const opts2 = { hanomanHome: o.hanomanHome, env: process.env };
|
|
21830
|
-
const
|
|
22039
|
+
const opts2 = { hanomanHome: o.hanomanHome, env: process.env, ...o.osHome ? { osHome: o.osHome } : {} };
|
|
22040
|
+
const scanned = scanGlobalSkills(opts2);
|
|
22041
|
+
const global2 = scanned.filter((s2) => s2.layer === "hanoman");
|
|
21831
22042
|
if (!global2.length) return out4;
|
|
21832
22043
|
const taken = new Set(scanProjectSkills("session", o.cwd, opts2).map((s2) => s2.name));
|
|
21833
|
-
const
|
|
22044
|
+
const userOwned = new Set(scanned.filter((s2) => s2.layer === "user" && s2.loadedBy.includes(o.agent)).map((s2) => s2.name));
|
|
22045
|
+
const todo = global2.filter((s2) => !taken.has(s2.name) && !(userOwned.has(s2.name) && isBundledCopy(s2.dir)));
|
|
21834
22046
|
if (!todo.length) return out4;
|
|
21835
22047
|
if (o.agent === "claude") {
|
|
21836
|
-
const root =
|
|
22048
|
+
const root = join17(o.tempDir, "skills-root");
|
|
21837
22049
|
try {
|
|
21838
|
-
|
|
22050
|
+
mkdirSync10(join17(root, ".claude", "skills"), { recursive: true });
|
|
21839
22051
|
for (const s2 of todo) {
|
|
21840
|
-
const link2 =
|
|
22052
|
+
const link2 = join17(root, ".claude", "skills", s2.name);
|
|
21841
22053
|
if (!lstatSync7(link2, { throwIfNoEntry: false })) symlinkSync(s2.dir, link2);
|
|
21842
22054
|
out4.injected.push(s2.name);
|
|
21843
22055
|
}
|
|
@@ -21858,25 +22070,25 @@ function injectGlobalSkills(o) {
|
|
|
21858
22070
|
let ownedLink;
|
|
21859
22071
|
let ownedPair = "";
|
|
21860
22072
|
try {
|
|
21861
|
-
const link2 =
|
|
22073
|
+
const link2 = join17(o.cwd, ".agents", "skills", s2.name);
|
|
21862
22074
|
if (o.agent === "antigravity") {
|
|
21863
|
-
for (const dir2 of [
|
|
22075
|
+
for (const dir2 of [join17(o.cwd, ".agents"), join17(o.cwd, ".agents", "skills")]) {
|
|
21864
22076
|
const st2 = lstatSync7(dir2, { throwIfNoEntry: false });
|
|
21865
22077
|
if (st2 && (!st2.isDirectory() || st2.isSymbolicLink())) throw new Error("akar skill bukan direktori aman");
|
|
21866
22078
|
}
|
|
21867
22079
|
}
|
|
21868
22080
|
if (lstatSync7(link2, { throwIfNoEntry: false })) continue;
|
|
21869
|
-
|
|
22081
|
+
mkdirSync10(join17(o.cwd, ".agents", "skills"), { recursive: true });
|
|
21870
22082
|
symlinkSync(s2.dir, link2);
|
|
21871
22083
|
ownedLink = link2;
|
|
21872
|
-
|
|
21873
|
-
const cur =
|
|
22084
|
+
mkdirSync10(join17(exclude, ".."), { recursive: true });
|
|
22085
|
+
const cur = existsSync11(exclude) ? readFileSync12(exclude, "utf8") : "";
|
|
21874
22086
|
const line = excludeLine(s2.name);
|
|
21875
22087
|
const lines2 = cur.split("\n");
|
|
21876
22088
|
const hasOperatorLine = lines2.some((entry2, index) => entry2 === line && !lines2[index - 1]?.startsWith(`${INJECT_MARKER}:antigravity:`));
|
|
21877
22089
|
if (o.agent === "antigravity" ? !hasOperatorLine : !lines2.includes(line)) {
|
|
21878
22090
|
if (o.agent === "antigravity") {
|
|
21879
|
-
const hash4 =
|
|
22091
|
+
const hash4 = createHash8("sha256").update(`${link2}\0${s2.dir}`).digest("hex");
|
|
21880
22092
|
ownedPair = `${INJECT_MARKER}:antigravity:${hash4}
|
|
21881
22093
|
${line}
|
|
21882
22094
|
`;
|
|
@@ -21889,9 +22101,9 @@ ${line}
|
|
|
21889
22101
|
}
|
|
21890
22102
|
}
|
|
21891
22103
|
if (o.agent === "antigravity") {
|
|
21892
|
-
|
|
22104
|
+
mkdirSync10(o.tempDir, { recursive: true, mode: 448 });
|
|
21893
22105
|
const st2 = lstatSync7(link2);
|
|
21894
|
-
|
|
22106
|
+
writeFileSync12(manifestPath(o.tempDir), JSON.stringify([...ownedSkills(o.tempDir), { link: link2, target: s2.dir, exclude, pair: ownedPair, ino: st2.ino, dev: st2.dev }]), { mode: 384 });
|
|
21895
22107
|
}
|
|
21896
22108
|
out4.injected.push(s2.name);
|
|
21897
22109
|
} catch (e) {
|
|
@@ -21901,7 +22113,7 @@ ${line}
|
|
|
21901
22113
|
} catch {
|
|
21902
22114
|
}
|
|
21903
22115
|
try {
|
|
21904
|
-
if (ownedPair)
|
|
22116
|
+
if (ownedPair) writeFileSync12(exclude, readFileSync12(exclude, "utf8").replace(ownedPair, ""));
|
|
21905
22117
|
} catch {
|
|
21906
22118
|
}
|
|
21907
22119
|
}
|
|
@@ -21913,9 +22125,9 @@ ${line}
|
|
|
21913
22125
|
function unexcludeInjected(repoDir, name2) {
|
|
21914
22126
|
try {
|
|
21915
22127
|
const f = excludeFile(repoDir);
|
|
21916
|
-
if (!
|
|
22128
|
+
if (!existsSync11(f)) return;
|
|
21917
22129
|
const line = excludeLine(name2);
|
|
21918
|
-
|
|
22130
|
+
writeFileSync12(f, readFileSync12(f, "utf8").split("\n").filter((l) => l !== line).join("\n"));
|
|
21919
22131
|
} catch {
|
|
21920
22132
|
}
|
|
21921
22133
|
}
|
|
@@ -21924,17 +22136,18 @@ var init_skill_inject = __esm({
|
|
|
21924
22136
|
"src/services/skill-inject.ts"() {
|
|
21925
22137
|
"use strict";
|
|
21926
22138
|
init_src2();
|
|
22139
|
+
init_bundled_skills();
|
|
21927
22140
|
INJECT_MARKER = "# hanoman:skill-inject";
|
|
21928
22141
|
excludeLine = (name2) => `/.agents/skills/${name2}`;
|
|
21929
|
-
manifestPath = (tempDir) =>
|
|
22142
|
+
manifestPath = (tempDir) => join17(tempDir, "antigravity-skills.json");
|
|
21930
22143
|
}
|
|
21931
22144
|
});
|
|
21932
22145
|
|
|
21933
22146
|
// src/services/antigravity-trust.ts
|
|
21934
22147
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
21935
|
-
import { closeSync as closeSync2, lstatSync as lstatSync8, mkdirSync as
|
|
22148
|
+
import { closeSync as closeSync2, lstatSync as lstatSync8, mkdirSync as mkdirSync11, openSync as openSync2, readFileSync as readFileSync13, realpathSync as realpathSync6, renameSync as renameSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync13 } from "node:fs";
|
|
21936
22149
|
import { homedir as homedir4 } from "node:os";
|
|
21937
|
-
import { join as
|
|
22150
|
+
import { join as join18 } from "node:path";
|
|
21938
22151
|
function safeOwned(path, directory) {
|
|
21939
22152
|
const st2 = lstatSync8(path);
|
|
21940
22153
|
if (st2.isSymbolicLink() || (directory ? !st2.isDirectory() : !st2.isFile()) || process.getuid && st2.uid !== process.getuid()) throw new Error("unsafe path");
|
|
@@ -21944,18 +22157,18 @@ function ensureAntigravityTrust(cwd, home3 = homedir4()) {
|
|
|
21944
22157
|
let fd;
|
|
21945
22158
|
try {
|
|
21946
22159
|
const canonical = realpathSync6(cwd);
|
|
21947
|
-
|
|
22160
|
+
mkdirSync11(home3, { recursive: true });
|
|
21948
22161
|
const root = realpathSync6(home3);
|
|
21949
|
-
for (const dir2 of [
|
|
21950
|
-
if (!lstatSync8(dir2, { throwIfNoEntry: false }))
|
|
22162
|
+
for (const dir2 of [join18(root, ".gemini"), join18(root, ".gemini", "antigravity-cli")]) {
|
|
22163
|
+
if (!lstatSync8(dir2, { throwIfNoEntry: false })) mkdirSync11(dir2, { mode: 448 });
|
|
21951
22164
|
safeOwned(dir2, true);
|
|
21952
22165
|
}
|
|
21953
|
-
const file =
|
|
22166
|
+
const file = join18(root, ".gemini", "antigravity-cli", "settings.json");
|
|
21954
22167
|
const original = lstatSync8(file, { throwIfNoEntry: false });
|
|
21955
22168
|
let settings = {};
|
|
21956
22169
|
if (original) {
|
|
21957
22170
|
safeOwned(file, false);
|
|
21958
|
-
const parsed = JSON.parse(
|
|
22171
|
+
const parsed = JSON.parse(readFileSync13(file, "utf8"));
|
|
21959
22172
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("invalid settings");
|
|
21960
22173
|
settings = parsed;
|
|
21961
22174
|
}
|
|
@@ -21967,14 +22180,14 @@ function ensureAntigravityTrust(cwd, home3 = homedir4()) {
|
|
|
21967
22180
|
const candidate = `${file}.${randomUUID2()}.tmp`;
|
|
21968
22181
|
fd = openSync2(candidate, "wx", 384);
|
|
21969
22182
|
temp = candidate;
|
|
21970
|
-
|
|
22183
|
+
writeFileSync13(fd, `${JSON.stringify({ ...settings, trustedWorkspaces: [...workspaces, canonical] }, null, 2)}
|
|
21971
22184
|
`);
|
|
21972
22185
|
closeSync2(fd);
|
|
21973
22186
|
fd = void 0;
|
|
21974
22187
|
const latest = lstatSync8(file, { throwIfNoEntry: false });
|
|
21975
22188
|
if (original ? !latest || latest.ino !== original.ino || latest.mtimeMs !== original.mtimeMs : latest)
|
|
21976
22189
|
throw new Error("settings changed during preparation");
|
|
21977
|
-
|
|
22190
|
+
renameSync5(temp, file);
|
|
21978
22191
|
temp = void 0;
|
|
21979
22192
|
} catch {
|
|
21980
22193
|
throw new Error("antigravity-trust-preparation-failed");
|
|
@@ -22024,14 +22237,14 @@ var init_local_binding = __esm({
|
|
|
22024
22237
|
});
|
|
22025
22238
|
|
|
22026
22239
|
// src/services/skill-library.ts
|
|
22027
|
-
import { join as
|
|
22028
|
-
import { existsSync as
|
|
22240
|
+
import { join as join19 } from "node:path";
|
|
22241
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
22029
22242
|
async function libraryAll() {
|
|
22030
22243
|
const global2 = scanGlobalSkills(opts());
|
|
22031
22244
|
const projects = await prisma.project.findMany({ orderBy: { name: "asc" }, select: { id: true, name: true } });
|
|
22032
22245
|
const groups = await Promise.all(projects.map(async (p3) => {
|
|
22033
22246
|
const repo = await resolveRepoDir(p3.id);
|
|
22034
|
-
if (!repo || !
|
|
22247
|
+
if (!repo || !existsSync12(repo)) return { projectId: p3.id, name: p3.name, skills: [], error: "repo tidak ditemukan" };
|
|
22035
22248
|
return { projectId: p3.id, name: p3.name, skills: scanProjectSkills(p3.id, repo, opts()) };
|
|
22036
22249
|
}));
|
|
22037
22250
|
return { global: global2, projects: groups };
|
|
@@ -22054,17 +22267,17 @@ async function targetParent(layer, source, projectId) {
|
|
|
22054
22267
|
if (layer === "user") {
|
|
22055
22268
|
const s2 = source ?? ".claude";
|
|
22056
22269
|
if (!USER_SOURCES.includes(s2)) throw new SkillError(400, "source user harus .claude/.codex/.agents/.gemini/antigravity-cli");
|
|
22057
|
-
if (s2 === ".agents") return
|
|
22058
|
-
if (s2 === ".gemini/antigravity-cli") return
|
|
22059
|
-
return
|
|
22270
|
+
if (s2 === ".agents") return join19(agentsSkillHome(process.env), "skills");
|
|
22271
|
+
if (s2 === ".gemini/antigravity-cli") return join19(agentSkillHome("antigravity", process.env), "skills");
|
|
22272
|
+
return join19(agentSkillHome(s2 === ".codex" ? "codex" : "claude", process.env), "skills");
|
|
22060
22273
|
}
|
|
22061
22274
|
if (layer === "project") {
|
|
22062
22275
|
if (!projectId) throw new SkillError(400, "projectId wajib untuk lapis project");
|
|
22063
22276
|
const s2 = source ?? ".claude";
|
|
22064
22277
|
if (!PROJECT_SOURCES.includes(s2)) throw new SkillError(400, "source project harus .claude/.agents/.codex");
|
|
22065
22278
|
const repo = await resolveRepoDir(projectId);
|
|
22066
|
-
if (!repo || !
|
|
22067
|
-
return
|
|
22279
|
+
if (!repo || !existsSync12(repo)) throw new SkillError(404, "repo project tidak ditemukan");
|
|
22280
|
+
return join19(repo, s2, "skills");
|
|
22068
22281
|
}
|
|
22069
22282
|
throw new SkillError(400, "layer harus hanoman, user, atau project");
|
|
22070
22283
|
}
|
|
@@ -35867,7 +36080,7 @@ var require_snapshot_utils = __commonJS({
|
|
|
35867
36080
|
var require_snapshot_recorder = __commonJS({
|
|
35868
36081
|
"../node_modules/.pnpm/undici@7.29.1/node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
|
|
35869
36082
|
"use strict";
|
|
35870
|
-
var { writeFile: writeFile9, readFile:
|
|
36083
|
+
var { writeFile: writeFile9, readFile: readFile12, mkdir: mkdir13 } = __require("node:fs/promises");
|
|
35871
36084
|
var { dirname: dirname20, resolve: resolve25 } = __require("node:path");
|
|
35872
36085
|
var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("node:timers");
|
|
35873
36086
|
var { InvalidArgumentError, UndiciError } = require_errors();
|
|
@@ -36069,7 +36282,7 @@ var require_snapshot_recorder = __commonJS({
|
|
|
36069
36282
|
throw new InvalidArgumentError("Snapshot path is required");
|
|
36070
36283
|
}
|
|
36071
36284
|
try {
|
|
36072
|
-
const data = await
|
|
36285
|
+
const data = await readFile12(resolve25(path), "utf8");
|
|
36073
36286
|
const parsed = JSON.parse(data);
|
|
36074
36287
|
if (Array.isArray(parsed)) {
|
|
36075
36288
|
this.#snapshots.clear();
|
|
@@ -47920,7 +48133,7 @@ ${captureLines}` : capture.stack;
|
|
|
47920
48133
|
module.exports.mockErrors = mockErrors;
|
|
47921
48134
|
var { EventSource } = require_eventsource();
|
|
47922
48135
|
module.exports.EventSource = EventSource;
|
|
47923
|
-
function
|
|
48136
|
+
function install2() {
|
|
47924
48137
|
globalThis.fetch = module.exports.fetch;
|
|
47925
48138
|
globalThis.Headers = module.exports.Headers;
|
|
47926
48139
|
globalThis.Response = module.exports.Response;
|
|
@@ -47932,7 +48145,7 @@ ${captureLines}` : capture.stack;
|
|
|
47932
48145
|
globalThis.MessageEvent = module.exports.MessageEvent;
|
|
47933
48146
|
globalThis.EventSource = module.exports.EventSource;
|
|
47934
48147
|
}
|
|
47935
|
-
module.exports.install =
|
|
48148
|
+
module.exports.install = install2;
|
|
47936
48149
|
}
|
|
47937
48150
|
});
|
|
47938
48151
|
|
|
@@ -48178,7 +48391,7 @@ var init_safe_outbound_request = __esm({
|
|
|
48178
48391
|
// src/services/uploads.ts
|
|
48179
48392
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
48180
48393
|
import { mkdir, writeFile, readFile as readFile2, unlink, rm } from "node:fs/promises";
|
|
48181
|
-
import { join as
|
|
48394
|
+
import { join as join20, resolve as resolve9 } from "node:path";
|
|
48182
48395
|
function extFor(mimeType) {
|
|
48183
48396
|
return EXT[mimeType] ?? ".bin";
|
|
48184
48397
|
}
|
|
@@ -48189,17 +48402,17 @@ async function saveUpload(buf, mimeType) {
|
|
|
48189
48402
|
const dir2 = uploadDir();
|
|
48190
48403
|
await mkdir(dir2, { recursive: true, mode: 448 });
|
|
48191
48404
|
const storageKey = `${randomUUID3()}${extFor(mimeType)}`;
|
|
48192
|
-
await writeFile(
|
|
48405
|
+
await writeFile(join20(dir2, storageKey), buf, { mode: 384 });
|
|
48193
48406
|
return { storageKey, size: buf.length };
|
|
48194
48407
|
}
|
|
48195
48408
|
function sessionUploadDir(sessionId2) {
|
|
48196
48409
|
if (!SESSION_ID.test(sessionId2)) throw new Error(`sessionId tak sah: ${sessionId2}`);
|
|
48197
|
-
return
|
|
48410
|
+
return join20(uploadDir(), "terminal", sessionId2);
|
|
48198
48411
|
}
|
|
48199
48412
|
async function saveSessionUpload(sessionId2, buf, mimeType) {
|
|
48200
48413
|
const dir2 = sessionUploadDir(sessionId2);
|
|
48201
48414
|
await mkdir(dir2, { recursive: true, mode: 448 });
|
|
48202
|
-
const path =
|
|
48415
|
+
const path = join20(dir2, `${randomUUID3()}${extFor(mimeType)}`);
|
|
48203
48416
|
await writeFile(path, buf, { mode: 384 });
|
|
48204
48417
|
return { path, size: buf.length };
|
|
48205
48418
|
}
|
|
@@ -48215,11 +48428,11 @@ async function dropSessionUploads(sessionId2) {
|
|
|
48215
48428
|
}
|
|
48216
48429
|
async function readUpload(storageKey) {
|
|
48217
48430
|
const safe3 = storageKey.replace(/[/\\]/g, "");
|
|
48218
|
-
return readFile2(
|
|
48431
|
+
return readFile2(join20(uploadDir(), safe3));
|
|
48219
48432
|
}
|
|
48220
48433
|
async function readUploadOrFetch(storageKey) {
|
|
48221
48434
|
const safe3 = storageKey.replace(/[/\\]/g, "");
|
|
48222
|
-
const target3 =
|
|
48435
|
+
const target3 = join20(uploadDir(), safe3);
|
|
48223
48436
|
try {
|
|
48224
48437
|
return await readFile2(target3);
|
|
48225
48438
|
} catch {
|
|
@@ -48246,7 +48459,7 @@ async function readUploadOrFetch(storageKey) {
|
|
|
48246
48459
|
}
|
|
48247
48460
|
async function deleteUpload(storageKey) {
|
|
48248
48461
|
const safe3 = storageKey.replace(/[/\\]/g, "");
|
|
48249
|
-
await unlink(
|
|
48462
|
+
await unlink(join20(uploadDir(), safe3)).catch(() => {
|
|
48250
48463
|
});
|
|
48251
48464
|
}
|
|
48252
48465
|
var EXT, SESSION_ID;
|
|
@@ -48689,14 +48902,14 @@ var init_session_sandbox = __esm({
|
|
|
48689
48902
|
});
|
|
48690
48903
|
|
|
48691
48904
|
// src/services/session-event-spool.ts
|
|
48692
|
-
import { join as
|
|
48905
|
+
import { join as join21 } from "node:path";
|
|
48693
48906
|
var sessionEventSpoolRoot, sessionEventDir;
|
|
48694
48907
|
var init_session_event_spool = __esm({
|
|
48695
48908
|
"src/services/session-event-spool.ts"() {
|
|
48696
48909
|
"use strict";
|
|
48697
48910
|
init_src2();
|
|
48698
|
-
sessionEventSpoolRoot = (env = process.env) =>
|
|
48699
|
-
sessionEventDir = (sessionId2, env = process.env) =>
|
|
48911
|
+
sessionEventSpoolRoot = (env = process.env) => join21(resolveHome(env), "session-events");
|
|
48912
|
+
sessionEventDir = (sessionId2, env = process.env) => join21(sessionEventSpoolRoot(env), sessionId2);
|
|
48700
48913
|
}
|
|
48701
48914
|
});
|
|
48702
48915
|
|
|
@@ -48720,9 +48933,9 @@ import { spawn } from "node-pty";
|
|
|
48720
48933
|
import { execFile as execFile2, execFileSync as execFileSync5 } from "node:child_process";
|
|
48721
48934
|
import { createRequire } from "node:module";
|
|
48722
48935
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
48723
|
-
import { chmodSync as
|
|
48936
|
+
import { chmodSync as chmodSync8, mkdirSync as mkdirSync12, readFileSync as readFileSync14, rmSync as rmSync7, statSync as statSync4, writeFileSync as writeFileSync14 } from "node:fs";
|
|
48724
48937
|
import { stat as stat2 } from "node:fs/promises";
|
|
48725
|
-
import { dirname as
|
|
48938
|
+
import { dirname as dirname11, resolve as resolvePath } from "node:path";
|
|
48726
48939
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
48727
48940
|
function doneSeenOf(id2) {
|
|
48728
48941
|
const known2 = phaseDoneSeen.get(id2);
|
|
@@ -48783,8 +48996,8 @@ function fitCustomAgentsToArgBudget(customDefs, render, limit = AGENTS_ARG_SAFE_
|
|
|
48783
48996
|
}
|
|
48784
48997
|
function noTtyPromptEnv() {
|
|
48785
48998
|
const path = askpassDenyPath();
|
|
48786
|
-
|
|
48787
|
-
|
|
48999
|
+
mkdirSync12(dirname11(path), { recursive: true, mode: 448 });
|
|
49000
|
+
writeFileSync14(path, ASKPASS_DENY, { mode: 448 });
|
|
48788
49001
|
return { SSH_ASKPASS: path, SSH_ASKPASS_REQUIRE: "force", GIT_TERMINAL_PROMPT: "0" };
|
|
48789
49002
|
}
|
|
48790
49003
|
function hanomanDbInheritUnsets(env = process.env) {
|
|
@@ -49037,8 +49250,8 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49037
49250
|
phaseDoneSeen.delete(id2);
|
|
49038
49251
|
const eventDir = opts2.command ? void 0 : sessionEventDir(id2);
|
|
49039
49252
|
if (eventDir) {
|
|
49040
|
-
|
|
49041
|
-
|
|
49253
|
+
mkdirSync12(eventDir, { recursive: true, mode: 448 });
|
|
49254
|
+
chmodSync8(eventDir, 448);
|
|
49042
49255
|
}
|
|
49043
49256
|
const agentForDefs = opts2.agent ?? "claude";
|
|
49044
49257
|
const sessionEffort = agentForDefs === "codex" && opts2.model && opts2.effort ? coerceCodexEffort(opts2.model, opts2.effort) : opts2.effort;
|
|
@@ -49082,7 +49295,7 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49082
49295
|
if (customDefs.length > 0 || requestedPhaseDefs.length > 0) {
|
|
49083
49296
|
const tempDir = agentTempDir(id2);
|
|
49084
49297
|
agentConfigDir = tempDir;
|
|
49085
|
-
|
|
49298
|
+
mkdirSync12(tempDir, { recursive: true, mode: 448 });
|
|
49086
49299
|
let hook;
|
|
49087
49300
|
const readOnlyHookFor = () => customDefs.some((def2) => def2.workspacePolicy === "read-only") ? hook ??= writeReadOnlyHook(tempDir) : void 0;
|
|
49088
49301
|
const attempt = (requested) => {
|
|
@@ -49097,16 +49310,16 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49097
49310
|
let phaseContextFile;
|
|
49098
49311
|
if (shared !== void 0) {
|
|
49099
49312
|
const path = phaseContextFilePath(id2);
|
|
49100
|
-
|
|
49101
|
-
|
|
49313
|
+
writeFileSync14(path, shared, { mode: 384 });
|
|
49314
|
+
chmodSync8(path, 384);
|
|
49102
49315
|
phaseContextFile = { context: shared, path };
|
|
49103
49316
|
}
|
|
49104
49317
|
const agentContractFiles = [];
|
|
49105
49318
|
for (const policy of new Set(customDefs.map((d) => d.workspacePolicy ?? "inherit"))) {
|
|
49106
49319
|
const path = agentContractFilePath(id2, policy);
|
|
49107
49320
|
const content = agentContractOf(policy);
|
|
49108
|
-
|
|
49109
|
-
|
|
49321
|
+
writeFileSync14(path, content, { mode: 384 });
|
|
49322
|
+
chmodSync8(path, 384);
|
|
49110
49323
|
agentContractFiles.push({ policy, content, path });
|
|
49111
49324
|
}
|
|
49112
49325
|
const fit = fitCustomAgentsToArgBudget(customDefs, (kept) => renderAgentsJson(
|
|
@@ -49128,7 +49341,7 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49128
49341
|
customDefs = fit.kept;
|
|
49129
49342
|
defs = [...withPhaseDelegation(requested, customDefs, agentForDefs), ...customDefs];
|
|
49130
49343
|
if (defs.length === 0) return [];
|
|
49131
|
-
|
|
49344
|
+
writeFileSync14(file, fit.json, { mode: 384 });
|
|
49132
49345
|
} catch (error) {
|
|
49133
49346
|
if (phaseDefs.length === 0) throw error;
|
|
49134
49347
|
return [error instanceof Error ? error.message : String(error)];
|
|
@@ -49208,8 +49421,8 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49208
49421
|
const sessionPrompt = bornPrompt(orchestrated);
|
|
49209
49422
|
if (!opts2.command && sessionPrompt) {
|
|
49210
49423
|
promptFile = promptFilePath(id2);
|
|
49211
|
-
|
|
49212
|
-
|
|
49424
|
+
mkdirSync12(dirname11(promptFile), { recursive: true, mode: 448 });
|
|
49425
|
+
writeFileSync14(promptFile, sessionPrompt + rosterBlock, { mode: 384 });
|
|
49213
49426
|
promptArg = `"$(cat ${sq(promptFile)})"`;
|
|
49214
49427
|
}
|
|
49215
49428
|
const agent = opts2.agent ?? "claude";
|
|
@@ -49220,8 +49433,8 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49220
49433
|
let goalGate;
|
|
49221
49434
|
if (agent === "codex" && opts2.goal && opts2.flow && opts2.specId) {
|
|
49222
49435
|
goalGate = goalGatePath(id2);
|
|
49223
|
-
|
|
49224
|
-
|
|
49436
|
+
mkdirSync12(dirname11(goalGate), { recursive: true, mode: 448 });
|
|
49437
|
+
writeFileSync14(goalGate, codexGoalScript({
|
|
49225
49438
|
flow: opts2.flow,
|
|
49226
49439
|
specId: opts2.specId,
|
|
49227
49440
|
condition: opts2.goal,
|
|
@@ -49268,7 +49481,7 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49268
49481
|
for (const [k, v] of Object.entries(sessionEventEnv(id2))) envPairs.push(`${k}=${sq(v)}`);
|
|
49269
49482
|
}
|
|
49270
49483
|
if (opts2.phaseFile) {
|
|
49271
|
-
|
|
49484
|
+
mkdirSync12(dirname11(opts2.phaseFile), { recursive: true });
|
|
49272
49485
|
envPairs.push(`HANOMAN_PHASE_FILE=${sq(opts2.phaseFile)}`);
|
|
49273
49486
|
}
|
|
49274
49487
|
if (!opts2.command) {
|
|
@@ -49297,7 +49510,7 @@ function createSession(projectId, cwd, opts2 = {}) {
|
|
|
49297
49510
|
});
|
|
49298
49511
|
cmd = wrapped;
|
|
49299
49512
|
}
|
|
49300
|
-
if (opts2.decisionFile)
|
|
49513
|
+
if (opts2.decisionFile) mkdirSync12(dirname11(opts2.decisionFile), { recursive: true });
|
|
49301
49514
|
const bootstrap = runtime === "antigravity" ? createAntigravityEnvBootstrap(cmd, agentEnvironment(runtime, { ...process.env, ...opts2.env })) : void 0;
|
|
49302
49515
|
if (bootstrap) cmd = bootstrap.command;
|
|
49303
49516
|
try {
|
|
@@ -49704,11 +49917,11 @@ function killSession(id2) {
|
|
|
49704
49917
|
cleanupAntigravityAgents({ cwd: p3.cwd, sessionId: id2 });
|
|
49705
49918
|
cleanupInjectedSkills(agentTempDir(id2));
|
|
49706
49919
|
try {
|
|
49707
|
-
|
|
49920
|
+
rmSync7(agentTempDir(id2), { recursive: true, force: true });
|
|
49708
49921
|
} catch {
|
|
49709
49922
|
}
|
|
49710
49923
|
try {
|
|
49711
|
-
|
|
49924
|
+
rmSync7(sessionEventDir(id2), { recursive: true, force: true });
|
|
49712
49925
|
} catch {
|
|
49713
49926
|
}
|
|
49714
49927
|
emitDeath({ sessionId: id2, exitCode: p3.exited ? p3.code : null, transcript });
|
|
@@ -49767,7 +49980,7 @@ var init_pty = __esm({
|
|
|
49767
49980
|
POLL_MS = 500;
|
|
49768
49981
|
markerFilled = (f) => {
|
|
49769
49982
|
try {
|
|
49770
|
-
return
|
|
49983
|
+
return statSync4(f).size > 0;
|
|
49771
49984
|
} catch {
|
|
49772
49985
|
return false;
|
|
49773
49986
|
}
|
|
@@ -49781,7 +49994,7 @@ var init_pty = __esm({
|
|
|
49781
49994
|
};
|
|
49782
49995
|
clearMarker = (f) => {
|
|
49783
49996
|
try {
|
|
49784
|
-
|
|
49997
|
+
writeFileSync14(f, "");
|
|
49785
49998
|
} catch {
|
|
49786
49999
|
}
|
|
49787
50000
|
};
|
|
@@ -49791,7 +50004,7 @@ var init_pty = __esm({
|
|
|
49791
50004
|
markerOnset = (f) => {
|
|
49792
50005
|
let raw;
|
|
49793
50006
|
try {
|
|
49794
|
-
raw =
|
|
50007
|
+
raw = readFileSync14(f, "utf8");
|
|
49795
50008
|
} catch {
|
|
49796
50009
|
return 0;
|
|
49797
50010
|
}
|
|
@@ -50045,7 +50258,7 @@ var init_redact_known = __esm({
|
|
|
50045
50258
|
// src/services/transcript-store.ts
|
|
50046
50259
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
50047
50260
|
import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile3, unlink as unlink2, readdir as readdir2, stat as stat3 } from "node:fs/promises";
|
|
50048
|
-
import { join as
|
|
50261
|
+
import { join as join22, resolve as resolve10, basename as basename6 } from "node:path";
|
|
50049
50262
|
function transcriptDir() {
|
|
50050
50263
|
return resolve10(effectiveStr("HANOMAN_TRANSCRIPT_DIR")?.trim() || resolveDataDirs().transcripts);
|
|
50051
50264
|
}
|
|
@@ -50065,13 +50278,13 @@ async function saveTranscript(text2) {
|
|
|
50065
50278
|
const dir2 = transcriptDir();
|
|
50066
50279
|
await mkdir2(dir2, { recursive: true, mode: 448 });
|
|
50067
50280
|
const key = `${randomUUID5()}.log`;
|
|
50068
|
-
await writeFile2(
|
|
50281
|
+
await writeFile2(join22(dir2, key), body, { encoding: "utf8", mode: 384 });
|
|
50069
50282
|
return { key, bytes: Buffer.byteLength(body, "utf8"), truncated };
|
|
50070
50283
|
}
|
|
50071
50284
|
async function readTranscript(key) {
|
|
50072
50285
|
if (!key) return null;
|
|
50073
50286
|
try {
|
|
50074
|
-
return await readFile3(
|
|
50287
|
+
return await readFile3(join22(transcriptDir(), basename6(key)), "utf8");
|
|
50075
50288
|
} catch {
|
|
50076
50289
|
return null;
|
|
50077
50290
|
}
|
|
@@ -50079,7 +50292,7 @@ async function readTranscript(key) {
|
|
|
50079
50292
|
async function deleteTranscript(key) {
|
|
50080
50293
|
if (!key) return;
|
|
50081
50294
|
try {
|
|
50082
|
-
await unlink2(
|
|
50295
|
+
await unlink2(join22(transcriptDir(), basename6(key)));
|
|
50083
50296
|
} catch (e) {
|
|
50084
50297
|
if (e.code !== "ENOENT") throw e;
|
|
50085
50298
|
}
|
|
@@ -50097,7 +50310,7 @@ async function listTranscripts() {
|
|
|
50097
50310
|
for (const name2 of names) {
|
|
50098
50311
|
if (!name2.endsWith(".log")) continue;
|
|
50099
50312
|
try {
|
|
50100
|
-
rows.push({ key: name2, mtimeMs: (await stat3(
|
|
50313
|
+
rows.push({ key: name2, mtimeMs: (await stat3(join22(dir2, name2))).mtimeMs });
|
|
50101
50314
|
} catch {
|
|
50102
50315
|
}
|
|
50103
50316
|
}
|
|
@@ -55390,13 +55603,13 @@ var init_tombstone = __esm({
|
|
|
55390
55603
|
});
|
|
55391
55604
|
|
|
55392
55605
|
// src/services/qa-attachment-sync.ts
|
|
55393
|
-
import { createHash as
|
|
55606
|
+
import { createHash as createHash9, randomUUID as randomUUID7 } from "node:crypto";
|
|
55394
55607
|
import { chmod, mkdir as mkdir4, rename as rename2, stat as stat4, writeFile as writeFile4 } from "node:fs/promises";
|
|
55395
|
-
import { join as
|
|
55608
|
+
import { join as join24 } from "node:path";
|
|
55396
55609
|
async function qaBytesPresent(storageKey) {
|
|
55397
55610
|
if (!QA_STORAGE_KEY.test(storageKey)) return false;
|
|
55398
55611
|
try {
|
|
55399
|
-
return (await stat4(
|
|
55612
|
+
return (await stat4(join24(uploadDir(), storageKey))).isFile();
|
|
55400
55613
|
} catch {
|
|
55401
55614
|
return false;
|
|
55402
55615
|
}
|
|
@@ -55411,7 +55624,7 @@ async function settleNewQaAttachment(id2) {
|
|
|
55411
55624
|
}
|
|
55412
55625
|
async function verifyQaBytes(meta, buf) {
|
|
55413
55626
|
if (buf.length !== meta.size || buf.length === 0 || buf.length > QA_SYNC_MAX_BYTES) return { ok: false, status: 400, error: "size" };
|
|
55414
|
-
if (
|
|
55627
|
+
if (createHash9("sha256").update(buf).digest("hex") !== meta.sha256) return { ok: false, status: 400, error: "sha256" };
|
|
55415
55628
|
if (SNIFFABLE2.has(meta.mimeType)) {
|
|
55416
55629
|
if ((await fileTypeFromBuffer(buf))?.mime !== meta.mimeType) return { ok: false, status: 415, error: "type" };
|
|
55417
55630
|
} else if (!isUtf8Text2(buf)) return { ok: false, status: 415, error: "type" };
|
|
@@ -55421,10 +55634,10 @@ async function storeQaBytes(storageKey, buf) {
|
|
|
55421
55634
|
if (!QA_STORAGE_KEY.test(storageKey)) throw new Error("storageKey tak sah");
|
|
55422
55635
|
const dir2 = uploadDir();
|
|
55423
55636
|
await mkdir4(dir2, { recursive: true, mode: 448 });
|
|
55424
|
-
const tmp =
|
|
55637
|
+
const tmp = join24(dir2, `${storageKey}.tmp-${randomUUID7()}`);
|
|
55425
55638
|
await writeFile4(tmp, buf, { mode: 384 });
|
|
55426
|
-
await rename2(tmp,
|
|
55427
|
-
await chmod(
|
|
55639
|
+
await rename2(tmp, join24(dir2, storageKey));
|
|
55640
|
+
await chmod(join24(dir2, storageKey), 384).catch(() => {
|
|
55428
55641
|
});
|
|
55429
55642
|
}
|
|
55430
55643
|
var QA_STORAGE_KEY, QA_SYNC_MAX_BYTES, SNIFFABLE2, isUtf8Text2;
|
|
@@ -55668,9 +55881,9 @@ async function pull(sinceCursor, limit = 500, maxBytes = PULL_MAX_BYTES, opts2 =
|
|
|
55668
55881
|
async function bootstrapSnapshot(after, maxBytes = PULL_MAX_BYTES, opts2 = {}) {
|
|
55669
55882
|
const tip = await prisma.syncLog.findFirst({ orderBy: { seq: "desc" }, select: { seq: true } });
|
|
55670
55883
|
const cursor = tip ? String(tip.seq) : "0";
|
|
55671
|
-
const
|
|
55672
|
-
const afterEntity =
|
|
55673
|
-
const afterId =
|
|
55884
|
+
const sep11 = after ? after.indexOf(":") : -1;
|
|
55885
|
+
const afterEntity = sep11 > 0 ? after.slice(0, sep11) : null;
|
|
55886
|
+
const afterId = sep11 > 0 ? after.slice(sep11 + 1) : null;
|
|
55674
55887
|
const startAt = afterEntity ? BOOTSTRAP_ORDER.indexOf(afterEntity) : 0;
|
|
55675
55888
|
if (startAt < 0) throw new Error("bootstrap cursor tak dikenal");
|
|
55676
55889
|
const records = [];
|
|
@@ -56075,9 +56288,9 @@ var init_sync_notify = __esm({
|
|
|
56075
56288
|
// src/services/safe-repo-path.ts
|
|
56076
56289
|
import { constants as constants3 } from "node:fs";
|
|
56077
56290
|
import { lstat, mkdir as mkdir5, open as open2, realpath, rename as rename3, unlink as unlink4 } from "node:fs/promises";
|
|
56078
|
-
import { isAbsolute as isAbsolute10, join as
|
|
56291
|
+
import { isAbsolute as isAbsolute10, join as join25, relative as relative6, resolve as resolve13, sep as sep7 } from "node:path";
|
|
56079
56292
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
56080
|
-
import { closeSync as closeSync3, fstatSync as fstatSync2, lstatSync as lstatSync9, mkdirSync as
|
|
56293
|
+
import { closeSync as closeSync3, fstatSync as fstatSync2, lstatSync as lstatSync9, mkdirSync as mkdirSync13, openSync as openSync3, readFileSync as readFileSync15, realpathSync as realpathSync7, renameSync as renameSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync15 } from "node:fs";
|
|
56081
56294
|
function components(rel) {
|
|
56082
56295
|
if (!rel || isAbsolute10(rel) || rel.includes("\0")) denied("path invalid");
|
|
56083
56296
|
const parts = rel.replace(/\\/g, "/").split("/");
|
|
@@ -56091,15 +56304,15 @@ async function canonicalRoot(root) {
|
|
|
56091
56304
|
return out4;
|
|
56092
56305
|
}
|
|
56093
56306
|
function beneath(root, candidate) {
|
|
56094
|
-
const rel =
|
|
56095
|
-
return rel === "" || !rel.startsWith(`..${
|
|
56307
|
+
const rel = relative6(root, candidate);
|
|
56308
|
+
return rel === "" || !rel.startsWith(`..${sep7}`) && rel !== ".." && !isAbsolute10(rel);
|
|
56096
56309
|
}
|
|
56097
56310
|
async function ensureRepoParents(root, rel) {
|
|
56098
56311
|
const base3 = await canonicalRoot(root);
|
|
56099
56312
|
const parts = components(rel).slice(0, -1);
|
|
56100
56313
|
let current = base3;
|
|
56101
56314
|
for (const part of parts) {
|
|
56102
|
-
current =
|
|
56315
|
+
current = join25(current, part);
|
|
56103
56316
|
try {
|
|
56104
56317
|
await mkdir5(current, { mode: 448 });
|
|
56105
56318
|
} catch (error) {
|
|
@@ -56116,9 +56329,9 @@ function ensureRepoParentsSync(root, rel) {
|
|
|
56116
56329
|
const parts = components(rel).slice(0, -1);
|
|
56117
56330
|
let current = base3;
|
|
56118
56331
|
for (const part of parts) {
|
|
56119
|
-
current =
|
|
56332
|
+
current = join25(current, part);
|
|
56120
56333
|
try {
|
|
56121
|
-
|
|
56334
|
+
mkdirSync13(current, { mode: 448 });
|
|
56122
56335
|
} catch (error) {
|
|
56123
56336
|
if (error.code !== "EEXIST") throw error;
|
|
56124
56337
|
}
|
|
@@ -56132,7 +56345,7 @@ async function resolveRepoEntry(root, rel, opts2 = {}) {
|
|
|
56132
56345
|
const parts = components(rel);
|
|
56133
56346
|
let current = base3;
|
|
56134
56347
|
for (let i = 0; i < parts.length; i++) {
|
|
56135
|
-
current =
|
|
56348
|
+
current = join25(current, parts[i]);
|
|
56136
56349
|
const stat8 = await lstat(current).catch((error) => {
|
|
56137
56350
|
if (error.code === "ENOENT" && opts2.allowMissingTail) return null;
|
|
56138
56351
|
if (error.code === "ENOENT" && opts2.allowMissingFinal && i === parts.length - 1) return null;
|
|
@@ -56153,7 +56366,7 @@ function assertSafeRepoPathSync(root, rel, allowMissingFinal = false, allowMissi
|
|
|
56153
56366
|
const parts = components(rel);
|
|
56154
56367
|
let current = base3;
|
|
56155
56368
|
for (let i = 0; i < parts.length; i++) {
|
|
56156
|
-
current =
|
|
56369
|
+
current = join25(current, parts[i]);
|
|
56157
56370
|
try {
|
|
56158
56371
|
const stat8 = lstatSync9(current);
|
|
56159
56372
|
if (stat8.isSymbolicLink()) denied("symlink");
|
|
@@ -56172,7 +56385,7 @@ function readRepoFileSync(root, rel) {
|
|
|
56172
56385
|
const fd = openSync3(path, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
|
|
56173
56386
|
try {
|
|
56174
56387
|
if (!fstatSync2(fd).isFile()) denied("bukan file regular");
|
|
56175
|
-
return
|
|
56388
|
+
return readFileSync15(fd);
|
|
56176
56389
|
} finally {
|
|
56177
56390
|
closeSync3(fd);
|
|
56178
56391
|
}
|
|
@@ -56181,12 +56394,12 @@ function writeRepoFileAtomicSync(root, rel, data) {
|
|
|
56181
56394
|
ensureRepoParentsSync(root, rel);
|
|
56182
56395
|
const path = assertSafeRepoPathSync(root, rel, true);
|
|
56183
56396
|
const parent = resolve13(path, "..");
|
|
56184
|
-
const parentRel =
|
|
56397
|
+
const parentRel = relative6(realpathSync7(root), parent);
|
|
56185
56398
|
if (parentRel) assertSafeRepoPathSync(root, parentRel);
|
|
56186
|
-
const temp =
|
|
56399
|
+
const temp = join25(parent, `.hanoman-${randomUUID8()}.tmp`);
|
|
56187
56400
|
const fd = openSync3(temp, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW ?? 0), 384);
|
|
56188
56401
|
try {
|
|
56189
|
-
|
|
56402
|
+
writeFileSync15(fd, data);
|
|
56190
56403
|
} finally {
|
|
56191
56404
|
closeSync3(fd);
|
|
56192
56405
|
}
|
|
@@ -56199,7 +56412,7 @@ function writeRepoFileAtomicSync(root, rel, data) {
|
|
|
56199
56412
|
}
|
|
56200
56413
|
})();
|
|
56201
56414
|
if (final?.isSymbolicLink() || final && !final.isFile()) denied("target berubah");
|
|
56202
|
-
|
|
56415
|
+
renameSync6(temp, path);
|
|
56203
56416
|
} catch (error) {
|
|
56204
56417
|
try {
|
|
56205
56418
|
unlinkSync4(temp);
|
|
@@ -56230,7 +56443,7 @@ async function writeRepoFileAtomic(root, rel, data) {
|
|
|
56230
56443
|
await mkdir5(entry2.parent, { recursive: false, mode: 448 }).catch((error) => {
|
|
56231
56444
|
if (error.code !== "EEXIST") throw error;
|
|
56232
56445
|
});
|
|
56233
|
-
const temp =
|
|
56446
|
+
const temp = join25(entry2.parent, `.hanoman-${randomUUID8()}.tmp`);
|
|
56234
56447
|
const handle = await open2(temp, constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW ?? 0), 384);
|
|
56235
56448
|
try {
|
|
56236
56449
|
await handle.writeFile(data);
|
|
@@ -56266,8 +56479,8 @@ var init_safe_repo_path = __esm({
|
|
|
56266
56479
|
// src/services/integrate.ts
|
|
56267
56480
|
import { execFile as execFile5 } from "node:child_process";
|
|
56268
56481
|
import { promisify as promisify4 } from "node:util";
|
|
56269
|
-
import { rmSync as
|
|
56270
|
-
import { join as
|
|
56482
|
+
import { rmSync as rmSync9 } from "node:fs";
|
|
56483
|
+
import { join as join26 } from "node:path";
|
|
56271
56484
|
async function worktreeForBranch(repoDir, branch) {
|
|
56272
56485
|
let path = null;
|
|
56273
56486
|
for (const line of (await out(repoDir, ["worktree", "list", "--porcelain"])).split("\n")) {
|
|
@@ -56292,7 +56505,7 @@ async function resolveTarget(repoDir, target3) {
|
|
|
56292
56505
|
async function reclaim(repoDir, wt) {
|
|
56293
56506
|
await sh(repoDir, ["worktree", "remove", "--force", wt]);
|
|
56294
56507
|
await sh(repoDir, ["worktree", "prune"]);
|
|
56295
|
-
|
|
56508
|
+
rmSync9(wt, { recursive: true, force: true });
|
|
56296
56509
|
}
|
|
56297
56510
|
async function integrateBranch(repoDir, src, op, target3) {
|
|
56298
56511
|
const source = await resolveSource(repoDir, src.branch);
|
|
@@ -56300,7 +56513,7 @@ async function integrateBranch(repoDir, src, op, target3) {
|
|
|
56300
56513
|
const tgt = await resolveTarget(repoDir, target3);
|
|
56301
56514
|
if (!tgt) return { status: "error", code: 400, error: `target "${target3}" tidak dikenal` };
|
|
56302
56515
|
await sh(repoDir, ["fetch", "origin"]);
|
|
56303
|
-
const wt =
|
|
56516
|
+
const wt = join26(repoDir, ".worktrees", `merge-${sanitize(src.mergeId)}`);
|
|
56304
56517
|
await reclaim(repoDir, wt);
|
|
56305
56518
|
const baseRef = op === "merge" ? tgt.ref : source;
|
|
56306
56519
|
const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `${baseRef}^{commit}`]);
|
|
@@ -56361,7 +56574,7 @@ async function mergeIntoCurrent(repoDir, source, opts2 = {}) {
|
|
|
56361
56574
|
const src = await resolveGraphSource(repoDir, source);
|
|
56362
56575
|
if (!src) return { status: "error", code: 400, error: `source "${source}" tak dikenal` };
|
|
56363
56576
|
await sh(repoDir, ["fetch", "origin"]);
|
|
56364
|
-
const wt =
|
|
56577
|
+
const wt = join26(repoDir, ".worktrees", `merge-${sanitize(current)}`);
|
|
56365
56578
|
await reclaim(repoDir, wt);
|
|
56366
56579
|
const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `refs/heads/${current}^{commit}`]);
|
|
56367
56580
|
if (!await ok(repoDir, ["worktree", "add", "--detach", "-q", wt, baseSha]))
|
|
@@ -56399,7 +56612,7 @@ async function replayCurrent(repoDir, source, cmd) {
|
|
|
56399
56612
|
const current = await out(repoDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
56400
56613
|
if (!current || current === "HEAD")
|
|
56401
56614
|
return { status: "error", code: 409, error: "HEAD detached \u2014 checkout sebuah branch dulu" };
|
|
56402
|
-
const wt =
|
|
56615
|
+
const wt = join26(repoDir, ".worktrees", `merge-${sanitize(current)}`);
|
|
56403
56616
|
await reclaim(repoDir, wt);
|
|
56404
56617
|
const baseSha = await out(repoDir, ["rev-parse", "--verify", "--end-of-options", `refs/heads/${current}^{commit}`]);
|
|
56405
56618
|
if (!await ok(repoDir, ["worktree", "add", "--detach", "-q", wt, baseSha]))
|
|
@@ -56459,11 +56672,11 @@ import { execFile as execFile6 } from "node:child_process";
|
|
|
56459
56672
|
import { promisify as promisify5 } from "node:util";
|
|
56460
56673
|
import { mkdtemp, copyFile, rm as rm2 } from "node:fs/promises";
|
|
56461
56674
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
56462
|
-
import { join as
|
|
56675
|
+
import { join as join27, resolve as resolve15 } from "node:path";
|
|
56463
56676
|
async function withTempIndex(wt, fn) {
|
|
56464
56677
|
const idx = (await exec4("git", ["rev-parse", "--git-path", "index"], { cwd: wt, ...GIT3 })).stdout.trim();
|
|
56465
|
-
const dir2 = await mkdtemp(
|
|
56466
|
-
const tmp =
|
|
56678
|
+
const dir2 = await mkdtemp(join27(tmpdir5(), "hanoman-idx-"));
|
|
56679
|
+
const tmp = join27(dir2, "index");
|
|
56467
56680
|
await copyFile(resolve15(wt, idx), tmp);
|
|
56468
56681
|
const env = { ...process.env, GIT_INDEX_FILE: tmp };
|
|
56469
56682
|
try {
|
|
@@ -56609,7 +56822,7 @@ var init_spec_review2 = __esm({
|
|
|
56609
56822
|
GIT3 = { maxBuffer: 1 << 24 };
|
|
56610
56823
|
MAX = 256 * 1024;
|
|
56611
56824
|
emptySpecReview = (reason) => ({ base: "", files: [], changed: [], empty: true, reason });
|
|
56612
|
-
worktreeDir = (repoDir, specId) =>
|
|
56825
|
+
worktreeDir = (repoDir, specId) => join27(repoDir, ".worktrees", specId.toLowerCase().replace(/[^a-z0-9_-]/g, "_"));
|
|
56613
56826
|
splitZ = (s2) => s2.split("\0").filter(Boolean);
|
|
56614
56827
|
EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
56615
56828
|
}
|
|
@@ -56917,7 +57130,7 @@ var init_client = __esm({
|
|
|
56917
57130
|
});
|
|
56918
57131
|
|
|
56919
57132
|
// src/services/telegram/protocol.ts
|
|
56920
|
-
import { createHash as
|
|
57133
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
56921
57134
|
function normalizeTelegramCommand(text2) {
|
|
56922
57135
|
const clean3 = text2.trim();
|
|
56923
57136
|
const match = clean3.match(/^\/([a-z0-9_]+)(?:@[a-z0-9_]+)?(?:\s+([\s\S]*))?$/i);
|
|
@@ -57006,7 +57219,7 @@ function parseTelegramUpdate(update2, allowlist, maxText = 12e3) {
|
|
|
57006
57219
|
};
|
|
57007
57220
|
}
|
|
57008
57221
|
function inboundDigest(input) {
|
|
57009
|
-
return
|
|
57222
|
+
return createHash11("sha256").update(`${input.updateId}\0${input.chatId}\0${input.userId}\0${input.kind}\0${input.text}`, "utf8").digest("hex");
|
|
57010
57223
|
}
|
|
57011
57224
|
function sanitizeTelegramOutput(text2, exactSecrets = []) {
|
|
57012
57225
|
let clean3 = text2.replace(ANSI, "").replace(CONTROLS, "");
|
|
@@ -57309,7 +57522,7 @@ var init_notifications2 = __esm({
|
|
|
57309
57522
|
});
|
|
57310
57523
|
|
|
57311
57524
|
// src/services/codex-trust.ts
|
|
57312
|
-
import { appendFileSync as appendFileSync3, mkdirSync as
|
|
57525
|
+
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync14, readFileSync as readFileSync16, realpathSync as realpathSync9 } from "node:fs";
|
|
57313
57526
|
import { homedir as homedir5 } from "node:os";
|
|
57314
57527
|
function ensureCodexTrust(repoDir, home3) {
|
|
57315
57528
|
const path = codexConfigPath(home3);
|
|
@@ -57322,11 +57535,11 @@ function ensureCodexTrust(repoDir, home3) {
|
|
|
57322
57535
|
try {
|
|
57323
57536
|
let existing = "";
|
|
57324
57537
|
try {
|
|
57325
|
-
existing =
|
|
57538
|
+
existing = readFileSync16(path, "utf8");
|
|
57326
57539
|
} catch {
|
|
57327
57540
|
}
|
|
57328
57541
|
if (existing.includes(header2)) return;
|
|
57329
|
-
|
|
57542
|
+
mkdirSync14(codexHome(home3), { recursive: true });
|
|
57330
57543
|
const lead = existing === "" || existing.endsWith("\n") ? "" : "\n";
|
|
57331
57544
|
appendFileSync3(path, `${lead}
|
|
57332
57545
|
${header2}
|
|
@@ -57466,8 +57679,8 @@ var init_device_sockets = __esm({
|
|
|
57466
57679
|
|
|
57467
57680
|
// src/services/exit-guard.ts
|
|
57468
57681
|
import { spawn as spawn3 } from "node:child_process";
|
|
57469
|
-
import { writeFileSync as
|
|
57470
|
-
import { join as
|
|
57682
|
+
import { writeFileSync as writeFileSync16 } from "node:fs";
|
|
57683
|
+
import { join as join31 } from "node:path";
|
|
57471
57684
|
function watchdogScript(pid, graceMs) {
|
|
57472
57685
|
const secs = Math.max(1, Math.ceil(graceMs / 1e3));
|
|
57473
57686
|
return `s=$(ps -o lstart= -p ${pid}); sleep ${secs}; [ -n "$s" ] && [ "$(ps -o lstart= -p ${pid})" = "$s" ] && kill -9 ${pid}`;
|
|
@@ -57483,8 +57696,8 @@ function launchWatchdog(script2, env = process.env) {
|
|
|
57483
57696
|
function exitGuarded(code, d = {}) {
|
|
57484
57697
|
const pid = d.pid ?? process.pid;
|
|
57485
57698
|
try {
|
|
57486
|
-
const write2 = d.writeIntent ?? ((p3, t) =>
|
|
57487
|
-
write2(
|
|
57699
|
+
const write2 = d.writeIntent ?? ((p3, t) => writeFileSync16(p3, t, { mode: 384 }));
|
|
57700
|
+
write2(join31(d.home ?? resolveHome(), EXIT_INTENT_FILE), formatExitIntent(pid, code));
|
|
57488
57701
|
} catch {
|
|
57489
57702
|
}
|
|
57490
57703
|
try {
|
|
@@ -57504,9 +57717,9 @@ var init_exit_guard = __esm({
|
|
|
57504
57717
|
});
|
|
57505
57718
|
|
|
57506
57719
|
// src/services/update.ts
|
|
57507
|
-
import { readFileSync as
|
|
57508
|
-
import { resolve as resolve20, dirname as
|
|
57509
|
-
import { fileURLToPath as
|
|
57720
|
+
import { readFileSync as readFileSync17 } from "node:fs";
|
|
57721
|
+
import { resolve as resolve20, dirname as dirname14 } from "node:path";
|
|
57722
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
57510
57723
|
function composeUpdate(x) {
|
|
57511
57724
|
const available = x.registryStatus === "ok" && x.latestVersion != null && compareSemver(x.latestVersion, x.currentVersion) > 0;
|
|
57512
57725
|
return {
|
|
@@ -57522,10 +57735,10 @@ function composeUpdate(x) {
|
|
|
57522
57735
|
};
|
|
57523
57736
|
}
|
|
57524
57737
|
function runningVersion() {
|
|
57525
|
-
const here =
|
|
57738
|
+
const here = dirname14(fileURLToPath4(import.meta.url));
|
|
57526
57739
|
for (const p3 of [resolve20(here, "build-info.json"), resolve20(here, "../package.json")]) {
|
|
57527
57740
|
try {
|
|
57528
|
-
const v = JSON.parse(
|
|
57741
|
+
const v = JSON.parse(readFileSync17(p3, "utf8")).version;
|
|
57529
57742
|
if (typeof v === "string" && v) return v;
|
|
57530
57743
|
} catch {
|
|
57531
57744
|
}
|
|
@@ -57961,7 +58174,7 @@ var init_remote_session = __esm({
|
|
|
57961
58174
|
|
|
57962
58175
|
// src/services/spec-attachment-dir.ts
|
|
57963
58176
|
import { mkdir as mkdir6, readdir as readdir5, readFile as readFile6, rm as rm4, writeFile as writeFile5 } from "node:fs/promises";
|
|
57964
|
-
import { join as
|
|
58177
|
+
import { join as join32 } from "node:path";
|
|
57965
58178
|
function uniqueName(taken, filename) {
|
|
57966
58179
|
if (!taken.has(filename)) {
|
|
57967
58180
|
taken.add(filename);
|
|
@@ -58009,19 +58222,19 @@ async function syncSpecAttachmentsDir(specId, projectId) {
|
|
|
58009
58222
|
for (const a of rows) {
|
|
58010
58223
|
let bytes;
|
|
58011
58224
|
try {
|
|
58012
|
-
bytes = await readFile6(
|
|
58225
|
+
bytes = await readFile6(join32(uploadDir(), a.storageKey));
|
|
58013
58226
|
} catch {
|
|
58014
58227
|
continue;
|
|
58015
58228
|
}
|
|
58016
58229
|
const filename = uniqueName(taken, a.filename);
|
|
58017
|
-
const path =
|
|
58230
|
+
const path = join32(dir2, filename);
|
|
58018
58231
|
await writeFile5(path, bytes, { mode: 384 });
|
|
58019
58232
|
items.push({ filename, mimeType: a.mimeType, size: a.size, path });
|
|
58020
58233
|
}
|
|
58021
|
-
await writeFile5(
|
|
58234
|
+
await writeFile5(join32(dir2, INDEX), renderIndex(specId, items), { mode: 384 });
|
|
58022
58235
|
const keep = /* @__PURE__ */ new Set([INDEX, ...items.map((a) => a.filename)]);
|
|
58023
58236
|
for (const name2 of await readdir5(dir2)) {
|
|
58024
|
-
if (!keep.has(name2)) await rm4(
|
|
58237
|
+
if (!keep.has(name2)) await rm4(join32(dir2, name2), { recursive: true, force: true }).catch(() => {
|
|
58025
58238
|
});
|
|
58026
58239
|
}
|
|
58027
58240
|
return items;
|
|
@@ -58040,7 +58253,7 @@ var init_spec_attachment_dir = __esm({
|
|
|
58040
58253
|
init_local_binding();
|
|
58041
58254
|
init_session_id();
|
|
58042
58255
|
init_uploads();
|
|
58043
|
-
specAttachmentsDir = (repoDir, sessionId2) =>
|
|
58256
|
+
specAttachmentsDir = (repoDir, sessionId2) => join32(repoDir, ".worktrees", ".attachments", sessionId2);
|
|
58044
58257
|
INDEX = "INDEX.md";
|
|
58045
58258
|
humanSize = (n2) => n2 >= 1024 * 1024 ? `${(n2 / 1024 / 1024).toFixed(1)} MB` : n2 >= 1024 ? `${Math.round(n2 / 1024)} KB` : `${n2} B`;
|
|
58046
58259
|
}
|
|
@@ -58435,7 +58648,7 @@ var init_runtime_defaults2 = __esm({
|
|
|
58435
58648
|
});
|
|
58436
58649
|
|
|
58437
58650
|
// src/services/agent-token.ts
|
|
58438
|
-
import { randomBytes as randomBytes2, createHash as
|
|
58651
|
+
import { randomBytes as randomBytes2, createHash as createHash13, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
58439
58652
|
function toAgentTokenView(t) {
|
|
58440
58653
|
return {
|
|
58441
58654
|
id: t.id,
|
|
@@ -58522,7 +58735,7 @@ var init_agent_token = __esm({
|
|
|
58522
58735
|
"src/services/agent-token.ts"() {
|
|
58523
58736
|
"use strict";
|
|
58524
58737
|
init_db();
|
|
58525
|
-
hash2 = (token) =>
|
|
58738
|
+
hash2 = (token) => createHash13("sha256").update(token).digest("hex");
|
|
58526
58739
|
AGENT_CREATOR_PREFIX = "agent:";
|
|
58527
58740
|
}
|
|
58528
58741
|
});
|
|
@@ -58851,11 +59064,11 @@ var init_project_config = __esm({
|
|
|
58851
59064
|
|
|
58852
59065
|
// src/services/limits.ts
|
|
58853
59066
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
58854
|
-
import { readFileSync as
|
|
59067
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
58855
59068
|
import { homedir as homedir6, userInfo } from "node:os";
|
|
58856
|
-
import { join as
|
|
59069
|
+
import { join as join34 } from "node:path";
|
|
58857
59070
|
function credsFile() {
|
|
58858
|
-
return
|
|
59071
|
+
return join34(effectiveStr("CLAUDE_CONFIG_DIR") ?? join34(homedir6(), ".claude"), ".credentials.json");
|
|
58859
59072
|
}
|
|
58860
59073
|
function keychainAccessToken(run6 = securityRun, account = osAccount()) {
|
|
58861
59074
|
const base3 = ["find-generic-password", "-s", "Claude Code-credentials"];
|
|
@@ -58874,7 +59087,7 @@ function readAccessToken() {
|
|
|
58874
59087
|
if (tok) return tok;
|
|
58875
59088
|
}
|
|
58876
59089
|
try {
|
|
58877
|
-
return JSON.parse(
|
|
59090
|
+
return JSON.parse(readFileSync18(credsFile(), "utf8"))?.claudeAiOauth?.accessToken ?? null;
|
|
58878
59091
|
} catch {
|
|
58879
59092
|
return null;
|
|
58880
59093
|
}
|
|
@@ -58958,7 +59171,7 @@ var init_limits = __esm({
|
|
|
58958
59171
|
// src/services/codex-limits.ts
|
|
58959
59172
|
import { open as open3, readdir as readdir6, stat as stat6 } from "node:fs/promises";
|
|
58960
59173
|
import { homedir as homedir7 } from "node:os";
|
|
58961
|
-
import { join as
|
|
59174
|
+
import { join as join35 } from "node:path";
|
|
58962
59175
|
function windowLabel(minutes) {
|
|
58963
59176
|
if (minutes === 300) return "Sesi 5 jam";
|
|
58964
59177
|
if (minutes === 10080) return "Mingguan";
|
|
@@ -58989,7 +59202,7 @@ async function recentRollouts(dir2) {
|
|
|
58989
59202
|
return [];
|
|
58990
59203
|
}
|
|
58991
59204
|
const stamped = await Promise.all(entries4.map(async (rel) => {
|
|
58992
|
-
const full =
|
|
59205
|
+
const full = join35(dir2, rel);
|
|
58993
59206
|
try {
|
|
58994
59207
|
return { full, mtime: (await stat6(full)).mtimeMs };
|
|
58995
59208
|
} catch {
|
|
@@ -59069,7 +59282,7 @@ var init_codex_limits = __esm({
|
|
|
59069
59282
|
MAX_FILES = 8;
|
|
59070
59283
|
cache2 = null;
|
|
59071
59284
|
freshUntil2 = 0;
|
|
59072
|
-
codexSessionsDir = () =>
|
|
59285
|
+
codexSessionsDir = () => join35(process.env.CODEX_HOME ?? join35(homedir7(), ".codex"), "sessions");
|
|
59073
59286
|
UNAVAILABLE = { status: "unavailable", windows: [], fetchedAt: null, plan: null };
|
|
59074
59287
|
severityOf = (pct3) => pct3 >= 90 ? "critical" : pct3 >= 70 ? "warning" : "normal";
|
|
59075
59288
|
}
|
|
@@ -59077,9 +59290,9 @@ var init_codex_limits = __esm({
|
|
|
59077
59290
|
|
|
59078
59291
|
// src/services/antigravity-limits.ts
|
|
59079
59292
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
59080
|
-
import { readFileSync as
|
|
59293
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
59081
59294
|
import { homedir as homedir8 } from "node:os";
|
|
59082
|
-
import { join as
|
|
59295
|
+
import { join as join36 } from "node:path";
|
|
59083
59296
|
function readAgyToken(run6 = securityRun2) {
|
|
59084
59297
|
try {
|
|
59085
59298
|
let raw = run6(["find-generic-password", "-s", "gemini", "-a", "antigravity", "-w"]).trim();
|
|
@@ -59095,7 +59308,7 @@ function readAgyToken(run6 = securityRun2) {
|
|
|
59095
59308
|
}
|
|
59096
59309
|
function defaultProject() {
|
|
59097
59310
|
try {
|
|
59098
|
-
return
|
|
59311
|
+
return readFileSync19(join36(agyHome(), "cache", "default_project_id.txt"), "utf8").trim() || null;
|
|
59099
59312
|
} catch {
|
|
59100
59313
|
return null;
|
|
59101
59314
|
}
|
|
@@ -59186,7 +59399,7 @@ var init_antigravity_limits = __esm({
|
|
|
59186
59399
|
freshUntil3 = 0;
|
|
59187
59400
|
UNAVAILABLE2 = { status: "unavailable", windows: [], fetchedAt: null };
|
|
59188
59401
|
securityRun2 = (args) => execFileSync7("security", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
59189
|
-
agyHome = () =>
|
|
59402
|
+
agyHome = () => join36(homedir8(), ".gemini", "antigravity-cli");
|
|
59190
59403
|
severityOf2 = (pct3) => pct3 >= 90 ? "critical" : pct3 >= 70 ? "warning" : "normal";
|
|
59191
59404
|
prodDeps = {
|
|
59192
59405
|
platform: process.platform,
|
|
@@ -59285,7 +59498,7 @@ var init_budget = __esm({
|
|
|
59285
59498
|
});
|
|
59286
59499
|
|
|
59287
59500
|
// src/services/session-dialog.ts
|
|
59288
|
-
import { createHash as
|
|
59501
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
59289
59502
|
function readSessionDialog(io) {
|
|
59290
59503
|
const text2 = io.capture();
|
|
59291
59504
|
const s2 = answerable(text2);
|
|
@@ -59341,7 +59554,7 @@ var init_session_dialog2 = __esm({
|
|
|
59341
59554
|
init_tui_dialog();
|
|
59342
59555
|
init_pty();
|
|
59343
59556
|
DIALOG_CHUNK_MS = 50;
|
|
59344
|
-
screenHashOf = (paneText2) =>
|
|
59557
|
+
screenHashOf = (paneText2) => createHash14("sha256").update(dialogKey(paneText2)).digest("hex").slice(0, 16);
|
|
59345
59558
|
answerable = (paneText2) => {
|
|
59346
59559
|
const s2 = readDialogScreen(paneText2);
|
|
59347
59560
|
if (s2?.kind !== "question") return null;
|
|
@@ -59644,8 +59857,8 @@ var init_prompt2 = __esm({
|
|
|
59644
59857
|
});
|
|
59645
59858
|
|
|
59646
59859
|
// src/services/lead/verdict.ts
|
|
59647
|
-
import { existsSync as
|
|
59648
|
-
import { isAbsolute as isAbsolute11, resolve as resolvePath2, relative as
|
|
59860
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
59861
|
+
import { isAbsolute as isAbsolute11, resolve as resolvePath2, relative as relative7 } from "node:path";
|
|
59649
59862
|
function extractJsonBlock(raw) {
|
|
59650
59863
|
const re = /```json\s*\n([\s\S]*?)```/g;
|
|
59651
59864
|
let last2 = null;
|
|
@@ -59678,9 +59891,9 @@ function keepExistingRefs(refs2, repoDir) {
|
|
|
59678
59891
|
if (!repoDir) continue;
|
|
59679
59892
|
if (isAbsolute11(ref)) continue;
|
|
59680
59893
|
const full = resolvePath2(repoDir, ref);
|
|
59681
|
-
const rel =
|
|
59894
|
+
const rel = relative7(repoDir, full);
|
|
59682
59895
|
if (rel.startsWith("..")) continue;
|
|
59683
|
-
if (
|
|
59896
|
+
if (existsSync18(full)) out4.push(ref);
|
|
59684
59897
|
}
|
|
59685
59898
|
return [...new Set(out4)];
|
|
59686
59899
|
}
|
|
@@ -60870,9 +61083,9 @@ var init_queue = __esm({
|
|
|
60870
61083
|
});
|
|
60871
61084
|
|
|
60872
61085
|
// src/services/git-auth.ts
|
|
60873
|
-
import { chmodSync as
|
|
61086
|
+
import { chmodSync as chmodSync9, mkdirSync as mkdirSync15, writeFileSync as writeFileSync17 } from "node:fs";
|
|
60874
61087
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
60875
|
-
import { dirname as
|
|
61088
|
+
import { dirname as dirname15 } from "node:path";
|
|
60876
61089
|
function httpsRemote(remote) {
|
|
60877
61090
|
const scp = /^[\w.-]+@github\.com:(.+)$/.exec(remote);
|
|
60878
61091
|
if (scp) return `https://github.com/${scp[1]}`;
|
|
@@ -60882,9 +61095,9 @@ function httpsRemote(remote) {
|
|
|
60882
61095
|
}
|
|
60883
61096
|
function gitAuthEnv(token) {
|
|
60884
61097
|
const path = gitAskpassPath();
|
|
60885
|
-
|
|
60886
|
-
|
|
60887
|
-
|
|
61098
|
+
mkdirSync15(dirname15(path), { recursive: true, mode: 448 });
|
|
61099
|
+
writeFileSync17(path, ASKPASS_TOKEN, { mode: 448 });
|
|
61100
|
+
chmodSync9(path, 448);
|
|
60888
61101
|
return { GIT_ASKPASS: path, GIT_TERMINAL_PROMPT: "0", HANOMAN_GIT_TOKEN: token };
|
|
60889
61102
|
}
|
|
60890
61103
|
var gitAskpassPath, ASKPASS_TOKEN, isGithubHttps;
|
|
@@ -63884,7 +64097,7 @@ var require_websocket2 = __commonJS({
|
|
|
63884
64097
|
var http = __require("http");
|
|
63885
64098
|
var net = __require("net");
|
|
63886
64099
|
var tls = __require("tls");
|
|
63887
|
-
var { randomBytes: randomBytes12, createHash:
|
|
64100
|
+
var { randomBytes: randomBytes12, createHash: createHash24 } = __require("crypto");
|
|
63888
64101
|
var { Duplex, Readable } = __require("stream");
|
|
63889
64102
|
var { URL: URL2 } = __require("url");
|
|
63890
64103
|
var PerMessageDeflate2 = require_permessage_deflate2();
|
|
@@ -64552,7 +64765,7 @@ var require_websocket2 = __commonJS({
|
|
|
64552
64765
|
abortHandshake(websocket2, socket2, "Invalid Upgrade header");
|
|
64553
64766
|
return;
|
|
64554
64767
|
}
|
|
64555
|
-
const digest3 =
|
|
64768
|
+
const digest3 = createHash24("sha1").update(key + GUID).digest("base64");
|
|
64556
64769
|
if (res.headers["sec-websocket-accept"] !== digest3) {
|
|
64557
64770
|
abortHandshake(websocket2, socket2, "Invalid Sec-WebSocket-Accept header");
|
|
64558
64771
|
return;
|
|
@@ -64921,7 +65134,7 @@ var require_websocket_server = __commonJS({
|
|
|
64921
65134
|
var EventEmitter = __require("events");
|
|
64922
65135
|
var http = __require("http");
|
|
64923
65136
|
var { Duplex } = __require("stream");
|
|
64924
|
-
var { createHash:
|
|
65137
|
+
var { createHash: createHash24 } = __require("crypto");
|
|
64925
65138
|
var extension2 = require_extension();
|
|
64926
65139
|
var PerMessageDeflate2 = require_permessage_deflate2();
|
|
64927
65140
|
var subprotocol2 = require_subprotocol();
|
|
@@ -65228,7 +65441,7 @@ var require_websocket_server = __commonJS({
|
|
|
65228
65441
|
);
|
|
65229
65442
|
}
|
|
65230
65443
|
if (this._state > RUNNING) return abortHandshake(socket2, 503);
|
|
65231
|
-
const digest3 =
|
|
65444
|
+
const digest3 = createHash24("sha1").update(key + GUID).digest("base64");
|
|
65232
65445
|
const headers = [
|
|
65233
65446
|
"HTTP/1.1 101 Switching Protocols",
|
|
65234
65447
|
"Upgrade: websocket",
|
|
@@ -65524,16 +65737,16 @@ var init_client2 = __esm({
|
|
|
65524
65737
|
});
|
|
65525
65738
|
|
|
65526
65739
|
// src/services/logs/spool.ts
|
|
65527
|
-
import { appendFile, mkdir as mkdir7, readFile as
|
|
65528
|
-
import { join as
|
|
65740
|
+
import { appendFile, mkdir as mkdir7, readFile as readFile8, readdir as readdir7, rm as rm5, stat as stat7 } from "node:fs/promises";
|
|
65741
|
+
import { join as join37 } from "node:path";
|
|
65529
65742
|
function spoolDir(lane) {
|
|
65530
|
-
return
|
|
65743
|
+
return join37(resolveDataDirs().home, "log-spool", lane);
|
|
65531
65744
|
}
|
|
65532
65745
|
async function currentSegment(lane) {
|
|
65533
65746
|
const dir2 = spoolDir(lane);
|
|
65534
65747
|
await mkdir7(dir2, { recursive: true, mode: 448 });
|
|
65535
65748
|
if (currentFile && currentBytes < LOG_SPOOL_SEGMENT_BYTES) return currentFile;
|
|
65536
|
-
currentFile =
|
|
65749
|
+
currentFile = join37(dir2, `${Date.now()}-${++segCounter}.ndjson`);
|
|
65537
65750
|
currentBytes = 0;
|
|
65538
65751
|
return currentFile;
|
|
65539
65752
|
}
|
|
@@ -65555,8 +65768,8 @@ async function readSpoolSegments(lane) {
|
|
|
65555
65768
|
const files2 = names.filter((n2) => n2.endsWith(".ndjson")).sort();
|
|
65556
65769
|
const out4 = [];
|
|
65557
65770
|
for (const name2 of files2) {
|
|
65558
|
-
const file =
|
|
65559
|
-
const raw = await
|
|
65771
|
+
const file = join37(dir2, name2);
|
|
65772
|
+
const raw = await readFile8(file, "utf8").catch(() => "");
|
|
65560
65773
|
const entries4 = raw.split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
65561
65774
|
out4.push({ file, entries: entries4 });
|
|
65562
65775
|
}
|
|
@@ -66220,8 +66433,8 @@ var init_git_token = __esm({
|
|
|
66220
66433
|
|
|
66221
66434
|
// src/services/project-checkout.ts
|
|
66222
66435
|
import { execFile as execFile14, spawnSync as spawnSync4 } from "node:child_process";
|
|
66223
|
-
import { existsSync as
|
|
66224
|
-
import { join as
|
|
66436
|
+
import { existsSync as existsSync19, readdirSync as readdirSync10, rmSync as rmSync10 } from "node:fs";
|
|
66437
|
+
import { join as join38 } from "node:path";
|
|
66225
66438
|
import { promisify as promisify13 } from "node:util";
|
|
66226
66439
|
async function runGit(args, o) {
|
|
66227
66440
|
const full = o.token ? ["-c", "credential.helper=", ...args] : args;
|
|
@@ -66243,8 +66456,8 @@ async function doClone(projectId, opts2) {
|
|
|
66243
66456
|
if (!project) throw new CheckoutError(`project "${projectId}" tidak ada`, "not-found");
|
|
66244
66457
|
if (!project.gitRemote) throw new CheckoutError(`project "${projectId}" tidak punya gitRemote untuk di-clone`, "no-remote");
|
|
66245
66458
|
const remote = project.gitRemote;
|
|
66246
|
-
const dest = opts2.dir ??
|
|
66247
|
-
if (
|
|
66459
|
+
const dest = opts2.dir ?? join38(checkoutRoot(), slug(project.id));
|
|
66460
|
+
if (existsSync19(dest) && !isEmptyDir(dest)) {
|
|
66248
66461
|
const origin = originOf2(dest);
|
|
66249
66462
|
if (origin && sameRemote(origin, remote)) {
|
|
66250
66463
|
await setBinding(projectId, dest);
|
|
@@ -66260,7 +66473,7 @@ async function doClone(projectId, opts2) {
|
|
|
66260
66473
|
timeout: CLONE_TIMEOUT_MS
|
|
66261
66474
|
});
|
|
66262
66475
|
if (!res.ok) {
|
|
66263
|
-
|
|
66476
|
+
rmSync10(dest, { recursive: true, force: true });
|
|
66264
66477
|
const auth = AUTH_RE.test(res.stderr);
|
|
66265
66478
|
throw new CheckoutError(
|
|
66266
66479
|
auth ? useAuth ? `token GitHub ditolak atau tanpa akses ke ${remote}` : `akses ke ${remote} ditolak: isi GITHUB_TOKEN di Settings atau hubungkan instance ini ke hub` : `git clone ${remote} gagal`,
|
|
@@ -66307,7 +66520,7 @@ var init_project_checkout = __esm({
|
|
|
66307
66520
|
CLONE_TIMEOUT_MS = 10 * 6e4;
|
|
66308
66521
|
FETCH_TIMEOUT_MS = 2 * 6e4;
|
|
66309
66522
|
AUTH_RE = /Authentication failed|could not read (Username|Password)|Invalid username or password|HTTP 40[13]|returned error: 40[13]|Repository not found|Permission denied \(publickey\)/i;
|
|
66310
|
-
checkoutRoot = () =>
|
|
66523
|
+
checkoutRoot = () => join38(resolveHome(), "projects");
|
|
66311
66524
|
slug = (id2) => id2.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
66312
66525
|
sameRemote = (a, b) => {
|
|
66313
66526
|
const norm5 = (u) => httpsRemote(u).replace(/\.git$/, "").replace(/\/+$/, "").toLowerCase();
|
|
@@ -66316,7 +66529,7 @@ var init_project_checkout = __esm({
|
|
|
66316
66529
|
redact = (text2, token) => token ? text2.split(token).join("***") : text2;
|
|
66317
66530
|
isEmptyDir = (dir2) => {
|
|
66318
66531
|
try {
|
|
66319
|
-
return
|
|
66532
|
+
return readdirSync10(dir2).length === 0;
|
|
66320
66533
|
} catch {
|
|
66321
66534
|
return false;
|
|
66322
66535
|
}
|
|
@@ -67085,7 +67298,7 @@ var init_approvals = __esm({
|
|
|
67085
67298
|
});
|
|
67086
67299
|
|
|
67087
67300
|
// src/services/telegram/gateway.ts
|
|
67088
|
-
import { createHash as
|
|
67301
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
67089
67302
|
var digestRaw, TelegramGateway;
|
|
67090
67303
|
var init_gateway = __esm({
|
|
67091
67304
|
"src/services/telegram/gateway.ts"() {
|
|
@@ -67094,7 +67307,7 @@ var init_gateway = __esm({
|
|
|
67094
67307
|
init_protocol();
|
|
67095
67308
|
init_runtime();
|
|
67096
67309
|
init_typing();
|
|
67097
|
-
digestRaw = (update2) =>
|
|
67310
|
+
digestRaw = (update2) => createHash15("sha256").update(JSON.stringify(update2)).digest("hex");
|
|
67098
67311
|
TelegramGateway = class {
|
|
67099
67312
|
constructor(deps) {
|
|
67100
67313
|
this.deps = deps;
|
|
@@ -67418,14 +67631,14 @@ var init_engine_command = __esm({
|
|
|
67418
67631
|
});
|
|
67419
67632
|
|
|
67420
67633
|
// src/services/telegram/session.ts
|
|
67421
|
-
import { createHash as
|
|
67634
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
67422
67635
|
var chatHash, telegramOperatorSessionId, formatTelegramTurn, TelegramSessionCoordinator;
|
|
67423
67636
|
var init_session = __esm({
|
|
67424
67637
|
"src/services/telegram/session.ts"() {
|
|
67425
67638
|
"use strict";
|
|
67426
67639
|
init_src2();
|
|
67427
67640
|
init_engine_command();
|
|
67428
|
-
chatHash = (chatId) =>
|
|
67641
|
+
chatHash = (chatId) => createHash16("sha256").update(chatId).digest("hex").slice(0, 16);
|
|
67429
67642
|
telegramOperatorSessionId = (chatId) => `telegram-${chatHash(chatId)}`;
|
|
67430
67643
|
formatTelegramTurn = (input) => `[Telegram update ${input.updateId} \xB7 chat ${input.chatId} \xB7 kind ${input.kind}]
|
|
67431
67644
|
${input.text}`;
|
|
@@ -67822,7 +68035,7 @@ __export(bootstrap_exports, {
|
|
|
67822
68035
|
reloadTelegramGateway: () => reloadTelegramGateway,
|
|
67823
68036
|
telegramSessionDeps: () => telegramSessionDeps
|
|
67824
68037
|
});
|
|
67825
|
-
import { mkdirSync as
|
|
68038
|
+
import { mkdirSync as mkdirSync16 } from "node:fs";
|
|
67826
68039
|
function parseTelegramAllowedUserIds(raw) {
|
|
67827
68040
|
const ids = raw.split(/[\s,]+/).filter(Boolean);
|
|
67828
68041
|
if (!ids.length) throw new Error("Telegram allowlist wajib berisi numeric user id");
|
|
@@ -67847,7 +68060,7 @@ function telegramSessionDeps(input) {
|
|
|
67847
68060
|
home: resolveHome(),
|
|
67848
68061
|
apiBase: input.apiBase,
|
|
67849
68062
|
agentToken: input.agentToken,
|
|
67850
|
-
ensureDir: (path) =>
|
|
68063
|
+
ensureDir: (path) => mkdirSync16(path, { recursive: true })
|
|
67851
68064
|
};
|
|
67852
68065
|
}
|
|
67853
68066
|
async function productionFactory(input) {
|
|
@@ -68069,17 +68282,17 @@ var init_codex_version = __esm({
|
|
|
68069
68282
|
});
|
|
68070
68283
|
|
|
68071
68284
|
// src/services/agent-tool-catalog.ts
|
|
68072
|
-
import { readFileSync as
|
|
68285
|
+
import { readFileSync as readFileSync27 } from "node:fs";
|
|
68073
68286
|
import { homedir as homedir13 } from "node:os";
|
|
68074
|
-
import { join as
|
|
68287
|
+
import { join as join51 } from "node:path";
|
|
68075
68288
|
function mcpServerNames(repoDir) {
|
|
68076
68289
|
const names = [];
|
|
68077
|
-
const claudeJson = readJson2(
|
|
68290
|
+
const claudeJson = readJson2(join51(home(), ".claude.json"));
|
|
68078
68291
|
names.push(...serversOf(claudeJson));
|
|
68079
68292
|
if (repoDir) {
|
|
68080
68293
|
const projects = claudeJson?.projects;
|
|
68081
68294
|
if (projects && typeof projects === "object") names.push(...serversOf(projects[repoDir]));
|
|
68082
|
-
names.push(...serversOf(readJson2(
|
|
68295
|
+
names.push(...serversOf(readJson2(join51(repoDir, ".mcp.json"))));
|
|
68083
68296
|
}
|
|
68084
68297
|
names.push(...codexServers());
|
|
68085
68298
|
return [...new Set(names.filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
@@ -68095,7 +68308,7 @@ var init_agent_tool_catalog = __esm({
|
|
|
68095
68308
|
home = () => process.env.HOME || homedir13();
|
|
68096
68309
|
readJson2 = (path) => {
|
|
68097
68310
|
try {
|
|
68098
|
-
return JSON.parse(
|
|
68311
|
+
return JSON.parse(readFileSync27(path, "utf8"));
|
|
68099
68312
|
} catch {
|
|
68100
68313
|
return null;
|
|
68101
68314
|
}
|
|
@@ -68108,7 +68321,7 @@ var init_agent_tool_catalog = __esm({
|
|
|
68108
68321
|
codexServers = () => {
|
|
68109
68322
|
let text2;
|
|
68110
68323
|
try {
|
|
68111
|
-
text2 =
|
|
68324
|
+
text2 = readFileSync27(join51(home(), ".codex", "config.toml"), "utf8");
|
|
68112
68325
|
} catch {
|
|
68113
68326
|
return [];
|
|
68114
68327
|
}
|
|
@@ -68124,7 +68337,7 @@ var init_agent_tool_catalog = __esm({
|
|
|
68124
68337
|
});
|
|
68125
68338
|
|
|
68126
68339
|
// src/services/builtin-agents.ts
|
|
68127
|
-
import { createHash as
|
|
68340
|
+
import { createHash as createHash20 } from "node:crypto";
|
|
68128
68341
|
async function seedBuiltinAgents() {
|
|
68129
68342
|
try {
|
|
68130
68343
|
const setting = await getSetting();
|
|
@@ -68226,7 +68439,7 @@ var init_builtin_agents2 = __esm({
|
|
|
68226
68439
|
init_settings3();
|
|
68227
68440
|
init_tombstone();
|
|
68228
68441
|
init_sync_notify();
|
|
68229
|
-
digest2 = (parts) =>
|
|
68442
|
+
digest2 = (parts) => createHash20("sha256").update(parts.join(" ")).digest("hex").slice(0, 16);
|
|
68230
68443
|
legacyFingerprint = (name2, description, instructions, tools) => digest2([name2, description, instructions, [...tools].join(",")]);
|
|
68231
68444
|
mentionPart = (v) => {
|
|
68232
68445
|
const m = mentionsOf(v);
|
|
@@ -71726,9 +71939,9 @@ import Fastify from "fastify";
|
|
|
71726
71939
|
import fastifyStatic from "@fastify/static";
|
|
71727
71940
|
import websocket from "@fastify/websocket";
|
|
71728
71941
|
import cookie from "@fastify/cookie";
|
|
71729
|
-
import { fileURLToPath as
|
|
71942
|
+
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
71730
71943
|
import { dirname as dirname19 } from "node:path";
|
|
71731
|
-
import { existsSync as
|
|
71944
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
71732
71945
|
|
|
71733
71946
|
// src/web-dir.ts
|
|
71734
71947
|
import { resolve as resolve11 } from "node:path";
|
|
@@ -71782,7 +71995,7 @@ async function agent_doc_default(app3, opts2) {
|
|
|
71782
71995
|
init_config2();
|
|
71783
71996
|
init_db();
|
|
71784
71997
|
init_uploads();
|
|
71785
|
-
import { createHash as
|
|
71998
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
71786
71999
|
|
|
71787
72000
|
// src/services/upload-pipeline.ts
|
|
71788
72001
|
init_file_type();
|
|
@@ -71792,7 +72005,7 @@ init_uploads();
|
|
|
71792
72005
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
71793
72006
|
import { spawn as spawn2 } from "node:child_process";
|
|
71794
72007
|
import { mkdir as mkdir3, rename, unlink as unlink3, writeFile as writeFile3 } from "node:fs/promises";
|
|
71795
|
-
import { basename as basename7, isAbsolute as isAbsolute9, join as
|
|
72008
|
+
import { basename as basename7, isAbsolute as isAbsolute9, join as join23 } from "node:path";
|
|
71796
72009
|
import sharp from "sharp";
|
|
71797
72010
|
var UPLOAD_LIMITS = {
|
|
71798
72011
|
fileBytes: 5 * 1024 * 1024,
|
|
@@ -71866,16 +72079,16 @@ function safeFilename2(input, extension2) {
|
|
|
71866
72079
|
}
|
|
71867
72080
|
async function commitToStorage(buffer, extension2, deps, beforePromote) {
|
|
71868
72081
|
const storageDir = deps.storageDir ?? uploadDir();
|
|
71869
|
-
const quarantineDir =
|
|
72082
|
+
const quarantineDir = join23(storageDir, ".quarantine");
|
|
71870
72083
|
await mkdir3(quarantineDir, { recursive: true, mode: 448 });
|
|
71871
72084
|
await mkdir3(storageDir, { recursive: true, mode: 448 });
|
|
71872
|
-
const quarantine =
|
|
72085
|
+
const quarantine = join23(quarantineDir, `${randomUUID6()}.upload`);
|
|
71873
72086
|
const storageKey = `${randomUUID6()}${extension2}`;
|
|
71874
72087
|
await writeFile3(quarantine, buffer, { mode: 384, flag: "wx" });
|
|
71875
72088
|
try {
|
|
71876
72089
|
await timeout((deps.scanner ?? scannerFromEnv)(quarantine), UPLOAD_LIMITS.scanMs, "UPLOAD_SCAN");
|
|
71877
72090
|
await beforePromote?.();
|
|
71878
|
-
await rename(quarantine,
|
|
72091
|
+
await rename(quarantine, join23(storageDir, storageKey));
|
|
71879
72092
|
} catch (error) {
|
|
71880
72093
|
await unlink3(quarantine).catch(() => {
|
|
71881
72094
|
});
|
|
@@ -72277,7 +72490,7 @@ async function addQaAttachments(report, owner, files2) {
|
|
|
72277
72490
|
continue;
|
|
72278
72491
|
}
|
|
72279
72492
|
try {
|
|
72280
|
-
const sha256 =
|
|
72493
|
+
const sha256 = createHash10("sha256").update(await readUpload(safe3.storageKey)).digest("hex");
|
|
72281
72494
|
const row = await prisma.qaAttachment.create({ data: {
|
|
72282
72495
|
reportId: report.id,
|
|
72283
72496
|
projectId: report.projectId,
|
|
@@ -72331,7 +72544,7 @@ init_src();
|
|
|
72331
72544
|
init_safe_repo_path();
|
|
72332
72545
|
import { execFile as execFile3 } from "node:child_process";
|
|
72333
72546
|
import { promisify as promisify2 } from "node:util";
|
|
72334
|
-
import { existsSync as
|
|
72547
|
+
import { existsSync as existsSync13, rmSync as rmSync8 } from "node:fs";
|
|
72335
72548
|
import { resolve as resolve14 } from "node:path";
|
|
72336
72549
|
var exec = promisify2(execFile3);
|
|
72337
72550
|
async function listRepoDocs(repoDir) {
|
|
@@ -72356,12 +72569,12 @@ function docsDirOf(repoDir) {
|
|
|
72356
72569
|
}
|
|
72357
72570
|
function resolveIndex(repoDir, docsDir) {
|
|
72358
72571
|
const rel = `${docsDir}/README.md`;
|
|
72359
|
-
return
|
|
72572
|
+
return existsSync13(resolve14(repoDir, rel)) ? rel : "";
|
|
72360
72573
|
}
|
|
72361
72574
|
var catOf = (rel) => rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : ".";
|
|
72362
72575
|
var nameOf = (rel) => rel.includes("/") ? rel.slice(rel.lastIndexOf("/") + 1) : rel;
|
|
72363
72576
|
async function scanRepoDocs(repoDir) {
|
|
72364
|
-
if (!repoDir || !
|
|
72577
|
+
if (!repoDir || !existsSync13(repoDir)) return { coverage: 0, tree: [] };
|
|
72365
72578
|
const files2 = await listRepoDocs(repoDir);
|
|
72366
72579
|
const docsDir = docsDirOf(repoDir);
|
|
72367
72580
|
const index = resolveIndex(repoDir, docsDir);
|
|
@@ -72409,8 +72622,8 @@ function writeDocFile(repoDir, rel, content) {
|
|
|
72409
72622
|
}
|
|
72410
72623
|
function deleteDocFile(repoDir, rel) {
|
|
72411
72624
|
const abs = docAbsPath(repoDir, rel);
|
|
72412
|
-
if (!
|
|
72413
|
-
|
|
72625
|
+
if (!existsSync13(abs)) return false;
|
|
72626
|
+
rmSync8(abs);
|
|
72414
72627
|
return true;
|
|
72415
72628
|
}
|
|
72416
72629
|
|
|
@@ -72728,7 +72941,7 @@ async function projects_default(app3) {
|
|
|
72728
72941
|
init_src();
|
|
72729
72942
|
init_src2();
|
|
72730
72943
|
init_integrate();
|
|
72731
|
-
import { existsSync as
|
|
72944
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
72732
72945
|
import { basename as basename9 } from "node:path";
|
|
72733
72946
|
|
|
72734
72947
|
// src/services/worktree-delete.ts
|
|
@@ -72747,7 +72960,7 @@ init_spec_review2();
|
|
|
72747
72960
|
init_safe_repo_path();
|
|
72748
72961
|
import { execFile as execFile7 } from "node:child_process";
|
|
72749
72962
|
import { promisify as promisify6 } from "node:util";
|
|
72750
|
-
import { existsSync as
|
|
72963
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
72751
72964
|
import { readdir as readdir3 } from "node:fs/promises";
|
|
72752
72965
|
import { resolve as resolve16 } from "node:path";
|
|
72753
72966
|
var exec5 = promisify6(execFile7);
|
|
@@ -72760,7 +72973,7 @@ function repoAbsPath(repoDir, rel, allowMissingFinal = false) {
|
|
|
72760
72973
|
return resolve16(repoDir, rel);
|
|
72761
72974
|
}
|
|
72762
72975
|
async function listRepoTree(repoDir, ref = "") {
|
|
72763
|
-
if (!repoDir || !
|
|
72976
|
+
if (!repoDir || !existsSync14(repoDir)) return [];
|
|
72764
72977
|
try {
|
|
72765
72978
|
const { stdout } = ref ? await exec5("git", ["ls-tree", "-r", "--name-only", "-z", ref], { cwd: repoDir, ...GIT4 }) : await exec5("git", ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], { cwd: repoDir, ...GIT4 });
|
|
72766
72979
|
return [...new Set(splitZ2(stdout))].sort();
|
|
@@ -72770,7 +72983,7 @@ async function listRepoTree(repoDir, ref = "") {
|
|
|
72770
72983
|
}
|
|
72771
72984
|
async function listIgnoredEntries(repoDir) {
|
|
72772
72985
|
const empty = { files: [], dirs: [] };
|
|
72773
|
-
if (!repoDir || !
|
|
72986
|
+
if (!repoDir || !existsSync14(repoDir)) return empty;
|
|
72774
72987
|
try {
|
|
72775
72988
|
const { stdout } = await exec5(
|
|
72776
72989
|
"git",
|
|
@@ -72823,7 +73036,7 @@ async function currentBranch(repoDir) {
|
|
|
72823
73036
|
}
|
|
72824
73037
|
async function repoStatus(repoDir) {
|
|
72825
73038
|
const empty = { branch: "", ahead: 0, behind: 0, staged: [], unstaged: [], untracked: [], clean: true };
|
|
72826
|
-
if (!repoDir || !
|
|
73039
|
+
if (!repoDir || !existsSync14(repoDir)) return empty;
|
|
72827
73040
|
try {
|
|
72828
73041
|
const { stdout } = await exec5("git", ["status", "--porcelain=v1", "-z", "-b"], { cwd: repoDir, ...GIT4 });
|
|
72829
73042
|
const parts = stdout.split("\0");
|
|
@@ -72856,7 +73069,7 @@ async function repoStatus(repoDir) {
|
|
|
72856
73069
|
}
|
|
72857
73070
|
}
|
|
72858
73071
|
async function listStashes(repoDir) {
|
|
72859
|
-
if (!repoDir || !
|
|
73072
|
+
if (!repoDir || !existsSync14(repoDir)) return [];
|
|
72860
73073
|
try {
|
|
72861
73074
|
const { stdout } = await exec5("git", ["stash", "list", `--format=%gd${US}%s${US}%aI`], { cwd: repoDir, ...GIT4 });
|
|
72862
73075
|
return stdout.split("\n").filter(Boolean).map((line) => {
|
|
@@ -72868,7 +73081,7 @@ async function listStashes(repoDir) {
|
|
|
72868
73081
|
}
|
|
72869
73082
|
}
|
|
72870
73083
|
async function listGraph(repoDir, limit = 200, opts2 = {}) {
|
|
72871
|
-
if (!repoDir || !
|
|
73084
|
+
if (!repoDir || !existsSync14(repoDir)) return { commits: [], current: "", total: 0 };
|
|
72872
73085
|
try {
|
|
72873
73086
|
const fmt = ["%H", "%P", "%an", "%aI", "%s", "%D"].join(US);
|
|
72874
73087
|
const refArgs = opts2.branches?.length ? ["--end-of-options", ...opts2.branches] : [
|
|
@@ -72938,7 +73151,7 @@ async function changedOf(repoDir, sha2) {
|
|
|
72938
73151
|
}
|
|
72939
73152
|
async function compareCommits(repoDir, from, to) {
|
|
72940
73153
|
const empty = { from, to, changed: [] };
|
|
72941
|
-
if (!repoDir || !
|
|
73154
|
+
if (!repoDir || !existsSync14(repoDir)) return empty;
|
|
72942
73155
|
try {
|
|
72943
73156
|
const [num, name2] = await Promise.all([
|
|
72944
73157
|
exec5("git", ["diff", "--numstat", "-z", "--no-renames", "--end-of-options", from, to], { cwd: repoDir, ...GIT4 }),
|
|
@@ -72950,7 +73163,7 @@ async function compareCommits(repoDir, from, to) {
|
|
|
72950
73163
|
}
|
|
72951
73164
|
}
|
|
72952
73165
|
async function searchCommits(repoDir, q2, by = "all") {
|
|
72953
|
-
if (!repoDir || !
|
|
73166
|
+
if (!repoDir || !existsSync14(repoDir) || !q2) return [];
|
|
72954
73167
|
const run6 = async (extra) => {
|
|
72955
73168
|
try {
|
|
72956
73169
|
return (await exec5("git", ["log", "--all", "--format=%H", ...extra], { cwd: repoDir, ...GIT4 })).stdout.split("\n").filter(Boolean);
|
|
@@ -73197,7 +73410,7 @@ async function runGitOp(repoDir, op) {
|
|
|
73197
73410
|
}
|
|
73198
73411
|
}
|
|
73199
73412
|
async function workingStatus(repoDir) {
|
|
73200
|
-
if (!repoDir || !
|
|
73413
|
+
if (!repoDir || !existsSync14(repoDir)) return { branch: "", staged: [], unstaged: [] };
|
|
73201
73414
|
const [branch, staged, unstaged] = await Promise.all([
|
|
73202
73415
|
currentBranch(repoDir),
|
|
73203
73416
|
changedFiles(repoDir, ["--cached"]),
|
|
@@ -73206,7 +73419,7 @@ async function workingStatus(repoDir) {
|
|
|
73206
73419
|
return { branch, staged, unstaged };
|
|
73207
73420
|
}
|
|
73208
73421
|
async function workingFileDiff(repoDir, path, staged) {
|
|
73209
|
-
if (!repoDir || !
|
|
73422
|
+
if (!repoDir || !existsSync14(repoDir)) return null;
|
|
73210
73423
|
repoAbsPath(repoDir, path, true);
|
|
73211
73424
|
const changed = staged ? await changedFiles(repoDir, ["--cached"]) : await withTempIndex(repoDir, (env) => changedFiles(repoDir, [], env));
|
|
73212
73425
|
const cf = changed.find((c) => c.path === path);
|
|
@@ -73415,7 +73628,7 @@ async function deleteBranches(repoDir, names, opts2) {
|
|
|
73415
73628
|
import { execFile as execFile9 } from "node:child_process";
|
|
73416
73629
|
import { realpathSync as realpathSync8 } from "node:fs";
|
|
73417
73630
|
import { stat as stat5 } from "node:fs/promises";
|
|
73418
|
-
import { basename as basename8, dirname as
|
|
73631
|
+
import { basename as basename8, dirname as dirname13, join as join28, resolve as resolve17, sep as sep8 } from "node:path";
|
|
73419
73632
|
import { promisify as promisify8 } from "node:util";
|
|
73420
73633
|
var exec7 = promisify8(execFile9);
|
|
73421
73634
|
var GIT6 = { timeout: 6e4, maxBuffer: 1 << 24, encoding: "utf8" };
|
|
@@ -73456,20 +73669,20 @@ var real = (p3) => {
|
|
|
73456
73669
|
return realpathSync8(p3);
|
|
73457
73670
|
} catch {
|
|
73458
73671
|
const path = resolve17(p3);
|
|
73459
|
-
const parent =
|
|
73460
|
-
return parent === path ? path :
|
|
73672
|
+
const parent = dirname13(path);
|
|
73673
|
+
return parent === path ? path : join28(real(parent), basename8(path));
|
|
73461
73674
|
}
|
|
73462
73675
|
};
|
|
73463
73676
|
function hasWorktreeSession(path, sessionId2, sessions) {
|
|
73464
73677
|
const target3 = real(path);
|
|
73465
73678
|
return sessions.some((s2) => {
|
|
73466
73679
|
const current = real(s2.cwd);
|
|
73467
|
-
return s2.id === sessionId2 || current === target3 || current.startsWith(target3 +
|
|
73680
|
+
return s2.id === sessionId2 || current === target3 || current.startsWith(target3 + sep8);
|
|
73468
73681
|
});
|
|
73469
73682
|
}
|
|
73470
73683
|
async function bornAt(path) {
|
|
73471
73684
|
try {
|
|
73472
|
-
const st2 = await stat5(
|
|
73685
|
+
const st2 = await stat5(join28(path, ".git"));
|
|
73473
73686
|
const ms = st2.birthtimeMs > 0 ? st2.birthtimeMs : st2.mtimeMs;
|
|
73474
73687
|
return new Date(ms).toISOString();
|
|
73475
73688
|
} catch {
|
|
@@ -73494,7 +73707,7 @@ async function listWorktrees(repoDir, inputs) {
|
|
|
73494
73707
|
const rows = [];
|
|
73495
73708
|
for (const w of raw) {
|
|
73496
73709
|
const path = resolve17(w.path);
|
|
73497
|
-
if (path === trash || path.startsWith(trash +
|
|
73710
|
+
if (path === trash || path.startsWith(trash + sep8)) continue;
|
|
73498
73711
|
const name2 = basename8(path);
|
|
73499
73712
|
const deletable = path !== baseReal && path !== mainPath;
|
|
73500
73713
|
const spec = inputs.specs.get(name2);
|
|
@@ -73681,12 +73894,12 @@ async function recordHeadSha(specId, worktree, read2 = readHead) {
|
|
|
73681
73894
|
init_local_binding();
|
|
73682
73895
|
|
|
73683
73896
|
// src/services/session-worktree.ts
|
|
73684
|
-
import { resolve as resolve18, sep as
|
|
73897
|
+
import { resolve as resolve18, sep as sep9 } from "node:path";
|
|
73685
73898
|
function ownsWorktree(repoDir, cwd) {
|
|
73686
73899
|
const base3 = resolve18(repoDir);
|
|
73687
73900
|
const dir2 = resolve18(cwd);
|
|
73688
73901
|
const container = resolve18(base3, ".worktrees");
|
|
73689
|
-
return dir2 !== base3 && dir2 !== container && dir2.startsWith(container +
|
|
73902
|
+
return dir2 !== base3 && dir2 !== container && dir2.startsWith(container + sep9);
|
|
73690
73903
|
}
|
|
73691
73904
|
|
|
73692
73905
|
// src/services/worktree-reaper.ts
|
|
@@ -73697,7 +73910,7 @@ init_notifications2();
|
|
|
73697
73910
|
import { execFile as execFile11 } from "node:child_process";
|
|
73698
73911
|
import { readdir as readdir4, rm as rm3 } from "node:fs/promises";
|
|
73699
73912
|
import { promisify as promisify10 } from "node:util";
|
|
73700
|
-
import { join as
|
|
73913
|
+
import { join as join29, resolve as resolve19 } from "node:path";
|
|
73701
73914
|
var TICK_MS = 6e4;
|
|
73702
73915
|
var trashDirOf = (repoDir) => resolve19(repoDir, ".worktrees", ".trash");
|
|
73703
73916
|
var exec8 = promisify10(execFile11);
|
|
@@ -73758,7 +73971,7 @@ async function sweepRepo(repoDir, projectId, deps = prodReaperDeps) {
|
|
|
73758
73971
|
}
|
|
73759
73972
|
let removed = 0;
|
|
73760
73973
|
for (const entry2 of entries4) {
|
|
73761
|
-
const path =
|
|
73974
|
+
const path = join29(dir2, entry2);
|
|
73762
73975
|
const known2 = pending.get(path);
|
|
73763
73976
|
const row = known2 ?? { path, repoDir, projectId, entry: entry2, sessionId: sessionIdOf(entry2), since: Date.now() };
|
|
73764
73977
|
pending.set(path, row);
|
|
@@ -74222,8 +74435,8 @@ function appendSourceHistory(current, entry2) {
|
|
|
74222
74435
|
|
|
74223
74436
|
// src/services/spec-reset.ts
|
|
74224
74437
|
import { execFile as execFile13 } from "node:child_process";
|
|
74225
|
-
import { existsSync as
|
|
74226
|
-
import { join as
|
|
74438
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
74439
|
+
import { join as join30 } from "node:path";
|
|
74227
74440
|
import { promisify as promisify12 } from "node:util";
|
|
74228
74441
|
|
|
74229
74442
|
// src/services/stage-artifacts.ts
|
|
@@ -74278,14 +74491,14 @@ async function planSpecReset(spec) {
|
|
|
74278
74491
|
const repoDir = await resolveRepoDir(spec.projectId);
|
|
74279
74492
|
if (!repoDir) return EMPTY3;
|
|
74280
74493
|
const sid = sessionIdForSpec(spec.id);
|
|
74281
|
-
const wt =
|
|
74494
|
+
const wt = join30(repoDir, ".worktrees", sid);
|
|
74282
74495
|
const branch = `hanoman/${sid}`;
|
|
74283
74496
|
const hasBranch = await shaResolvable(repoDir, `refs/heads/${branch}`);
|
|
74284
74497
|
return {
|
|
74285
74498
|
// Berkas fase seluruh stage yang ditinggalkan — rentang yang sama persis dengan revert stage
|
|
74286
74499
|
// SPEC-167, karena tujuan akhirnya memang sama: item berdiri lagi di `brainstorming`.
|
|
74287
74500
|
wouldDelete: await artifactsToRemove(spec.projectId, spec.id, "brainstorming", spec.stage),
|
|
74288
|
-
worktree:
|
|
74501
|
+
worktree: existsSync15(wt) ? wt : null,
|
|
74289
74502
|
branch: hasBranch ? branch : null
|
|
74290
74503
|
};
|
|
74291
74504
|
}
|
|
@@ -74362,6 +74575,61 @@ init_remote_session();
|
|
|
74362
74575
|
init_registry();
|
|
74363
74576
|
init_spec_attachment_dir();
|
|
74364
74577
|
init_uploads();
|
|
74578
|
+
|
|
74579
|
+
// src/services/spec-mockups.ts
|
|
74580
|
+
init_local_binding();
|
|
74581
|
+
init_spec_review2();
|
|
74582
|
+
import { existsSync as existsSync16, readdirSync as readdirSync9, realpathSync as realpathSync10, statSync as statSync5 } from "node:fs";
|
|
74583
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
74584
|
+
import { extname as extname2, join as join33, sep as sep10 } from "node:path";
|
|
74585
|
+
var MOCKUP_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*\.(png|jpe?g|webp)$/i;
|
|
74586
|
+
var MIME = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp" };
|
|
74587
|
+
var isDir = (p3) => {
|
|
74588
|
+
try {
|
|
74589
|
+
return statSync5(p3).isDirectory();
|
|
74590
|
+
} catch {
|
|
74591
|
+
return false;
|
|
74592
|
+
}
|
|
74593
|
+
};
|
|
74594
|
+
async function resolveMockupDir(spec) {
|
|
74595
|
+
const repoDir = await resolveRepoDir(spec.projectId);
|
|
74596
|
+
if (!repoDir) return null;
|
|
74597
|
+
const rel = join33("docs", "mockups", spec.id.toLowerCase());
|
|
74598
|
+
const worktree = join33(worktreeDir(repoDir, spec.id), rel);
|
|
74599
|
+
if (isDir(worktree)) return { source: "worktree", dir: worktree };
|
|
74600
|
+
const repo = join33(repoDir, rel);
|
|
74601
|
+
return isDir(repo) ? { source: "repo", dir: repo } : null;
|
|
74602
|
+
}
|
|
74603
|
+
async function listMockups(spec) {
|
|
74604
|
+
const found = await resolveMockupDir(spec);
|
|
74605
|
+
if (!found) return { source: null, dir: null, items: [] };
|
|
74606
|
+
const items = readdirSync9(found.dir).filter((n2) => MOCKUP_NAME.test(n2)).sort().flatMap((name2) => {
|
|
74607
|
+
try {
|
|
74608
|
+
const st2 = statSync5(join33(found.dir, name2));
|
|
74609
|
+
return st2.isFile() ? [{ name: name2, size: st2.size, modifiedAt: st2.mtime.toISOString() }] : [];
|
|
74610
|
+
} catch {
|
|
74611
|
+
return [];
|
|
74612
|
+
}
|
|
74613
|
+
});
|
|
74614
|
+
return { source: found.source, dir: found.dir, items };
|
|
74615
|
+
}
|
|
74616
|
+
async function readMockup(spec, name2) {
|
|
74617
|
+
if (!MOCKUP_NAME.test(name2)) return null;
|
|
74618
|
+
const found = await resolveMockupDir(spec);
|
|
74619
|
+
if (!found) return null;
|
|
74620
|
+
const file = join33(found.dir, name2);
|
|
74621
|
+
if (!existsSync16(file)) return null;
|
|
74622
|
+
try {
|
|
74623
|
+
const root = realpathSync10(found.dir);
|
|
74624
|
+
const real2 = realpathSync10(file);
|
|
74625
|
+
if (!real2.startsWith(root + sep10) || !statSync5(real2).isFile()) return null;
|
|
74626
|
+
return { buf: await readFile7(real2), mime: MIME[extname2(real2).toLowerCase()] ?? "application/octet-stream" };
|
|
74627
|
+
} catch {
|
|
74628
|
+
return null;
|
|
74629
|
+
}
|
|
74630
|
+
}
|
|
74631
|
+
|
|
74632
|
+
// src/routes/specs.ts
|
|
74365
74633
|
init_stage_machine();
|
|
74366
74634
|
|
|
74367
74635
|
// src/services/spec-docs.ts
|
|
@@ -76843,7 +77111,7 @@ init_stage_machine();
|
|
|
76843
77111
|
init_notifications2();
|
|
76844
77112
|
init_sync_notify();
|
|
76845
77113
|
init_spec_deps();
|
|
76846
|
-
import { createHash as
|
|
77114
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
76847
77115
|
var specNum = (id2) => Number.parseInt(id2.match(/\d+/)?.[0] ?? "0", 10);
|
|
76848
77116
|
var selectOf = (shape) => Object.fromEntries(
|
|
76849
77117
|
Object.keys(shape).filter((k) => k !== "blockedBy").map((k) => [k, true])
|
|
@@ -76935,7 +77203,7 @@ async function liveSignature() {
|
|
|
76935
77203
|
async function specsDigest() {
|
|
76936
77204
|
const agg = await prisma.spec.aggregate({ _count: { _all: true }, _max: { updatedAt: true }, _sum: { version: true } });
|
|
76937
77205
|
const db = `${agg._count._all}:${agg._max.updatedAt?.getTime() ?? 0}:${agg._sum.version ?? 0}`;
|
|
76938
|
-
return
|
|
77206
|
+
return createHash12("sha1").update(`${db}|${await liveSignature()}`).digest("hex");
|
|
76939
77207
|
}
|
|
76940
77208
|
async function liveSpecs(filter = {}) {
|
|
76941
77209
|
await liveOverlayTick();
|
|
@@ -77263,6 +77531,24 @@ ${item.context}` : item.context;
|
|
|
77263
77531
|
await syncSpecAttachmentsDir(spec.id, spec.projectId);
|
|
77264
77532
|
return reply.code(201).send(result);
|
|
77265
77533
|
});
|
|
77534
|
+
app3.get("/specs/:id/mockups", async (req, reply) => {
|
|
77535
|
+
const { id: id2 } = req.params;
|
|
77536
|
+
const spec = await prisma.spec.findUnique({ where: { id: id2 }, select: { id: true, projectId: true } });
|
|
77537
|
+
if (!spec) return reply.code(404).send({ error: "not found" });
|
|
77538
|
+
return listMockups(spec);
|
|
77539
|
+
});
|
|
77540
|
+
app3.get("/specs/:id/mockups/:name", async (req, reply) => {
|
|
77541
|
+
const { id: id2, name: name2 } = req.params;
|
|
77542
|
+
const spec = await prisma.spec.findUnique({ where: { id: id2 }, select: { id: true, projectId: true } });
|
|
77543
|
+
const file = spec ? await readMockup(spec, name2) : null;
|
|
77544
|
+
if (!file) return reply.code(404).send({ error: "not found" });
|
|
77545
|
+
reply.header("content-type", file.mime);
|
|
77546
|
+
reply.header("content-disposition", `inline; filename="${name2}"`);
|
|
77547
|
+
reply.header("x-content-type-options", "nosniff");
|
|
77548
|
+
reply.header("content-security-policy", "sandbox; default-src 'none'");
|
|
77549
|
+
reply.header("cache-control", "no-cache");
|
|
77550
|
+
return reply.send(file.buf);
|
|
77551
|
+
});
|
|
77266
77552
|
app3.get("/specs/:id/attachments/:attId", async (req, reply) => {
|
|
77267
77553
|
const { id: id2, attId } = req.params;
|
|
77268
77554
|
const a = await prisma.specAttachment.findUnique({ where: { id: attId } });
|
|
@@ -77358,7 +77644,7 @@ ${item.context}` : item.context;
|
|
|
77358
77644
|
});
|
|
77359
77645
|
const specWithProject = (id2) => prisma.spec.findUnique({ where: { id: id2 }, include: { project: true } });
|
|
77360
77646
|
const resolveReview = async (repoDir, spec) => {
|
|
77361
|
-
if (
|
|
77647
|
+
if (existsSync17(worktreeDir(repoDir, spec.id))) return { wt: true };
|
|
77362
77648
|
if (spec.baseSha && spec.headSha && await shaResolvable(repoDir, spec.baseSha) && await shaResolvable(repoDir, spec.headSha))
|
|
77363
77649
|
return { wt: false, base: spec.baseSha, head: spec.headSha };
|
|
77364
77650
|
const r = await specCommitRange(repoDir, spec.id);
|
|
@@ -77654,7 +77940,7 @@ import { spawn as spawn5 } from "node:child_process";
|
|
|
77654
77940
|
// src/services/git-remotes.ts
|
|
77655
77941
|
import { execFile as execFile16 } from "node:child_process";
|
|
77656
77942
|
import { promisify as promisify15 } from "node:util";
|
|
77657
|
-
import { existsSync as
|
|
77943
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
77658
77944
|
var exec10 = promisify15(execFile16);
|
|
77659
77945
|
var GIT7 = { maxBuffer: 1 << 24, encoding: "utf8" };
|
|
77660
77946
|
function parseRemote(url2) {
|
|
@@ -77673,7 +77959,7 @@ function prUrl(remoteUrl, branch, base3 = "main") {
|
|
|
77673
77959
|
return null;
|
|
77674
77960
|
}
|
|
77675
77961
|
async function listRemotes(repoDir) {
|
|
77676
|
-
if (!repoDir || !
|
|
77962
|
+
if (!repoDir || !existsSync20(repoDir)) return [];
|
|
77677
77963
|
try {
|
|
77678
77964
|
const { stdout } = await exec10("git", ["remote", "-v"], { cwd: repoDir, ...GIT7 });
|
|
77679
77965
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -77721,7 +78007,7 @@ init_integrate();
|
|
|
77721
78007
|
init_safe_repo_path();
|
|
77722
78008
|
import { lstat as lstat2, rename as rename4, rm as rm6 } from "node:fs/promises";
|
|
77723
78009
|
import { createWriteStream } from "node:fs";
|
|
77724
|
-
import { join as
|
|
78010
|
+
import { join as join39 } from "node:path";
|
|
77725
78011
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
77726
78012
|
import { pipeline } from "node:stream/promises";
|
|
77727
78013
|
var EntryExistsError = class extends Error {
|
|
@@ -77782,7 +78068,7 @@ async function saveUpload2(repoDir, rel, source, opts2 = {}) {
|
|
|
77782
78068
|
if (current && !opts2.overwrite) return { status: "exists" };
|
|
77783
78069
|
if (current && !current.isFile())
|
|
77784
78070
|
throw new PathContainmentError("repository path ditolak: target bukan file regular");
|
|
77785
|
-
const temp =
|
|
78071
|
+
const temp = join39(entry2.parent, `.hanoman-${randomUUID11()}.tmp`);
|
|
77786
78072
|
try {
|
|
77787
78073
|
await pipeline(source, createWriteStream(temp, { flags: "wx", mode: 384 }));
|
|
77788
78074
|
if (opts2.isTruncated?.()) {
|
|
@@ -78273,15 +78559,15 @@ async function finishGraphOp(reply, id2, repoDir, r, verb) {
|
|
|
78273
78559
|
// src/routes/fs.ts
|
|
78274
78560
|
import { readdir as readdir8 } from "node:fs/promises";
|
|
78275
78561
|
import { homedir as homedir9 } from "node:os";
|
|
78276
|
-
import { resolve as resolve22, dirname as
|
|
78562
|
+
import { resolve as resolve22, dirname as dirname16, join as join40 } from "node:path";
|
|
78277
78563
|
async function fs_default(app3) {
|
|
78278
78564
|
app3.get("/fs/browse", async (req, reply) => {
|
|
78279
78565
|
const q2 = req.query.path;
|
|
78280
78566
|
const dir2 = q2 && q2.trim() ? resolve22(q2.trim()) : homedir9();
|
|
78281
78567
|
try {
|
|
78282
78568
|
const ents = await readdir8(dir2, { withFileTypes: true });
|
|
78283
|
-
const entries4 = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path:
|
|
78284
|
-
const parent =
|
|
78569
|
+
const entries4 = ents.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => ({ name: e.name, path: join40(dir2, e.name) })).sort((a, b) => a.name.localeCompare(b.name));
|
|
78570
|
+
const parent = dirname16(dir2);
|
|
78285
78571
|
return { path: dir2, parent: parent === dir2 ? null : parent, entries: entries4 };
|
|
78286
78572
|
} catch {
|
|
78287
78573
|
return reply.code(400).send({ error: `tak bisa membaca folder "${dir2}"` });
|
|
@@ -78295,7 +78581,7 @@ init_src();
|
|
|
78295
78581
|
init_src2();
|
|
78296
78582
|
init_session_phases();
|
|
78297
78583
|
init_spec_review2();
|
|
78298
|
-
import { existsSync as
|
|
78584
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
78299
78585
|
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
78300
78586
|
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
78301
78587
|
init_integrate();
|
|
@@ -78360,7 +78646,7 @@ function setBounded(map, key, value, maxKeys) {
|
|
|
78360
78646
|
// src/services/auth.ts
|
|
78361
78647
|
init_src2();
|
|
78362
78648
|
init_db();
|
|
78363
|
-
import { randomBytes as randomBytes4, scrypt as scryptCb, timingSafeEqual as timingSafeEqual4, createHash as
|
|
78649
|
+
import { randomBytes as randomBytes4, scrypt as scryptCb, timingSafeEqual as timingSafeEqual4, createHash as createHash17 } from "node:crypto";
|
|
78364
78650
|
import { promisify as promisify16 } from "node:util";
|
|
78365
78651
|
var scrypt = promisify16(scryptCb);
|
|
78366
78652
|
var COOKIE_NAME = "hn_session";
|
|
@@ -78378,7 +78664,7 @@ async function verifyPassword(pw, stored) {
|
|
|
78378
78664
|
return key.length === want.length && timingSafeEqual4(key, want);
|
|
78379
78665
|
}
|
|
78380
78666
|
var newSessionToken = () => randomBytes4(32).toString("base64url");
|
|
78381
|
-
var sessionId = (token) =>
|
|
78667
|
+
var sessionId = (token) => createHash17("sha256").update(token).digest("hex");
|
|
78382
78668
|
async function createSession2(userId) {
|
|
78383
78669
|
const token = newSessionToken();
|
|
78384
78670
|
await prisma.session.create({
|
|
@@ -78439,9 +78725,9 @@ function cookieOpts(req) {
|
|
|
78439
78725
|
|
|
78440
78726
|
// src/services/device-token.ts
|
|
78441
78727
|
init_db();
|
|
78442
|
-
import { randomBytes as randomBytes5, createHash as
|
|
78728
|
+
import { randomBytes as randomBytes5, createHash as createHash18 } from "node:crypto";
|
|
78443
78729
|
var newDeviceToken = () => randomBytes5(32).toString("base64url");
|
|
78444
|
-
var tokenHash = (token) =>
|
|
78730
|
+
var tokenHash = (token) => createHash18("sha256").update(token).digest("hex");
|
|
78445
78731
|
async function issueDeviceToken(userId, name2) {
|
|
78446
78732
|
const token = newDeviceToken();
|
|
78447
78733
|
const row = await prisma.deviceToken.create({ data: { userId, name: name2, tokenHash: tokenHash(token) } });
|
|
@@ -78654,14 +78940,14 @@ function installCommand(m, agent, shell = shellBin()) {
|
|
|
78654
78940
|
init_local_binding();
|
|
78655
78941
|
|
|
78656
78942
|
// src/services/terminal-diag.ts
|
|
78657
|
-
import { appendFileSync as appendFileSync4, mkdirSync as
|
|
78658
|
-
import { join as
|
|
78943
|
+
import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync17, statSync as statSync6, rmSync as rmSync11 } from "node:fs";
|
|
78944
|
+
import { join as join41 } from "node:path";
|
|
78659
78945
|
var DIAG_MAX_BYTES = 2 * 1024 * 1024;
|
|
78660
78946
|
var KINDS = /* @__PURE__ */ new Set(["key", "comp", "data", "ack", "pred"]);
|
|
78661
78947
|
var ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
78662
78948
|
function diagFile(home3, sessionId2) {
|
|
78663
78949
|
if (!ID.test(sessionId2)) throw new Error(`id sesi tak sah untuk diag: ${sessionId2}`);
|
|
78664
|
-
return
|
|
78950
|
+
return join41(home3, "diag", `${sessionId2}.jsonl`);
|
|
78665
78951
|
}
|
|
78666
78952
|
function usable(ev) {
|
|
78667
78953
|
if (!ev || typeof ev !== "object") return false;
|
|
@@ -78672,9 +78958,9 @@ function appendDiag(home3, sessionId2, events) {
|
|
|
78672
78958
|
const file = diagFile(home3, sessionId2);
|
|
78673
78959
|
const rows = events.filter(usable);
|
|
78674
78960
|
if (!rows.length) return;
|
|
78675
|
-
|
|
78961
|
+
mkdirSync17(join41(home3, "diag"), { recursive: true });
|
|
78676
78962
|
try {
|
|
78677
|
-
if (
|
|
78963
|
+
if (statSync6(file).size > DIAG_MAX_BYTES) rmSync11(file);
|
|
78678
78964
|
} catch {
|
|
78679
78965
|
}
|
|
78680
78966
|
appendFileSync4(file, rows.map((r) => JSON.stringify(r)).join("\n") + "\n");
|
|
@@ -78754,11 +79040,11 @@ init_uploads();
|
|
|
78754
79040
|
// src/services/agent-invocations.ts
|
|
78755
79041
|
init_src();
|
|
78756
79042
|
init_db();
|
|
78757
|
-
import { createHash as
|
|
79043
|
+
import { createHash as createHash19, randomUUID as randomUUID12 } from "node:crypto";
|
|
78758
79044
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
78759
79045
|
import { homedir as homedir10 } from "node:os";
|
|
78760
|
-
import { readFileSync as
|
|
78761
|
-
import { isAbsolute as isAbsolute12, relative as
|
|
79046
|
+
import { readFileSync as readFileSync20, realpathSync as realpathSync11, statSync as statSync7 } from "node:fs";
|
|
79047
|
+
import { isAbsolute as isAbsolute12, relative as relative8, resolve as resolve23 } from "node:path";
|
|
78762
79048
|
|
|
78763
79049
|
// src/services/session-event-relay.ts
|
|
78764
79050
|
init_session_event_token();
|
|
@@ -78766,7 +79052,7 @@ init_session_event_spool();
|
|
|
78766
79052
|
init_ingress_policy();
|
|
78767
79053
|
import { constants as constants4 } from "node:fs";
|
|
78768
79054
|
import { mkdir as mkdir8, open as open4, readdir as readdir9, rm as rm7 } from "node:fs/promises";
|
|
78769
|
-
import { join as
|
|
79055
|
+
import { join as join42 } from "node:path";
|
|
78770
79056
|
var MAX_EVENT_BYTES = 1e6;
|
|
78771
79057
|
var MAX_FILES_PER_DRAIN = 1e3;
|
|
78772
79058
|
var SESSION_ID_RE = /^[a-z0-9_-]+$/;
|
|
@@ -78817,7 +79103,7 @@ async function drainSpool(app3, root, observation, host2) {
|
|
|
78817
79103
|
let readBuffer;
|
|
78818
79104
|
for (const session of await readdir9(root, { withFileTypes: true })) {
|
|
78819
79105
|
if (!session.isDirectory() || !SESSION_ID_RE.test(session.name)) continue;
|
|
78820
|
-
const dir2 =
|
|
79106
|
+
const dir2 = join42(root, session.name);
|
|
78821
79107
|
let entries4;
|
|
78822
79108
|
try {
|
|
78823
79109
|
entries4 = await readdir9(dir2, { withFileTypes: true });
|
|
@@ -78829,7 +79115,7 @@ async function drainSpool(app3, root, observation, host2) {
|
|
|
78829
79115
|
if (!entry2.isFile() || !entry2.name.endsWith(".json")) continue;
|
|
78830
79116
|
const eventAt = /^(\d{1,15})-/.exec(entry2.name)?.[1];
|
|
78831
79117
|
if (++examined > MAX_FILES_PER_DRAIN) return delivered;
|
|
78832
|
-
const path =
|
|
79118
|
+
const path = join42(dir2, entry2.name);
|
|
78833
79119
|
let payload;
|
|
78834
79120
|
let handle;
|
|
78835
79121
|
try {
|
|
@@ -78916,7 +79202,7 @@ var MAX_TRANSCRIPT_BYTES2 = 10 * 1024 * 1024;
|
|
|
78916
79202
|
var ANSI2 = /[\u001b\u009b](?:\][^\u0007]*(?:\u0007|\u001b\\)|\[[0-?]*[ -/]*[@-~]|[@-_])/g;
|
|
78917
79203
|
var snapshotHashes = /* @__PURE__ */ new Map();
|
|
78918
79204
|
var keyOf = (x) => `${x.sessionId}\0${x.runtimeInvocationId}`;
|
|
78919
|
-
var hash3 = (value) =>
|
|
79205
|
+
var hash3 = (value) => createHash19("sha256").update(value).digest("hex");
|
|
78920
79206
|
var defaultGitStatus = (cwd) => {
|
|
78921
79207
|
try {
|
|
78922
79208
|
return execFileSync8("git", ["-C", cwd, "status", "--porcelain=v1", "-z"], {
|
|
@@ -78951,7 +79237,7 @@ var transcriptRoots = () => [
|
|
|
78951
79237
|
resolve23(process.env.CODEX_HOME ?? `${homedir10()}/.codex`)
|
|
78952
79238
|
];
|
|
78953
79239
|
var inside = (path, root) => {
|
|
78954
|
-
const rel =
|
|
79240
|
+
const rel = relative8(root, path);
|
|
78955
79241
|
return rel === "" || !rel.startsWith("..") && !isAbsolute12(rel);
|
|
78956
79242
|
};
|
|
78957
79243
|
var EMPTY_USAGE = { inputTokens: null, outputTokens: null, cachedTokens: null };
|
|
@@ -79024,12 +79310,12 @@ function usageFromText(text2) {
|
|
|
79024
79310
|
function transcriptUsage(path, roots = transcriptRoots()) {
|
|
79025
79311
|
if (!path) return EMPTY_USAGE;
|
|
79026
79312
|
try {
|
|
79027
|
-
const real2 =
|
|
79028
|
-
const safeRoots = roots.map((root) =>
|
|
79313
|
+
const real2 = realpathSync11(path);
|
|
79314
|
+
const safeRoots = roots.map((root) => realpathSync11(root));
|
|
79029
79315
|
if (!safeRoots.some((root) => inside(real2, root))) return EMPTY_USAGE;
|
|
79030
|
-
const info =
|
|
79316
|
+
const info = statSync7(real2);
|
|
79031
79317
|
if (!info.isFile() || info.size > MAX_TRANSCRIPT_BYTES2) return EMPTY_USAGE;
|
|
79032
|
-
return usageFromText(
|
|
79318
|
+
return usageFromText(readFileSync20(real2, "utf8"));
|
|
79033
79319
|
} catch {
|
|
79034
79320
|
return EMPTY_USAGE;
|
|
79035
79321
|
}
|
|
@@ -79314,25 +79600,25 @@ init_ask();
|
|
|
79314
79600
|
// src/services/decision-guard.ts
|
|
79315
79601
|
init_src2();
|
|
79316
79602
|
init_db();
|
|
79317
|
-
import { mkdirSync as
|
|
79318
|
-
import { join as
|
|
79603
|
+
import { mkdirSync as mkdirSync18, readFileSync as readFileSync21, readdirSync as readdirSync11, rmSync as rmSync12, statSync as statSync8, writeFileSync as writeFileSync18 } from "node:fs";
|
|
79604
|
+
import { join as join43 } from "node:path";
|
|
79319
79605
|
var SESSION_HEADER = "x-hanoman-session";
|
|
79320
79606
|
var SAFE_ID = /^[A-Za-z0-9._-]{1,200}$/;
|
|
79321
79607
|
var launchers = /* @__PURE__ */ new Map();
|
|
79322
79608
|
var STALE_MS = 30 * 24 * 60 * 6e4;
|
|
79323
|
-
var launcherDir = (env = process.env) =>
|
|
79609
|
+
var launcherDir = (env = process.env) => join43(resolveHome(env), "session-launchers");
|
|
79324
79610
|
function recordLauncher(sessionId2, agentId, env = process.env) {
|
|
79325
79611
|
if (!SAFE_ID.test(sessionId2)) return;
|
|
79326
79612
|
const known2 = launchersFor(sessionId2, env);
|
|
79327
79613
|
known2.add(agentId);
|
|
79328
79614
|
try {
|
|
79329
79615
|
const dir2 = launcherDir(env);
|
|
79330
|
-
|
|
79331
|
-
|
|
79616
|
+
mkdirSync18(dir2, { recursive: true, mode: 448 });
|
|
79617
|
+
writeFileSync18(join43(dir2, sessionId2), [...known2].join("\n"), { mode: 384 });
|
|
79332
79618
|
const now = Date.now();
|
|
79333
|
-
for (const f of
|
|
79619
|
+
for (const f of readdirSync11(dir2)) {
|
|
79334
79620
|
try {
|
|
79335
|
-
if (now -
|
|
79621
|
+
if (now - statSync8(join43(dir2, f)).mtimeMs > STALE_MS) rmSync12(join43(dir2, f), { force: true });
|
|
79336
79622
|
} catch {
|
|
79337
79623
|
}
|
|
79338
79624
|
}
|
|
@@ -79344,7 +79630,7 @@ function launchersFor(sessionId2, env) {
|
|
|
79344
79630
|
if (hot) return hot;
|
|
79345
79631
|
const ids = /* @__PURE__ */ new Set();
|
|
79346
79632
|
try {
|
|
79347
|
-
for (const l of
|
|
79633
|
+
for (const l of readFileSync21(join43(launcherDir(env), sessionId2), "utf8").split("\n")) if (l.trim()) ids.add(l.trim());
|
|
79348
79634
|
} catch {
|
|
79349
79635
|
}
|
|
79350
79636
|
launchers.set(sessionId2, ids);
|
|
@@ -79796,7 +80082,7 @@ async function terminal_default(app3, opts2) {
|
|
|
79796
80082
|
if (!repoDir) return { ok: false, code: 409, msg: "project belum punya repoDir" };
|
|
79797
80083
|
if (!s2.cwd.includes("/.worktrees/"))
|
|
79798
80084
|
return { ok: false, code: 409, msg: "belum ada worktree untuk di-review \u2014 jalankan/lanjutkan sesi dulu", empty: "no-worktree" };
|
|
79799
|
-
if (!
|
|
80085
|
+
if (!existsSync21(s2.cwd))
|
|
79800
80086
|
return { ok: false, code: 409, msg: "belum ada worktree untuk di-review \u2014 jalankan/lanjutkan sesi dulu", empty: "no-commit" };
|
|
79801
80087
|
return { ok: true, id: s2.id, repoDir };
|
|
79802
80088
|
};
|
|
@@ -80059,7 +80345,7 @@ async function terminalWorkspaceRoutes(app3) {
|
|
|
80059
80345
|
// src/routes/vps.ts
|
|
80060
80346
|
init_db();
|
|
80061
80347
|
init_src();
|
|
80062
|
-
import { readFileSync as
|
|
80348
|
+
import { readFileSync as readFileSync26, existsSync as existsSync24 } from "node:fs";
|
|
80063
80349
|
import { homedir as homedir12 } from "node:os";
|
|
80064
80350
|
|
|
80065
80351
|
// src/vps/catalog/catalog.data.ts
|
|
@@ -80267,14 +80553,14 @@ function resolveComponents(ids, profile) {
|
|
|
80267
80553
|
}
|
|
80268
80554
|
|
|
80269
80555
|
// src/services/vps-provision.ts
|
|
80270
|
-
import { readFileSync as
|
|
80556
|
+
import { readFileSync as readFileSync23 } from "node:fs";
|
|
80271
80557
|
|
|
80272
80558
|
// src/services/vps-ssh.ts
|
|
80273
80559
|
init_config2();
|
|
80274
80560
|
import { spawn as spawn6 } from "node:child_process";
|
|
80275
|
-
import { mkdtempSync as mkdtempSync4, rmSync as
|
|
80561
|
+
import { mkdtempSync as mkdtempSync4, rmSync as rmSync13, writeFileSync as writeFileSync19 } from "node:fs";
|
|
80276
80562
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
80277
|
-
import { dirname as
|
|
80563
|
+
import { dirname as dirname17, join as join44 } from "node:path";
|
|
80278
80564
|
var sshBin = () => effectiveStr("HANOMAN_SSH_BIN") ?? "ssh";
|
|
80279
80565
|
function consoleArgv(t) {
|
|
80280
80566
|
return [
|
|
@@ -80289,9 +80575,9 @@ function consoleArgv(t) {
|
|
|
80289
80575
|
];
|
|
80290
80576
|
}
|
|
80291
80577
|
function askpassScript() {
|
|
80292
|
-
const dir2 = mkdtempSync4(
|
|
80293
|
-
const path =
|
|
80294
|
-
|
|
80578
|
+
const dir2 = mkdtempSync4(join44(tmpdir7(), "hanoman-askpass-"));
|
|
80579
|
+
const path = join44(dir2, "askpass.sh");
|
|
80580
|
+
writeFileSync19(path, `#!/bin/sh
|
|
80295
80581
|
printf '%s' "$HANOMAN_SSH_PASSWORD"
|
|
80296
80582
|
`, { mode: 448 });
|
|
80297
80583
|
return path;
|
|
@@ -80330,7 +80616,7 @@ function sshExec(t, remoteCmd, opts2 = {}) {
|
|
|
80330
80616
|
const timer11 = setTimeout(() => p3.kill("SIGKILL"), opts2.timeoutMs ?? 6e4);
|
|
80331
80617
|
const done = (r) => {
|
|
80332
80618
|
clearTimeout(timer11);
|
|
80333
|
-
if (askpass)
|
|
80619
|
+
if (askpass) rmSync13(dirname17(askpass), { recursive: true, force: true });
|
|
80334
80620
|
resolve25(r);
|
|
80335
80621
|
};
|
|
80336
80622
|
p3.stdout.on("data", (d) => {
|
|
@@ -80350,26 +80636,10 @@ function sshExec(t, remoteCmd, opts2 = {}) {
|
|
|
80350
80636
|
|
|
80351
80637
|
// src/services/vps-audit.ts
|
|
80352
80638
|
init_db();
|
|
80353
|
-
|
|
80354
|
-
import {
|
|
80355
|
-
import {
|
|
80356
|
-
|
|
80357
|
-
// src/runner/deps.ts
|
|
80358
|
-
import { existsSync as existsSync19 } from "node:fs";
|
|
80359
|
-
import { dirname as dirname17, join as join42 } from "node:path";
|
|
80360
|
-
function repoRootFrom(startDir) {
|
|
80361
|
-
let dir2 = startDir;
|
|
80362
|
-
for (let i = 0; i < 8; i++) {
|
|
80363
|
-
if (existsSync19(join42(dir2, "pnpm-workspace.yaml"))) return dir2;
|
|
80364
|
-
const parent = dirname17(dir2);
|
|
80365
|
-
if (parent === dir2) break;
|
|
80366
|
-
dir2 = parent;
|
|
80367
|
-
}
|
|
80368
|
-
return startDir;
|
|
80369
|
-
}
|
|
80370
|
-
var repoRoot = (startDir = process.cwd()) => repoRootFrom(startDir);
|
|
80371
|
-
|
|
80372
|
-
// src/services/vps-audit.ts
|
|
80639
|
+
init_deps();
|
|
80640
|
+
import { existsSync as existsSync22, readFileSync as readFileSync22 } from "node:fs";
|
|
80641
|
+
import { join as join45 } from "node:path";
|
|
80642
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
80373
80643
|
init_sync_notify();
|
|
80374
80644
|
|
|
80375
80645
|
// src/vps/scoring.ts
|
|
@@ -80469,11 +80739,11 @@ function parseHealth(out4) {
|
|
|
80469
80739
|
return { uptime: h2.uptime ?? "", disk: h2.disk ?? "", mem: h2.mem ?? "", load: h2.load ?? "" };
|
|
80470
80740
|
}
|
|
80471
80741
|
var target2 = (v) => ({ host: v.host, port: v.port, user: v.user, keyPath: v.keyPath });
|
|
80472
|
-
var packagedScript = (f) =>
|
|
80473
|
-
var
|
|
80742
|
+
var packagedScript = (f) => fileURLToPath5(new URL(`../scripts/vps/${f}`, import.meta.url));
|
|
80743
|
+
var moduleDir2 = () => fileURLToPath5(new URL(".", import.meta.url));
|
|
80474
80744
|
var scriptPath = (f) => {
|
|
80475
80745
|
const packed = packagedScript(f);
|
|
80476
|
-
return
|
|
80746
|
+
return existsSync22(packed) ? packed : join45(repoRoot(moduleDir2()), "server", "scripts", "vps", f);
|
|
80477
80747
|
};
|
|
80478
80748
|
async function itemStatesOf(vpsId) {
|
|
80479
80749
|
const rows = await prisma.vpsItemState.findMany({ where: { vpsId } });
|
|
@@ -80483,7 +80753,7 @@ async function runAudit(v) {
|
|
|
80483
80753
|
const r = await sshExec(
|
|
80484
80754
|
target2(v),
|
|
80485
80755
|
"sudo -n bash -s",
|
|
80486
|
-
{ stdin:
|
|
80756
|
+
{ stdin: readFileSync22(scriptPath("audit.sh"), "utf8"), timeoutMs: 6e4 }
|
|
80487
80757
|
);
|
|
80488
80758
|
const audit = parseAudit(r.out);
|
|
80489
80759
|
if (r.code !== 0 || audit.length === 0) return { ok: false, out: r.out };
|
|
@@ -80563,7 +80833,7 @@ function parseProvisionSteps(out4) {
|
|
|
80563
80833
|
return m ? [{ item: m[1], status: m[2], detail: (m[3] ?? "").trim() }] : [];
|
|
80564
80834
|
});
|
|
80565
80835
|
}
|
|
80566
|
-
var script = () =>
|
|
80836
|
+
var script = () => readFileSync23(scriptPath("provision.sh"), "utf8");
|
|
80567
80837
|
async function probeComponents(v) {
|
|
80568
80838
|
const r = await sshExec(v, "env MODE=probe bash -s", { stdin: script(), timeoutMs: PROBE_TIMEOUT_MS });
|
|
80569
80839
|
const components2 = parseComponents(r.out);
|
|
@@ -80590,7 +80860,7 @@ async function readSetupToken(v, home3 = "/var/lib/hanoman") {
|
|
|
80590
80860
|
}
|
|
80591
80861
|
|
|
80592
80862
|
// src/services/vps-remediate.ts
|
|
80593
|
-
import { readFileSync as
|
|
80863
|
+
import { readFileSync as readFileSync24 } from "node:fs";
|
|
80594
80864
|
function parseSteps2(out4) {
|
|
80595
80865
|
return out4.split("\n").flatMap((line) => {
|
|
80596
80866
|
const m = line.match(/^STEP (\S+) (would|ok|fail)(?: (.*))?$/);
|
|
@@ -80601,7 +80871,7 @@ async function remediate(v, items, dryRun) {
|
|
|
80601
80871
|
const r = await sshExec(
|
|
80602
80872
|
v,
|
|
80603
80873
|
`sudo -n env ITEMS=${items.join(",")} SSH_PORT=${v.port} DRY_RUN=${dryRun ? "1" : ""} bash -s`,
|
|
80604
|
-
{ stdin:
|
|
80874
|
+
{ stdin: readFileSync24(scriptPath("remediate.sh"), "utf8"), timeoutMs: 3e5 }
|
|
80605
80875
|
);
|
|
80606
80876
|
return { ok: r.code === 0, steps: parseSteps2(r.out), out: r.out };
|
|
80607
80877
|
}
|
|
@@ -80718,47 +80988,47 @@ async function listVpsWithScore() {
|
|
|
80718
80988
|
init_src2();
|
|
80719
80989
|
init_config2();
|
|
80720
80990
|
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
80721
|
-
import { chmodSync as
|
|
80991
|
+
import { chmodSync as chmodSync10, copyFileSync, existsSync as existsSync23, mkdirSync as mkdirSync19, readFileSync as readFileSync25, unlinkSync as unlinkSync5 } from "node:fs";
|
|
80722
80992
|
import { homedir as homedir11 } from "node:os";
|
|
80723
|
-
import { join as
|
|
80993
|
+
import { join as join46 } from "node:path";
|
|
80724
80994
|
var keyDir = () => effectiveStr("HANOMAN_SSH_KEY_DIR") ?? resolveDataDirs().sshKeys;
|
|
80725
80995
|
var KEY_FILES = ["id_ed25519", "id_ed25519.pub"];
|
|
80726
80996
|
function adoptLegacyKey(dir2) {
|
|
80727
80997
|
if (effectiveStr("HANOMAN_SSH_KEY_DIR")) return;
|
|
80728
|
-
const legacy =
|
|
80998
|
+
const legacy = join46(homedir11(), ".hanoman");
|
|
80729
80999
|
if (legacy === dir2) return;
|
|
80730
|
-
if (!KEY_FILES.every((f) =>
|
|
80731
|
-
|
|
81000
|
+
if (!KEY_FILES.every((f) => existsSync23(join46(legacy, f)))) return;
|
|
81001
|
+
mkdirSync19(dir2, { recursive: true, mode: 448 });
|
|
80732
81002
|
for (const f of KEY_FILES) {
|
|
80733
|
-
copyFileSync(
|
|
80734
|
-
|
|
81003
|
+
copyFileSync(join46(legacy, f), join46(dir2, f));
|
|
81004
|
+
chmodSync10(join46(dir2, f), 384);
|
|
80735
81005
|
}
|
|
80736
|
-
for (const f of KEY_FILES) unlinkSync5(
|
|
81006
|
+
for (const f of KEY_FILES) unlinkSync5(join46(legacy, f));
|
|
80737
81007
|
console.log(`vps: key SSH dipindah dari ${legacy} ke ${dir2} (SPEC-846 \u2014 satu batas backup)`);
|
|
80738
81008
|
}
|
|
80739
81009
|
function ensureHanomanKey() {
|
|
80740
81010
|
const dir2 = keyDir();
|
|
80741
|
-
const privPath =
|
|
81011
|
+
const privPath = join46(dir2, KEY_FILES[0]);
|
|
80742
81012
|
const pubPath = `${privPath}.pub`;
|
|
80743
|
-
if (!
|
|
80744
|
-
if (!
|
|
80745
|
-
|
|
81013
|
+
if (!existsSync23(privPath)) adoptLegacyKey(dir2);
|
|
81014
|
+
if (!existsSync23(privPath)) {
|
|
81015
|
+
mkdirSync19(dir2, { recursive: true, mode: 448 });
|
|
80746
81016
|
execFileSync10(
|
|
80747
81017
|
"ssh-keygen",
|
|
80748
81018
|
["-t", "ed25519", "-N", "", "-C", "hanoman", "-f", privPath],
|
|
80749
81019
|
{ stdio: ["ignore", "ignore", "pipe"] }
|
|
80750
81020
|
);
|
|
80751
81021
|
}
|
|
80752
|
-
return { privPath, pubPath, pub:
|
|
81022
|
+
return { privPath, pubPath, pub: readFileSync25(pubPath, "utf8").trim() };
|
|
80753
81023
|
}
|
|
80754
81024
|
|
|
80755
81025
|
// src/services/vps-bootstrap.ts
|
|
80756
81026
|
var INSTALL_CMD = `mkdir -p ~/.ssh && chmod 700 ~/.ssh && touch ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys && k=$(cat) && { grep -qxF "$k" ~/.ssh/authorized_keys || printf '%s\\n' "$k" >> ~/.ssh/authorized_keys; }`;
|
|
80757
81027
|
async function bootstrapKey(t, password) {
|
|
80758
81028
|
const { privPath, pub } = ensureHanomanKey();
|
|
80759
|
-
const
|
|
81029
|
+
const install2 = await sshExec(t, INSTALL_CMD, { stdin: `${pub}
|
|
80760
81030
|
`, password, timeoutMs: 6e4 });
|
|
80761
|
-
if (
|
|
81031
|
+
if (install2.code !== 0) return { ok: false, out: install2.out };
|
|
80762
81032
|
const verify = await sshExec({ ...t, keyPath: privPath }, "true", { timeoutMs: 3e4 });
|
|
80763
81033
|
if (verify.code !== 0) return { ok: false, out: verify.out };
|
|
80764
81034
|
return { ok: true, keyPath: privPath };
|
|
@@ -80769,7 +81039,7 @@ init_session_launch_gate();
|
|
|
80769
81039
|
init_settings3();
|
|
80770
81040
|
init_sync_notify();
|
|
80771
81041
|
function keyMissing(v) {
|
|
80772
|
-
return !!v.keyPath && !
|
|
81042
|
+
return !!v.keyPath && !existsSync24(v.keyPath);
|
|
80773
81043
|
}
|
|
80774
81044
|
async function vps_default(app3) {
|
|
80775
81045
|
app3.get("/vps", async () => listVpsWithScore());
|
|
@@ -80997,7 +81267,7 @@ async function vps_default(app3) {
|
|
|
80997
81267
|
const r = await sshExec(
|
|
80998
81268
|
v,
|
|
80999
81269
|
`sudo -n env SSH_PORT=${v.port} SSH_USER=${v.user} bash -s`,
|
|
81000
|
-
{ stdin:
|
|
81270
|
+
{ stdin: readFileSync26(scriptPath("harden.sh"), "utf8"), timeoutMs: 3e5 }
|
|
81001
81271
|
);
|
|
81002
81272
|
if (r.code !== 0) return reply.code(502).send({ error: "harden gagal lewat ssh", transcript: r.out });
|
|
81003
81273
|
const verify = await sshExec(v, "true", { timeoutMs: 3e4 });
|
|
@@ -81146,8 +81416,8 @@ async function antigravity(app3) {
|
|
|
81146
81416
|
init_src2();
|
|
81147
81417
|
init_src();
|
|
81148
81418
|
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
81149
|
-
import { readFile as
|
|
81150
|
-
import { dirname as dirname18, join as
|
|
81419
|
+
import { readFile as readFile9, writeFile as writeFile6, rename as rename5, mkdir as mkdir9, rm as rm9 } from "node:fs/promises";
|
|
81420
|
+
import { dirname as dirname18, join as join48 } from "node:path";
|
|
81151
81421
|
|
|
81152
81422
|
// src/services/model-catalog-parser.ts
|
|
81153
81423
|
init_zod();
|
|
@@ -81254,7 +81524,7 @@ init_session_sandbox();
|
|
|
81254
81524
|
import { spawn as spawn7 } from "node:child_process";
|
|
81255
81525
|
import { mkdtemp as mkdtemp2, rm as rm8 } from "node:fs/promises";
|
|
81256
81526
|
import { tmpdir as tmpdir8 } from "node:os";
|
|
81257
|
-
import { join as
|
|
81527
|
+
import { join as join47 } from "node:path";
|
|
81258
81528
|
var MAX_BYTES = 4 * 1024 * 1024;
|
|
81259
81529
|
var TIMEOUT_MS = 2e4;
|
|
81260
81530
|
var quote3 = (s2) => "'" + s2.replaceAll("'", "'\\''") + "'";
|
|
@@ -81353,7 +81623,7 @@ function runCatalogCommand(command, agent, cwd, env, timeoutMs = TIMEOUT_MS) {
|
|
|
81353
81623
|
}
|
|
81354
81624
|
async function probeModelCatalog(agent) {
|
|
81355
81625
|
assertAntigravityLaunch(agent);
|
|
81356
|
-
const cwd = await mkdtemp2(
|
|
81626
|
+
const cwd = await mkdtemp2(join47(tmpdir8(), "hanoman-models-"));
|
|
81357
81627
|
const env = agentEnvironment(agent, process.env);
|
|
81358
81628
|
for (const key of ["HANOMAN_CLAUDE_BIN", "HANOMAN_CODEX_BIN", "HANOMAN_PODMAN_BIN", "HANOMAN_ANTIGRAVITY_BIN"]) {
|
|
81359
81629
|
const value = effectiveStr(key);
|
|
@@ -81464,12 +81734,12 @@ async function writeModelCatalogCache(file, catalog, current = () => true) {
|
|
|
81464
81734
|
await rm9(temp, { force: true });
|
|
81465
81735
|
}
|
|
81466
81736
|
}
|
|
81467
|
-
var cacheFile = () =>
|
|
81737
|
+
var cacheFile = () => join48(resolveHome(), "model-catalog.json");
|
|
81468
81738
|
var modelCatalogService = createModelCatalogService({
|
|
81469
81739
|
probe: probeModelCatalog,
|
|
81470
81740
|
key: catalogProbeKey,
|
|
81471
81741
|
read: async () => {
|
|
81472
|
-
const content = await
|
|
81742
|
+
const content = await readFile9(cacheFile(), "utf8");
|
|
81473
81743
|
if (Buffer.byteLength(content) > 4 * 1024 * 1024) throw new Error("oversized cache");
|
|
81474
81744
|
return JSON.parse(content);
|
|
81475
81745
|
},
|
|
@@ -82653,8 +82923,8 @@ init_db();
|
|
|
82653
82923
|
init_pty();
|
|
82654
82924
|
init_config5();
|
|
82655
82925
|
init_inbox();
|
|
82656
|
-
import { mkdirSync as
|
|
82657
|
-
import { join as
|
|
82926
|
+
import { mkdirSync as mkdirSync20, writeFileSync as writeFileSync20 } from "node:fs";
|
|
82927
|
+
import { join as join49 } from "node:path";
|
|
82658
82928
|
init_human_input();
|
|
82659
82929
|
var idle = /* @__PURE__ */ new Set();
|
|
82660
82930
|
var sending = /* @__PURE__ */ new Set();
|
|
@@ -82665,7 +82935,7 @@ function isLeadIdle(id2) {
|
|
|
82665
82935
|
return idle.has(id2);
|
|
82666
82936
|
}
|
|
82667
82937
|
var HUMAN_QUIET_MS = 12e4;
|
|
82668
|
-
var batchDir = (projectId) =>
|
|
82938
|
+
var batchDir = (projectId) => join49(resolveHome(), "lead-batches", projectId);
|
|
82669
82939
|
var briefFn = async () => "(situasi tak tersedia \u2014 panggil /brief)";
|
|
82670
82940
|
function setBriefBuilder(fn) {
|
|
82671
82941
|
briefFn = fn;
|
|
@@ -82685,9 +82955,9 @@ var prodCourierDeps = {
|
|
|
82685
82955
|
brief: (p3) => briefFn(p3).catch(() => "(situasi tak tersedia \u2014 panggil /brief)"),
|
|
82686
82956
|
write: (projectId, text2) => {
|
|
82687
82957
|
const dir2 = batchDir(projectId);
|
|
82688
|
-
|
|
82689
|
-
const file =
|
|
82690
|
-
|
|
82958
|
+
mkdirSync20(dir2, { recursive: true });
|
|
82959
|
+
const file = join49(dir2, `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.md`);
|
|
82960
|
+
writeFileSync20(file, text2);
|
|
82691
82961
|
return file;
|
|
82692
82962
|
},
|
|
82693
82963
|
send: (id2, line) => sendToPane(id2, line),
|
|
@@ -83485,7 +83755,7 @@ init_src();
|
|
|
83485
83755
|
init_db();
|
|
83486
83756
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
83487
83757
|
import { mkdir as mkdir10, rename as rename6, writeFile as writeFile7 } from "node:fs/promises";
|
|
83488
|
-
import { join as
|
|
83758
|
+
import { join as join50 } from "node:path";
|
|
83489
83759
|
|
|
83490
83760
|
// src/services/device-auth.ts
|
|
83491
83761
|
function bearerToken2(req) {
|
|
@@ -83743,9 +84013,9 @@ async function sync_default(app3) {
|
|
|
83743
84013
|
const deviceId = req.device.id;
|
|
83744
84014
|
const withTranscriptKey = await Promise.all(parsed.data.entries.map(async (e) => {
|
|
83745
84015
|
if (!e.transcript) return e;
|
|
83746
|
-
const dir2 =
|
|
84016
|
+
const dir2 = join50(resolveDataDirs().home, "remote-transcripts", deviceId);
|
|
83747
84017
|
await mkdir10(dir2, { recursive: true, mode: 448 });
|
|
83748
|
-
const final =
|
|
84018
|
+
const final = join50(dir2, `${e.seq}.txt`);
|
|
83749
84019
|
const tmp = `${final}.tmp`;
|
|
83750
84020
|
await writeFile7(tmp, e.transcript, { encoding: "utf8", mode: 384 });
|
|
83751
84021
|
await rename6(tmp, final);
|
|
@@ -84300,9 +84570,9 @@ init_db();
|
|
|
84300
84570
|
init_db();
|
|
84301
84571
|
init_uploads();
|
|
84302
84572
|
init_src();
|
|
84303
|
-
import { createHash as
|
|
84573
|
+
import { createHash as createHash21, randomBytes as randomBytes7 } from "node:crypto";
|
|
84304
84574
|
function hashAccessKey(key) {
|
|
84305
|
-
return
|
|
84575
|
+
return createHash21("sha256").update(key).digest("hex");
|
|
84306
84576
|
}
|
|
84307
84577
|
function generateAccessKey() {
|
|
84308
84578
|
const key = "hnm_tkt_" + randomBytes7(24).toString("hex");
|
|
@@ -87148,9 +87418,9 @@ init_codex_trust();
|
|
|
87148
87418
|
init_qa_attachment_transfer();
|
|
87149
87419
|
init_uploads();
|
|
87150
87420
|
init_ask_via2();
|
|
87151
|
-
import { createHash as
|
|
87421
|
+
import { createHash as createHash22 } from "node:crypto";
|
|
87152
87422
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
87153
|
-
import { join as
|
|
87423
|
+
import { join as join52 } from "node:path";
|
|
87154
87424
|
var QaSessionError = class extends Error {
|
|
87155
87425
|
constructor(code, message, needsBind = false) {
|
|
87156
87426
|
super(message);
|
|
@@ -87158,7 +87428,7 @@ var QaSessionError = class extends Error {
|
|
|
87158
87428
|
this.needsBind = needsBind;
|
|
87159
87429
|
}
|
|
87160
87430
|
};
|
|
87161
|
-
var qaSessionId = (findingId) => `qa-${
|
|
87431
|
+
var qaSessionId = (findingId) => `qa-${createHash22("sha256").update(findingId).digest("hex").slice(0, 24)}`;
|
|
87162
87432
|
async function startQaFindingSession(d, fid) {
|
|
87163
87433
|
const f = d.findings.find((x) => x.id === fid);
|
|
87164
87434
|
if (!f) throw new QaSessionError(404, "temuan tidak ditemukan");
|
|
@@ -87177,7 +87447,7 @@ async function startQaFindingSession(d, fid) {
|
|
|
87177
87447
|
const saved = await saveSessionUpload(id2, bytes, a.mimeType);
|
|
87178
87448
|
files2.push(`${JSON.stringify(a.filename)} (${a.mimeType}): ${saved.path}`);
|
|
87179
87449
|
}
|
|
87180
|
-
const wt =
|
|
87450
|
+
const wt = join52(repoDir, ".worktrees", id2);
|
|
87181
87451
|
const branch = `qa/${id2.slice(3)}`;
|
|
87182
87452
|
const reused = realGit.worktreeAlive(wt);
|
|
87183
87453
|
try {
|
|
@@ -88720,9 +88990,9 @@ ${baru}`;
|
|
|
88720
88990
|
// src/services/portal-chat/workspace.ts
|
|
88721
88991
|
init_src();
|
|
88722
88992
|
init_db();
|
|
88723
|
-
import { mkdirSync as
|
|
88993
|
+
import { mkdirSync as mkdirSync21, mkdtempSync as mkdtempSync5, rmSync as rmSync14, writeFileSync as writeFileSync21 } from "node:fs";
|
|
88724
88994
|
import { tmpdir as tmpdir9 } from "node:os";
|
|
88725
|
-
import { join as
|
|
88995
|
+
import { join as join53 } from "node:path";
|
|
88726
88996
|
var STAGE_LABEL = {
|
|
88727
88997
|
brainstorming: "Dirumuskan",
|
|
88728
88998
|
objective: "Dirumuskan",
|
|
@@ -88765,7 +89035,7 @@ function renderTicketDoc(tickets2) {
|
|
|
88765
89035
|
${baris.join("\n")}`;
|
|
88766
89036
|
}
|
|
88767
89037
|
async function buildChatWorkspace(projectId) {
|
|
88768
|
-
const dir2 = mkdtempSync5(
|
|
89038
|
+
const dir2 = mkdtempSync5(join53(tmpdir9(), "hanoman-portal-chat-"));
|
|
88769
89039
|
try {
|
|
88770
89040
|
const project = await prisma.project.findUnique({
|
|
88771
89041
|
where: { id: projectId },
|
|
@@ -88780,7 +89050,7 @@ async function buildChatWorkspace(projectId) {
|
|
|
88780
89050
|
});
|
|
88781
89051
|
const files2 = [];
|
|
88782
89052
|
const tulis = (rel, isi) => {
|
|
88783
|
-
|
|
89053
|
+
writeFileSync21(join53(dir2, rel), isi, { mode: 384 });
|
|
88784
89054
|
files2.push(rel);
|
|
88785
89055
|
};
|
|
88786
89056
|
tulis("project.md", renderProjectDoc(project));
|
|
@@ -88788,16 +89058,16 @@ async function buildChatWorkspace(projectId) {
|
|
|
88788
89058
|
tulis("laporan.md", renderTicketDoc(tickets2.map((t) => toPortalTicket(t, null))));
|
|
88789
89059
|
const prds = await listPrds(projectId);
|
|
88790
89060
|
if (prds.length) {
|
|
88791
|
-
|
|
89061
|
+
mkdirSync21(join53(dir2, "dokumen"), { mode: 448 });
|
|
88792
89062
|
for (const prd of prds) {
|
|
88793
89063
|
const isi = await readPrd(projectId, prd.path);
|
|
88794
89064
|
if (!isi) continue;
|
|
88795
|
-
tulis(
|
|
89065
|
+
tulis(join53("dokumen", `${prd.slug.replaceAll("/", "-")}.md`), isi);
|
|
88796
89066
|
}
|
|
88797
89067
|
}
|
|
88798
|
-
return { dir: dir2, files: files2, cleanup: () =>
|
|
89068
|
+
return { dir: dir2, files: files2, cleanup: () => rmSync14(dir2, { recursive: true, force: true }) };
|
|
88799
89069
|
} catch (error) {
|
|
88800
|
-
|
|
89070
|
+
rmSync14(dir2, { recursive: true, force: true });
|
|
88801
89071
|
throw error;
|
|
88802
89072
|
}
|
|
88803
89073
|
}
|
|
@@ -89317,20 +89587,20 @@ init_src();
|
|
|
89317
89587
|
init_db();
|
|
89318
89588
|
|
|
89319
89589
|
// src/services/bootstrap.ts
|
|
89320
|
-
import { createHash as
|
|
89321
|
-
import { chmod as chmod2, lstat as lstat3, mkdir as mkdir11, readFile as
|
|
89322
|
-
import { join as
|
|
89590
|
+
import { createHash as createHash23, randomBytes as randomBytes11, timingSafeEqual as timingSafeEqual5 } from "node:crypto";
|
|
89591
|
+
import { chmod as chmod2, lstat as lstat3, mkdir as mkdir11, readFile as readFile10, unlink as unlink5, writeFile as writeFile8 } from "node:fs/promises";
|
|
89592
|
+
import { join as join54 } from "node:path";
|
|
89323
89593
|
var TTL_MS7 = 15 * 6e4;
|
|
89324
89594
|
var SETUP_TOKEN_FILE = "setup.token";
|
|
89325
89595
|
var BootstrapError = class extends Error {
|
|
89326
89596
|
code = "BOOTSTRAP_PROOF";
|
|
89327
89597
|
};
|
|
89328
|
-
var tokenPath = (home3) =>
|
|
89598
|
+
var tokenPath = (home3) => join54(home3, SETUP_TOKEN_FILE);
|
|
89329
89599
|
async function readStored(home3) {
|
|
89330
89600
|
try {
|
|
89331
89601
|
const info = await lstat3(tokenPath(home3));
|
|
89332
89602
|
if (info.isSymbolicLink() || !info.isFile()) throw new BootstrapError("invalid setup proof");
|
|
89333
|
-
const [token, expires] = (await
|
|
89603
|
+
const [token, expires] = (await readFile10(tokenPath(home3), "utf8")).trim().split("\n");
|
|
89334
89604
|
const expiresAt = Date.parse(expires ?? "");
|
|
89335
89605
|
if (!token || !Number.isFinite(expiresAt)) throw new BootstrapError("invalid setup proof");
|
|
89336
89606
|
return { token, expiresAt };
|
|
@@ -89370,8 +89640,8 @@ ${new Date(expiresAt).toISOString()}
|
|
|
89370
89640
|
}
|
|
89371
89641
|
async function verifySetupToken(candidate, home3, now = Date.now()) {
|
|
89372
89642
|
const stored = await readStored(home3);
|
|
89373
|
-
const got =
|
|
89374
|
-
const want =
|
|
89643
|
+
const got = createHash23("sha256").update(candidate).digest();
|
|
89644
|
+
const want = createHash23("sha256").update(stored.token).digest();
|
|
89375
89645
|
if (stored.expiresAt <= now || !timingSafeEqual5(got, want)) throw new BootstrapError("invalid setup proof");
|
|
89376
89646
|
}
|
|
89377
89647
|
async function consumeSetupToken(home3) {
|
|
@@ -90162,8 +90432,8 @@ init_db();
|
|
|
90162
90432
|
init_event_log();
|
|
90163
90433
|
init_transcript_store();
|
|
90164
90434
|
init_src2();
|
|
90165
|
-
import { readFile as
|
|
90166
|
-
import { join as
|
|
90435
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
90436
|
+
import { join as join55 } from "node:path";
|
|
90167
90437
|
function decodeCursor(c) {
|
|
90168
90438
|
if (!c) return null;
|
|
90169
90439
|
try {
|
|
@@ -90201,9 +90471,9 @@ async function readRemoteTranscript(id2) {
|
|
|
90201
90471
|
const row = await prisma.logEntry.findUnique({ where: { id: id2 } });
|
|
90202
90472
|
if (!row || row.lane !== "transcript" || !row.transcriptKey) return null;
|
|
90203
90473
|
if (row.deviceId === "local") return readTranscript(row.transcriptKey);
|
|
90204
|
-
const path =
|
|
90474
|
+
const path = join55(resolveDataDirs().home, "remote-transcripts", row.transcriptKey);
|
|
90205
90475
|
try {
|
|
90206
|
-
return await
|
|
90476
|
+
return await readFile11(path, "utf8");
|
|
90207
90477
|
} catch {
|
|
90208
90478
|
return null;
|
|
90209
90479
|
}
|
|
@@ -90398,7 +90668,7 @@ var SETUP_PUBLIC = /* @__PURE__ */ new Set([
|
|
|
90398
90668
|
"POST /api/setup"
|
|
90399
90669
|
]);
|
|
90400
90670
|
function buildApp({ requireAuth = true, agentDocFile, env = process.env } = {}) {
|
|
90401
|
-
const docFile = agentDocFile !== void 0 ? agentDocFile : pickGuideFile(dirname19(
|
|
90671
|
+
const docFile = agentDocFile !== void 0 ? agentDocFile : pickGuideFile(dirname19(fileURLToPath6(import.meta.url)), env, existsSync25);
|
|
90402
90672
|
const ingress = loadIngressPolicy(env);
|
|
90403
90673
|
const app3 = Fastify({ logger: false, trustProxy: trustProxyFromEnv(env) });
|
|
90404
90674
|
app3.setErrorHandler((error, _req, reply) => {
|
|
@@ -90547,7 +90817,7 @@ function buildApp({ requireAuth = true, agentDocFile, env = process.env } = {})
|
|
|
90547
90817
|
await api.register(qaBacklog);
|
|
90548
90818
|
}, { prefix: "/api" });
|
|
90549
90819
|
if (shouldServeWeb(env)) {
|
|
90550
|
-
const dist = pickWebDir(dirname19(
|
|
90820
|
+
const dist = pickWebDir(dirname19(fileURLToPath6(import.meta.url)), env, existsSync25);
|
|
90551
90821
|
if (dist) {
|
|
90552
90822
|
app3.register(fastifyStatic, { root: dist });
|
|
90553
90823
|
app3.setNotFoundHandler((req, reply) => req.url.startsWith("/api") ? reply.code(404).send({ error: "not found" }) : req.url.startsWith("/assets/") ? reply.code(404).type("text/plain").send("not found") : reply.sendFile("index.html"));
|
|
@@ -90992,6 +91262,7 @@ async function resumeReconciledSessions(cutoff, deps = prodDeps4) {
|
|
|
90992
91262
|
init_event_log();
|
|
90993
91263
|
init_settings3();
|
|
90994
91264
|
init_custom_agents2();
|
|
91265
|
+
init_bundled_skills();
|
|
90995
91266
|
init_pty();
|
|
90996
91267
|
init_bootstrap();
|
|
90997
91268
|
|
|
@@ -91483,9 +91754,9 @@ async function pruneLogs(now, retention, opts2 = {}) {
|
|
|
91483
91754
|
}
|
|
91484
91755
|
async function reconcileRemoteTranscripts() {
|
|
91485
91756
|
const { readdir: readdir10, unlink: unlink6, stat: stat8 } = await import("node:fs/promises");
|
|
91486
|
-
const { join:
|
|
91757
|
+
const { join: join56 } = await import("node:path");
|
|
91487
91758
|
const { resolveDataDirs: resolveDataDirs2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
91488
|
-
const root =
|
|
91759
|
+
const root = join56(resolveDataDirs2().home, "remote-transcripts");
|
|
91489
91760
|
let deviceDirs;
|
|
91490
91761
|
try {
|
|
91491
91762
|
deviceDirs = await readdir10(root);
|
|
@@ -91499,16 +91770,16 @@ async function reconcileRemoteTranscripts() {
|
|
|
91499
91770
|
for (const dev of deviceDirs) {
|
|
91500
91771
|
let files2;
|
|
91501
91772
|
try {
|
|
91502
|
-
files2 = await readdir10(
|
|
91773
|
+
files2 = await readdir10(join56(root, dev));
|
|
91503
91774
|
} catch {
|
|
91504
91775
|
continue;
|
|
91505
91776
|
}
|
|
91506
91777
|
for (const f of files2) {
|
|
91507
91778
|
const key = `${dev}/${f}`;
|
|
91508
91779
|
if (referenced.has(key)) continue;
|
|
91509
|
-
const st2 = await stat8(
|
|
91780
|
+
const st2 = await stat8(join56(root, dev, f)).catch(() => null);
|
|
91510
91781
|
if (!st2 || Date.now() - st2.mtimeMs < gracePeriodMs) continue;
|
|
91511
|
-
await unlink6(
|
|
91782
|
+
await unlink6(join56(root, dev, f)).catch(() => {
|
|
91512
91783
|
});
|
|
91513
91784
|
orphans++;
|
|
91514
91785
|
}
|
|
@@ -91743,6 +92014,9 @@ bootstrapReady.then(async () => {
|
|
|
91743
92014
|
console.error("config runtime gagal dimuat \u2014 memakai env/default:", e);
|
|
91744
92015
|
}
|
|
91745
92016
|
await installCustomAgents();
|
|
92017
|
+
const skills2 = seedBundledSkills(home2);
|
|
92018
|
+
if (skills2.seeded.length || skills2.updated.length)
|
|
92019
|
+
console.log(`skill bundel: di-seed ${skills2.seeded.join(", ") || "-"} \xB7 diperbarui ${skills2.updated.join(", ") || "-"}`);
|
|
91746
92020
|
return app2.listen({ port, host });
|
|
91747
92021
|
}).then(async () => {
|
|
91748
92022
|
console.log(`hanoman api ${host}:${port}`);
|