@ubiquity-os/plugin-sdk 3.6.3 → 3.7.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/dist/configuration.d.mts +16 -11
- package/dist/configuration.d.ts +16 -11
- package/dist/configuration.js +219 -36
- package/dist/configuration.mjs +219 -36
- package/dist/{context-BbEmsEct.d.ts → context-BE4WjJZf.d.ts} +1 -0
- package/dist/{context-sqbr2o6i.d.mts → context-Ckj1HMjz.d.mts} +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +209 -124
- package/dist/index.mjs +209 -124
- package/dist/llm.d.mts +1 -1
- package/dist/llm.d.ts +1 -1
- package/dist/llm.js +46 -11
- package/dist/llm.mjs +46 -11
- package/dist/signature.js +2 -2
- package/dist/signature.mjs +2 -2
- package/package.json +1 -1
package/dist/llm.js
CHANGED
|
@@ -31,6 +31,14 @@ function normalizeBaseUrl(baseUrl) {
|
|
|
31
31
|
}
|
|
32
32
|
return normalized;
|
|
33
33
|
}
|
|
34
|
+
var MAX_LLM_RETRIES = 2;
|
|
35
|
+
var RETRY_BACKOFF_MS = [250, 750];
|
|
36
|
+
function getRetryDelayMs(attempt) {
|
|
37
|
+
return RETRY_BACKOFF_MS[Math.min(attempt, RETRY_BACKOFF_MS.length - 1)] ?? 750;
|
|
38
|
+
}
|
|
39
|
+
function sleep(ms) {
|
|
40
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
41
|
+
}
|
|
34
42
|
function getEnvString(name) {
|
|
35
43
|
if (typeof process === "undefined" || !process?.env) return EMPTY_STRING;
|
|
36
44
|
return String(process.env[name] ?? EMPTY_STRING).trim();
|
|
@@ -45,7 +53,11 @@ function getAiBaseUrl(options) {
|
|
|
45
53
|
}
|
|
46
54
|
async function callLlm(options, input) {
|
|
47
55
|
const authToken = String(input.authToken ?? EMPTY_STRING).trim();
|
|
48
|
-
if (!authToken)
|
|
56
|
+
if (!authToken) {
|
|
57
|
+
const err = new Error("Missing authToken in input");
|
|
58
|
+
err.status = 401;
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
49
61
|
const kernelToken = "ubiquityKernelToken" in input ? input.ubiquityKernelToken : void 0;
|
|
50
62
|
const payload = getPayload(input);
|
|
51
63
|
const { owner, repo, installationId } = getRepoMetadata(payload);
|
|
@@ -65,13 +77,7 @@ async function callLlm(options, input) {
|
|
|
65
77
|
installationId,
|
|
66
78
|
ubiquityKernelToken: kernelToken
|
|
67
79
|
});
|
|
68
|
-
const response = await
|
|
69
|
-
if (!response.ok) {
|
|
70
|
-
const err = await response.text();
|
|
71
|
-
const error = new Error(`LLM API error: ${response.status} - ${err}`);
|
|
72
|
-
error.status = response.status;
|
|
73
|
-
throw error;
|
|
74
|
-
}
|
|
80
|
+
const response = await fetchWithRetry(url, { method: "POST", headers, body }, MAX_LLM_RETRIES);
|
|
75
81
|
if (isStream) {
|
|
76
82
|
if (!response.body) {
|
|
77
83
|
throw new Error("LLM API error: missing response body for streaming request");
|
|
@@ -83,17 +89,46 @@ async function callLlm(options, input) {
|
|
|
83
89
|
function ensureKernelToken(authToken, kernelToken) {
|
|
84
90
|
const isKernelTokenRequired = authToken.startsWith("gh");
|
|
85
91
|
if (isKernelTokenRequired && !kernelToken) {
|
|
86
|
-
|
|
92
|
+
const err = new Error("Missing ubiquityKernelToken in input (kernel attestation is required for GitHub auth)");
|
|
93
|
+
err.status = 401;
|
|
94
|
+
throw err;
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
97
|
function ensureMessages(messages) {
|
|
90
98
|
if (!Array.isArray(messages) || messages.length === 0) {
|
|
91
|
-
|
|
99
|
+
const err = new Error("messages must be a non-empty array");
|
|
100
|
+
err.status = 400;
|
|
101
|
+
throw err;
|
|
92
102
|
}
|
|
93
103
|
}
|
|
94
104
|
function buildAiUrl(options, baseUrl) {
|
|
95
105
|
return `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
|
|
96
106
|
}
|
|
107
|
+
async function fetchWithRetry(url, options, maxRetries) {
|
|
108
|
+
let attempt = 0;
|
|
109
|
+
let lastError;
|
|
110
|
+
while (attempt <= maxRetries) {
|
|
111
|
+
try {
|
|
112
|
+
const response = await fetch(url, options);
|
|
113
|
+
if (response.ok) return response;
|
|
114
|
+
const errText = await response.text();
|
|
115
|
+
if (response.status >= 500 && attempt < maxRetries) {
|
|
116
|
+
await sleep(getRetryDelayMs(attempt));
|
|
117
|
+
attempt += 1;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const error = new Error(`LLM API error: ${response.status} - ${errText}`);
|
|
121
|
+
error.status = response.status;
|
|
122
|
+
throw error;
|
|
123
|
+
} catch (error) {
|
|
124
|
+
lastError = error;
|
|
125
|
+
if (attempt >= maxRetries) throw error;
|
|
126
|
+
await sleep(getRetryDelayMs(attempt));
|
|
127
|
+
attempt += 1;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
throw lastError ?? new Error("LLM API error: request failed after retries");
|
|
131
|
+
}
|
|
97
132
|
function getPayload(input) {
|
|
98
133
|
if ("payload" in input) {
|
|
99
134
|
return input.payload;
|
|
@@ -155,7 +190,7 @@ function getEventData(event) {
|
|
|
155
190
|
if (!event.trim()) return null;
|
|
156
191
|
const dataLines = event.split("\n").filter((line) => line.startsWith("data:"));
|
|
157
192
|
if (!dataLines.length) return null;
|
|
158
|
-
const data = dataLines.map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5).replace(/^ /,
|
|
193
|
+
const data = dataLines.map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5).replace(/^ /, EMPTY_STRING)).join("\n");
|
|
159
194
|
return data || null;
|
|
160
195
|
}
|
|
161
196
|
function parseEventData(data) {
|
package/dist/llm.mjs
CHANGED
|
@@ -7,6 +7,14 @@ function normalizeBaseUrl(baseUrl) {
|
|
|
7
7
|
}
|
|
8
8
|
return normalized;
|
|
9
9
|
}
|
|
10
|
+
var MAX_LLM_RETRIES = 2;
|
|
11
|
+
var RETRY_BACKOFF_MS = [250, 750];
|
|
12
|
+
function getRetryDelayMs(attempt) {
|
|
13
|
+
return RETRY_BACKOFF_MS[Math.min(attempt, RETRY_BACKOFF_MS.length - 1)] ?? 750;
|
|
14
|
+
}
|
|
15
|
+
function sleep(ms) {
|
|
16
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
17
|
+
}
|
|
10
18
|
function getEnvString(name) {
|
|
11
19
|
if (typeof process === "undefined" || !process?.env) return EMPTY_STRING;
|
|
12
20
|
return String(process.env[name] ?? EMPTY_STRING).trim();
|
|
@@ -21,7 +29,11 @@ function getAiBaseUrl(options) {
|
|
|
21
29
|
}
|
|
22
30
|
async function callLlm(options, input) {
|
|
23
31
|
const authToken = String(input.authToken ?? EMPTY_STRING).trim();
|
|
24
|
-
if (!authToken)
|
|
32
|
+
if (!authToken) {
|
|
33
|
+
const err = new Error("Missing authToken in input");
|
|
34
|
+
err.status = 401;
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
25
37
|
const kernelToken = "ubiquityKernelToken" in input ? input.ubiquityKernelToken : void 0;
|
|
26
38
|
const payload = getPayload(input);
|
|
27
39
|
const { owner, repo, installationId } = getRepoMetadata(payload);
|
|
@@ -41,13 +53,7 @@ async function callLlm(options, input) {
|
|
|
41
53
|
installationId,
|
|
42
54
|
ubiquityKernelToken: kernelToken
|
|
43
55
|
});
|
|
44
|
-
const response = await
|
|
45
|
-
if (!response.ok) {
|
|
46
|
-
const err = await response.text();
|
|
47
|
-
const error = new Error(`LLM API error: ${response.status} - ${err}`);
|
|
48
|
-
error.status = response.status;
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
56
|
+
const response = await fetchWithRetry(url, { method: "POST", headers, body }, MAX_LLM_RETRIES);
|
|
51
57
|
if (isStream) {
|
|
52
58
|
if (!response.body) {
|
|
53
59
|
throw new Error("LLM API error: missing response body for streaming request");
|
|
@@ -59,17 +65,46 @@ async function callLlm(options, input) {
|
|
|
59
65
|
function ensureKernelToken(authToken, kernelToken) {
|
|
60
66
|
const isKernelTokenRequired = authToken.startsWith("gh");
|
|
61
67
|
if (isKernelTokenRequired && !kernelToken) {
|
|
62
|
-
|
|
68
|
+
const err = new Error("Missing ubiquityKernelToken in input (kernel attestation is required for GitHub auth)");
|
|
69
|
+
err.status = 401;
|
|
70
|
+
throw err;
|
|
63
71
|
}
|
|
64
72
|
}
|
|
65
73
|
function ensureMessages(messages) {
|
|
66
74
|
if (!Array.isArray(messages) || messages.length === 0) {
|
|
67
|
-
|
|
75
|
+
const err = new Error("messages must be a non-empty array");
|
|
76
|
+
err.status = 400;
|
|
77
|
+
throw err;
|
|
68
78
|
}
|
|
69
79
|
}
|
|
70
80
|
function buildAiUrl(options, baseUrl) {
|
|
71
81
|
return `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
|
|
72
82
|
}
|
|
83
|
+
async function fetchWithRetry(url, options, maxRetries) {
|
|
84
|
+
let attempt = 0;
|
|
85
|
+
let lastError;
|
|
86
|
+
while (attempt <= maxRetries) {
|
|
87
|
+
try {
|
|
88
|
+
const response = await fetch(url, options);
|
|
89
|
+
if (response.ok) return response;
|
|
90
|
+
const errText = await response.text();
|
|
91
|
+
if (response.status >= 500 && attempt < maxRetries) {
|
|
92
|
+
await sleep(getRetryDelayMs(attempt));
|
|
93
|
+
attempt += 1;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const error = new Error(`LLM API error: ${response.status} - ${errText}`);
|
|
97
|
+
error.status = response.status;
|
|
98
|
+
throw error;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
lastError = error;
|
|
101
|
+
if (attempt >= maxRetries) throw error;
|
|
102
|
+
await sleep(getRetryDelayMs(attempt));
|
|
103
|
+
attempt += 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
throw lastError ?? new Error("LLM API error: request failed after retries");
|
|
107
|
+
}
|
|
73
108
|
function getPayload(input) {
|
|
74
109
|
if ("payload" in input) {
|
|
75
110
|
return input.payload;
|
|
@@ -131,7 +166,7 @@ function getEventData(event) {
|
|
|
131
166
|
if (!event.trim()) return null;
|
|
132
167
|
const dataLines = event.split("\n").filter((line) => line.startsWith("data:"));
|
|
133
168
|
if (!dataLines.length) return null;
|
|
134
|
-
const data = dataLines.map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5).replace(/^ /,
|
|
169
|
+
const data = dataLines.map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5).replace(/^ /, EMPTY_STRING)).join("\n");
|
|
135
170
|
return data || null;
|
|
136
171
|
}
|
|
137
172
|
function parseEventData(data) {
|
package/dist/signature.js
CHANGED
|
@@ -83,7 +83,7 @@ async function verifySignature(publicKeyPem, inputs, signature) {
|
|
|
83
83
|
ref: inputs.ref,
|
|
84
84
|
command: inputs.command
|
|
85
85
|
};
|
|
86
|
-
const pemContents = publicKeyPem.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").
|
|
86
|
+
const pemContents = publicKeyPem.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace(/\s+/g, "");
|
|
87
87
|
const binaryDer = Uint8Array.from(atob(pemContents), (c) => c.charCodeAt(0));
|
|
88
88
|
const publicKey = await crypto.subtle.importKey(
|
|
89
89
|
"spki",
|
|
@@ -104,7 +104,7 @@ async function verifySignature(publicKeyPem, inputs, signature) {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
async function importRsaPrivateKey(pem) {
|
|
107
|
-
const pemContents = pem.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").
|
|
107
|
+
const pemContents = pem.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replace(/\s+/g, "");
|
|
108
108
|
const binaryDer = Uint8Array.from(atob(pemContents), (c) => c.charCodeAt(0));
|
|
109
109
|
return await crypto.subtle.importKey(
|
|
110
110
|
"pkcs8",
|
package/dist/signature.mjs
CHANGED
|
@@ -55,7 +55,7 @@ async function verifySignature(publicKeyPem, inputs, signature) {
|
|
|
55
55
|
ref: inputs.ref,
|
|
56
56
|
command: inputs.command
|
|
57
57
|
};
|
|
58
|
-
const pemContents = publicKeyPem.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").
|
|
58
|
+
const pemContents = publicKeyPem.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace(/\s+/g, "");
|
|
59
59
|
const binaryDer = Uint8Array.from(atob(pemContents), (c) => c.charCodeAt(0));
|
|
60
60
|
const publicKey = await crypto.subtle.importKey(
|
|
61
61
|
"spki",
|
|
@@ -76,7 +76,7 @@ async function verifySignature(publicKeyPem, inputs, signature) {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
async function importRsaPrivateKey(pem) {
|
|
79
|
-
const pemContents = pem.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").
|
|
79
|
+
const pemContents = pem.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replace(/\s+/g, "");
|
|
80
80
|
const binaryDer = Uint8Array.from(atob(pemContents), (c) => c.charCodeAt(0));
|
|
81
81
|
return await crypto.subtle.importKey(
|
|
82
82
|
"pkcs8",
|