@ubiquity-os/plugin-sdk 3.6.3 → 3.8.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/llm.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ChatCompletionMessageParam, ChatCompletionCreateParamsNonStreaming, ChatCompletion, ChatCompletionChunk } from 'openai/resources/chat/completions';
2
- import { C as Context } from './context-sqbr2o6i.mjs';
2
+ import { C as Context } from './context-Dwl3aRX-.mjs';
3
3
  import { PluginInput } from './signature.mjs';
4
4
  import '@octokit/webhooks';
5
5
  import '@ubiquity-os/ubiquity-os-logger';
package/dist/llm.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ChatCompletionMessageParam, ChatCompletionCreateParamsNonStreaming, ChatCompletion, ChatCompletionChunk } from 'openai/resources/chat/completions';
2
- import { C as Context } from './context-BbEmsEct.js';
2
+ import { C as Context } from './context-zLHgu52i.js';
3
3
  import { PluginInput } from './signature.js';
4
4
  import '@octokit/webhooks';
5
5
  import '@ubiquity-os/ubiquity-os-logger';
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) throw new Error("Missing authToken in input");
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 fetch(url, { method: "POST", headers, body });
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
- throw new Error("Missing ubiquityKernelToken in input (kernel attestation is required for GitHub auth)");
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
- throw new Error("messages must be a non-empty array");
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(/^ /, "")).join("\n");
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) throw new Error("Missing authToken in input");
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 fetch(url, { method: "POST", headers, body });
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
- throw new Error("Missing ubiquityKernelToken in input (kernel attestation is required for GitHub auth)");
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
- throw new Error("messages must be a non-empty array");
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(/^ /, "")).join("\n");
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-----", "").trim();
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-----", "").trim();
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",
@@ -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-----", "").trim();
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-----", "").trim();
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubiquity-os/plugin-sdk",
3
- "version": "3.6.3",
3
+ "version": "3.8.0",
4
4
  "description": "SDK for plugin support.",
5
5
  "author": "Ubiquity DAO",
6
6
  "license": "MIT",