@2en/clawly-plugins 1.34.0-beta.2 → 1.34.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/github.ts +212 -0
  2. package/package.json +2 -1
package/github.ts ADDED
@@ -0,0 +1,212 @@
1
+ /**
2
+ * GitHub integration via Clawly backend.
3
+ *
4
+ * Gateway methods (external RPC):
5
+ * - github.listRepos — list accessible repositories
6
+ * - github.authToken — get a short-lived installation access token for git operations
7
+ * - github.getConnectUrl — get OAuth URL to connect a GitHub account
8
+ *
9
+ * Agent tools (LLM-callable):
10
+ * - github_list_repos — list accessible repositories
11
+ * - github_auth_token — get a short-lived token for git clone/pull/fetch
12
+ * - github_connect — get OAuth URL to connect a GitHub account
13
+ */
14
+
15
+ import type {PluginApi} from './index'
16
+ import type {GatewayCfg, HandlerResult} from './gateway-fetch'
17
+ import {gatewayFetch, toToolResult} from './gateway-fetch'
18
+
19
+ // ── Handlers ──
20
+
21
+ async function handleListRepos(
22
+ cfg: GatewayCfg,
23
+ params: Record<string, unknown>,
24
+ ): Promise<HandlerResult> {
25
+ const qs = new URLSearchParams()
26
+ if (typeof params.page === 'number') qs.set('page', String(params.page))
27
+ if (typeof params.perPage === 'number') qs.set('per_page', String(params.perPage))
28
+
29
+ const query = qs.toString()
30
+ const res = await gatewayFetch(cfg, `/github/repos${query ? `?${query}` : ''}`)
31
+ const data = await res.json()
32
+ if (!res.ok)
33
+ return {
34
+ ok: false,
35
+ error: {code: data.error ?? 'error', message: data.message ?? res.statusText},
36
+ }
37
+ return {ok: true, data}
38
+ }
39
+
40
+ async function handleAuthToken(cfg: GatewayCfg): Promise<HandlerResult> {
41
+ const res = await gatewayFetch(cfg, '/github/clone-token')
42
+ const data = (await res.json()) as {
43
+ token?: string
44
+ expiresAt?: string
45
+ error?: string
46
+ message?: string
47
+ }
48
+ if (!res.ok)
49
+ return {
50
+ ok: false,
51
+ error: {code: data.error ?? 'error', message: data.message ?? res.statusText},
52
+ }
53
+ if (!data.token) return {ok: false, error: {code: 'error', message: 'No token returned'}}
54
+ return {
55
+ ok: true,
56
+ data: {
57
+ token: data.token,
58
+ expiresAt: data.expiresAt,
59
+ usage: `git clone https://x-access-token:<token>@github.com/{owner}/{repo}.git`,
60
+ },
61
+ }
62
+ }
63
+
64
+ async function handleGetConnectUrl(
65
+ cfg: GatewayCfg,
66
+ params: Record<string, unknown>,
67
+ ): Promise<HandlerResult> {
68
+ const res = await gatewayFetch(cfg, '/connections/start', {
69
+ method: 'POST',
70
+ body: JSON.stringify({provider: 'github'}),
71
+ })
72
+ const data = await res.json()
73
+ if (!res.ok)
74
+ return {
75
+ ok: false,
76
+ error: {code: data.error ?? 'error', message: data.message ?? res.statusText},
77
+ }
78
+ return {ok: true, data}
79
+ }
80
+
81
+ // ── Registration ──
82
+
83
+ export function registerGitHub(api: PluginApi, cfg: GatewayCfg) {
84
+ // Gateway methods (external RPC)
85
+
86
+ api.registerGatewayMethod('github.listRepos', async ({params, respond}) => {
87
+ try {
88
+ const r = await handleListRepos(cfg, params)
89
+ r.ok ? respond(true, r.data) : respond(false, undefined, r.error)
90
+ } catch (err) {
91
+ respond(false, undefined, {
92
+ code: 'error',
93
+ message: err instanceof Error ? err.message : String(err),
94
+ })
95
+ }
96
+ })
97
+
98
+ api.registerGatewayMethod('github.authToken', async ({respond}) => {
99
+ try {
100
+ const r = await handleAuthToken(cfg)
101
+ r.ok ? respond(true, r.data) : respond(false, undefined, r.error)
102
+ } catch (err) {
103
+ respond(false, undefined, {
104
+ code: 'error',
105
+ message: err instanceof Error ? err.message : String(err),
106
+ })
107
+ }
108
+ })
109
+
110
+ api.registerGatewayMethod('github.getConnectUrl', async ({params, respond}) => {
111
+ try {
112
+ const r = await handleGetConnectUrl(cfg, params)
113
+ r.ok ? respond(true, r.data) : respond(false, undefined, r.error)
114
+ } catch (err) {
115
+ respond(false, undefined, {
116
+ code: 'error',
117
+ message: err instanceof Error ? err.message : String(err),
118
+ })
119
+ }
120
+ })
121
+
122
+ // Agent tools (LLM-callable)
123
+
124
+ api.registerTool({
125
+ name: 'github_list_repos',
126
+ description:
127
+ 'List GitHub repositories accessible via the connected GitHub App installation. ' +
128
+ 'Returns repo names, descriptions, languages, and default branches. ' +
129
+ 'If no GitHub account is connected, the result will contain a CONNECTION_REQUIRED error — ' +
130
+ 'use github_connect to get an OAuth URL.',
131
+ parameters: {
132
+ type: 'object',
133
+ properties: {
134
+ page: {type: 'number', description: 'Page number (default 1)'},
135
+ perPage: {type: 'number', description: 'Results per page (default 30, max 100)'},
136
+ },
137
+ },
138
+ async execute(_toolCallId, params) {
139
+ try {
140
+ return toToolResult(await handleListRepos(cfg, params))
141
+ } catch (err) {
142
+ return {
143
+ content: [
144
+ {
145
+ type: 'text',
146
+ text: JSON.stringify({error: err instanceof Error ? err.message : String(err)}),
147
+ },
148
+ ],
149
+ }
150
+ }
151
+ },
152
+ })
153
+
154
+ api.registerTool({
155
+ name: 'github_auth_token',
156
+ description:
157
+ 'Get a short-lived GitHub installation access token (~1 hour) for git operations. ' +
158
+ 'Use the returned token to authenticate git commands, e.g.: ' +
159
+ 'git clone https://x-access-token:<token>@github.com/{owner}/{repo}.git ' +
160
+ 'If no GitHub account is connected, the result will contain a CONNECTION_REQUIRED error — ' +
161
+ 'use github_connect to get an OAuth URL.',
162
+ parameters: {
163
+ type: 'object',
164
+ properties: {},
165
+ },
166
+ async execute() {
167
+ try {
168
+ return toToolResult(await handleAuthToken(cfg))
169
+ } catch (err) {
170
+ return {
171
+ content: [
172
+ {
173
+ type: 'text',
174
+ text: JSON.stringify({error: err instanceof Error ? err.message : String(err)}),
175
+ },
176
+ ],
177
+ }
178
+ }
179
+ },
180
+ })
181
+
182
+ api.registerTool({
183
+ name: 'github_connect',
184
+ description:
185
+ 'Get an OAuth URL to connect a GitHub account. ' +
186
+ 'Returns an authorizeUrl that the user should open in a browser to authorize access. ' +
187
+ 'Use this when GitHub operations return CONNECTION_REQUIRED.',
188
+ parameters: {
189
+ type: 'object',
190
+ properties: {},
191
+ },
192
+ async execute(_toolCallId, params) {
193
+ try {
194
+ return toToolResult(await handleGetConnectUrl(cfg, params))
195
+ } catch (err) {
196
+ return {
197
+ content: [
198
+ {
199
+ type: 'text',
200
+ text: JSON.stringify({error: err instanceof Error ? err.message : String(err)}),
201
+ },
202
+ ],
203
+ }
204
+ }
205
+ },
206
+ })
207
+
208
+ api.logger.info(
209
+ 'github: registered gateway methods (github.listRepos, github.authToken, github.getConnectUrl) ' +
210
+ 'and agent tools (github_list_repos, github_auth_token, github_connect)',
211
+ )
212
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2en/clawly-plugins",
3
- "version": "1.34.0-beta.2",
3
+ "version": "1.34.0-beta.3",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "repository": {
@@ -33,6 +33,7 @@
33
33
  "cron-history.ts",
34
34
  "cron-hook.ts",
35
35
  "email.ts",
36
+ "github.ts",
36
37
  "gateway-fetch.ts",
37
38
  "http",
38
39
  "media-understanding.ts",