patchrelay 0.25.5 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/build-info.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
|
+
import { summarizeIssueStatusNote } from "./issue-status-note.js";
|
|
3
4
|
const STATE_COLORS = {
|
|
4
5
|
delegated: "blue",
|
|
5
6
|
preparing: "blue",
|
|
@@ -28,12 +29,6 @@ const STATE_SHORT = {
|
|
|
28
29
|
escalated: "escalated",
|
|
29
30
|
awaiting_input: "paused",
|
|
30
31
|
};
|
|
31
|
-
const RUN_SHORT = {
|
|
32
|
-
implementation: "impl",
|
|
33
|
-
ci_repair: "ci",
|
|
34
|
-
review_fix: "review",
|
|
35
|
-
queue_repair: "merge",
|
|
36
|
-
};
|
|
37
32
|
const STATUS_SHORT = {
|
|
38
33
|
running: "\u25b8",
|
|
39
34
|
completed: "\u2713",
|
|
@@ -43,15 +38,6 @@ const STATUS_SHORT = {
|
|
|
43
38
|
function stateColor(state) {
|
|
44
39
|
return STATE_COLORS[state] ?? "white";
|
|
45
40
|
}
|
|
46
|
-
function formatRun(issue) {
|
|
47
|
-
const run = issue.activeRunType ?? issue.latestRunType;
|
|
48
|
-
if (!run)
|
|
49
|
-
return "";
|
|
50
|
-
const runLabel = RUN_SHORT[run] ?? run;
|
|
51
|
-
const status = issue.activeRunType ? "running" : issue.latestRunStatus;
|
|
52
|
-
const statusLabel = status ? STATUS_SHORT[status] ?? status : "";
|
|
53
|
-
return `${runLabel} ${statusLabel}`;
|
|
54
|
-
}
|
|
55
41
|
function formatPr(issue) {
|
|
56
42
|
if (!issue.prNumber)
|
|
57
43
|
return "";
|
|
@@ -81,7 +67,7 @@ function relativeTime(iso) {
|
|
|
81
67
|
function truncate(text, max) {
|
|
82
68
|
if (max <= 0)
|
|
83
69
|
return "";
|
|
84
|
-
return text.length > max ?
|
|
70
|
+
return text.length > max ? text.slice(0, max) : text;
|
|
85
71
|
}
|
|
86
72
|
const TERMINAL_STATES = new Set(["done", "failed", "escalated", "awaiting_input"]);
|
|
87
73
|
function formatStatus(issue) {
|
|
@@ -103,5 +89,6 @@ export function IssueRow({ issue, selected, titleWidth }) {
|
|
|
103
89
|
const ago = relativeTime(issue.updatedAt);
|
|
104
90
|
const tw = titleWidth ?? 30;
|
|
105
91
|
const title = issue.title ? truncate(issue.title, tw) : "";
|
|
106
|
-
|
|
92
|
+
const detail = selected ? summarizeIssueStatusNote(issue.statusNote) : undefined;
|
|
93
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: detail ? 1 : 0, children: [_jsxs(Box, { children: [_jsx(Text, { color: selected ? "blueBright" : "white", bold: selected, children: selected ? "\u25b8" : " " }), _jsx(Text, { bold: true, children: ` ${key.padEnd(9)}` }), _jsx(Text, { color: stateColor(issue.factoryState), children: ` ${status.padEnd(12)}` }), _jsx(Text, { dimColor: true, children: ` ${pr.padEnd(6)}` }), _jsx(Text, { dimColor: true, children: ` ${ago.padStart(3)}` }), title ? _jsx(Text, { dimColor: true, children: ` ${title}` }) : null] }), detail ? (_jsx(Box, { paddingLeft: 4, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: detail }) })) : null] }));
|
|
107
94
|
}
|
|
@@ -51,15 +51,37 @@ function detailPrefix(detail) {
|
|
|
51
51
|
return "$ ";
|
|
52
52
|
return "";
|
|
53
53
|
}
|
|
54
|
+
function verboseItemLabel(type) {
|
|
55
|
+
switch (type) {
|
|
56
|
+
case "agentMessage":
|
|
57
|
+
return "message";
|
|
58
|
+
case "commandExecution":
|
|
59
|
+
return "command";
|
|
60
|
+
case "fileChange":
|
|
61
|
+
return "files";
|
|
62
|
+
case "mcpToolCall":
|
|
63
|
+
case "dynamicToolCall":
|
|
64
|
+
return "tool";
|
|
65
|
+
case "userMessage":
|
|
66
|
+
return "you";
|
|
67
|
+
case "plan":
|
|
68
|
+
return "plan";
|
|
69
|
+
case "reasoning":
|
|
70
|
+
return "reasoning";
|
|
71
|
+
default:
|
|
72
|
+
return type;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
54
75
|
function FeedRow({ entry }) {
|
|
55
76
|
const label = entry.feed.status ?? entry.feed.feedKind;
|
|
56
77
|
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [formatTime(entry.at), " "] }), _jsx(Text, { color: "cyan", bold: true, children: label.padEnd(12) })] }), _jsx(Box, { paddingLeft: 6, children: _jsx(Text, { wrap: "wrap", children: entry.feed.summary }) })] }));
|
|
57
78
|
}
|
|
58
|
-
function RunRow({ entry }) {
|
|
79
|
+
function RunRow({ entry, mode, }) {
|
|
59
80
|
const run = entry.run;
|
|
60
81
|
const color = runStatusColor(run.status);
|
|
61
82
|
const duration = run.endedAt ? formatDuration(run.startedAt, run.endedAt) : undefined;
|
|
62
|
-
|
|
83
|
+
const showVerboseItems = mode === "verbose" && entry.items.length > 0;
|
|
84
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [formatTime(entry.at), " "] }), _jsx(Text, { bold: true, color: "yellow", children: (RUN_LABELS[run.runType] ?? run.runType).padEnd(12) }), _jsxs(Text, { bold: true, color: color, children: [" ", runStatusLabel(run.status)] }), duration ? _jsx(Text, { dimColor: true, children: ` ${duration}` }) : null] }), entry.details.length > 0 && _jsx(Text, { children: " " }), entry.details.map((detail, index) => (_jsx(Box, { paddingLeft: 6, marginBottom: index === entry.details.length - 1 ? 0 : 1, children: _jsxs(Text, { wrap: "wrap", ...(detailColor(detail) ? { color: detailColor(detail) } : {}), bold: detail.tone === "message", children: [detailPrefix(detail), detail.text] }) }, `${entry.id}-detail-${index}`))), showVerboseItems && _jsx(Text, { children: " " }), showVerboseItems && entry.items.map((itemEntry, index) => (_jsxs(Box, { flexDirection: "column", paddingLeft: 6, marginBottom: index === entry.items.length - 1 ? 0 : 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { dimColor: true, children: [formatTime(itemEntry.at), " "] }), _jsx(Text, { dimColor: true, children: verboseItemLabel(itemEntry.item.type) })] }), _jsx(Box, { paddingLeft: 2, children: _jsx(ItemLine, { item: itemEntry.item }) })] }, `${entry.id}-item-${index}`)))] }));
|
|
63
85
|
}
|
|
64
86
|
function ItemRow({ entry, mode, }) {
|
|
65
87
|
return (_jsxs(Box, { flexDirection: "column", paddingLeft: 6, marginBottom: mode === "verbose" ? 1 : 0, children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { dimColor: true, children: [formatTime(entry.at), " "] }), _jsx(Text, { dimColor: true, children: entry.item.type })] }), _jsx(Box, { paddingLeft: 2, children: _jsx(ItemLine, { item: entry.item }) })] }));
|
|
@@ -73,7 +95,7 @@ export function TimelineRow({ entry, mode }) {
|
|
|
73
95
|
case "feed":
|
|
74
96
|
return _jsx(FeedRow, { entry: entry });
|
|
75
97
|
case "run":
|
|
76
|
-
return _jsx(RunRow, { entry: entry });
|
|
98
|
+
return _jsx(RunRow, { entry: entry, mode: mode });
|
|
77
99
|
case "item":
|
|
78
100
|
return _jsx(ItemRow, { entry: entry, mode: mode });
|
|
79
101
|
case "ci-checks":
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function stripMarkdownLinks(text) {
|
|
2
|
+
return text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
3
|
+
}
|
|
4
|
+
export function summarizeIssueStatusNote(raw) {
|
|
5
|
+
if (!raw)
|
|
6
|
+
return undefined;
|
|
7
|
+
const firstParagraph = raw.split(/\n\s*\n/).find((part) => part.trim().length > 0)?.trim();
|
|
8
|
+
if (!firstParagraph)
|
|
9
|
+
return undefined;
|
|
10
|
+
const normalized = stripMarkdownLinks(firstParagraph)
|
|
11
|
+
.replace(/`([^`]+)`/g, "$1")
|
|
12
|
+
.replace(/\s+/g, " ")
|
|
13
|
+
.trim();
|
|
14
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
15
|
+
}
|
package/dist/service.js
CHANGED
|
@@ -9,6 +9,31 @@ import { buildSessionStatusUrl, createSessionStatusToken, deriveSessionStatusSig
|
|
|
9
9
|
import { ServiceRuntime } from "./service-runtime.js";
|
|
10
10
|
import { WebhookHandler } from "./webhook-handler.js";
|
|
11
11
|
import { acceptIncomingWebhook } from "./service-webhooks.js";
|
|
12
|
+
function parseObjectJson(value) {
|
|
13
|
+
if (!value)
|
|
14
|
+
return undefined;
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(value);
|
|
17
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function extractStatusNote(summaryJson, reportJson) {
|
|
24
|
+
const summary = parseObjectJson(summaryJson);
|
|
25
|
+
if (typeof summary?.latestAssistantMessage === "string" && summary.latestAssistantMessage.trim()) {
|
|
26
|
+
return summary.latestAssistantMessage;
|
|
27
|
+
}
|
|
28
|
+
const report = parseObjectJson(reportJson);
|
|
29
|
+
const assistantMessages = report?.assistantMessages;
|
|
30
|
+
if (Array.isArray(assistantMessages)) {
|
|
31
|
+
const latest = assistantMessages.findLast((value) => typeof value === "string" && value.trim().length > 0);
|
|
32
|
+
if (typeof latest === "string")
|
|
33
|
+
return latest;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
12
37
|
export class PatchRelayService {
|
|
13
38
|
config;
|
|
14
39
|
db;
|
|
@@ -166,7 +191,9 @@ export class PatchRelayService {
|
|
|
166
191
|
i.pr_number, i.pr_review_state, i.pr_check_status,
|
|
167
192
|
active_run.run_type AS active_run_type,
|
|
168
193
|
latest_run.run_type AS latest_run_type,
|
|
169
|
-
latest_run.status AS latest_run_status
|
|
194
|
+
latest_run.status AS latest_run_status,
|
|
195
|
+
latest_run.summary_json AS latest_run_summary_json,
|
|
196
|
+
latest_run.report_json AS latest_run_report_json
|
|
170
197
|
FROM issues i
|
|
171
198
|
LEFT JOIN runs active_run ON active_run.id = i.active_run_id
|
|
172
199
|
LEFT JOIN runs latest_run ON latest_run.id = (
|
|
@@ -176,20 +203,24 @@ export class PatchRelayService {
|
|
|
176
203
|
)
|
|
177
204
|
ORDER BY i.updated_at DESC, i.issue_key ASC`)
|
|
178
205
|
.all();
|
|
179
|
-
return rows.map((row) =>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
206
|
+
return rows.map((row) => {
|
|
207
|
+
const statusNote = extractStatusNote(typeof row.latest_run_summary_json === "string" ? row.latest_run_summary_json : undefined, typeof row.latest_run_report_json === "string" ? row.latest_run_report_json : undefined);
|
|
208
|
+
return {
|
|
209
|
+
...(row.issue_key !== null ? { issueKey: String(row.issue_key) } : {}),
|
|
210
|
+
...(row.title !== null ? { title: String(row.title) } : {}),
|
|
211
|
+
...(statusNote ? { statusNote } : {}),
|
|
212
|
+
projectId: String(row.project_id),
|
|
213
|
+
factoryState: String(row.factory_state ?? "delegated"),
|
|
214
|
+
...(row.current_linear_state !== null ? { currentLinearState: String(row.current_linear_state) } : {}),
|
|
215
|
+
...(row.active_run_type !== null ? { activeRunType: String(row.active_run_type) } : {}),
|
|
216
|
+
...(row.latest_run_type !== null ? { latestRunType: String(row.latest_run_type) } : {}),
|
|
217
|
+
...(row.latest_run_status !== null ? { latestRunStatus: String(row.latest_run_status) } : {}),
|
|
218
|
+
...(row.pr_number !== null ? { prNumber: Number(row.pr_number) } : {}),
|
|
219
|
+
...(row.pr_review_state !== null ? { prReviewState: String(row.pr_review_state) } : {}),
|
|
220
|
+
...(row.pr_check_status !== null ? { prCheckStatus: String(row.pr_check_status) } : {}),
|
|
221
|
+
updatedAt: String(row.updated_at),
|
|
222
|
+
};
|
|
223
|
+
});
|
|
193
224
|
}
|
|
194
225
|
subscribeCodexNotifications(listener) {
|
|
195
226
|
let trackedThreadId;
|