@holon-run/agentinbox 1.0.0-beta.1 → 1.0.0-beta.3
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/src/cli.js +5 -2
- package/dist/src/current_agent.js +22 -4
- package/dist/src/service.js +60 -4
- package/dist/src/source_schema.js +2 -0
- package/dist/src/sources/github.js +129 -8
- package/dist/src/sources/github_ci.js +16 -0
- package/dist/src/sources/remote.js +1 -1
- package/dist/src/sources/remote_modules.js +163 -157
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -871,7 +871,7 @@ async function printCurrentAgent(client) {
|
|
|
871
871
|
const records = await listAgentsWithTargets(client);
|
|
872
872
|
const current = (0, current_agent_1.resolveCurrentAgent)(records, context);
|
|
873
873
|
if (!current) {
|
|
874
|
-
throw new Error(
|
|
874
|
+
throw new Error(noCurrentAgentMessage());
|
|
875
875
|
}
|
|
876
876
|
console.log((0, util_1.jsonResponse)(current));
|
|
877
877
|
}
|
|
@@ -907,7 +907,7 @@ async function selectAgentForCommand(client, options) {
|
|
|
907
907
|
};
|
|
908
908
|
}
|
|
909
909
|
if (!options.autoRegister) {
|
|
910
|
-
throw new Error(
|
|
910
|
+
throw new Error(noCurrentAgentMessage());
|
|
911
911
|
}
|
|
912
912
|
await requestRemote(client, "/agents", {
|
|
913
913
|
backend: contextForCurrent.backend,
|
|
@@ -946,6 +946,9 @@ async function requestRemote(client, endpoint, body, method = "POST") {
|
|
|
946
946
|
}
|
|
947
947
|
return { data: response.data };
|
|
948
948
|
}
|
|
949
|
+
function noCurrentAgentMessage() {
|
|
950
|
+
return "no current agent is registered for this terminal/runtime context; run `agentinbox agent list` to inspect offline agents, then `agentinbox agent register` or `agentinbox agent register --force-rebind --agent-id <agentId>` to rebind one";
|
|
951
|
+
}
|
|
949
952
|
function takeFlagValue(args, flag) {
|
|
950
953
|
const index = args.indexOf(flag);
|
|
951
954
|
if (index === -1) {
|
|
@@ -39,6 +39,9 @@ function resolveCurrentAgent(agents, context) {
|
|
|
39
39
|
matchesCurrentTerminal: matchesCurrentTerminal(agent, context),
|
|
40
40
|
matchesCurrentRuntime: matchesCurrentRuntime(agent, context),
|
|
41
41
|
terminalIdentity: terminalIdentityForAgent(agent),
|
|
42
|
+
notifyCapable: notifyCapableForAgent(agent),
|
|
43
|
+
activeTargetCount: activeActivationTargetCount(agent),
|
|
44
|
+
offlineTargetCount: offlineActivationTargetCount(agent),
|
|
42
45
|
};
|
|
43
46
|
}
|
|
44
47
|
function annotateAgents(agents, context) {
|
|
@@ -52,24 +55,27 @@ function annotateAgents(agents, context) {
|
|
|
52
55
|
matchesCurrentRuntime: context ? matchesCurrentRuntime(entry, context) : false,
|
|
53
56
|
terminalIdentity: terminalIdentityForAgent(entry),
|
|
54
57
|
isCurrent: current?.agentId === entry.agent.agentId,
|
|
58
|
+
notifyCapable: notifyCapableForAgent(entry),
|
|
59
|
+
activeTargetCount: activeActivationTargetCount(entry),
|
|
60
|
+
offlineTargetCount: offlineActivationTargetCount(entry),
|
|
55
61
|
})),
|
|
56
62
|
};
|
|
57
63
|
}
|
|
58
64
|
function resolveAgentRecord(agents, context) {
|
|
59
65
|
if (context.tmuxPaneId) {
|
|
60
|
-
const match = agents.find((agent) =>
|
|
66
|
+
const match = agents.find((agent) => terminalTargetsForCurrentMatch(agent).some((target) => target.backend === "tmux" && target.tmuxPaneId === context.tmuxPaneId));
|
|
61
67
|
if (match) {
|
|
62
68
|
return match;
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
if (context.itermSessionId) {
|
|
66
|
-
const match = agents.find((agent) =>
|
|
72
|
+
const match = agents.find((agent) => terminalTargetsForCurrentMatch(agent).some((target) => target.backend === "iterm2" && target.itermSessionId === context.itermSessionId));
|
|
67
73
|
if (match) {
|
|
68
74
|
return match;
|
|
69
75
|
}
|
|
70
76
|
}
|
|
71
77
|
if (context.tty) {
|
|
72
|
-
const match = agents.find((agent) =>
|
|
78
|
+
const match = agents.find((agent) => terminalTargetsForCurrentMatch(agent).some((target) => target.tty === context.tty));
|
|
73
79
|
if (match) {
|
|
74
80
|
return match;
|
|
75
81
|
}
|
|
@@ -94,6 +100,18 @@ function activeTerminalTargets(agent) {
|
|
|
94
100
|
}
|
|
95
101
|
return terminalTargets(agent).filter((target) => target.status === "active");
|
|
96
102
|
}
|
|
103
|
+
function terminalTargetsForCurrentMatch(agent) {
|
|
104
|
+
return activeTerminalTargets(agent);
|
|
105
|
+
}
|
|
106
|
+
function activeActivationTargetCount(agent) {
|
|
107
|
+
return agent.activationTargets.filter((target) => target.status === "active").length;
|
|
108
|
+
}
|
|
109
|
+
function offlineActivationTargetCount(agent) {
|
|
110
|
+
return agent.activationTargets.filter((target) => target.status === "offline").length;
|
|
111
|
+
}
|
|
112
|
+
function notifyCapableForAgent(agent) {
|
|
113
|
+
return agent.agent.status === "active" && activeActivationTargetCount(agent) > 0;
|
|
114
|
+
}
|
|
97
115
|
function terminalIdentityForAgent(agent) {
|
|
98
116
|
const target = activeTerminalTargets(agent)[0];
|
|
99
117
|
if (!target) {
|
|
@@ -111,7 +129,7 @@ function terminalIdentityForAgent(agent) {
|
|
|
111
129
|
return null;
|
|
112
130
|
}
|
|
113
131
|
function matchesCurrentTerminal(agent, context) {
|
|
114
|
-
return
|
|
132
|
+
return terminalTargetsForCurrentMatch(agent).some((target) => (context.tmuxPaneId != null && target.tmuxPaneId === context.tmuxPaneId)
|
|
115
133
|
|| (context.itermSessionId != null && target.itermSessionId === context.itermSessionId)
|
|
116
134
|
|| (context.tty != null && target.tty === context.tty));
|
|
117
135
|
}
|
package/dist/src/service.js
CHANGED
|
@@ -505,7 +505,7 @@ class AgentInboxService {
|
|
|
505
505
|
return { removed: true };
|
|
506
506
|
}
|
|
507
507
|
registerTimer(input) {
|
|
508
|
-
this.
|
|
508
|
+
this.assertAgentNotifyCapable(input.agentId, "timer add");
|
|
509
509
|
const normalized = normalizeTimerInput(input);
|
|
510
510
|
const timezone = normalized.timezone ?? detectHostTimezone();
|
|
511
511
|
assertValidTimeZone(timezone);
|
|
@@ -794,7 +794,7 @@ class AgentInboxService {
|
|
|
794
794
|
return sibling;
|
|
795
795
|
}
|
|
796
796
|
async registerSingleSubscription(input) {
|
|
797
|
-
this.
|
|
797
|
+
this.assertAgentNotifyCapable(input.agentId, "subscription add");
|
|
798
798
|
const source = this.store.getSource(input.sourceId);
|
|
799
799
|
if (!source) {
|
|
800
800
|
throw new Error(`unknown source: ${input.sourceId}`);
|
|
@@ -1595,6 +1595,19 @@ class AgentInboxService {
|
|
|
1595
1595
|
watcher.onItems(items);
|
|
1596
1596
|
}
|
|
1597
1597
|
}
|
|
1598
|
+
assertAgentNotifyCapable(agentId, operation) {
|
|
1599
|
+
this.getAgent(agentId);
|
|
1600
|
+
const targets = this.store.listActivationTargetsForAgent(agentId);
|
|
1601
|
+
const activeTargetCount = targets.filter((target) => target.status === "active").length;
|
|
1602
|
+
if (activeTargetCount > 0) {
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
const offlineTargetCount = targets.filter((target) => target.status === "offline").length;
|
|
1606
|
+
const targetSummary = targets.length === 0
|
|
1607
|
+
? "it has no activation targets"
|
|
1608
|
+
: `all ${offlineTargetCount} activation target(s) are offline`;
|
|
1609
|
+
throw new Error(`agent ${agentId} is not notify-capable: ${targetSummary}; refusing ${operation} to avoid a silent notification black hole. Rebind with \`agentinbox agent register --force-rebind --agent-id ${agentId}\` or resume/add a target first.`);
|
|
1610
|
+
}
|
|
1598
1611
|
resolveAutoAgentId(input) {
|
|
1599
1612
|
for (let attempt = 0; attempt < 32; attempt += 1) {
|
|
1600
1613
|
const candidate = (0, terminal_1.assignedAgentIdFromContext)({
|
|
@@ -1784,6 +1797,11 @@ class AgentInboxService {
|
|
|
1784
1797
|
buffer.inFlight = true;
|
|
1785
1798
|
const entries = buffer.pending.splice(0, buffer.pending.length);
|
|
1786
1799
|
try {
|
|
1800
|
+
if (!this.activationTargetExists(buffer.agentId, buffer.targetId)) {
|
|
1801
|
+
this.notificationBuffers.delete(key);
|
|
1802
|
+
buffer.inFlight = false;
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1787
1805
|
const state = this.store.getActivationDispatchState(buffer.agentId, buffer.targetId);
|
|
1788
1806
|
const shouldAttemptDispatch = !state || (state.status === "dirty" && state.leaseExpiresAt == null);
|
|
1789
1807
|
if (shouldAttemptDispatch) {
|
|
@@ -1921,7 +1939,11 @@ class AgentInboxService {
|
|
|
1921
1939
|
if (!state) {
|
|
1922
1940
|
return;
|
|
1923
1941
|
}
|
|
1924
|
-
const target = this.
|
|
1942
|
+
const target = this.getActivationTargetIfExists(agentId, targetId);
|
|
1943
|
+
if (!target) {
|
|
1944
|
+
this.store.deleteActivationDispatchState(agentId, targetId);
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1925
1947
|
const inbox = this.ensureInboxForAgent(agentId);
|
|
1926
1948
|
const unacked = this.store.listInboxEntries(inbox.inboxId, { includeAcked: false }).length;
|
|
1927
1949
|
if (unacked === 0) {
|
|
@@ -1988,7 +2010,10 @@ class AgentInboxService {
|
|
|
1988
2010
|
}
|
|
1989
2011
|
}
|
|
1990
2012
|
async dispatchActivationTarget(input) {
|
|
1991
|
-
const target = this.
|
|
2013
|
+
const target = this.getActivationTargetIfExists(input.agentId, input.targetId);
|
|
2014
|
+
if (!target) {
|
|
2015
|
+
return "offline";
|
|
2016
|
+
}
|
|
1992
2017
|
if (target.status === "offline") {
|
|
1993
2018
|
return "offline";
|
|
1994
2019
|
}
|
|
@@ -2029,6 +2054,9 @@ class AgentInboxService {
|
|
|
2029
2054
|
try {
|
|
2030
2055
|
if (target.kind === "terminal") {
|
|
2031
2056
|
const gate = await this.activationGate.evaluate(target);
|
|
2057
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2058
|
+
return "offline";
|
|
2059
|
+
}
|
|
2032
2060
|
this.logger.debug("activation.gate_decision", {
|
|
2033
2061
|
agentId: input.agentId,
|
|
2034
2062
|
targetId: target.targetId,
|
|
@@ -2049,6 +2077,9 @@ class AgentInboxService {
|
|
|
2049
2077
|
return "offline";
|
|
2050
2078
|
}
|
|
2051
2079
|
const reminder = await this.buildTerminalReminder(target, inbox.inboxId, totalUnackedCount, input.summary, input.entries);
|
|
2080
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2081
|
+
return "offline";
|
|
2082
|
+
}
|
|
2052
2083
|
const prompt = reminder.prompt;
|
|
2053
2084
|
const promptFingerprint = reminder.fingerprint;
|
|
2054
2085
|
terminalPrompt = prompt;
|
|
@@ -2134,6 +2165,9 @@ class AgentInboxService {
|
|
|
2134
2165
|
subscriptionIds: input.subscriptionIds,
|
|
2135
2166
|
});
|
|
2136
2167
|
await this.terminalDispatcher.dispatch(target, prompt);
|
|
2168
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2169
|
+
return "offline";
|
|
2170
|
+
}
|
|
2137
2171
|
this.logger.debug("activation.terminal_dispatch_succeeded", {
|
|
2138
2172
|
agentId: input.agentId,
|
|
2139
2173
|
targetId: target.targetId,
|
|
@@ -2162,6 +2196,9 @@ class AgentInboxService {
|
|
|
2162
2196
|
deliveredAt: null,
|
|
2163
2197
|
};
|
|
2164
2198
|
await this.activationDispatcher.dispatch(target.url, activation);
|
|
2199
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2200
|
+
return "offline";
|
|
2201
|
+
}
|
|
2165
2202
|
this.store.insertActivation(activation);
|
|
2166
2203
|
}
|
|
2167
2204
|
this.markActivationTargetDelivered(target.targetId);
|
|
@@ -2196,8 +2233,14 @@ class AgentInboxService {
|
|
|
2196
2233
|
error,
|
|
2197
2234
|
message,
|
|
2198
2235
|
});
|
|
2236
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2237
|
+
return "offline";
|
|
2238
|
+
}
|
|
2199
2239
|
if (target.kind === "terminal") {
|
|
2200
2240
|
const exists = await this.terminalDispatcher.probe(target);
|
|
2241
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2242
|
+
return "offline";
|
|
2243
|
+
}
|
|
2201
2244
|
this.logger.debug("activation.terminal_probe_after_failure", {
|
|
2202
2245
|
agentId: input.agentId,
|
|
2203
2246
|
targetId: target.targetId,
|
|
@@ -2219,6 +2262,19 @@ class AgentInboxService {
|
|
|
2219
2262
|
return "retryable_failure";
|
|
2220
2263
|
}
|
|
2221
2264
|
}
|
|
2265
|
+
getActivationTargetIfExists(agentId, targetId) {
|
|
2266
|
+
if (!this.store.getAgent(agentId)) {
|
|
2267
|
+
return null;
|
|
2268
|
+
}
|
|
2269
|
+
const target = this.store.getActivationTarget(targetId);
|
|
2270
|
+
if (!target || target.agentId !== agentId) {
|
|
2271
|
+
return null;
|
|
2272
|
+
}
|
|
2273
|
+
return target;
|
|
2274
|
+
}
|
|
2275
|
+
activationTargetExists(agentId, targetId) {
|
|
2276
|
+
return this.getActivationTargetIfExists(agentId, targetId) != null;
|
|
2277
|
+
}
|
|
2222
2278
|
upsertDirtyDispatchState(agentId, targetId, entries) {
|
|
2223
2279
|
this.store.upsertActivationDispatchState({
|
|
2224
2280
|
agentId,
|
|
@@ -89,6 +89,7 @@ const SOURCE_SCHEMAS = {
|
|
|
89
89
|
{ name: "event", type: "string|null", description: "GitHub trigger event for the workflow run." },
|
|
90
90
|
{ name: "headBranch", type: "string|null", description: "Head branch for the workflow run." },
|
|
91
91
|
{ name: "headSha", type: "string|null", description: "Head commit SHA for the workflow run." },
|
|
92
|
+
{ name: "pullRequestNumbers", type: "number[]", description: "Pull request numbers associated with the workflow run." },
|
|
92
93
|
{ name: "actor", type: "string|null", description: "Actor login for the workflow run." },
|
|
93
94
|
{ name: "commitMessage", type: "string|null", description: "Head commit message when present." },
|
|
94
95
|
{ name: "htmlUrl", type: "string|null", description: "GitHub Actions run URL." },
|
|
@@ -101,6 +102,7 @@ const SOURCE_SCHEMAS = {
|
|
|
101
102
|
conclusion: "failure",
|
|
102
103
|
event: "pull_request",
|
|
103
104
|
head_branch: "main",
|
|
105
|
+
pull_requests: [{ number: 93 }],
|
|
104
106
|
actor: "jolestar",
|
|
105
107
|
head_commit: { message: "fix ci" },
|
|
106
108
|
},
|
|
@@ -4,6 +4,7 @@ exports.GithubDeliveryAdapter = exports.GithubUxcClient = exports.DEFAULT_GITHUB
|
|
|
4
4
|
exports.githubDeliveryOperationsForHandle = githubDeliveryOperationsForHandle;
|
|
5
5
|
exports.invokeGithubDeliveryOperation = invokeGithubDeliveryOperation;
|
|
6
6
|
exports.normalizeGithubRepoEvent = normalizeGithubRepoEvent;
|
|
7
|
+
exports.hydrateGithubRepoEventIfNeeded = hydrateGithubRepoEventIfNeeded;
|
|
7
8
|
exports.canonicalGithubPullRequestRef = canonicalGithubPullRequestRef;
|
|
8
9
|
exports.deriveGithubTrackedResource = deriveGithubTrackedResource;
|
|
9
10
|
exports.githubSubscriptionShortcutSpec = githubSubscriptionShortcutSpec;
|
|
@@ -12,6 +13,14 @@ exports.projectGithubLifecycleSignal = projectGithubLifecycleSignal;
|
|
|
12
13
|
exports.parseGithubSourceConfig = parseGithubSourceConfig;
|
|
13
14
|
const uxc_daemon_client_1 = require("@holon-run/uxc-daemon-client");
|
|
14
15
|
exports.GITHUB_ENDPOINT = "https://api.github.com";
|
|
16
|
+
const DEFAULT_GITHUB_CALL_CLIENT = new uxc_daemon_client_1.UxcDaemonClient({ env: process.env });
|
|
17
|
+
const GITHUB_EVENT_HYDRATION_MIN_PER_PAGE = 10;
|
|
18
|
+
const GITHUB_EVENT_HYDRATION_MAX_PER_PAGE = 30;
|
|
19
|
+
const GITHUB_EVENT_HYDRATION_MAX_PAGES = 3;
|
|
20
|
+
const HYDRATABLE_GITHUB_REPO_EVENT_TYPES = new Set([
|
|
21
|
+
"PullRequestReviewEvent",
|
|
22
|
+
"PullRequestReviewCommentEvent",
|
|
23
|
+
]);
|
|
15
24
|
exports.DEFAULT_GITHUB_EVENT_TYPES = [
|
|
16
25
|
"IssuesEvent",
|
|
17
26
|
"IssueCommentEvent",
|
|
@@ -21,7 +30,7 @@ exports.DEFAULT_GITHUB_EVENT_TYPES = [
|
|
|
21
30
|
];
|
|
22
31
|
class GithubUxcClient {
|
|
23
32
|
client;
|
|
24
|
-
constructor(client =
|
|
33
|
+
constructor(client = DEFAULT_GITHUB_CALL_CLIENT) {
|
|
25
34
|
this.client = client;
|
|
26
35
|
}
|
|
27
36
|
async createIssueComment(input) {
|
|
@@ -222,7 +231,7 @@ function normalizeGithubRepoEvent(source, config, raw) {
|
|
|
222
231
|
if (!eventType || !(config.eventTypes ?? exports.DEFAULT_GITHUB_EVENT_TYPES).includes(eventType)) {
|
|
223
232
|
return null;
|
|
224
233
|
}
|
|
225
|
-
const eventId =
|
|
234
|
+
const eventId = asIdString(event.id);
|
|
226
235
|
if (!eventId) {
|
|
227
236
|
return null;
|
|
228
237
|
}
|
|
@@ -234,18 +243,17 @@ function normalizeGithubRepoEvent(source, config, raw) {
|
|
|
234
243
|
const review = asRecord(payload.review);
|
|
235
244
|
const comment = asRecord(payload.comment);
|
|
236
245
|
const action = asString(payload.action) ?? "observed";
|
|
237
|
-
const
|
|
246
|
+
const url = extractGithubRepoEventUrl(event, issue, pullRequest, review, comment);
|
|
247
|
+
const number = asNumber(issue.number)
|
|
248
|
+
?? asNumber(pullRequest.number)
|
|
249
|
+
?? asNumber(payload.number)
|
|
250
|
+
?? extractGithubNumberFromUrl(url);
|
|
238
251
|
const isPullRequest = eventType.startsWith("PullRequest");
|
|
239
252
|
const title = asString(issue.title) ?? asString(pullRequest.title) ?? null;
|
|
240
253
|
const reviewState = asString(review.state) ?? null;
|
|
241
254
|
const body = asString(comment.body) ?? asString(review.body) ?? asString(issue.body) ?? asString(pullRequest.body) ?? null;
|
|
242
255
|
const labels = extractLabels(issue.labels);
|
|
243
256
|
const mentions = extractMentions(title, body);
|
|
244
|
-
const url = asString(comment.html_url) ??
|
|
245
|
-
asString(review.html_url) ??
|
|
246
|
-
asString(issue.html_url) ??
|
|
247
|
-
asString(pullRequest.html_url) ??
|
|
248
|
-
asString(event.url);
|
|
249
257
|
const deliveryHandle = buildGithubDeliveryHandle(config, eventType, number, comment);
|
|
250
258
|
return {
|
|
251
259
|
sourceId: source.sourceId,
|
|
@@ -283,6 +291,47 @@ function normalizeGithubRepoEvent(source, config, raw) {
|
|
|
283
291
|
deliveryHandle,
|
|
284
292
|
};
|
|
285
293
|
}
|
|
294
|
+
async function hydrateGithubRepoEventIfNeeded(config, raw, client = DEFAULT_GITHUB_CALL_CLIENT) {
|
|
295
|
+
if (!shouldHydrateGithubRepoEvent(raw)) {
|
|
296
|
+
return raw;
|
|
297
|
+
}
|
|
298
|
+
const eventId = asIdString(raw.id);
|
|
299
|
+
if (!eventId) {
|
|
300
|
+
return raw;
|
|
301
|
+
}
|
|
302
|
+
const perPage = computeGithubHydrationPerPage(config.perPage);
|
|
303
|
+
for (let page = 1; page <= GITHUB_EVENT_HYDRATION_MAX_PAGES; page += 1) {
|
|
304
|
+
let response;
|
|
305
|
+
try {
|
|
306
|
+
response = await client.call({
|
|
307
|
+
endpoint: exports.GITHUB_ENDPOINT,
|
|
308
|
+
operation: "get:/repos/{owner}/{repo}/events",
|
|
309
|
+
payload: {
|
|
310
|
+
owner: config.owner,
|
|
311
|
+
repo: config.repo,
|
|
312
|
+
per_page: perPage,
|
|
313
|
+
page,
|
|
314
|
+
},
|
|
315
|
+
options: { auth: config.uxcAuth },
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return raw;
|
|
320
|
+
}
|
|
321
|
+
const candidates = Array.isArray(response.data) ? response.data : [];
|
|
322
|
+
if (candidates.length === 0) {
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
for (const candidateValue of candidates) {
|
|
326
|
+
const candidate = asRecord(candidateValue);
|
|
327
|
+
if (asIdString(candidate.id) !== eventId) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
return hasGithubRepoEventMetadata(candidate) ? candidate : raw;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return raw;
|
|
334
|
+
}
|
|
286
335
|
function canonicalGithubPullRequestRef(source, number) {
|
|
287
336
|
const config = parseGithubSourceConfig(source);
|
|
288
337
|
return `repo:${config.owner}/${config.repo}:pr:${number}`;
|
|
@@ -333,6 +382,11 @@ function expandGithubSubscriptionShortcut(input) {
|
|
|
333
382
|
...(withCi
|
|
334
383
|
? [{
|
|
335
384
|
streamKind: "ci_runs",
|
|
385
|
+
filter: {
|
|
386
|
+
metadata: {
|
|
387
|
+
pullRequestNumbers: [number],
|
|
388
|
+
},
|
|
389
|
+
},
|
|
336
390
|
trackedResourceRef,
|
|
337
391
|
cleanupPolicy: { mode: "on_terminal" },
|
|
338
392
|
}]
|
|
@@ -433,6 +487,64 @@ function extractMentions(...values) {
|
|
|
433
487
|
}
|
|
434
488
|
return Array.from(seen.values()).sort();
|
|
435
489
|
}
|
|
490
|
+
function computeGithubHydrationPerPage(perPage) {
|
|
491
|
+
const configured = Number.isInteger(perPage) && perPage && perPage > 0
|
|
492
|
+
? perPage
|
|
493
|
+
: GITHUB_EVENT_HYDRATION_MIN_PER_PAGE;
|
|
494
|
+
return Math.min(GITHUB_EVENT_HYDRATION_MAX_PER_PAGE, Math.max(GITHUB_EVENT_HYDRATION_MIN_PER_PAGE, configured));
|
|
495
|
+
}
|
|
496
|
+
function extractGithubRepoEventUrl(event, issue, pullRequest, review, comment) {
|
|
497
|
+
return (asString(comment.html_url)
|
|
498
|
+
?? asString(review.html_url)
|
|
499
|
+
?? asString(issue.html_url)
|
|
500
|
+
?? asString(pullRequest.html_url)
|
|
501
|
+
?? asString(comment.pull_request_url)
|
|
502
|
+
?? asString(review.pull_request_url)
|
|
503
|
+
?? asString(event.url));
|
|
504
|
+
}
|
|
505
|
+
function extractGithubNumberFromUrl(url) {
|
|
506
|
+
if (!url) {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
const match = url.match(/\/(?:pull|pulls|issues)\/(\d+)(?:$|[/?#])/);
|
|
510
|
+
return match ? Number(match[1]) : null;
|
|
511
|
+
}
|
|
512
|
+
function shouldHydrateGithubRepoEvent(raw) {
|
|
513
|
+
const eventType = asString(raw.type);
|
|
514
|
+
if (!eventType || !HYDRATABLE_GITHUB_REPO_EVENT_TYPES.has(eventType)) {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
const payload = asRecord(raw.payload);
|
|
518
|
+
const issue = asRecord(payload.issue);
|
|
519
|
+
const pullRequest = asRecord(payload.pull_request);
|
|
520
|
+
const review = asRecord(payload.review);
|
|
521
|
+
const comment = asRecord(payload.comment);
|
|
522
|
+
const url = extractGithubRepoEventUrl(raw, issue, pullRequest, review, comment);
|
|
523
|
+
const number = asNumber(issue.number)
|
|
524
|
+
?? asNumber(pullRequest.number)
|
|
525
|
+
?? asNumber(payload.number)
|
|
526
|
+
?? extractGithubNumberFromUrl(url);
|
|
527
|
+
if (number) {
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
return [pullRequest, review, comment].some(isUxcPreviewTruncatedObject);
|
|
531
|
+
}
|
|
532
|
+
function hasGithubRepoEventMetadata(raw) {
|
|
533
|
+
const payload = asRecord(raw.payload);
|
|
534
|
+
const issue = asRecord(payload.issue);
|
|
535
|
+
const pullRequest = asRecord(payload.pull_request);
|
|
536
|
+
const review = asRecord(payload.review);
|
|
537
|
+
const comment = asRecord(payload.comment);
|
|
538
|
+
return Boolean(asNumber(issue.number)
|
|
539
|
+
?? asNumber(pullRequest.number)
|
|
540
|
+
?? asNumber(payload.number)
|
|
541
|
+
?? extractGithubNumberFromUrl(extractGithubRepoEventUrl(raw, issue, pullRequest, review, comment))
|
|
542
|
+
?? asString(review.state)
|
|
543
|
+
?? extractGithubRepoEventUrl(raw, issue, pullRequest, review, comment));
|
|
544
|
+
}
|
|
545
|
+
function isUxcPreviewTruncatedObject(value) {
|
|
546
|
+
return value._uxc_preview_truncated === true;
|
|
547
|
+
}
|
|
436
548
|
function parseTargetRef(targetRef) {
|
|
437
549
|
const match = targetRef.match(/^([^/]+)\/([^#]+)#(\d+)$/);
|
|
438
550
|
if (!match) {
|
|
@@ -495,6 +607,15 @@ function asRecord(value) {
|
|
|
495
607
|
function asString(value) {
|
|
496
608
|
return typeof value === "string" ? value : null;
|
|
497
609
|
}
|
|
610
|
+
function asIdString(value) {
|
|
611
|
+
if (typeof value === "string") {
|
|
612
|
+
return value;
|
|
613
|
+
}
|
|
614
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
615
|
+
return String(value);
|
|
616
|
+
}
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
498
619
|
function asNumber(value) {
|
|
499
620
|
return typeof value === "number" ? value : null;
|
|
500
621
|
}
|
|
@@ -240,6 +240,7 @@ function normalizeGithubWorkflowRunEvent(source, config, raw) {
|
|
|
240
240
|
const variant = buildWorkflowRunVariant(workflowName, status, conclusion);
|
|
241
241
|
const actor = asRecord(run.actor);
|
|
242
242
|
const headCommit = asRecord(run.head_commit);
|
|
243
|
+
const pullRequestNumbers = extractPullRequestNumbers(run.pull_requests);
|
|
243
244
|
return {
|
|
244
245
|
sourceId: source.sourceId,
|
|
245
246
|
sourceNativeId: `workflow_run:${runId}`,
|
|
@@ -259,6 +260,7 @@ function normalizeGithubWorkflowRunEvent(source, config, raw) {
|
|
|
259
260
|
event: asString(run.event),
|
|
260
261
|
headSha: asString(run.head_sha),
|
|
261
262
|
headBranch: asString(run.head_branch),
|
|
263
|
+
pullRequestNumbers,
|
|
262
264
|
runNumber: asNumber(run.run_number),
|
|
263
265
|
runAttempt: asNumber(run.run_attempt),
|
|
264
266
|
actor: asString(actor.login),
|
|
@@ -277,6 +279,7 @@ function normalizeGithubWorkflowRunEvent(source, config, raw) {
|
|
|
277
279
|
event: asString(run.event),
|
|
278
280
|
head_sha: asString(run.head_sha),
|
|
279
281
|
head_branch: asString(run.head_branch),
|
|
282
|
+
pull_requests: pullRequestNumbers.map((number) => ({ number })),
|
|
280
283
|
run_number: asNumber(run.run_number),
|
|
281
284
|
run_attempt: asNumber(run.run_attempt),
|
|
282
285
|
html_url: asString(run.html_url),
|
|
@@ -343,6 +346,19 @@ function asString(value) {
|
|
|
343
346
|
function asNumber(value) {
|
|
344
347
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
345
348
|
}
|
|
349
|
+
function extractPullRequestNumbers(value) {
|
|
350
|
+
if (!Array.isArray(value)) {
|
|
351
|
+
return [];
|
|
352
|
+
}
|
|
353
|
+
const numbers = new Set();
|
|
354
|
+
for (const entry of value) {
|
|
355
|
+
const number = asNumber(asRecord(entry).number);
|
|
356
|
+
if (number && Number.isInteger(number) && number > 0) {
|
|
357
|
+
numbers.add(number);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return Array.from(numbers).sort((left, right) => left - right);
|
|
361
|
+
}
|
|
346
362
|
function inferWorkflowRunStatus(run, conclusion) {
|
|
347
363
|
return asString(run.status)
|
|
348
364
|
?? (conclusion ? "completed" : null)
|
|
@@ -300,7 +300,7 @@ class RemoteSourceRuntime {
|
|
|
300
300
|
if (Object.keys(rawPayload).length === 0) {
|
|
301
301
|
continue;
|
|
302
302
|
}
|
|
303
|
-
const mapped = module.mapRawEvent(rawPayload, moduleSource);
|
|
303
|
+
const mapped = await module.mapRawEvent(rawPayload, moduleSource);
|
|
304
304
|
if (!mapped) {
|
|
305
305
|
continue;
|
|
306
306
|
}
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.RemoteSourceModuleRegistry = void 0;
|
|
7
7
|
exports.builtInModuleIdForSourceType = builtInModuleIdForSourceType;
|
|
8
|
+
exports.createGithubRepoRemoteModule = createGithubRepoRemoteModule;
|
|
8
9
|
exports.moduleConfigForSource = moduleConfigForSource;
|
|
9
10
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -133,169 +134,173 @@ function asNonEmptyString(value) {
|
|
|
133
134
|
const trimmed = value.trim();
|
|
134
135
|
return trimmed.length > 0 ? trimmed : null;
|
|
135
136
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
sourceKind: "github_repo",
|
|
148
|
-
aliases: ["github_repo"],
|
|
149
|
-
configSchema: [
|
|
150
|
-
{ name: "owner", type: "string", required: true, description: "GitHub repository owner." },
|
|
151
|
-
{ name: "repo", type: "string", required: true, description: "GitHub repository name." },
|
|
152
|
-
{ name: "uxcAuth", type: "string", required: false, description: "Optional uxc auth profile." },
|
|
153
|
-
{ name: "eventTypes", type: "string[]", required: false, description: "Optional GitHub event type allowlist." },
|
|
154
|
-
{ name: "perPage", type: "number", required: false, description: `Repository events requested per poll. Default ${config.perPage ?? 10}.` },
|
|
155
|
-
{ name: "pollIntervalSecs", type: "number", required: false, description: `Polling interval in seconds. Default ${config.pollIntervalSecs ?? 30}.` },
|
|
156
|
-
],
|
|
157
|
-
metadataFields: [
|
|
158
|
-
{ name: "eventType", type: "string", description: "GitHub event type such as IssueCommentEvent." },
|
|
159
|
-
{ name: "action", type: "string", description: "GitHub event action suffix such as created." },
|
|
160
|
-
{ name: "author", type: "string|null", description: "Actor login for the event." },
|
|
161
|
-
{ name: "isPullRequest", type: "boolean", description: "Whether the event targets a pull request surface." },
|
|
162
|
-
{ name: "reviewState", type: "string|null", description: "Review decision state for PullRequestReviewEvent such as approved or changes_requested." },
|
|
163
|
-
{ name: "labels", type: "string[]", description: "Labels extracted from the issue or pull request." },
|
|
164
|
-
{ name: "mentions", type: "string[]", description: "Mention handles extracted from title/body/comment text." },
|
|
165
|
-
{ name: "number", type: "number|null", description: "Issue or pull request number when present." },
|
|
166
|
-
{ name: "repoFullName", type: "string", description: "Repository full name in owner/repo form." },
|
|
167
|
-
{ name: "title", type: "string|null", description: "Issue, pull request, or comment title." },
|
|
168
|
-
{ name: "body", type: "string|null", description: "Issue, pull request, or comment body text." },
|
|
169
|
-
{ name: "url", type: "string|null", description: "Primary GitHub HTML URL for the event target." },
|
|
170
|
-
],
|
|
171
|
-
payloadExamples: [
|
|
172
|
-
{
|
|
173
|
-
id: "1234567891",
|
|
174
|
-
type: "PullRequestReviewEvent",
|
|
175
|
-
action: "created",
|
|
176
|
-
actor: "Copilot",
|
|
177
|
-
pull_request: { number: 67, title: "feat: add remote module capability hooks" },
|
|
178
|
-
review: { state: "commented", body: "review summary" },
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
id: "1234567892",
|
|
182
|
-
type: "PullRequestEvent",
|
|
183
|
-
action: "closed",
|
|
184
|
-
actor: "jolestar",
|
|
185
|
-
pull_request: { number: 72, title: "feat: add cleanup policy lifecycle engine", merged: true },
|
|
186
|
-
},
|
|
187
|
-
],
|
|
188
|
-
eventVariantExamples: [
|
|
189
|
-
"IssueCommentEvent.created",
|
|
190
|
-
"PullRequestEvent.opened",
|
|
191
|
-
"PullRequestEvent.closed",
|
|
192
|
-
"PullRequestReviewEvent.created",
|
|
193
|
-
"PullRequestReviewCommentEvent.created",
|
|
194
|
-
],
|
|
195
|
-
};
|
|
196
|
-
},
|
|
197
|
-
listSubscriptionShortcuts() {
|
|
198
|
-
return (0, github_1.githubSubscriptionShortcutSpec)();
|
|
199
|
-
},
|
|
200
|
-
expandSubscriptionShortcut(input) {
|
|
201
|
-
return (0, github_1.expandGithubSubscriptionShortcut)(input);
|
|
202
|
-
},
|
|
203
|
-
deriveTrackedResource(filter, source) {
|
|
204
|
-
return (0, github_1.deriveGithubTrackedResource)(filter, source);
|
|
205
|
-
},
|
|
206
|
-
deriveNotificationGrouping(item) {
|
|
207
|
-
const number = typeof item.metadata.number === "number" ? item.metadata.number : null;
|
|
208
|
-
if (!number) {
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
const variant = item.eventVariant;
|
|
212
|
-
if (variant.startsWith("PullRequestReviewCommentEvent.")) {
|
|
213
|
-
return {
|
|
214
|
-
groupable: true,
|
|
215
|
-
resourceRef: `pr:${number}`,
|
|
216
|
-
eventFamily: "review_comments",
|
|
217
|
-
summaryHint: `review comments on PR #${number}`,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
if (variant.startsWith("PullRequestReviewEvent.")) {
|
|
137
|
+
function createGithubRepoRemoteModule(options) {
|
|
138
|
+
return {
|
|
139
|
+
id: "builtin.github_repo",
|
|
140
|
+
listDeliveryOperations(input) {
|
|
141
|
+
return (0, github_1.githubDeliveryOperationsForHandle)(input.handle);
|
|
142
|
+
},
|
|
143
|
+
async invokeDeliveryOperation(input) {
|
|
144
|
+
return (0, github_1.invokeGithubDeliveryOperation)(input.handle, input.operation, input.input);
|
|
145
|
+
},
|
|
146
|
+
describeCapabilities(source) {
|
|
147
|
+
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
221
148
|
return {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
149
|
+
sourceKind: "github_repo",
|
|
150
|
+
aliases: ["github_repo"],
|
|
151
|
+
configSchema: [
|
|
152
|
+
{ name: "owner", type: "string", required: true, description: "GitHub repository owner." },
|
|
153
|
+
{ name: "repo", type: "string", required: true, description: "GitHub repository name." },
|
|
154
|
+
{ name: "uxcAuth", type: "string", required: false, description: "Optional uxc auth profile." },
|
|
155
|
+
{ name: "eventTypes", type: "string[]", required: false, description: "Optional GitHub event type allowlist." },
|
|
156
|
+
{ name: "perPage", type: "number", required: false, description: `Repository events requested per poll. Default ${config.perPage ?? 10}.` },
|
|
157
|
+
{ name: "pollIntervalSecs", type: "number", required: false, description: `Polling interval in seconds. Default ${config.pollIntervalSecs ?? 30}.` },
|
|
158
|
+
],
|
|
159
|
+
metadataFields: [
|
|
160
|
+
{ name: "eventType", type: "string", description: "GitHub event type such as IssueCommentEvent." },
|
|
161
|
+
{ name: "action", type: "string", description: "GitHub event action suffix such as created." },
|
|
162
|
+
{ name: "author", type: "string|null", description: "Actor login for the event." },
|
|
163
|
+
{ name: "isPullRequest", type: "boolean", description: "Whether the event targets a pull request surface." },
|
|
164
|
+
{ name: "reviewState", type: "string|null", description: "Review decision state for PullRequestReviewEvent such as approved or changes_requested." },
|
|
165
|
+
{ name: "labels", type: "string[]", description: "Labels extracted from the issue or pull request." },
|
|
166
|
+
{ name: "mentions", type: "string[]", description: "Mention handles extracted from title/body/comment text." },
|
|
167
|
+
{ name: "number", type: "number|null", description: "Issue or pull request number when present." },
|
|
168
|
+
{ name: "repoFullName", type: "string", description: "Repository full name in owner/repo form." },
|
|
169
|
+
{ name: "title", type: "string|null", description: "Issue, pull request, or comment title." },
|
|
170
|
+
{ name: "body", type: "string|null", description: "Issue, pull request, or comment body text." },
|
|
171
|
+
{ name: "url", type: "string|null", description: "Primary GitHub HTML URL for the event target." },
|
|
172
|
+
],
|
|
173
|
+
payloadExamples: [
|
|
174
|
+
{
|
|
175
|
+
id: "1234567891",
|
|
176
|
+
type: "PullRequestReviewEvent",
|
|
177
|
+
action: "created",
|
|
178
|
+
actor: "Copilot",
|
|
179
|
+
pull_request: { number: 67, title: "feat: add remote module capability hooks" },
|
|
180
|
+
review: { state: "commented", body: "review summary" },
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: "1234567892",
|
|
184
|
+
type: "PullRequestEvent",
|
|
185
|
+
action: "closed",
|
|
186
|
+
actor: "jolestar",
|
|
187
|
+
pull_request: { number: 72, title: "feat: add cleanup policy lifecycle engine", merged: true },
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
eventVariantExamples: [
|
|
191
|
+
"IssueCommentEvent.created",
|
|
192
|
+
"PullRequestEvent.opened",
|
|
193
|
+
"PullRequestEvent.closed",
|
|
194
|
+
"PullRequestReviewEvent.created",
|
|
195
|
+
"PullRequestReviewCommentEvent.created",
|
|
196
|
+
],
|
|
226
197
|
};
|
|
227
|
-
}
|
|
228
|
-
|
|
198
|
+
},
|
|
199
|
+
listSubscriptionShortcuts() {
|
|
200
|
+
return (0, github_1.githubSubscriptionShortcutSpec)();
|
|
201
|
+
},
|
|
202
|
+
expandSubscriptionShortcut(input) {
|
|
203
|
+
return (0, github_1.expandGithubSubscriptionShortcut)(input);
|
|
204
|
+
},
|
|
205
|
+
deriveTrackedResource(filter, source) {
|
|
206
|
+
return (0, github_1.deriveGithubTrackedResource)(filter, source);
|
|
207
|
+
},
|
|
208
|
+
deriveNotificationGrouping(item) {
|
|
209
|
+
const number = typeof item.metadata.number === "number" ? item.metadata.number : null;
|
|
210
|
+
if (!number) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const variant = item.eventVariant;
|
|
214
|
+
if (variant.startsWith("PullRequestReviewCommentEvent.")) {
|
|
215
|
+
return {
|
|
216
|
+
groupable: true,
|
|
217
|
+
resourceRef: `pr:${number}`,
|
|
218
|
+
eventFamily: "review_comments",
|
|
219
|
+
summaryHint: `review comments on PR #${number}`,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (variant.startsWith("PullRequestReviewEvent.")) {
|
|
223
|
+
return {
|
|
224
|
+
groupable: true,
|
|
225
|
+
resourceRef: `pr:${number}`,
|
|
226
|
+
eventFamily: "reviews",
|
|
227
|
+
summaryHint: `reviews on PR #${number}`,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (variant.startsWith("IssueCommentEvent.")) {
|
|
231
|
+
return {
|
|
232
|
+
groupable: true,
|
|
233
|
+
resourceRef: item.metadata.isPullRequest === true ? `pr:${number}` : `issue:${number}`,
|
|
234
|
+
eventFamily: "comments",
|
|
235
|
+
summaryHint: item.metadata.isPullRequest === true ? `comments on PR #${number}` : `comments on issue #${number}`,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (variant.startsWith("PullRequestEvent.closed")) {
|
|
239
|
+
return {
|
|
240
|
+
groupable: true,
|
|
241
|
+
resourceRef: `pr:${number}`,
|
|
242
|
+
eventFamily: "lifecycle",
|
|
243
|
+
summaryHint: `PR #${number} lifecycle updates`,
|
|
244
|
+
flushClass: "immediate",
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
},
|
|
249
|
+
summarizeDigestThread(items, _source, grouping) {
|
|
250
|
+
if (!grouping.summaryHint || items.length === 0) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
const count = items.length;
|
|
254
|
+
return `${count} ${grouping.summaryHint}`;
|
|
255
|
+
},
|
|
256
|
+
projectLifecycleSignal(rawPayload, source) {
|
|
257
|
+
return (0, github_1.projectGithubLifecycleSignal)(rawPayload, source);
|
|
258
|
+
},
|
|
259
|
+
validateConfig(source) {
|
|
260
|
+
(0, github_1.parseGithubSourceConfig)(source);
|
|
261
|
+
},
|
|
262
|
+
buildManagedSourceSpec(source) {
|
|
263
|
+
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
229
264
|
return {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
265
|
+
endpoint: github_1.GITHUB_ENDPOINT,
|
|
266
|
+
operation_id: "get:/repos/{owner}/{repo}/events",
|
|
267
|
+
args: {
|
|
268
|
+
owner: config.owner,
|
|
269
|
+
repo: config.repo,
|
|
270
|
+
per_page: config.perPage ?? 10,
|
|
271
|
+
},
|
|
272
|
+
mode: "poll",
|
|
273
|
+
poll_config: {
|
|
274
|
+
interval_secs: config.pollIntervalSecs ?? 30,
|
|
275
|
+
extract_items_pointer: "",
|
|
276
|
+
checkpoint_strategy: {
|
|
277
|
+
type: "item_key",
|
|
278
|
+
item_key_pointer: "/id",
|
|
279
|
+
seen_window: 1024,
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
options: { auth: config.uxcAuth },
|
|
234
283
|
};
|
|
235
|
-
}
|
|
236
|
-
|
|
284
|
+
},
|
|
285
|
+
async mapRawEvent(rawPayload, source) {
|
|
286
|
+
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
287
|
+
const hydratedPayload = await (0, github_1.hydrateGithubRepoEventIfNeeded)(config, rawPayload, options?.callClient);
|
|
288
|
+
const normalized = (0, github_1.normalizeGithubRepoEvent)(source, config, hydratedPayload);
|
|
289
|
+
if (!normalized) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
237
292
|
return {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
293
|
+
sourceNativeId: normalized.sourceNativeId,
|
|
294
|
+
eventVariant: normalized.eventVariant,
|
|
295
|
+
metadata: normalized.metadata ?? {},
|
|
296
|
+
rawPayload: normalized.rawPayload ?? hydratedPayload,
|
|
297
|
+
occurredAt: normalized.occurredAt,
|
|
298
|
+
deliveryHandle: normalized.deliveryHandle,
|
|
243
299
|
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (!grouping.summaryHint || items.length === 0) {
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
const count = items.length;
|
|
252
|
-
return `${count} ${grouping.summaryHint}`;
|
|
253
|
-
},
|
|
254
|
-
projectLifecycleSignal(rawPayload, source) {
|
|
255
|
-
return (0, github_1.projectGithubLifecycleSignal)(rawPayload, source);
|
|
256
|
-
},
|
|
257
|
-
validateConfig(source) {
|
|
258
|
-
(0, github_1.parseGithubSourceConfig)(source);
|
|
259
|
-
},
|
|
260
|
-
buildManagedSourceSpec(source) {
|
|
261
|
-
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
262
|
-
return {
|
|
263
|
-
endpoint: github_1.GITHUB_ENDPOINT,
|
|
264
|
-
operation_id: "get:/repos/{owner}/{repo}/events",
|
|
265
|
-
args: {
|
|
266
|
-
owner: config.owner,
|
|
267
|
-
repo: config.repo,
|
|
268
|
-
per_page: config.perPage ?? 10,
|
|
269
|
-
},
|
|
270
|
-
mode: "poll",
|
|
271
|
-
poll_config: {
|
|
272
|
-
interval_secs: config.pollIntervalSecs ?? 30,
|
|
273
|
-
extract_items_pointer: "",
|
|
274
|
-
checkpoint_strategy: {
|
|
275
|
-
type: "item_key",
|
|
276
|
-
item_key_pointer: "/id",
|
|
277
|
-
seen_window: 1024,
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
options: { auth: config.uxcAuth },
|
|
281
|
-
};
|
|
282
|
-
},
|
|
283
|
-
mapRawEvent(rawPayload, source) {
|
|
284
|
-
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
285
|
-
const normalized = (0, github_1.normalizeGithubRepoEvent)(source, config, rawPayload);
|
|
286
|
-
if (!normalized) {
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
return {
|
|
290
|
-
sourceNativeId: normalized.sourceNativeId,
|
|
291
|
-
eventVariant: normalized.eventVariant,
|
|
292
|
-
metadata: normalized.metadata ?? {},
|
|
293
|
-
rawPayload: normalized.rawPayload ?? rawPayload,
|
|
294
|
-
occurredAt: normalized.occurredAt,
|
|
295
|
-
deliveryHandle: normalized.deliveryHandle,
|
|
296
|
-
};
|
|
297
|
-
},
|
|
298
|
-
};
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const GITHUB_REPO_MODULE = createGithubRepoRemoteModule();
|
|
299
304
|
const GITHUB_REPO_CI_MODULE = {
|
|
300
305
|
id: "builtin.github_repo_ci",
|
|
301
306
|
describeCapabilities() {
|
|
@@ -319,6 +324,7 @@ const GITHUB_REPO_CI_MODULE = {
|
|
|
319
324
|
{ name: "event", type: "string|null", description: "GitHub trigger event for the workflow run." },
|
|
320
325
|
{ name: "headBranch", type: "string|null", description: "Head branch for the workflow run." },
|
|
321
326
|
{ name: "headSha", type: "string|null", description: "Head commit SHA for the workflow run." },
|
|
327
|
+
{ name: "pullRequestNumbers", type: "number[]", description: "Pull request numbers associated with the workflow run." },
|
|
322
328
|
{ name: "actor", type: "string|null", description: "Actor login for the workflow run." },
|
|
323
329
|
{ name: "commitMessage", type: "string|null", description: "Head commit message when present." },
|
|
324
330
|
{ name: "htmlUrl", type: "string|null", description: "GitHub Actions run URL." },
|