devin-oauth-opencode 0.2.0
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/README.md +12 -0
- package/bin/setup.js +199 -0
- package/dist/auth.d.ts +17 -0
- package/dist/auth.js +88 -0
- package/dist/browser-auth.d.ts +34 -0
- package/dist/browser-auth.js +126 -0
- package/dist/cloud.d.ts +21 -0
- package/dist/cloud.js +332 -0
- package/dist/devin.d.ts +34 -0
- package/dist/devin.js +9 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +108 -0
- package/dist/model-discovery.d.ts +37 -0
- package/dist/model-discovery.js +340 -0
- package/dist/models.d.ts +27 -0
- package/dist/models.js +150 -0
- package/dist/proxy.d.ts +10 -0
- package/dist/proxy.js +405 -0
- package/package.json +64 -0
package/dist/cloud.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import http2 from "node:http2";
|
|
3
|
+
import { getApiKey, getLatestDevinVersion, DevinError, DevinErrorCode } from "./auth";
|
|
4
|
+
import { resolveDevinModel } from "./models";
|
|
5
|
+
const CODEIUM_API_ORIGIN = "https://server.codeium.com";
|
|
6
|
+
const GET_CHAT_MESSAGE_PATH = "/exa.api_server_pb.ApiServerService/GetChatMessage";
|
|
7
|
+
const CLOUD_SESSION_IDLE_MS = 60_000;
|
|
8
|
+
let cloudClient;
|
|
9
|
+
let cloudConnectPromise;
|
|
10
|
+
let cloudIdleTimer;
|
|
11
|
+
function encodeVarint(value) {
|
|
12
|
+
const bytes = [];
|
|
13
|
+
let current = BigInt(value);
|
|
14
|
+
while (current > 127n) {
|
|
15
|
+
bytes.push(Number(current & 0x7fn) | 0x80);
|
|
16
|
+
current >>= 7n;
|
|
17
|
+
}
|
|
18
|
+
bytes.push(Number(current));
|
|
19
|
+
return bytes;
|
|
20
|
+
}
|
|
21
|
+
function encodeString(fieldNum, value) {
|
|
22
|
+
const bytes = Buffer.from(value, "utf8");
|
|
23
|
+
return Buffer.from([
|
|
24
|
+
...encodeVarint((fieldNum << 3) | 2),
|
|
25
|
+
...encodeVarint(bytes.length),
|
|
26
|
+
...bytes,
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
function encodeMessage(fieldNum, value) {
|
|
30
|
+
return Buffer.from([
|
|
31
|
+
...encodeVarint((fieldNum << 3) | 2),
|
|
32
|
+
...encodeVarint(value.length),
|
|
33
|
+
...value,
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
function encodeVarintField(fieldNum, value) {
|
|
37
|
+
return Buffer.from([...encodeVarint((fieldNum << 3) | 0), ...encodeVarint(value)]);
|
|
38
|
+
}
|
|
39
|
+
function connectEnvelope(payload) {
|
|
40
|
+
const frame = Buffer.alloc(5);
|
|
41
|
+
frame[0] = 0;
|
|
42
|
+
frame.writeUInt32BE(payload.length, 1);
|
|
43
|
+
return Buffer.concat([frame, payload]);
|
|
44
|
+
}
|
|
45
|
+
function readVarint(buffer, offset) {
|
|
46
|
+
let value = 0n;
|
|
47
|
+
let shift = 0n;
|
|
48
|
+
let next = offset;
|
|
49
|
+
while (next < buffer.length) {
|
|
50
|
+
const byte = buffer[next];
|
|
51
|
+
value |= BigInt(byte & 0x7f) << shift;
|
|
52
|
+
next++;
|
|
53
|
+
if (byte < 0x80)
|
|
54
|
+
return { value, next };
|
|
55
|
+
shift += 7n;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function parseFields(buffer) {
|
|
60
|
+
const fields = [];
|
|
61
|
+
let offset = 0;
|
|
62
|
+
while (offset < buffer.length) {
|
|
63
|
+
const key = readVarint(buffer, offset);
|
|
64
|
+
if (!key)
|
|
65
|
+
break;
|
|
66
|
+
offset = key.next;
|
|
67
|
+
const fieldNum = Number(key.value >> 3n);
|
|
68
|
+
const wireType = Number(key.value & 7n);
|
|
69
|
+
if (wireType === 0) {
|
|
70
|
+
const value = readVarint(buffer, offset);
|
|
71
|
+
if (!value)
|
|
72
|
+
break;
|
|
73
|
+
fields.push({ fieldNum, wireType, value: value.value });
|
|
74
|
+
offset = value.next;
|
|
75
|
+
}
|
|
76
|
+
else if (wireType === 2) {
|
|
77
|
+
const length = readVarint(buffer, offset);
|
|
78
|
+
if (!length)
|
|
79
|
+
break;
|
|
80
|
+
offset = length.next;
|
|
81
|
+
const end = offset + Number(length.value);
|
|
82
|
+
if (end > buffer.length)
|
|
83
|
+
break;
|
|
84
|
+
fields.push({ fieldNum, wireType, value: buffer.subarray(offset, end) });
|
|
85
|
+
offset = end;
|
|
86
|
+
}
|
|
87
|
+
else if (wireType === 5) {
|
|
88
|
+
offset += 4;
|
|
89
|
+
}
|
|
90
|
+
else if (wireType === 1) {
|
|
91
|
+
offset += 8;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return fields;
|
|
98
|
+
}
|
|
99
|
+
function parseConnectEnvelopes(buffer) {
|
|
100
|
+
const envelopes = [];
|
|
101
|
+
let offset = 0;
|
|
102
|
+
while (offset + 5 <= buffer.length) {
|
|
103
|
+
const flags = buffer[offset];
|
|
104
|
+
const length = buffer.readUInt32BE(offset + 1);
|
|
105
|
+
offset += 5;
|
|
106
|
+
const end = offset + length;
|
|
107
|
+
if (end > buffer.length)
|
|
108
|
+
break;
|
|
109
|
+
envelopes.push({ flags, body: buffer.subarray(offset, end) });
|
|
110
|
+
offset = end;
|
|
111
|
+
}
|
|
112
|
+
return envelopes;
|
|
113
|
+
}
|
|
114
|
+
function takeConnectEnvelopes(buffer) {
|
|
115
|
+
const envelopes = [];
|
|
116
|
+
let offset = 0;
|
|
117
|
+
while (offset + 5 <= buffer.length) {
|
|
118
|
+
const flags = buffer[offset];
|
|
119
|
+
const length = buffer.readUInt32BE(offset + 1);
|
|
120
|
+
const frameStart = offset + 5;
|
|
121
|
+
const frameEnd = frameStart + length;
|
|
122
|
+
if (frameEnd > buffer.length)
|
|
123
|
+
break;
|
|
124
|
+
envelopes.push({ flags, body: buffer.subarray(frameStart, frameEnd) });
|
|
125
|
+
offset = frameEnd;
|
|
126
|
+
}
|
|
127
|
+
return { envelopes, remaining: buffer.subarray(offset) };
|
|
128
|
+
}
|
|
129
|
+
function isCloudClientHealthy(client) {
|
|
130
|
+
return Boolean(client && !client.closed && !client.destroyed);
|
|
131
|
+
}
|
|
132
|
+
function clearCloudClient(client = cloudClient) {
|
|
133
|
+
if (client && cloudClient === client) {
|
|
134
|
+
cloudClient = undefined;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export function closeDevinCloudConnection() {
|
|
138
|
+
if (cloudIdleTimer)
|
|
139
|
+
clearTimeout(cloudIdleTimer);
|
|
140
|
+
cloudIdleTimer = undefined;
|
|
141
|
+
cloudConnectPromise = undefined;
|
|
142
|
+
const client = cloudClient;
|
|
143
|
+
cloudClient = undefined;
|
|
144
|
+
if (client && !client.destroyed) {
|
|
145
|
+
client.close();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function resetCloudIdleTimer() {
|
|
149
|
+
if (cloudIdleTimer)
|
|
150
|
+
clearTimeout(cloudIdleTimer);
|
|
151
|
+
cloudIdleTimer = setTimeout(() => {
|
|
152
|
+
closeDevinCloudConnection();
|
|
153
|
+
}, CLOUD_SESSION_IDLE_MS);
|
|
154
|
+
}
|
|
155
|
+
async function getCloudClient() {
|
|
156
|
+
if (isCloudClientHealthy(cloudClient)) {
|
|
157
|
+
resetCloudIdleTimer();
|
|
158
|
+
return cloudClient;
|
|
159
|
+
}
|
|
160
|
+
if (cloudConnectPromise)
|
|
161
|
+
return cloudConnectPromise;
|
|
162
|
+
cloudConnectPromise = new Promise((resolve, reject) => {
|
|
163
|
+
const client = http2.connect(CODEIUM_API_ORIGIN);
|
|
164
|
+
const cleanup = () => {
|
|
165
|
+
client.off("connect", onConnect);
|
|
166
|
+
client.off("error", onError);
|
|
167
|
+
};
|
|
168
|
+
const onConnect = () => {
|
|
169
|
+
cleanup();
|
|
170
|
+
cloudClient = client;
|
|
171
|
+
client.once("close", () => clearCloudClient(client));
|
|
172
|
+
client.once("error", () => {
|
|
173
|
+
clearCloudClient(client);
|
|
174
|
+
client.destroy();
|
|
175
|
+
});
|
|
176
|
+
client.once("goaway", () => clearCloudClient(client));
|
|
177
|
+
resetCloudIdleTimer();
|
|
178
|
+
resolve(client);
|
|
179
|
+
};
|
|
180
|
+
const onError = (error) => {
|
|
181
|
+
cleanup();
|
|
182
|
+
client.destroy();
|
|
183
|
+
reject(new DevinError(error.message, DevinErrorCode.ConnectionFailed));
|
|
184
|
+
};
|
|
185
|
+
client.once("connect", onConnect);
|
|
186
|
+
client.once("error", onError);
|
|
187
|
+
});
|
|
188
|
+
const connectPromise = cloudConnectPromise.finally(() => {
|
|
189
|
+
cloudConnectPromise = undefined;
|
|
190
|
+
});
|
|
191
|
+
cloudConnectPromise = connectPromise;
|
|
192
|
+
return connectPromise;
|
|
193
|
+
}
|
|
194
|
+
export async function warmDevinCloudConnection() {
|
|
195
|
+
await getCloudClient();
|
|
196
|
+
}
|
|
197
|
+
function textContent(content) {
|
|
198
|
+
if (content == null)
|
|
199
|
+
return "";
|
|
200
|
+
if (typeof content === "string")
|
|
201
|
+
return content;
|
|
202
|
+
return content
|
|
203
|
+
.filter((part) => part.type === "text" && part.text)
|
|
204
|
+
.map((part) => part.text)
|
|
205
|
+
.join("\n");
|
|
206
|
+
}
|
|
207
|
+
function sourceForRole(role) {
|
|
208
|
+
if (role === "system")
|
|
209
|
+
return 5;
|
|
210
|
+
if (role === "tool")
|
|
211
|
+
return 4;
|
|
212
|
+
return 1;
|
|
213
|
+
}
|
|
214
|
+
function buildMetadata(apiKey, version) {
|
|
215
|
+
return Buffer.concat([
|
|
216
|
+
encodeString(3, apiKey),
|
|
217
|
+
encodeString(1, "devin"),
|
|
218
|
+
encodeString(7, version),
|
|
219
|
+
encodeString(2, version),
|
|
220
|
+
encodeString(12, "devin"),
|
|
221
|
+
encodeString(10, crypto.randomUUID()),
|
|
222
|
+
encodeString(4, "en-US"),
|
|
223
|
+
encodeString(28, "devin"),
|
|
224
|
+
]);
|
|
225
|
+
}
|
|
226
|
+
function buildChatPrompt(message) {
|
|
227
|
+
let prompt = textContent(message.content).trim();
|
|
228
|
+
if (message.role === "assistant" && message.tool_calls?.length) {
|
|
229
|
+
prompt = `${prompt}\nASSISTANT TOOL_CALLS: ${JSON.stringify(message.tool_calls)}`.trim();
|
|
230
|
+
}
|
|
231
|
+
else if (message.role === "tool") {
|
|
232
|
+
prompt = `TOOL RESULT${message.tool_call_id ? ` ${message.tool_call_id}` : ""}: ${prompt}`;
|
|
233
|
+
}
|
|
234
|
+
return Buffer.concat([
|
|
235
|
+
encodeString(1, crypto.randomUUID()),
|
|
236
|
+
encodeVarintField(2, sourceForRole(message.role)),
|
|
237
|
+
encodeString(3, prompt),
|
|
238
|
+
]);
|
|
239
|
+
}
|
|
240
|
+
function buildGetChatMessageRequest(request, apiKey, version) {
|
|
241
|
+
const model = resolveDevinModel(request.model);
|
|
242
|
+
const prompts = request.messages
|
|
243
|
+
.map((message) => ({ message, prompt: textContent(message.content).trim() }))
|
|
244
|
+
.filter(({ prompt, message }) => prompt || (message.role === "assistant" && message.tool_calls?.length))
|
|
245
|
+
.map(({ message }) => encodeMessage(3, buildChatPrompt(message)));
|
|
246
|
+
if (prompts.length === 0) {
|
|
247
|
+
throw new Error("No prompt text found in request messages");
|
|
248
|
+
}
|
|
249
|
+
return Buffer.concat([
|
|
250
|
+
encodeMessage(1, buildMetadata(apiKey, version)),
|
|
251
|
+
...prompts,
|
|
252
|
+
encodeVarintField(7, 5),
|
|
253
|
+
encodeString(21, model.modelUid),
|
|
254
|
+
]);
|
|
255
|
+
}
|
|
256
|
+
export async function* streamDevinCloudChat(request) {
|
|
257
|
+
const apiKey = getApiKey();
|
|
258
|
+
const body = connectEnvelope(buildGetChatMessageRequest(request, apiKey, await getLatestDevinVersion()));
|
|
259
|
+
const client = await getCloudClient();
|
|
260
|
+
const req = client.request({
|
|
261
|
+
":method": "POST",
|
|
262
|
+
":path": GET_CHAT_MESSAGE_PATH,
|
|
263
|
+
"content-type": "application/connect+proto",
|
|
264
|
+
"connect-protocol-version": "1",
|
|
265
|
+
accept: "application/connect+proto",
|
|
266
|
+
});
|
|
267
|
+
try {
|
|
268
|
+
req.end(body);
|
|
269
|
+
let buffered = Buffer.alloc(0);
|
|
270
|
+
const timeout = setTimeout(() => {
|
|
271
|
+
req.close(http2.constants.NGHTTP2_CANCEL);
|
|
272
|
+
client.destroy();
|
|
273
|
+
}, 120_000);
|
|
274
|
+
try {
|
|
275
|
+
for await (const chunk of req) {
|
|
276
|
+
buffered = Buffer.concat([buffered, Buffer.from(chunk)]);
|
|
277
|
+
const parsed = takeConnectEnvelopes(buffered);
|
|
278
|
+
buffered = parsed.remaining;
|
|
279
|
+
for (const envelope of parsed.envelopes) {
|
|
280
|
+
if (envelope.flags === 2) {
|
|
281
|
+
const trailer = envelope.body.toString("utf8");
|
|
282
|
+
if (trailer.includes('"error"')) {
|
|
283
|
+
throw new DevinError(trailer, DevinErrorCode.StreamError);
|
|
284
|
+
}
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
for (const field of parseFields(envelope.body)) {
|
|
288
|
+
if (field.fieldNum === 3 && Buffer.isBuffer(field.value)) {
|
|
289
|
+
yield field.value.toString("utf8");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
finally {
|
|
296
|
+
clearTimeout(timeout);
|
|
297
|
+
}
|
|
298
|
+
const parsed = takeConnectEnvelopes(buffered);
|
|
299
|
+
for (const envelope of parsed.envelopes) {
|
|
300
|
+
if (envelope.flags === 2) {
|
|
301
|
+
const trailer = envelope.body.toString("utf8");
|
|
302
|
+
if (trailer.includes('"error"')) {
|
|
303
|
+
throw new DevinError(trailer, DevinErrorCode.StreamError);
|
|
304
|
+
}
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
for (const field of parseFields(envelope.body)) {
|
|
308
|
+
if (field.fieldNum === 3 && Buffer.isBuffer(field.value)) {
|
|
309
|
+
yield field.value.toString("utf8");
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (parsed.remaining.length > 0) {
|
|
314
|
+
throw new DevinError("Cloud chat response ended with an incomplete Connect frame", DevinErrorCode.StreamError);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
finally {
|
|
318
|
+
resetCloudIdleTimer();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
export async function collectDevinCloudChat(request) {
|
|
322
|
+
const chunks = [];
|
|
323
|
+
for await (const chunk of streamDevinCloudChat(request)) {
|
|
324
|
+
chunks.push(chunk);
|
|
325
|
+
}
|
|
326
|
+
return chunks.join("");
|
|
327
|
+
}
|
|
328
|
+
export const __test = {
|
|
329
|
+
buildGetChatMessageRequest,
|
|
330
|
+
parseFields,
|
|
331
|
+
parseConnectEnvelopes,
|
|
332
|
+
};
|
package/dist/devin.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface ContentPart {
|
|
2
|
+
type: string;
|
|
3
|
+
text?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface OpenAIMessage {
|
|
6
|
+
role: "system" | "user" | "assistant" | "tool";
|
|
7
|
+
content: string | null | ContentPart[];
|
|
8
|
+
tool_call_id?: string;
|
|
9
|
+
tool_calls?: Array<{
|
|
10
|
+
id: string;
|
|
11
|
+
type: "function";
|
|
12
|
+
function: {
|
|
13
|
+
name: string;
|
|
14
|
+
arguments: string;
|
|
15
|
+
};
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
export interface ChatCompletionRequest {
|
|
19
|
+
model?: string;
|
|
20
|
+
messages: OpenAIMessage[];
|
|
21
|
+
stream?: boolean;
|
|
22
|
+
temperature?: number;
|
|
23
|
+
max_tokens?: number;
|
|
24
|
+
tools?: Array<{
|
|
25
|
+
type?: "function";
|
|
26
|
+
function?: {
|
|
27
|
+
name?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
parameters?: Record<string, unknown>;
|
|
30
|
+
};
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
export declare function streamDevinChat(request: ChatCompletionRequest): AsyncGenerator<string, void, unknown>;
|
|
34
|
+
export declare function collectDevinChat(request: ChatCompletionRequest): Promise<string>;
|
package/dist/devin.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { collectDevinCloudChat, streamDevinCloudChat } from "./cloud";
|
|
2
|
+
export async function* streamDevinChat(request) {
|
|
3
|
+
for await (const chunk of streamDevinCloudChat(request)) {
|
|
4
|
+
yield chunk;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export async function collectDevinChat(request) {
|
|
8
|
+
return collectDevinCloudChat(request);
|
|
9
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { buildDevinProviderModels, proxyBaseURL, startProxy, } from "./proxy";
|
|
2
|
+
import { getDevinVersion, getLatestDevinVersion, setDevinApiKeyForSession } from "./auth";
|
|
3
|
+
import { getCachedDevinModels, getDiscoveredDevinModels } from "./model-discovery";
|
|
4
|
+
import { warmDevinCloudConnection } from "./cloud";
|
|
5
|
+
import { DEVIN_PROVIDER_ID } from "./models";
|
|
6
|
+
import { exchangeDevinCode, startDevinLoopbackAuth, } from "./browser-auth";
|
|
7
|
+
export const DevinAuthPlugin = async (_input) => {
|
|
8
|
+
function refreshModelsInBackground(provider, port, apiKey) {
|
|
9
|
+
if (!apiKey || apiKey === "devin-local")
|
|
10
|
+
return;
|
|
11
|
+
void (async () => {
|
|
12
|
+
const version = await getLatestDevinVersion(fetch, { timeoutMs: 1_500 });
|
|
13
|
+
const models = await getDiscoveredDevinModels(apiKey, version, { timeoutMs: 2_000 });
|
|
14
|
+
if (provider) {
|
|
15
|
+
provider.models = buildDevinProviderModels(port, models);
|
|
16
|
+
}
|
|
17
|
+
})().catch(() => { });
|
|
18
|
+
}
|
|
19
|
+
async function ensureProxy(provider, apiKey) {
|
|
20
|
+
const port = await startProxy();
|
|
21
|
+
const models = getCachedDevinModels(apiKey, getDevinVersion());
|
|
22
|
+
if (provider) {
|
|
23
|
+
provider.models = buildDevinProviderModels(port, models);
|
|
24
|
+
}
|
|
25
|
+
refreshModelsInBackground(provider, port, apiKey);
|
|
26
|
+
if (apiKey && apiKey !== "devin-local") {
|
|
27
|
+
warmDevinCloudConnection().catch(() => { });
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
baseURL: proxyBaseURL(port),
|
|
31
|
+
apiKey: "devin-local",
|
|
32
|
+
async fetch(requestInput, init) {
|
|
33
|
+
if (init?.headers) {
|
|
34
|
+
if (init.headers instanceof Headers) {
|
|
35
|
+
init.headers.delete("authorization");
|
|
36
|
+
}
|
|
37
|
+
else if (Array.isArray(init.headers)) {
|
|
38
|
+
init.headers = init.headers.filter(([key]) => key.toLowerCase() !== "authorization");
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
delete init.headers.authorization;
|
|
42
|
+
delete init.headers.Authorization;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return fetch(requestInput, init);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
auth: {
|
|
51
|
+
provider: DEVIN_PROVIDER_ID,
|
|
52
|
+
async loader(getAuth, provider) {
|
|
53
|
+
const auth = await getAuth().catch(() => undefined);
|
|
54
|
+
if (auth?.type === "api" && auth.key !== "devin-local") {
|
|
55
|
+
setDevinApiKeyForSession(auth.key);
|
|
56
|
+
}
|
|
57
|
+
return ensureProxy(provider, auth?.type === "api" ? auth.key : undefined);
|
|
58
|
+
},
|
|
59
|
+
methods: [
|
|
60
|
+
{
|
|
61
|
+
type: "oauth",
|
|
62
|
+
label: "Login with Devin",
|
|
63
|
+
prompts: [
|
|
64
|
+
{
|
|
65
|
+
type: "text",
|
|
66
|
+
key: "loginHint",
|
|
67
|
+
message: "Devin email (optional)",
|
|
68
|
+
placeholder: "you@example.com",
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
async authorize(inputs = {}) {
|
|
72
|
+
const loopback = startDevinLoopbackAuth({ loginHint: inputs.loginHint });
|
|
73
|
+
return {
|
|
74
|
+
url: loopback.url,
|
|
75
|
+
instructions: "Complete Devin login in your browser. This window will close automatically after the localhost callback completes.",
|
|
76
|
+
method: "auto",
|
|
77
|
+
async callback() {
|
|
78
|
+
try {
|
|
79
|
+
const code = await loopback.waitForCode();
|
|
80
|
+
const auth = await exchangeDevinCode(code, loopback.verifier);
|
|
81
|
+
setDevinApiKeyForSession(auth.apiKey);
|
|
82
|
+
return {
|
|
83
|
+
type: "success",
|
|
84
|
+
key: auth.apiKey,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return {
|
|
89
|
+
type: "failed",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
async "chat.params"(input, output) {
|
|
99
|
+
if (input.model?.providerID !== DEVIN_PROVIDER_ID)
|
|
100
|
+
return;
|
|
101
|
+
const port = await startProxy();
|
|
102
|
+
output.options = output.options || {};
|
|
103
|
+
output.options.baseURL = proxyBaseURL(port);
|
|
104
|
+
output.options.apiKey = output.options.apiKey || "devin-local";
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
export default DevinAuthPlugin;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ModelCost, DevinModel } from "./models";
|
|
2
|
+
export declare function getRequiredDevinModelIds(): readonly string[];
|
|
3
|
+
type ProtobufValue = bigint | Buffer;
|
|
4
|
+
interface ProtobufField {
|
|
5
|
+
fieldNum: number;
|
|
6
|
+
wireType: number;
|
|
7
|
+
value: ProtobufValue;
|
|
8
|
+
}
|
|
9
|
+
interface DevinModelConfig {
|
|
10
|
+
id: string;
|
|
11
|
+
modelUid: string;
|
|
12
|
+
name: string;
|
|
13
|
+
cost?: ModelCost;
|
|
14
|
+
}
|
|
15
|
+
declare function encodeString(fieldNum: number, value: string): Buffer;
|
|
16
|
+
declare function connectEnvelope(payload: Buffer): Buffer;
|
|
17
|
+
declare function parseFields(buffer: Buffer): ProtobufField[];
|
|
18
|
+
declare function parseConnectEnvelopes(buffer: Buffer): Array<{
|
|
19
|
+
flags: number;
|
|
20
|
+
body: Buffer;
|
|
21
|
+
}>;
|
|
22
|
+
declare function parseModelConfigs(buffer: Buffer): DevinModelConfig[];
|
|
23
|
+
export declare function getCachedDevinModels(apiKey?: string, version?: string): DevinModel[];
|
|
24
|
+
export declare function getDiscoveredDevinModels(apiKey?: string, version?: string, options?: {
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
allowStale?: boolean;
|
|
27
|
+
}): Promise<DevinModel[]>;
|
|
28
|
+
export declare function clearDiscoveredModelCache(): void;
|
|
29
|
+
export declare const __test: {
|
|
30
|
+
clearDiscoveredModelCache: typeof clearDiscoveredModelCache;
|
|
31
|
+
connectEnvelope: typeof connectEnvelope;
|
|
32
|
+
encodeString: typeof encodeString;
|
|
33
|
+
parseConnectEnvelopes: typeof parseConnectEnvelopes;
|
|
34
|
+
parseFields: typeof parseFields;
|
|
35
|
+
parseModelConfigs: typeof parseModelConfigs;
|
|
36
|
+
};
|
|
37
|
+
export {};
|