@jiraacp/cli 2026.405.4
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/README.md +283 -0
- package/dist/abort-GQE4OI5S.js +103 -0
- package/dist/abort-GQE4OI5S.js.map +1 -0
- package/dist/abort-VMRQOADY.js +96 -0
- package/dist/abort-VMRQOADY.js.map +1 -0
- package/dist/bot-WOTETAJY.js +13 -0
- package/dist/bot-WOTETAJY.js.map +1 -0
- package/dist/cancel-clarification-4G5S2HJZ.js +64 -0
- package/dist/cancel-clarification-4G5S2HJZ.js.map +1 -0
- package/dist/chunk-3U373M37.js +67 -0
- package/dist/chunk-3U373M37.js.map +1 -0
- package/dist/chunk-3YHD4SIN.js +97 -0
- package/dist/chunk-3YHD4SIN.js.map +1 -0
- package/dist/chunk-6IY6CRUJ.js +690 -0
- package/dist/chunk-6IY6CRUJ.js.map +1 -0
- package/dist/chunk-B6OA3XJK.js +1167 -0
- package/dist/chunk-B6OA3XJK.js.map +1 -0
- package/dist/chunk-BM4R6NST.js +191 -0
- package/dist/chunk-BM4R6NST.js.map +1 -0
- package/dist/chunk-FLPIU2QO.js +77 -0
- package/dist/chunk-FLPIU2QO.js.map +1 -0
- package/dist/chunk-H7YXX4UA.js +86 -0
- package/dist/chunk-H7YXX4UA.js.map +1 -0
- package/dist/chunk-IT74N3UH.js +19 -0
- package/dist/chunk-IT74N3UH.js.map +1 -0
- package/dist/chunk-JOT4UVSO.js +186 -0
- package/dist/chunk-JOT4UVSO.js.map +1 -0
- package/dist/chunk-KSJKCLEJ.js +222 -0
- package/dist/chunk-KSJKCLEJ.js.map +1 -0
- package/dist/chunk-LIEW4ULF.js +139 -0
- package/dist/chunk-LIEW4ULF.js.map +1 -0
- package/dist/chunk-M4V3YOCY.js +82 -0
- package/dist/chunk-M4V3YOCY.js.map +1 -0
- package/dist/chunk-MMWQHH25.js +207 -0
- package/dist/chunk-MMWQHH25.js.map +1 -0
- package/dist/chunk-OJ4CNF73.js +78 -0
- package/dist/chunk-OJ4CNF73.js.map +1 -0
- package/dist/chunk-PFJAC3RO.js +137 -0
- package/dist/chunk-PFJAC3RO.js.map +1 -0
- package/dist/chunk-PVKVCUNR.js +159 -0
- package/dist/chunk-PVKVCUNR.js.map +1 -0
- package/dist/chunk-RXT4WSIY.js +35 -0
- package/dist/chunk-RXT4WSIY.js.map +1 -0
- package/dist/chunk-RZK74PDF.js +34 -0
- package/dist/chunk-RZK74PDF.js.map +1 -0
- package/dist/chunk-UDTWVKRX.js +68 -0
- package/dist/chunk-UDTWVKRX.js.map +1 -0
- package/dist/chunk-VCEONSWJ.js +307 -0
- package/dist/chunk-VCEONSWJ.js.map +1 -0
- package/dist/chunk-VWBCDZWQ.js +119 -0
- package/dist/chunk-VWBCDZWQ.js.map +1 -0
- package/dist/chunk-WEJCTFQB.js +228 -0
- package/dist/chunk-WEJCTFQB.js.map +1 -0
- package/dist/chunk-YJK7IRPI.js +223 -0
- package/dist/chunk-YJK7IRPI.js.map +1 -0
- package/dist/claude-md-HQ6L4CRP.js +8 -0
- package/dist/claude-md-HQ6L4CRP.js.map +1 -0
- package/dist/cli.js +276 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands-RG45VBTZ.js +407 -0
- package/dist/commands-RG45VBTZ.js.map +1 -0
- package/dist/commands-WYVRVE5Z.js +400 -0
- package/dist/commands-WYVRVE5Z.js.map +1 -0
- package/dist/config-edit-G7O56HXO.js +50 -0
- package/dist/config-edit-G7O56HXO.js.map +1 -0
- package/dist/config-set-QN3JRNZL.js +63 -0
- package/dist/config-set-QN3JRNZL.js.map +1 -0
- package/dist/daemon-CGBV55JK.js +104 -0
- package/dist/daemon-CGBV55JK.js.map +1 -0
- package/dist/dashboard-YVFJ5DXR.js +143 -0
- package/dist/dashboard-YVFJ5DXR.js.map +1 -0
- package/dist/doctor-BPTLVLTD.js +98 -0
- package/dist/doctor-BPTLVLTD.js.map +1 -0
- package/dist/human-loop-RBTA2TYK.js +16 -0
- package/dist/human-loop-RBTA2TYK.js.map +1 -0
- package/dist/human-loop-XGWXUNCS.js +18 -0
- package/dist/human-loop-XGWXUNCS.js.map +1 -0
- package/dist/index.d.ts +583 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/loader-DGW7HCJ5.js +21 -0
- package/dist/loader-DGW7HCJ5.js.map +1 -0
- package/dist/logs-JUVQWN6C.js +93 -0
- package/dist/logs-JUVQWN6C.js.map +1 -0
- package/dist/mcp.js +132 -0
- package/dist/mcp.js.map +1 -0
- package/dist/orchestrator-3MGXX3QW.js +22 -0
- package/dist/orchestrator-3MGXX3QW.js.map +1 -0
- package/dist/orchestrator-BVUKN5N3.js +13 -0
- package/dist/orchestrator-BVUKN5N3.js.map +1 -0
- package/dist/pause-FLDZ3OD6.js +62 -0
- package/dist/pause-FLDZ3OD6.js.map +1 -0
- package/dist/projects-QMIGNW7U.js +129 -0
- package/dist/projects-QMIGNW7U.js.map +1 -0
- package/dist/replay-M4JEG4Z4.js +151 -0
- package/dist/replay-M4JEG4Z4.js.map +1 -0
- package/dist/schedule-CDHD77VZ.js +17 -0
- package/dist/schedule-CDHD77VZ.js.map +1 -0
- package/dist/serve-XI7JTIPZ.js +231 -0
- package/dist/serve-XI7JTIPZ.js.map +1 -0
- package/dist/sprint-KZZWVNK6.js +200 -0
- package/dist/sprint-KZZWVNK6.js.map +1 -0
- package/dist/status-I6GU2LWE.js +48 -0
- package/dist/status-I6GU2LWE.js.map +1 -0
- package/dist/topic-manager-4AMEPMFI.js +12 -0
- package/dist/topic-manager-4AMEPMFI.js.map +1 -0
- package/dist/triage-WNHGPVZQ.js +251 -0
- package/dist/triage-WNHGPVZQ.js.map +1 -0
- package/dist/usage-AWWBI37F.js +155 -0
- package/dist/usage-AWWBI37F.js.map +1 -0
- package/dist/wizard-CYEJJLNF.js +190 -0
- package/dist/wizard-CYEJJLNF.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/replay.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport pc from \"picocolors\";\nimport { getRunDir, getEvents } from \"../pipeline/state.js\";\nimport type { PipelineEvent } from \"../pipeline/state.js\";\nimport { createLogger } from \"../utils/logger.js\";\n\nconst log = createLogger(\"replay\");\n\nfunction formatTime(iso: string): string {\n return iso.slice(11, 19);\n}\n\nfunction elapsedSeconds(from: string, to: string): string {\n const diff = new Date(to).getTime() - new Date(from).getTime();\n return `${(diff / 1000).toFixed(1)}s`;\n}\n\nfunction printEvent(event: PipelineEvent, startedAt: string | null): void {\n const ts = pc.gray(`[${formatTime(event.timestamp)}]`);\n\n switch (event.type) {\n case \"STARTED\":\n process.stdout.write(`${ts} ${pc.cyan(\"▶ STARTED\")}\\n`);\n break;\n\n case \"STAGE_STARTED\":\n process.stdout.write(\n `${ts} ${pc.blue(` → stage:${event.stage} started`)}\\n`,\n );\n break;\n\n case \"STAGE_COMPLETED\": {\n const elapsed = startedAt\n ? pc.gray(` (${elapsedSeconds(startedAt, event.timestamp)})`)\n : \"\";\n process.stdout.write(\n `${ts} ${pc.green(` ✓ stage:${event.stage}`)}${elapsed}\\n`,\n );\n break;\n }\n\n case \"STAGE_FAILED\":\n process.stdout.write(\n `${ts} ${pc.red(` ✗ stage:${event.stage} ${event.error}`)}\\n`,\n );\n break;\n\n case \"STAGE_SKIPPED\":\n process.stdout.write(\n `${ts} ${pc.gray(` ⊘ stage:${event.stage} skipped — ${event.reason}`)}\\n`,\n );\n break;\n\n case \"CLARIFICATION_REQUESTED\": {\n process.stdout.write(\n `${ts} ${pc.yellow(\" ⚠ CLARIFICATION REQUESTED\")}\\n`,\n );\n for (const q of event.questions) {\n process.stdout.write(` ${pc.yellow(\"•\")} ${q}\\n`);\n }\n break;\n }\n\n case \"CLARIFICATION_RECEIVED\": {\n const preview = event.answers.slice(0, 80);\n process.stdout.write(\n `${ts} ${pc.green(\" ✓ CLARIFICATION RECEIVED\")} ${pc.gray(preview)}\\n`,\n );\n break;\n }\n\n case \"HUMAN_APPROVAL_REQUESTED\":\n process.stdout.write(\n `${ts} ${pc.yellow(\" ⚠ HUMAN APPROVAL REQUESTED\")}\\n`,\n );\n break;\n\n case \"HUMAN_APPROVED\":\n process.stdout.write(`${ts} ${pc.green(\" ✓ HUMAN APPROVED\")}\\n`);\n break;\n\n case \"HUMAN_REJECTED\":\n process.stdout.write(\n `${ts} ${pc.red(` ✗ HUMAN REJECTED ${event.reason}`)}\\n`,\n );\n break;\n\n case \"PIPELINE_COMPLETED\": {\n const elapsed = startedAt\n ? pc.gray(` (${elapsedSeconds(startedAt, event.timestamp)})`)\n : \"\";\n process.stdout.write(\n `${ts} ${pc.bold(pc.green(\"✓ PIPELINE COMPLETED\"))}${elapsed}\\n`,\n );\n break;\n }\n\n case \"PIPELINE_ABORTED\": {\n const reason = event.reason ? ` — ${event.reason}` : \"\";\n process.stdout.write(\n `${ts} ${pc.bold(pc.red(`✗ PIPELINE ABORTED${reason}`))}\\n`,\n );\n break;\n }\n\n default: {\n // Compile error here if a new PipelineEvent type is added without handling it\n const _exhaustive: never = event;\n const fallback = _exhaustive as { type: string };\n process.stdout.write(`${ts} ${pc.gray(fallback.type)}\\n`);\n break;\n }\n }\n}\n\nexport async function replayRun(\n ticketKey: string,\n projectName: string,\n): Promise<void> {\n log.debug({ ticketKey, projectName }, \"replay started\");\n\n const runDir = getRunDir(projectName, ticketKey);\n\n if (!fs.existsSync(runDir)) {\n process.stderr.write(\n `Error: no run directory found for ${ticketKey} (project: ${projectName})\\n`,\n );\n process.exitCode = 1;\n return;\n }\n\n const events = getEvents(runDir);\n\n if (events.length === 0) {\n process.stderr.write(\n `Error: no events found for ${ticketKey} (project: ${projectName})\\n`,\n );\n process.exitCode = 1;\n return;\n }\n\n const startEvent = events.find((e) => e.type === \"STARTED\");\n const startedAt = startEvent?.timestamp ?? null;\n\n const headerTimestamp = startedAt ? pc.gray(` started ${startedAt}`) : \"\";\n process.stdout.write(\n `\\n${pc.bold(pc.cyan(`⟳ Replay: ${ticketKey}`))}${headerTimestamp}\\n\\n`,\n );\n\n for (const event of events) {\n printEvent(event, startedAt);\n }\n\n process.stdout.write(\"\\n\");\n}\n"],"mappings":";;;;;;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AAKf,IAAM,MAAM,aAAa,QAAQ;AAEjC,SAAS,WAAW,KAAqB;AACvC,SAAO,IAAI,MAAM,IAAI,EAAE;AACzB;AAEA,SAAS,eAAe,MAAc,IAAoB;AACxD,QAAM,OAAO,IAAI,KAAK,EAAE,EAAE,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,QAAQ;AAC7D,SAAO,IAAI,OAAO,KAAM,QAAQ,CAAC,CAAC;AACpC;AAEA,SAAS,WAAW,OAAsB,WAAgC;AACxE,QAAM,KAAK,GAAG,KAAK,IAAI,WAAW,MAAM,SAAS,CAAC,GAAG;AAErD,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,cAAQ,OAAO,MAAM,GAAG,EAAE,IAAI,GAAG,KAAK,gBAAW,CAAC;AAAA,CAAI;AACtD;AAAA,IAEF,KAAK;AACH,cAAQ,OAAO;AAAA,QACb,GAAG,EAAE,IAAI,GAAG,KAAK,kBAAa,MAAM,KAAK,UAAU,CAAC;AAAA;AAAA,MACtD;AACA;AAAA,IAEF,KAAK,mBAAmB;AACtB,YAAM,UAAU,YACZ,GAAG,KAAK,KAAK,eAAe,WAAW,MAAM,SAAS,CAAC,GAAG,IAC1D;AACJ,cAAQ,OAAO;AAAA,QACb,GAAG,EAAE,IAAI,GAAG,MAAM,kBAAa,MAAM,KAAK,EAAE,CAAC,GAAG,OAAO;AAAA;AAAA,MACzD;AACA;AAAA,IACF;AAAA,IAEA,KAAK;AACH,cAAQ,OAAO;AAAA,QACb,GAAG,EAAE,IAAI,GAAG,IAAI,kBAAa,MAAM,KAAK,KAAK,MAAM,KAAK,EAAE,CAAC;AAAA;AAAA,MAC7D;AACA;AAAA,IAEF,KAAK;AACH,cAAQ,OAAO;AAAA,QACb,GAAG,EAAE,IAAI,GAAG,KAAK,kBAAa,MAAM,KAAK,mBAAc,MAAM,MAAM,EAAE,CAAC;AAAA;AAAA,MACxE;AACA;AAAA,IAEF,KAAK,2BAA2B;AAC9B,cAAQ,OAAO;AAAA,QACb,GAAG,EAAE,IAAI,GAAG,OAAO,kCAA6B,CAAC;AAAA;AAAA,MACnD;AACA,iBAAW,KAAK,MAAM,WAAW;AAC/B,gBAAQ,OAAO,MAAM,UAAU,GAAG,OAAO,QAAG,CAAC,IAAI,CAAC;AAAA,CAAI;AAAA,MACxD;AACA;AAAA,IACF;AAAA,IAEA,KAAK,0BAA0B;AAC7B,YAAM,UAAU,MAAM,QAAQ,MAAM,GAAG,EAAE;AACzC,cAAQ,OAAO;AAAA,QACb,GAAG,EAAE,IAAI,GAAG,MAAM,iCAA4B,CAAC,IAAI,GAAG,KAAK,OAAO,CAAC;AAAA;AAAA,MACrE;AACA;AAAA,IACF;AAAA,IAEA,KAAK;AACH,cAAQ,OAAO;AAAA,QACb,GAAG,EAAE,IAAI,GAAG,OAAO,mCAA8B,CAAC;AAAA;AAAA,MACpD;AACA;AAAA,IAEF,KAAK;AACH,cAAQ,OAAO,MAAM,GAAG,EAAE,IAAI,GAAG,MAAM,yBAAoB,CAAC;AAAA,CAAI;AAChE;AAAA,IAEF,KAAK;AACH,cAAQ,OAAO;AAAA,QACb,GAAG,EAAE,IAAI,GAAG,IAAI,4BAAuB,MAAM,MAAM,EAAE,CAAC;AAAA;AAAA,MACxD;AACA;AAAA,IAEF,KAAK,sBAAsB;AACzB,YAAM,UAAU,YACZ,GAAG,KAAK,KAAK,eAAe,WAAW,MAAM,SAAS,CAAC,GAAG,IAC1D;AACJ,cAAQ,OAAO;AAAA,QACb,GAAG,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,2BAAsB,CAAC,CAAC,GAAG,OAAO;AAAA;AAAA,MAC9D;AACA;AAAA,IACF;AAAA,IAEA,KAAK,oBAAoB;AACvB,YAAM,SAAS,MAAM,SAAS,WAAM,MAAM,MAAM,KAAK;AACrD,cAAQ,OAAO;AAAA,QACb,GAAG,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,0BAAqB,MAAM,EAAE,CAAC,CAAC;AAAA;AAAA,MACzD;AACA;AAAA,IACF;AAAA,IAEA,SAAS;AAEP,YAAM,cAAqB;AAC3B,YAAM,WAAW;AACjB,cAAQ,OAAO,MAAM,GAAG,EAAE,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC;AAAA,CAAI;AACxD;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAsB,UACpB,WACA,aACe;AACf,MAAI,MAAM,EAAE,WAAW,YAAY,GAAG,gBAAgB;AAEtD,QAAM,SAAS,UAAU,aAAa,SAAS;AAE/C,MAAI,CAAC,GAAG,WAAW,MAAM,GAAG;AAC1B,YAAQ,OAAO;AAAA,MACb,qCAAqC,SAAS,cAAc,WAAW;AAAA;AAAA,IACzE;AACA,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,SAAS,UAAU,MAAM;AAE/B,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,OAAO;AAAA,MACb,8BAA8B,SAAS,cAAc,WAAW;AAAA;AAAA,IAClE;AACA,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,aAAa,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS;AAC1D,QAAM,YAAY,YAAY,aAAa;AAE3C,QAAM,kBAAkB,YAAY,GAAG,KAAK,aAAa,SAAS,EAAE,IAAI;AACxE,UAAQ,OAAO;AAAA,IACb;AAAA,EAAK,GAAG,KAAK,GAAG,KAAK,kBAAa,SAAS,EAAE,CAAC,CAAC,GAAG,eAAe;AAAA;AAAA;AAAA,EACnE;AAEA,aAAW,SAAS,QAAQ;AAC1B,eAAW,OAAO,SAAS;AAAA,EAC7B;AAEA,UAAQ,OAAO,MAAM,IAAI;AAC3B;","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
cronMatchesNow,
|
|
4
|
+
loadSchedules,
|
|
5
|
+
scheduleAdd,
|
|
6
|
+
scheduleList,
|
|
7
|
+
scheduleRemove
|
|
8
|
+
} from "./chunk-PFJAC3RO.js";
|
|
9
|
+
import "./chunk-IT74N3UH.js";
|
|
10
|
+
export {
|
|
11
|
+
cronMatchesNow,
|
|
12
|
+
loadSchedules,
|
|
13
|
+
scheduleAdd,
|
|
14
|
+
scheduleList,
|
|
15
|
+
scheduleRemove
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=schedule-CDHD77VZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
cronMatchesNow,
|
|
4
|
+
loadSchedules
|
|
5
|
+
} from "./chunk-PFJAC3RO.js";
|
|
6
|
+
import {
|
|
7
|
+
runPipeline
|
|
8
|
+
} from "./chunk-6IY6CRUJ.js";
|
|
9
|
+
import "./chunk-RXT4WSIY.js";
|
|
10
|
+
import "./chunk-VCEONSWJ.js";
|
|
11
|
+
import "./chunk-UDTWVKRX.js";
|
|
12
|
+
import "./chunk-JOT4UVSO.js";
|
|
13
|
+
import "./chunk-YJK7IRPI.js";
|
|
14
|
+
import "./chunk-FLPIU2QO.js";
|
|
15
|
+
import "./chunk-PVKVCUNR.js";
|
|
16
|
+
import "./chunk-RZK74PDF.js";
|
|
17
|
+
import "./chunk-VWBCDZWQ.js";
|
|
18
|
+
import {
|
|
19
|
+
detectProjectName,
|
|
20
|
+
loadConfig
|
|
21
|
+
} from "./chunk-3YHD4SIN.js";
|
|
22
|
+
import "./chunk-LIEW4ULF.js";
|
|
23
|
+
import "./chunk-3U373M37.js";
|
|
24
|
+
import {
|
|
25
|
+
initBot
|
|
26
|
+
} from "./chunk-OJ4CNF73.js";
|
|
27
|
+
import {
|
|
28
|
+
createLogger
|
|
29
|
+
} from "./chunk-IT74N3UH.js";
|
|
30
|
+
|
|
31
|
+
// src/commands/serve.ts
|
|
32
|
+
import http from "http";
|
|
33
|
+
import { readFileSync } from "fs";
|
|
34
|
+
import { fileURLToPath } from "url";
|
|
35
|
+
import path from "path";
|
|
36
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
37
|
+
var pkg = JSON.parse(
|
|
38
|
+
readFileSync(path.join(__dirname, "..", "package.json"), "utf8")
|
|
39
|
+
);
|
|
40
|
+
var logger = createLogger("serve");
|
|
41
|
+
var startedAt = Date.now();
|
|
42
|
+
function parseBody(req) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
let body = "";
|
|
45
|
+
req.on("data", (chunk) => {
|
|
46
|
+
body += chunk.toString();
|
|
47
|
+
});
|
|
48
|
+
req.on("end", () => resolve(body));
|
|
49
|
+
req.on("error", reject);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function sendJson(res, status, data) {
|
|
53
|
+
const body = JSON.stringify(data);
|
|
54
|
+
res.writeHead(status, {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
"Content-Length": Buffer.byteLength(body)
|
|
57
|
+
});
|
|
58
|
+
res.end(body);
|
|
59
|
+
}
|
|
60
|
+
async function handleWebhook(req, res, secret) {
|
|
61
|
+
if (secret) {
|
|
62
|
+
const incoming = req.headers["x-jira-acp-secret"];
|
|
63
|
+
if (incoming !== secret) {
|
|
64
|
+
sendJson(res, 401, { error: "Unauthorized" });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const rawBody = await parseBody(req);
|
|
69
|
+
let payload;
|
|
70
|
+
try {
|
|
71
|
+
payload = JSON.parse(rawBody);
|
|
72
|
+
} catch {
|
|
73
|
+
sendJson(res, 400, { error: "Invalid JSON body" });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const ticketKey = payload.ticketKey ?? payload.issue?.key;
|
|
77
|
+
const projectName = payload.projectName ?? detectProjectName(process.cwd());
|
|
78
|
+
if (!ticketKey) {
|
|
79
|
+
sendJson(res, 400, { error: "Missing ticketKey or issue.key in payload" });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
logger.info(
|
|
83
|
+
{ ticketKey, projectName },
|
|
84
|
+
"Webhook received \u2014 triggering pipeline"
|
|
85
|
+
);
|
|
86
|
+
sendJson(res, 202, { accepted: true, ticketKey, projectName });
|
|
87
|
+
setImmediate(async () => {
|
|
88
|
+
try {
|
|
89
|
+
const config = loadConfig(projectName);
|
|
90
|
+
await runPipeline(ticketKey, config);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
logger.error(
|
|
93
|
+
{ ticketKey, err: err instanceof Error ? err.message : String(err) },
|
|
94
|
+
"Pipeline failed via webhook"
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function startScheduleRunner() {
|
|
100
|
+
setInterval(async () => {
|
|
101
|
+
const schedules = loadSchedules();
|
|
102
|
+
const now = /* @__PURE__ */ new Date();
|
|
103
|
+
for (const entry of schedules) {
|
|
104
|
+
if (!entry.enabled) continue;
|
|
105
|
+
if (!cronMatchesNow(entry.cron, now)) continue;
|
|
106
|
+
logger.info(
|
|
107
|
+
{ id: entry.id, project: entry.projectName, cron: entry.cron },
|
|
108
|
+
"Scheduled run triggered"
|
|
109
|
+
);
|
|
110
|
+
setImmediate(async () => {
|
|
111
|
+
try {
|
|
112
|
+
const config = loadConfig(entry.projectName);
|
|
113
|
+
await runPipeline("__scheduled__", config);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
logger.error(
|
|
116
|
+
{
|
|
117
|
+
id: entry.id,
|
|
118
|
+
err: err instanceof Error ? err.message : String(err)
|
|
119
|
+
},
|
|
120
|
+
"Scheduled pipeline failed"
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}, 6e4);
|
|
126
|
+
}
|
|
127
|
+
async function startServe(port) {
|
|
128
|
+
const secret = process.env["JIRA_ACP_WEBHOOK_SECRET"];
|
|
129
|
+
const server = http.createServer(
|
|
130
|
+
(req, res) => {
|
|
131
|
+
const url = req.url ?? "/";
|
|
132
|
+
const method = req.method ?? "GET";
|
|
133
|
+
logger.info({ method, url }, "Request received");
|
|
134
|
+
if (method === "GET" && url === "/health") {
|
|
135
|
+
sendJson(res, 200, {
|
|
136
|
+
status: "ok",
|
|
137
|
+
version: pkg.version,
|
|
138
|
+
uptimeSeconds: Math.floor((Date.now() - startedAt) / 1e3)
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (method === "POST" && url === "/webhook/jira") {
|
|
143
|
+
handleWebhook(req, res, secret).catch((err) => {
|
|
144
|
+
logger.error(
|
|
145
|
+
{ err: err instanceof Error ? err.message : String(err) },
|
|
146
|
+
"Webhook handler error"
|
|
147
|
+
);
|
|
148
|
+
if (!res.headersSent) {
|
|
149
|
+
sendJson(res, 500, { error: "Internal server error" });
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
sendJson(res, 404, { error: "Not found" });
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
server.listen(port, () => {
|
|
158
|
+
logger.info({ port }, "jiraACP serve started");
|
|
159
|
+
process.stdout.write(
|
|
160
|
+
`jiraACP serve listening on port ${port}
|
|
161
|
+
POST /webhook/jira \u2014 trigger pipeline via Jira automation
|
|
162
|
+
GET /health \u2014 health check
|
|
163
|
+
`
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
startScheduleRunner();
|
|
167
|
+
const projectNotifiers = [];
|
|
168
|
+
try {
|
|
169
|
+
const { listProjects, loadConfig: loadConfig2 } = await import("./loader-DGW7HCJ5.js");
|
|
170
|
+
const { getBot } = await import("./bot-WOTETAJY.js");
|
|
171
|
+
const { getOrCreateSystemTopic } = await import("./topic-manager-4AMEPMFI.js");
|
|
172
|
+
for (const name of listProjects()) {
|
|
173
|
+
try {
|
|
174
|
+
const cfg = loadConfig2(name);
|
|
175
|
+
if (!cfg.telegram?.botToken) continue;
|
|
176
|
+
if (projectNotifiers.length === 0) {
|
|
177
|
+
await initBot(cfg.telegram.botToken);
|
|
178
|
+
}
|
|
179
|
+
const bot = getBot(cfg.telegram.botToken);
|
|
180
|
+
const systemTopicId = await getOrCreateSystemTopic(
|
|
181
|
+
bot,
|
|
182
|
+
cfg.telegram.chatId,
|
|
183
|
+
name
|
|
184
|
+
).catch(() => void 0);
|
|
185
|
+
projectNotifiers.push({
|
|
186
|
+
chatId: cfg.telegram.chatId,
|
|
187
|
+
systemTopicId,
|
|
188
|
+
botToken: cfg.telegram.botToken
|
|
189
|
+
});
|
|
190
|
+
await bot.api.sendMessage(
|
|
191
|
+
cfg.telegram.chatId,
|
|
192
|
+
`\u{1F7E2} <b>jiraACP</b> v${pkg.version} started
|
|
193
|
+
Port: <code>${port}</code> \xB7 Project: <b>${name}</b>`,
|
|
194
|
+
{
|
|
195
|
+
parse_mode: "HTML",
|
|
196
|
+
...systemTopicId ? { message_thread_id: systemTopicId } : {}
|
|
197
|
+
}
|
|
198
|
+
).catch(() => void 0);
|
|
199
|
+
} catch {
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} catch (err) {
|
|
203
|
+
logger.warn({ err }, "Failed to init Telegram bot");
|
|
204
|
+
}
|
|
205
|
+
async function shutdown() {
|
|
206
|
+
logger.info("Shutting down serve");
|
|
207
|
+
if (projectNotifiers.length > 0) {
|
|
208
|
+
const { getBot } = await import("./bot-WOTETAJY.js");
|
|
209
|
+
for (const { chatId, systemTopicId, botToken } of projectNotifiers) {
|
|
210
|
+
const bot = getBot(botToken);
|
|
211
|
+
await bot.api.sendMessage(chatId, "\u{1F534} <b>jiraACP</b> server shutting down", {
|
|
212
|
+
parse_mode: "HTML",
|
|
213
|
+
...systemTopicId ? { message_thread_id: systemTopicId } : {}
|
|
214
|
+
}).catch(() => void 0);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
server.close(() => process.exit(0));
|
|
218
|
+
}
|
|
219
|
+
process.once("SIGINT", () => {
|
|
220
|
+
shutdown().catch(() => process.exit(0));
|
|
221
|
+
});
|
|
222
|
+
process.once("SIGTERM", () => {
|
|
223
|
+
shutdown().catch(() => process.exit(0));
|
|
224
|
+
});
|
|
225
|
+
await new Promise(() => {
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
export {
|
|
229
|
+
startServe
|
|
230
|
+
};
|
|
231
|
+
//# sourceMappingURL=serve-XI7JTIPZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/serve.ts"],"sourcesContent":["import http from \"node:http\";\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport path from \"node:path\";\nimport { createLogger } from \"../utils/logger.js\";\nimport { loadConfig, detectProjectName } from \"../config/loader.js\";\nimport { runPipeline } from \"../pipeline/orchestrator.js\";\nimport { loadSchedules, cronMatchesNow } from \"./schedule.js\";\nimport { initBot } from \"../integrations/telegram/bot.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(\n readFileSync(path.join(__dirname, \"..\", \"package.json\"), \"utf8\"),\n) as { version: string };\n\nconst logger = createLogger(\"serve\");\nconst startedAt = Date.now();\n\ninterface JiraWebhookPayload {\n issue?: { key?: string };\n ticketKey?: string;\n projectName?: string;\n}\n\nfunction parseBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n let body = \"\";\n req.on(\"data\", (chunk: Buffer) => {\n body += chunk.toString();\n });\n req.on(\"end\", () => resolve(body));\n req.on(\"error\", reject);\n });\n}\n\nfunction sendJson(\n res: http.ServerResponse,\n status: number,\n data: Record<string, unknown>,\n): void {\n const body = JSON.stringify(data);\n res.writeHead(status, {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(body),\n });\n res.end(body);\n}\n\nasync function handleWebhook(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n secret: string | undefined,\n): Promise<void> {\n // Validate secret header if configured\n if (secret) {\n const incoming = req.headers[\"x-jira-acp-secret\"];\n if (incoming !== secret) {\n sendJson(res, 401, { error: \"Unauthorized\" });\n return;\n }\n }\n\n const rawBody = await parseBody(req);\n let payload: JiraWebhookPayload;\n try {\n payload = JSON.parse(rawBody) as JiraWebhookPayload;\n } catch {\n sendJson(res, 400, { error: \"Invalid JSON body\" });\n return;\n }\n\n const ticketKey = payload.ticketKey ?? payload.issue?.key;\n const projectName = payload.projectName ?? detectProjectName(process.cwd());\n\n if (!ticketKey) {\n sendJson(res, 400, { error: \"Missing ticketKey or issue.key in payload\" });\n return;\n }\n\n logger.info(\n { ticketKey, projectName },\n \"Webhook received — triggering pipeline\",\n );\n sendJson(res, 202, { accepted: true, ticketKey, projectName });\n\n // Fire pipeline asynchronously — do not await\n setImmediate(async () => {\n try {\n const config = loadConfig(projectName);\n await runPipeline(ticketKey, config);\n } catch (err) {\n logger.error(\n { ticketKey, err: err instanceof Error ? err.message : String(err) },\n \"Pipeline failed via webhook\",\n );\n }\n });\n}\n\nfunction startScheduleRunner(): void {\n // Check schedules every minute\n setInterval(async () => {\n const schedules = loadSchedules();\n const now = new Date();\n for (const entry of schedules) {\n if (!entry.enabled) continue;\n if (!cronMatchesNow(entry.cron, now)) continue;\n\n logger.info(\n { id: entry.id, project: entry.projectName, cron: entry.cron },\n \"Scheduled run triggered\",\n );\n\n setImmediate(async () => {\n try {\n const config = loadConfig(entry.projectName);\n await runPipeline(\"__scheduled__\", config);\n } catch (err) {\n logger.error(\n {\n id: entry.id,\n err: err instanceof Error ? err.message : String(err),\n },\n \"Scheduled pipeline failed\",\n );\n }\n });\n }\n }, 60_000);\n}\n\nexport async function startServe(port: number): Promise<void> {\n const secret = process.env[\"JIRA_ACP_WEBHOOK_SECRET\"];\n\n const server = http.createServer(\n (req: http.IncomingMessage, res: http.ServerResponse) => {\n const url = req.url ?? \"/\";\n const method = req.method ?? \"GET\";\n\n logger.info({ method, url }, \"Request received\");\n\n if (method === \"GET\" && url === \"/health\") {\n sendJson(res, 200, {\n status: \"ok\",\n version: pkg.version,\n uptimeSeconds: Math.floor((Date.now() - startedAt) / 1_000),\n });\n return;\n }\n\n if (method === \"POST\" && url === \"/webhook/jira\") {\n handleWebhook(req, res, secret).catch((err: unknown) => {\n logger.error(\n { err: err instanceof Error ? err.message : String(err) },\n \"Webhook handler error\",\n );\n if (!res.headersSent) {\n sendJson(res, 500, { error: \"Internal server error\" });\n }\n });\n return;\n }\n\n sendJson(res, 404, { error: \"Not found\" });\n },\n );\n\n server.listen(port, () => {\n logger.info({ port }, \"jiraACP serve started\");\n process.stdout.write(\n `jiraACP serve listening on port ${port}\\n` +\n ` POST /webhook/jira — trigger pipeline via Jira automation\\n` +\n ` GET /health — health check\\n`,\n );\n });\n\n // Start cron schedule runner\n startScheduleRunner();\n\n // Start Telegram bot, create system notification topic, send startup message\n interface ProjectNotifier {\n chatId: number | string;\n systemTopicId: number | undefined;\n botToken: string;\n }\n const projectNotifiers: ProjectNotifier[] = [];\n\n try {\n const { listProjects, loadConfig } = await import(\"../config/loader.js\");\n const { getBot } = await import(\"../integrations/telegram/bot.js\");\n const { getOrCreateSystemTopic } =\n await import(\"../integrations/telegram/topic-manager.js\");\n\n for (const name of listProjects()) {\n try {\n const cfg = loadConfig(name);\n if (!cfg.telegram?.botToken) continue;\n\n if (projectNotifiers.length === 0) {\n await initBot(cfg.telegram.botToken);\n }\n\n const bot = getBot(cfg.telegram.botToken);\n const systemTopicId = await getOrCreateSystemTopic(\n bot,\n cfg.telegram.chatId,\n name,\n ).catch(() => undefined);\n\n projectNotifiers.push({\n chatId: cfg.telegram.chatId,\n systemTopicId,\n botToken: cfg.telegram.botToken,\n });\n\n await bot.api\n .sendMessage(\n cfg.telegram.chatId,\n `🟢 <b>jiraACP</b> v${pkg.version} started\\nPort: <code>${port}</code> · Project: <b>${name}</b>`,\n {\n parse_mode: \"HTML\",\n ...(systemTopicId ? { message_thread_id: systemTopicId } : {}),\n },\n )\n .catch(() => undefined);\n } catch {\n // skip misconfigured projects\n }\n }\n } catch (err) {\n logger.warn({ err }, \"Failed to init Telegram bot\");\n }\n\n // Graceful shutdown\n async function shutdown(): Promise<void> {\n logger.info(\"Shutting down serve\");\n\n if (projectNotifiers.length > 0) {\n const { getBot } = await import(\"../integrations/telegram/bot.js\");\n for (const { chatId, systemTopicId, botToken } of projectNotifiers) {\n const bot = getBot(botToken);\n await bot.api\n .sendMessage(chatId, \"🔴 <b>jiraACP</b> server shutting down\", {\n parse_mode: \"HTML\",\n ...(systemTopicId ? { message_thread_id: systemTopicId } : {}),\n })\n .catch(() => undefined);\n }\n }\n\n server.close(() => process.exit(0));\n }\n\n process.once(\"SIGINT\", () => {\n shutdown().catch(() => process.exit(0));\n });\n process.once(\"SIGTERM\", () => {\n shutdown().catch(() => process.exit(0));\n });\n\n // Keep process alive\n await new Promise<never>(() => {\n /* intentionally never resolves */\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,UAAU;AACjB,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AAOjB,IAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAC7D,IAAM,MAAM,KAAK;AAAA,EACf,aAAa,KAAK,KAAK,WAAW,MAAM,cAAc,GAAG,MAAM;AACjE;AAEA,IAAM,SAAS,aAAa,OAAO;AACnC,IAAM,YAAY,KAAK,IAAI;AAQ3B,SAAS,UAAU,KAA4C;AAC7D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,OAAO;AACX,QAAI,GAAG,QAAQ,CAAC,UAAkB;AAChC,cAAQ,MAAM,SAAS;AAAA,IACzB,CAAC;AACD,QAAI,GAAG,OAAO,MAAM,QAAQ,IAAI,CAAC;AACjC,QAAI,GAAG,SAAS,MAAM;AAAA,EACxB,CAAC;AACH;AAEA,SAAS,SACP,KACA,QACA,MACM;AACN,QAAM,OAAO,KAAK,UAAU,IAAI;AAChC,MAAI,UAAU,QAAQ;AAAA,IACpB,gBAAgB;AAAA,IAChB,kBAAkB,OAAO,WAAW,IAAI;AAAA,EAC1C,CAAC;AACD,MAAI,IAAI,IAAI;AACd;AAEA,eAAe,cACb,KACA,KACA,QACe;AAEf,MAAI,QAAQ;AACV,UAAM,WAAW,IAAI,QAAQ,mBAAmB;AAChD,QAAI,aAAa,QAAQ;AACvB,eAAS,KAAK,KAAK,EAAE,OAAO,eAAe,CAAC;AAC5C;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,UAAU,GAAG;AACnC,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,OAAO;AAAA,EAC9B,QAAQ;AACN,aAAS,KAAK,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACjD;AAAA,EACF;AAEA,QAAM,YAAY,QAAQ,aAAa,QAAQ,OAAO;AACtD,QAAM,cAAc,QAAQ,eAAe,kBAAkB,QAAQ,IAAI,CAAC;AAE1E,MAAI,CAAC,WAAW;AACd,aAAS,KAAK,KAAK,EAAE,OAAO,4CAA4C,CAAC;AACzE;AAAA,EACF;AAEA,SAAO;AAAA,IACL,EAAE,WAAW,YAAY;AAAA,IACzB;AAAA,EACF;AACA,WAAS,KAAK,KAAK,EAAE,UAAU,MAAM,WAAW,YAAY,CAAC;AAG7D,eAAa,YAAY;AACvB,QAAI;AACF,YAAM,SAAS,WAAW,WAAW;AACrC,YAAM,YAAY,WAAW,MAAM;AAAA,IACrC,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,EAAE,WAAW,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,QACnE;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,SAAS,sBAA4B;AAEnC,cAAY,YAAY;AACtB,UAAM,YAAY,cAAc;AAChC,UAAM,MAAM,oBAAI,KAAK;AACrB,eAAW,SAAS,WAAW;AAC7B,UAAI,CAAC,MAAM,QAAS;AACpB,UAAI,CAAC,eAAe,MAAM,MAAM,GAAG,EAAG;AAEtC,aAAO;AAAA,QACL,EAAE,IAAI,MAAM,IAAI,SAAS,MAAM,aAAa,MAAM,MAAM,KAAK;AAAA,QAC7D;AAAA,MACF;AAEA,mBAAa,YAAY;AACvB,YAAI;AACF,gBAAM,SAAS,WAAW,MAAM,WAAW;AAC3C,gBAAM,YAAY,iBAAiB,MAAM;AAAA,QAC3C,SAAS,KAAK;AACZ,iBAAO;AAAA,YACL;AAAA,cACE,IAAI,MAAM;AAAA,cACV,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,YACtD;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,GAAG,GAAM;AACX;AAEA,eAAsB,WAAW,MAA6B;AAC5D,QAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,QAAM,SAAS,KAAK;AAAA,IAClB,CAAC,KAA2B,QAA6B;AACvD,YAAM,MAAM,IAAI,OAAO;AACvB,YAAM,SAAS,IAAI,UAAU;AAE7B,aAAO,KAAK,EAAE,QAAQ,IAAI,GAAG,kBAAkB;AAE/C,UAAI,WAAW,SAAS,QAAQ,WAAW;AACzC,iBAAS,KAAK,KAAK;AAAA,UACjB,QAAQ;AAAA,UACR,SAAS,IAAI;AAAA,UACb,eAAe,KAAK,OAAO,KAAK,IAAI,IAAI,aAAa,GAAK;AAAA,QAC5D,CAAC;AACD;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,QAAQ,iBAAiB;AAChD,sBAAc,KAAK,KAAK,MAAM,EAAE,MAAM,CAAC,QAAiB;AACtD,iBAAO;AAAA,YACL,EAAE,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,YACxD;AAAA,UACF;AACA,cAAI,CAAC,IAAI,aAAa;AACpB,qBAAS,KAAK,KAAK,EAAE,OAAO,wBAAwB,CAAC;AAAA,UACvD;AAAA,QACF,CAAC;AACD;AAAA,MACF;AAEA,eAAS,KAAK,KAAK,EAAE,OAAO,YAAY,CAAC;AAAA,IAC3C;AAAA,EACF;AAEA,SAAO,OAAO,MAAM,MAAM;AACxB,WAAO,KAAK,EAAE,KAAK,GAAG,uBAAuB;AAC7C,YAAQ,OAAO;AAAA,MACb,mCAAmC,IAAI;AAAA;AAAA;AAAA;AAAA,IAGzC;AAAA,EACF,CAAC;AAGD,sBAAoB;AAQpB,QAAM,mBAAsC,CAAC;AAE7C,MAAI;AACF,UAAM,EAAE,cAAc,YAAAA,YAAW,IAAI,MAAM,OAAO,sBAAqB;AACvE,UAAM,EAAE,OAAO,IAAI,MAAM,OAAO,mBAAiC;AACjE,UAAM,EAAE,uBAAuB,IAC7B,MAAM,OAAO,6BAA2C;AAE1D,eAAW,QAAQ,aAAa,GAAG;AACjC,UAAI;AACF,cAAM,MAAMA,YAAW,IAAI;AAC3B,YAAI,CAAC,IAAI,UAAU,SAAU;AAE7B,YAAI,iBAAiB,WAAW,GAAG;AACjC,gBAAM,QAAQ,IAAI,SAAS,QAAQ;AAAA,QACrC;AAEA,cAAM,MAAM,OAAO,IAAI,SAAS,QAAQ;AACxC,cAAM,gBAAgB,MAAM;AAAA,UAC1B;AAAA,UACA,IAAI,SAAS;AAAA,UACb;AAAA,QACF,EAAE,MAAM,MAAM,MAAS;AAEvB,yBAAiB,KAAK;AAAA,UACpB,QAAQ,IAAI,SAAS;AAAA,UACrB;AAAA,UACA,UAAU,IAAI,SAAS;AAAA,QACzB,CAAC;AAED,cAAM,IAAI,IACP;AAAA,UACC,IAAI,SAAS;AAAA,UACb,6BAAsB,IAAI,OAAO;AAAA,cAAyB,IAAI,4BAAyB,IAAI;AAAA,UAC3F;AAAA,YACE,YAAY;AAAA,YACZ,GAAI,gBAAgB,EAAE,mBAAmB,cAAc,IAAI,CAAC;AAAA,UAC9D;AAAA,QACF,EACC,MAAM,MAAM,MAAS;AAAA,MAC1B,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,KAAK,EAAE,IAAI,GAAG,6BAA6B;AAAA,EACpD;AAGA,iBAAe,WAA0B;AACvC,WAAO,KAAK,qBAAqB;AAEjC,QAAI,iBAAiB,SAAS,GAAG;AAC/B,YAAM,EAAE,OAAO,IAAI,MAAM,OAAO,mBAAiC;AACjE,iBAAW,EAAE,QAAQ,eAAe,SAAS,KAAK,kBAAkB;AAClE,cAAM,MAAM,OAAO,QAAQ;AAC3B,cAAM,IAAI,IACP,YAAY,QAAQ,iDAA0C;AAAA,UAC7D,YAAY;AAAA,UACZ,GAAI,gBAAgB,EAAE,mBAAmB,cAAc,IAAI,CAAC;AAAA,QAC9D,CAAC,EACA,MAAM,MAAM,MAAS;AAAA,MAC1B;AAAA,IACF;AAEA,WAAO,MAAM,MAAM,QAAQ,KAAK,CAAC,CAAC;AAAA,EACpC;AAEA,UAAQ,KAAK,UAAU,MAAM;AAC3B,aAAS,EAAE,MAAM,MAAM,QAAQ,KAAK,CAAC,CAAC;AAAA,EACxC,CAAC;AACD,UAAQ,KAAK,WAAW,MAAM;AAC5B,aAAS,EAAE,MAAM,MAAM,QAAQ,KAAK,CAAC,CAAC;AAAA,EACxC,CAAC;AAGD,QAAM,IAAI,QAAe,MAAM;AAAA,EAE/B,CAAC;AACH;","names":["loadConfig"]}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
runPipeline
|
|
4
|
+
} from "./chunk-6IY6CRUJ.js";
|
|
5
|
+
import "./chunk-RXT4WSIY.js";
|
|
6
|
+
import "./chunk-VCEONSWJ.js";
|
|
7
|
+
import "./chunk-UDTWVKRX.js";
|
|
8
|
+
import {
|
|
9
|
+
getClient
|
|
10
|
+
} from "./chunk-JOT4UVSO.js";
|
|
11
|
+
import "./chunk-YJK7IRPI.js";
|
|
12
|
+
import "./chunk-FLPIU2QO.js";
|
|
13
|
+
import "./chunk-PVKVCUNR.js";
|
|
14
|
+
import "./chunk-RZK74PDF.js";
|
|
15
|
+
import "./chunk-VWBCDZWQ.js";
|
|
16
|
+
import {
|
|
17
|
+
loadConfig
|
|
18
|
+
} from "./chunk-3YHD4SIN.js";
|
|
19
|
+
import "./chunk-LIEW4ULF.js";
|
|
20
|
+
import "./chunk-3U373M37.js";
|
|
21
|
+
import "./chunk-OJ4CNF73.js";
|
|
22
|
+
import {
|
|
23
|
+
createLogger
|
|
24
|
+
} from "./chunk-IT74N3UH.js";
|
|
25
|
+
|
|
26
|
+
// src/commands/sprint.ts
|
|
27
|
+
import pc from "picocolors";
|
|
28
|
+
async function runSprint(opts) {
|
|
29
|
+
const logger = createLogger("sprint");
|
|
30
|
+
const config = loadConfig(opts.projectName);
|
|
31
|
+
const jiraClient = getClient(config.jira.instance);
|
|
32
|
+
const maxParallel = Math.min(
|
|
33
|
+
opts.parallel,
|
|
34
|
+
config.pipeline?.maxConcurrentRuns ?? 2
|
|
35
|
+
);
|
|
36
|
+
logger.info(
|
|
37
|
+
{ project: opts.projectName, maxParallel },
|
|
38
|
+
"Starting sprint run"
|
|
39
|
+
);
|
|
40
|
+
process.stdout.write(
|
|
41
|
+
`
|
|
42
|
+
${pc.bold("jiraACP sprint")} \u2014 ${opts.projectName} (max ${maxParallel} parallel)
|
|
43
|
+
|
|
44
|
+
`
|
|
45
|
+
);
|
|
46
|
+
process.stdout.write("Fetching sprint tickets from Jira...\n");
|
|
47
|
+
let tickets;
|
|
48
|
+
try {
|
|
49
|
+
tickets = await fetchSprintTickets(
|
|
50
|
+
jiraClient,
|
|
51
|
+
config.jira.projectKey,
|
|
52
|
+
config.jira.assignees,
|
|
53
|
+
opts.sprint,
|
|
54
|
+
opts.filter
|
|
55
|
+
);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
process.stderr.write(
|
|
58
|
+
`Failed to fetch sprint tickets: ${err instanceof Error ? err.message : String(err)}
|
|
59
|
+
`
|
|
60
|
+
);
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (tickets.length === 0) {
|
|
65
|
+
process.stdout.write(
|
|
66
|
+
pc.gray(" No assigned tickets found in current sprint.\n\n")
|
|
67
|
+
);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
process.stdout.write(
|
|
71
|
+
`Found ${pc.bold(String(tickets.length))} ticket(s)
|
|
72
|
+
|
|
73
|
+
`
|
|
74
|
+
);
|
|
75
|
+
if (opts.dryRun) {
|
|
76
|
+
process.stdout.write(pc.yellow(" --dry-run mode: would run:\n"));
|
|
77
|
+
for (const t of tickets) {
|
|
78
|
+
const deps = t.blockedBy.length > 0 ? ` (blocked by: ${t.blockedBy.join(", ")})` : "";
|
|
79
|
+
process.stdout.write(
|
|
80
|
+
` ${t.key} ${pc.gray(t.summary)}${pc.red(deps)}
|
|
81
|
+
`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
process.stdout.write("\n");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const runs = new Map(
|
|
88
|
+
tickets.map((t) => [t.key, { key: t.key, status: "pending" }])
|
|
89
|
+
);
|
|
90
|
+
printProgress(runs);
|
|
91
|
+
let anyFailed = false;
|
|
92
|
+
while (hasRemaining(runs)) {
|
|
93
|
+
const ready = tickets.filter((t) => {
|
|
94
|
+
const run = runs.get(t.key);
|
|
95
|
+
if (run?.status !== "pending") return false;
|
|
96
|
+
return t.blockedBy.every((dep) => runs.get(dep)?.status === "completed");
|
|
97
|
+
});
|
|
98
|
+
if (ready.length === 0) {
|
|
99
|
+
const stuck = [...runs.values()].filter((r) => r.status === "pending");
|
|
100
|
+
logger.warn(
|
|
101
|
+
{ stuck: stuck.map((s) => s.key) },
|
|
102
|
+
"Sprint deadlock detected"
|
|
103
|
+
);
|
|
104
|
+
for (const r of stuck) {
|
|
105
|
+
r.status = "failed";
|
|
106
|
+
r.error = "Blocked by failed/missing dependency";
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
const batch = ready.slice(0, maxParallel);
|
|
111
|
+
for (const t of batch) {
|
|
112
|
+
runs.get(t.key).status = "running";
|
|
113
|
+
}
|
|
114
|
+
printProgress(runs);
|
|
115
|
+
const results = await Promise.allSettled(
|
|
116
|
+
batch.map(async (t) => {
|
|
117
|
+
const run = runs.get(t.key);
|
|
118
|
+
try {
|
|
119
|
+
await runPipeline(t.key, config, { dryRun: opts.dryRun });
|
|
120
|
+
run.status = "completed";
|
|
121
|
+
} catch (err) {
|
|
122
|
+
run.status = "failed";
|
|
123
|
+
run.error = err instanceof Error ? err.message : String(err);
|
|
124
|
+
anyFailed = true;
|
|
125
|
+
logger.error({ ticketKey: t.key, err: run.error }, "Pipeline failed");
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
void results;
|
|
130
|
+
printProgress(runs);
|
|
131
|
+
}
|
|
132
|
+
const completed = [...runs.values()].filter(
|
|
133
|
+
(r) => r.status === "completed"
|
|
134
|
+
).length;
|
|
135
|
+
const failed = [...runs.values()].filter((r) => r.status === "failed").length;
|
|
136
|
+
process.stdout.write(
|
|
137
|
+
`
|
|
138
|
+
${pc.bold("Sprint complete:")} ${pc.green(`${completed} done`)} ${failed > 0 ? pc.red(`${failed} failed`) : ""}
|
|
139
|
+
`
|
|
140
|
+
);
|
|
141
|
+
if (failed > 0) {
|
|
142
|
+
for (const r of runs.values()) {
|
|
143
|
+
if (r.status === "failed") {
|
|
144
|
+
process.stdout.write(` ${pc.red("\u2717")} ${r.key}: ${r.error}
|
|
145
|
+
`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
process.stdout.write("\n");
|
|
149
|
+
}
|
|
150
|
+
if (anyFailed) process.exitCode = 1;
|
|
151
|
+
}
|
|
152
|
+
async function fetchSprintTickets(jiraClient, projectKey, assignees, sprint, extraFilter) {
|
|
153
|
+
const sprintClause = sprint ? `sprint = "${sprint}"` : `sprint in openSprints()`;
|
|
154
|
+
const assigneeClause = assignees.length > 0 ? `assignee in (${assignees.map((a) => `"${a}"`).join(", ")})` : "";
|
|
155
|
+
const parts = [
|
|
156
|
+
`project = "${projectKey}"`,
|
|
157
|
+
sprintClause,
|
|
158
|
+
assigneeClause,
|
|
159
|
+
extraFilter
|
|
160
|
+
].filter(Boolean);
|
|
161
|
+
const jql = parts.join(" AND ");
|
|
162
|
+
const result = await jiraClient.searchIssues({ jql, maxResults: 50 });
|
|
163
|
+
const issues = result.issues ?? [];
|
|
164
|
+
return issues.map((issue) => {
|
|
165
|
+
const links = issue.fields.issuelinks ?? [];
|
|
166
|
+
const blockedBy = links.filter((l) => l.type.inward === "is blocked by" && l.inwardIssue).map((l) => l.inwardIssue.key);
|
|
167
|
+
return {
|
|
168
|
+
key: issue.key,
|
|
169
|
+
summary: issue.fields.summary,
|
|
170
|
+
blockedBy
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function hasRemaining(runs) {
|
|
175
|
+
return [...runs.values()].some(
|
|
176
|
+
(r) => r.status === "pending" || r.status === "running"
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
function printProgress(runs) {
|
|
180
|
+
const rows = [...runs.values()];
|
|
181
|
+
const statusIcon = (r) => {
|
|
182
|
+
switch (r.status) {
|
|
183
|
+
case "completed":
|
|
184
|
+
return pc.green("\u2713");
|
|
185
|
+
case "failed":
|
|
186
|
+
return pc.red("\u2717");
|
|
187
|
+
case "running":
|
|
188
|
+
return pc.cyan("\u27F3");
|
|
189
|
+
default:
|
|
190
|
+
return pc.gray("\xB7");
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
process.stdout.write(
|
|
194
|
+
rows.map((r) => ` ${statusIcon(r)} ${r.key}`).join(" ") + "\n"
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
export {
|
|
198
|
+
runSprint
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=sprint-KZZWVNK6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/sprint.ts"],"sourcesContent":["import pc from \"picocolors\";\nimport { loadConfig } from \"../config/loader.js\";\nimport { createLogger } from \"../utils/logger.js\";\nimport { getClient as getJiraClient } from \"../integrations/jira/client.js\";\nimport { runPipeline } from \"../pipeline/orchestrator.js\";\n\nexport interface SprintOptions {\n projectName: string;\n sprint?: string;\n parallel: number;\n filter?: string;\n dryRun?: boolean;\n}\n\ninterface JiraTicket {\n key: string;\n summary: string;\n blockedBy: string[];\n}\n\ntype RunStatus = \"pending\" | \"running\" | \"completed\" | \"failed\";\n\ninterface TicketRun {\n key: string;\n status: RunStatus;\n error?: string;\n}\n\nexport async function runSprint(opts: SprintOptions): Promise<void> {\n const logger = createLogger(\"sprint\");\n const config = loadConfig(opts.projectName);\n const jiraClient = getJiraClient(config.jira.instance);\n\n const maxParallel = Math.min(\n opts.parallel,\n config.pipeline?.maxConcurrentRuns ?? 2,\n );\n\n logger.info(\n { project: opts.projectName, maxParallel },\n \"Starting sprint run\",\n );\n process.stdout.write(\n `\\n${pc.bold(\"jiraACP sprint\")} — ${opts.projectName} (max ${maxParallel} parallel)\\n\\n`,\n );\n\n // Fetch sprint tickets\n process.stdout.write(\"Fetching sprint tickets from Jira...\\n\");\n let tickets: JiraTicket[];\n try {\n tickets = await fetchSprintTickets(\n jiraClient,\n config.jira.projectKey,\n config.jira.assignees,\n opts.sprint,\n opts.filter,\n );\n } catch (err) {\n process.stderr.write(\n `Failed to fetch sprint tickets: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exitCode = 1;\n return;\n }\n\n if (tickets.length === 0) {\n process.stdout.write(\n pc.gray(\" No assigned tickets found in current sprint.\\n\\n\"),\n );\n return;\n }\n\n process.stdout.write(\n `Found ${pc.bold(String(tickets.length))} ticket(s)\\n\\n`,\n );\n\n if (opts.dryRun) {\n process.stdout.write(pc.yellow(\" --dry-run mode: would run:\\n\"));\n for (const t of tickets) {\n const deps =\n t.blockedBy.length > 0\n ? ` (blocked by: ${t.blockedBy.join(\", \")})`\n : \"\";\n process.stdout.write(\n ` ${t.key} ${pc.gray(t.summary)}${pc.red(deps)}\\n`,\n );\n }\n process.stdout.write(\"\\n\");\n return;\n }\n\n // Build DAG and run in topological waves\n const runs = new Map<string, TicketRun>(\n tickets.map((t) => [t.key, { key: t.key, status: \"pending\" as RunStatus }]),\n );\n\n printProgress(runs);\n\n let anyFailed = false;\n\n while (hasRemaining(runs)) {\n // Find tickets ready to run: pending + all blockers completed\n const ready = tickets.filter((t) => {\n const run = runs.get(t.key);\n if (run?.status !== \"pending\") return false;\n return t.blockedBy.every((dep) => runs.get(dep)?.status === \"completed\");\n });\n\n if (ready.length === 0) {\n // Deadlock: remaining pending tickets all have unresolvable blockers\n const stuck = [...runs.values()].filter((r) => r.status === \"pending\");\n logger.warn(\n { stuck: stuck.map((s) => s.key) },\n \"Sprint deadlock detected\",\n );\n for (const r of stuck) {\n r.status = \"failed\";\n r.error = \"Blocked by failed/missing dependency\";\n }\n break;\n }\n\n // Run up to maxParallel at a time\n const batch = ready.slice(0, maxParallel);\n for (const t of batch) {\n runs.get(t.key)!.status = \"running\";\n }\n printProgress(runs);\n\n const results = await Promise.allSettled(\n batch.map(async (t) => {\n const run = runs.get(t.key)!;\n try {\n await runPipeline(t.key, config, { dryRun: opts.dryRun });\n run.status = \"completed\";\n } catch (err) {\n run.status = \"failed\";\n run.error = err instanceof Error ? err.message : String(err);\n anyFailed = true;\n logger.error({ ticketKey: t.key, err: run.error }, \"Pipeline failed\");\n }\n }),\n );\n\n // allSettled — individual errors already captured above\n void results;\n printProgress(runs);\n }\n\n // Final summary\n const completed = [...runs.values()].filter(\n (r) => r.status === \"completed\",\n ).length;\n const failed = [...runs.values()].filter((r) => r.status === \"failed\").length;\n\n process.stdout.write(\n `\\n${pc.bold(\"Sprint complete:\")} ${pc.green(`${completed} done`)} ${failed > 0 ? pc.red(`${failed} failed`) : \"\"}\\n`,\n );\n\n if (failed > 0) {\n for (const r of runs.values()) {\n if (r.status === \"failed\") {\n process.stdout.write(` ${pc.red(\"✗\")} ${r.key}: ${r.error}\\n`);\n }\n }\n process.stdout.write(\"\\n\");\n }\n\n if (anyFailed) process.exitCode = 1;\n}\n\nasync function fetchSprintTickets(\n jiraClient: ReturnType<typeof getJiraClient>,\n projectKey: string,\n assignees: string[],\n sprint?: string,\n extraFilter?: string,\n): Promise<JiraTicket[]> {\n const sprintClause = sprint\n ? `sprint = \"${sprint}\"`\n : `sprint in openSprints()`;\n\n const assigneeClause =\n assignees.length > 0\n ? `assignee in (${assignees.map((a) => `\"${a}\"`).join(\", \")})`\n : \"\";\n\n const parts = [\n `project = \"${projectKey}\"`,\n sprintClause,\n assigneeClause,\n extraFilter,\n ].filter(Boolean);\n\n const jql = parts.join(\" AND \");\n\n const result = (await jiraClient.searchIssues({ jql, maxResults: 50 })) as {\n issues?: Array<{\n key: string;\n fields: {\n summary: string;\n issuelinks?: Array<{\n type: { name: string; inward: string };\n inwardIssue?: { key: string };\n outwardIssue?: { key: string };\n }>;\n };\n }>;\n };\n\n const issues = result.issues ?? [];\n\n return issues.map((issue) => {\n const links = issue.fields.issuelinks ?? [];\n // \"is blocked by\" links: the inwardIssue is the blocker of this ticket\n const blockedBy = links\n .filter((l) => l.type.inward === \"is blocked by\" && l.inwardIssue)\n .map((l) => l.inwardIssue!.key);\n\n return {\n key: issue.key,\n summary: issue.fields.summary,\n blockedBy,\n };\n });\n}\n\nfunction hasRemaining(runs: Map<string, TicketRun>): boolean {\n return [...runs.values()].some(\n (r) => r.status === \"pending\" || r.status === \"running\",\n );\n}\n\nfunction printProgress(runs: Map<string, TicketRun>): void {\n const rows = [...runs.values()];\n const statusIcon = (r: TicketRun): string => {\n switch (r.status) {\n case \"completed\":\n return pc.green(\"✓\");\n case \"failed\":\n return pc.red(\"✗\");\n case \"running\":\n return pc.cyan(\"⟳\");\n default:\n return pc.gray(\"·\");\n }\n };\n\n process.stdout.write(\n rows.map((r) => ` ${statusIcon(r)} ${r.key}`).join(\" \") + \"\\n\",\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
StateManager,
|
|
4
|
+
getRunDir
|
|
5
|
+
} from "./chunk-VWBCDZWQ.js";
|
|
6
|
+
import {
|
|
7
|
+
HOME_DIR,
|
|
8
|
+
listProjects
|
|
9
|
+
} from "./chunk-3YHD4SIN.js";
|
|
10
|
+
import "./chunk-LIEW4ULF.js";
|
|
11
|
+
|
|
12
|
+
// src/commands/status.ts
|
|
13
|
+
import fs from "fs";
|
|
14
|
+
import path from "path";
|
|
15
|
+
import pc from "picocolors";
|
|
16
|
+
async function showStatus(projectName, ticketKey) {
|
|
17
|
+
const projects = projectName ? [projectName] : listProjects();
|
|
18
|
+
if (projects.length === 0) {
|
|
19
|
+
console.log(pc.gray(" No projects configured. Run: jiraACP init"));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
console.log(pc.bold("\n jiraACP status\n"));
|
|
23
|
+
console.log(
|
|
24
|
+
` ${"Project".padEnd(20)} ${"Ticket".padEnd(15)} ${"Stage".padEnd(12)} ${"Status".padEnd(12)} Started`
|
|
25
|
+
);
|
|
26
|
+
console.log(" " + "\u2500".repeat(72));
|
|
27
|
+
for (const project of projects) {
|
|
28
|
+
const runsDir = path.join(HOME_DIR, "runs", project);
|
|
29
|
+
if (!fs.existsSync(runsDir)) continue;
|
|
30
|
+
const tickets = ticketKey ? [ticketKey] : fs.readdirSync(runsDir).filter((f) => fs.statSync(path.join(runsDir, f)).isDirectory());
|
|
31
|
+
for (const key of tickets) {
|
|
32
|
+
const runDir = getRunDir(project, key);
|
|
33
|
+
if (!fs.existsSync(path.join(runDir, "state.json"))) continue;
|
|
34
|
+
const state = new StateManager(runDir).current;
|
|
35
|
+
const stage = state.currentStage ?? (state.isCompleted ? "done" : state.failedStage ?? "\u2014");
|
|
36
|
+
const status = state.isCompleted ? pc.green("completed") : state.isAborted ? pc.red("aborted") : state.pendingClarification ? pc.yellow("waiting") : pc.cyan("running");
|
|
37
|
+
const started = state.startedAt ? new Date(state.startedAt).toLocaleTimeString() : "\u2014";
|
|
38
|
+
console.log(
|
|
39
|
+
` ${project.padEnd(20)} ${key.padEnd(15)} ${String(stage).padEnd(12)} ${status.padEnd(20)} ${started}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
console.log();
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
showStatus
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=status-I6GU2LWE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/status.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport pc from \"picocolors\";\nimport { StateManager, getRunDir } from \"../pipeline/state.js\";\nimport { listProjects, HOME_DIR } from \"../config/loader.js\";\n\nexport async function showStatus(\n projectName?: string,\n ticketKey?: string,\n): Promise<void> {\n const projects = projectName ? [projectName] : listProjects();\n\n if (projects.length === 0) {\n console.log(pc.gray(\" No projects configured. Run: jiraACP init\"));\n return;\n }\n\n console.log(pc.bold(\"\\n jiraACP status\\n\"));\n console.log(\n ` ${\"Project\".padEnd(20)} ${\"Ticket\".padEnd(15)} ${\"Stage\".padEnd(12)} ${\"Status\".padEnd(12)} Started`,\n );\n console.log(\" \" + \"─\".repeat(72));\n\n for (const project of projects) {\n const runsDir = path.join(HOME_DIR, \"runs\", project);\n if (!fs.existsSync(runsDir)) continue;\n\n const tickets = ticketKey\n ? [ticketKey]\n : fs\n .readdirSync(runsDir)\n .filter((f) => fs.statSync(path.join(runsDir, f)).isDirectory());\n\n for (const key of tickets) {\n const runDir = getRunDir(project, key);\n if (!fs.existsSync(path.join(runDir, \"state.json\"))) continue;\n\n const state = new StateManager(runDir).current;\n const stage =\n state.currentStage ??\n (state.isCompleted ? \"done\" : (state.failedStage ?? \"—\"));\n const status = state.isCompleted\n ? pc.green(\"completed\")\n : state.isAborted\n ? pc.red(\"aborted\")\n : state.pendingClarification\n ? pc.yellow(\"waiting\")\n : pc.cyan(\"running\");\n\n const started = state.startedAt\n ? new Date(state.startedAt).toLocaleTimeString()\n : \"—\";\n\n console.log(\n ` ${project.padEnd(20)} ${key.padEnd(15)} ${String(stage).padEnd(12)} ${status.padEnd(20)} ${started}`,\n );\n }\n }\n\n console.log();\n}\n"],"mappings":";;;;;;;;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,QAAQ;AAIf,eAAsB,WACpB,aACA,WACe;AACf,QAAM,WAAW,cAAc,CAAC,WAAW,IAAI,aAAa;AAE5D,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAI,GAAG,KAAK,6CAA6C,CAAC;AAClE;AAAA,EACF;AAEA,UAAQ,IAAI,GAAG,KAAK,sBAAsB,CAAC;AAC3C,UAAQ;AAAA,IACN,KAAK,UAAU,OAAO,EAAE,CAAC,IAAI,SAAS,OAAO,EAAE,CAAC,IAAI,QAAQ,OAAO,EAAE,CAAC,IAAI,SAAS,OAAO,EAAE,CAAC;AAAA,EAC/F;AACA,UAAQ,IAAI,OAAO,SAAI,OAAO,EAAE,CAAC;AAEjC,aAAW,WAAW,UAAU;AAC9B,UAAM,UAAU,KAAK,KAAK,UAAU,QAAQ,OAAO;AACnD,QAAI,CAAC,GAAG,WAAW,OAAO,EAAG;AAE7B,UAAM,UAAU,YACZ,CAAC,SAAS,IACV,GACG,YAAY,OAAO,EACnB,OAAO,CAAC,MAAM,GAAG,SAAS,KAAK,KAAK,SAAS,CAAC,CAAC,EAAE,YAAY,CAAC;AAErE,eAAW,OAAO,SAAS;AACzB,YAAM,SAAS,UAAU,SAAS,GAAG;AACrC,UAAI,CAAC,GAAG,WAAW,KAAK,KAAK,QAAQ,YAAY,CAAC,EAAG;AAErD,YAAM,QAAQ,IAAI,aAAa,MAAM,EAAE;AACvC,YAAM,QACJ,MAAM,iBACL,MAAM,cAAc,SAAU,MAAM,eAAe;AACtD,YAAM,SAAS,MAAM,cACjB,GAAG,MAAM,WAAW,IACpB,MAAM,YACJ,GAAG,IAAI,SAAS,IAChB,MAAM,uBACJ,GAAG,OAAO,SAAS,IACnB,GAAG,KAAK,SAAS;AAEzB,YAAM,UAAU,MAAM,YAClB,IAAI,KAAK,MAAM,SAAS,EAAE,mBAAmB,IAC7C;AAEJ,cAAQ;AAAA,QACN,KAAK,QAAQ,OAAO,EAAE,CAAC,IAAI,IAAI,OAAO,EAAE,CAAC,IAAI,OAAO,KAAK,EAAE,OAAO,EAAE,CAAC,IAAI,OAAO,OAAO,EAAE,CAAC,IAAI,OAAO;AAAA,MACvG;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,IAAI;AACd;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|