@onyx.dev/onyx-database 1.1.0 → 1.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.
@@ -9,6 +9,7 @@ var process__default = /*#__PURE__*/_interopDefault(process);
9
9
 
10
10
  // src/config/defaults.ts
11
11
  var DEFAULT_BASE_URL = "https://api.onyx.dev";
12
+ var DEFAULT_AI_BASE_URL = "https://ai.onyx.dev";
12
13
  var sanitizeBaseUrl = (u) => u.replace(/\/+$/, "");
13
14
 
14
15
  // src/errors/config-error.ts
@@ -67,6 +68,7 @@ function readEnv(targetId) {
67
68
  if (targetId && envId !== targetId) return {};
68
69
  const res = dropUndefined({
69
70
  baseUrl: pick("ONYX_DATABASE_BASE_URL"),
71
+ aiBaseUrl: pick("ONYX_AI_BASE_URL"),
70
72
  databaseId: envId,
71
73
  apiKey: pick("ONYX_DATABASE_API_KEY"),
72
74
  apiSecret: pick("ONYX_DATABASE_API_SECRET")
@@ -203,6 +205,7 @@ async function resolveConfig(input) {
203
205
  }
204
206
  const merged = {
205
207
  baseUrl: DEFAULT_BASE_URL,
208
+ aiBaseUrl: DEFAULT_AI_BASE_URL,
206
209
  ...dropUndefined(home),
207
210
  ...dropUndefined(project),
208
211
  ...dropUndefined(cfgPath),
@@ -211,6 +214,7 @@ async function resolveConfig(input) {
211
214
  };
212
215
  dbg("merged (pre-validate):", mask(merged));
213
216
  const baseUrl = sanitizeBaseUrl(merged.baseUrl ?? DEFAULT_BASE_URL);
217
+ const aiBaseUrl = sanitizeBaseUrl(merged.aiBaseUrl ?? DEFAULT_AI_BASE_URL);
214
218
  const databaseId = merged.databaseId ?? "";
215
219
  const apiKey = merged.apiKey ?? "";
216
220
  const apiSecret = merged.apiSecret ?? "";
@@ -246,6 +250,7 @@ async function resolveConfig(input) {
246
250
  }
247
251
  const resolved = {
248
252
  baseUrl,
253
+ aiBaseUrl,
249
254
  databaseId,
250
255
  apiKey,
251
256
  apiSecret,
@@ -256,6 +261,7 @@ async function resolveConfig(input) {
256
261
  };
257
262
  const source = {
258
263
  databaseId: input?.databaseId ? "explicit config" : env.databaseId ? "env" : cfgPath.databaseId ? "env ONYX_CONFIG_PATH" : project.databaseId ? "project file" : home.databaseId ? "home profile" : "unknown",
264
+ aiBaseUrl: input?.aiBaseUrl ? "explicit config" : env.aiBaseUrl ? "env" : cfgPath.aiBaseUrl ? "env ONYX_CONFIG_PATH" : project.aiBaseUrl ? "project file" : home.aiBaseUrl ? "home profile" : "default",
259
265
  apiKey: input?.apiKey ? "explicit config" : env.apiKey ? "env" : cfgPath.apiKey ? "env ONYX_CONFIG_PATH" : project.apiKey ? "project file" : home.apiKey ? "home profile" : "unknown",
260
266
  apiSecret: input?.apiSecret ? "explicit config" : env.apiSecret ? "env" : cfgPath.apiSecret ? "env ONYX_CONFIG_PATH" : project.apiSecret ? "project file" : home.apiSecret ? "home profile" : "unknown"
261
267
  };
@@ -1363,6 +1369,7 @@ var OnyxDatabaseImpl = class {
1363
1369
  cfgPromise;
1364
1370
  resolved = null;
1365
1371
  http = null;
1372
+ aiHttp = null;
1366
1373
  streams = /* @__PURE__ */ new Set();
1367
1374
  requestLoggingEnabled;
1368
1375
  responseLoggingEnabled;
@@ -1397,6 +1404,30 @@ var OnyxDatabaseImpl = class {
1397
1404
  databaseId: this.resolved.databaseId
1398
1405
  };
1399
1406
  }
1407
+ async ensureAiClient() {
1408
+ if (!this.resolved) {
1409
+ this.resolved = await this.cfgPromise;
1410
+ }
1411
+ if (!this.aiHttp) {
1412
+ this.aiHttp = new HttpClient({
1413
+ baseUrl: this.resolved.aiBaseUrl,
1414
+ apiKey: this.resolved.apiKey,
1415
+ apiSecret: this.resolved.apiSecret,
1416
+ fetchImpl: this.resolved.fetch,
1417
+ requestLoggingEnabled: this.requestLoggingEnabled,
1418
+ responseLoggingEnabled: this.responseLoggingEnabled,
1419
+ retryEnabled: this.resolved.retryEnabled,
1420
+ maxRetries: this.resolved.maxRetries,
1421
+ retryInitialDelayMs: this.resolved.retryInitialDelayMs
1422
+ });
1423
+ }
1424
+ return {
1425
+ http: this.aiHttp,
1426
+ fetchImpl: this.resolved.fetch,
1427
+ aiBaseUrl: this.resolved.aiBaseUrl,
1428
+ databaseId: this.resolved.databaseId
1429
+ };
1430
+ }
1400
1431
  registerStream(handle) {
1401
1432
  this.streams.add(handle);
1402
1433
  return {
@@ -1409,7 +1440,34 @@ var OnyxDatabaseImpl = class {
1409
1440
  }
1410
1441
  };
1411
1442
  }
1443
+ getRequestLoggingEnabled() {
1444
+ return this.requestLoggingEnabled;
1445
+ }
1446
+ getResponseLoggingEnabled() {
1447
+ return this.responseLoggingEnabled;
1448
+ }
1412
1449
  /** -------- IOnyxDatabase -------- */
1450
+ chat() {
1451
+ return new AiChatClientImpl(
1452
+ () => this.ensureAiClient(),
1453
+ (handle) => this.registerStream(handle),
1454
+ () => this.requestLoggingEnabled,
1455
+ () => this.responseLoggingEnabled
1456
+ );
1457
+ }
1458
+ async getModels() {
1459
+ const { http } = await this.ensureAiClient();
1460
+ return http.request("GET", "/v1/models");
1461
+ }
1462
+ async getModel(modelId) {
1463
+ const { http } = await this.ensureAiClient();
1464
+ const path = `/v1/models/${encodeURIComponent(modelId)}`;
1465
+ return http.request("GET", path);
1466
+ }
1467
+ async requestScriptApproval(input) {
1468
+ const { http } = await this.ensureAiClient();
1469
+ return http.request("POST", "/api/script-approvals", input);
1470
+ }
1413
1471
  from(table) {
1414
1472
  return new QueryBuilderImpl(this, String(table), this.defaultPartition);
1415
1473
  }
@@ -1959,6 +2017,153 @@ var CascadeBuilderImpl = class {
1959
2017
  return this.db.delete(table, primaryKey, opts);
1960
2018
  }
1961
2019
  };
2020
+ var AiChatClientImpl = class {
2021
+ constructor(resolveAiClient, registerStream, requestLoggingEnabled, responseLoggingEnabled) {
2022
+ this.resolveAiClient = resolveAiClient;
2023
+ this.registerStream = registerStream;
2024
+ this.requestLoggingEnabled = requestLoggingEnabled;
2025
+ this.responseLoggingEnabled = responseLoggingEnabled;
2026
+ }
2027
+ normalizeEventData(rawEvent) {
2028
+ const lines = rawEvent.split("\n");
2029
+ const dataLines = [];
2030
+ for (const line of lines) {
2031
+ const trimmed = line.trim();
2032
+ if (!trimmed || trimmed.startsWith(":")) continue;
2033
+ if (trimmed.startsWith("data:")) {
2034
+ dataLines.push(trimmed.slice(5).trim());
2035
+ } else {
2036
+ dataLines.push(trimmed);
2037
+ }
2038
+ }
2039
+ if (!dataLines.length) return null;
2040
+ const joined = dataLines.join("\n").trim();
2041
+ return joined.length > 0 ? joined : null;
2042
+ }
2043
+ logRequest(url, body, headers) {
2044
+ if (!this.requestLoggingEnabled()) return;
2045
+ console.log(`POST ${url}`);
2046
+ if (body != null) {
2047
+ const serialized = typeof body === "string" ? body : JSON.stringify(body);
2048
+ console.log(serialized);
2049
+ }
2050
+ const headerLog = { ...headers };
2051
+ if (headerLog["x-onyx-secret"]) headerLog["x-onyx-secret"] = "[REDACTED]";
2052
+ console.log("Headers:", headerLog);
2053
+ }
2054
+ logResponse(status, statusText, raw) {
2055
+ if (!this.responseLoggingEnabled()) return;
2056
+ const statusLine = `${status} ${statusText}`.trim();
2057
+ console.log(statusLine);
2058
+ if (raw && raw.trim().length > 0) {
2059
+ console.log(raw);
2060
+ }
2061
+ }
2062
+ toOnyxError(status, statusText, raw) {
2063
+ let parsed = raw;
2064
+ try {
2065
+ parsed = parseJsonAllowNaN(raw);
2066
+ } catch {
2067
+ }
2068
+ const message = typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "object" && typeof parsed.error?.message === "string" ? String(parsed.error.message) : `${status} ${statusText}`;
2069
+ return new OnyxHttpError(message, status, statusText, parsed, raw);
2070
+ }
2071
+ async create(request, options) {
2072
+ const body = { ...request, stream: !!request.stream };
2073
+ const { http, fetchImpl, aiBaseUrl, databaseId } = await this.resolveAiClient();
2074
+ const params = new URLSearchParams();
2075
+ const scopedDb = options?.databaseId ?? databaseId;
2076
+ if (scopedDb) params.append("databaseId", scopedDb);
2077
+ const path = `/v1/chat/completions${params.size ? `?${params.toString()}` : ""}`;
2078
+ if (!body.stream) {
2079
+ return http.request("POST", path, body);
2080
+ }
2081
+ const url = `${aiBaseUrl}${path}`;
2082
+ const headers = http.headers({ Accept: "text/event-stream" });
2083
+ this.logRequest(url, body, headers);
2084
+ const res = await fetchImpl(url, {
2085
+ method: "POST",
2086
+ headers,
2087
+ body: JSON.stringify(body)
2088
+ });
2089
+ if (!res.ok) {
2090
+ const raw = await res.text();
2091
+ this.logResponse(res.status, res.statusText, raw);
2092
+ throw this.toOnyxError(res.status, res.statusText, raw);
2093
+ }
2094
+ this.logResponse(res.status, res.statusText);
2095
+ const bodyStream = res.body;
2096
+ const reader = bodyStream && typeof bodyStream.getReader === "function" ? bodyStream.getReader() : null;
2097
+ if (!reader) {
2098
+ throw new OnyxHttpError(
2099
+ "Streaming response body is not readable",
2100
+ res.status,
2101
+ res.statusText,
2102
+ null,
2103
+ ""
2104
+ );
2105
+ }
2106
+ let canceled = false;
2107
+ const handle = {
2108
+ cancel: () => {
2109
+ if (canceled) return;
2110
+ canceled = true;
2111
+ try {
2112
+ reader.cancel?.();
2113
+ } catch {
2114
+ }
2115
+ }
2116
+ };
2117
+ const registered = this.registerStream(handle);
2118
+ const normalizeEvent = (rawEvent) => this.normalizeEventData(rawEvent);
2119
+ const stream = {
2120
+ async *[Symbol.asyncIterator]() {
2121
+ const decoder = new TextDecoder("utf-8");
2122
+ let buffer = "";
2123
+ try {
2124
+ while (!canceled) {
2125
+ const { done, value } = await reader.read();
2126
+ if (done) break;
2127
+ if (value) {
2128
+ buffer += decoder.decode(value, { stream: true });
2129
+ }
2130
+ let splitIndex = buffer.indexOf("\n\n");
2131
+ while (splitIndex !== -1) {
2132
+ const rawEvent = buffer.slice(0, splitIndex);
2133
+ buffer = buffer.slice(splitIndex + 2);
2134
+ const data = normalizeEvent(rawEvent);
2135
+ if (data === "[DONE]") return;
2136
+ if (data) {
2137
+ try {
2138
+ yield parseJsonAllowNaN(data);
2139
+ } catch {
2140
+ }
2141
+ }
2142
+ splitIndex = buffer.indexOf("\n\n");
2143
+ }
2144
+ }
2145
+ buffer += decoder.decode();
2146
+ const remaining = buffer.trim();
2147
+ if (remaining && remaining !== "[DONE]") {
2148
+ const data = normalizeEvent(remaining);
2149
+ if (data && data !== "[DONE]") {
2150
+ try {
2151
+ yield parseJsonAllowNaN(data);
2152
+ } catch {
2153
+ }
2154
+ }
2155
+ }
2156
+ } finally {
2157
+ registered.cancel();
2158
+ }
2159
+ },
2160
+ cancel() {
2161
+ registered.cancel();
2162
+ }
2163
+ };
2164
+ return stream;
2165
+ }
2166
+ };
1962
2167
  function createOnyxFacade(resolveConfig2) {
1963
2168
  let cachedCfg = null;
1964
2169
  function resolveConfigWithCache(config) {