@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,407 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
setVerbosity
|
|
4
|
+
} from "./chunk-RZK74PDF.js";
|
|
5
|
+
import {
|
|
6
|
+
StateManager,
|
|
7
|
+
getRunDir
|
|
8
|
+
} from "./chunk-VWBCDZWQ.js";
|
|
9
|
+
import {
|
|
10
|
+
HOME_DIR,
|
|
11
|
+
listProjects,
|
|
12
|
+
loadConfig
|
|
13
|
+
} from "./chunk-3YHD4SIN.js";
|
|
14
|
+
import "./chunk-LIEW4ULF.js";
|
|
15
|
+
import {
|
|
16
|
+
archiveTopic
|
|
17
|
+
} from "./chunk-3U373M37.js";
|
|
18
|
+
import {
|
|
19
|
+
getBot
|
|
20
|
+
} from "./chunk-OJ4CNF73.js";
|
|
21
|
+
import "./chunk-IT74N3UH.js";
|
|
22
|
+
|
|
23
|
+
// src/integrations/telegram/commands.ts
|
|
24
|
+
import fs from "fs";
|
|
25
|
+
import path from "path";
|
|
26
|
+
import axios from "axios";
|
|
27
|
+
function parseArgs(text, command) {
|
|
28
|
+
return text.replace(new RegExp(`^\\/${command}(?:@\\S+)?\\s*`), "").trim().split(/\s+/).filter(Boolean);
|
|
29
|
+
}
|
|
30
|
+
function findProjectForTicket(ticketKey) {
|
|
31
|
+
const prefix = ticketKey.split("-")[0]?.toUpperCase();
|
|
32
|
+
if (!prefix) return null;
|
|
33
|
+
const projects = listProjects();
|
|
34
|
+
for (const name of projects) {
|
|
35
|
+
try {
|
|
36
|
+
const cfg = loadConfig(name);
|
|
37
|
+
if (cfg.jira.projectKey.toUpperCase() === prefix) return name;
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return projects.length === 1 ? projects[0] ?? null : null;
|
|
42
|
+
}
|
|
43
|
+
function jiraClientFrom(projectName) {
|
|
44
|
+
const cfg = loadConfig(projectName);
|
|
45
|
+
return {
|
|
46
|
+
client: axios.create({
|
|
47
|
+
baseURL: `${cfg.jira.url}/rest/api/3`,
|
|
48
|
+
headers: {
|
|
49
|
+
Authorization: `Basic ${Buffer.from(`${cfg.jira.email}:${cfg.jira.token}`).toString("base64")}`,
|
|
50
|
+
"Content-Type": "application/json",
|
|
51
|
+
Accept: "application/json"
|
|
52
|
+
}
|
|
53
|
+
}),
|
|
54
|
+
cfg
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function reply(ctx, text) {
|
|
58
|
+
await ctx.reply(text, { parse_mode: "HTML" });
|
|
59
|
+
}
|
|
60
|
+
async function handleRun(ctx) {
|
|
61
|
+
const args = parseArgs(ctx.message?.text ?? "", "run");
|
|
62
|
+
const ticketKey = args[0];
|
|
63
|
+
const projectArg = args[1];
|
|
64
|
+
if (!ticketKey) {
|
|
65
|
+
await reply(ctx, "Usage: /run PROJ-123 [project]");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const projectName = projectArg ?? findProjectForTicket(ticketKey);
|
|
69
|
+
if (!projectName) {
|
|
70
|
+
await reply(
|
|
71
|
+
ctx,
|
|
72
|
+
`Cannot determine project for <b>${ticketKey}</b>. Specify: /run ${ticketKey} <project>`
|
|
73
|
+
);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
await reply(
|
|
77
|
+
ctx,
|
|
78
|
+
`\u25B6\uFE0F Starting pipeline for <b>${ticketKey}</b> (${projectName})\u2026`
|
|
79
|
+
);
|
|
80
|
+
setImmediate(async () => {
|
|
81
|
+
try {
|
|
82
|
+
const { runPipeline } = await import("./orchestrator-3MGXX3QW.js");
|
|
83
|
+
const config = loadConfig(projectName);
|
|
84
|
+
await runPipeline(ticketKey, config);
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async function handleAbort(ctx) {
|
|
90
|
+
const args = parseArgs(ctx.message?.text ?? "", "abort");
|
|
91
|
+
const ticketKey = args[0];
|
|
92
|
+
const projectArg = args[1];
|
|
93
|
+
if (!ticketKey) {
|
|
94
|
+
await reply(ctx, "Usage: /abort PROJ-123 [project]");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const projectName = projectArg ?? findProjectForTicket(ticketKey);
|
|
98
|
+
if (!projectName) {
|
|
99
|
+
await reply(ctx, `Cannot determine project for <b>${ticketKey}</b>.`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const { abortPipeline } = await import("./abort-GQE4OI5S.js");
|
|
104
|
+
await abortPipeline(ticketKey, projectName, "Aborted via Telegram");
|
|
105
|
+
await reply(ctx, `\u{1F6D1} Pipeline for <b>${ticketKey}</b> aborted.`);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
await reply(
|
|
108
|
+
ctx,
|
|
109
|
+
`\u274C Abort failed: ${err instanceof Error ? err.message : String(err)}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function handleResume(ctx) {
|
|
114
|
+
const args = parseArgs(ctx.message?.text ?? "", "resume");
|
|
115
|
+
const ticketKey = args[0];
|
|
116
|
+
const projectArg = args[1];
|
|
117
|
+
if (!ticketKey) {
|
|
118
|
+
await reply(ctx, "Usage: /resume PROJ-123 [project]");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const projectName = projectArg ?? findProjectForTicket(ticketKey);
|
|
122
|
+
if (!projectName) {
|
|
123
|
+
await reply(ctx, `Cannot determine project for <b>${ticketKey}</b>.`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
await reply(
|
|
127
|
+
ctx,
|
|
128
|
+
`\u{1F501} Resuming pipeline for <b>${ticketKey}</b> (${projectName})\u2026`
|
|
129
|
+
);
|
|
130
|
+
setImmediate(async () => {
|
|
131
|
+
try {
|
|
132
|
+
const { resumePipeline } = await import("./orchestrator-3MGXX3QW.js");
|
|
133
|
+
const config = loadConfig(projectName);
|
|
134
|
+
await resumePipeline(ticketKey, config);
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async function handleStatus(ctx) {
|
|
140
|
+
const args = parseArgs(ctx.message?.text ?? "", "status");
|
|
141
|
+
const ticketKey = args[0];
|
|
142
|
+
const projects = listProjects();
|
|
143
|
+
if (projects.length === 0) {
|
|
144
|
+
await reply(ctx, "No projects configured.");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const lines = ["<b>jiraACP status</b>\n"];
|
|
148
|
+
for (const project of projects) {
|
|
149
|
+
const runsDir = path.join(HOME_DIR, "runs", project);
|
|
150
|
+
if (!fs.existsSync(runsDir)) continue;
|
|
151
|
+
const tickets = ticketKey ? [ticketKey] : fs.readdirSync(runsDir).filter((f) => fs.statSync(path.join(runsDir, f)).isDirectory());
|
|
152
|
+
for (const key of tickets) {
|
|
153
|
+
const runDir = getRunDir(project, key);
|
|
154
|
+
if (!fs.existsSync(path.join(runDir, "state.json"))) continue;
|
|
155
|
+
const state = new StateManager(runDir).current;
|
|
156
|
+
const stage = state.currentStage ?? (state.isCompleted ? "done" : state.failedStage ?? "\u2014");
|
|
157
|
+
const statusEmoji = state.isCompleted ? "\u2705" : state.isAborted ? "\u{1F6D1}" : state.pendingClarification ? "\u{1F4AC}" : state.pendingHumanApproval ? "\u{1F440}" : "\u{1F504}";
|
|
158
|
+
lines.push(`${statusEmoji} <b>${key}</b> \u2014 ${stage} <i>(${project})</i>`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (lines.length === 1) lines.push("No active runs.");
|
|
162
|
+
await reply(ctx, lines.join("\n"));
|
|
163
|
+
}
|
|
164
|
+
async function handleLogs(ctx) {
|
|
165
|
+
const args = parseArgs(ctx.message?.text ?? "", "logs");
|
|
166
|
+
const ticketKey = args[0];
|
|
167
|
+
const projectArg = args[1];
|
|
168
|
+
if (!ticketKey) {
|
|
169
|
+
await reply(ctx, "Usage: /logs PROJ-123 [project]");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const projectName = projectArg ?? findProjectForTicket(ticketKey);
|
|
173
|
+
if (!projectName) {
|
|
174
|
+
await reply(ctx, `Cannot determine project for <b>${ticketKey}</b>.`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const runDir = getRunDir(projectName, ticketKey);
|
|
178
|
+
const statePath = path.join(runDir, "state.json");
|
|
179
|
+
if (!fs.existsSync(statePath)) {
|
|
180
|
+
await reply(ctx, `No run found for <b>${ticketKey}</b>.`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const events = fs.readFileSync(statePath, "utf8").split("\n").filter(Boolean).map((line) => {
|
|
184
|
+
try {
|
|
185
|
+
return JSON.parse(line);
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}).filter((e) => e !== null).slice(-20);
|
|
190
|
+
const lines = [`<b>Logs: ${ticketKey}</b>
|
|
191
|
+
`];
|
|
192
|
+
for (const e of events) {
|
|
193
|
+
const time = new Date(e.timestamp).toLocaleTimeString();
|
|
194
|
+
const detail = e.stage ? ` [${e.stage}]` : e.reason ? ` \u2014 ${e.reason.slice(0, 60)}` : "";
|
|
195
|
+
lines.push(`<code>${time}</code> ${e.type}${detail}`);
|
|
196
|
+
}
|
|
197
|
+
await reply(ctx, lines.join("\n"));
|
|
198
|
+
}
|
|
199
|
+
async function handleProjects(ctx) {
|
|
200
|
+
const projects = listProjects();
|
|
201
|
+
if (projects.length === 0) {
|
|
202
|
+
await reply(ctx, "No projects configured.\nRun: <code>jiraACP init</code>");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const lines = ["<b>Configured projects</b>\n"];
|
|
206
|
+
for (const name of projects) {
|
|
207
|
+
try {
|
|
208
|
+
const cfg = loadConfig(name);
|
|
209
|
+
lines.push(`\u2022 <b>${name}</b> \u2014 ${cfg.jira.projectKey} @ ${cfg.jira.url}`);
|
|
210
|
+
} catch {
|
|
211
|
+
lines.push(`\u2022 <b>${name}</b> \u2014 <i>(config error)</i>`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
await reply(ctx, lines.join("\n"));
|
|
215
|
+
}
|
|
216
|
+
async function handleTickets(ctx) {
|
|
217
|
+
const args = parseArgs(ctx.message?.text ?? "", "tickets");
|
|
218
|
+
const projectArg = args[0];
|
|
219
|
+
const projects = listProjects();
|
|
220
|
+
const projectName = projectArg ?? (projects.length === 1 ? projects[0] : null);
|
|
221
|
+
if (!projectName) {
|
|
222
|
+
await reply(
|
|
223
|
+
ctx,
|
|
224
|
+
`Specify project: /tickets <project>
|
|
225
|
+
Available: ${projects.join(", ")}`
|
|
226
|
+
);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
await reply(ctx, `\u{1F50D} Fetching tickets for <b>${projectName}</b>\u2026`);
|
|
230
|
+
const STATUS_ICON = {
|
|
231
|
+
"In Progress": "\u{1F504}",
|
|
232
|
+
"In Review": "\u{1F440}",
|
|
233
|
+
Done: "\u2705",
|
|
234
|
+
"Spec Review": "\u{1F4CB}"
|
|
235
|
+
};
|
|
236
|
+
try {
|
|
237
|
+
const { client, cfg } = jiraClientFrom(projectName);
|
|
238
|
+
const assigneeJql = cfg.jira.assignees.map((a) => `assignee = "${a}"`).join(" OR ");
|
|
239
|
+
const { data } = await client.post("/search/jql", {
|
|
240
|
+
jql: `project = ${cfg.jira.projectKey} AND (${assigneeJql}) AND status != Done ORDER BY fixVersion ASC, created DESC`,
|
|
241
|
+
fields: ["summary", "status", "priority", "assignee", "fixVersions"],
|
|
242
|
+
maxResults: 50
|
|
243
|
+
});
|
|
244
|
+
const issues = data.issues ?? [];
|
|
245
|
+
if (issues.length === 0) {
|
|
246
|
+
await reply(ctx, "No open tickets found.");
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
250
|
+
for (const issue of issues) {
|
|
251
|
+
const version = issue.fields.fixVersions?.[0]?.name ?? "Unplanned";
|
|
252
|
+
if (!grouped.has(version)) grouped.set(version, []);
|
|
253
|
+
grouped.get(version).push(issue);
|
|
254
|
+
}
|
|
255
|
+
const lines = [
|
|
256
|
+
`<b>${projectName}</b> \u2014 ${issues.length} open tickets
|
|
257
|
+
`
|
|
258
|
+
];
|
|
259
|
+
for (const [version, vIssues] of grouped) {
|
|
260
|
+
lines.push(`<b>\u{1F4E6} ${version}</b> (${vIssues.length})`);
|
|
261
|
+
for (const issue of vIssues) {
|
|
262
|
+
const status = issue.fields.status.name;
|
|
263
|
+
const icon = STATUS_ICON[status] ?? "\u2B1C";
|
|
264
|
+
const prio = issue.fields.priority?.name ?? "";
|
|
265
|
+
const assignee = issue.fields.assignee?.displayName ?? "Unassigned";
|
|
266
|
+
lines.push(
|
|
267
|
+
`${icon} <b>${issue.key}</b> ${issue.fields.summary.slice(0, 55)}
|
|
268
|
+
${status} \xB7 ${prio} \xB7 ${assignee}`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
lines.push("");
|
|
272
|
+
}
|
|
273
|
+
await reply(ctx, lines.join("\n"));
|
|
274
|
+
} catch (err) {
|
|
275
|
+
await reply(
|
|
276
|
+
ctx,
|
|
277
|
+
`\u274C Jira error: ${err instanceof Error ? err.message : String(err)}`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async function handleTicket(ctx) {
|
|
282
|
+
const args = parseArgs(ctx.message?.text ?? "", "ticket");
|
|
283
|
+
const ticketKey = args[0];
|
|
284
|
+
if (!ticketKey) {
|
|
285
|
+
await reply(ctx, "Usage: /ticket PROJ-123");
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const projectName = findProjectForTicket(ticketKey);
|
|
289
|
+
if (!projectName) {
|
|
290
|
+
await reply(ctx, `Cannot determine project for <b>${ticketKey}</b>.`);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
let extractText2 = function(node) {
|
|
295
|
+
if (!node) return "";
|
|
296
|
+
if (typeof node === "string") return node;
|
|
297
|
+
const n = node;
|
|
298
|
+
if (n.type === "text") return n.text ?? "";
|
|
299
|
+
if (n.content) return n.content.map(extractText2).join(" ");
|
|
300
|
+
return "";
|
|
301
|
+
};
|
|
302
|
+
var extractText = extractText2;
|
|
303
|
+
const { client, cfg } = jiraClientFrom(projectName);
|
|
304
|
+
const { data } = await client.get(`/issue/${ticketKey}`);
|
|
305
|
+
const description = extractText2(data.fields.description).slice(0, 400);
|
|
306
|
+
const ac = extractText2(
|
|
307
|
+
data.fields[cfg.jira.acceptanceCriteriaField ?? "customfield_10016"]
|
|
308
|
+
).slice(0, 300);
|
|
309
|
+
const lines = [
|
|
310
|
+
`<b>${data.key}</b> \u2014 ${data.fields.summary}`,
|
|
311
|
+
`Status: ${data.fields.status.name}`,
|
|
312
|
+
`Priority: ${data.fields.priority?.name ?? "\u2014"}`,
|
|
313
|
+
`Assignee: ${data.fields.assignee?.displayName ?? "Unassigned"}`
|
|
314
|
+
];
|
|
315
|
+
if (description) lines.push(`
|
|
316
|
+
<b>Description</b>
|
|
317
|
+
${description}`);
|
|
318
|
+
if (ac) lines.push(`
|
|
319
|
+
<b>Acceptance Criteria</b>
|
|
320
|
+
${ac}`);
|
|
321
|
+
await reply(ctx, lines.join("\n"));
|
|
322
|
+
} catch (err) {
|
|
323
|
+
await reply(
|
|
324
|
+
ctx,
|
|
325
|
+
`\u274C Jira error: ${err instanceof Error ? err.message : String(err)}`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async function handleArchive(ctx) {
|
|
330
|
+
const args = parseArgs(ctx.message?.text ?? "", "archive");
|
|
331
|
+
const ticketKey = args[0];
|
|
332
|
+
const projectArg = args[1];
|
|
333
|
+
if (!ticketKey) {
|
|
334
|
+
await reply(ctx, "Usage: /archive PROJ-123 [project]");
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const projectName = projectArg ?? findProjectForTicket(ticketKey);
|
|
338
|
+
if (!projectName) {
|
|
339
|
+
await reply(ctx, `Cannot determine project for <b>${ticketKey}</b>.`);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const chatId = ctx.chat?.id;
|
|
343
|
+
if (!chatId) return;
|
|
344
|
+
try {
|
|
345
|
+
const cfg = loadConfig(projectName);
|
|
346
|
+
const bot = getBot(cfg.telegram.botToken);
|
|
347
|
+
await archiveTopic(bot, chatId, ticketKey, projectName);
|
|
348
|
+
await reply(ctx, `\u{1F5C2} Topic for <b>${ticketKey}</b> archived.`);
|
|
349
|
+
} catch (err) {
|
|
350
|
+
await reply(
|
|
351
|
+
ctx,
|
|
352
|
+
`\u274C Archive failed: ${err instanceof Error ? err.message : String(err)}`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async function handleVerbosity(ctx) {
|
|
357
|
+
const args = parseArgs(ctx.message?.text ?? "", "verbosity");
|
|
358
|
+
const chatId = ctx.chat?.id;
|
|
359
|
+
if (!chatId) return;
|
|
360
|
+
const level = args[0];
|
|
361
|
+
const valid = ["low", "medium", "high"];
|
|
362
|
+
if (!level || !valid.includes(level)) {
|
|
363
|
+
await ctx.reply("Choose verbosity level:", {
|
|
364
|
+
reply_markup: {
|
|
365
|
+
inline_keyboard: [
|
|
366
|
+
[
|
|
367
|
+
{ text: "\u{1F515} low", callback_data: `verbosity:low` },
|
|
368
|
+
{ text: "\u{1F514} medium", callback_data: `verbosity:medium` },
|
|
369
|
+
{ text: "\u{1F4E3} high", callback_data: `verbosity:high` }
|
|
370
|
+
]
|
|
371
|
+
]
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
setVerbosity(chatId, level);
|
|
377
|
+
await reply(ctx, `\u2705 Verbosity set to <b>${level}</b>.`);
|
|
378
|
+
}
|
|
379
|
+
function registerBotCommands(token) {
|
|
380
|
+
const bot = getBot(token);
|
|
381
|
+
bot.command("run", (ctx) => handleRun(ctx).catch(() => void 0));
|
|
382
|
+
bot.command("abort", (ctx) => handleAbort(ctx).catch(() => void 0));
|
|
383
|
+
bot.command("resume", (ctx) => handleResume(ctx).catch(() => void 0));
|
|
384
|
+
bot.command("status", (ctx) => handleStatus(ctx).catch(() => void 0));
|
|
385
|
+
bot.command("logs", (ctx) => handleLogs(ctx).catch(() => void 0));
|
|
386
|
+
bot.command("projects", (ctx) => handleProjects(ctx).catch(() => void 0));
|
|
387
|
+
bot.command("tickets", (ctx) => handleTickets(ctx).catch(() => void 0));
|
|
388
|
+
bot.command("ticket", (ctx) => handleTicket(ctx).catch(() => void 0));
|
|
389
|
+
bot.command("archive", (ctx) => handleArchive(ctx).catch(() => void 0));
|
|
390
|
+
bot.command(
|
|
391
|
+
"verbosity",
|
|
392
|
+
(ctx) => handleVerbosity(ctx).catch(() => void 0)
|
|
393
|
+
);
|
|
394
|
+
bot.on("callback_query:data", async (ctx) => {
|
|
395
|
+
const data = ctx.callbackQuery.data ?? "";
|
|
396
|
+
if (!data.startsWith("verbosity:")) return;
|
|
397
|
+
const level = data.slice("verbosity:".length);
|
|
398
|
+
const chatId = ctx.chat?.id;
|
|
399
|
+
if (chatId) setVerbosity(chatId, level);
|
|
400
|
+
await ctx.answerCallbackQuery({ text: `Verbosity set to ${level}` });
|
|
401
|
+
await ctx.editMessageReplyMarkup({ reply_markup: { inline_keyboard: [] } });
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
export {
|
|
405
|
+
registerBotCommands
|
|
406
|
+
};
|
|
407
|
+
//# sourceMappingURL=commands-RG45VBTZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/integrations/telegram/commands.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport axios from \"axios\";\nimport type { Context } from \"grammy\";\nimport { getBot } from \"./bot.js\";\nimport { archiveTopic } from \"./topic-manager.js\";\nimport { setVerbosity, type Verbosity } from \"./prefs.js\";\nimport { listProjects, loadConfig, HOME_DIR } from \"../../config/loader.js\";\nimport { StateManager, getRunDir } from \"../../pipeline/state.js\";\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction parseArgs(text: string, command: string): string[] {\n return text\n .replace(new RegExp(`^\\\\/${command}(?:@\\\\S+)?\\\\s*`), \"\")\n .trim()\n .split(/\\s+/)\n .filter(Boolean);\n}\n\n/** Find project by matching jira.projectKey prefix against ticketKey (e.g. \"PROJ\" in \"PROJ-123\") */\nfunction findProjectForTicket(ticketKey: string): string | null {\n const prefix = ticketKey.split(\"-\")[0]?.toUpperCase();\n if (!prefix) return null;\n\n const projects = listProjects();\n for (const name of projects) {\n try {\n const cfg = loadConfig(name);\n if (cfg.jira.projectKey.toUpperCase() === prefix) return name;\n } catch {\n // skip misconfigured projects\n }\n }\n return projects.length === 1 ? (projects[0] ?? null) : null;\n}\n\nfunction jiraClientFrom(projectName: string) {\n const cfg = loadConfig(projectName);\n return {\n client: axios.create({\n baseURL: `${cfg.jira.url}/rest/api/3`,\n headers: {\n Authorization: `Basic ${Buffer.from(`${cfg.jira.email}:${cfg.jira.token}`).toString(\"base64\")}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n }),\n cfg,\n };\n}\n\nasync function reply(ctx: Context, text: string): Promise<void> {\n await ctx.reply(text, { parse_mode: \"HTML\" });\n}\n\n// ── Command handlers ──────────────────────────────────────────────────────────\n\nasync function handleRun(ctx: Context): Promise<void> {\n const args = parseArgs(ctx.message?.text ?? \"\", \"run\");\n const ticketKey = args[0];\n const projectArg = args[1];\n\n if (!ticketKey) {\n await reply(ctx, \"Usage: /run PROJ-123 [project]\");\n return;\n }\n\n const projectName = projectArg ?? findProjectForTicket(ticketKey);\n if (!projectName) {\n await reply(\n ctx,\n `Cannot determine project for <b>${ticketKey}</b>. Specify: /run ${ticketKey} <project>`,\n );\n return;\n }\n\n await reply(\n ctx,\n `▶️ Starting pipeline for <b>${ticketKey}</b> (${projectName})…`,\n );\n\n setImmediate(async () => {\n try {\n const { runPipeline } = await import(\"../../pipeline/orchestrator.js\");\n const config = loadConfig(projectName);\n await runPipeline(ticketKey, config);\n } catch {\n // errors are sent via notifier inside the pipeline\n }\n });\n}\n\nasync function handleAbort(ctx: Context): Promise<void> {\n const args = parseArgs(ctx.message?.text ?? \"\", \"abort\");\n const ticketKey = args[0];\n const projectArg = args[1];\n\n if (!ticketKey) {\n await reply(ctx, \"Usage: /abort PROJ-123 [project]\");\n return;\n }\n\n const projectName = projectArg ?? findProjectForTicket(ticketKey);\n if (!projectName) {\n await reply(ctx, `Cannot determine project for <b>${ticketKey}</b>.`);\n return;\n }\n\n try {\n const { abortPipeline } = await import(\"../../commands/abort.js\");\n await abortPipeline(ticketKey, projectName, \"Aborted via Telegram\");\n await reply(ctx, `🛑 Pipeline for <b>${ticketKey}</b> aborted.`);\n } catch (err) {\n await reply(\n ctx,\n `❌ Abort failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nasync function handleResume(ctx: Context): Promise<void> {\n const args = parseArgs(ctx.message?.text ?? \"\", \"resume\");\n const ticketKey = args[0];\n const projectArg = args[1];\n\n if (!ticketKey) {\n await reply(ctx, \"Usage: /resume PROJ-123 [project]\");\n return;\n }\n\n const projectName = projectArg ?? findProjectForTicket(ticketKey);\n if (!projectName) {\n await reply(ctx, `Cannot determine project for <b>${ticketKey}</b>.`);\n return;\n }\n\n await reply(\n ctx,\n `🔁 Resuming pipeline for <b>${ticketKey}</b> (${projectName})…`,\n );\n\n setImmediate(async () => {\n try {\n const { resumePipeline } = await import(\"../../pipeline/orchestrator.js\");\n const config = loadConfig(projectName);\n await resumePipeline(ticketKey, config);\n } catch {\n // errors sent via notifier\n }\n });\n}\n\nasync function handleStatus(ctx: Context): Promise<void> {\n const args = parseArgs(ctx.message?.text ?? \"\", \"status\");\n const ticketKey = args[0];\n\n const projects = listProjects();\n if (projects.length === 0) {\n await reply(ctx, \"No projects configured.\");\n return;\n }\n\n const lines: string[] = [\"<b>jiraACP status</b>\\n\"];\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\n const statusEmoji = state.isCompleted\n ? \"✅\"\n : state.isAborted\n ? \"🛑\"\n : state.pendingClarification\n ? \"💬\"\n : state.pendingHumanApproval\n ? \"👀\"\n : \"🔄\";\n\n lines.push(`${statusEmoji} <b>${key}</b> — ${stage} <i>(${project})</i>`);\n }\n }\n\n if (lines.length === 1) lines.push(\"No active runs.\");\n await reply(ctx, lines.join(\"\\n\"));\n}\n\nasync function handleLogs(ctx: Context): Promise<void> {\n const args = parseArgs(ctx.message?.text ?? \"\", \"logs\");\n const ticketKey = args[0];\n const projectArg = args[1];\n\n if (!ticketKey) {\n await reply(ctx, \"Usage: /logs PROJ-123 [project]\");\n return;\n }\n\n const projectName = projectArg ?? findProjectForTicket(ticketKey);\n if (!projectName) {\n await reply(ctx, `Cannot determine project for <b>${ticketKey}</b>.`);\n return;\n }\n\n const runDir = getRunDir(projectName, ticketKey);\n const statePath = path.join(runDir, \"state.json\");\n\n if (!fs.existsSync(statePath)) {\n await reply(ctx, `No run found for <b>${ticketKey}</b>.`);\n return;\n }\n\n const events = fs\n .readFileSync(statePath, \"utf8\")\n .split(\"\\n\")\n .filter(Boolean)\n .map((line) => {\n try {\n return JSON.parse(line) as {\n type: string;\n timestamp: string;\n stage?: string;\n error?: string;\n reason?: string;\n };\n } catch {\n return null;\n }\n })\n .filter((e): e is NonNullable<typeof e> => e !== null)\n .slice(-20); // last 20 events\n\n const lines = [`<b>Logs: ${ticketKey}</b>\\n`];\n for (const e of events) {\n const time = new Date(e.timestamp).toLocaleTimeString();\n const detail = e.stage\n ? ` [${e.stage}]`\n : e.reason\n ? ` — ${e.reason.slice(0, 60)}`\n : \"\";\n lines.push(`<code>${time}</code> ${e.type}${detail}`);\n }\n\n await reply(ctx, lines.join(\"\\n\"));\n}\n\nasync function handleProjects(ctx: Context): Promise<void> {\n const projects = listProjects();\n if (projects.length === 0) {\n await reply(ctx, \"No projects configured.\\nRun: <code>jiraACP init</code>\");\n return;\n }\n\n const lines = [\"<b>Configured projects</b>\\n\"];\n for (const name of projects) {\n try {\n const cfg = loadConfig(name);\n lines.push(`• <b>${name}</b> — ${cfg.jira.projectKey} @ ${cfg.jira.url}`);\n } catch {\n lines.push(`• <b>${name}</b> — <i>(config error)</i>`);\n }\n }\n await reply(ctx, lines.join(\"\\n\"));\n}\n\nasync function handleTickets(ctx: Context): Promise<void> {\n const args = parseArgs(ctx.message?.text ?? \"\", \"tickets\");\n const projectArg = args[0];\n\n const projects = listProjects();\n const projectName =\n projectArg ?? (projects.length === 1 ? projects[0] : null);\n\n if (!projectName) {\n await reply(\n ctx,\n `Specify project: /tickets <project>\\nAvailable: ${projects.join(\", \")}`,\n );\n return;\n }\n\n await reply(ctx, `🔍 Fetching tickets for <b>${projectName}</b>…`);\n\n interface JiraIssue {\n key: string;\n fields: {\n summary: string;\n status: { name: string };\n priority?: { name: string };\n assignee?: { displayName: string } | null;\n fixVersions?: Array<{ name: string }>;\n };\n }\n\n const STATUS_ICON: Record<string, string> = {\n \"In Progress\": \"🔄\",\n \"In Review\": \"👀\",\n Done: \"✅\",\n \"Spec Review\": \"📋\",\n };\n\n try {\n const { client, cfg } = jiraClientFrom(projectName);\n\n const assigneeJql = cfg.jira.assignees\n .map((a) => `assignee = \"${a}\"`)\n .join(\" OR \");\n\n // POST /search/jql — groups by fixVersion, same approach as jira_tickets.py\n const { data } = await client.post(\"/search/jql\", {\n jql: `project = ${cfg.jira.projectKey} AND (${assigneeJql}) AND status != Done ORDER BY fixVersion ASC, created DESC`,\n fields: [\"summary\", \"status\", \"priority\", \"assignee\", \"fixVersions\"],\n maxResults: 50,\n });\n\n const issues = (data.issues ?? []) as JiraIssue[];\n if (issues.length === 0) {\n await reply(ctx, \"No open tickets found.\");\n return;\n }\n\n // Group by fixVersion\n const grouped = new Map<string, JiraIssue[]>();\n for (const issue of issues) {\n const version = issue.fields.fixVersions?.[0]?.name ?? \"Unplanned\";\n if (!grouped.has(version)) grouped.set(version, []);\n grouped.get(version)!.push(issue);\n }\n\n const lines: string[] = [\n `<b>${projectName}</b> — ${issues.length} open tickets\\n`,\n ];\n for (const [version, vIssues] of grouped) {\n lines.push(`<b>📦 ${version}</b> (${vIssues.length})`);\n for (const issue of vIssues) {\n const status = issue.fields.status.name;\n const icon = STATUS_ICON[status] ?? \"⬜\";\n const prio = issue.fields.priority?.name ?? \"\";\n const assignee = issue.fields.assignee?.displayName ?? \"Unassigned\";\n lines.push(\n `${icon} <b>${issue.key}</b> ${issue.fields.summary.slice(0, 55)}\\n ${status} · ${prio} · ${assignee}`,\n );\n }\n lines.push(\"\");\n }\n\n await reply(ctx, lines.join(\"\\n\"));\n } catch (err) {\n await reply(\n ctx,\n `❌ Jira error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nasync function handleTicket(ctx: Context): Promise<void> {\n const args = parseArgs(ctx.message?.text ?? \"\", \"ticket\");\n const ticketKey = args[0];\n\n if (!ticketKey) {\n await reply(ctx, \"Usage: /ticket PROJ-123\");\n return;\n }\n\n const projectName = findProjectForTicket(ticketKey);\n if (!projectName) {\n await reply(ctx, `Cannot determine project for <b>${ticketKey}</b>.`);\n return;\n }\n\n try {\n const { client, cfg } = jiraClientFrom(projectName);\n const { data } = await client.get(`/issue/${ticketKey}`);\n\n function extractText(node: unknown): string {\n if (!node) return \"\";\n if (typeof node === \"string\") return node;\n const n = node as { type?: string; text?: string; content?: unknown[] };\n if (n.type === \"text\") return n.text ?? \"\";\n if (n.content) return n.content.map(extractText).join(\" \");\n return \"\";\n }\n\n const description = extractText(data.fields.description).slice(0, 400);\n const ac = extractText(\n data.fields[cfg.jira.acceptanceCriteriaField ?? \"customfield_10016\"],\n ).slice(0, 300);\n\n const lines = [\n `<b>${data.key}</b> — ${data.fields.summary}`,\n `Status: ${data.fields.status.name}`,\n `Priority: ${data.fields.priority?.name ?? \"—\"}`,\n `Assignee: ${data.fields.assignee?.displayName ?? \"Unassigned\"}`,\n ];\n\n if (description) lines.push(`\\n<b>Description</b>\\n${description}`);\n if (ac) lines.push(`\\n<b>Acceptance Criteria</b>\\n${ac}`);\n\n await reply(ctx, lines.join(\"\\n\"));\n } catch (err) {\n await reply(\n ctx,\n `❌ Jira error: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nasync function handleArchive(ctx: Context): Promise<void> {\n const args = parseArgs(ctx.message?.text ?? \"\", \"archive\");\n const ticketKey = args[0];\n const projectArg = args[1];\n\n if (!ticketKey) {\n await reply(ctx, \"Usage: /archive PROJ-123 [project]\");\n return;\n }\n\n const projectName = projectArg ?? findProjectForTicket(ticketKey);\n if (!projectName) {\n await reply(ctx, `Cannot determine project for <b>${ticketKey}</b>.`);\n return;\n }\n\n const chatId = ctx.chat?.id;\n if (!chatId) return;\n\n try {\n const cfg = loadConfig(projectName);\n const bot = getBot(cfg.telegram.botToken);\n await archiveTopic(bot, chatId, ticketKey, projectName);\n await reply(ctx, `🗂 Topic for <b>${ticketKey}</b> archived.`);\n } catch (err) {\n await reply(\n ctx,\n `❌ Archive failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\nasync function handleVerbosity(ctx: Context): Promise<void> {\n const args = parseArgs(ctx.message?.text ?? \"\", \"verbosity\");\n const chatId = ctx.chat?.id;\n if (!chatId) return;\n\n const level = args[0] as Verbosity | undefined;\n const valid: Verbosity[] = [\"low\", \"medium\", \"high\"];\n\n if (!level || !valid.includes(level)) {\n await ctx.reply(\"Choose verbosity level:\", {\n reply_markup: {\n inline_keyboard: [\n [\n { text: \"🔕 low\", callback_data: `verbosity:low` },\n { text: \"🔔 medium\", callback_data: `verbosity:medium` },\n { text: \"📣 high\", callback_data: `verbosity:high` },\n ],\n ],\n },\n });\n return;\n }\n\n setVerbosity(chatId, level);\n await reply(ctx, `✅ Verbosity set to <b>${level}</b>.`);\n}\n\n// ── Registration ──────────────────────────────────────────────────────────────\n\nexport function registerBotCommands(token: string): void {\n const bot = getBot(token);\n\n bot.command(\"run\", (ctx) => handleRun(ctx).catch(() => undefined));\n bot.command(\"abort\", (ctx) => handleAbort(ctx).catch(() => undefined));\n bot.command(\"resume\", (ctx) => handleResume(ctx).catch(() => undefined));\n bot.command(\"status\", (ctx) => handleStatus(ctx).catch(() => undefined));\n bot.command(\"logs\", (ctx) => handleLogs(ctx).catch(() => undefined));\n bot.command(\"projects\", (ctx) => handleProjects(ctx).catch(() => undefined));\n bot.command(\"tickets\", (ctx) => handleTickets(ctx).catch(() => undefined));\n bot.command(\"ticket\", (ctx) => handleTicket(ctx).catch(() => undefined));\n bot.command(\"archive\", (ctx) => handleArchive(ctx).catch(() => undefined));\n bot.command(\"verbosity\", (ctx) =>\n handleVerbosity(ctx).catch(() => undefined),\n );\n\n // Inline keyboard for verbosity\n bot.on(\"callback_query:data\", async (ctx) => {\n const data = ctx.callbackQuery.data ?? \"\";\n if (!data.startsWith(\"verbosity:\")) return;\n\n const level = data.slice(\"verbosity:\".length) as Verbosity;\n const chatId = ctx.chat?.id;\n if (chatId) setVerbosity(chatId, level);\n\n await ctx.answerCallbackQuery({ text: `Verbosity set to ${level}` });\n await ctx.editMessageReplyMarkup({ reply_markup: { inline_keyboard: [] } });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,WAAW;AAUlB,SAAS,UAAU,MAAc,SAA2B;AAC1D,SAAO,KACJ,QAAQ,IAAI,OAAO,OAAO,OAAO,gBAAgB,GAAG,EAAE,EACtD,KAAK,EACL,MAAM,KAAK,EACX,OAAO,OAAO;AACnB;AAGA,SAAS,qBAAqB,WAAkC;AAC9D,QAAM,SAAS,UAAU,MAAM,GAAG,EAAE,CAAC,GAAG,YAAY;AACpD,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,WAAW,aAAa;AAC9B,aAAW,QAAQ,UAAU;AAC3B,QAAI;AACF,YAAM,MAAM,WAAW,IAAI;AAC3B,UAAI,IAAI,KAAK,WAAW,YAAY,MAAM,OAAQ,QAAO;AAAA,IAC3D,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,SAAS,WAAW,IAAK,SAAS,CAAC,KAAK,OAAQ;AACzD;AAEA,SAAS,eAAe,aAAqB;AAC3C,QAAM,MAAM,WAAW,WAAW;AAClC,SAAO;AAAA,IACL,QAAQ,MAAM,OAAO;AAAA,MACnB,SAAS,GAAG,IAAI,KAAK,GAAG;AAAA,MACxB,SAAS;AAAA,QACP,eAAe,SAAS,OAAO,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,SAAS,QAAQ,CAAC;AAAA,QAC7F,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MACV;AAAA,IACF,CAAC;AAAA,IACD;AAAA,EACF;AACF;AAEA,eAAe,MAAM,KAAc,MAA6B;AAC9D,QAAM,IAAI,MAAM,MAAM,EAAE,YAAY,OAAO,CAAC;AAC9C;AAIA,eAAe,UAAU,KAA6B;AACpD,QAAM,OAAO,UAAU,IAAI,SAAS,QAAQ,IAAI,KAAK;AACrD,QAAM,YAAY,KAAK,CAAC;AACxB,QAAM,aAAa,KAAK,CAAC;AAEzB,MAAI,CAAC,WAAW;AACd,UAAM,MAAM,KAAK,gCAAgC;AACjD;AAAA,EACF;AAEA,QAAM,cAAc,cAAc,qBAAqB,SAAS;AAChE,MAAI,CAAC,aAAa;AAChB,UAAM;AAAA,MACJ;AAAA,MACA,mCAAmC,SAAS,uBAAuB,SAAS;AAAA,IAC9E;AACA;AAAA,EACF;AAEA,QAAM;AAAA,IACJ;AAAA,IACA,yCAA+B,SAAS,SAAS,WAAW;AAAA,EAC9D;AAEA,eAAa,YAAY;AACvB,QAAI;AACF,YAAM,EAAE,YAAY,IAAI,MAAM,OAAO,4BAAgC;AACrE,YAAM,SAAS,WAAW,WAAW;AACrC,YAAM,YAAY,WAAW,MAAM;AAAA,IACrC,QAAQ;AAAA,IAER;AAAA,EACF,CAAC;AACH;AAEA,eAAe,YAAY,KAA6B;AACtD,QAAM,OAAO,UAAU,IAAI,SAAS,QAAQ,IAAI,OAAO;AACvD,QAAM,YAAY,KAAK,CAAC;AACxB,QAAM,aAAa,KAAK,CAAC;AAEzB,MAAI,CAAC,WAAW;AACd,UAAM,MAAM,KAAK,kCAAkC;AACnD;AAAA,EACF;AAEA,QAAM,cAAc,cAAc,qBAAqB,SAAS;AAChE,MAAI,CAAC,aAAa;AAChB,UAAM,MAAM,KAAK,mCAAmC,SAAS,OAAO;AACpE;AAAA,EACF;AAEA,MAAI;AACF,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,qBAAyB;AAChE,UAAM,cAAc,WAAW,aAAa,sBAAsB;AAClE,UAAM,MAAM,KAAK,6BAAsB,SAAS,eAAe;AAAA,EACjE,SAAS,KAAK;AACZ,UAAM;AAAA,MACJ;AAAA,MACA,wBAAmB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACrE;AAAA,EACF;AACF;AAEA,eAAe,aAAa,KAA6B;AACvD,QAAM,OAAO,UAAU,IAAI,SAAS,QAAQ,IAAI,QAAQ;AACxD,QAAM,YAAY,KAAK,CAAC;AACxB,QAAM,aAAa,KAAK,CAAC;AAEzB,MAAI,CAAC,WAAW;AACd,UAAM,MAAM,KAAK,mCAAmC;AACpD;AAAA,EACF;AAEA,QAAM,cAAc,cAAc,qBAAqB,SAAS;AAChE,MAAI,CAAC,aAAa;AAChB,UAAM,MAAM,KAAK,mCAAmC,SAAS,OAAO;AACpE;AAAA,EACF;AAEA,QAAM;AAAA,IACJ;AAAA,IACA,sCAA+B,SAAS,SAAS,WAAW;AAAA,EAC9D;AAEA,eAAa,YAAY;AACvB,QAAI;AACF,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,4BAAgC;AACxE,YAAM,SAAS,WAAW,WAAW;AACrC,YAAM,eAAe,WAAW,MAAM;AAAA,IACxC,QAAQ;AAAA,IAER;AAAA,EACF,CAAC;AACH;AAEA,eAAe,aAAa,KAA6B;AACvD,QAAM,OAAO,UAAU,IAAI,SAAS,QAAQ,IAAI,QAAQ;AACxD,QAAM,YAAY,KAAK,CAAC;AAExB,QAAM,WAAW,aAAa;AAC9B,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,MAAM,KAAK,yBAAyB;AAC1C;AAAA,EACF;AAEA,QAAM,QAAkB,CAAC,yBAAyB;AAElD,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;AAEtD,YAAM,cAAc,MAAM,cACtB,WACA,MAAM,YACJ,cACA,MAAM,uBACJ,cACA,MAAM,uBACJ,cACA;AAEV,YAAM,KAAK,GAAG,WAAW,OAAO,GAAG,eAAU,KAAK,QAAQ,OAAO,OAAO;AAAA,IAC1E;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,EAAG,OAAM,KAAK,iBAAiB;AACpD,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AACnC;AAEA,eAAe,WAAW,KAA6B;AACrD,QAAM,OAAO,UAAU,IAAI,SAAS,QAAQ,IAAI,MAAM;AACtD,QAAM,YAAY,KAAK,CAAC;AACxB,QAAM,aAAa,KAAK,CAAC;AAEzB,MAAI,CAAC,WAAW;AACd,UAAM,MAAM,KAAK,iCAAiC;AAClD;AAAA,EACF;AAEA,QAAM,cAAc,cAAc,qBAAqB,SAAS;AAChE,MAAI,CAAC,aAAa;AAChB,UAAM,MAAM,KAAK,mCAAmC,SAAS,OAAO;AACpE;AAAA,EACF;AAEA,QAAM,SAAS,UAAU,aAAa,SAAS;AAC/C,QAAM,YAAY,KAAK,KAAK,QAAQ,YAAY;AAEhD,MAAI,CAAC,GAAG,WAAW,SAAS,GAAG;AAC7B,UAAM,MAAM,KAAK,uBAAuB,SAAS,OAAO;AACxD;AAAA,EACF;AAEA,QAAM,SAAS,GACZ,aAAa,WAAW,MAAM,EAC9B,MAAM,IAAI,EACV,OAAO,OAAO,EACd,IAAI,CAAC,SAAS;AACb,QAAI;AACF,aAAO,KAAK,MAAM,IAAI;AAAA,IAOxB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,CAAC,EACA,OAAO,CAAC,MAAkC,MAAM,IAAI,EACpD,MAAM,GAAG;AAEZ,QAAM,QAAQ,CAAC,YAAY,SAAS;AAAA,CAAQ;AAC5C,aAAW,KAAK,QAAQ;AACtB,UAAM,OAAO,IAAI,KAAK,EAAE,SAAS,EAAE,mBAAmB;AACtD,UAAM,SAAS,EAAE,QACb,KAAK,EAAE,KAAK,MACZ,EAAE,SACA,WAAM,EAAE,OAAO,MAAM,GAAG,EAAE,CAAC,KAC3B;AACN,UAAM,KAAK,SAAS,IAAI,WAAW,EAAE,IAAI,GAAG,MAAM,EAAE;AAAA,EACtD;AAEA,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AACnC;AAEA,eAAe,eAAe,KAA6B;AACzD,QAAM,WAAW,aAAa;AAC9B,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,MAAM,KAAK,yDAAyD;AAC1E;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC,8BAA8B;AAC7C,aAAW,QAAQ,UAAU;AAC3B,QAAI;AACF,YAAM,MAAM,WAAW,IAAI;AAC3B,YAAM,KAAK,aAAQ,IAAI,eAAU,IAAI,KAAK,UAAU,MAAM,IAAI,KAAK,GAAG,EAAE;AAAA,IAC1E,QAAQ;AACN,YAAM,KAAK,aAAQ,IAAI,mCAA8B;AAAA,IACvD;AAAA,EACF;AACA,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AACnC;AAEA,eAAe,cAAc,KAA6B;AACxD,QAAM,OAAO,UAAU,IAAI,SAAS,QAAQ,IAAI,SAAS;AACzD,QAAM,aAAa,KAAK,CAAC;AAEzB,QAAM,WAAW,aAAa;AAC9B,QAAM,cACJ,eAAe,SAAS,WAAW,IAAI,SAAS,CAAC,IAAI;AAEvD,MAAI,CAAC,aAAa;AAChB,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,aAAyD,SAAS,KAAK,IAAI,CAAC;AAAA,IAC9E;AACA;AAAA,EACF;AAEA,QAAM,MAAM,KAAK,qCAA8B,WAAW,YAAO;AAajE,QAAM,cAAsC;AAAA,IAC1C,eAAe;AAAA,IACf,aAAa;AAAA,IACb,MAAM;AAAA,IACN,eAAe;AAAA,EACjB;AAEA,MAAI;AACF,UAAM,EAAE,QAAQ,IAAI,IAAI,eAAe,WAAW;AAElD,UAAM,cAAc,IAAI,KAAK,UAC1B,IAAI,CAAC,MAAM,eAAe,CAAC,GAAG,EAC9B,KAAK,MAAM;AAGd,UAAM,EAAE,KAAK,IAAI,MAAM,OAAO,KAAK,eAAe;AAAA,MAChD,KAAK,aAAa,IAAI,KAAK,UAAU,SAAS,WAAW;AAAA,MACzD,QAAQ,CAAC,WAAW,UAAU,YAAY,YAAY,aAAa;AAAA,MACnE,YAAY;AAAA,IACd,CAAC;AAED,UAAM,SAAU,KAAK,UAAU,CAAC;AAChC,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,MAAM,KAAK,wBAAwB;AACzC;AAAA,IACF;AAGA,UAAM,UAAU,oBAAI,IAAyB;AAC7C,eAAW,SAAS,QAAQ;AAC1B,YAAM,UAAU,MAAM,OAAO,cAAc,CAAC,GAAG,QAAQ;AACvD,UAAI,CAAC,QAAQ,IAAI,OAAO,EAAG,SAAQ,IAAI,SAAS,CAAC,CAAC;AAClD,cAAQ,IAAI,OAAO,EAAG,KAAK,KAAK;AAAA,IAClC;AAEA,UAAM,QAAkB;AAAA,MACtB,MAAM,WAAW,eAAU,OAAO,MAAM;AAAA;AAAA,IAC1C;AACA,eAAW,CAAC,SAAS,OAAO,KAAK,SAAS;AACxC,YAAM,KAAK,gBAAS,OAAO,SAAS,QAAQ,MAAM,GAAG;AACrD,iBAAW,SAAS,SAAS;AAC3B,cAAM,SAAS,MAAM,OAAO,OAAO;AACnC,cAAM,OAAO,YAAY,MAAM,KAAK;AACpC,cAAM,OAAO,MAAM,OAAO,UAAU,QAAQ;AAC5C,cAAM,WAAW,MAAM,OAAO,UAAU,eAAe;AACvD,cAAM;AAAA,UACJ,GAAG,IAAI,OAAO,MAAM,GAAG,QAAQ,MAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,CAAC;AAAA,KAAQ,MAAM,SAAM,IAAI,SAAM,QAAQ;AAAA,QACxG;AAAA,MACF;AACA,YAAM,KAAK,EAAE;AAAA,IACf;AAEA,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AAAA,EACnC,SAAS,KAAK;AACZ,UAAM;AAAA,MACJ;AAAA,MACA,sBAAiB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACnE;AAAA,EACF;AACF;AAEA,eAAe,aAAa,KAA6B;AACvD,QAAM,OAAO,UAAU,IAAI,SAAS,QAAQ,IAAI,QAAQ;AACxD,QAAM,YAAY,KAAK,CAAC;AAExB,MAAI,CAAC,WAAW;AACd,UAAM,MAAM,KAAK,yBAAyB;AAC1C;AAAA,EACF;AAEA,QAAM,cAAc,qBAAqB,SAAS;AAClD,MAAI,CAAC,aAAa;AAChB,UAAM,MAAM,KAAK,mCAAmC,SAAS,OAAO;AACpE;AAAA,EACF;AAEA,MAAI;AAIF,QAASA,eAAT,SAAqB,MAAuB;AAC1C,UAAI,CAAC,KAAM,QAAO;AAClB,UAAI,OAAO,SAAS,SAAU,QAAO;AACrC,YAAM,IAAI;AACV,UAAI,EAAE,SAAS,OAAQ,QAAO,EAAE,QAAQ;AACxC,UAAI,EAAE,QAAS,QAAO,EAAE,QAAQ,IAAIA,YAAW,EAAE,KAAK,GAAG;AACzD,aAAO;AAAA,IACT;AAPS,sBAAAA;AAHT,UAAM,EAAE,QAAQ,IAAI,IAAI,eAAe,WAAW;AAClD,UAAM,EAAE,KAAK,IAAI,MAAM,OAAO,IAAI,UAAU,SAAS,EAAE;AAWvD,UAAM,cAAcA,aAAY,KAAK,OAAO,WAAW,EAAE,MAAM,GAAG,GAAG;AACrE,UAAM,KAAKA;AAAA,MACT,KAAK,OAAO,IAAI,KAAK,2BAA2B,mBAAmB;AAAA,IACrE,EAAE,MAAM,GAAG,GAAG;AAEd,UAAM,QAAQ;AAAA,MACZ,MAAM,KAAK,GAAG,eAAU,KAAK,OAAO,OAAO;AAAA,MAC3C,WAAW,KAAK,OAAO,OAAO,IAAI;AAAA,MAClC,aAAa,KAAK,OAAO,UAAU,QAAQ,QAAG;AAAA,MAC9C,aAAa,KAAK,OAAO,UAAU,eAAe,YAAY;AAAA,IAChE;AAEA,QAAI,YAAa,OAAM,KAAK;AAAA;AAAA,EAAyB,WAAW,EAAE;AAClE,QAAI,GAAI,OAAM,KAAK;AAAA;AAAA,EAAiC,EAAE,EAAE;AAExD,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AAAA,EACnC,SAAS,KAAK;AACZ,UAAM;AAAA,MACJ;AAAA,MACA,sBAAiB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACnE;AAAA,EACF;AACF;AAEA,eAAe,cAAc,KAA6B;AACxD,QAAM,OAAO,UAAU,IAAI,SAAS,QAAQ,IAAI,SAAS;AACzD,QAAM,YAAY,KAAK,CAAC;AACxB,QAAM,aAAa,KAAK,CAAC;AAEzB,MAAI,CAAC,WAAW;AACd,UAAM,MAAM,KAAK,oCAAoC;AACrD;AAAA,EACF;AAEA,QAAM,cAAc,cAAc,qBAAqB,SAAS;AAChE,MAAI,CAAC,aAAa;AAChB,UAAM,MAAM,KAAK,mCAAmC,SAAS,OAAO;AACpE;AAAA,EACF;AAEA,QAAM,SAAS,IAAI,MAAM;AACzB,MAAI,CAAC,OAAQ;AAEb,MAAI;AACF,UAAM,MAAM,WAAW,WAAW;AAClC,UAAM,MAAM,OAAO,IAAI,SAAS,QAAQ;AACxC,UAAM,aAAa,KAAK,QAAQ,WAAW,WAAW;AACtD,UAAM,MAAM,KAAK,0BAAmB,SAAS,gBAAgB;AAAA,EAC/D,SAAS,KAAK;AACZ,UAAM;AAAA,MACJ;AAAA,MACA,0BAAqB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IACvE;AAAA,EACF;AACF;AAEA,eAAe,gBAAgB,KAA6B;AAC1D,QAAM,OAAO,UAAU,IAAI,SAAS,QAAQ,IAAI,WAAW;AAC3D,QAAM,SAAS,IAAI,MAAM;AACzB,MAAI,CAAC,OAAQ;AAEb,QAAM,QAAQ,KAAK,CAAC;AACpB,QAAM,QAAqB,CAAC,OAAO,UAAU,MAAM;AAEnD,MAAI,CAAC,SAAS,CAAC,MAAM,SAAS,KAAK,GAAG;AACpC,UAAM,IAAI,MAAM,2BAA2B;AAAA,MACzC,cAAc;AAAA,QACZ,iBAAiB;AAAA,UACf;AAAA,YACE,EAAE,MAAM,iBAAU,eAAe,gBAAgB;AAAA,YACjD,EAAE,MAAM,oBAAa,eAAe,mBAAmB;AAAA,YACvD,EAAE,MAAM,kBAAW,eAAe,iBAAiB;AAAA,UACrD;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AACD;AAAA,EACF;AAEA,eAAa,QAAQ,KAAK;AAC1B,QAAM,MAAM,KAAK,8BAAyB,KAAK,OAAO;AACxD;AAIO,SAAS,oBAAoB,OAAqB;AACvD,QAAM,MAAM,OAAO,KAAK;AAExB,MAAI,QAAQ,OAAO,CAAC,QAAQ,UAAU,GAAG,EAAE,MAAM,MAAM,MAAS,CAAC;AACjE,MAAI,QAAQ,SAAS,CAAC,QAAQ,YAAY,GAAG,EAAE,MAAM,MAAM,MAAS,CAAC;AACrE,MAAI,QAAQ,UAAU,CAAC,QAAQ,aAAa,GAAG,EAAE,MAAM,MAAM,MAAS,CAAC;AACvE,MAAI,QAAQ,UAAU,CAAC,QAAQ,aAAa,GAAG,EAAE,MAAM,MAAM,MAAS,CAAC;AACvE,MAAI,QAAQ,QAAQ,CAAC,QAAQ,WAAW,GAAG,EAAE,MAAM,MAAM,MAAS,CAAC;AACnE,MAAI,QAAQ,YAAY,CAAC,QAAQ,eAAe,GAAG,EAAE,MAAM,MAAM,MAAS,CAAC;AAC3E,MAAI,QAAQ,WAAW,CAAC,QAAQ,cAAc,GAAG,EAAE,MAAM,MAAM,MAAS,CAAC;AACzE,MAAI,QAAQ,UAAU,CAAC,QAAQ,aAAa,GAAG,EAAE,MAAM,MAAM,MAAS,CAAC;AACvE,MAAI,QAAQ,WAAW,CAAC,QAAQ,cAAc,GAAG,EAAE,MAAM,MAAM,MAAS,CAAC;AACzE,MAAI;AAAA,IAAQ;AAAA,IAAa,CAAC,QACxB,gBAAgB,GAAG,EAAE,MAAM,MAAM,MAAS;AAAA,EAC5C;AAGA,MAAI,GAAG,uBAAuB,OAAO,QAAQ;AAC3C,UAAM,OAAO,IAAI,cAAc,QAAQ;AACvC,QAAI,CAAC,KAAK,WAAW,YAAY,EAAG;AAEpC,UAAM,QAAQ,KAAK,MAAM,aAAa,MAAM;AAC5C,UAAM,SAAS,IAAI,MAAM;AACzB,QAAI,OAAQ,cAAa,QAAQ,KAAK;AAEtC,UAAM,IAAI,oBAAoB,EAAE,MAAM,oBAAoB,KAAK,GAAG,CAAC;AACnE,UAAM,IAAI,uBAAuB,EAAE,cAAc,EAAE,iBAAiB,CAAC,EAAE,EAAE,CAAC;AAAA,EAC5E,CAAC;AACH;","names":["extractText"]}
|