patchrelay 0.35.10 → 0.35.12

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.
Files changed (50) hide show
  1. package/README.md +41 -9
  2. package/dist/build-info.json +3 -3
  3. package/dist/cli/args.js +0 -1
  4. package/dist/cli/commands/issues.js +2 -56
  5. package/dist/cli/commands/watch.js +5 -0
  6. package/dist/cli/data.js +110 -47
  7. package/dist/cli/formatters/text.js +6 -90
  8. package/dist/cli/help.js +3 -8
  9. package/dist/cli/index.js +0 -48
  10. package/dist/cli/operator-client.js +0 -82
  11. package/dist/cli/watch/App.js +1 -12
  12. package/dist/cli/watch/HelpBar.js +2 -2
  13. package/dist/cli/watch/IssueDetailView.js +57 -26
  14. package/dist/cli/watch/IssueRow.js +71 -27
  15. package/dist/cli/watch/StatusBar.js +7 -4
  16. package/dist/cli/watch/state-visualization.js +48 -23
  17. package/dist/cli/watch/timeline-builder.js +2 -1
  18. package/dist/cli/watch/use-detail-stream.js +10 -104
  19. package/dist/cli/watch/use-watch-stream.js +11 -102
  20. package/dist/cli/watch/watch-state.js +18 -50
  21. package/dist/codex-thread-utils.js +3 -0
  22. package/dist/db/migrations.js +239 -2
  23. package/dist/db.js +628 -39
  24. package/dist/github-app-token.js +7 -0
  25. package/dist/github-failure-context.js +44 -1
  26. package/dist/github-rollup.js +47 -0
  27. package/dist/github-webhook-handler.js +248 -51
  28. package/dist/github-webhooks.js +5 -0
  29. package/dist/http.js +12 -264
  30. package/dist/idle-reconciliation.js +275 -74
  31. package/dist/issue-query-service.js +221 -129
  32. package/dist/issue-session-events.js +151 -0
  33. package/dist/issue-session.js +99 -0
  34. package/dist/linear-client.js +39 -25
  35. package/dist/linear-session-reporting.js +12 -0
  36. package/dist/linear-session-sync.js +253 -24
  37. package/dist/linear-workflow.js +33 -0
  38. package/dist/merge-queue-protocol.js +0 -51
  39. package/dist/preflight.js +1 -4
  40. package/dist/queue-health-monitor.js +11 -7
  41. package/dist/run-orchestrator.js +1295 -146
  42. package/dist/run-reporting.js +5 -3
  43. package/dist/service.js +279 -102
  44. package/dist/status-note.js +56 -0
  45. package/dist/waiting-reason.js +65 -0
  46. package/dist/webhook-handler.js +270 -79
  47. package/package.json +1 -1
  48. package/dist/cli/commands/feed.js +0 -60
  49. package/dist/cli/watch/FeedView.js +0 -28
  50. package/dist/cli/watch/use-feed-stream.js +0 -92
