@2en/clawly-plugins 1.34.0-beta.1 → 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.
- package/github.ts +212 -0
- package/index.ts +2 -0
- 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/index.ts
CHANGED
|
@@ -51,6 +51,7 @@ import {setupConfig} from './config-setup'
|
|
|
51
51
|
import {registerCronHistory} from './cron-history'
|
|
52
52
|
import {registerCronHook} from './cron-hook'
|
|
53
53
|
import {registerEmail} from './email'
|
|
54
|
+
import {registerGitHub} from './github'
|
|
54
55
|
import {registerGateway} from './gateway'
|
|
55
56
|
import {getGatewayConfig} from './gateway-fetch'
|
|
56
57
|
import {
|
|
@@ -99,6 +100,7 @@ export default {
|
|
|
99
100
|
if (gw.baseUrl && gw.token) {
|
|
100
101
|
registerEmail(api, gw)
|
|
101
102
|
registerCalendar(api, gw)
|
|
103
|
+
registerGitHub(api, gw)
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
api.logger.info(`Loaded ${api.id} plugin. (debug-instrumented build)`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@2en/clawly-plugins",
|
|
3
|
-
"version": "1.34.0-beta.
|
|
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",
|