patchrelay 0.8.9 → 0.9.1
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 +64 -62
- package/dist/agent-session-plan.js +17 -17
- package/dist/build-info.json +3 -3
- package/dist/cli/args.js +1 -1
- package/dist/cli/commands/issues.js +18 -18
- package/dist/cli/data.js +109 -298
- package/dist/cli/formatters/text.js +22 -28
- package/dist/cli/help.js +7 -7
- package/dist/cli/index.js +3 -3
- package/dist/config.js +13 -166
- package/dist/db/migrations.js +46 -154
- package/dist/db.js +369 -45
- package/dist/factory-state.js +55 -0
- package/dist/github-webhook-handler.js +199 -0
- package/dist/github-webhooks.js +166 -0
- package/dist/hook-runner.js +28 -0
- package/dist/http.js +48 -22
- package/dist/issue-query-service.js +33 -38
- package/dist/linear-workflow.js +5 -118
- package/dist/preflight.js +1 -6
- package/dist/project-resolution.js +12 -1
- package/dist/run-orchestrator.js +446 -0
- package/dist/{stage-reporting.js → run-reporting.js} +11 -13
- package/dist/service-runtime.js +12 -61
- package/dist/service-webhooks.js +7 -52
- package/dist/service.js +39 -61
- package/dist/webhook-handler.js +387 -0
- package/dist/webhook-installation-handler.js +3 -8
- package/package.json +2 -1
- package/dist/db/authoritative-ledger-store.js +0 -536
- package/dist/db/issue-projection-store.js +0 -54
- package/dist/db/issue-workflow-coordinator.js +0 -320
- package/dist/db/issue-workflow-store.js +0 -194
- package/dist/db/run-report-store.js +0 -33
- package/dist/db/stage-event-store.js +0 -33
- package/dist/db/webhook-event-store.js +0 -59
- package/dist/db-ports.js +0 -5
- package/dist/ledger-ports.js +0 -1
- package/dist/reconciliation-action-applier.js +0 -68
- package/dist/reconciliation-actions.js +0 -1
- package/dist/reconciliation-engine.js +0 -350
- package/dist/reconciliation-snapshot-builder.js +0 -135
- package/dist/reconciliation-types.js +0 -1
- package/dist/service-stage-finalizer.js +0 -753
- package/dist/service-stage-runner.js +0 -336
- package/dist/service-webhook-processor.js +0 -411
- package/dist/stage-agent-activity-publisher.js +0 -59
- package/dist/stage-event-ports.js +0 -1
- package/dist/stage-failure.js +0 -92
- package/dist/stage-handoff.js +0 -107
- package/dist/stage-launch.js +0 -84
- package/dist/stage-lifecycle-publisher.js +0 -284
- package/dist/stage-turn-input-dispatcher.js +0 -104
- package/dist/webhook-agent-session-handler.js +0 -228
- package/dist/webhook-comment-handler.js +0 -141
- package/dist/webhook-desired-stage-recorder.js +0 -122
- package/dist/webhook-event-ports.js +0 -1
- package/dist/workflow-policy.js +0 -149
- package/dist/workflow-ports.js +0 -1
- /package/dist/{installation-ports.js → github-types.js} +0 -0
package/dist/service-runtime.js
CHANGED
|
@@ -2,46 +2,14 @@ import { SerialWorkQueue } from "./service-queue.js";
|
|
|
2
2
|
const ISSUE_KEY_DELIMITER = "::";
|
|
3
3
|
const DEFAULT_RECONCILE_INTERVAL_MS = 5_000;
|
|
4
4
|
const DEFAULT_RECONCILE_TIMEOUT_MS = 60_000;
|
|
5
|
-
function toReconciler(value) {
|
|
6
|
-
if (typeof value === "function") {
|
|
7
|
-
return {
|
|
8
|
-
reconcileActiveStageRuns: value,
|
|
9
|
-
};
|
|
10
|
-
}
|
|
11
|
-
return value;
|
|
12
|
-
}
|
|
13
|
-
function toReadyIssueSource(value) {
|
|
14
|
-
if (typeof value === "function") {
|
|
15
|
-
return {
|
|
16
|
-
listIssuesReadyForExecution: value,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
return value;
|
|
20
|
-
}
|
|
21
|
-
function toWebhookProcessor(value) {
|
|
22
|
-
if (typeof value === "function") {
|
|
23
|
-
return {
|
|
24
|
-
processWebhookEvent: value,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
function toIssueProcessor(value) {
|
|
30
|
-
if (typeof value === "function") {
|
|
31
|
-
return {
|
|
32
|
-
processIssue: value,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
5
|
function makeIssueQueueKey(item) {
|
|
38
6
|
return `${item.projectId}${ISSUE_KEY_DELIMITER}${item.issueId}`;
|
|
39
7
|
}
|
|
40
|
-
// ServiceRuntime is the coordination seam for the harness. It is responsible for
|
|
41
|
-
// startup reconciliation, queue ownership, and handing eligible work to the stage runner.
|
|
42
8
|
export class ServiceRuntime {
|
|
43
9
|
codex;
|
|
44
10
|
logger;
|
|
11
|
+
runReconciler;
|
|
12
|
+
readyIssueSource;
|
|
45
13
|
options;
|
|
46
14
|
webhookQueue;
|
|
47
15
|
issueQueue;
|
|
@@ -49,27 +17,19 @@ export class ServiceRuntime {
|
|
|
49
17
|
startupError;
|
|
50
18
|
reconcileTimer;
|
|
51
19
|
reconcileInProgress = false;
|
|
52
|
-
constructor(codex, logger,
|
|
20
|
+
constructor(codex, logger, runReconciler, readyIssueSource, webhookProcessor, issueProcessor, options = {}) {
|
|
53
21
|
this.codex = codex;
|
|
54
22
|
this.logger = logger;
|
|
23
|
+
this.runReconciler = runReconciler;
|
|
24
|
+
this.readyIssueSource = readyIssueSource;
|
|
55
25
|
this.options = options;
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
58
|
-
this.webhookProcessor = toWebhookProcessor(webhookProcessor);
|
|
59
|
-
this.issueProcessor = toIssueProcessor(issueProcessor);
|
|
60
|
-
this.webhookQueue = new SerialWorkQueue((eventId) => this.webhookProcessor.processWebhookEvent(eventId), logger, (eventId) => String(eventId));
|
|
61
|
-
this.issueQueue = new SerialWorkQueue((item) => this.issueProcessor.processIssue(item), logger, makeIssueQueueKey);
|
|
26
|
+
this.webhookQueue = new SerialWorkQueue((eventId) => webhookProcessor.processWebhookEvent(eventId), logger, (eventId) => String(eventId));
|
|
27
|
+
this.issueQueue = new SerialWorkQueue((item) => issueProcessor.processIssue(item), logger, makeIssueQueueKey);
|
|
62
28
|
}
|
|
63
|
-
stageRunReconciler;
|
|
64
|
-
readyIssueSource;
|
|
65
|
-
webhookProcessor;
|
|
66
|
-
issueProcessor;
|
|
67
29
|
async start() {
|
|
68
30
|
try {
|
|
69
31
|
await this.codex.start();
|
|
70
|
-
|
|
71
|
-
// resolve or release any previously claimed work deterministically.
|
|
72
|
-
await this.stageRunReconciler.reconcileActiveStageRuns();
|
|
32
|
+
await this.runReconciler.reconcileActiveRuns();
|
|
73
33
|
for (const issue of this.readyIssueSource.listIssuesReadyForExecution()) {
|
|
74
34
|
this.enqueueIssue(issue.projectId, issue.linearIssueId);
|
|
75
35
|
}
|
|
@@ -116,21 +76,18 @@ export class ServiceRuntime {
|
|
|
116
76
|
}
|
|
117
77
|
}
|
|
118
78
|
async runBackgroundReconcile() {
|
|
119
|
-
if (!this.ready || !this.codex.isStarted())
|
|
79
|
+
if (!this.ready || !this.codex.isStarted())
|
|
120
80
|
return;
|
|
121
|
-
}
|
|
122
81
|
if (this.reconcileInProgress) {
|
|
123
82
|
this.scheduleBackgroundReconcile();
|
|
124
83
|
return;
|
|
125
84
|
}
|
|
126
85
|
this.reconcileInProgress = true;
|
|
127
86
|
try {
|
|
128
|
-
await promiseWithTimeout(this.
|
|
87
|
+
await promiseWithTimeout(this.runReconciler.reconcileActiveRuns(), this.options.reconcileTimeoutMs ?? DEFAULT_RECONCILE_TIMEOUT_MS, "Background active-run reconciliation");
|
|
129
88
|
}
|
|
130
89
|
catch (error) {
|
|
131
|
-
this.logger.warn({
|
|
132
|
-
error: error instanceof Error ? error.message : String(error),
|
|
133
|
-
}, "Background active-stage reconciliation failed");
|
|
90
|
+
this.logger.warn({ error: error instanceof Error ? error.message : String(error) }, "Background active-run reconciliation failed");
|
|
134
91
|
}
|
|
135
92
|
finally {
|
|
136
93
|
this.reconcileInProgress = false;
|
|
@@ -146,12 +103,6 @@ function promiseWithTimeout(promise, timeoutMs, label) {
|
|
|
146
103
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
147
104
|
}, timeoutMs);
|
|
148
105
|
timeout.unref?.();
|
|
149
|
-
promise.then((value) => {
|
|
150
|
-
clearTimeout(timeout);
|
|
151
|
-
resolve(value);
|
|
152
|
-
}, (error) => {
|
|
153
|
-
clearTimeout(timeout);
|
|
154
|
-
reject(error);
|
|
155
|
-
});
|
|
106
|
+
promise.then((value) => { clearTimeout(timeout); resolve(value); }, (error) => { clearTimeout(timeout); reject(error); });
|
|
156
107
|
});
|
|
157
108
|
}
|
package/dist/service-webhooks.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { normalizeWebhook } from "./webhooks.js";
|
|
2
|
-
import {
|
|
2
|
+
import { timestampMsWithinSkew, verifyHmacSha256Hex } from "./utils.js";
|
|
3
3
|
export async function acceptIncomingWebhook(params) {
|
|
4
4
|
const receivedAt = new Date().toISOString();
|
|
5
5
|
const signature = typeof params.headers["linear-signature"] === "string" ? params.headers["linear-signature"] : "";
|
|
@@ -21,47 +21,24 @@ export async function acceptIncomingWebhook(params) {
|
|
|
21
21
|
}
|
|
22
22
|
let normalized;
|
|
23
23
|
try {
|
|
24
|
-
normalized = normalizeWebhook({
|
|
25
|
-
webhookId: params.webhookId,
|
|
26
|
-
payload,
|
|
27
|
-
});
|
|
24
|
+
normalized = normalizeWebhook({ webhookId: params.webhookId, payload });
|
|
28
25
|
}
|
|
29
26
|
catch (error) {
|
|
30
27
|
params.logger.warn({ webhookId: params.webhookId, error }, "Rejecting unsupported webhook payload");
|
|
31
28
|
return { status: 400, body: { ok: false, reason: "unsupported_payload" } };
|
|
32
29
|
}
|
|
33
|
-
const sanitizedHeaders = redactSensitiveHeaders(params.headers);
|
|
34
|
-
const headersJson = JSON.stringify(sanitizedHeaders);
|
|
35
30
|
const payloadJson = JSON.stringify(payload);
|
|
36
31
|
logWebhookSummary(params.logger, normalized);
|
|
37
32
|
const stored = params.stores.webhookEvents.insertWebhookEvent({
|
|
38
33
|
webhookId: params.webhookId,
|
|
39
34
|
receivedAt,
|
|
40
|
-
eventType: normalized.eventType,
|
|
41
|
-
...(normalized.issue ? { issueId: normalized.issue.id } : {}),
|
|
42
|
-
headersJson,
|
|
43
35
|
payloadJson,
|
|
44
|
-
signatureValid: true,
|
|
45
|
-
dedupeStatus: "accepted",
|
|
46
36
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
webhookId: params.webhookId,
|
|
50
|
-
receivedAt,
|
|
51
|
-
normalized,
|
|
52
|
-
headersJson,
|
|
53
|
-
payloadJson,
|
|
54
|
-
});
|
|
37
|
+
const isDuplicate = stored.dedupeStatus === "duplicate";
|
|
38
|
+
if (isDuplicate) {
|
|
55
39
|
params.logger.info({ webhookId: params.webhookId, webhookEventId: stored.id }, "Ignoring duplicate webhook delivery");
|
|
56
40
|
return { status: 200, body: { ok: true, duplicate: true } };
|
|
57
41
|
}
|
|
58
|
-
recordEventReceipt(params.stores, {
|
|
59
|
-
webhookId: params.webhookId,
|
|
60
|
-
receivedAt,
|
|
61
|
-
normalized,
|
|
62
|
-
headersJson,
|
|
63
|
-
payloadJson,
|
|
64
|
-
});
|
|
65
42
|
params.logger.info({
|
|
66
43
|
webhookId: params.webhookId,
|
|
67
44
|
webhookEventId: stored.id,
|
|
@@ -70,27 +47,11 @@ export async function acceptIncomingWebhook(params) {
|
|
|
70
47
|
issueId: normalized.issue?.id,
|
|
71
48
|
}, "Accepted Linear webhook for asynchronous processing");
|
|
72
49
|
return {
|
|
73
|
-
accepted: {
|
|
74
|
-
id: stored.id,
|
|
75
|
-
normalized,
|
|
76
|
-
payload,
|
|
77
|
-
},
|
|
50
|
+
accepted: { id: stored.id, normalized, payload },
|
|
78
51
|
status: 200,
|
|
79
52
|
body: { ok: true, accepted: true, webhookEventId: stored.id },
|
|
80
53
|
};
|
|
81
54
|
}
|
|
82
|
-
function recordEventReceipt(stores, params) {
|
|
83
|
-
stores.eventReceipts.insertEventReceipt({
|
|
84
|
-
source: "linear-webhook",
|
|
85
|
-
externalId: params.webhookId,
|
|
86
|
-
eventType: params.normalized.eventType,
|
|
87
|
-
receivedAt: params.receivedAt,
|
|
88
|
-
acceptanceStatus: "accepted",
|
|
89
|
-
...(params.normalized.issue ? { linearIssueId: params.normalized.issue.id } : {}),
|
|
90
|
-
headersJson: params.headersJson,
|
|
91
|
-
payloadJson: params.payloadJson,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
55
|
function logWebhookSummary(logger, normalized) {
|
|
95
56
|
const issueRef = normalized.issue?.identifier ?? normalized.issue?.id ?? normalized.installation?.appUserId ?? normalized.entityType;
|
|
96
57
|
const stateName = normalized.issue?.stateName;
|
|
@@ -100,9 +61,7 @@ function logWebhookSummary(logger, normalized) {
|
|
|
100
61
|
normalized.triggerEvent,
|
|
101
62
|
stateName ? `to ${stateName}` : undefined,
|
|
102
63
|
title ? `(${title})` : undefined,
|
|
103
|
-
]
|
|
104
|
-
.filter(Boolean)
|
|
105
|
-
.join(" ");
|
|
64
|
+
].filter(Boolean).join(" ");
|
|
106
65
|
logger.info({
|
|
107
66
|
issueKey: normalized.issue?.identifier,
|
|
108
67
|
triggerEvent: normalized.triggerEvent,
|
|
@@ -111,9 +70,5 @@ function logWebhookSummary(logger, normalized) {
|
|
|
111
70
|
appUserId: normalized.installation?.appUserId,
|
|
112
71
|
notificationType: normalized.installation?.notificationType,
|
|
113
72
|
}, summary);
|
|
114
|
-
logger.debug({
|
|
115
|
-
webhookId: normalized.webhookId,
|
|
116
|
-
eventType: normalized.eventType,
|
|
117
|
-
issueId: normalized.issue?.id,
|
|
118
|
-
}, "Webhook metadata");
|
|
73
|
+
logger.debug({ webhookId: normalized.webhookId, eventType: normalized.eventType, issueId: normalized.issue?.id }, "Webhook metadata");
|
|
119
74
|
}
|
package/dist/service.js
CHANGED
|
@@ -1,49 +1,21 @@
|
|
|
1
|
+
import { GitHubWebhookHandler } from "./github-webhook-handler.js";
|
|
1
2
|
import { IssueQueryService } from "./issue-query-service.js";
|
|
2
3
|
import { LinearOAuthService } from "./linear-oauth-service.js";
|
|
4
|
+
import { RunOrchestrator } from "./run-orchestrator.js";
|
|
3
5
|
import { OperatorEventFeed } from "./operator-feed.js";
|
|
4
6
|
import { buildSessionStatusUrl, createSessionStatusToken, deriveSessionStatusSigningSecret, verifySessionStatusToken, } from "./public-agent-session-status.js";
|
|
5
7
|
import { ServiceRuntime } from "./service-runtime.js";
|
|
6
|
-
import {
|
|
7
|
-
import { ServiceStageRunner } from "./service-stage-runner.js";
|
|
8
|
-
import { ServiceWebhookProcessor } from "./service-webhook-processor.js";
|
|
8
|
+
import { WebhookHandler } from "./webhook-handler.js";
|
|
9
9
|
import { acceptIncomingWebhook } from "./service-webhooks.js";
|
|
10
|
-
function createServiceStores(db) {
|
|
11
|
-
return {
|
|
12
|
-
webhookEvents: db.webhookEvents,
|
|
13
|
-
eventReceipts: db.eventReceipts,
|
|
14
|
-
issueControl: db.issueControl,
|
|
15
|
-
issueSessions: db.issueSessions,
|
|
16
|
-
workspaceOwnership: db.workspaceOwnership,
|
|
17
|
-
runLeases: db.runLeases,
|
|
18
|
-
obligations: db.obligations,
|
|
19
|
-
workflowCoordinator: db.workflowCoordinator,
|
|
20
|
-
issueWorkflows: db.issueWorkflows,
|
|
21
|
-
stageEvents: db.stageEvents,
|
|
22
|
-
linearInstallations: db.linearInstallations,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function createReadyIssueSource(stores) {
|
|
26
|
-
return {
|
|
27
|
-
listIssuesReadyForExecution: () => stores.issueControl.listIssueControlsReadyForLaunch().map((issue) => ({
|
|
28
|
-
projectId: issue.projectId,
|
|
29
|
-
linearIssueId: issue.linearIssueId,
|
|
30
|
-
})),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
// PatchRelayService wires together the harness layers:
|
|
34
|
-
// - integration: webhook intake, OAuth, Linear client access
|
|
35
|
-
// - coordination: runtime queueing, stage launch, completion, reconciliation
|
|
36
|
-
// - execution: Codex app-server and worktree-backed stage runs
|
|
37
|
-
// - observability: issue/report query surfaces
|
|
38
10
|
export class PatchRelayService {
|
|
39
11
|
config;
|
|
40
12
|
db;
|
|
41
13
|
codex;
|
|
42
14
|
logger;
|
|
43
15
|
linearProvider;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
16
|
+
orchestrator;
|
|
17
|
+
webhookHandler;
|
|
18
|
+
githubWebhookHandler;
|
|
47
19
|
oauthService;
|
|
48
20
|
queryService;
|
|
49
21
|
runtime;
|
|
@@ -55,22 +27,19 @@ export class PatchRelayService {
|
|
|
55
27
|
this.logger = logger;
|
|
56
28
|
this.linearProvider = toLinearClientProvider(linearProvider);
|
|
57
29
|
this.feed = new OperatorEventFeed(db.operatorFeed);
|
|
58
|
-
const stores = createServiceStores(db);
|
|
59
|
-
this.stageRunner = new ServiceStageRunner(config, stores, codex, this.linearProvider, logger, (fn) => db.connection.transaction(fn)(), this.feed);
|
|
60
30
|
let enqueueIssue = () => {
|
|
61
31
|
throw new Error("Service runtime enqueueIssue is not initialized");
|
|
62
32
|
};
|
|
63
|
-
this.
|
|
64
|
-
this.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
});
|
|
33
|
+
this.orchestrator = new RunOrchestrator(config, db, codex, this.linearProvider, (projectId, issueId) => enqueueIssue(projectId, issueId), logger, this.feed);
|
|
34
|
+
this.webhookHandler = new WebhookHandler(config, db, this.linearProvider, codex, (projectId, issueId) => enqueueIssue(projectId, issueId), logger, this.feed);
|
|
35
|
+
this.githubWebhookHandler = new GitHubWebhookHandler(config, db, this.linearProvider, (projectId, issueId) => enqueueIssue(projectId, issueId), logger, this.feed);
|
|
36
|
+
const runtime = new ServiceRuntime(codex, logger, this.orchestrator, { listIssuesReadyForExecution: () => db.listIssuesReadyForExecution() }, this.webhookHandler, { processIssue: (item) => this.orchestrator.run(item) });
|
|
68
37
|
enqueueIssue = (projectId, issueId) => runtime.enqueueIssue(projectId, issueId);
|
|
69
|
-
this.oauthService = new LinearOAuthService(config,
|
|
70
|
-
this.queryService = new IssueQueryService(
|
|
38
|
+
this.oauthService = new LinearOAuthService(config, { linearInstallations: db.linearInstallations }, logger);
|
|
39
|
+
this.queryService = new IssueQueryService(db, codex, this.orchestrator);
|
|
71
40
|
this.runtime = runtime;
|
|
72
41
|
this.codex.on("notification", (notification) => {
|
|
73
|
-
void this.
|
|
42
|
+
void this.orchestrator.handleCodexNotification(notification);
|
|
74
43
|
});
|
|
75
44
|
}
|
|
76
45
|
async start() {
|
|
@@ -104,8 +73,12 @@ export class PatchRelayService {
|
|
|
104
73
|
const result = await acceptIncomingWebhook({
|
|
105
74
|
config: this.config,
|
|
106
75
|
stores: {
|
|
107
|
-
webhookEvents:
|
|
108
|
-
|
|
76
|
+
webhookEvents: {
|
|
77
|
+
insertWebhookEvent: (p) => {
|
|
78
|
+
const r = this.db.insertFullWebhookEvent(p);
|
|
79
|
+
return { id: r.id, dedupeStatus: r.dedupeStatus };
|
|
80
|
+
},
|
|
81
|
+
},
|
|
109
82
|
},
|
|
110
83
|
logger: this.logger,
|
|
111
84
|
webhookId: params.webhookId,
|
|
@@ -122,11 +95,22 @@ export class PatchRelayService {
|
|
|
122
95
|
body: result.body,
|
|
123
96
|
};
|
|
124
97
|
}
|
|
98
|
+
async acceptGitHubWebhook(params) {
|
|
99
|
+
const result = await this.githubWebhookHandler.acceptGitHubWebhook(params);
|
|
100
|
+
if (result.body.accepted && result.body.webhookEventId) {
|
|
101
|
+
// Process inline since GitHub events are lightweight (just PR state updates)
|
|
102
|
+
await this.githubWebhookHandler.processGitHubWebhookEvent({
|
|
103
|
+
eventType: params.eventType,
|
|
104
|
+
rawBody: params.rawBody.toString("utf8"),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
125
109
|
async processWebhookEvent(webhookEventId) {
|
|
126
|
-
await this.
|
|
110
|
+
await this.webhookHandler.processWebhookEvent(webhookEventId);
|
|
127
111
|
}
|
|
128
112
|
async processIssue(item) {
|
|
129
|
-
await this.
|
|
113
|
+
await this.orchestrator.run(item);
|
|
130
114
|
}
|
|
131
115
|
async getIssueOverview(issueKey) {
|
|
132
116
|
return await this.queryService.getIssueOverview(issueKey);
|
|
@@ -134,16 +118,15 @@ export class PatchRelayService {
|
|
|
134
118
|
async getIssueReport(issueKey) {
|
|
135
119
|
return await this.queryService.getIssueReport(issueKey);
|
|
136
120
|
}
|
|
137
|
-
async
|
|
138
|
-
return await this.queryService.
|
|
121
|
+
async getRunEvents(issueKey, runId) {
|
|
122
|
+
return await this.queryService.getRunEvents(issueKey, runId);
|
|
139
123
|
}
|
|
140
|
-
async
|
|
141
|
-
return await this.
|
|
124
|
+
async getActiveRunStatus(issueKey) {
|
|
125
|
+
return await this.orchestrator.getActiveRunStatus(issueKey);
|
|
142
126
|
}
|
|
143
127
|
createPublicAgentSessionStatusLink(issueKey, options) {
|
|
144
|
-
if (!this.config.server.publicBaseUrl)
|
|
128
|
+
if (!this.config.server.publicBaseUrl)
|
|
145
129
|
return undefined;
|
|
146
|
-
}
|
|
147
130
|
const signingSecret = deriveSessionStatusSigningSecret(this.config.linear.tokenEncryptionKey);
|
|
148
131
|
const token = createSessionStatusToken({
|
|
149
132
|
issueKey,
|
|
@@ -152,11 +135,7 @@ export class PatchRelayService {
|
|
|
152
135
|
...(options?.ttlSeconds !== undefined ? { ttlSeconds: options.ttlSeconds } : {}),
|
|
153
136
|
});
|
|
154
137
|
return {
|
|
155
|
-
url: buildSessionStatusUrl({
|
|
156
|
-
publicBaseUrl: this.config.server.publicBaseUrl,
|
|
157
|
-
issueKey,
|
|
158
|
-
token: token.token,
|
|
159
|
-
}),
|
|
138
|
+
url: buildSessionStatusUrl({ publicBaseUrl: this.config.server.publicBaseUrl, issueKey, token: token.token }),
|
|
160
139
|
issueKey: token.issueKey,
|
|
161
140
|
expiresAt: token.expiresAt,
|
|
162
141
|
};
|
|
@@ -168,9 +147,8 @@ export class PatchRelayService {
|
|
|
168
147
|
return { status: "invalid_token" };
|
|
169
148
|
}
|
|
170
149
|
const sessionStatus = await this.queryService.getPublicAgentSessionStatus(params.issueKey);
|
|
171
|
-
if (!sessionStatus)
|
|
150
|
+
if (!sessionStatus)
|
|
172
151
|
return { status: "issue_not_found" };
|
|
173
|
-
}
|
|
174
152
|
return {
|
|
175
153
|
status: "ok",
|
|
176
154
|
issueKey: params.issueKey,
|