@@ -1,60 +0,0 @@
1
- import { formatJson } from "../formatters/json.js";
2
- import { formatOperatorFeed, formatOperatorFeedEvent } from "../formatters/text.js";
3
- import { writeOutput } from "../output.js";
4
- function parseLimit(value) {
5
- if (typeof value !== "string") {
6
- return 50;
7
- }
8
- const trimmed = value.trim();
9
- if (!/^\d+$/.test(trimmed)) {
10
- throw new Error("--limit must be a positive integer.");
11
- }
12
- const parsed = Number(trimmed);
13
- if (!Number.isSafeInteger(parsed) || parsed <= 0) {
14
- throw new Error("--limit must be a positive integer.");
15
- }
16
- return parsed;
17
- }
18
- function readOptionalStringFlag(parsed, name) {
19
- const value = parsed.flags.get(name);
20
- if (value === true) {
21
- throw new Error(`--${name} requires a value.`);
22
- }
23
- return typeof value === "string" ? value.trim() || undefined : undefined;
24
- }
25
- export async function handleFeedCommand(params) {
26
- const limit = parseLimit(params.parsed.flags.get("limit"));
27
- const follow = params.parsed.flags.get("follow") === true;
28
- const issueKey = readOptionalStringFlag(params.parsed, "issue");
29
- const projectId = readOptionalStringFlag(params.parsed, "repo");
30
- const kind = readOptionalStringFlag(params.parsed, "kind");
31
- const stage = readOptionalStringFlag(params.parsed, "stage");
32
- const status = readOptionalStringFlag(params.parsed, "status");
33
- const workflowId = readOptionalStringFlag(params.parsed, "workflow");
34
- const query = {
35
- limit,
36
- ...(issueKey ? { issueKey } : {}),
37
- ...(projectId ? { projectId } : {}),
38
- ...(kind ? { kind } : {}),
39
- ...(stage ? { stage } : {}),
40
- ...(status ? { status } : {}),
41
- ...(workflowId ? { workflowId } : {}),
42
- };
43
- if (!follow) {
44
- const result = await params.data.listOperatorFeed(query);
45
- writeOutput(params.stdout, params.json
46
- ? formatJson(result)
47
- : formatOperatorFeed(result, { color: "isTTY" in params.stdout && params.stdout.isTTY === true }));
48
- return 0;
49
- }
50
- if (params.json) {
51
- await params.data.followOperatorFeed((event) => {
52
- writeOutput(params.stdout, formatJson(event));
53
- }, query);
54
- return 0;
55
- }
56
- await params.data.followOperatorFeed((event) => {
57
- writeOutput(params.stdout, formatOperatorFeedEvent(event, { color: "isTTY" in params.stdout && params.stdout.isTTY === true }));
58
- }, query);
59
- return 0;
60
- }
@@ -1,28 +0,0 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Box, Text } from "ink";
3
- import { HelpBar } from "./HelpBar.js";
4
- import { FreshnessBadge } from "./FreshnessBadge.js";
5
- const TAIL_SIZE = 30;
6
- const KIND_COLORS = {
7
- stage: "cyan",
8
- turn: "yellow",
9
- github: "green",
10
- webhook: "blue",
11
- agent: "magenta",
12
- service: "white",
13
- workflow: "cyan",
14
- linear: "blue",
15
- comment: "cyan",
16
- };
17
- function formatTime(iso) {
18
- return new Date(iso).toLocaleTimeString("en-GB", { hour12: false });
19
- }
20
- function FeedEventRow({ event }) {
21
- const kindColor = KIND_COLORS[event.kind] ?? "white";
22
- return (_jsxs(Box, { children: [_jsxs(Text, { dimColor: true, children: [formatTime(event.at), " "] }), _jsx(Text, { color: kindColor, children: (event.status ?? event.kind).padEnd(14) }), event.issueKey && _jsx(Text, { bold: true, children: ` ${event.issueKey.padEnd(9)}` }), _jsxs(Text, { children: [" ", event.summary] })] }));
23
- }
24
- export function FeedView({ events, connected, lastServerMessageAt }) {
25
- const visible = events.length > TAIL_SIZE ? events.slice(-TAIL_SIZE) : events;
26
- const skipped = events.length - visible.length;
27
- return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { bold: true, children: "Operator Feed" }), _jsx(FreshnessBadge, { connected: connected, lastServerMessageAt: lastServerMessageAt })] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: events.length === 0 ? (_jsx(Text, { dimColor: true, children: "No feed events yet." })) : (_jsxs(_Fragment, { children: [skipped > 0 && _jsxs(Text, { dimColor: true, children: [" ... ", skipped, " earlier"] }), visible.map((event) => (_jsx(FeedEventRow, { event: event }, event.id)))] })) }), _jsx(Box, { marginTop: 1, children: _jsx(HelpBar, { view: "feed" }) })] }));
28
- }
@@ -1,92 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
- export function useFeedStream(options) {
3
- const optionsRef = useRef(options);
4
- optionsRef.current = options;
5
- useEffect(() => {
6
- if (!options.active)
7
- return;
8
- const abortController = new AbortController();
9
- const { baseUrl, bearerToken, dispatch } = optionsRef.current;
10
- void (async () => {
11
- try {
12
- const url = new URL("/api/feed", baseUrl);
13
- url.searchParams.set("follow", "1");
14
- url.searchParams.set("limit", "100");
15
- const headers = { accept: "text/event-stream" };
16
- if (bearerToken)
17
- headers.authorization = `Bearer ${bearerToken}`;
18
- const response = await fetch(url, { headers, signal: abortController.signal });
19
- if (!response.ok || !response.body)
20
- return;
21
- const reader = response.body.getReader();
22
- const decoder = new TextDecoder();
23
- let buffer = "";
24
- let eventType = "";
25
- let dataLines = [];
26
- let initialBatch = [];
27
- let snapshotSent = false;
28
- while (true) {
29
- const { done, value } = await reader.read();
30
- if (done)
31
- break;
32
- buffer += decoder.decode(value, { stream: true });
33
- let newlineIndex = buffer.indexOf("\n");
34
- while (newlineIndex !== -1) {
35
- const rawLine = buffer.slice(0, newlineIndex);
36
- buffer = buffer.slice(newlineIndex + 1);
37
- const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
38
- if (!line) {
39
- if (dataLines.length > 0 && eventType === "feed") {
40
- try {
41
- const event = JSON.parse(dataLines.join("\n"));
42
- if (!snapshotSent) {
43
- initialBatch.push(event);
44
- }
45
- else {
46
- dispatch({ type: "feed-new-event", event });
47
- }
48
- }
49
- catch { /* ignore parse errors */ }
50
- dataLines = [];
51
- eventType = "";
52
- }
53
- // After processing a batch of initial events, flush snapshot
54
- if (!snapshotSent && initialBatch.length > 0) {
55
- // Use a microtask to batch initial events
56
- const batch = initialBatch;
57
- initialBatch = [];
58
- snapshotSent = true;
59
- dispatch({ type: "feed-snapshot", events: batch });
60
- }
61
- newlineIndex = buffer.indexOf("\n");
62
- continue;
63
- }
64
- if (line.startsWith(":")) {
65
- // Keepalive or comment - flush initial batch if pending
66
- if (!snapshotSent && initialBatch.length > 0) {
67
- snapshotSent = true;
68
- dispatch({ type: "feed-snapshot", events: initialBatch });
69
- initialBatch = [];
70
- }
71
- newlineIndex = buffer.indexOf("\n");
72
- continue;
73
- }
74
- if (line.startsWith("event:")) {
75
- eventType = line.slice(6).trim();
76
- }
77
- else if (line.startsWith("data:")) {
78
- dataLines.push(line.slice(5).trimStart());
79
- }
80
- newlineIndex = buffer.indexOf("\n");
81
- }
82
- }
83
- }
84
- catch {
85
- // Stream ended or aborted
86
- }
87
- })();
88
- return () => {
89
- abortController.abort();
90
- };
91
- }, [options.active]);
92
- }