@holon-run/agentinbox 0.1.0 → 0.1.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.
@@ -1,76 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GithubDeliveryAdapter = exports.GithubSourceRuntime = exports.GithubUxcClient = void 0;
3
+ exports.GithubDeliveryAdapter = exports.GithubUxcClient = exports.DEFAULT_GITHUB_EVENT_TYPES = exports.GITHUB_ENDPOINT = void 0;
4
4
  exports.normalizeGithubRepoEvent = normalizeGithubRepoEvent;
5
+ exports.parseGithubSourceConfig = parseGithubSourceConfig;
5
6
  const uxc_daemon_client_1 = require("@holon-run/uxc-daemon-client");
6
- const GITHUB_ENDPOINT = "https://api.github.com";
7
- const DEFAULT_EVENT_TYPES = [
7
+ exports.GITHUB_ENDPOINT = "https://api.github.com";
8
+ exports.DEFAULT_GITHUB_EVENT_TYPES = [
8
9
  "IssuesEvent",
9
10
  "IssueCommentEvent",
10
11
  "PullRequestEvent",
11
12
  "PullRequestReviewCommentEvent",
12
13
  ];
13
- const MAX_ERROR_BACKOFF_MULTIPLIER = 8;
14
14
  class GithubUxcClient {
15
15
  client;
16
16
  constructor(client = new uxc_daemon_client_1.UxcDaemonClient({ env: process.env })) {
17
17
  this.client = client;
18
18
  }
19
- async ensureRepoEventsSubscription(config, checkpoint) {
20
- if (checkpoint.uxcJobId) {
21
- try {
22
- const status = await this.client.subscribeStatus(checkpoint.uxcJobId);
23
- if (status.status === "running" || status.status === "reconnecting") {
24
- return {
25
- job_id: checkpoint.uxcJobId,
26
- mode: "poll",
27
- protocol: "openapi",
28
- endpoint: GITHUB_ENDPOINT,
29
- sink: "memory:",
30
- status: status.status,
31
- };
32
- }
33
- }
34
- catch {
35
- // Recreate the job below.
36
- }
37
- }
38
- const started = await this.client.subscribeStart({
39
- endpoint: GITHUB_ENDPOINT,
40
- operationId: "get:/repos/{owner}/{repo}/events",
41
- args: { owner: config.owner, repo: config.repo, per_page: config.perPage ?? 10 },
42
- mode: "poll",
43
- pollConfig: {
44
- interval_secs: config.pollIntervalSecs ?? 30,
45
- extract_items_pointer: "",
46
- checkpoint_strategy: {
47
- type: "item_key",
48
- item_key_pointer: "/id",
49
- seen_window: 1024,
50
- },
51
- },
52
- options: { auth: config.uxcAuth },
53
- sink: "memory:",
54
- ephemeral: false,
55
- });
56
- return started;
57
- }
58
- async readSubscriptionEvents(jobId, afterSeq) {
59
- const response = await this.client.subscriptionEvents({
60
- jobId,
61
- afterSeq,
62
- limit: 100,
63
- waitMs: 10,
64
- });
65
- return {
66
- events: response.events,
67
- nextAfterSeq: response.next_after_seq,
68
- status: response.status,
69
- };
70
- }
71
19
  async createIssueComment(input) {
72
20
  await this.client.call({
73
- endpoint: GITHUB_ENDPOINT,
21
+ endpoint: exports.GITHUB_ENDPOINT,
74
22
  operation: "post:/repos/{owner}/{repo}/issues/{issue_number}/comments",
75
23
  payload: {
76
24
  owner: input.owner,
@@ -83,7 +31,7 @@ class GithubUxcClient {
83
31
  }
84
32
  async replyToReviewComment(input) {
85
33
  await this.client.call({
86
- endpoint: GITHUB_ENDPOINT,
34
+ endpoint: exports.GITHUB_ENDPOINT,
87
35
  operation: "post:/repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies",
88
36
  payload: {
89
37
  owner: input.owner,
@@ -97,177 +45,6 @@ class GithubUxcClient {
97
45
  }
98
46
  }
99
47
  exports.GithubUxcClient = GithubUxcClient;
100
- class GithubSourceRuntime {
101
- store;
102
- appendSourceEvent;
103
- client;
104
- interval = null;
105
- inFlight = new Set();
106
- errorCounts = new Map();
107
- nextRetryAt = new Map();
108
- constructor(store, appendSourceEvent, client) {
109
- this.store = store;
110
- this.appendSourceEvent = appendSourceEvent;
111
- this.client = client ?? new GithubUxcClient();
112
- }
113
- async ensureSource(source) {
114
- if (source.sourceType !== "github_repo") {
115
- return;
116
- }
117
- const config = parseGithubSourceConfig(source);
118
- const checkpoint = parseGithubCheckpoint(source.checkpoint);
119
- const started = await this.client.ensureRepoEventsSubscription(config, checkpoint);
120
- this.store.updateSourceRuntime(source.sourceId, {
121
- status: "active",
122
- checkpoint: JSON.stringify({
123
- ...checkpoint,
124
- uxcJobId: started.job_id,
125
- }),
126
- });
127
- }
128
- async start() {
129
- if (this.interval) {
130
- return;
131
- }
132
- this.interval = setInterval(() => {
133
- void this.syncAll();
134
- }, 2_000);
135
- try {
136
- await this.syncAll();
137
- }
138
- catch (error) {
139
- console.warn("github_repo initial sync failed:", error);
140
- }
141
- }
142
- async stop() {
143
- if (this.interval) {
144
- clearInterval(this.interval);
145
- this.interval = null;
146
- }
147
- }
148
- async pollSource(sourceId) {
149
- return this.syncSource(sourceId);
150
- }
151
- status() {
152
- return {
153
- activeSourceIds: Array.from(this.inFlight.values()).sort(),
154
- erroredSourceIds: Array.from(this.errorCounts.keys()).sort(),
155
- };
156
- }
157
- async syncAll() {
158
- const sources = this.store
159
- .listSources()
160
- .filter((source) => source.sourceType === "github_repo" && source.status !== "paused");
161
- for (const source of sources) {
162
- try {
163
- await this.syncSource(source.sourceId);
164
- }
165
- catch (error) {
166
- console.warn(`github_repo sync failed for ${source.sourceId}:`, error);
167
- }
168
- }
169
- }
170
- async syncSource(sourceId) {
171
- if (this.inFlight.has(sourceId)) {
172
- return {
173
- sourceId,
174
- sourceType: "github_repo",
175
- appended: 0,
176
- deduped: 0,
177
- eventsRead: 0,
178
- note: "source sync already in flight",
179
- };
180
- }
181
- this.inFlight.add(sourceId);
182
- try {
183
- const source = this.store.getSource(sourceId);
184
- if (!source) {
185
- throw new Error(`unknown source: ${sourceId}`);
186
- }
187
- const config = parseGithubSourceConfig(source);
188
- if (source.status === "error") {
189
- const retryAt = this.nextRetryAt.get(sourceId) ?? 0;
190
- if (Date.now() < retryAt) {
191
- return {
192
- sourceId,
193
- sourceType: "github_repo",
194
- appended: 0,
195
- deduped: 0,
196
- eventsRead: 0,
197
- note: "error backoff not elapsed",
198
- };
199
- }
200
- }
201
- let checkpoint = parseGithubCheckpoint(source.checkpoint);
202
- const subscription = await this.client.ensureRepoEventsSubscription(config, checkpoint);
203
- if (subscription.job_id !== checkpoint.uxcJobId) {
204
- checkpoint = { ...checkpoint, uxcJobId: subscription.job_id };
205
- }
206
- const batch = await this.client.readSubscriptionEvents(checkpoint.uxcJobId, checkpoint.afterSeq ?? 0);
207
- let appended = 0;
208
- let deduped = 0;
209
- for (const event of batch.events) {
210
- if (event.event_kind !== "data") {
211
- continue;
212
- }
213
- const normalized = normalizeGithubRepoEvent(source, config, event.data);
214
- if (!normalized) {
215
- continue;
216
- }
217
- const result = await this.appendSourceEvent(normalized);
218
- appended += result.appended;
219
- deduped += result.deduped;
220
- }
221
- this.store.updateSourceRuntime(sourceId, {
222
- status: "active",
223
- checkpoint: JSON.stringify({
224
- ...checkpoint,
225
- uxcJobId: checkpoint.uxcJobId,
226
- afterSeq: batch.nextAfterSeq,
227
- lastEventAt: new Date().toISOString(),
228
- lastError: null,
229
- }),
230
- });
231
- this.errorCounts.delete(sourceId);
232
- this.nextRetryAt.delete(sourceId);
233
- return {
234
- sourceId,
235
- sourceType: "github_repo",
236
- appended,
237
- deduped,
238
- eventsRead: batch.events.length,
239
- note: `subscription status=${batch.status}`,
240
- };
241
- }
242
- catch (error) {
243
- const source = this.store.getSource(sourceId);
244
- if (source) {
245
- const checkpoint = parseGithubCheckpoint(source.checkpoint);
246
- const config = parseGithubSourceConfig(source);
247
- const nextErrorCount = (this.errorCounts.get(sourceId) ?? 0) + 1;
248
- this.errorCounts.set(sourceId, nextErrorCount);
249
- this.nextRetryAt.set(sourceId, Date.now() + computeErrorBackoffMs(config.pollIntervalSecs ?? 30, nextErrorCount));
250
- this.store.updateSourceRuntime(sourceId, {
251
- status: "error",
252
- checkpoint: JSON.stringify({
253
- ...checkpoint,
254
- lastError: error instanceof Error ? error.message : String(error),
255
- }),
256
- });
257
- }
258
- throw error;
259
- }
260
- finally {
261
- this.inFlight.delete(sourceId);
262
- }
263
- }
264
- }
265
- exports.GithubSourceRuntime = GithubSourceRuntime;
266
- function computeErrorBackoffMs(pollIntervalSecs, errorCount) {
267
- const baseMs = Math.max(1, pollIntervalSecs) * 1000;
268
- const multiplier = Math.min(2 ** Math.max(0, errorCount - 1), MAX_ERROR_BACKOFF_MULTIPLIER);
269
- return baseMs * multiplier;
270
- }
271
48
  class GithubDeliveryAdapter {
272
49
  client;
273
50
  constructor(client) {
@@ -308,7 +85,7 @@ function normalizeGithubRepoEvent(source, config, raw) {
308
85
  }
309
86
  const event = raw;
310
87
  const eventType = asString(event.type);
311
- if (!eventType || !(config.eventTypes ?? DEFAULT_EVENT_TYPES).includes(eventType)) {
88
+ if (!eventType || !(config.eventTypes ?? exports.DEFAULT_GITHUB_EVENT_TYPES).includes(eventType)) {
312
89
  return null;
313
90
  }
314
91
  const eventId = asString(event.id);
@@ -331,7 +108,7 @@ function normalizeGithubRepoEvent(source, config, raw) {
331
108
  const url = asString(comment.html_url) ??
332
109
  asString(issue.html_url) ??
333
110
  asString(pullRequest.html_url) ??
334
- asString(event["url"]);
111
+ asString(event.url);
335
112
  const deliveryHandle = buildGithubDeliveryHandle(config, eventType, number, comment);
336
113
  return {
337
114
  sourceId: source.sourceId,
@@ -403,7 +180,7 @@ function parseGithubSourceConfig(source) {
403
180
  uxcAuth: asString(config.uxcAuth) ?? asString(config.credentialRef) ?? undefined,
404
181
  pollIntervalSecs: asNumber(config.pollIntervalSecs) ?? 30,
405
182
  perPage: asNumber(config.perPage) ?? 10,
406
- eventTypes: asStringArray(config.eventTypes) ?? DEFAULT_EVENT_TYPES,
183
+ eventTypes: asStringArray(config.eventTypes) ?? exports.DEFAULT_GITHUB_EVENT_TYPES,
407
184
  };
408
185
  }
409
186
  return {
@@ -412,20 +189,9 @@ function parseGithubSourceConfig(source) {
412
189
  uxcAuth: asString(config.uxcAuth) ?? asString(config.credentialRef) ?? undefined,
413
190
  pollIntervalSecs: asNumber(config.pollIntervalSecs) ?? 30,
414
191
  perPage: asNumber(config.perPage) ?? 10,
415
- eventTypes: asStringArray(config.eventTypes) ?? DEFAULT_EVENT_TYPES,
192
+ eventTypes: asStringArray(config.eventTypes) ?? exports.DEFAULT_GITHUB_EVENT_TYPES,
416
193
  };
417
194
  }
418
- function parseGithubCheckpoint(checkpoint) {
419
- if (!checkpoint) {
420
- return {};
421
- }
422
- try {
423
- return JSON.parse(checkpoint);
424
- }
425
- catch {
426
- return {};
427
- }
428
- }
429
195
  function extractLabels(raw) {
430
196
  if (!Array.isArray(raw)) {
431
197
  return [];
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GithubCiSourceRuntime = exports.GithubActionsUxcClient = void 0;
3
+ exports.GithubCiSourceRuntime = exports.GithubActionsUxcClient = exports.DEFAULT_GITHUB_CI_PER_PAGE = exports.DEFAULT_GITHUB_CI_POLL_INTERVAL_SECS = exports.GITHUB_CI_ENDPOINT = void 0;
4
4
  exports.normalizeGithubWorkflowRunEvent = normalizeGithubWorkflowRunEvent;
5
+ exports.parseGithubCiSourceConfig = parseGithubCiSourceConfig;
5
6
  const uxc_daemon_client_1 = require("@holon-run/uxc-daemon-client");
6
- const GITHUB_ENDPOINT = "https://api.github.com";
7
- const DEFAULT_POLL_INTERVAL_SECS = 30;
8
- const DEFAULT_PER_PAGE = 20;
7
+ exports.GITHUB_CI_ENDPOINT = "https://api.github.com";
8
+ exports.DEFAULT_GITHUB_CI_POLL_INTERVAL_SECS = 30;
9
+ exports.DEFAULT_GITHUB_CI_PER_PAGE = 20;
9
10
  const MAX_SEEN_KEYS = 512;
10
11
  const MAX_ERROR_BACKOFF_MULTIPLIER = 8;
11
12
  class GithubActionsUxcClient {
@@ -17,7 +18,7 @@ class GithubActionsUxcClient {
17
18
  const payload = {
18
19
  owner: config.owner,
19
20
  repo: config.repo,
20
- per_page: config.perPage ?? DEFAULT_PER_PAGE,
21
+ per_page: config.perPage ?? exports.DEFAULT_GITHUB_CI_PER_PAGE,
21
22
  };
22
23
  if (config.eventFilter) {
23
24
  payload.event = config.eventFilter;
@@ -29,7 +30,7 @@ class GithubActionsUxcClient {
29
30
  payload.status = config.statusFilter;
30
31
  }
31
32
  const response = await this.client.call({
32
- endpoint: GITHUB_ENDPOINT,
33
+ endpoint: exports.GITHUB_CI_ENDPOINT,
33
34
  operation: "get:/repos/{owner}/{repo}/actions/runs",
34
35
  payload,
35
36
  options: { auth: config.uxcAuth },
@@ -138,7 +139,7 @@ class GithubCiSourceRuntime {
138
139
  };
139
140
  }
140
141
  const lastPollAt = this.lastPollAt.get(sourceId) ?? 0;
141
- const pollIntervalMs = (config.pollIntervalSecs ?? DEFAULT_POLL_INTERVAL_SECS) * 1000;
142
+ const pollIntervalMs = (config.pollIntervalSecs ?? exports.DEFAULT_GITHUB_CI_POLL_INTERVAL_SECS) * 1000;
142
143
  if (Date.now() - lastPollAt < pollIntervalMs) {
143
144
  return {
144
145
  sourceId,
@@ -205,7 +206,7 @@ class GithubCiSourceRuntime {
205
206
  const config = parseGithubCiSourceConfig(source);
206
207
  const nextErrorCount = (this.errorCounts.get(sourceId) ?? 0) + 1;
207
208
  this.errorCounts.set(sourceId, nextErrorCount);
208
- this.nextRetryAt.set(sourceId, Date.now() + computeErrorBackoffMs(config.pollIntervalSecs ?? DEFAULT_POLL_INTERVAL_SECS, nextErrorCount));
209
+ this.nextRetryAt.set(sourceId, Date.now() + computeErrorBackoffMs(config.pollIntervalSecs ?? exports.DEFAULT_GITHUB_CI_POLL_INTERVAL_SECS, nextErrorCount));
209
210
  this.store.updateSourceRuntime(sourceId, {
210
211
  status: "error",
211
212
  checkpoint: JSON.stringify({
@@ -301,8 +302,8 @@ function parseGithubCiSourceConfig(source) {
301
302
  owner: fallbackOwner,
302
303
  repo: fallbackRepo,
303
304
  uxcAuth: asString(config.uxcAuth) ?? asString(config.credentialRef) ?? undefined,
304
- pollIntervalSecs: asNumber(config.pollIntervalSecs) ?? DEFAULT_POLL_INTERVAL_SECS,
305
- perPage: asNumber(config.perPage) ?? DEFAULT_PER_PAGE,
305
+ pollIntervalSecs: asNumber(config.pollIntervalSecs) ?? exports.DEFAULT_GITHUB_CI_POLL_INTERVAL_SECS,
306
+ perPage: asNumber(config.perPage) ?? exports.DEFAULT_GITHUB_CI_PER_PAGE,
306
307
  eventFilter: asString(config.eventFilter) ?? undefined,
307
308
  branch: asString(config.branch) ?? undefined,
308
309
  statusFilter: asString(config.statusFilter) ?? undefined,
@@ -312,8 +313,8 @@ function parseGithubCiSourceConfig(source) {
312
313
  owner,
313
314
  repo,
314
315
  uxcAuth: asString(config.uxcAuth) ?? asString(config.credentialRef) ?? undefined,
315
- pollIntervalSecs: asNumber(config.pollIntervalSecs) ?? DEFAULT_POLL_INTERVAL_SECS,
316
- perPage: asNumber(config.perPage) ?? DEFAULT_PER_PAGE,
316
+ pollIntervalSecs: asNumber(config.pollIntervalSecs) ?? exports.DEFAULT_GITHUB_CI_POLL_INTERVAL_SECS,
317
+ perPage: asNumber(config.perPage) ?? exports.DEFAULT_GITHUB_CI_PER_PAGE,
317
318
  eventFilter: asString(config.eventFilter) ?? undefined,
318
319
  branch: asString(config.branch) ?? undefined,
319
320
  statusFilter: asString(config.statusFilter) ?? undefined,