@elizaos/plugin-github 2.0.0-alpha.6 → 2.0.0-beta.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/dist/index.js CHANGED
@@ -1,2219 +1,2362 @@
1
- // index.ts
2
- import { logger as logger9 } from "@elizaos/core";
1
+ import "./chunk-KU2MGW2W.js";
3
2
 
4
- // actions/createBranch.ts
3
+ // src/index.ts
5
4
  import {
6
- logger as logger2
5
+ getConnectorAccountManager as getConnectorAccountManager2,
6
+ logger as logger7,
7
+ promoteSubactionsToActions
7
8
  } from "@elizaos/core";
8
9
 
9
- // generated/specs/specs.ts
10
- var coreActionsSpec = {
11
- version: "1.0.0",
12
- actions: [
13
- {
14
- name: "CREATE_GITHUB_BRANCH",
15
- description: "",
16
- parameters: []
17
- },
18
- {
19
- name: "CREATE_GITHUB_COMMENT",
20
- description: "",
21
- parameters: []
22
- },
23
- {
24
- name: "CREATE_GITHUB_ISSUE",
25
- description: "",
26
- parameters: []
27
- },
28
- {
29
- name: "CREATE_GITHUB_PULL_REQUEST",
30
- description: "",
31
- parameters: []
32
- },
33
- {
34
- name: "MERGE_GITHUB_PULL_REQUEST",
35
- description: "",
36
- parameters: []
37
- },
38
- {
39
- name: "PUSH_GITHUB_CODE",
40
- description: "",
41
- parameters: []
42
- },
43
- {
44
- name: "REVIEW_GITHUB_PULL_REQUEST",
45
- description: "",
46
- parameters: []
47
- }
48
- ]
49
- };
50
- var allActionsSpec = {
51
- version: "1.0.0",
52
- actions: [
53
- {
54
- name: "CREATE_GITHUB_BRANCH",
55
- description: "",
56
- parameters: []
57
- },
58
- {
59
- name: "CREATE_GITHUB_COMMENT",
60
- description: "",
61
- parameters: []
62
- },
63
- {
64
- name: "CREATE_GITHUB_ISSUE",
65
- description: "",
66
- parameters: []
67
- },
68
- {
69
- name: "CREATE_GITHUB_PULL_REQUEST",
70
- description: "",
71
- parameters: []
72
- },
73
- {
74
- name: "MERGE_GITHUB_PULL_REQUEST",
75
- description: "",
76
- parameters: []
77
- },
78
- {
79
- name: "PUSH_GITHUB_CODE",
80
- description: "",
81
- parameters: []
82
- },
83
- {
84
- name: "REVIEW_GITHUB_PULL_REQUEST",
85
- description: "",
86
- parameters: []
87
- }
88
- ]
89
- };
90
- var coreProvidersSpec = {
91
- version: "1.0.0",
92
- providers: [
93
- {
94
- name: "GITHUB_ISSUE_CONTEXT",
95
- description: "Provides detailed context about a specific GitHub issue or pull request when referenced",
96
- dynamic: true
97
- },
98
- {
99
- name: "GITHUB_REPOSITORY_STATE",
100
- description: "Provides context about the current GitHub repository including recent activity",
101
- dynamic: true
102
- }
103
- ]
104
- };
105
- var allProvidersSpec = {
106
- version: "1.0.0",
107
- providers: [
108
- {
109
- name: "GITHUB_ISSUE_CONTEXT",
110
- description: "Provides detailed context about a specific GitHub issue or pull request when referenced",
111
- dynamic: true
112
- },
113
- {
114
- name: "GITHUB_REPOSITORY_STATE",
115
- description: "Provides context about the current GitHub repository including recent activity",
116
- dynamic: true
117
- }
118
- ]
119
- };
120
- var coreEvaluatorsSpec = {
121
- version: "1.0.0",
122
- evaluators: []
123
- };
124
- var allEvaluatorsSpec = {
125
- version: "1.0.0",
126
- evaluators: []
127
- };
128
- var coreActionDocs = coreActionsSpec.actions;
129
- var allActionDocs = allActionsSpec.actions;
130
- var coreProviderDocs = coreProvidersSpec.providers;
131
- var allProviderDocs = allProvidersSpec.providers;
132
- var coreEvaluatorDocs = coreEvaluatorsSpec.evaluators;
133
- var allEvaluatorDocs = allEvaluatorsSpec.evaluators;
10
+ // src/actions/issue-op.ts
11
+ import { logger } from "@elizaos/core";
134
12
 
135
- // generated/specs/spec-helpers.ts
136
- var coreActionMap = new Map(coreActionDocs.map((doc) => [doc.name, doc]));
137
- var allActionMap = new Map(allActionDocs.map((doc) => [doc.name, doc]));
138
- var coreProviderMap = new Map(coreProviderDocs.map((doc) => [doc.name, doc]));
139
- var allProviderMap = new Map(allProviderDocs.map((doc) => [doc.name, doc]));
140
- var coreEvaluatorMap = new Map(coreEvaluatorDocs.map((doc) => [doc.name, doc]));
141
- var allEvaluatorMap = new Map(allEvaluatorDocs.map((doc) => [doc.name, doc]));
142
- function getActionSpec(name) {
143
- return coreActionMap.get(name) ?? allActionMap.get(name);
144
- }
145
- function requireActionSpec(name) {
146
- const spec = getActionSpec(name);
147
- if (!spec) {
148
- throw new Error(`Action spec not found: ${name}`);
13
+ // src/connector-credential-refs.ts
14
+ import {
15
+ CONNECTOR_ACCOUNT_STORAGE_SERVICE_TYPE,
16
+ getConnectorAccountManager
17
+ } from "@elizaos/core";
18
+ var OAUTH_TOKENS_CREDENTIAL_TYPE = "oauth.tokens";
19
+ async function persistConnectorCredentialRefs(params) {
20
+ const refs = [];
21
+ const vaultWriters = resolveVaultWriters(params.runtime, {
22
+ provider: params.provider,
23
+ accountId: params.accountIdForRef,
24
+ caller: params.caller
25
+ });
26
+ if (vaultWriters.length === 0) {
27
+ throw new Error(
28
+ `No durable connector credential store or vault writer is available for ${params.provider} account ${params.accountIdForRef}. Refusing to mark OAuth account connected without persisted credentials.`
29
+ );
30
+ }
31
+ if (!params.storageAccountId) {
32
+ throw new Error(
33
+ `No durable connector account id is available for ${params.provider} account ${params.accountIdForRef}. Refusing to mark OAuth account connected without persisted credential refs.`
34
+ );
35
+ }
36
+ const storageWriters = resolveCredentialRefWriters(
37
+ params.runtime,
38
+ params.manager,
39
+ params.storageAccountId
40
+ );
41
+ if (storageWriters.length === 0) {
42
+ throw new Error(
43
+ `No durable connector credential ref writer is available for ${params.provider} account ${params.storageAccountId}. Refusing to mark OAuth account connected without persisted credential refs.`
44
+ );
45
+ }
46
+ for (const credential of params.credentials) {
47
+ const plannedRef = buildConnectorCredentialVaultRef({
48
+ agentId: nonEmptyString(params.runtime.agentId) ?? "agent",
49
+ provider: params.provider,
50
+ accountId: params.accountIdForRef,
51
+ credentialType: credential.credentialType
52
+ });
53
+ const vaultRef = await writeWithFirstAvailableVault(
54
+ vaultWriters,
55
+ plannedRef,
56
+ credential
57
+ );
58
+ refs.push({
59
+ credentialType: credential.credentialType,
60
+ vaultRef,
61
+ ...credential.expiresAt !== void 0 ? { expiresAt: credential.expiresAt } : {},
62
+ ...credential.metadata ? { metadata: credential.metadata } : {}
63
+ });
149
64
  }
150
- return spec;
65
+ if (refs.length > 0) {
66
+ await writeRefsToStorage(storageWriters, refs);
67
+ }
68
+ return {
69
+ refs,
70
+ vaultAvailable: vaultWriters.length > 0,
71
+ storageAvailable: storageWriters.length > 0
72
+ };
151
73
  }
152
- function getProviderSpec(name) {
153
- return coreProviderMap.get(name) ?? allProviderMap.get(name);
74
+ async function loadConnectorOAuthAccessToken(params) {
75
+ const { records } = await loadConnectorCredentialRecords(params);
76
+ const tokenRecord = records.find(
77
+ (record) => sameCredentialType(record.credentialType, OAUTH_TOKENS_CREDENTIAL_TYPE)
78
+ );
79
+ if (!tokenRecord) return null;
80
+ const raw = nonEmptyString(tokenRecord.value) ?? (tokenRecord.vaultRef ? await readCredentialSecret(
81
+ params.runtime,
82
+ tokenRecord.vaultRef,
83
+ params.caller
84
+ ) : void 0);
85
+ const parsed = raw ? parseMaybeJson(raw) : void 0;
86
+ const tokenSet = asRecord(parsed);
87
+ return nonEmptyString(tokenSet?.access_token) ?? null;
154
88
  }
155
- function requireProviderSpec(name) {
156
- const spec = getProviderSpec(name);
157
- if (!spec) {
158
- throw new Error(`Provider spec not found: ${name}`);
89
+ async function listConnectorAccounts(runtime, provider) {
90
+ try {
91
+ return await getConnectorAccountManager(runtime).listAccounts(provider);
92
+ } catch {
93
+ const storage = getService(
94
+ runtime,
95
+ CONNECTOR_ACCOUNT_STORAGE_SERVICE_TYPE
96
+ );
97
+ if (typeof storage?.listAccounts === "function") {
98
+ return storage.listAccounts(provider);
99
+ }
159
100
  }
160
- return spec;
101
+ return [];
161
102
  }
162
-
163
- // service.ts
164
- import { logger, Service } from "@elizaos/core";
165
- import { Octokit } from "@octokit/rest";
166
-
167
- // config.ts
168
- import { z as z2 } from "zod";
169
-
170
- // error.ts
171
- class GitHubError extends Error {
172
- constructor(message) {
173
- super(message);
174
- this.name = "GitHubError";
175
- Object.setPrototypeOf(this, GitHubError.prototype);
176
- }
177
- isRetryable() {
178
- return false;
179
- }
180
- retryAfterMs() {
181
- return null;
103
+ function credentialRefRecordsFromMetadata(metadata) {
104
+ const record = asRecord(metadata);
105
+ if (!record) return [];
106
+ const oauth = asRecord(record.oauth);
107
+ return [
108
+ ...credentialRefsFromUnknown(record.credentialRefs),
109
+ ...credentialRefsFromUnknown(record.oauthCredentialRefs),
110
+ ...credentialRefsFromUnknown(oauth?.credentialRefs)
111
+ ];
112
+ }
113
+ async function loadConnectorCredentialRecords(params) {
114
+ const accounts = await listConnectorAccounts(params.runtime, params.provider);
115
+ const account = accounts.find(
116
+ (candidate) => candidate.id === params.accountId || candidate.externalId === params.accountId || candidate.displayHandle === params.accountId
117
+ );
118
+ if (!account) return { records: [] };
119
+ const records = [...credentialRefRecordsFromMetadata(account.metadata)];
120
+ for (const source of [
121
+ getService(params.runtime, CONNECTOR_ACCOUNT_STORAGE_SERVICE_TYPE),
122
+ params.runtime.adapter
123
+ ]) {
124
+ const reader = source;
125
+ if (typeof reader?.listConnectorAccountCredentialRefs === "function") {
126
+ records.push(
127
+ ...await reader.listConnectorAccountCredentialRefs({
128
+ accountId: account.id
129
+ })
130
+ );
131
+ } else if (typeof reader?.getConnectorAccountCredentialRef === "function") {
132
+ const ref = await reader.getConnectorAccountCredentialRef({
133
+ accountId: account.id,
134
+ credentialType: OAUTH_TOKENS_CREDENTIAL_TYPE
135
+ });
136
+ if (ref) records.push(ref);
137
+ }
182
138
  }
139
+ return { records };
183
140
  }
184
- class ConfigError extends GitHubError {
185
- constructor(message) {
186
- super(`Configuration error: ${message}`);
187
- this.name = "ConfigError";
188
- Object.setPrototypeOf(this, ConfigError.prototype);
141
+ function credentialRefsFromUnknown(value) {
142
+ if (Array.isArray(value)) {
143
+ return value.flatMap((entry) => {
144
+ const ref = credentialRefFromRecord(asRecord(entry));
145
+ return ref ? [ref] : [];
146
+ });
189
147
  }
148
+ const record = asRecord(value);
149
+ if (!record) return [];
150
+ return Object.entries(record).flatMap(([credentialType, entry]) => {
151
+ const entryRecord = asRecord(entry);
152
+ if (entryRecord) {
153
+ const ref = credentialRefFromRecord({ credentialType, ...entryRecord });
154
+ return ref ? [ref] : [];
155
+ }
156
+ const vaultRef = nonEmptyString(entry);
157
+ return vaultRef ? [{ credentialType, vaultRef }] : [];
158
+ });
190
159
  }
191
-
192
- class MissingSettingError extends GitHubError {
193
- settingName;
194
- constructor(settingName) {
195
- super(`Missing required setting: ${settingName}`);
196
- this.name = "MissingSettingError";
197
- this.settingName = settingName;
198
- Object.setPrototypeOf(this, MissingSettingError.prototype);
160
+ function credentialRefFromRecord(record) {
161
+ if (!record) return null;
162
+ const credentialType = nonEmptyString(
163
+ record.credentialType ?? record.type ?? record.name
164
+ );
165
+ const vaultRef = nonEmptyString(record.vaultRef ?? record.ref);
166
+ if (!credentialType || !vaultRef) return null;
167
+ return {
168
+ credentialType,
169
+ vaultRef,
170
+ metadata: asRecord(record.metadata) ?? null,
171
+ expiresAt: record.expiresAt,
172
+ updatedAt: record.updatedAt,
173
+ version: record.version ?? record.credentialVersion
174
+ };
175
+ }
176
+ async function readCredentialSecret(runtime, vaultRef, caller) {
177
+ for (const reader of resolveSecretReaders(runtime)) {
178
+ try {
179
+ const value = await readSecret(reader, vaultRef, caller, runtime);
180
+ const trimmed = nonEmptyString(value);
181
+ if (trimmed) return trimmed;
182
+ } catch {
183
+ }
199
184
  }
185
+ return void 0;
200
186
  }
201
- class FileNotFoundError extends GitHubError {
202
- path;
203
- constructor(path, owner, repo) {
204
- super(`File not found: ${path} in ${owner}/${repo}`);
205
- this.name = "FileNotFoundError";
206
- this.path = path;
207
- Object.setPrototypeOf(this, FileNotFoundError.prototype);
187
+ function resolveVaultWriters(runtime, context) {
188
+ const writers = [];
189
+ const credentialStore = getFirstService(runtime, [
190
+ "connector_credential_store",
191
+ "CONNECTOR_CREDENTIAL_STORE",
192
+ "connectorCredentialStore",
193
+ "credential_store"
194
+ ]);
195
+ if (typeof credentialStore?.putSecret === "function") {
196
+ writers.push({
197
+ name: "connector_credential_store",
198
+ write: async (vaultRef, credential) => credentialStore.putSecret?.({
199
+ vaultRef,
200
+ agentId: nonEmptyString(runtime.agentId) ?? "agent",
201
+ provider: context.provider,
202
+ accountId: context.accountId,
203
+ credentialType: credential.credentialType,
204
+ value: credential.value,
205
+ caller: context.caller
206
+ }) ?? vaultRef
207
+ });
208
+ }
209
+ const vault = getFirstService(runtime, ["vault", "VAULT"]);
210
+ if (typeof vault?.set === "function") {
211
+ writers.push({
212
+ name: "vault",
213
+ write: async (vaultRef, credential) => {
214
+ await vault.set?.(vaultRef, credential.value, {
215
+ sensitive: true,
216
+ caller: context.caller
217
+ });
218
+ return vaultRef;
219
+ }
220
+ });
221
+ }
222
+ const secrets = getService(runtime, "SECRETS");
223
+ if (typeof secrets?.setGlobal === "function" || typeof secrets?.set === "function") {
224
+ writers.push({
225
+ name: "SECRETS",
226
+ write: async (vaultRef, credential) => {
227
+ if (typeof secrets.setGlobal === "function") {
228
+ await secrets.setGlobal(vaultRef, credential.value, {
229
+ sensitive: true
230
+ });
231
+ return vaultRef;
232
+ }
233
+ await secrets.set?.(
234
+ vaultRef,
235
+ credential.value,
236
+ { level: "global", agentId: runtime.agentId },
237
+ { sensitive: true }
238
+ );
239
+ return vaultRef;
240
+ }
241
+ });
208
242
  }
243
+ return writers;
209
244
  }
210
-
211
- // types.ts
212
- import { z } from "zod";
213
- var repositoryRefSchema = z.object({
214
- owner: z.string().min(1, "Owner is required"),
215
- repo: z.string().min(1, "Repo is required")
216
- });
217
- var fileRefSchema = repositoryRefSchema.extend({
218
- path: z.string().min(1, "Path is required"),
219
- branch: z.string().optional()
220
- });
221
- var createIssueSchema = repositoryRefSchema.extend({
222
- title: z.string().min(1, "Title is required"),
223
- body: z.string().optional(),
224
- assignees: z.array(z.string()).optional(),
225
- labels: z.array(z.string()).optional(),
226
- milestone: z.number().optional()
227
- });
228
- var updateIssueSchema = repositoryRefSchema.extend({
229
- issueNumber: z.number().min(1, "Issue number is required"),
230
- title: z.string().optional(),
231
- body: z.string().optional(),
232
- state: z.enum(["open", "closed"]).optional(),
233
- stateReason: z.enum(["completed", "not_planned", "reopened"]).optional(),
234
- assignees: z.array(z.string()).optional(),
235
- labels: z.array(z.string()).optional(),
236
- milestone: z.number().nullable().optional()
237
- });
238
- var createPullRequestSchema = repositoryRefSchema.extend({
239
- title: z.string().min(1, "Title is required"),
240
- body: z.string().optional(),
241
- head: z.string().min(1, "Head branch is required"),
242
- base: z.string().min(1, "Base branch is required"),
243
- draft: z.boolean().optional(),
244
- maintainerCanModify: z.boolean().optional()
245
- });
246
- var createReviewSchema = repositoryRefSchema.extend({
247
- pullNumber: z.number().min(1, "Pull request number is required"),
248
- body: z.string().optional(),
249
- event: z.enum(["APPROVE", "REQUEST_CHANGES", "COMMENT"]),
250
- commitId: z.string().optional(),
251
- comments: z.array(z.object({
252
- path: z.string(),
253
- line: z.number(),
254
- body: z.string(),
255
- side: z.enum(["LEFT", "RIGHT"]).optional(),
256
- startLine: z.number().optional(),
257
- startSide: z.enum(["LEFT", "RIGHT"]).optional()
258
- })).optional()
259
- });
260
- var createCommentSchema = repositoryRefSchema.extend({
261
- issueNumber: z.number().min(1, "Issue number is required"),
262
- body: z.string().min(1, "Comment body is required")
263
- });
264
- var createBranchSchema = repositoryRefSchema.extend({
265
- branchName: z.string().min(1, "Branch name is required"),
266
- fromRef: z.string().min(1, "Source ref is required")
267
- });
268
- var createCommitSchema = repositoryRefSchema.extend({
269
- message: z.string().min(1, "Commit message is required"),
270
- files: z.array(z.object({
271
- path: z.string().min(1, "File path is required"),
272
- content: z.string(),
273
- encoding: z.enum(["utf-8", "base64"]).optional(),
274
- operation: z.enum(["add", "modify", "delete"]).optional()
275
- })),
276
- branch: z.string().min(1, "Branch is required"),
277
- parentSha: z.string().optional(),
278
- authorName: z.string().optional(),
279
- authorEmail: z.string().optional()
280
- });
281
- var mergePullRequestSchema = repositoryRefSchema.extend({
282
- pullNumber: z.number().min(1, "Pull request number is required"),
283
- commitTitle: z.string().optional(),
284
- commitMessage: z.string().optional(),
285
- mergeMethod: z.enum(["merge", "squash", "rebase"]).optional(),
286
- sha: z.string().optional()
287
- });
288
- var gitHubSettingsSchema = z.object({
289
- apiToken: z.string().min(1, "API token is required"),
290
- owner: z.string().optional(),
291
- repo: z.string().optional(),
292
- branch: z.string().optional(),
293
- webhookSecret: z.string().optional(),
294
- appId: z.string().optional(),
295
- appPrivateKey: z.string().optional(),
296
- installationId: z.string().optional()
297
- });
298
- function formatZodErrors(error) {
299
- return z.prettifyError(error);
245
+ function resolveSecretReaders(runtime) {
246
+ return [
247
+ getFirstService(runtime, [
248
+ "connector_credential_store",
249
+ "CONNECTOR_CREDENTIAL_STORE",
250
+ "connectorCredentialStore",
251
+ "credential_store"
252
+ ]),
253
+ getFirstService(runtime, ["vault", "VAULT"]),
254
+ getService(runtime, "SECRETS")
255
+ ].filter(Boolean);
300
256
  }
301
-
302
- // config.ts
303
- var configSchema = z2.object({
304
- apiToken: z2.string().min(1, "API token is required"),
305
- owner: z2.string().optional(),
306
- repo: z2.string().optional(),
307
- branch: z2.string().default("main"),
308
- webhookSecret: z2.string().optional(),
309
- appId: z2.string().optional(),
310
- appPrivateKey: z2.string().optional(),
311
- installationId: z2.string().optional()
312
- });
313
-
314
- class GitHubPluginConfig {
315
- apiToken;
316
- owner;
317
- repo;
318
- branch;
319
- webhookSecret;
320
- appId;
321
- appPrivateKey;
322
- installationId;
323
- constructor(config) {
324
- this.apiToken = config.apiToken;
325
- this.owner = config.owner;
326
- this.repo = config.repo;
327
- this.branch = config.branch;
328
- this.webhookSecret = config.webhookSecret;
329
- this.appId = config.appId;
330
- this.appPrivateKey = config.appPrivateKey;
331
- this.installationId = config.installationId;
332
- }
333
- static fromRuntime(runtime) {
334
- const apiToken = runtime.getSetting("GITHUB_API_TOKEN");
335
- if (!apiToken) {
336
- throw new MissingSettingError("GITHUB_API_TOKEN");
337
- }
338
- const rawConfig = {
339
- apiToken,
340
- owner: runtime.getSetting("GITHUB_OWNER") ?? undefined,
341
- repo: runtime.getSetting("GITHUB_REPO") ?? undefined,
342
- branch: runtime.getSetting("GITHUB_BRANCH") ?? "main",
343
- webhookSecret: runtime.getSetting("GITHUB_WEBHOOK_SECRET") ?? undefined,
344
- appId: runtime.getSetting("GITHUB_APP_ID") ?? undefined,
345
- appPrivateKey: runtime.getSetting("GITHUB_APP_PRIVATE_KEY") ?? undefined,
346
- installationId: runtime.getSetting("GITHUB_INSTALLATION_ID") ?? undefined
347
- };
348
- const result = configSchema.safeParse(rawConfig);
349
- if (!result.success) {
350
- throw new ConfigError(formatZodErrors(result.error));
351
- }
352
- return new GitHubPluginConfig(result.data);
257
+ async function readSecret(reader, vaultRef, caller, runtime) {
258
+ const candidate = reader;
259
+ if (typeof candidate.reveal === "function") {
260
+ return candidate.reveal(vaultRef, caller);
353
261
  }
354
- static fromSettings(settings) {
355
- const result = configSchema.safeParse({
356
- ...settings,
357
- branch: settings.branch ?? "main"
262
+ if (typeof candidate.get !== "function") return null;
263
+ if (reader && reader.constructor?.name === "SecretsService") {
264
+ return candidate.get(vaultRef, {
265
+ level: "global",
266
+ agentId: runtime.agentId
358
267
  });
359
- if (!result.success) {
360
- throw new ConfigError(formatZodErrors(result.error));
361
- }
362
- return new GitHubPluginConfig(result.data);
363
268
  }
364
- static fromEnv() {
365
- const apiToken = process.env.GITHUB_API_TOKEN;
366
- if (!apiToken) {
367
- throw new MissingSettingError("GITHUB_API_TOKEN");
368
- }
369
- const rawConfig = {
370
- apiToken,
371
- owner: process.env.GITHUB_OWNER,
372
- repo: process.env.GITHUB_REPO,
373
- branch: process.env.GITHUB_BRANCH ?? "main",
374
- webhookSecret: process.env.GITHUB_WEBHOOK_SECRET,
375
- appId: process.env.GITHUB_APP_ID,
376
- appPrivateKey: process.env.GITHUB_APP_PRIVATE_KEY,
377
- installationId: process.env.GITHUB_INSTALLATION_ID
378
- };
379
- const result = configSchema.safeParse(rawConfig);
380
- if (!result.success) {
381
- throw new ConfigError(formatZodErrors(result.error));
269
+ return candidate.get(vaultRef, { reveal: true, caller });
270
+ }
271
+ function resolveCredentialRefWriters(runtime, manager, accountId) {
272
+ const candidates = [
273
+ manager?.getStorage?.(),
274
+ getService(runtime, CONNECTOR_ACCOUNT_STORAGE_SERVICE_TYPE),
275
+ runtime.adapter
276
+ ].filter(Boolean);
277
+ const writers = [];
278
+ for (const candidate of candidates) {
279
+ const writer = candidate;
280
+ if (typeof writer.setConnectorAccountCredentialRef === "function") {
281
+ writers.push({
282
+ name: "setConnectorAccountCredentialRef",
283
+ write: async (ref) => {
284
+ await writer.setConnectorAccountCredentialRef?.({
285
+ accountId,
286
+ credentialType: ref.credentialType,
287
+ vaultRef: ref.vaultRef,
288
+ ...ref.metadata ? { metadata: ref.metadata } : {},
289
+ ...ref.expiresAt !== void 0 ? { expiresAt: ref.expiresAt } : {}
290
+ });
291
+ }
292
+ });
293
+ } else if (typeof writer.setCredentialRef === "function") {
294
+ writers.push({
295
+ name: "setCredentialRef",
296
+ write: async (ref) => {
297
+ await writer.setCredentialRef?.({
298
+ accountId,
299
+ credentialType: ref.credentialType,
300
+ vaultRef: ref.vaultRef,
301
+ ...ref.metadata ? { metadata: ref.metadata } : {},
302
+ ...ref.expiresAt !== void 0 ? { expiresAt: ref.expiresAt } : {}
303
+ });
304
+ }
305
+ });
382
306
  }
383
- return new GitHubPluginConfig(result.data);
384
307
  }
385
- getRepositoryRef(owner, repo) {
386
- const resolvedOwner = owner ?? this.owner;
387
- const resolvedRepo = repo ?? this.repo;
388
- if (!resolvedOwner) {
389
- throw new MissingSettingError("owner (GITHUB_OWNER)");
308
+ return writers;
309
+ }
310
+ async function writeWithFirstAvailableVault(writers, plannedRef, credential) {
311
+ const errors = [];
312
+ for (const writer of writers) {
313
+ try {
314
+ return await writer.write(plannedRef, credential);
315
+ } catch (error) {
316
+ errors.push(
317
+ `${writer.name}: ${error instanceof Error ? error.message : String(error)}`
318
+ );
390
319
  }
391
- if (!resolvedRepo) {
392
- throw new MissingSettingError("repo (GITHUB_REPO)");
320
+ }
321
+ throw new Error(
322
+ `Failed to persist connector credential ref ${plannedRef}: ${errors.join("; ")}`
323
+ );
324
+ }
325
+ async function writeRefsToStorage(writers, refs) {
326
+ const errors = [];
327
+ for (const writer of writers) {
328
+ try {
329
+ for (const ref of refs) {
330
+ await writer.write(ref);
331
+ }
332
+ return;
333
+ } catch (error) {
334
+ errors.push(
335
+ `${writer.name}: ${error instanceof Error ? error.message : String(error)}`
336
+ );
393
337
  }
394
- return { owner: resolvedOwner, repo: resolvedRepo };
395
338
  }
396
- hasAppAuth() {
397
- return !!(this.appId && this.appPrivateKey);
339
+ throw new Error(
340
+ `Failed to persist connector credential refs: ${errors.join("; ")}`
341
+ );
342
+ }
343
+ function buildConnectorCredentialVaultRef(params) {
344
+ return [
345
+ "connector",
346
+ normalizeVaultSegment(params.agentId),
347
+ normalizeVaultSegment(params.provider),
348
+ normalizeVaultSegment(params.accountId),
349
+ normalizeVaultSegment(params.credentialType)
350
+ ].join(".");
351
+ }
352
+ function normalizeVaultSegment(value) {
353
+ const normalized = value.trim().replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "");
354
+ return (normalized || "unknown").slice(0, 64);
355
+ }
356
+ function sameCredentialType(left, right) {
357
+ return left.toLowerCase() === right.toLowerCase();
358
+ }
359
+ function parseMaybeJson(value) {
360
+ const trimmed = value.trim();
361
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return void 0;
362
+ try {
363
+ return JSON.parse(trimmed);
364
+ } catch {
365
+ return void 0;
398
366
  }
399
- validate() {
400
- if (!this.apiToken.startsWith("ghp_") && !this.apiToken.startsWith("gho_") && !this.apiToken.startsWith("ghu_") && !this.apiToken.startsWith("ghs_") && !this.apiToken.startsWith("ghr_") && !this.apiToken.startsWith("github_pat_")) {
401
- console.warn("GitHub API token format not recognized. Ensure it is a valid token.");
402
- }
403
- if (this.hasAppAuth() && !this.installationId) {
404
- throw new ConfigError("GITHUB_INSTALLATION_ID is required when using GitHub App authentication");
405
- }
367
+ }
368
+ function getFirstService(runtime, serviceTypes) {
369
+ for (const serviceType of serviceTypes) {
370
+ const service = getService(runtime, serviceType);
371
+ if (service) return service;
406
372
  }
407
- toSettings() {
408
- return {
409
- apiToken: this.apiToken,
410
- owner: this.owner,
411
- repo: this.repo,
412
- branch: this.branch,
413
- webhookSecret: this.webhookSecret,
414
- appId: this.appId,
415
- appPrivateKey: this.appPrivateKey,
416
- installationId: this.installationId
417
- };
373
+ return null;
374
+ }
375
+ function getService(runtime, serviceType) {
376
+ try {
377
+ return runtime.getService?.(serviceType) ?? null;
378
+ } catch {
379
+ return null;
418
380
  }
419
381
  }
420
- function validateGitHubConfig(runtime) {
421
- const config = GitHubPluginConfig.fromRuntime(runtime);
422
- config.validate();
423
- return config;
382
+ function asRecord(value) {
383
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
384
+ }
385
+ function nonEmptyString(value) {
386
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
424
387
  }
425
388
 
426
- // service.ts
427
- var GITHUB_SERVICE_NAME = "github";
389
+ // src/types.ts
390
+ var GITHUB_SERVICE_TYPE = "github";
391
+ var GitHubActions = {
392
+ GITHUB_ISSUE_OP: "GITHUB_ISSUE",
393
+ GITHUB_PR_OP: "GITHUB_PR",
394
+ GITHUB_NOTIFICATION_TRIAGE: "GITHUB_NOTIFICATION_TRIAGE"
395
+ };
428
396
 
429
- class GitHubService extends Service {
430
- static serviceType = GITHUB_SERVICE_NAME;
431
- octokit = null;
432
- _config = null;
433
- capabilityDescription = "GitHub integration for repository management, issues, pull requests, and code reviews";
434
- get name() {
435
- return GITHUB_SERVICE_NAME;
436
- }
437
- getConfig() {
438
- if (!this._config) {
439
- throw new Error("GitHub service not initialized");
440
- }
441
- return this._config;
397
+ // src/accounts.ts
398
+ var DEFAULT_GITHUB_USER_ACCOUNT_ID = "user";
399
+ var DEFAULT_GITHUB_AGENT_ACCOUNT_ID = "agent";
400
+ function nonEmptyString2(value) {
401
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
402
+ }
403
+ function readSetting(runtime, key) {
404
+ return nonEmptyString2(runtime.getSetting(key));
405
+ }
406
+ function normalizeRole(value) {
407
+ return value === "user" || value === "agent" ? value : void 0;
408
+ }
409
+ function defaultGitHubAccountIdForRole(role) {
410
+ return role === "user" ? DEFAULT_GITHUB_USER_ACCOUNT_ID : DEFAULT_GITHUB_AGENT_ACCOUNT_ID;
411
+ }
412
+ function normalizeGitHubAccountId(value) {
413
+ return nonEmptyString2(value);
414
+ }
415
+ function resolveGitHubAccountSelection(options, defaultRole) {
416
+ const requestedAccountId = normalizeGitHubAccountId(options?.accountId);
417
+ const requestedRole = normalizeRole(options?.as);
418
+ return {
419
+ accountId: requestedAccountId,
420
+ role: requestedRole ?? normalizeRole(requestedAccountId) ?? defaultRole
421
+ };
422
+ }
423
+ function parseAccountsJson(raw) {
424
+ if (!raw) return [];
425
+ try {
426
+ const parsed = JSON.parse(raw);
427
+ if (Array.isArray(parsed)) {
428
+ return parsed.filter(
429
+ (item) => Boolean(item) && typeof item === "object" && !Array.isArray(item)
430
+ );
431
+ }
432
+ if (parsed && typeof parsed === "object") {
433
+ return Object.entries(parsed).filter(([, value]) => value && typeof value === "object").map(([id, value]) => ({
434
+ ...value,
435
+ accountId: value.accountId ?? id
436
+ }));
437
+ }
438
+ } catch {
439
+ return [];
442
440
  }
443
- getClient() {
444
- if (!this.octokit) {
445
- throw new Error("GitHub service not initialized");
441
+ return [];
442
+ }
443
+ function readRawField(record, keys) {
444
+ const credentials = record.credentials && typeof record.credentials === "object" ? record.credentials : {};
445
+ const settings = record.settings && typeof record.settings === "object" ? record.settings : {};
446
+ for (const source of [record, credentials, settings]) {
447
+ for (const key of keys) {
448
+ const value = nonEmptyString2(source[key]);
449
+ if (value) return value;
446
450
  }
447
- return this.octokit;
448
- }
449
- async start(runtime) {
450
- logger.info("Starting GitHub service...");
451
- this._config = validateGitHubConfig(runtime);
452
- const settings = this._config.toSettings();
453
- this.config = {
454
- apiToken: settings.apiToken ?? "",
455
- owner: settings.owner ?? undefined,
456
- repo: settings.repo ?? undefined,
457
- branch: settings.branch ?? "main",
458
- webhookSecret: settings.webhookSecret ?? undefined,
459
- appId: settings.appId ?? undefined,
460
- appPrivateKey: settings.appPrivateKey ?? undefined,
461
- installationId: settings.installationId ?? undefined
462
- };
463
- this.octokit = new Octokit({
464
- auth: this._config.apiToken,
465
- userAgent: "elizaos-plugin-github/1.0.0"
466
- });
467
- const { data: user } = await this.octokit.users.getAuthenticated();
468
- logger.info(`GitHub service started - authenticated as ${user.login}`);
469
451
  }
470
- async stop() {
471
- logger.info("Stopping GitHub service...");
472
- this.octokit = null;
473
- this._config = null;
474
- this.config = undefined;
475
- logger.info("GitHub service stopped");
476
- }
477
- async getRepository(params) {
478
- const client = this.getClient();
479
- const { owner, repo } = this.resolveRepoRef(params);
480
- const { data } = await client.repos.get({ owner, repo });
481
- return this.mapRepository(data);
482
- }
483
- async listRepositories(username, options) {
484
- const client = this.getClient();
485
- const { data } = username ? await client.repos.listForUser({
486
- username,
487
- type: options?.type ?? "owner",
488
- per_page: options?.perPage ?? 30,
489
- page: options?.page ?? 1
490
- }) : await client.repos.listForAuthenticatedUser({
491
- type: options?.type ?? "all",
492
- per_page: options?.perPage ?? 30,
493
- page: options?.page ?? 1
494
- });
495
- return data.map((r) => this.mapRepository(r));
496
- }
497
- async createIssue(params) {
498
- const client = this.getClient();
499
- const { owner, repo } = this.resolveRepoRef(params);
500
- const { data } = await client.issues.create({
501
- owner,
502
- repo,
503
- title: params.title,
504
- body: params.body,
505
- assignees: params.assignees,
506
- labels: params.labels,
507
- milestone: params.milestone
508
- });
509
- return this.mapIssue(data);
510
- }
511
- async getIssue(params) {
512
- const client = this.getClient();
513
- const { owner, repo } = this.resolveRepoRef(params);
514
- const { data } = await client.issues.get({
515
- owner,
516
- repo,
517
- issue_number: params.issueNumber
518
- });
519
- return this.mapIssue(data);
520
- }
521
- async updateIssue(params) {
522
- const client = this.getClient();
523
- const { owner, repo } = this.resolveRepoRef(params);
524
- const { data } = await client.issues.update({
525
- owner,
526
- repo,
527
- issue_number: params.issueNumber,
528
- title: params.title,
529
- body: params.body,
530
- state: params.state,
531
- state_reason: params.stateReason,
532
- assignees: params.assignees,
533
- labels: params.labels,
534
- milestone: params.milestone ?? undefined
535
- });
536
- return this.mapIssue(data);
537
- }
538
- async listIssues(params) {
539
- const client = this.getClient();
540
- const { owner, repo } = this.resolveRepoRef(params);
541
- const { data } = await client.issues.listForRepo({
542
- owner,
543
- repo,
544
- state: params.state ?? "open",
545
- labels: params.labels,
546
- sort: params.sort ?? "created",
547
- direction: params.direction ?? "desc",
548
- assignee: params.assignee,
549
- creator: params.creator,
550
- mentioned: params.mentioned,
551
- per_page: params.perPage ?? 30,
552
- page: params.page ?? 1
553
- });
554
- return data.filter((issue) => !issue.pull_request).map((issue) => this.mapIssue(issue));
452
+ return void 0;
453
+ }
454
+ function accountFromRecord(record) {
455
+ const accountId = normalizeGitHubAccountId(
456
+ record.accountId ?? record.id ?? record.name
457
+ );
458
+ const role = normalizeRole(record.role) ?? normalizeRole(record.as) ?? normalizeRole(accountId);
459
+ const token = readRawField(record, [
460
+ "GITHUB_PAT",
461
+ "GITHUB_TOKEN",
462
+ "GITHUB_ACCESS_TOKEN",
463
+ "token",
464
+ "pat",
465
+ "accessToken",
466
+ "access"
467
+ ]);
468
+ if (!accountId || !role || !token) {
469
+ return null;
555
470
  }
556
- async closeIssue(params) {
557
- return this.updateIssue({
558
- ...params,
559
- state: "closed",
560
- stateReason: params.reason ?? "completed"
561
- });
471
+ return {
472
+ accountId,
473
+ role,
474
+ token,
475
+ label: nonEmptyString2(record.label ?? record.displayName)
476
+ };
477
+ }
478
+ function addAccount(accounts, account) {
479
+ if (account) {
480
+ accounts.set(account.accountId, account);
562
481
  }
563
- async reopenIssue(params) {
564
- return this.updateIssue({
565
- ...params,
566
- state: "open",
567
- stateReason: "reopened"
568
- });
482
+ }
483
+ function readGitHubAccounts(runtime) {
484
+ const accounts = /* @__PURE__ */ new Map();
485
+ const characterConfig = runtime.character?.settings?.github;
486
+ const characterAccounts = characterConfig?.accounts;
487
+ if (Array.isArray(characterAccounts)) {
488
+ for (const item of characterAccounts) {
489
+ if (item && typeof item === "object") {
490
+ addAccount(accounts, accountFromRecord(item));
491
+ }
492
+ }
493
+ } else if (characterAccounts && typeof characterAccounts === "object") {
494
+ for (const [id, value] of Object.entries(
495
+ characterAccounts
496
+ )) {
497
+ if (value && typeof value === "object") {
498
+ addAccount(
499
+ accounts,
500
+ accountFromRecord({
501
+ ...value,
502
+ accountId: value.accountId ?? id
503
+ })
504
+ );
505
+ }
506
+ }
569
507
  }
570
- async createPullRequest(params) {
571
- const client = this.getClient();
572
- const { owner, repo } = this.resolveRepoRef(params);
573
- const { data } = await client.pulls.create({
574
- owner,
575
- repo,
576
- title: params.title,
577
- body: params.body,
578
- head: params.head,
579
- base: params.base,
580
- draft: params.draft,
581
- maintainer_can_modify: params.maintainerCanModify
582
- });
583
- return this.mapPullRequest(data);
584
- }
585
- async getPullRequest(params) {
586
- const client = this.getClient();
587
- const { owner, repo } = this.resolveRepoRef(params);
588
- const { data } = await client.pulls.get({
589
- owner,
590
- repo,
591
- pull_number: params.pullNumber
592
- });
593
- return this.mapPullRequest(data);
594
- }
595
- async updatePullRequest(params) {
596
- const client = this.getClient();
597
- const { owner, repo } = this.resolveRepoRef(params);
598
- const { data } = await client.pulls.update({
599
- owner,
600
- repo,
601
- pull_number: params.pullNumber,
602
- title: params.title,
603
- body: params.body,
604
- state: params.state,
605
- base: params.base,
606
- maintainer_can_modify: params.maintainerCanModify
607
- });
608
- return this.mapPullRequest(data);
609
- }
610
- async listPullRequests(params) {
611
- const client = this.getClient();
612
- const { owner, repo } = this.resolveRepoRef(params);
613
- const { data } = await client.pulls.list({
614
- owner,
615
- repo,
616
- state: params.state ?? "open",
617
- head: params.head,
618
- base: params.base,
619
- sort: params.sort ?? "created",
620
- direction: params.direction ?? "desc",
621
- per_page: params.perPage ?? 30,
622
- page: params.page ?? 1
623
- });
624
- return data.map((pr) => this.mapPullRequest(pr));
625
- }
626
- async mergePullRequest(params) {
627
- const client = this.getClient();
628
- const { owner, repo } = this.resolveRepoRef(params);
629
- const { data } = await client.pulls.merge({
630
- owner,
631
- repo,
632
- pull_number: params.pullNumber,
633
- commit_title: params.commitTitle,
634
- commit_message: params.commitMessage,
635
- merge_method: params.mergeMethod ?? "merge",
636
- sha: params.sha
637
- });
638
- return {
639
- sha: data.sha,
640
- merged: data.merged,
641
- message: data.message
642
- };
508
+ for (const record of parseAccountsJson(
509
+ readSetting(runtime, "GITHUB_ACCOUNTS")
510
+ )) {
511
+ addAccount(accounts, accountFromRecord(record));
643
512
  }
644
- async closePullRequest(params) {
645
- return this.updatePullRequest({
646
- ...params,
647
- state: "closed"
648
- });
513
+ addAccount(
514
+ accounts,
515
+ legacyAccount(
516
+ runtime,
517
+ "user",
518
+ readSetting(runtime, "GITHUB_USER_ACCOUNT_ID") ?? DEFAULT_GITHUB_USER_ACCOUNT_ID,
519
+ "GITHUB_USER_PAT",
520
+ "ELIZA_E2E_GITHUB_USER_PAT"
521
+ )
522
+ );
523
+ addAccount(
524
+ accounts,
525
+ legacyAccount(
526
+ runtime,
527
+ "agent",
528
+ readSetting(runtime, "GITHUB_AGENT_ACCOUNT_ID") ?? DEFAULT_GITHUB_AGENT_ACCOUNT_ID,
529
+ "GITHUB_AGENT_PAT",
530
+ "ELIZA_E2E_GITHUB_AGENT_PAT"
531
+ )
532
+ );
533
+ return Array.from(accounts.values());
534
+ }
535
+ async function readGitHubAccountsWithConnectorCredentials(runtime) {
536
+ const accounts = /* @__PURE__ */ new Map();
537
+ for (const account of readGitHubAccounts(runtime)) {
538
+ accounts.set(account.accountId, account);
649
539
  }
650
- async createReview(params) {
651
- const client = this.getClient();
652
- const { owner, repo } = this.resolveRepoRef(params);
653
- const { data } = await client.pulls.createReview({
654
- owner,
655
- repo,
656
- pull_number: params.pullNumber,
657
- body: params.body,
658
- event: params.event,
659
- commit_id: params.commitId,
660
- comments: params.comments?.map((c) => ({
661
- path: c.path,
662
- line: c.line,
663
- body: c.body,
664
- side: c.side,
665
- start_line: c.startLine,
666
- start_side: c.startSide
667
- }))
668
- });
669
- return this.mapReview(data);
670
- }
671
- async listReviews(params) {
672
- const client = this.getClient();
673
- const { owner, repo } = this.resolveRepoRef(params);
674
- const { data } = await client.pulls.listReviews({
675
- owner,
676
- repo,
677
- pull_number: params.pullNumber
678
- });
679
- return data.map((r) => this.mapReview(r));
680
- }
681
- async createComment(params) {
682
- const client = this.getClient();
683
- const { owner, repo } = this.resolveRepoRef(params);
684
- const { data } = await client.issues.createComment({
685
- owner,
686
- repo,
687
- issue_number: params.issueNumber,
688
- body: params.body
689
- });
690
- return this.mapComment(data);
691
- }
692
- async listComments(params) {
693
- const client = this.getClient();
694
- const { owner, repo } = this.resolveRepoRef(params);
695
- const { data } = await client.issues.listComments({
696
- owner,
697
- repo,
698
- issue_number: params.issueNumber,
699
- per_page: params.perPage ?? 30,
700
- page: params.page ?? 1
540
+ const connectorAccounts = await listConnectorAccounts(
541
+ runtime,
542
+ GITHUB_SERVICE_TYPE
543
+ );
544
+ for (const account of connectorAccounts) {
545
+ if (account.status !== "connected") continue;
546
+ const token = await loadConnectorOAuthAccessToken({
547
+ runtime,
548
+ provider: GITHUB_SERVICE_TYPE,
549
+ accountId: account.id,
550
+ caller: "plugin-github"
701
551
  });
702
- return data.map((c) => this.mapComment(c));
703
- }
704
- async createBranch(params) {
705
- const client = this.getClient();
706
- const { owner, repo } = this.resolveRepoRef(params);
707
- let sha;
708
- if (params.fromRef.match(/^[0-9a-f]{40}$/i)) {
709
- sha = params.fromRef;
710
- } else {
711
- const { data: refData } = await client.git.getRef({
712
- owner,
713
- repo,
714
- ref: `heads/${params.fromRef}`
715
- });
716
- sha = refData.object.sha;
717
- }
718
- await client.git.createRef({
719
- owner,
720
- repo,
721
- ref: `refs/heads/${params.branchName}`,
722
- sha
552
+ if (!token) continue;
553
+ accounts.set(account.id, {
554
+ accountId: account.id,
555
+ role: connectorRoleToIdentity(account.role),
556
+ token,
557
+ label: account.label ?? account.displayHandle
723
558
  });
724
- return {
725
- name: params.branchName,
726
- sha,
727
- protected: false
728
- };
729
559
  }
730
- async deleteBranch(params) {
731
- const client = this.getClient();
732
- const { owner, repo } = this.resolveRepoRef(params);
733
- await client.git.deleteRef({
734
- owner,
735
- repo,
736
- ref: `heads/${params.branchName}`
737
- });
560
+ return Array.from(accounts.values());
561
+ }
562
+ function legacyAccount(runtime, role, accountId, primaryKey, fallbackKey) {
563
+ const token = readSetting(runtime, primaryKey) ?? readSetting(runtime, fallbackKey);
564
+ if (!token) return null;
565
+ return { accountId, role, token };
566
+ }
567
+ function connectorRoleToIdentity(role) {
568
+ return typeof role === "string" && role.toUpperCase() === "AGENT" ? "agent" : "user";
569
+ }
570
+ function resolveGitHubAccount(accounts, selection) {
571
+ if (selection.accountId) {
572
+ const exact = accounts.find(
573
+ (account) => account.accountId === selection.accountId
574
+ );
575
+ if (exact) return exact;
576
+ return null;
738
577
  }
739
- async listBranches(params) {
740
- const client = this.getClient();
741
- const { owner, repo } = this.resolveRepoRef(params);
742
- const { data } = await client.repos.listBranches({
743
- owner,
744
- repo,
745
- per_page: params.perPage ?? 30,
746
- page: params.page ?? 1
747
- });
748
- return data.map((b) => ({
749
- name: b.name,
750
- sha: b.commit.sha,
751
- protected: b.protected
752
- }));
753
- }
754
- async getFile(params) {
755
- const client = this.getClient();
756
- const { owner, repo } = this.resolveRepoRef(params);
757
- const { data } = await client.repos.getContent({
758
- owner,
759
- repo,
760
- path: params.path,
761
- ref: params.branch
762
- });
763
- if (Array.isArray(data)) {
764
- throw new FileNotFoundError(`${params.path} is a directory, not a file`, owner, repo);
765
- }
766
- if (data.type !== "file") {
767
- throw new FileNotFoundError(`${params.path} is not a file`, owner, repo);
768
- }
769
- let content = "";
770
- if ("content" in data && data.content) {
771
- content = Buffer.from(data.content, "base64").toString("utf-8");
772
- }
773
- return {
774
- name: data.name,
775
- path: data.path,
776
- content,
777
- sha: data.sha,
778
- size: data.size,
779
- type: data.type,
780
- encoding: "encoding" in data ? data.encoding ?? "base64" : "base64",
781
- htmlUrl: data.html_url ?? "",
782
- downloadUrl: data.download_url
783
- };
578
+ const legacyId = defaultGitHubAccountIdForRole(selection.role);
579
+ return accounts.find((account) => account.accountId === legacyId) ?? accounts.find((account) => account.role === selection.role) ?? null;
580
+ }
581
+
582
+ // src/action-helpers.ts
583
+ function getClient(runtime, selection) {
584
+ const service = runtime.getService(GITHUB_SERVICE_TYPE);
585
+ if (!service) {
586
+ return null;
784
587
  }
785
- async listDirectory(params) {
786
- const client = this.getClient();
787
- const { owner, repo } = this.resolveRepoRef(params);
788
- const { data } = await client.repos.getContent({
789
- owner,
790
- repo,
791
- path: params.path,
792
- ref: params.branch
793
- });
794
- if (!Array.isArray(data)) {
795
- throw new FileNotFoundError(`${params.path} is a file, not a directory`, owner, repo);
796
- }
797
- return data.map((entry) => ({
798
- name: entry.name,
799
- path: entry.path,
800
- sha: entry.sha,
801
- size: entry.size,
802
- type: entry.type,
803
- htmlUrl: entry.html_url ?? "",
804
- downloadUrl: entry.download_url
805
- }));
806
- }
807
- async createCommit(params) {
808
- const client = this.getClient();
809
- const { owner, repo } = this.resolveRepoRef(params);
810
- let parentSha = params.parentSha;
811
- if (!parentSha) {
812
- const { data: refData } = await client.git.getRef({
813
- owner,
814
- repo,
815
- ref: `heads/${params.branch}`
816
- });
817
- parentSha = refData.object.sha;
818
- }
819
- const { data: parentCommit } = await client.git.getCommit({
820
- owner,
821
- repo,
822
- commit_sha: parentSha
823
- });
824
- const treeItems = [];
825
- for (const file of params.files) {
826
- if (file.operation === "delete") {
827
- continue;
828
- }
829
- const { data: blob } = await client.git.createBlob({
830
- owner,
831
- repo,
832
- content: file.content,
833
- encoding: file.encoding ?? "utf-8"
834
- });
835
- treeItems.push({
836
- path: file.path,
837
- mode: "100644",
838
- type: "blob",
839
- sha: blob.sha
840
- });
841
- }
842
- const { data: newTree } = await client.git.createTree({
843
- owner,
844
- repo,
845
- base_tree: parentCommit.tree.sha,
846
- tree: treeItems
847
- });
848
- const { data: commit } = await client.git.createCommit({
849
- owner,
850
- repo,
851
- message: params.message,
852
- tree: newTree.sha,
853
- parents: [parentSha],
854
- author: params.authorName ? {
855
- name: params.authorName,
856
- email: params.authorEmail ?? `${params.authorName}@users.noreply.github.com`
857
- } : undefined
858
- });
859
- await client.git.updateRef({
860
- owner,
861
- repo,
862
- ref: `heads/${params.branch}`,
863
- sha: commit.sha
864
- });
865
- return {
866
- sha: commit.sha,
867
- message: commit.message,
868
- author: {
869
- name: commit.author?.name ?? "Unknown",
870
- email: commit.author?.email ?? "",
871
- date: commit.author?.date ?? new Date().toISOString()
872
- },
873
- committer: {
874
- name: commit.committer?.name ?? "Unknown",
875
- email: commit.committer?.email ?? "",
876
- date: commit.committer?.date ?? new Date().toISOString()
877
- },
878
- timestamp: commit.author?.date ?? new Date().toISOString(),
879
- htmlUrl: commit.html_url,
880
- parents: commit.parents.map((p) => p.sha)
881
- };
588
+ return service.getOctokit(selection);
589
+ }
590
+ function requireString(options, key) {
591
+ const v = options?.[key];
592
+ return typeof v === "string" && v.length > 0 ? v : null;
593
+ }
594
+ function requireNumber(options, key) {
595
+ const v = options?.[key];
596
+ if (typeof v === "number" && Number.isInteger(v)) {
597
+ return v;
882
598
  }
883
- async listCommits(params) {
884
- const client = this.getClient();
885
- const { owner, repo } = this.resolveRepoRef(params);
886
- const branch = params.branch ?? this._config?.branch ?? "main";
887
- const { data } = await client.repos.listCommits({
888
- owner,
889
- repo,
890
- sha: branch,
891
- path: params.path,
892
- per_page: params.perPage ?? 30,
893
- page: params.page ?? 1
894
- });
895
- return data.map((c) => ({
896
- sha: c.sha,
897
- message: c.commit.message,
898
- author: {
899
- name: c.commit.author?.name ?? "Unknown",
900
- email: c.commit.author?.email ?? "",
901
- date: c.commit.author?.date ?? ""
902
- },
903
- committer: {
904
- name: c.commit.committer?.name ?? "Unknown",
905
- email: c.commit.committer?.email ?? "",
906
- date: c.commit.committer?.date ?? ""
907
- },
908
- timestamp: c.commit.author?.date ?? "",
909
- htmlUrl: c.html_url,
910
- parents: c.parents.map((p) => p.sha)
911
- }));
912
- }
913
- async getAuthenticatedUser() {
914
- const client = this.getClient();
915
- const { data } = await client.users.getAuthenticated();
916
- return this.mapUser(data);
917
- }
918
- async getUser(username) {
919
- const client = this.getClient();
920
- const { data } = await client.users.getByUsername({ username });
921
- return this.mapUser(data);
922
- }
923
- resolveRepoRef(params) {
924
- const owner = params.owner ?? this._config?.owner;
925
- const repo = params.repo ?? this._config?.repo;
926
- if (!owner || !repo) {
927
- throw new Error("Repository owner and name are required. Configure defaults or provide them explicitly.");
599
+ if (typeof v === "string" && /^\d+$/.test(v)) {
600
+ return Number(v);
601
+ }
602
+ return null;
603
+ }
604
+ function requireStringArray(options, key) {
605
+ const v = options?.[key];
606
+ if (!Array.isArray(v)) {
607
+ return null;
608
+ }
609
+ const result = [];
610
+ for (const item of v) {
611
+ if (typeof item !== "string" || item.length === 0) {
612
+ return null;
928
613
  }
929
- return { owner, repo };
614
+ result.push(item);
930
615
  }
931
- mapRepository(data) {
932
- const d = data;
933
- const license = d.license;
934
- return {
935
- id: d.id,
936
- name: d.name,
937
- fullName: d.full_name,
938
- owner: this.mapUser(d.owner),
939
- description: d.description,
940
- private: d.private,
941
- fork: d.fork,
942
- defaultBranch: d.default_branch,
943
- language: d.language,
944
- stargazersCount: d.stargazers_count,
945
- forksCount: d.forks_count,
946
- openIssuesCount: d.open_issues_count,
947
- watchersCount: d.watchers_count,
948
- htmlUrl: d.html_url,
949
- cloneUrl: d.clone_url,
950
- sshUrl: d.ssh_url,
951
- createdAt: d.created_at,
952
- updatedAt: d.updated_at,
953
- pushedAt: d.pushed_at,
954
- topics: d.topics ?? [],
955
- license: license ? {
956
- key: license.key,
957
- name: license.name,
958
- spdxId: license.spdx_id,
959
- url: license.url
960
- } : null
961
- };
616
+ return result;
617
+ }
618
+ function optionalStringArray(options, key) {
619
+ const v = options?.[key];
620
+ if (v === void 0) {
621
+ return void 0;
962
622
  }
963
- mapUser(data) {
964
- const d = data;
965
- return {
966
- id: d.id,
967
- login: d.login,
968
- name: d.name ?? null,
969
- avatarUrl: d.avatar_url,
970
- htmlUrl: d.html_url,
971
- type: d.type
972
- };
623
+ return requireStringArray(options, key) ?? void 0;
624
+ }
625
+ function splitRepo(repo) {
626
+ const parts = repo.split("/");
627
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
628
+ return null;
973
629
  }
974
- mapIssue(data) {
975
- const d = data;
976
- return {
977
- number: d.number,
978
- title: d.title,
979
- body: d.body,
980
- state: d.state,
981
- stateReason: d.state_reason ?? null,
982
- user: this.mapUser(d.user),
983
- assignees: (d.assignees ?? []).map((a) => this.mapUser(a)),
984
- labels: (d.labels ?? []).map((l) => this.mapLabel(l)),
985
- milestone: d.milestone ? this.mapMilestone(d.milestone) : null,
986
- createdAt: d.created_at,
987
- updatedAt: d.updated_at,
988
- closedAt: d.closed_at,
989
- htmlUrl: d.html_url,
990
- comments: d.comments,
991
- isPullRequest: !!d.pull_request
992
- };
630
+ return { owner: parts[0], name: parts[1] };
631
+ }
632
+ function isConfirmed(options) {
633
+ return options?.confirmed === true;
634
+ }
635
+ function needsClientError(selection) {
636
+ const accountSuffix = selection.accountId ? ` accountId "${selection.accountId}"` : ` ${selection.role} account`;
637
+ return `GitHub${accountSuffix} token not configured (set GITHUB_ACCOUNTS or ${selection.role === "user" ? "GITHUB_USER_PAT" : "GITHUB_AGENT_PAT"})`;
638
+ }
639
+ function getServiceOrNull(runtime) {
640
+ return runtime.getService(GITHUB_SERVICE_TYPE);
641
+ }
642
+ function buildResolvedClient(runtime, selection) {
643
+ if (!getServiceOrNull(runtime)) {
644
+ return { error: "GitHub service not available" };
993
645
  }
994
- mapLabel(data) {
995
- if (typeof data === "string") {
996
- return {
997
- id: 0,
998
- name: data,
999
- color: "",
1000
- description: null,
1001
- default: false
1002
- };
1003
- }
1004
- const d = data;
1005
- return {
1006
- id: d.id,
1007
- name: d.name,
1008
- color: d.color,
1009
- description: d.description,
1010
- default: d.default
1011
- };
646
+ const resolvedSelection = typeof selection === "string" ? { role: selection } : selection;
647
+ const client = getClient(runtime, resolvedSelection);
648
+ if (!client) {
649
+ return { error: needsClientError(resolvedSelection) };
1012
650
  }
1013
- mapMilestone(data) {
1014
- const d = data;
1015
- return {
1016
- number: d.number,
1017
- title: d.title,
1018
- description: d.description,
1019
- state: d.state,
1020
- dueOn: d.due_on,
1021
- createdAt: d.created_at,
1022
- updatedAt: d.updated_at,
1023
- closedAt: d.closed_at,
1024
- openIssues: d.open_issues,
1025
- closedIssues: d.closed_issues
1026
- };
651
+ return {
652
+ client,
653
+ identity: resolvedSelection.role,
654
+ accountId: resolvedSelection.accountId
655
+ };
656
+ }
657
+ function resolveAccountSelection(options, defaultIdentity) {
658
+ return resolveGitHubAccountSelection(options, defaultIdentity);
659
+ }
660
+ function describeSelection(selection) {
661
+ return selection.accountId ? `${selection.role} (${selection.accountId})` : selection.role;
662
+ }
663
+
664
+ // src/rate-limit.ts
665
+ function toErrorLike(value) {
666
+ if (typeof value !== "object" || value === null) {
667
+ return { message: String(value) };
1027
668
  }
1028
- mapPullRequest(data) {
1029
- const d = data;
1030
- const head = d.head;
1031
- const base = d.base;
1032
- const headRepo = head.repo;
1033
- const baseRepo = base.repo;
1034
- const headRepoOwner = headRepo?.owner;
1035
- const baseRepoOwner = baseRepo?.owner;
1036
- return {
1037
- number: d.number,
1038
- title: d.title,
1039
- body: d.body,
1040
- state: d.state,
1041
- draft: d.draft ?? false,
1042
- merged: d.merged ?? false,
1043
- mergeable: d.mergeable,
1044
- mergeableState: d.mergeable_state ?? "unknown",
1045
- user: this.mapUser(d.user),
1046
- head: {
1047
- ref: head.ref,
1048
- label: head.label,
1049
- sha: head.sha,
1050
- repo: headRepo ? {
1051
- owner: headRepoOwner?.login,
1052
- repo: headRepo.name
1053
- } : null
1054
- },
1055
- base: {
1056
- ref: base.ref,
1057
- label: base.label,
1058
- sha: base.sha,
1059
- repo: baseRepo ? {
1060
- owner: baseRepoOwner?.login,
1061
- repo: baseRepo.name
1062
- } : null
1063
- },
1064
- assignees: (d.assignees ?? []).map((a) => this.mapUser(a)),
1065
- requestedReviewers: (d.requested_reviewers ?? []).map((r) => this.mapUser(r)),
1066
- labels: (d.labels ?? []).map((l) => this.mapLabel(l)),
1067
- milestone: d.milestone ? this.mapMilestone(d.milestone) : null,
1068
- createdAt: d.created_at,
1069
- updatedAt: d.updated_at,
1070
- closedAt: d.closed_at,
1071
- mergedAt: d.merged_at,
1072
- htmlUrl: d.html_url,
1073
- commits: d.commits ?? 0,
1074
- additions: d.additions ?? 0,
1075
- deletions: d.deletions ?? 0,
1076
- changedFiles: d.changed_files ?? 0
1077
- };
669
+ return value;
670
+ }
671
+ function headerNumber(headers, name) {
672
+ if (!headers) {
673
+ return null;
1078
674
  }
1079
- mapReview(data) {
1080
- const d = data;
1081
- return {
1082
- id: d.id,
1083
- user: this.mapUser(d.user),
1084
- body: d.body,
1085
- state: d.state,
1086
- commitId: d.commit_id,
1087
- htmlUrl: d.html_url,
1088
- submittedAt: d.submitted_at
1089
- };
675
+ const raw = headers[name] ?? headers[name.toLowerCase()];
676
+ if (raw === void 0) {
677
+ return null;
1090
678
  }
1091
- mapComment(data) {
1092
- const d = data;
1093
- return {
1094
- id: d.id,
1095
- body: d.body,
1096
- user: this.mapUser(d.user),
1097
- createdAt: d.created_at,
1098
- updatedAt: d.updated_at,
1099
- htmlUrl: d.html_url
1100
- };
679
+ const num = typeof raw === "number" ? raw : Number(raw);
680
+ return Number.isFinite(num) ? num : null;
681
+ }
682
+ function inspectRateLimit(err) {
683
+ const e = toErrorLike(err);
684
+ const headers = e.response?.headers;
685
+ const remaining = headerNumber(headers, "x-ratelimit-remaining");
686
+ const resetSeconds = headerNumber(headers, "x-ratelimit-reset");
687
+ const isRateLimited = e.status === 403 && remaining === 0;
688
+ return {
689
+ isRateLimited,
690
+ remaining,
691
+ resetAtMs: resetSeconds === null ? null : resetSeconds * 1e3
692
+ };
693
+ }
694
+ function formatRateLimitMessage(details) {
695
+ if (!details.isRateLimited) {
696
+ return "GitHub request failed";
697
+ }
698
+ if (details.resetAtMs === null) {
699
+ return "GitHub rate limit exhausted";
700
+ }
701
+ const reset = new Date(details.resetAtMs).toISOString();
702
+ return `GitHub rate limit exhausted; resets at ${reset}`;
703
+ }
704
+ function errorMessage(err) {
705
+ if (err instanceof Error) {
706
+ return err.message;
707
+ }
708
+ if (typeof err === "string") {
709
+ return err;
1101
710
  }
711
+ const e = toErrorLike(err);
712
+ return e.message ?? "unknown error";
1102
713
  }
1103
714
 
1104
- // actions/createBranch.ts
1105
- var spec = requireActionSpec("CREATE_BRANCH");
1106
- var examples = [
1107
- [
1108
- {
1109
- name: "{{user1}}",
1110
- content: {
1111
- text: "Create a branch called feature/new-feature from main"
1112
- }
1113
- },
1114
- {
1115
- name: "{{agent}}",
1116
- content: {
1117
- text: "I'll create the feature/new-feature branch from main.",
1118
- actions: ["CREATE_GITHUB_BRANCH"]
1119
- }
1120
- }
1121
- ],
1122
- [
1123
- {
1124
- name: "{{user1}}",
1125
- content: {
1126
- text: "Make a new branch fix/bug-123 based on develop"
1127
- }
1128
- },
715
+ // src/actions/issue-op.ts
716
+ var SUPPORTED_OPS = /* @__PURE__ */ new Set([
717
+ "create",
718
+ "assign",
719
+ "close",
720
+ "reopen",
721
+ "comment",
722
+ "label"
723
+ ]);
724
+ function parseOp(value) {
725
+ if (typeof value !== "string") return null;
726
+ return SUPPORTED_OPS.has(value) ? value : null;
727
+ }
728
+ function describeOp(op) {
729
+ switch (op) {
730
+ case "create":
731
+ return "create";
732
+ case "assign":
733
+ return "assign";
734
+ case "close":
735
+ return "close";
736
+ case "reopen":
737
+ return "reopen";
738
+ case "comment":
739
+ return "comment on";
740
+ case "label":
741
+ return "label";
742
+ }
743
+ }
744
+ async function runCreate(resolved, parts, repo, options, callback) {
745
+ const title = requireString(options, "title");
746
+ const body = requireString(options, "body");
747
+ const labels = optionalStringArray(options, "labels");
748
+ const assignees = optionalStringArray(options, "assignees");
749
+ if (!title) {
750
+ const err = "GITHUB_ISSUE_OP create requires title";
751
+ await callback?.({ text: err });
752
+ return { success: false, error: err };
753
+ }
754
+ const resp = await resolved.client.issues.create({
755
+ owner: parts.owner,
756
+ repo: parts.name,
757
+ title,
758
+ body: body ?? void 0,
759
+ labels,
760
+ assignees
761
+ });
762
+ await callback?.({
763
+ text: `Created issue ${repo}#${resp.data.number}: ${resp.data.html_url}`
764
+ });
765
+ return {
766
+ success: true,
767
+ data: {
768
+ op: "create",
769
+ number: resp.data.number,
770
+ url: resp.data.html_url
771
+ }
772
+ };
773
+ }
774
+ async function runAssign(resolved, parts, repo, options, callback) {
775
+ const number = requireNumber(options, "number");
776
+ const assignees = requireStringArray(options, "assignees");
777
+ if (!number || !assignees || assignees.length === 0) {
778
+ const err = "GITHUB_ISSUE_OP assign requires number (integer) and assignees (non-empty string[])";
779
+ await callback?.({ text: err });
780
+ return { success: false, error: err };
781
+ }
782
+ const resp = await resolved.client.issues.addAssignees({
783
+ owner: parts.owner,
784
+ repo: parts.name,
785
+ issue_number: number,
786
+ assignees
787
+ });
788
+ const actual = (resp.data.assignees ?? []).map((a) => a?.login).filter((x) => typeof x === "string");
789
+ await callback?.({
790
+ text: `Assigned [${actual.join(", ")}] to ${repo}#${number}`
791
+ });
792
+ return {
793
+ success: true,
794
+ data: { op: "assign", number, assignees: actual }
795
+ };
796
+ }
797
+ async function runStateChange(resolved, parts, repo, options, callback, target) {
798
+ const number = requireNumber(options, "number");
799
+ if (!number) {
800
+ const err = `GITHUB_ISSUE_OP ${target === "closed" ? "close" : "reopen"} requires number (integer)`;
801
+ await callback?.({ text: err });
802
+ return { success: false, error: err };
803
+ }
804
+ const resp = await resolved.client.issues.update({
805
+ owner: parts.owner,
806
+ repo: parts.name,
807
+ issue_number: number,
808
+ state: target
809
+ });
810
+ const verb = target === "closed" ? "Closed" : "Reopened";
811
+ await callback?.({ text: `${verb} ${repo}#${number}: ${resp.data.title}` });
812
+ return {
813
+ success: true,
814
+ data: {
815
+ op: target === "closed" ? "close" : "reopen",
816
+ number,
817
+ title: resp.data.title
818
+ }
819
+ };
820
+ }
821
+ async function runComment(resolved, parts, repo, options, callback) {
822
+ const number = requireNumber(options, "number");
823
+ const body = requireString(options, "body");
824
+ if (!number || !body) {
825
+ const err = "GITHUB_ISSUE_OP comment requires number (integer) and body";
826
+ await callback?.({ text: err });
827
+ return { success: false, error: err };
828
+ }
829
+ const resp = await resolved.client.issues.createComment({
830
+ owner: parts.owner,
831
+ repo: parts.name,
832
+ issue_number: number,
833
+ body
834
+ });
835
+ await callback?.({
836
+ text: `Commented on ${repo}#${number}: ${resp.data.html_url}`
837
+ });
838
+ return {
839
+ success: true,
840
+ data: {
841
+ op: "comment",
842
+ number,
843
+ commentId: resp.data.id,
844
+ url: resp.data.html_url
845
+ }
846
+ };
847
+ }
848
+ async function runLabel(resolved, parts, repo, options, callback) {
849
+ const number = requireNumber(options, "number");
850
+ const labels = requireStringArray(options, "labels");
851
+ if (!number || !labels || labels.length === 0) {
852
+ const err = "GITHUB_ISSUE_OP label requires number (integer) and labels (non-empty string[])";
853
+ await callback?.({ text: err });
854
+ return { success: false, error: err };
855
+ }
856
+ const resp = await resolved.client.issues.addLabels({
857
+ owner: parts.owner,
858
+ repo: parts.name,
859
+ issue_number: number,
860
+ labels
861
+ });
862
+ const applied = (resp.data ?? []).map((label) => typeof label === "string" ? label : label?.name ?? null).filter((x) => typeof x === "string");
863
+ await callback?.({
864
+ text: `Applied labels [${applied.join(", ")}] to ${repo}#${number}`
865
+ });
866
+ return {
867
+ success: true,
868
+ data: { op: "label", number, labels: applied }
869
+ };
870
+ }
871
+ function buildPreview(op, repo, identity, options) {
872
+ const number = requireNumber(options, "number");
873
+ const title = requireString(options, "title");
874
+ const body = requireString(options, "body");
875
+ const assignees = optionalStringArray(options, "assignees");
876
+ const labels = optionalStringArray(options, "labels");
877
+ const head = `About to ${describeOp(op)} ${repo}`;
878
+ const target = number ? `#${number}` : "";
879
+ const detail = (() => {
880
+ switch (op) {
881
+ case "create":
882
+ return ` issue: "${title ?? "(no title)"}"${labels ? ` [labels: ${labels.join(", ")}]` : ""}${assignees ? ` [assignees: ${assignees.join(", ")}]` : ""}`;
883
+ case "assign":
884
+ return ` with [${assignees?.join(", ") ?? ""}]`;
885
+ case "label":
886
+ return ` with [${labels?.join(", ") ?? ""}]`;
887
+ case "comment":
888
+ return body ? ` body: "${body.slice(0, 120)}"` : "";
889
+ default:
890
+ return "";
891
+ }
892
+ })();
893
+ return `${head}${target}${detail} as ${identity}. Re-invoke with confirmed: true to proceed.`;
894
+ }
895
+ var issueOpAction = {
896
+ name: GitHubActions.GITHUB_ISSUE_OP,
897
+ contexts: ["code", "tasks", "connectors", "automation"],
898
+ contextGate: { anyOf: ["code", "tasks", "connectors", "automation"] },
899
+ roleGate: { minRole: "USER" },
900
+ similes: [
901
+ "CREATE_ISSUE",
902
+ "OPEN_ISSUE",
903
+ "FILE_ISSUE",
904
+ "GITHUB_CREATE_ISSUE",
905
+ "ASSIGN_ISSUE",
906
+ "ASSIGN_GITHUB_ISSUE",
907
+ "ADD_ASSIGNEE",
908
+ "CLOSE_ISSUE",
909
+ "REOPEN_ISSUE",
910
+ "COMMENT_ISSUE",
911
+ "ADD_ISSUE_COMMENT",
912
+ "LABEL_ISSUE",
913
+ "ADD_ISSUE_LABEL",
914
+ "MANAGE_ISSUES"
915
+ ],
916
+ description: "Single router for GitHub issue ops: create, assign, close, reopen, comment, label. Requires confirmed:true.",
917
+ descriptionCompressed: "GitHub issue ops: create, assign, close, reopen, comment, label.",
918
+ parameters: [
1129
919
  {
1130
- name: "{{agent}}",
1131
- content: {
1132
- text: "Creating branch fix/bug-123 from develop.",
1133
- actions: ["CREATE_GITHUB_BRANCH"]
1134
- }
1135
- }
1136
- ]
1137
- ];
1138
- var createBranchAction = {
1139
- name: "CREATE_GITHUB_BRANCH",
1140
- similes: spec.similes ? [...spec.similes] : [],
1141
- description: spec.description,
1142
- validate: async (runtime, message, state, options) => {
1143
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
1144
- const __avText = __avTextRaw.toLowerCase();
1145
- const __avKeywords = ["create", "github", "branch"];
1146
- const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
1147
- const __avRegex = /\b(?:create|github|branch)\b/i;
1148
- const __avRegexOk = Boolean(__avText.match(__avRegex));
1149
- const __avSource = String(message?.content?.source ?? message?.source ?? "");
1150
- const __avExpectedSource = "";
1151
- const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
1152
- const __avOptions = options && typeof options === "object" ? options : {};
1153
- const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
1154
- if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
1155
- return false;
1156
- }
1157
- const __avLegacyValidate = async (runtime2, message2, _state) => {
1158
- const service = runtime2.getService(GITHUB_SERVICE_NAME);
1159
- if (!service) {
1160
- return false;
1161
- }
1162
- const text = message2.content.text?.toLowerCase() ?? "";
1163
- return text.includes("branch") || text.includes("fork") || text.includes("checkout");
1164
- };
1165
- try {
1166
- return Boolean(await __avLegacyValidate(runtime, message, state, options));
1167
- } catch {
1168
- return false;
1169
- }
1170
- },
1171
- handler: async (runtime, _message, state, _options, callback) => {
1172
- const service = runtime.getService(GITHUB_SERVICE_NAME);
1173
- if (!service) {
1174
- logger2.error("GitHub service not available");
1175
- if (callback) {
1176
- await callback({
1177
- text: "GitHub service is not available. Please ensure the plugin is properly configured."
1178
- });
1179
- }
1180
- return { success: false };
1181
- }
1182
- try {
1183
- const params = {
1184
- owner: state?.owner ?? service.getConfig().owner ?? "",
1185
- repo: state?.repo ?? service.getConfig().repo ?? "",
1186
- branchName: state?.branchName ?? "",
1187
- fromRef: state?.fromRef ?? service.getConfig().branch ?? "main"
1188
- };
1189
- const validation = createBranchSchema.safeParse(params);
1190
- if (!validation.success) {
1191
- const errors = formatZodErrors(validation.error);
1192
- logger2.error(`Invalid branch parameters: ${errors}`);
1193
- if (callback) {
1194
- await callback({
1195
- text: `I couldn't create the branch due to missing information: ${errors}`
1196
- });
1197
- }
1198
- return { success: false };
1199
- }
1200
- const branch = await service.createBranch(params);
1201
- logger2.info(`Created branch ${branch.name} from ${params.fromRef}`);
1202
- if (callback) {
1203
- await callback({
1204
- text: `Created branch "${branch.name}" from ${params.fromRef}.
1205
-
1206
- Latest commit: ${branch.sha.slice(0, 7)}`
1207
- });
1208
- }
1209
- return { success: true };
1210
- } catch (error) {
1211
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1212
- logger2.error(`Failed to create branch: ${errorMessage}`);
1213
- if (callback) {
1214
- await callback({
1215
- text: `Failed to create the branch: ${errorMessage}`
1216
- });
1217
- }
1218
- return { success: false };
1219
- }
1220
- },
1221
- examples
1222
- };
1223
- // actions/createComment.ts
1224
- import {
1225
- logger as logger3
1226
- } from "@elizaos/core";
1227
- var spec2 = requireActionSpec("CREATE_COMMENT");
1228
- var examples2 = [
1229
- [
920
+ name: "subaction",
921
+ description: "Issue operation: create, assign, close, reopen, comment, or label.",
922
+ required: true,
923
+ schema: { type: "string", enum: [...SUPPORTED_OPS] }
924
+ },
1230
925
  {
1231
- name: "{{user1}}",
1232
- content: {
1233
- text: "Comment on issue #42 saying 'I'll take a look at this today'"
1234
- }
926
+ name: "repo",
927
+ description: "Repository in owner/name form.",
928
+ required: true,
929
+ schema: { type: "string" }
1235
930
  },
1236
931
  {
1237
- name: "{{agent}}",
1238
- content: {
1239
- text: "I'll add that comment to issue #42.",
1240
- actions: ["CREATE_GITHUB_COMMENT"]
1241
- }
1242
- }
1243
- ],
1244
- [
932
+ name: "number",
933
+ description: "Issue number for existing-issue operations.",
934
+ required: false,
935
+ schema: { type: "number" }
936
+ },
1245
937
  {
1246
- name: "{{user1}}",
1247
- content: {
1248
- text: "Reply to PR #15 with 'Thanks for the fix!'"
1249
- }
938
+ name: "title",
939
+ description: "Issue title for create.",
940
+ required: false,
941
+ schema: { type: "string" }
1250
942
  },
1251
943
  {
1252
- name: "{{agent}}",
1253
- content: {
1254
- text: "Adding your comment to pull request #15.",
1255
- actions: ["CREATE_GITHUB_COMMENT"]
1256
- }
1257
- }
1258
- ]
1259
- ];
1260
- var createCommentAction = {
1261
- name: "CREATE_GITHUB_COMMENT",
1262
- similes: spec2.similes ? [...spec2.similes] : [],
1263
- description: spec2.description,
1264
- validate: async (runtime, message, state, options) => {
1265
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
1266
- const __avText = __avTextRaw.toLowerCase();
1267
- const __avKeywords = ["create", "github", "comment"];
1268
- const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
1269
- const __avRegex = /\b(?:create|github|comment)\b/i;
1270
- const __avRegexOk = Boolean(__avText.match(__avRegex));
1271
- const __avSource = String(message?.content?.source ?? message?.source ?? "");
1272
- const __avExpectedSource = "";
1273
- const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
1274
- const __avOptions = options && typeof options === "object" ? options : {};
1275
- const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
1276
- if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
1277
- return false;
1278
- }
1279
- const __avLegacyValidate = async (runtime2, message2, _state) => {
1280
- const service = runtime2.getService(GITHUB_SERVICE_NAME);
1281
- if (!service) {
1282
- return false;
1283
- }
1284
- const text = message2.content.text?.toLowerCase() ?? "";
1285
- return text.includes("comment") || text.includes("reply") || text.includes("respond");
1286
- };
1287
- try {
1288
- return Boolean(await __avLegacyValidate(runtime, message, state, options));
1289
- } catch {
1290
- return false;
1291
- }
1292
- },
1293
- handler: async (runtime, message, state, _options, callback) => {
1294
- const service = runtime.getService(GITHUB_SERVICE_NAME);
1295
- if (!service) {
1296
- logger3.error("GitHub service not available");
1297
- if (callback) {
1298
- await callback({
1299
- text: "GitHub service is not available. Please ensure the plugin is properly configured."
1300
- });
1301
- }
1302
- return { success: false };
1303
- }
1304
- try {
1305
- const content = message.content;
1306
- const text = content.text ?? "";
1307
- const params = {
1308
- owner: state?.owner ?? service.getConfig().owner ?? "",
1309
- repo: state?.repo ?? service.getConfig().repo ?? "",
1310
- issueNumber: state?.issueNumber ?? 0,
1311
- body: state?.body ?? text
1312
- };
1313
- const validation = createCommentSchema.safeParse(params);
1314
- if (!validation.success) {
1315
- const errors = formatZodErrors(validation.error);
1316
- logger3.error(`Invalid comment parameters: ${errors}`);
1317
- if (callback) {
1318
- await callback({
1319
- text: `I couldn't create the comment due to missing information: ${errors}`
1320
- });
1321
- }
1322
- return { success: false };
1323
- }
1324
- const comment = await service.createComment(params);
1325
- logger3.info(`Created comment on #${params.issueNumber}`);
1326
- if (callback) {
1327
- await callback({
1328
- text: `Added comment to #${params.issueNumber}.
1329
-
1330
- View it at: ${comment.htmlUrl}`
1331
- });
1332
- }
1333
- return { success: true };
1334
- } catch (error) {
1335
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1336
- logger3.error(`Failed to create comment: ${errorMessage}`);
1337
- if (callback) {
1338
- await callback({
1339
- text: `Failed to create the comment: ${errorMessage}`
1340
- });
1341
- }
1342
- return { success: false };
1343
- }
1344
- },
1345
- examples: examples2
1346
- };
1347
- // actions/createIssue.ts
1348
- import {
1349
- logger as logger4
1350
- } from "@elizaos/core";
1351
- var spec3 = requireActionSpec("CREATE_ISSUE");
1352
- var examples3 = [
1353
- [
944
+ name: "body",
945
+ description: "Issue body or comment body.",
946
+ required: false,
947
+ schema: { type: "string" }
948
+ },
1354
949
  {
1355
- name: "{{user1}}",
1356
- content: {
1357
- text: "Create an issue in my-org/my-repo with title 'Bug: Login fails' and body 'Users cannot log in after update'"
1358
- }
950
+ name: "assignees",
951
+ description: "GitHub usernames to assign.",
952
+ required: false,
953
+ schema: { type: "array", items: { type: "string" } }
1359
954
  },
1360
955
  {
1361
- name: "{{agent}}",
1362
- content: {
1363
- text: "I'll create that issue for you in my-org/my-repo.",
1364
- actions: ["CREATE_GITHUB_ISSUE"]
1365
- }
1366
- }
1367
- ],
1368
- [
956
+ name: "labels",
957
+ description: "Labels to apply on create or label.",
958
+ required: false,
959
+ schema: { type: "array", items: { type: "string" } }
960
+ },
1369
961
  {
1370
- name: "{{user1}}",
1371
- content: {
1372
- text: "Open a new issue titled 'Add dark mode support' with labels 'enhancement' and 'ui'"
1373
- }
962
+ name: "as",
963
+ description: "Identity to use: agent or user.",
964
+ required: false,
965
+ schema: { type: "string", enum: ["agent", "user"], default: "agent" }
1374
966
  },
1375
967
  {
1376
- name: "{{agent}}",
1377
- content: {
1378
- text: "Creating a new issue with the title 'Add dark mode support' and the specified labels.",
1379
- actions: ["CREATE_GITHUB_ISSUE"]
1380
- }
968
+ name: "accountId",
969
+ description: "Optional GitHub account id from GITHUB_ACCOUNTS. Defaults by role.",
970
+ required: false,
971
+ schema: { type: "string" }
972
+ },
973
+ {
974
+ name: "confirmed",
975
+ description: "Must be true to perform the write operation.",
976
+ required: false,
977
+ schema: { type: "boolean", default: false }
1381
978
  }
1382
- ]
1383
- ];
1384
- var createIssueAction = {
1385
- name: "CREATE_GITHUB_ISSUE",
1386
- similes: spec3.similes ? [...spec3.similes] : [],
1387
- description: spec3.description,
1388
- validate: async (runtime, message, state, options) => {
1389
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
1390
- const __avText = __avTextRaw.toLowerCase();
1391
- const __avKeywords = ["create", "github", "issue"];
1392
- const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
1393
- const __avRegex = /\b(?:create|github|issue)\b/i;
1394
- const __avRegexOk = Boolean(__avText.match(__avRegex));
1395
- const __avSource = String(message?.content?.source ?? message?.source ?? "");
1396
- const __avExpectedSource = "";
1397
- const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
1398
- const __avOptions = options && typeof options === "object" ? options : {};
1399
- const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
1400
- if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
1401
- return false;
979
+ ],
980
+ validate: async (runtime, _message) => {
981
+ const r = buildResolvedClient(runtime, "agent");
982
+ return !("error" in r);
983
+ },
984
+ handler: async (runtime, _message, _state, options, callback) => {
985
+ const op = parseOp(options?.op);
986
+ if (!op) {
987
+ const err = "GITHUB_ISSUE_OP requires op (create|assign|close|reopen|comment|label)";
988
+ await callback?.({ text: err });
989
+ return { success: false, error: err };
990
+ }
991
+ const selection = resolveAccountSelection(options, "agent");
992
+ const repo = requireString(options, "repo");
993
+ if (!repo) {
994
+ const err = "GITHUB_ISSUE_OP requires repo (owner/name)";
995
+ await callback?.({ text: err });
996
+ return { success: false, error: err };
997
+ }
998
+ const parts = splitRepo(repo);
999
+ if (!parts) {
1000
+ const err = `Invalid repo "${repo}" \u2014 expected "owner/name"`;
1001
+ await callback?.({ text: err });
1002
+ return { success: false, error: err };
1003
+ }
1004
+ if (!isConfirmed(options)) {
1005
+ const preview = buildPreview(op, repo, describeSelection(selection), options);
1006
+ await callback?.({ text: preview });
1007
+ return { success: false, requiresConfirmation: true, preview };
1008
+ }
1009
+ const resolved = buildResolvedClient(runtime, selection);
1010
+ if ("error" in resolved) {
1011
+ await callback?.({ text: resolved.error });
1012
+ return { success: false, error: resolved.error };
1402
1013
  }
1403
- const __avLegacyValidate = async (runtime2, message2, _state) => {
1404
- const service = runtime2.getService(GITHUB_SERVICE_NAME);
1405
- if (!service) {
1406
- return false;
1407
- }
1408
- const text = message2.content.text?.toLowerCase() ?? "";
1409
- return text.includes("issue") || text.includes("bug") || text.includes("report") || text.includes("ticket");
1410
- };
1411
1014
  try {
1412
- return Boolean(await __avLegacyValidate(runtime, message, state, options));
1413
- } catch {
1414
- return false;
1015
+ switch (op) {
1016
+ case "create":
1017
+ return await runCreate(resolved, parts, repo, options, callback);
1018
+ case "assign":
1019
+ return await runAssign(resolved, parts, repo, options, callback);
1020
+ case "close":
1021
+ return await runStateChange(
1022
+ resolved,
1023
+ parts,
1024
+ repo,
1025
+ options,
1026
+ callback,
1027
+ "closed"
1028
+ );
1029
+ case "reopen":
1030
+ return await runStateChange(
1031
+ resolved,
1032
+ parts,
1033
+ repo,
1034
+ options,
1035
+ callback,
1036
+ "open"
1037
+ );
1038
+ case "comment":
1039
+ return await runComment(resolved, parts, repo, options, callback);
1040
+ case "label":
1041
+ return await runLabel(resolved, parts, repo, options, callback);
1042
+ }
1043
+ } catch (err) {
1044
+ const rl = inspectRateLimit(err);
1045
+ const message = rl.isRateLimited ? formatRateLimitMessage(rl) : `GITHUB_ISSUE_OP ${op} failed: ${errorMessage(err)}`;
1046
+ logger.warn({ message }, "[GitHub:GITHUB_ISSUE_OP]");
1047
+ await callback?.({ text: message });
1048
+ return { success: false, error: message };
1415
1049
  }
1416
1050
  },
1417
- handler: async (runtime, message, state, _options, callback) => {
1418
- const service = runtime.getService(GITHUB_SERVICE_NAME);
1419
- if (!service) {
1420
- logger4.error("GitHub service not available");
1421
- if (callback) {
1422
- await callback({
1423
- text: "GitHub service is not available. Please ensure the plugin is properly configured."
1424
- });
1051
+ examples: [
1052
+ [
1053
+ {
1054
+ name: "{{user1}}",
1055
+ content: {
1056
+ text: "Open an issue in elizaOS/eliza titled 'Docs gap'"
1057
+ }
1058
+ },
1059
+ {
1060
+ name: "{{agentName}}",
1061
+ content: {
1062
+ text: "Created issue elizaOS/eliza#101"
1063
+ }
1425
1064
  }
1426
- return { success: false };
1427
- }
1428
- try {
1429
- const content = message.content;
1430
- const text = content.text ?? "";
1431
- const params = {
1432
- owner: state?.owner ?? service.getConfig().owner ?? "",
1433
- repo: state?.repo ?? service.getConfig().repo ?? "",
1434
- title: state?.title ?? text.slice(0, 100),
1435
- body: state?.body ?? text,
1436
- labels: state?.labels ?? [],
1437
- assignees: state?.assignees ?? []
1438
- };
1439
- const validation = createIssueSchema.safeParse(params);
1440
- if (!validation.success) {
1441
- const errors = formatZodErrors(validation.error);
1442
- logger4.error(`Invalid issue parameters: ${errors}`);
1443
- if (callback) {
1444
- await callback({
1445
- text: `I couldn't create the issue due to missing information: ${errors}`
1446
- });
1065
+ ],
1066
+ [
1067
+ {
1068
+ name: "{{user1}}",
1069
+ content: {
1070
+ text: "Close issue elizaOS/eliza#42"
1071
+ }
1072
+ },
1073
+ {
1074
+ name: "{{agentName}}",
1075
+ content: {
1076
+ text: "Closed elizaOS/eliza#42"
1447
1077
  }
1448
- return { success: false };
1449
1078
  }
1450
- const issue = await service.createIssue(params);
1451
- logger4.info(`Created issue #${issue.number}: ${issue.title}`);
1452
- if (callback) {
1453
- await callback({
1454
- text: `Created issue #${issue.number}: "${issue.title}"
1079
+ ]
1080
+ ]
1081
+ };
1455
1082
 
1456
- View it at: ${issue.htmlUrl}`
1457
- });
1458
- }
1459
- return { success: true };
1460
- } catch (error) {
1461
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1462
- logger4.error(`Failed to create issue: ${errorMessage}`);
1463
- if (callback) {
1464
- await callback({
1465
- text: `Failed to create the issue: ${errorMessage}`
1466
- });
1467
- }
1468
- return { success: false };
1469
- }
1470
- },
1471
- examples: examples3
1083
+ // src/actions/notification-triage.ts
1084
+ import { logger as logger2 } from "@elizaos/core";
1085
+ var REASON_SCORES = {
1086
+ security_advisory: 100,
1087
+ team_mention: 70,
1088
+ author: 60,
1089
+ mention: 55,
1090
+ assign: 50,
1091
+ review_requested: 80,
1092
+ state_change: 20,
1093
+ comment: 30,
1094
+ subscribed: 10,
1095
+ manual: 15,
1096
+ invitation: 40,
1097
+ ci_activity: 25
1472
1098
  };
1473
- // actions/createPullRequest.ts
1474
- import {
1475
- logger as logger5
1476
- } from "@elizaos/core";
1477
- var spec4 = requireActionSpec("CREATE_PULL_REQUEST");
1478
- var examples4 = [
1479
- [
1099
+ var SUBJECT_TYPE_SCORES = {
1100
+ PullRequest: 20,
1101
+ Issue: 15,
1102
+ Release: 10,
1103
+ Commit: 5,
1104
+ Discussion: 8
1105
+ };
1106
+ var NOTIFICATION_TRIAGE_LIMIT = 25;
1107
+ function scoreNotification(params) {
1108
+ const base = REASON_SCORES[params.reason] ?? 10;
1109
+ const subject = SUBJECT_TYPE_SCORES[params.subjectType] ?? 0;
1110
+ let freshness = 0;
1111
+ if (params.repoPushedAtMs !== null) {
1112
+ const ageHours = (params.nowMs - params.repoPushedAtMs) / (1e3 * 60 * 60);
1113
+ if (ageHours < 1) freshness = 20;
1114
+ else if (ageHours < 6) freshness = 15;
1115
+ else if (ageHours < 24) freshness = 10;
1116
+ else if (ageHours < 24 * 7) freshness = 5;
1117
+ }
1118
+ return base + subject + freshness;
1119
+ }
1120
+ var notificationTriageAction = {
1121
+ name: GitHubActions.GITHUB_NOTIFICATION_TRIAGE,
1122
+ contexts: ["code", "tasks", "connectors", "automation"],
1123
+ contextGate: { anyOf: ["code", "tasks", "connectors", "automation"] },
1124
+ roleGate: { minRole: "USER" },
1125
+ similes: ["TRIAGE_GITHUB_NOTIFICATIONS", "GITHUB_INBOX"],
1126
+ description: "Returns unread GitHub notifications sorted by a priority score derived from reason, subject type, and repo freshness.",
1127
+ descriptionCompressed: "return unread GitHub notification sort priority score derive reason, subject type, repo freshness",
1128
+ parameters: [
1480
1129
  {
1481
- name: spec4.name,
1482
- content: {
1483
- text: "Create a pull request from feature/dark-mode to main with title 'Add dark mode support'"
1484
- }
1130
+ name: "as",
1131
+ description: "Identity to use when reading notifications: user or agent.",
1132
+ required: false,
1133
+ schema: { type: "string", enum: ["user", "agent"], default: "user" }
1485
1134
  },
1486
1135
  {
1487
- name: "{{agent}}",
1488
- content: {
1489
- text: "I'll create a pull request from feature/dark-mode to main.",
1490
- actions: ["CREATE_GITHUB_PULL_REQUEST"]
1491
- }
1136
+ name: "accountId",
1137
+ description: "Optional GitHub account id from GITHUB_ACCOUNTS. Defaults by role.",
1138
+ required: false,
1139
+ schema: { type: "string" }
1492
1140
  }
1493
1141
  ],
1494
- [
1495
- {
1496
- name: "{{user1}}",
1497
- content: {
1498
- text: "Open a PR to merge my-branch into develop"
1499
- }
1500
- },
1501
- {
1502
- name: "{{agent}}",
1503
- content: {
1504
- text: "Creating a pull request to merge my-branch into develop.",
1505
- actions: ["CREATE_GITHUB_PULL_REQUEST"]
1506
- }
1507
- }
1508
- ]
1509
- ];
1510
- var createPullRequestAction = {
1511
- name: "CREATE_GITHUB_PULL_REQUEST",
1512
- similes: spec4.similes ? [...spec4.similes] : [],
1513
- description: spec4.description,
1514
- validate: async (runtime, message, state, options) => {
1515
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
1516
- const __avText = __avTextRaw.toLowerCase();
1517
- const __avKeywords = ["create", "github", "pull", "request"];
1518
- const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
1519
- const __avRegex = /\b(?:create|github|pull|request)\b/i;
1520
- const __avRegexOk = Boolean(__avText.match(__avRegex));
1521
- const __avSource = String(message?.content?.source ?? message?.source ?? "");
1522
- const __avExpectedSource = "";
1523
- const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
1524
- const __avOptions = options && typeof options === "object" ? options : {};
1525
- const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
1526
- if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
1527
- return false;
1528
- }
1529
- const __avLegacyValidate = async (runtime2, message2, _state) => {
1530
- const service = runtime2.getService(GITHUB_SERVICE_NAME);
1531
- if (!service) {
1532
- return false;
1533
- }
1534
- const text = message2.content.text?.toLowerCase() ?? "";
1535
- return text.includes("pull request") || text.includes("pr") || text.includes("merge");
1536
- };
1537
- try {
1538
- return Boolean(await __avLegacyValidate(runtime, message, state, options));
1539
- } catch {
1540
- return false;
1541
- }
1142
+ validate: async (runtime, _message) => {
1143
+ const r = buildResolvedClient(runtime, "user");
1144
+ return !("error" in r);
1542
1145
  },
1543
- handler: async (runtime, message, state, _options, callback) => {
1544
- const service = runtime.getService(GITHUB_SERVICE_NAME);
1545
- if (!service) {
1546
- logger5.error("GitHub service not available");
1547
- if (callback) {
1548
- await callback({
1549
- text: "GitHub service is not available. Please ensure the plugin is properly configured."
1550
- });
1551
- }
1552
- return { success: false };
1146
+ handler: async (runtime, _message, _state, options, callback) => {
1147
+ const selection = resolveAccountSelection(options, "user");
1148
+ const resolved = buildResolvedClient(runtime, selection);
1149
+ if ("error" in resolved) {
1150
+ await callback?.({ text: resolved.error });
1151
+ return { success: false, error: resolved.error };
1553
1152
  }
1554
1153
  try {
1555
- const content = message.content;
1556
- const text = content.text ?? "";
1557
- const params = {
1558
- owner: state?.owner ?? service.getConfig().owner ?? "",
1559
- repo: state?.repo ?? service.getConfig().repo ?? "",
1560
- title: state?.title ?? text.slice(0, 100),
1561
- body: state?.body ?? text,
1562
- head: state?.head ?? "",
1563
- base: state?.base ?? service.getConfig().branch ?? "main",
1564
- draft: state?.draft ?? false
1565
- };
1566
- const validation = createPullRequestSchema.safeParse(params);
1567
- if (!validation.success) {
1568
- const errors = formatZodErrors(validation.error);
1569
- logger5.error(`Invalid pull request parameters: ${errors}`);
1570
- if (callback) {
1571
- await callback({
1572
- text: `I couldn't create the pull request due to missing information: ${errors}`
1573
- });
1154
+ const resp = await resolved.client.activity.listNotificationsForAuthenticatedUser({
1155
+ all: false,
1156
+ per_page: 50
1157
+ });
1158
+ const notifications = resp.data;
1159
+ const nowMs = Date.now();
1160
+ const triaged = notifications.map((n) => {
1161
+ const repoPushedAt = n.repository?.pushed_at ?? null;
1162
+ const repoPushedAtMs = typeof repoPushedAt === "string" ? Date.parse(repoPushedAt) : null;
1163
+ const reason = typeof n.reason === "string" ? n.reason : "unknown";
1164
+ const subjectType = typeof n.subject?.type === "string" ? n.subject.type : "Unknown";
1165
+ return {
1166
+ id: n.id,
1167
+ reason,
1168
+ repo: n.repository?.full_name ?? "unknown",
1169
+ title: n.subject?.title ?? "(untitled)",
1170
+ subjectType,
1171
+ url: n.subject?.url ?? null,
1172
+ updatedAt: n.updated_at,
1173
+ score: scoreNotification({
1174
+ reason,
1175
+ subjectType,
1176
+ repoPushedAtMs: repoPushedAtMs !== null && Number.isFinite(repoPushedAtMs) ? repoPushedAtMs : null,
1177
+ nowMs
1178
+ })
1179
+ };
1180
+ });
1181
+ triaged.sort((a, b) => b.score - a.score);
1182
+ const boundedTriaged = triaged.slice(0, NOTIFICATION_TRIAGE_LIMIT);
1183
+ await callback?.({
1184
+ text: `Triaged ${boundedTriaged.length} unread notification(s)`
1185
+ });
1186
+ return {
1187
+ success: true,
1188
+ data: {
1189
+ notifications: boundedTriaged,
1190
+ notificationLimit: NOTIFICATION_TRIAGE_LIMIT,
1191
+ totalUnread: triaged.length
1574
1192
  }
1575
- return { success: false };
1193
+ };
1194
+ } catch (err) {
1195
+ const rl = inspectRateLimit(err);
1196
+ const message = rl.isRateLimited ? formatRateLimitMessage(rl) : `GITHUB_NOTIFICATION_TRIAGE failed: ${errorMessage(err)}`;
1197
+ logger2.warn({ message }, "[GitHub:GITHUB_NOTIFICATION_TRIAGE]");
1198
+ await callback?.({ text: message });
1199
+ return { success: false, error: message };
1200
+ }
1201
+ },
1202
+ examples: [
1203
+ [
1204
+ {
1205
+ name: "{{user1}}",
1206
+ content: { text: "What's in my GitHub inbox?" }
1207
+ },
1208
+ {
1209
+ name: "{{agentName}}",
1210
+ content: { text: "Triaged 7 unread notification(s)" }
1576
1211
  }
1577
- const pr = await service.createPullRequest(params);
1578
- logger5.info(`Created pull request #${pr.number}: ${pr.title}`);
1579
- if (callback) {
1580
- await callback({
1581
- text: `Created pull request #${pr.number}: "${pr.title}"
1582
-
1583
- From: ${pr.head.ref}
1584
- To: ${pr.base.ref}
1212
+ ]
1213
+ ]
1214
+ };
1585
1215
 
1586
- View it at: ${pr.htmlUrl}`
1587
- });
1588
- }
1589
- return { success: true };
1590
- } catch (error) {
1591
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1592
- logger5.error(`Failed to create pull request: ${errorMessage}`);
1593
- if (callback) {
1594
- await callback({
1595
- text: `Failed to create the pull request: ${errorMessage}`
1596
- });
1216
+ // src/actions/pr-op.ts
1217
+ import { logger as logger3 } from "@elizaos/core";
1218
+ var SUPPORTED_OPS2 = /* @__PURE__ */ new Set(["list", "review"]);
1219
+ var EVENT_BY_ACTION = {
1220
+ approve: "APPROVE",
1221
+ "request-changes": "REQUEST_CHANGES",
1222
+ comment: "COMMENT"
1223
+ };
1224
+ function parseOp2(value) {
1225
+ if (typeof value !== "string") return null;
1226
+ return SUPPORTED_OPS2.has(value) ? value : null;
1227
+ }
1228
+ function parseState(value) {
1229
+ return value === "closed" || value === "all" ? value : "open";
1230
+ }
1231
+ function parseReviewAction(value) {
1232
+ return value === "approve" || value === "request-changes" || value === "comment" ? value : null;
1233
+ }
1234
+ async function runList(runtime, options, callback) {
1235
+ const selection = resolveAccountSelection(options, "agent");
1236
+ const resolved = buildResolvedClient(runtime, selection);
1237
+ if ("error" in resolved) {
1238
+ await callback?.({ text: resolved.error });
1239
+ return { success: false, error: resolved.error };
1240
+ }
1241
+ const state = parseState(options?.state);
1242
+ const author = requireString(options, "author");
1243
+ const repo = requireString(options, "repo");
1244
+ const prs = [];
1245
+ if (repo) {
1246
+ const parts = splitRepo(repo);
1247
+ if (!parts) {
1248
+ const err = `Invalid repo "${repo}" \u2014 expected "owner/name"`;
1249
+ await callback?.({ text: err });
1250
+ return { success: false, error: err };
1251
+ }
1252
+ const resp = await resolved.client.pulls.list({
1253
+ owner: parts.owner,
1254
+ repo: parts.name,
1255
+ state,
1256
+ per_page: 100
1257
+ });
1258
+ for (const pr of resp.data) {
1259
+ if (author && pr.user?.login !== author) {
1260
+ continue;
1597
1261
  }
1598
- return { success: false };
1262
+ prs.push({
1263
+ repo,
1264
+ number: pr.number,
1265
+ title: pr.title,
1266
+ author: pr.user?.login ?? null,
1267
+ state: pr.state,
1268
+ url: pr.html_url
1269
+ });
1599
1270
  }
1600
- },
1601
- examples: examples4
1602
- };
1603
- // actions/mergePullRequest.ts
1604
- import {
1605
- logger as logger6
1606
- } from "@elizaos/core";
1607
- var spec5 = requireActionSpec("MERGE_PULL_REQUEST");
1608
- var examples5 = [
1609
- [
1271
+ } else {
1272
+ const q = [
1273
+ "is:pr",
1274
+ state === "all" ? "" : `is:${state}`,
1275
+ author ? `author:${author}` : ""
1276
+ ].filter(Boolean).join(" ");
1277
+ const resp = await resolved.client.search.issuesAndPullRequests({
1278
+ q,
1279
+ per_page: 50
1280
+ });
1281
+ for (const item of resp.data.items) {
1282
+ const match = /\/repos\/([^/]+\/[^/]+)(?:\/|$)/.exec(item.repository_url);
1283
+ const repoName = match?.[1] ?? item.repository_url;
1284
+ prs.push({
1285
+ repo: repoName,
1286
+ number: item.number,
1287
+ title: item.title,
1288
+ author: item.user?.login ?? null,
1289
+ state: item.state,
1290
+ url: item.html_url
1291
+ });
1292
+ }
1293
+ }
1294
+ await callback?.({ text: `Found ${prs.length} pull request(s)` });
1295
+ return { success: true, data: { op: "list", prs } };
1296
+ }
1297
+ async function runReview(runtime, options, callback) {
1298
+ const selection = resolveAccountSelection(options, "user");
1299
+ const repo = requireString(options, "repo");
1300
+ const number = requireNumber(options, "number");
1301
+ const action = parseReviewAction(options?.action);
1302
+ const body = requireString(options, "body");
1303
+ if (!repo || !number || !action) {
1304
+ const err = "GITHUB_PR_OP review requires repo (owner/name), number (integer), and action (approve|request-changes|comment)";
1305
+ await callback?.({ text: err });
1306
+ return { success: false, error: err };
1307
+ }
1308
+ const parts = splitRepo(repo);
1309
+ if (!parts) {
1310
+ const err = `Invalid repo "${repo}" \u2014 expected "owner/name"`;
1311
+ await callback?.({ text: err });
1312
+ return { success: false, error: err };
1313
+ }
1314
+ if (!isConfirmed(options)) {
1315
+ const preview = `About to ${action.replace("-", " ")} PR ${repo}#${number}` + (body ? ` with body: "${body.slice(0, 120)}"` : "") + ` as ${describeSelection(selection)}. Re-invoke with confirmed: true to proceed.`;
1316
+ await callback?.({ text: preview });
1317
+ return { success: false, requiresConfirmation: true, preview };
1318
+ }
1319
+ if (action === "request-changes" && !body) {
1320
+ const err = "request-changes review requires a body explaining the changes";
1321
+ await callback?.({ text: err });
1322
+ return { success: false, error: err };
1323
+ }
1324
+ const resolved = buildResolvedClient(runtime, selection);
1325
+ if ("error" in resolved) {
1326
+ await callback?.({ text: resolved.error });
1327
+ return { success: false, error: resolved.error };
1328
+ }
1329
+ const resp = await resolved.client.pulls.createReview({
1330
+ owner: parts.owner,
1331
+ repo: parts.name,
1332
+ pull_number: number,
1333
+ event: EVENT_BY_ACTION[action],
1334
+ body: body ?? void 0
1335
+ });
1336
+ await callback?.({ text: `Submitted ${action} review on ${repo}#${number}` });
1337
+ return { success: true, data: { op: "review", id: resp.data.id } };
1338
+ }
1339
+ var prOpAction = {
1340
+ name: GitHubActions.GITHUB_PR_OP,
1341
+ contexts: ["code", "tasks", "connectors", "automation"],
1342
+ contextGate: { anyOf: ["code", "tasks", "connectors", "automation"] },
1343
+ roleGate: { minRole: "USER" },
1344
+ similes: [
1345
+ "LIST_PRS",
1346
+ "LIST_PULL_REQUESTS",
1347
+ "SHOW_PRS",
1348
+ "GITHUB_LIST_PRS",
1349
+ "REVIEW_PR",
1350
+ "APPROVE_PR",
1351
+ "REQUEST_CHANGES",
1352
+ "COMMENT_ON_PR"
1353
+ ],
1354
+ description: "Single router for GitHub PR ops: list and review. Review requires confirmed:true.",
1355
+ descriptionCompressed: "GitHub PR ops: list pull requests, submit review with confirmation.",
1356
+ parameters: [
1610
1357
  {
1611
- name: spec5.name,
1612
- content: {
1613
- text: "Merge pull request #42"
1614
- }
1358
+ name: "subaction",
1359
+ description: "PR operation: list or review.",
1360
+ required: true,
1361
+ schema: { type: "string", enum: [...SUPPORTED_OPS2] }
1615
1362
  },
1616
1363
  {
1617
- name: "{{agent}}",
1618
- content: {
1619
- text: "I'll merge pull request #42.",
1620
- actions: ["MERGE_GITHUB_PULL_REQUEST"]
1621
- }
1622
- }
1623
- ],
1624
- [
1364
+ name: "repo",
1365
+ description: "Repository in owner/name form.",
1366
+ required: false,
1367
+ schema: { type: "string" }
1368
+ },
1625
1369
  {
1626
- name: "{{user1}}",
1627
- content: {
1628
- text: "Squash and merge PR #15 with title 'Feature: Add dark mode'"
1629
- }
1370
+ name: "number",
1371
+ description: "Pull request number for review.",
1372
+ required: false,
1373
+ schema: { type: "number" }
1630
1374
  },
1631
1375
  {
1632
- name: "{{agent}}",
1633
- content: {
1634
- text: "Squash merging pull request #15 with your custom commit title.",
1635
- actions: ["MERGE_GITHUB_PULL_REQUEST"]
1636
- }
1637
- }
1638
- ]
1639
- ];
1640
- var mergePullRequestAction = {
1641
- name: "MERGE_GITHUB_PULL_REQUEST",
1642
- similes: spec5.similes ? [...spec5.similes] : [],
1643
- description: spec5.description,
1644
- validate: async (runtime, message, state, options) => {
1645
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
1646
- const __avText = __avTextRaw.toLowerCase();
1647
- const __avKeywords = ["merge", "github", "pull", "request"];
1648
- const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
1649
- const __avRegex = /\b(?:merge|github|pull|request)\b/i;
1650
- const __avRegexOk = Boolean(__avText.match(__avRegex));
1651
- const __avSource = String(message?.content?.source ?? message?.source ?? "");
1652
- const __avExpectedSource = "";
1653
- const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
1654
- const __avOptions = options && typeof options === "object" ? options : {};
1655
- const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
1656
- if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
1657
- return false;
1658
- }
1659
- const __avLegacyValidate = async (runtime2, message2, _state) => {
1660
- const service = runtime2.getService(GITHUB_SERVICE_NAME);
1661
- if (!service) {
1662
- return false;
1663
- }
1664
- const text = message2.content.text?.toLowerCase() ?? "";
1665
- return text.includes("merge");
1666
- };
1667
- try {
1668
- return Boolean(await __avLegacyValidate(runtime, message, state, options));
1669
- } catch {
1670
- return false;
1671
- }
1672
- },
1673
- handler: async (runtime, message, state, _options, callback) => {
1674
- const service = runtime.getService(GITHUB_SERVICE_NAME);
1675
- if (!service) {
1676
- logger6.error("GitHub service not available");
1677
- if (callback) {
1678
- await callback({
1679
- text: "GitHub service is not available. Please ensure the plugin is properly configured."
1680
- });
1376
+ name: "state",
1377
+ description: "PR state for list.",
1378
+ required: false,
1379
+ schema: {
1380
+ type: "string",
1381
+ enum: ["open", "closed", "all"],
1382
+ default: "open"
1681
1383
  }
1682
- return { success: false };
1683
- }
1684
- try {
1685
- const content = message.content;
1686
- const text = content.text?.toLowerCase() ?? "";
1687
- let mergeMethod = "merge";
1688
- if (text.includes("squash")) {
1689
- mergeMethod = "squash";
1690
- } else if (text.includes("rebase")) {
1691
- mergeMethod = "rebase";
1692
- }
1693
- const params = {
1694
- owner: state?.owner ?? service.getConfig().owner ?? "",
1695
- repo: state?.repo ?? service.getConfig().repo ?? "",
1696
- pullNumber: state?.pullNumber ?? 0,
1697
- commitTitle: state?.commitTitle,
1698
- commitMessage: state?.commitMessage,
1699
- mergeMethod: state?.mergeMethod ?? mergeMethod
1700
- };
1701
- const validation = mergePullRequestSchema.safeParse(params);
1702
- if (!validation.success) {
1703
- const errors = formatZodErrors(validation.error);
1704
- logger6.error(`Invalid merge parameters: ${errors}`);
1705
- if (callback) {
1706
- await callback({
1707
- text: `I couldn't merge the pull request due to missing information: ${errors}`
1708
- });
1709
- }
1710
- return { success: false };
1711
- }
1712
- const result = await service.mergePullRequest(params);
1713
- if (result.merged) {
1714
- logger6.info(`Merged pull request #${params.pullNumber}`);
1715
- if (callback) {
1716
- await callback({
1717
- text: `Successfully merged pull request #${params.pullNumber}.
1718
-
1719
- Merge commit: ${result.sha.slice(0, 7)}
1720
- Method: ${params.mergeMethod}`
1721
- });
1722
- }
1723
- return { success: true };
1724
- } else {
1725
- logger6.warn(`Pull request #${params.pullNumber} was not merged: ${result.message}`);
1726
- if (callback) {
1727
- await callback({
1728
- text: `Could not merge pull request #${params.pullNumber}: ${result.message}`
1729
- });
1730
- }
1731
- return { success: false };
1732
- }
1733
- } catch (error) {
1734
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1735
- logger6.error(`Failed to merge pull request: ${errorMessage}`);
1736
- if (callback) {
1737
- await callback({
1738
- text: `Failed to merge the pull request: ${errorMessage}`
1739
- });
1740
- }
1741
- return { success: false };
1742
- }
1743
- },
1744
- examples: examples5
1745
- };
1746
- // actions/pushCode.ts
1747
- import {
1748
- logger as logger7
1749
- } from "@elizaos/core";
1750
- var spec6 = requireActionSpec("PUSH_CODE");
1751
- var examples6 = [
1752
- [
1384
+ },
1753
1385
  {
1754
- name: spec6.name,
1755
- content: {
1756
- text: "Push the file changes to the feature/dark-mode branch with message 'Add dark mode styles'"
1757
- }
1386
+ name: "author",
1387
+ description: "Optional PR author username filter for list.",
1388
+ required: false,
1389
+ schema: { type: "string" }
1758
1390
  },
1759
1391
  {
1760
- name: "{{agent}}",
1761
- content: {
1762
- text: "I'll commit and push those changes to feature/dark-mode.",
1763
- actions: ["PUSH_GITHUB_CODE"]
1392
+ name: "action",
1393
+ description: "Review action: approve, request-changes, or comment.",
1394
+ required: false,
1395
+ schema: {
1396
+ type: "string",
1397
+ enum: ["approve", "request-changes", "comment"]
1764
1398
  }
1765
- }
1766
- ],
1767
- [
1399
+ },
1768
1400
  {
1769
- name: "{{user1}}",
1770
- content: {
1771
- text: "Commit these files to main: README.md with content 'Hello World'"
1772
- }
1401
+ name: "body",
1402
+ description: "Review body for comment or request-changes.",
1403
+ required: false,
1404
+ schema: { type: "string" }
1773
1405
  },
1774
1406
  {
1775
- name: "{{agent}}",
1776
- content: {
1777
- text: "Committing README.md to main branch.",
1778
- actions: ["PUSH_GITHUB_CODE"]
1779
- }
1407
+ name: "as",
1408
+ description: "Identity to use: agent or user.",
1409
+ required: false,
1410
+ schema: { type: "string", enum: ["agent", "user"], default: "agent" }
1411
+ },
1412
+ {
1413
+ name: "accountId",
1414
+ description: "Optional GitHub account id from GITHUB_ACCOUNTS. Defaults by role.",
1415
+ required: false,
1416
+ schema: { type: "string" }
1417
+ },
1418
+ {
1419
+ name: "confirmed",
1420
+ description: "Must be true to submit a review.",
1421
+ required: false,
1422
+ schema: { type: "boolean", default: false }
1780
1423
  }
1781
- ]
1782
- ];
1783
- var pushCodeAction = {
1784
- name: "PUSH_GITHUB_CODE",
1785
- similes: spec6.similes ? [...spec6.similes] : [],
1786
- description: spec6.description,
1787
- validate: async (runtime, message, state, options) => {
1788
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
1789
- const __avText = __avTextRaw.toLowerCase();
1790
- const __avKeywords = ["push", "github", "code"];
1791
- const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
1792
- const __avRegex = /\b(?:push|github|code)\b/i;
1793
- const __avRegexOk = Boolean(__avText.match(__avRegex));
1794
- const __avSource = String(message?.content?.source ?? message?.source ?? "");
1795
- const __avExpectedSource = "";
1796
- const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
1797
- const __avOptions = options && typeof options === "object" ? options : {};
1798
- const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
1799
- if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
1800
- return false;
1424
+ ],
1425
+ validate: async (runtime, _message) => {
1426
+ const r = buildResolvedClient(runtime, "agent");
1427
+ return !("error" in r);
1428
+ },
1429
+ handler: async (runtime, _message, _state, options, callback) => {
1430
+ const op = parseOp2(options?.op);
1431
+ if (!op) {
1432
+ const err = "GITHUB_PR_OP requires op (list|review)";
1433
+ await callback?.({ text: err });
1434
+ return { success: false, error: err };
1801
1435
  }
1802
- const __avLegacyValidate = async (runtime2, message2, _state) => {
1803
- const service = runtime2.getService(GITHUB_SERVICE_NAME);
1804
- if (!service) {
1805
- return false;
1806
- }
1807
- const text = message2.content.text?.toLowerCase() ?? "";
1808
- return text.includes("push") || text.includes("commit") || text.includes("save") || text.includes("upload");
1809
- };
1810
1436
  try {
1811
- return Boolean(await __avLegacyValidate(runtime, message, state, options));
1812
- } catch {
1813
- return false;
1437
+ return op === "list" ? await runList(runtime, options, callback) : await runReview(runtime, options, callback);
1438
+ } catch (err) {
1439
+ const rl = inspectRateLimit(err);
1440
+ const message = rl.isRateLimited ? formatRateLimitMessage(rl) : `GITHUB_PR_OP ${op} failed: ${errorMessage(err)}`;
1441
+ logger3.warn({ message }, "[GitHub:GITHUB_PR_OP]");
1442
+ await callback?.({ text: message });
1443
+ return { success: false, error: message };
1814
1444
  }
1815
1445
  },
1816
- handler: async (runtime, message, state, _options, callback) => {
1817
- const service = runtime.getService(GITHUB_SERVICE_NAME);
1818
- if (!service) {
1819
- logger7.error("GitHub service not available");
1820
- if (callback) {
1821
- await callback({
1822
- text: "GitHub service is not available. Please ensure the plugin is properly configured."
1823
- });
1824
- }
1825
- return { success: false };
1826
- }
1827
- try {
1828
- const content = message.content;
1829
- const text = content.text ?? "";
1830
- const files = state?.files ?? [];
1831
- const params = {
1832
- owner: state?.owner ?? service.getConfig().owner ?? "",
1833
- repo: state?.repo ?? service.getConfig().repo ?? "",
1834
- message: state?.message ?? text.slice(0, 100),
1835
- files,
1836
- branch: state?.branch ?? service.getConfig().branch ?? "main",
1837
- authorName: state?.authorName,
1838
- authorEmail: state?.authorEmail
1839
- };
1840
- const validation = createCommitSchema.safeParse(params);
1841
- if (!validation.success) {
1842
- const errors = formatZodErrors(validation.error);
1843
- logger7.error(`Invalid commit parameters: ${errors}`);
1844
- if (callback) {
1845
- await callback({
1846
- text: `I couldn't push the code due to missing information: ${errors}`
1847
- });
1446
+ examples: [
1447
+ [
1448
+ {
1449
+ name: "{{user1}}",
1450
+ content: { text: "Show me open PRs on elizaOS/eliza" }
1451
+ },
1452
+ {
1453
+ name: "{{agentName}}",
1454
+ content: { text: "Found 3 pull request(s)" }
1455
+ }
1456
+ ],
1457
+ [
1458
+ {
1459
+ name: "{{user1}}",
1460
+ content: { text: "Approve PR #42 on elizaOS/eliza" }
1461
+ },
1462
+ {
1463
+ name: "{{agentName}}",
1464
+ content: {
1465
+ text: "Submitted approve review on elizaOS/eliza#42"
1848
1466
  }
1849
- return { success: false };
1850
1467
  }
1851
- const commit = await service.createCommit(params);
1852
- logger7.info(`Created commit ${commit.sha.slice(0, 7)} on ${params.branch}`);
1853
- if (callback) {
1854
- await callback({
1855
- text: `Pushed ${files.length} file(s) to ${params.branch}.
1856
-
1857
- Commit: ${commit.sha.slice(0, 7)}
1858
- Message: ${commit.message}
1468
+ ]
1469
+ ]
1470
+ };
1859
1471
 
1860
- View at: ${commit.htmlUrl}`
1861
- });
1862
- }
1863
- return { success: true };
1864
- } catch (error) {
1865
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1866
- logger7.error(`Failed to push code: ${errorMessage}`);
1867
- if (callback) {
1868
- await callback({
1869
- text: `Failed to push the code: ${errorMessage}`
1870
- });
1871
- }
1872
- return { success: false };
1873
- }
1874
- },
1875
- examples: examples6
1472
+ // src/actions/github.ts
1473
+ var GITHUB_ACTIONS = [
1474
+ "pr_list",
1475
+ "pr_review",
1476
+ "issue_create",
1477
+ "issue_assign",
1478
+ "issue_close",
1479
+ "issue_reopen",
1480
+ "issue_comment",
1481
+ "issue_label",
1482
+ "notification_triage"
1483
+ ];
1484
+ var ISSUE_OP_BY_ACTION = {
1485
+ issue_create: "create",
1486
+ issue_assign: "assign",
1487
+ issue_close: "close",
1488
+ issue_reopen: "reopen",
1489
+ issue_comment: "comment",
1490
+ issue_label: "label"
1876
1491
  };
1877
- // actions/reviewPullRequest.ts
1878
- import {
1879
- logger as logger8
1880
- } from "@elizaos/core";
1881
- var spec7 = requireActionSpec("REVIEW_PULL_REQUEST");
1882
- var examples7 = [
1883
- [
1492
+ function readParameters(options) {
1493
+ if (!options || typeof options !== "object") return {};
1494
+ const record = options;
1495
+ const params = record.parameters;
1496
+ return params && typeof params === "object" && !Array.isArray(params) ? { ...params } : { ...record };
1497
+ }
1498
+ function readAction(options) {
1499
+ const params = readParameters(options);
1500
+ const raw = params.action ?? params.subaction ?? params.op ?? params.operation ?? params.verb;
1501
+ if (typeof raw !== "string") return void 0;
1502
+ const normalized = raw.trim().toLowerCase().replace(/[-\s]+/g, "_");
1503
+ return GITHUB_ACTIONS.includes(normalized) ? normalized : void 0;
1504
+ }
1505
+ function withParameters(options, parameters) {
1506
+ if (!options || typeof options !== "object") return { parameters };
1507
+ return { ...options, parameters };
1508
+ }
1509
+ function delegate(target, runtime, message, state, options, callback) {
1510
+ return target.handler(
1511
+ runtime,
1512
+ message,
1513
+ state,
1514
+ options,
1515
+ callback
1516
+ );
1517
+ }
1518
+ var githubAction = {
1519
+ name: "GITHUB",
1520
+ contexts: ["code", "tasks", "connectors", "automation"],
1521
+ contextGate: { anyOf: ["code", "tasks", "connectors", "automation"] },
1522
+ roleGate: { minRole: "USER" },
1523
+ similes: [
1524
+ "GITHUB_PR_OP",
1525
+ "GITHUB_ISSUE_OP",
1526
+ "GITHUB_NOTIFICATION_TRIAGE",
1527
+ "GITHUB_PULL_REQUEST",
1528
+ "GITHUB_ISSUE",
1529
+ "GITHUB_NOTIFICATIONS"
1530
+ ],
1531
+ description: "GitHub umbrella for pull requests, issues, and notification triage. Use action=pr_list/pr_review/issue_create/issue_assign/issue_close/issue_reopen/issue_comment/issue_label/notification_triage.",
1532
+ descriptionCompressed: "GitHub: pr_list|pr_review|issue_create|issue_assign|issue_close|issue_reopen|issue_comment|issue_label|notification_triage",
1533
+ parameters: [
1884
1534
  {
1885
- name: spec7.name,
1886
- content: {
1887
- text: "Approve pull request #42 with comment 'LGTM!'"
1888
- }
1535
+ name: "action",
1536
+ description: "GitHub operation to run.",
1537
+ required: true,
1538
+ schema: { type: "string", enum: [...GITHUB_ACTIONS] }
1889
1539
  },
1890
1540
  {
1891
- name: "{{agent}}",
1892
- content: {
1893
- text: "I'll approve pull request #42 with that comment.",
1894
- actions: ["REVIEW_GITHUB_PULL_REQUEST"]
1895
- }
1896
- }
1897
- ],
1898
- [
1541
+ name: "repo",
1542
+ description: "Repository in owner/name form.",
1543
+ required: false,
1544
+ schema: { type: "string" }
1545
+ },
1899
1546
  {
1900
- name: "{{user1}}",
1901
- content: {
1902
- text: "Request changes on PR #15 - the tests are failing"
1903
- }
1547
+ name: "number",
1548
+ description: "Pull request or issue number.",
1549
+ required: false,
1550
+ schema: { type: "number" }
1904
1551
  },
1905
1552
  {
1906
- name: "{{agent}}",
1907
- content: {
1908
- text: "I'll request changes on pull request #15 with your feedback.",
1909
- actions: ["REVIEW_GITHUB_PULL_REQUEST"]
1910
- }
1911
- }
1912
- ]
1913
- ];
1914
- var reviewPullRequestAction = {
1915
- name: "REVIEW_GITHUB_PULL_REQUEST",
1916
- similes: spec7.similes ? [...spec7.similes] : [],
1917
- description: spec7.description,
1918
- validate: async (runtime, message, state, options) => {
1919
- const __avTextRaw = typeof message?.content?.text === "string" ? message.content.text : "";
1920
- const __avText = __avTextRaw.toLowerCase();
1921
- const __avKeywords = ["review", "github", "pull", "request"];
1922
- const __avKeywordOk = __avKeywords.length > 0 && __avKeywords.some((kw) => kw.length > 0 && __avText.includes(kw));
1923
- const __avRegex = /\b(?:review|github|pull|request)\b/i;
1924
- const __avRegexOk = Boolean(__avText.match(__avRegex));
1925
- const __avSource = String(message?.content?.source ?? message?.source ?? "");
1926
- const __avExpectedSource = "";
1927
- const __avSourceOk = __avExpectedSource ? __avSource === __avExpectedSource : Boolean(__avSource || state || runtime?.agentId || runtime?.getService);
1928
- const __avOptions = options && typeof options === "object" ? options : {};
1929
- const __avInputOk = __avText.trim().length > 0 || Object.keys(__avOptions).length > 0 || Boolean(message?.content && typeof message.content === "object");
1930
- if (!(__avKeywordOk && __avRegexOk && __avSourceOk && __avInputOk)) {
1931
- return false;
1932
- }
1933
- const __avLegacyValidate = async (runtime2, message2, _state) => {
1934
- const service = runtime2.getService(GITHUB_SERVICE_NAME);
1935
- if (!service) {
1936
- return false;
1937
- }
1938
- const text = message2.content.text?.toLowerCase() ?? "";
1939
- return text.includes("review") || text.includes("approve") || text.includes("request changes") || text.includes("lgtm");
1940
- };
1941
- try {
1942
- return Boolean(await __avLegacyValidate(runtime, message, state, options));
1943
- } catch {
1944
- return false;
1553
+ name: "state",
1554
+ description: "PR state for pr_list: open, closed, or all.",
1555
+ required: false,
1556
+ schema: { type: "string", enum: ["open", "closed", "all"], default: "open" }
1557
+ },
1558
+ {
1559
+ name: "author",
1560
+ description: "Optional PR author username filter for pr_list.",
1561
+ required: false,
1562
+ schema: { type: "string" }
1563
+ },
1564
+ {
1565
+ name: "review_action",
1566
+ description: "For action=pr_review: approve, request-changes, or comment.",
1567
+ required: false,
1568
+ schema: { type: "string", enum: ["approve", "request-changes", "comment"] }
1569
+ },
1570
+ {
1571
+ name: "title",
1572
+ description: "Issue title for action=issue_create.",
1573
+ required: false,
1574
+ schema: { type: "string" }
1575
+ },
1576
+ {
1577
+ name: "body",
1578
+ description: "Issue body, issue comment body, or PR review body.",
1579
+ required: false,
1580
+ schema: { type: "string" }
1581
+ },
1582
+ {
1583
+ name: "assignees",
1584
+ description: "GitHub usernames to assign.",
1585
+ required: false,
1586
+ schema: { type: "array", items: { type: "string" } }
1587
+ },
1588
+ {
1589
+ name: "labels",
1590
+ description: "Labels to apply on issue create or issue_label.",
1591
+ required: false,
1592
+ schema: { type: "array", items: { type: "string" } }
1593
+ },
1594
+ {
1595
+ name: "as",
1596
+ description: "Identity to use: agent or user.",
1597
+ required: false,
1598
+ schema: { type: "string", enum: ["agent", "user"], default: "agent" }
1599
+ },
1600
+ {
1601
+ name: "accountId",
1602
+ description: "Optional GitHub account id from GITHUB_ACCOUNTS. Defaults by role.",
1603
+ required: false,
1604
+ schema: { type: "string" }
1605
+ },
1606
+ {
1607
+ name: "confirmed",
1608
+ description: "Must be true for GitHub write operations.",
1609
+ required: false,
1610
+ schema: { type: "boolean", default: false }
1945
1611
  }
1946
- },
1947
- handler: async (runtime, message, state, _options, callback) => {
1948
- const service = runtime.getService(GITHUB_SERVICE_NAME);
1949
- if (!service) {
1950
- logger8.error("GitHub service not available");
1951
- if (callback) {
1952
- await callback({
1953
- text: "GitHub service is not available. Please ensure the plugin is properly configured."
1954
- });
1955
- }
1956
- return { success: false };
1612
+ ],
1613
+ validate: async (runtime, message, state) => await prOpAction.validate(runtime, message, state) || await issueOpAction.validate(runtime, message, state) || await notificationTriageAction.validate(runtime, message, state),
1614
+ handler: async (runtime, message, state, options, callback) => {
1615
+ const action = readAction(options);
1616
+ const params = readParameters(options);
1617
+ if (!action) {
1618
+ return {
1619
+ success: false,
1620
+ text: "GITHUB requires action=pr_list/pr_review/issue_create/issue_assign/issue_close/issue_reopen/issue_comment/issue_label/notification_triage.",
1621
+ data: { error: "MISSING_ACTION" }
1622
+ };
1957
1623
  }
1958
- try {
1959
- const content = message.content;
1960
- const text = content.text ?? "";
1961
- let event = "COMMENT";
1962
- const lowerText = text.toLowerCase();
1963
- if (lowerText.includes("approve") || lowerText.includes("lgtm") || lowerText.includes("looks good")) {
1964
- event = "APPROVE";
1965
- } else if (lowerText.includes("request changes") || lowerText.includes("needs work") || lowerText.includes("fix")) {
1966
- event = "REQUEST_CHANGES";
1967
- }
1968
- const params = {
1969
- owner: state?.owner ?? service.getConfig().owner ?? "",
1970
- repo: state?.repo ?? service.getConfig().repo ?? "",
1971
- pullNumber: state?.pullNumber ?? 0,
1972
- body: state?.body ?? text,
1973
- event: state?.event ?? event
1624
+ if (action === "notification_triage") {
1625
+ return delegate(
1626
+ notificationTriageAction,
1627
+ runtime,
1628
+ message,
1629
+ state,
1630
+ withParameters(options, params),
1631
+ callback
1632
+ );
1633
+ }
1634
+ if (action === "pr_list" || action === "pr_review") {
1635
+ const childParams = {
1636
+ ...params,
1637
+ op: action === "pr_list" ? "list" : "review",
1638
+ ...action === "pr_review" && params.review_action ? { action: params.review_action } : {}
1974
1639
  };
1975
- const validation = createReviewSchema.safeParse(params);
1976
- if (!validation.success) {
1977
- const errors = formatZodErrors(validation.error);
1978
- logger8.error(`Invalid review parameters: ${errors}`);
1979
- if (callback) {
1980
- await callback({
1981
- text: `I couldn't create the review due to missing information: ${errors}`
1982
- });
1983
- }
1984
- return { success: false };
1985
- }
1986
- const review = await service.createReview(params);
1987
- const eventLabel = review.state === "APPROVED" ? "approved" : review.state === "CHANGES_REQUESTED" ? "requested changes on" : "commented on";
1988
- logger8.info(`Created ${review.state} review on PR #${params.pullNumber}`);
1989
- if (callback) {
1990
- await callback({
1991
- text: `I've ${eventLabel} pull request #${params.pullNumber}.
1992
-
1993
- View the review at: ${review.htmlUrl}`
1994
- });
1995
- }
1996
- return { success: true };
1997
- } catch (error) {
1998
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1999
- logger8.error(`Failed to create review: ${errorMessage}`);
2000
- if (callback) {
2001
- await callback({
2002
- text: `Failed to create the review: ${errorMessage}`
2003
- });
2004
- }
2005
- return { success: false };
1640
+ return delegate(
1641
+ prOpAction,
1642
+ runtime,
1643
+ message,
1644
+ state,
1645
+ withParameters(options, childParams),
1646
+ callback
1647
+ );
1648
+ }
1649
+ const issueOp = ISSUE_OP_BY_ACTION[action];
1650
+ if (issueOp) {
1651
+ return delegate(
1652
+ issueOpAction,
1653
+ runtime,
1654
+ message,
1655
+ state,
1656
+ withParameters(options, { ...params, op: issueOp }),
1657
+ callback
1658
+ );
2006
1659
  }
2007
- },
2008
- examples: examples7
1660
+ return {
1661
+ success: false,
1662
+ text: `Unsupported GITHUB action: ${action}`,
1663
+ data: { error: "UNSUPPORTED_ACTION", action }
1664
+ };
1665
+ }
2009
1666
  };
2010
- // actions/index.ts
2011
- var allActions = [
2012
- createIssueAction,
2013
- createPullRequestAction,
2014
- reviewPullRequestAction,
2015
- createCommentAction,
2016
- createBranchAction,
2017
- pushCodeAction,
2018
- mergePullRequestAction
2019
- ];
2020
1667
 
2021
- // providers/issueContext.ts
2022
- function extractIssueNumber(text) {
2023
- const patterns = [/#(\d+)/, /issue\s*#?(\d+)/i, /pr\s*#?(\d+)/i, /pull\s*request\s*#?(\d+)/i];
2024
- for (const pattern of patterns) {
2025
- const match = text.match(pattern);
2026
- if (match?.[1]) {
2027
- return Number.parseInt(match[1], 10);
2028
- }
1668
+ // src/connector-account-provider.ts
1669
+ import {
1670
+ logger as logger4
1671
+ } from "@elizaos/core";
1672
+ var GITHUB_AUTHORIZATION_ENDPOINT = "https://github.com/login/oauth/authorize";
1673
+ var GITHUB_TOKEN_ENDPOINT = "https://github.com/login/oauth/access_token";
1674
+ var GITHUB_USER_ENDPOINT = "https://api.github.com/user";
1675
+ var DEFAULT_PURPOSES = [
1676
+ "posting",
1677
+ "reading",
1678
+ "admin"
1679
+ ];
1680
+ function nonEmptyString3(value) {
1681
+ if (typeof value !== "string") return void 0;
1682
+ const trimmed = value.trim();
1683
+ return trimmed.length > 0 ? trimmed : void 0;
1684
+ }
1685
+ function readSetting2(runtime, key) {
1686
+ return nonEmptyString3(runtime.getSetting?.(key));
1687
+ }
1688
+ function readClientConfig(runtime) {
1689
+ const clientId = readSetting2(runtime, "GITHUB_OAUTH_CLIENT_ID");
1690
+ const clientSecret = readSetting2(runtime, "GITHUB_OAUTH_CLIENT_SECRET");
1691
+ const redirectUri = readSetting2(runtime, "GITHUB_OAUTH_REDIRECT_URI");
1692
+ if (!clientId || !clientSecret || !redirectUri) {
1693
+ throw new Error(
1694
+ "GitHub OAuth requires GITHUB_OAUTH_CLIENT_ID, GITHUB_OAUTH_CLIENT_SECRET, and GITHUB_OAUTH_REDIRECT_URI to be configured."
1695
+ );
2029
1696
  }
2030
- return null;
1697
+ return { clientId, clientSecret, redirectUri };
2031
1698
  }
2032
- var spec8 = requireProviderSpec("issueContext");
2033
- var issueContextProvider = {
2034
- name: spec8.name,
2035
- description: "Provides detailed context about a specific GitHub issue or pull request when referenced",
2036
- dynamic: true,
2037
- get: async (runtime, message, _state) => {
2038
- const service = runtime.getService(GITHUB_SERVICE_NAME);
2039
- if (!service) {
2040
- return { text: null };
2041
- }
2042
- const text = message.content.text ?? "";
2043
- const issueNumber = extractIssueNumber(text);
2044
- if (!issueNumber) {
2045
- return { text: null };
2046
- }
2047
- try {
2048
- const config = service.getConfig();
2049
- if (!config.owner || !config.repo) {
2050
- return { text: null };
2051
- }
2052
- try {
2053
- const issue = await service.getIssue({
2054
- owner: config.owner,
2055
- repo: config.repo,
2056
- issueNumber
2057
- });
2058
- if (issue.isPullRequest) {
2059
- const pr = await service.getPullRequest({
2060
- owner: config.owner,
2061
- repo: config.repo,
2062
- pullNumber: issueNumber
2063
- });
2064
- const labels2 = pr.labels.map((l) => l.name).join(", ");
2065
- const assignees2 = pr.assignees.map((a) => a.login).join(", ");
2066
- const reviewers = pr.requestedReviewers.map((r) => r.login).join(", ");
2067
- const parts2 = [
2068
- `## Pull Request #${pr.number}: ${pr.title}`,
2069
- "",
2070
- `**State:** ${pr.state}${pr.draft ? " (Draft)" : ""}${pr.merged ? " (Merged)" : ""}`,
2071
- `**Author:** ${pr.user.login}`,
2072
- `**Branch:** ${pr.head.ref} → ${pr.base.ref}`,
2073
- `**Created:** ${pr.createdAt}`,
2074
- `**Updated:** ${pr.updatedAt}`
2075
- ];
2076
- if (labels2) {
2077
- parts2.push(`**Labels:** ${labels2}`);
2078
- }
2079
- if (assignees2) {
2080
- parts2.push(`**Assignees:** ${assignees2}`);
1699
+ function parseScopes(value) {
1700
+ if (!value) return [];
1701
+ return value.split(/[,\s]+/).map((scope) => scope.trim()).filter(Boolean);
1702
+ }
1703
+ function defaultRoleFromAccountId(accountId) {
1704
+ if (accountId === DEFAULT_GITHUB_USER_ACCOUNT_ID) return "OWNER";
1705
+ if (accountId === DEFAULT_GITHUB_AGENT_ACCOUNT_ID) return "AGENT";
1706
+ return "OWNER";
1707
+ }
1708
+ function roleFromMetadata(metadata, accountId) {
1709
+ const record = metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : {};
1710
+ const raw = nonEmptyString3(record.role ?? record.accountRole);
1711
+ const normalized = raw?.toUpperCase();
1712
+ if (normalized === "OWNER" || normalized === "AGENT" || normalized === "TEAM") {
1713
+ return normalized;
1714
+ }
1715
+ return defaultRoleFromAccountId(accountId);
1716
+ }
1717
+ async function exchangeCodeForToken(args) {
1718
+ const response = await fetch(GITHUB_TOKEN_ENDPOINT, {
1719
+ method: "POST",
1720
+ headers: {
1721
+ "Content-Type": "application/x-www-form-urlencoded",
1722
+ Accept: "application/json"
1723
+ },
1724
+ body: new URLSearchParams({
1725
+ client_id: args.clientId,
1726
+ client_secret: args.clientSecret,
1727
+ code: args.code,
1728
+ redirect_uri: args.redirectUri
1729
+ }).toString()
1730
+ });
1731
+ if (!response.ok) {
1732
+ const body = await response.text();
1733
+ throw new Error(
1734
+ `GitHub token exchange failed with ${response.status}: ${body}`
1735
+ );
1736
+ }
1737
+ const parsed = await response.json();
1738
+ if (parsed.error) {
1739
+ throw new Error(
1740
+ `GitHub token exchange returned error ${parsed.error}: ${parsed.error_description ?? "no description"}`
1741
+ );
1742
+ }
1743
+ if (!parsed.access_token) {
1744
+ throw new Error("GitHub token exchange returned no access_token.");
1745
+ }
1746
+ return parsed;
1747
+ }
1748
+ async function fetchGitHubUser(accessToken) {
1749
+ const response = await fetch(GITHUB_USER_ENDPOINT, {
1750
+ headers: {
1751
+ Authorization: `Bearer ${accessToken}`,
1752
+ Accept: "application/vnd.github+json",
1753
+ "X-GitHub-Api-Version": "2022-11-28"
1754
+ }
1755
+ });
1756
+ if (!response.ok) {
1757
+ throw new Error(`GitHub /user request failed with ${response.status}`);
1758
+ }
1759
+ const parsed = await response.json();
1760
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1761
+ throw new Error("GitHub /user returned an invalid payload.");
1762
+ }
1763
+ return parsed;
1764
+ }
1765
+ function synthesizeEnvAccounts(runtime) {
1766
+ const now = Date.now();
1767
+ return readGitHubAccounts(runtime).map((account) => ({
1768
+ id: account.accountId,
1769
+ provider: GITHUB_SERVICE_TYPE,
1770
+ label: account.label ?? `GitHub ${account.role} (${account.accountId})`,
1771
+ role: account.role === "user" ? "OWNER" : "AGENT",
1772
+ purpose: DEFAULT_PURPOSES,
1773
+ accessGate: "open",
1774
+ status: "connected",
1775
+ displayHandle: account.accountId,
1776
+ createdAt: now,
1777
+ updatedAt: now,
1778
+ metadata: { authMethod: "pat", source: "env" }
1779
+ }));
1780
+ }
1781
+ function createGitHubConnectorAccountProvider(runtime) {
1782
+ return {
1783
+ provider: GITHUB_SERVICE_TYPE,
1784
+ label: "GitHub",
1785
+ listAccounts: async (manager) => {
1786
+ const stored = await manager.getStorage().listAccounts(GITHUB_SERVICE_TYPE);
1787
+ if (stored.length > 0) return stored;
1788
+ return synthesizeEnvAccounts(runtime);
1789
+ },
1790
+ createAccount: async (input, _manager) => {
1791
+ return {
1792
+ ...input,
1793
+ provider: GITHUB_SERVICE_TYPE,
1794
+ role: input.role ?? "OWNER",
1795
+ purpose: input.purpose ?? DEFAULT_PURPOSES,
1796
+ accessGate: input.accessGate ?? "open",
1797
+ status: input.status ?? "pending"
1798
+ };
1799
+ },
1800
+ patchAccount: async (_accountId, patch, _manager) => {
1801
+ return { ...patch, provider: GITHUB_SERVICE_TYPE };
1802
+ },
1803
+ deleteAccount: async (_accountId, _manager) => {
1804
+ },
1805
+ startOAuth: async (request, _manager) => {
1806
+ const config = readClientConfig(runtime);
1807
+ const redirectUri = request.redirectUri ?? config.redirectUri;
1808
+ const scopes = request.scopes && request.scopes.length > 0 ? request.scopes : ["repo", "read:user", "user:email", "notifications"];
1809
+ const params = new URLSearchParams({
1810
+ client_id: config.clientId,
1811
+ redirect_uri: redirectUri,
1812
+ state: request.flow.state,
1813
+ scope: scopes.join(" "),
1814
+ allow_signup: "false"
1815
+ });
1816
+ return {
1817
+ authUrl: `${GITHUB_AUTHORIZATION_ENDPOINT}?${params.toString()}`,
1818
+ metadata: {
1819
+ ...request.metadata,
1820
+ requestedScopes: scopes,
1821
+ redirectUri
1822
+ }
1823
+ };
1824
+ },
1825
+ completeOAuth: async (request, manager) => {
1826
+ const code = nonEmptyString3(request.code);
1827
+ if (!code) {
1828
+ throw new Error(
1829
+ "GitHub OAuth callback is missing an authorization code."
1830
+ );
1831
+ }
1832
+ const config = readClientConfig(runtime);
1833
+ const redirectUri = nonEmptyString3(request.flow.redirectUri) ?? config.redirectUri;
1834
+ const tokens = await exchangeCodeForToken({
1835
+ clientId: config.clientId,
1836
+ clientSecret: config.clientSecret,
1837
+ redirectUri,
1838
+ code
1839
+ });
1840
+ if (!tokens.access_token) {
1841
+ throw new Error("GitHub token exchange returned no access_token.");
1842
+ }
1843
+ const user = await fetchGitHubUser(tokens.access_token);
1844
+ const externalId = nonEmptyString3(user.id ? String(user.id) : void 0);
1845
+ const login = nonEmptyString3(user.login);
1846
+ if (!login) {
1847
+ throw new Error("GitHub /user payload did not include a login.");
1848
+ }
1849
+ const expiresAt = typeof tokens.expires_in === "number" ? Date.now() + tokens.expires_in * 1e3 : void 0;
1850
+ const oauthCredentialVersion = String(Date.now());
1851
+ const accountMetadata = {
1852
+ authMethod: "oauth",
1853
+ login,
1854
+ githubUserId: user.id ?? null,
1855
+ email: nonEmptyString3(user.email) ?? null,
1856
+ type: nonEmptyString3(user.type) ?? null,
1857
+ tokenType: nonEmptyString3(tokens.token_type) ?? "bearer",
1858
+ grantedScopes: parseScopes(tokens.scope),
1859
+ hasRefreshToken: Boolean(tokens.refresh_token),
1860
+ expiresAt,
1861
+ oauthCredentialVersion
1862
+ };
1863
+ const pendingAccount = await manager.upsertAccount(
1864
+ GITHUB_SERVICE_TYPE,
1865
+ {
1866
+ provider: GITHUB_SERVICE_TYPE,
1867
+ role: roleFromMetadata(request.flow.metadata, request.flow.accountId),
1868
+ purpose: DEFAULT_PURPOSES,
1869
+ accessGate: "open",
1870
+ status: "pending",
1871
+ externalId: externalId ?? login,
1872
+ displayHandle: login,
1873
+ label: nonEmptyString3(user.name) ?? login,
1874
+ metadata: accountMetadata
1875
+ },
1876
+ request.flow.accountId
1877
+ );
1878
+ const credentialPersist = await persistConnectorCredentialRefs({
1879
+ runtime,
1880
+ manager,
1881
+ provider: GITHUB_SERVICE_TYPE,
1882
+ accountIdForRef: pendingAccount.id,
1883
+ storageAccountId: pendingAccount.id,
1884
+ caller: "plugin-github",
1885
+ credentials: [
1886
+ {
1887
+ credentialType: "oauth.tokens",
1888
+ value: JSON.stringify({
1889
+ access_token: tokens.access_token,
1890
+ ...tokens.refresh_token ? { refresh_token: tokens.refresh_token } : {},
1891
+ ...expiresAt !== void 0 ? { expires_at: expiresAt } : {},
1892
+ token_type: nonEmptyString3(tokens.token_type) ?? "bearer",
1893
+ scope: tokens.scope ?? ""
1894
+ }),
1895
+ ...expiresAt !== void 0 ? { expiresAt } : {},
1896
+ metadata: {
1897
+ provider: GITHUB_SERVICE_TYPE,
1898
+ hasRefreshToken: Boolean(tokens.refresh_token)
1899
+ }
2081
1900
  }
2082
- if (reviewers) {
2083
- parts2.push(`**Reviewers Requested:** ${reviewers}`);
1901
+ ]
1902
+ });
1903
+ const accountPatch = {
1904
+ ...pendingAccount,
1905
+ id: pendingAccount.id,
1906
+ provider: GITHUB_SERVICE_TYPE,
1907
+ status: "connected",
1908
+ metadata: {
1909
+ ...accountMetadata,
1910
+ credentialRefs: credentialPersist.refs,
1911
+ credentialRefStorage: {
1912
+ vaultAvailable: credentialPersist.vaultAvailable,
1913
+ storageAvailable: credentialPersist.storageAvailable
2084
1914
  }
2085
- parts2.push("", `**Changes:** +${pr.additions} / -${pr.deletions} (${pr.changedFiles} files)`, "", "### Description", pr.body ?? "_No description provided_", "", `**URL:** ${pr.htmlUrl}`);
2086
- return { text: parts2.join(`
2087
- `) };
2088
- }
2089
- const labels = issue.labels.map((l) => l.name).join(", ");
2090
- const assignees = issue.assignees.map((a) => a.login).join(", ");
2091
- const parts = [
2092
- `## Issue #${issue.number}: ${issue.title}`,
2093
- "",
2094
- `**State:** ${issue.state}${issue.stateReason ? ` (${issue.stateReason})` : ""}`,
2095
- `**Author:** ${issue.user.login}`,
2096
- `**Created:** ${issue.createdAt}`,
2097
- `**Updated:** ${issue.updatedAt}`,
2098
- `**Comments:** ${issue.comments}`
2099
- ];
2100
- if (labels) {
2101
- parts.push(`**Labels:** ${labels}`);
2102
- }
2103
- if (assignees) {
2104
- parts.push(`**Assignees:** ${assignees}`);
2105
1915
  }
2106
- if (issue.milestone) {
2107
- parts.push(`**Milestone:** ${issue.milestone.title}`);
2108
- }
2109
- parts.push("", "### Description", issue.body ?? "_No description provided_", "", `**URL:** ${issue.htmlUrl}`);
2110
- return { text: parts.join(`
2111
- `) };
2112
- } catch {
2113
- return {
2114
- text: `Issue/PR #${issueNumber} not found in ${config.owner}/${config.repo}`
2115
- };
2116
- }
2117
- } catch (error) {
2118
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
2119
- return { text: `Unable to fetch issue context: ${errorMessage}` };
1916
+ };
1917
+ logger4.info(
1918
+ {
1919
+ src: "plugin:github:connector",
1920
+ login
1921
+ },
1922
+ "GitHub OAuth completed"
1923
+ );
1924
+ return {
1925
+ account: accountPatch,
1926
+ flow: { status: "completed" }
1927
+ };
2120
1928
  }
1929
+ };
1930
+ }
1931
+
1932
+ // src/routes/github-routes.ts
1933
+ import { logger as logger5 } from "@elizaos/core";
1934
+
1935
+ // src/github-credentials.ts
1936
+ import fs from "fs/promises";
1937
+ import path from "path";
1938
+ import { resolveStateDir } from "@elizaos/core";
1939
+ function getCredentialFilePath() {
1940
+ return path.join(resolveStateDir(), "credentials", "github.json");
1941
+ }
1942
+ function isGitHubCredentials(value) {
1943
+ if (!value || typeof value !== "object") return false;
1944
+ const v = value;
1945
+ return typeof v.token === "string" && typeof v.username === "string" && Array.isArray(v.scopes) && v.scopes.every((s) => typeof s === "string") && typeof v.savedAt === "number";
1946
+ }
1947
+ async function loadCredentials() {
1948
+ const filePath = getCredentialFilePath();
1949
+ let raw;
1950
+ try {
1951
+ raw = await fs.readFile(filePath, "utf-8");
1952
+ } catch {
1953
+ return null;
2121
1954
  }
2122
- };
2123
- // providers/repositoryState.ts
2124
- var spec9 = requireProviderSpec("repositoryState");
2125
- var repositoryStateProvider = {
2126
- name: spec9.name,
2127
- description: "Provides context about the current GitHub repository including recent activity",
2128
- dynamic: true,
2129
- get: async (runtime, _message, _state) => {
2130
- const service = runtime.getService(GITHUB_SERVICE_NAME);
2131
- if (!service) {
2132
- return { text: null };
1955
+ let parsed;
1956
+ try {
1957
+ parsed = JSON.parse(raw);
1958
+ } catch {
1959
+ return null;
1960
+ }
1961
+ return isGitHubCredentials(parsed) ? parsed : null;
1962
+ }
1963
+ async function loadMetadata() {
1964
+ const creds = await loadCredentials();
1965
+ if (!creds) return null;
1966
+ const { token: _token, ...metadata } = creds;
1967
+ return metadata;
1968
+ }
1969
+ async function saveCredentials(creds) {
1970
+ const filePath = getCredentialFilePath();
1971
+ const directory = path.dirname(filePath);
1972
+ await fs.mkdir(directory, { recursive: true, mode: 448 });
1973
+ await fs.chmod(directory, 448);
1974
+ const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
1975
+ await fs.writeFile(tmpPath, JSON.stringify(creds, null, 2), {
1976
+ mode: 384
1977
+ });
1978
+ await fs.rename(tmpPath, filePath);
1979
+ }
1980
+ async function clearCredentials() {
1981
+ const filePath = getCredentialFilePath();
1982
+ try {
1983
+ await fs.unlink(filePath);
1984
+ } catch (err) {
1985
+ if (err.code === "ENOENT") return;
1986
+ throw err;
1987
+ }
1988
+ }
1989
+ function buildCredentialsFromUserResponse(token, user, scopes, now = Date.now()) {
1990
+ return {
1991
+ token,
1992
+ username: user.login,
1993
+ scopes,
1994
+ savedAt: now
1995
+ };
1996
+ }
1997
+
1998
+ // src/routes/github-routes.ts
1999
+ var GITHUB_USER_URL = "https://api.github.com/user";
2000
+ var VALIDATION_TIMEOUT_MS = 1e4;
2001
+ var MAX_BODY_BYTES = 8 * 1024;
2002
+ async function readJsonBody(req) {
2003
+ const chunks = [];
2004
+ let total = 0;
2005
+ for await (const chunk of req) {
2006
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
2007
+ total += buf.length;
2008
+ if (total > MAX_BODY_BYTES) return null;
2009
+ chunks.push(buf);
2010
+ }
2011
+ if (chunks.length === 0) return null;
2012
+ try {
2013
+ const parsed = JSON.parse(Buffer.concat(chunks).toString("utf-8"));
2014
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
2015
+ } catch {
2016
+ return null;
2017
+ }
2018
+ }
2019
+ function sendJson(ctx, status, body) {
2020
+ if (ctx.json) {
2021
+ ctx.json(status, body);
2022
+ return;
2023
+ }
2024
+ ctx.res.statusCode = status;
2025
+ ctx.res.setHeader("Content-Type", "application/json; charset=utf-8");
2026
+ ctx.res.end(JSON.stringify(body));
2027
+ }
2028
+ function metadataToStatus(metadata) {
2029
+ if (!metadata) return { connected: false };
2030
+ return {
2031
+ connected: true,
2032
+ username: metadata.username,
2033
+ scopes: metadata.scopes,
2034
+ savedAt: metadata.savedAt
2035
+ };
2036
+ }
2037
+ async function validateToken(token, fetchImpl) {
2038
+ const controller = new AbortController();
2039
+ const timer = setTimeout(() => controller.abort(), VALIDATION_TIMEOUT_MS);
2040
+ let response;
2041
+ try {
2042
+ response = await fetchImpl(GITHUB_USER_URL, {
2043
+ headers: {
2044
+ Authorization: `Bearer ${token}`,
2045
+ Accept: "application/vnd.github+json",
2046
+ "User-Agent": "eliza-github-connection"
2047
+ },
2048
+ signal: controller.signal
2049
+ });
2050
+ } finally {
2051
+ clearTimeout(timer);
2052
+ }
2053
+ if (response.status === 401) {
2054
+ throw new Error("Token rejected by GitHub: bad credentials.");
2055
+ }
2056
+ if (response.status === 403) {
2057
+ throw new Error(
2058
+ "Token rejected by GitHub: forbidden. Check the token has at least `read:user` scope."
2059
+ );
2060
+ }
2061
+ if (!response.ok) {
2062
+ throw new Error(
2063
+ `GitHub returned ${response.status} validating the token. Try again or generate a new token.`
2064
+ );
2065
+ }
2066
+ const body = await response.json();
2067
+ if (typeof body?.login !== "string" || body.login.length === 0) {
2068
+ throw new Error("GitHub /user response was missing the login field.");
2069
+ }
2070
+ const scopesHeader = response.headers.get("x-oauth-scopes") ?? "";
2071
+ const scopes = scopesHeader.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
2072
+ return { user: body, scopes };
2073
+ }
2074
+ async function handleGetToken(ctx) {
2075
+ const metadata = await loadMetadata();
2076
+ sendJson(ctx, 200, metadataToStatus(metadata));
2077
+ return true;
2078
+ }
2079
+ async function handlePostToken(ctx) {
2080
+ const body = await readJsonBody(ctx.req);
2081
+ const token = body && typeof body.token === "string" ? body.token.trim() : "";
2082
+ if (token.length === 0) {
2083
+ sendJson(ctx, 400, { error: "Missing `token` in request body." });
2084
+ return true;
2085
+ }
2086
+ const fetchImpl = ctx.fetch ?? fetch;
2087
+ let validated;
2088
+ try {
2089
+ validated = await validateToken(token, fetchImpl);
2090
+ } catch (err) {
2091
+ const message = err instanceof Error ? err.message : String(err);
2092
+ logger5.warn(`[github-routes] token validation failed: ${message}`);
2093
+ sendJson(ctx, 400, { error: message });
2094
+ return true;
2095
+ }
2096
+ const credentials = buildCredentialsFromUserResponse(
2097
+ token,
2098
+ validated.user,
2099
+ validated.scopes
2100
+ );
2101
+ await saveCredentials(credentials);
2102
+ logger5.info(
2103
+ `[github-routes] saved github token for @${validated.user.login} (scopes=${validated.scopes.join(",") || "(none)"})`
2104
+ );
2105
+ sendJson(ctx, 200, metadataToStatus(credentials));
2106
+ return true;
2107
+ }
2108
+ async function handleDeleteToken(ctx) {
2109
+ await clearCredentials();
2110
+ logger5.info("[github-routes] cleared saved github token");
2111
+ sendJson(ctx, 200, { connected: false });
2112
+ return true;
2113
+ }
2114
+ async function handleGitHubRoutes(ctx) {
2115
+ if (ctx.pathname !== "/api/github/token") return false;
2116
+ switch (ctx.method) {
2117
+ case "GET":
2118
+ return handleGetToken(ctx);
2119
+ case "POST":
2120
+ return handlePostToken(ctx);
2121
+ case "DELETE":
2122
+ return handleDeleteToken(ctx);
2123
+ default:
2124
+ sendJson(ctx, 405, { error: "Method not allowed" });
2125
+ return true;
2126
+ }
2127
+ }
2128
+
2129
+ // src/search-category.ts
2130
+ var GITHUB_PULL_REQUESTS_SEARCH_CATEGORY = {
2131
+ category: "github_pull_requests",
2132
+ label: "GitHub pull requests",
2133
+ description: "Search GitHub pull requests in a repo or across accessible repositories.",
2134
+ contexts: ["code", "automation"],
2135
+ filters: [
2136
+ { name: "query", label: "Query", type: "string" },
2137
+ {
2138
+ name: "repo",
2139
+ label: "Repository",
2140
+ description: "Repository in owner/name format. Omit to search accessible repositories.",
2141
+ type: "string"
2142
+ },
2143
+ {
2144
+ name: "state",
2145
+ label: "State",
2146
+ description: "Pull request state.",
2147
+ type: "enum",
2148
+ default: "open",
2149
+ options: [
2150
+ { label: "Open", value: "open" },
2151
+ { label: "Closed", value: "closed" },
2152
+ { label: "All", value: "all" }
2153
+ ]
2154
+ },
2155
+ {
2156
+ name: "author",
2157
+ label: "Author",
2158
+ description: "GitHub login to filter by.",
2159
+ type: "string"
2160
+ },
2161
+ {
2162
+ name: "as",
2163
+ label: "Identity",
2164
+ description: "Configured GitHub identity token to use.",
2165
+ type: "enum",
2166
+ default: "agent",
2167
+ options: [
2168
+ { label: "Agent", value: "agent" },
2169
+ { label: "User", value: "user" }
2170
+ ]
2171
+ },
2172
+ {
2173
+ name: "accountId",
2174
+ label: "Account",
2175
+ description: "Optional configured GitHub account id. Defaults by identity role.",
2176
+ type: "string"
2177
+ },
2178
+ {
2179
+ name: "limit",
2180
+ label: "Limit",
2181
+ description: "Maximum pull requests to return.",
2182
+ type: "number",
2183
+ default: 50
2133
2184
  }
2134
- try {
2135
- const config = service.getConfig();
2136
- if (!config.owner || !config.repo) {
2137
- return {
2138
- text: "GitHub repository not configured. Please set GITHUB_OWNER and GITHUB_REPO."
2139
- };
2140
- }
2141
- const repo = await service.getRepository({
2142
- owner: config.owner,
2143
- repo: config.repo
2144
- });
2145
- const issues = await service.listIssues({
2146
- owner: config.owner,
2147
- repo: config.repo,
2148
- state: "open",
2149
- perPage: 5
2150
- });
2151
- const pullRequests = await service.listPullRequests({
2152
- owner: config.owner,
2153
- repo: config.repo,
2154
- state: "open",
2155
- perPage: 5
2185
+ ],
2186
+ resultSchemaSummary: "PRSummary[] with repo, number, title, author, state, and url.",
2187
+ capabilities: ["pull-requests", "issues-search", "repositories"],
2188
+ source: "plugin:github",
2189
+ serviceType: "github"
2190
+ };
2191
+ function hasSearchCategory(runtime, category) {
2192
+ try {
2193
+ runtime.getSearchCategory(category, { includeDisabled: true });
2194
+ return true;
2195
+ } catch {
2196
+ return false;
2197
+ }
2198
+ }
2199
+ function registerGitHubSearchCategory(runtime) {
2200
+ if (!hasSearchCategory(runtime, GITHUB_PULL_REQUESTS_SEARCH_CATEGORY.category)) {
2201
+ runtime.registerSearchCategory(GITHUB_PULL_REQUESTS_SEARCH_CATEGORY);
2202
+ }
2203
+ }
2204
+
2205
+ // src/services/github-service.ts
2206
+ import { logger as logger6, Service } from "@elizaos/core";
2207
+ import { Octokit } from "@octokit/rest";
2208
+ function normalizeSelector(selector) {
2209
+ if (selector === "user" || selector === "agent") {
2210
+ return { role: selector };
2211
+ }
2212
+ return {
2213
+ accountId: typeof selector.accountId === "string" && selector.accountId.trim() ? selector.accountId.trim() : void 0,
2214
+ role: selector.role ?? selector.as ?? "agent"
2215
+ };
2216
+ }
2217
+ var GitHubService = class _GitHubService extends Service {
2218
+ constructor(runtime, createClient = (auth) => new Octokit({ auth })) {
2219
+ super(runtime);
2220
+ this.createClient = createClient;
2221
+ }
2222
+ createClient;
2223
+ static serviceType = GITHUB_SERVICE_TYPE;
2224
+ capabilityDescription = "GitHub REST API integration for PRs, issues, and notifications";
2225
+ clients = /* @__PURE__ */ new Map();
2226
+ static async start(runtime, createClient) {
2227
+ const service = new _GitHubService(runtime, createClient);
2228
+ await service.initialize();
2229
+ return service;
2230
+ }
2231
+ async initialize() {
2232
+ if (!this.runtime) {
2233
+ return;
2234
+ }
2235
+ const accounts = await readGitHubAccountsWithConnectorCredentials(this.runtime);
2236
+ this.clients.clear();
2237
+ for (const account of accounts) {
2238
+ this.clients.set(account.accountId, {
2239
+ account,
2240
+ client: this.createClient(account.token)
2156
2241
  });
2157
- const parts = [
2158
- `## GitHub Repository: ${repo.fullName}`,
2159
- "",
2160
- `**Description:** ${repo.description ?? "No description"}`,
2161
- `**Default Branch:** ${repo.defaultBranch}`,
2162
- `**Language:** ${repo.language ?? "Not specified"}`,
2163
- `**Stars:** ${repo.stargazersCount} | **Forks:** ${repo.forksCount}`,
2164
- `**Open Issues:** ${repo.openIssuesCount}`,
2165
- ""
2166
- ];
2167
- if (issues.length > 0) {
2168
- parts.push("### Recent Open Issues");
2169
- for (const issue of issues) {
2170
- const labels = issue.labels.map((l) => l.name).join(", ");
2171
- parts.push(`- #${issue.number}: ${issue.title}${labels ? ` [${labels}]` : ""}`);
2172
- }
2173
- parts.push("");
2174
- }
2175
- if (pullRequests.length > 0) {
2176
- parts.push("### Recent Open Pull Requests");
2177
- for (const pr of pullRequests) {
2178
- const status = pr.draft ? "[DRAFT] " : "";
2179
- parts.push(`- #${pr.number}: ${status}${pr.title} (${pr.head.ref} → ${pr.base.ref})`);
2180
- }
2181
- parts.push("");
2242
+ }
2243
+ for (const role of ["user", "agent"]) {
2244
+ if (!resolveGitHubAccount(accounts, { role })) {
2245
+ logger6.info(
2246
+ `[GitHubService] no GitHub ${role} account configured \u2014 ${role}-acting calls will be rejected`
2247
+ );
2182
2248
  }
2183
- return { text: parts.join(`
2184
- `) };
2185
- } catch (error) {
2186
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
2187
- return {
2188
- text: `Unable to fetch GitHub repository state: ${errorMessage}`
2189
- };
2190
2249
  }
2250
+ logger6.info(
2251
+ `[GitHubService] configured ${this.clients.size} GitHub account(s)`
2252
+ );
2253
+ }
2254
+ getOctokit(selector) {
2255
+ const selection = normalizeSelector(selector);
2256
+ const account = resolveGitHubAccount(
2257
+ Array.from(this.clients.values()).map((record) => record.account),
2258
+ selection
2259
+ );
2260
+ if (!account) {
2261
+ return null;
2262
+ }
2263
+ return this.clients.get(account.accountId)?.client ?? null;
2264
+ }
2265
+ /**
2266
+ * Allows tests to inject an Octokit-shaped mock without going through
2267
+ * environment variables. Not part of the public runtime contract.
2268
+ */
2269
+ setClientForTesting(as, client, accountId = defaultGitHubAccountIdForRole(as)) {
2270
+ if (!client) {
2271
+ this.clients.delete(accountId);
2272
+ return;
2273
+ }
2274
+ this.clients.set(accountId, {
2275
+ account: { accountId, role: as, token: "test" },
2276
+ client
2277
+ });
2278
+ }
2279
+ async stop() {
2280
+ this.clients.clear();
2191
2281
  }
2192
2282
  };
2193
- // providers/index.ts
2194
- var allProviders = [repositoryStateProvider, issueContextProvider];
2195
2283
 
2196
- // index.ts
2284
+ // src/index.ts
2285
+ function createGitHubRouteHandler(method) {
2286
+ return async (req, res, runtime) => {
2287
+ const httpReq = req;
2288
+ const httpRes = res;
2289
+ const url = new URL(httpReq.url ?? "/api/github/token", "http://localhost");
2290
+ void runtime;
2291
+ await handleGitHubRoutes({
2292
+ req: httpReq,
2293
+ res: httpRes,
2294
+ method,
2295
+ pathname: url.pathname
2296
+ });
2297
+ };
2298
+ }
2299
+ var githubRoutes = [
2300
+ {
2301
+ type: "GET",
2302
+ path: "/api/github/token",
2303
+ rawPath: true,
2304
+ handler: createGitHubRouteHandler("GET")
2305
+ },
2306
+ {
2307
+ type: "POST",
2308
+ path: "/api/github/token",
2309
+ rawPath: true,
2310
+ handler: createGitHubRouteHandler("POST")
2311
+ },
2312
+ {
2313
+ type: "DELETE",
2314
+ path: "/api/github/token",
2315
+ rawPath: true,
2316
+ handler: createGitHubRouteHandler("DELETE")
2317
+ }
2318
+ ];
2197
2319
  var githubPlugin = {
2198
2320
  name: "github",
2199
- description: "GitHub integration for repository management, issues, pull requests, and code reviews",
2321
+ description: "GitHub integration for pull requests, issues, and notification triage",
2200
2322
  services: [GitHubService],
2201
- actions: allActions,
2202
- providers: allProviders,
2323
+ actions: [...promoteSubactionsToActions(githubAction)],
2324
+ routes: githubRoutes,
2203
2325
  init: async (_config, runtime) => {
2204
- const token = runtime.getSetting("GITHUB_API_TOKEN");
2205
- const owner = runtime.getSetting("GITHUB_OWNER");
2206
- const repo = runtime.getSetting("GITHUB_REPO");
2207
- const branch = runtime.getSetting("GITHUB_BRANCH") ?? "main";
2208
- logger9.info(`GitHub Plugin - Token: ${token ? "configured" : "not configured"}, Owner: ${owner ?? "not set"}, Repo: ${repo ?? "not set"}, Branch: ${branch}`);
2209
- if (!token) {
2210
- logger9.warn("GitHub API Token not provided - plugin will not be functional");
2326
+ registerGitHubSearchCategory(runtime);
2327
+ try {
2328
+ const manager = getConnectorAccountManager2(runtime);
2329
+ manager.registerProvider(createGitHubConnectorAccountProvider(runtime));
2330
+ } catch (err) {
2331
+ logger7.warn(
2332
+ {
2333
+ src: "plugin:github",
2334
+ err: err instanceof Error ? err.message : String(err)
2335
+ },
2336
+ "Failed to register GitHub provider with ConnectorAccountManager"
2337
+ );
2211
2338
  }
2212
2339
  }
2213
2340
  };
2214
- var typescript_default = githubPlugin;
2341
+ var index_default = githubPlugin;
2215
2342
  export {
2216
- typescript_default as default
2343
+ DEFAULT_GITHUB_AGENT_ACCOUNT_ID,
2344
+ DEFAULT_GITHUB_USER_ACCOUNT_ID,
2345
+ GITHUB_SERVICE_TYPE,
2346
+ GitHubActions,
2347
+ GitHubService,
2348
+ createGitHubConnectorAccountProvider,
2349
+ index_default as default,
2350
+ defaultGitHubAccountIdForRole,
2351
+ githubAction,
2352
+ githubPlugin,
2353
+ issueOpAction,
2354
+ normalizeGitHubAccountId,
2355
+ notificationTriageAction,
2356
+ prOpAction,
2357
+ readGitHubAccounts,
2358
+ readGitHubAccountsWithConnectorCredentials,
2359
+ resolveGitHubAccount,
2360
+ resolveGitHubAccountSelection,
2361
+ scoreNotification
2217
2362
  };
2218
-
2219
- //# debugId=8467D22198144F6864756E2164756E21