@thyn-ai/sqai 0.1.4 → 0.1.6

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/index.js CHANGED
@@ -112,13 +112,13 @@ var SQAI_ERROR_CODES = [
112
112
  "contract_mismatch",
113
113
  "invalid_intent"
114
114
  ];
115
- var RETRYABLE_ALGENTA_CODES = /* @__PURE__ */ new Set([
115
+ var RETRYABLE_ENGINE_CODES = /* @__PURE__ */ new Set([
116
116
  "network_error",
117
117
  "timeout",
118
118
  "rate_limited",
119
119
  "service_unavailable"
120
120
  ]);
121
- function fromAlgentaError(error) {
121
+ function fromEngineError(error) {
122
122
  if (error instanceof SqaiError) {
123
123
  return error;
124
124
  }
@@ -128,7 +128,7 @@ function fromAlgentaError(error) {
128
128
  const details = errorLike.details && typeof errorLike.details === "object" ? errorLike.details : {};
129
129
  return new SqaiError(code, message, {
130
130
  source: "engine",
131
- retryable: RETRYABLE_ALGENTA_CODES.has(code),
131
+ retryable: RETRYABLE_ENGINE_CODES.has(code),
132
132
  details
133
133
  });
134
134
  }
@@ -137,12 +137,14 @@ function fromAlgentaError(error) {
137
137
  var DEFAULT_BUILD_SERVICE_URL = "https://sqai-buildservice.fly.dev";
138
138
  function readEnv(env = process.env) {
139
139
  const apiKey = (env.SQAI_API_KEY ?? "").trim() || void 0;
140
- const engineUrl = (env.SQAI_ENGINE_URL ?? "").trim() || void 0;
140
+ const deploymentUrl = (env.SQAI_DEPLOYMENT_URL ?? "").trim() || void 0;
141
+ const legacyEngineUrl = (env.SQAI_ENGINE_URL ?? "").trim() || void 0;
142
+ const engineUrl = deploymentUrl ?? legacyEngineUrl;
141
143
  const autoInstall = (env.SQAI_RUNTIME_AUTO_INSTALL ?? "").trim() !== "0";
142
144
  const modulesRaw = (env.SQAI_RUNTIME_MODULES ?? "").split(",").map((m) => m.trim()).filter((m) => m.length > 0);
143
145
  const runtimeModules = modulesRaw.length > 0 ? modulesRaw : void 0;
144
146
  const buildServiceUrl = (env.SQAI_BUILD_SERVICE_URL ?? "").trim() || DEFAULT_BUILD_SERVICE_URL;
145
- return { apiKey, engineUrl, autoInstall, runtimeModules, buildServiceUrl };
147
+ return { apiKey, engineUrl, deploymentUrl, autoInstall, runtimeModules, buildServiceUrl };
146
148
  }
147
149
  function inferMode(explicit, resolved) {
148
150
  if (explicit) {
@@ -151,9 +153,6 @@ function inferMode(explicit, resolved) {
151
153
  if (resolved.engineUrl) {
152
154
  return "self_hosted";
153
155
  }
154
- if (resolved.apiKey) {
155
- return "api";
156
- }
157
156
  return "local";
158
157
  }
159
158
 
@@ -315,22 +314,180 @@ var ResultStore = class {
315
314
  };
316
315
 
317
316
  // src/runtime/provision.ts
318
- import { homedir, platform as osPlatform, arch as osArch } from "os";
319
- import { join as join2 } from "path";
320
- import { createWriteStream, existsSync, promises as fs2 } from "fs";
321
- import { createHash as createHash3, randomBytes as randomBytes2 } from "crypto";
317
+ import { homedir as homedir2, platform as osPlatform, arch as osArch } from "os";
318
+ import { join as join3 } from "path";
319
+ import { createWriteStream, existsSync as existsSync2, promises as fs2 } from "fs";
320
+ import { createHash as createHash4, randomBytes as randomBytes2 } from "crypto";
322
321
  import { Readable } from "stream";
323
322
  import { pipeline } from "stream/promises";
324
323
  import { MojoRuntime } from "algenta-sdk";
325
324
 
325
+ // src/runtime/auth.ts
326
+ import { createHash as createHash2, createPrivateKey, createPublicKey, generateKeyPairSync } from "crypto";
327
+ import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync, rmSync } from "fs";
328
+ import { homedir, hostname } from "os";
329
+ import { join } from "path";
330
+ var CLIENT_ID = "sqai-cli";
331
+ var SCOPE = "sqai";
332
+ var PRODUCT = "sqai";
333
+ var DEFAULT_PLAN = "sqai_developer";
334
+ var DEFAULT_ACCOUNTS_URL = "https://accounts.thyn.ai";
335
+ var DEFAULT_API_URL = "https://api.sqai.com";
336
+ var LoginError = class extends Error {
337
+ };
338
+ function sqaiHome() {
339
+ return process.env.SQAI_HOME ?? join(homedir(), ".sqai");
340
+ }
341
+ var p = (name) => join(sqaiHome(), name);
342
+ var apiKeyPath = () => p("api_key");
343
+ var tokenPath = () => p("token");
344
+ var deviceIdPath = () => p("device_id");
345
+ var accountsUrl = () => (process.env.SQAI_ACCOUNTS_URL ?? DEFAULT_ACCOUNTS_URL).replace(/\/$/, "");
346
+ var apiUrl = () => (process.env.SQAI_API_URL ?? DEFAULT_API_URL).replace(/\/$/, "");
347
+ function readSecret(path) {
348
+ try {
349
+ const v = readFileSync(path, "utf8").trim();
350
+ return v || void 0;
351
+ } catch {
352
+ return void 0;
353
+ }
354
+ }
355
+ async function readCachedApiKey() {
356
+ return process.env.SQAI_API_KEY ?? process.env.ALGENTA_API_KEY ?? readSecret(apiKeyPath());
357
+ }
358
+ function cache(path, value) {
359
+ mkdirSync(sqaiHome(), { recursive: true });
360
+ writeFileSync(path, value.trim() + "\n", "utf8");
361
+ try {
362
+ chmodSync(path, 384);
363
+ } catch {
364
+ }
365
+ }
366
+ function deviceId() {
367
+ const existing = readSecret(deviceIdPath());
368
+ if (existing) return existing;
369
+ const value = createHash2("sha256").update(`${hostname()}:${Date.now()}:${Math.random()}`).digest("hex");
370
+ cache(deviceIdPath(), value);
371
+ return value;
372
+ }
373
+ async function request(method, url, opts = {}) {
374
+ const headers = { Accept: "application/json", "User-Agent": "sqai-cli" };
375
+ if (opts.body !== void 0) headers["Content-Type"] = "application/json";
376
+ if (opts.bearer) headers["Authorization"] = `Bearer ${opts.bearer}`;
377
+ let res;
378
+ try {
379
+ res = await fetch(url, {
380
+ method,
381
+ headers,
382
+ body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
383
+ });
384
+ } catch (e) {
385
+ throw new LoginError(`${method} ${url}: ${e instanceof Error ? e.message : String(e)}`);
386
+ }
387
+ let json = {};
388
+ try {
389
+ json = await res.json();
390
+ } catch {
391
+ json = {};
392
+ }
393
+ return { status: res.status, json };
394
+ }
395
+ async function deviceAuthorize(accounts, openBrowser, maxWaitMs = 3e5) {
396
+ const { json: start } = await request("POST", `${accounts}/oauth/device/code`, {
397
+ body: { client_id: CLIENT_ID, scope: SCOPE }
398
+ });
399
+ const deviceCode = start.device_code;
400
+ const userCode = start.user_code;
401
+ const verify = start.verification_uri_complete || start.verification_uri;
402
+ const interval = Number(start.interval ?? 5);
403
+ if (!deviceCode || !userCode || !verify) throw new LoginError("accounts device-code response missing fields");
404
+ process.stdout.write(`
405
+ Open ${verify}
406
+ and enter code: ${userCode}
407
+
408
+ `);
409
+ if (openBrowser) await tryOpenBrowser(verify);
410
+ const deadline = Date.now() + maxWaitMs;
411
+ while (Date.now() < deadline) {
412
+ await new Promise((r) => setTimeout(r, interval * 1e3));
413
+ const { status, json: tok } = await request("POST", `${accounts}/oauth/device/token`, {
414
+ body: {
415
+ client_id: CLIENT_ID,
416
+ device_code: deviceCode,
417
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
418
+ }
419
+ });
420
+ const access = tok.access_token;
421
+ if (access) return access;
422
+ const error = tok.error;
423
+ if (error === "authorization_pending" || error === "slow_down" || status === 400 || status === 428) continue;
424
+ throw new LoginError(`device authorization failed: ${error ?? `HTTP ${status}`}`);
425
+ }
426
+ throw new LoginError("timed out waiting for device authorization");
427
+ }
428
+ async function tryOpenBrowser(url) {
429
+ try {
430
+ const { spawn: spawn2 } = await import("child_process");
431
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
432
+ spawn2(cmd, [url], { stdio: "ignore", detached: true, shell: process.platform === "win32" }).unref();
433
+ } catch {
434
+ }
435
+ }
436
+ async function createApiKey(api, accessToken, plan) {
437
+ const { status, json } = await request("POST", `${api}/v1/products/${PRODUCT}/onboard`, {
438
+ bearer: accessToken,
439
+ body: { name: CLIENT_ID, product: PRODUCT, plan }
440
+ });
441
+ const key = json.raw_key || json.key || json.api_key;
442
+ if (status >= 400 || !key) {
443
+ const detail = json.error && typeof json.error === "object" ? json.error.message : void 0;
444
+ throw new LoginError(`could not create API key (HTTP ${status})${detail ? `: ${detail}` : ""}`);
445
+ }
446
+ return key;
447
+ }
448
+ async function login(opts = {}) {
449
+ const plan = opts.plan ?? DEFAULT_PLAN;
450
+ const accounts = accountsUrl();
451
+ const api = apiUrl();
452
+ let token = opts.accessToken ?? process.env.SQAI_TOKEN;
453
+ if (token) {
454
+ process.stdout.write("using supplied token (headless)\n");
455
+ } else {
456
+ process.stdout.write(`authenticating via ${accounts} \u2026
457
+ `);
458
+ token = await deviceAuthorize(accounts, opts.openBrowser ?? true);
459
+ }
460
+ cache(tokenPath(), token);
461
+ process.stdout.write("creating your free SQAI Developer API key \u2026\n");
462
+ const apiKey = await createApiKey(api, token, plan);
463
+ cache(apiKeyPath(), apiKey);
464
+ deviceId();
465
+ process.stdout.write(
466
+ "\n \u2713 sqai login complete \u2014 the local runtime will provision a device-bound offline license on first use.\n SQAI runs fully on your machine; no data leaves it.\n\n"
467
+ );
468
+ return {
469
+ device_id: deviceId(),
470
+ plan,
471
+ api_key_prefix: apiKey.includes("_") ? `${apiKey.split("_", 1)[0]}_\u2026` : `${apiKey.slice(0, 6)}\u2026`
472
+ };
473
+ }
474
+ function logout() {
475
+ for (const path of [tokenPath(), apiKeyPath()]) {
476
+ try {
477
+ rmSync(path);
478
+ } catch {
479
+ }
480
+ }
481
+ }
482
+
326
483
  // src/runtime/bundle.ts
327
484
  import { spawn } from "child_process";
328
- import { createHash as createHash2, timingSafeEqual } from "crypto";
485
+ import { createHash as createHash3, timingSafeEqual } from "crypto";
329
486
  import { createReadStream, promises as fs } from "fs";
330
- import { join } from "path";
487
+ import { join as join2 } from "path";
331
488
 
332
489
  // src/runtime/trust.ts
333
- import { createPublicKey, verify as cryptoVerify } from "crypto";
490
+ import { createPublicKey as createPublicKey2, verify as cryptoVerify } from "crypto";
334
491
  var RELEASE_1_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
335
492
  MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7m4G1dEMi5FppLa5hu9N
336
493
  4pUyoGELPaN5X7bO+9Qktgbv21+c8I4sQCufKouZEVNMNluKNrWcQ5dSwOuVxxS9
@@ -429,7 +586,7 @@ function keyValidNow(key, nowMs) {
429
586
  }
430
587
  function signatureVerifies(manifestBytes, signature, publicKeyPem) {
431
588
  try {
432
- const key = createPublicKey(publicKeyPem);
589
+ const key = createPublicKey2(publicKeyPem);
433
590
  if (key.asymmetricKeyType !== "rsa") {
434
591
  return false;
435
592
  }
@@ -613,7 +770,7 @@ async function hashTarMember(tarball, member, cap) {
613
770
  const child = spawn("tar", ["-xOf", tarball, "--", member], {
614
771
  stdio: ["ignore", "pipe", "pipe"]
615
772
  });
616
- const digest = createHash2("sha256");
773
+ const digest = createHash3("sha256");
617
774
  let total = 0;
618
775
  let overflow = false;
619
776
  child.stdout.on("data", (chunk) => {
@@ -673,7 +830,7 @@ async function extractTarball(tarball, destinationDir) {
673
830
  }
674
831
  function sha256File(path) {
675
832
  return new Promise((resolve, reject) => {
676
- const digest = createHash2("sha256");
833
+ const digest = createHash3("sha256");
677
834
  const stream = createReadStream(path);
678
835
  stream.on("data", (chunk) => digest.update(chunk));
679
836
  stream.on("error", reject);
@@ -685,6 +842,9 @@ function digestsEqual(actualHex, expectedHex) {
685
842
  const expected = Buffer.from(expectedHex.toLowerCase(), "utf-8");
686
843
  return actual.length === expected.length && timingSafeEqual(actual, expected);
687
844
  }
845
+ function isGeneratedBytecodeCache(path) {
846
+ return path.split("/").includes("__pycache__") && path.endsWith(".pyc") && isSafeRelativePath(path);
847
+ }
688
848
  function parseVerifiedManifest(manifestBytes) {
689
849
  let manifest;
690
850
  try {
@@ -833,7 +993,7 @@ async function verifyBundleTarball(tarball, expectations) {
833
993
  return manifest;
834
994
  }
835
995
  async function listFilesRecursively(root, relative = "") {
836
- const entries = await fs.readdir(join(root, relative), { withFileTypes: true });
996
+ const entries = await fs.readdir(join2(root, relative), { withFileTypes: true });
837
997
  const files = [];
838
998
  for (const entry of entries) {
839
999
  const relPath = relative === "" ? entry.name : `${relative}/${entry.name}`;
@@ -857,8 +1017,8 @@ async function listFilesRecursively(root, relative = "") {
857
1017
  return files;
858
1018
  }
859
1019
  async function verifyInstalledBundleDir(installDir, expectations) {
860
- const manifestPath = join(installDir, MANIFEST_NAME);
861
- const signaturePath = join(installDir, MANIFEST_SIG_NAME);
1020
+ const manifestPath = join2(installDir, MANIFEST_NAME);
1021
+ const signaturePath = join2(installDir, MANIFEST_SIG_NAME);
862
1022
  let manifestBytes;
863
1023
  let signature;
864
1024
  try {
@@ -893,12 +1053,15 @@ async function verifyInstalledBundleDir(installDir, expectations) {
893
1053
  }
894
1054
  const entry = byPath.get(relPath);
895
1055
  if (entry === void 0) {
1056
+ if (isGeneratedBytecodeCache(relPath)) {
1057
+ continue;
1058
+ }
896
1059
  throw new BundleVerificationError(
897
1060
  "unlisted_member",
898
1061
  `installed file not listed in signed manifest: '${relPath}'`
899
1062
  );
900
1063
  }
901
- const absolute = join(installDir, relPath);
1064
+ const absolute = join2(installDir, relPath);
902
1065
  const stat = await fs.lstat(absolute);
903
1066
  if (stat.nlink > 1) {
904
1067
  throw new BundleVerificationError(
@@ -963,20 +1126,20 @@ function currentPlatformKey() {
963
1126
  function cacheDirFor(brand) {
964
1127
  const os = osPlatform();
965
1128
  if (os === "darwin") {
966
- return join2(homedir(), "Library", "Caches", brand, "runtime");
1129
+ return join3(homedir2(), "Library", "Caches", brand, "runtime");
967
1130
  }
968
1131
  if (os === "win32") {
969
- const localAppData = process.env.LOCALAPPDATA ?? join2(homedir(), "AppData", "Local");
970
- return join2(localAppData, brand, "runtime");
1132
+ const localAppData = process.env.LOCALAPPDATA ?? join3(homedir2(), "AppData", "Local");
1133
+ return join3(localAppData, brand, "runtime");
971
1134
  }
972
- const xdgCache = process.env.XDG_CACHE_HOME ?? join2(homedir(), ".cache");
973
- return join2(xdgCache, brand.toLowerCase(), "runtime");
1135
+ const xdgCache = process.env.XDG_CACHE_HOME ?? join3(homedir2(), ".cache");
1136
+ return join3(xdgCache, brand.toLowerCase(), "runtime");
974
1137
  }
975
1138
  function runtimeCacheDir() {
976
1139
  const current = cacheDirFor("SQAI");
977
- if (!existsSync(current)) {
1140
+ if (!existsSync2(current)) {
978
1141
  const legacy = cacheDirFor("Algenta");
979
- if (legacy !== current && existsSync(legacy)) {
1142
+ if (legacy !== current && existsSync2(legacy)) {
980
1143
  return legacy;
981
1144
  }
982
1145
  }
@@ -1061,6 +1224,21 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1061
1224
  }
1062
1225
  return new MojoRuntime(config ?? {});
1063
1226
  }
1227
+ async ensureLocalLoginKey() {
1228
+ if (this.options.runtimeFactory || process.env.ALGENTA_API_KEY || process.env.DE_API_KEY) {
1229
+ return;
1230
+ }
1231
+ const key = await readCachedApiKey();
1232
+ if (key) {
1233
+ process.env.ALGENTA_API_KEY = key;
1234
+ return;
1235
+ }
1236
+ throw new SqaiError(
1237
+ "login_required",
1238
+ "Local compute requires a free SQAI device login. Run `sqai login` once, or set SQAI_API_KEY in this process.",
1239
+ { details: { reason: "login_key_missing" } }
1240
+ );
1241
+ }
1064
1242
  cacheDir() {
1065
1243
  return this.options.cacheDir ?? runtimeCacheDir();
1066
1244
  }
@@ -1125,7 +1303,7 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1125
1303
  localCacheKey(modules, platformKey) {
1126
1304
  const canonical = modules.join(",");
1127
1305
  const version = this.options.contract.runtime_bundle.version;
1128
- return createHash3("sha256").update(`${canonical}|${platformKey}|${version}`).digest("hex").slice(0, 20);
1306
+ return createHash4("sha256").update(`${canonical}|${platformKey}|${version}`).digest("hex").slice(0, 20);
1129
1307
  }
1130
1308
  /**
1131
1309
  * Deterministic per-filter daemon endpoint so a filtered runtime never collides
@@ -1134,7 +1312,7 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1134
1312
  filterEndpoint(cacheKey) {
1135
1313
  const seed = parseInt(cacheKey.slice(0, 6), 16) || 0;
1136
1314
  const port = 50100 + seed % 800;
1137
- return { sock: join2(this.cacheDir(), `${cacheKey}.sock`), tcp: `127.0.0.1:${port}` };
1315
+ return { sock: join3(this.cacheDir(), `${cacheKey}.sock`), tcp: `127.0.0.1:${port}` };
1138
1316
  }
1139
1317
  static DAEMON_ENDPOINT_ENV = [
1140
1318
  "ALGENTA_DAEMON_SOCK",
@@ -1151,7 +1329,7 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1151
1329
  process.env.ALGENTA_DAEMON_SOCK = endpoint.sock;
1152
1330
  process.env.ALGENTA_DAEMON_TCP = endpoint.tcp;
1153
1331
  process.env.ALGENTA_MOJO_SOCK = `${endpoint.sock}.worker`;
1154
- process.env.ALGENTA_RUNTIME_DIR = join2(installDir, ".runtime");
1332
+ process.env.ALGENTA_RUNTIME_DIR = join3(installDir, ".runtime");
1155
1333
  return saved;
1156
1334
  }
1157
1335
  restoreEnv(saved) {
@@ -1166,6 +1344,7 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1166
1344
  async doEnsure() {
1167
1345
  const platformKey = currentPlatformKey();
1168
1346
  const filter = this.filterModules();
1347
+ await this.ensureLocalLoginKey();
1169
1348
  const probeEndpoint = filter ? this.filterEndpoint(this.localCacheKey(filter, platformKey)) : void 0;
1170
1349
  const runtime = this.newRuntime(probeEndpoint ? { tcpAddress: probeEndpoint.tcp } : void 0);
1171
1350
  try {
@@ -1193,7 +1372,7 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1193
1372
  const supported = Object.keys(bundle.platforms);
1194
1373
  throw new SqaiError(
1195
1374
  "unsupported_platform",
1196
- supported.length === 0 ? "No managed runtime bundle is published yet for any platform. Start a local Algenta runtime daemon, or set SQAI_ENGINE_URL / SQAI_API_KEY to use a hosted engine." : `No managed runtime bundle is published for '${platformKey}'.`,
1375
+ supported.length === 0 ? "No managed runtime bundle is published yet for any platform. Run `sqai login`, or set SQAI_DEPLOYMENT_URL / SQAI_API_KEY for a managed SQAI deployment." : `No managed runtime bundle is published for '${platformKey}'.`,
1197
1376
  { details: { platform: platformKey, supported } }
1198
1377
  );
1199
1378
  }
@@ -1266,9 +1445,9 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1266
1445
  }
1267
1446
  /** Verify an existing install if present; returns null when absent/invalid. */
1268
1447
  async verifiedInstallOrNull(platformKey, version, removeOnFailure, installKey = version) {
1269
- const installDir = join2(this.cacheDir(), installKey);
1448
+ const installDir = join3(this.cacheDir(), installKey);
1270
1449
  try {
1271
- await fs2.access(join2(installDir, MANIFEST_NAME));
1450
+ await fs2.access(join3(installDir, MANIFEST_NAME));
1272
1451
  } catch {
1273
1452
  return null;
1274
1453
  }
@@ -1301,12 +1480,22 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1301
1480
  }
1302
1481
  async lockIsStale(lockPath) {
1303
1482
  const freshMs = this.options.lockFreshMs ?? LOCK_FRESH_MS;
1483
+ const ageFromMtime = async () => {
1484
+ try {
1485
+ const stat = await fs2.stat(lockPath);
1486
+ return Date.now() - stat.mtimeMs;
1487
+ } catch (error) {
1488
+ const code = error.code;
1489
+ return code === "ENOENT" ? null : Number.POSITIVE_INFINITY;
1490
+ }
1491
+ };
1304
1492
  try {
1305
1493
  const raw = await fs2.readFile(lockPath, "utf-8");
1306
1494
  const parsed = JSON.parse(raw);
1307
1495
  const createdAt = typeof parsed.created_at === "string" ? Date.parse(parsed.created_at) : NaN;
1308
1496
  if (!Number.isFinite(createdAt)) {
1309
- return true;
1497
+ const age = await ageFromMtime();
1498
+ return age !== null && age >= freshMs;
1310
1499
  }
1311
1500
  return Date.now() - createdAt >= freshMs;
1312
1501
  } catch (error) {
@@ -1314,7 +1503,8 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1314
1503
  if (code === "ENOENT") {
1315
1504
  return false;
1316
1505
  }
1317
- return true;
1506
+ const age = await ageFromMtime();
1507
+ return age !== null && age >= freshMs;
1318
1508
  }
1319
1509
  }
1320
1510
  /** Serialize installers on `<cacheDir>/<installKey>.lock`; returns the verified install. */
@@ -1325,7 +1515,7 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1325
1515
  if (existing) {
1326
1516
  return existing;
1327
1517
  }
1328
- const lockPath = join2(cacheDir, `${installKey}.lock`);
1518
+ const lockPath = join3(cacheDir, `${installKey}.lock`);
1329
1519
  const waitTimeoutMs = this.options.lockWaitTimeoutMs ?? LOCK_WAIT_TIMEOUT_MS;
1330
1520
  const pollIntervalMs = this.options.lockPollIntervalMs ?? LOCK_POLL_INTERVAL_MS;
1331
1521
  const deadline = Date.now() + waitTimeoutMs;
@@ -1371,9 +1561,9 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1371
1561
  async downloadVerifyExtract(platformKey, version, platformBundle, installKey = version) {
1372
1562
  const cacheDir = this.cacheDir();
1373
1563
  const token = randomBytes2(6).toString("hex");
1374
- const tarballPath = join2(cacheDir, `tmp-${token}.tgz`);
1375
- const extractDir = join2(cacheDir, `tmp-${token}`);
1376
- const installDir = join2(cacheDir, installKey);
1564
+ const tarballPath = join3(cacheDir, `tmp-${token}.tgz`);
1565
+ const extractDir = join3(cacheDir, `tmp-${token}`);
1566
+ const installDir = join3(cacheDir, installKey);
1377
1567
  const url = resolveBundleUrl(platformBundle.url, version, platformKey);
1378
1568
  try {
1379
1569
  const download = this.options.downloadBundle ?? defaultDownloadBundle;
@@ -1448,10 +1638,11 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1448
1638
  throw this.wrapVerificationError(error, platformKey, version);
1449
1639
  }
1450
1640
  const daemonCommand = [
1451
- join2(installed.installDir, ...entryRelative.split("/")),
1641
+ join3(installed.installDir, ...entryRelative.split("/")),
1452
1642
  "start",
1453
1643
  "--foreground"
1454
1644
  ];
1645
+ await this.ensureLocalLoginKey();
1455
1646
  const managed = this.newRuntime(
1456
1647
  endpoint ? { autoStart: true, daemonCommand, tcpAddress: endpoint.tcp } : { autoStart: true, daemonCommand }
1457
1648
  );
@@ -1486,7 +1677,7 @@ var RuntimeProvisioner = class _RuntimeProvisioner {
1486
1677
  }
1487
1678
  try {
1488
1679
  await fs2.writeFile(
1489
- join2(this.cacheDir(), "state.json"),
1680
+ join3(this.cacheDir(), "state.json"),
1490
1681
  JSON.stringify(
1491
1682
  { pid: null, version, started_at: (/* @__PURE__ */ new Date()).toISOString() },
1492
1683
  null,
@@ -1520,7 +1711,7 @@ async function dispatchComputation(target, entry, argsVector, seed, requestId, t
1520
1711
  request_id: response.request_id ?? requestId
1521
1712
  };
1522
1713
  } catch (error) {
1523
- throw fromAlgentaError(error);
1714
+ throw fromEngineError(error);
1524
1715
  }
1525
1716
  }
1526
1717
  return dispatchHttp(target, module, functionName, args, requestId);
@@ -1557,7 +1748,7 @@ async function dispatchHttp(target, module, functionName, args, requestId) {
1557
1748
  } catch (error) {
1558
1749
  throw new SqaiError(
1559
1750
  "compute_runtime_unavailable",
1560
- `The hosted compute endpoint is unreachable: ${error.message}. Check SQAI_ENGINE_URL / SQAI_API_KEY.`,
1751
+ `The SQAI deployment endpoint is unreachable: ${error.message}. Check SQAI_DEPLOYMENT_URL / SQAI_API_KEY.`,
1561
1752
  { retryable: true }
1562
1753
  );
1563
1754
  }
@@ -1740,7 +1931,7 @@ var SQAI = class {
1740
1931
  const env = readEnv();
1741
1932
  this.mode = inferMode(config.mode, env);
1742
1933
  this.apiKey = config.apiKey ?? env.apiKey;
1743
- this.engineUrl = config.engineUrl ?? env.engineUrl;
1934
+ this.engineUrl = config.deploymentUrl ?? config.engineUrl ?? env.engineUrl;
1744
1935
  this.tenantId = config.tenantId ?? null;
1745
1936
  this.policy = config.policy;
1746
1937
  this.configuredTarget = config.computeTarget;
@@ -1768,7 +1959,7 @@ var SQAI = class {
1768
1959
  this.algentaRuntime = this.rawRuntime;
1769
1960
  }
1770
1961
  // ── Query plane ────────────────────────────────────────────────────────────
1771
- async connect(source, options = {}) {
1962
+ async connect(source = null, options = {}) {
1772
1963
  try {
1773
1964
  const registration = await this.algentaRuntime.connect(source, options);
1774
1965
  const mapped = mapSource(registration);
@@ -1787,7 +1978,84 @@ var SQAI = class {
1787
1978
  this.sources.set(mapped.name, mapped);
1788
1979
  return mapped;
1789
1980
  } catch (error) {
1790
- throw error instanceof SqaiError ? error : fromAlgentaError(error);
1981
+ throw error instanceof SqaiError ? error : fromEngineError(error);
1982
+ }
1983
+ }
1984
+ async createConnector(request2) {
1985
+ const createConnector = this.algentaRuntime.createConnector;
1986
+ if (typeof createConnector !== "function") {
1987
+ throw unsupportedSubstrateOperation("createConnector");
1988
+ }
1989
+ try {
1990
+ return await createConnector.call(this.algentaRuntime, request2);
1991
+ } catch (error) {
1992
+ throw fromEngineError(error);
1993
+ }
1994
+ }
1995
+ async listConnectors(options = {}) {
1996
+ const listConnectors = this.algentaRuntime.listConnectors;
1997
+ if (typeof listConnectors !== "function") {
1998
+ throw unsupportedSubstrateOperation("listConnectors");
1999
+ }
2000
+ try {
2001
+ return await listConnectors.call(this.algentaRuntime, options);
2002
+ } catch (error) {
2003
+ throw fromEngineError(error);
2004
+ }
2005
+ }
2006
+ async getConnector(connectorId) {
2007
+ const getConnector = this.algentaRuntime.getConnector;
2008
+ if (typeof getConnector !== "function") {
2009
+ throw unsupportedSubstrateOperation("getConnector");
2010
+ }
2011
+ try {
2012
+ return await getConnector.call(this.algentaRuntime, connectorId);
2013
+ } catch (error) {
2014
+ throw fromEngineError(error);
2015
+ }
2016
+ }
2017
+ async updateConnector(connectorId, request2) {
2018
+ const updateConnector = this.algentaRuntime.updateConnector;
2019
+ if (typeof updateConnector !== "function") {
2020
+ throw unsupportedSubstrateOperation("updateConnector");
2021
+ }
2022
+ try {
2023
+ return await updateConnector.call(this.algentaRuntime, connectorId, request2);
2024
+ } catch (error) {
2025
+ throw fromEngineError(error);
2026
+ }
2027
+ }
2028
+ async testConnector(connectorIdOrConnector) {
2029
+ const testConnector = this.algentaRuntime.testConnector;
2030
+ if (typeof testConnector !== "function") {
2031
+ throw unsupportedSubstrateOperation("testConnector");
2032
+ }
2033
+ try {
2034
+ return await testConnector.call(this.algentaRuntime, connectorIdOrConnector);
2035
+ } catch (error) {
2036
+ throw fromEngineError(error);
2037
+ }
2038
+ }
2039
+ async browseConnector(connectorIdOrConnector) {
2040
+ const browseConnector = this.algentaRuntime.browseConnector;
2041
+ if (typeof browseConnector !== "function") {
2042
+ throw unsupportedSubstrateOperation("browseConnector");
2043
+ }
2044
+ try {
2045
+ return await browseConnector.call(this.algentaRuntime, connectorIdOrConnector);
2046
+ } catch (error) {
2047
+ throw fromEngineError(error);
2048
+ }
2049
+ }
2050
+ async deleteConnector(connectorId) {
2051
+ const deleteConnector = this.algentaRuntime.deleteConnector;
2052
+ if (typeof deleteConnector !== "function") {
2053
+ throw unsupportedSubstrateOperation("deleteConnector");
2054
+ }
2055
+ try {
2056
+ await deleteConnector.call(this.algentaRuntime, connectorId);
2057
+ } catch (error) {
2058
+ throw fromEngineError(error);
1791
2059
  }
1792
2060
  }
1793
2061
  listSources() {
@@ -1798,28 +2066,28 @@ var SQAI = class {
1798
2066
  try {
1799
2067
  return await this.algentaRuntime.resolve(spec);
1800
2068
  } catch (error) {
1801
- throw fromAlgentaError(error);
2069
+ throw fromEngineError(error);
1802
2070
  }
1803
2071
  }
1804
2072
  async query(plan) {
1805
2073
  try {
1806
2074
  return await this.algentaRuntime.query(plan);
1807
2075
  } catch (error) {
1808
- throw fromAlgentaError(error);
2076
+ throw fromEngineError(error);
1809
2077
  }
1810
2078
  }
1811
2079
  async queryWithMetadata(plan) {
1812
2080
  try {
1813
2081
  return await this.algentaRuntime.queryWithMetadata(plan);
1814
2082
  } catch (error) {
1815
- throw fromAlgentaError(error);
2083
+ throw fromEngineError(error);
1816
2084
  }
1817
2085
  }
1818
2086
  async verify(plan) {
1819
2087
  try {
1820
2088
  return await this.algentaRuntime.verify(plan);
1821
2089
  } catch (error) {
1822
- throw fromAlgentaError(error);
2090
+ throw fromEngineError(error);
1823
2091
  }
1824
2092
  }
1825
2093
  async ask(spec) {
@@ -1841,14 +2109,14 @@ var SQAI = class {
1841
2109
  if (typeof this.algentaRuntime.extractColumns !== "function") {
1842
2110
  throw new SqaiError(
1843
2111
  "unsupported_operation",
1844
- "The installed algenta-sdk build does not provide extractColumns; upgrade the substrate.",
1845
- { details: { required: "algenta-sdk extractColumns" } }
2112
+ "The installed SQAI package does not provide extractColumns; upgrade SQAI.",
2113
+ { details: { required: "sqai.extractColumns" } }
1846
2114
  );
1847
2115
  }
1848
2116
  try {
1849
2117
  return await this.algentaRuntime.extractColumns(sourceName, columns, options);
1850
2118
  } catch (error) {
1851
- throw fromAlgentaError(error);
2119
+ throw fromEngineError(error);
1852
2120
  }
1853
2121
  }
1854
2122
  // ── Computation plane ──────────────────────────────────────────────────────
@@ -1978,7 +2246,7 @@ var SQAI = class {
1978
2246
  "API mode requires SQAI_API_KEY for the computation plane."
1979
2247
  );
1980
2248
  }
1981
- return { kind: "http", baseUrl: "https://api.algenta.ai", apiKey: this.apiKey };
2249
+ return { kind: "http", baseUrl: "https://api.sqai.com", apiKey: this.apiKey };
1982
2250
  }
1983
2251
  const runtime = await this.provisioner.ensure();
1984
2252
  return { kind: "local", runtime };
@@ -2036,6 +2304,13 @@ var SQAI = class {
2036
2304
  function createSQAI(config = {}) {
2037
2305
  return new SQAI(config);
2038
2306
  }
2307
+ function unsupportedSubstrateOperation(operation) {
2308
+ return new SqaiError(
2309
+ "unsupported_operation",
2310
+ `The installed SQAI package does not provide ${operation}; upgrade SQAI.`,
2311
+ { details: { required: `sqai.${operation}` } }
2312
+ );
2313
+ }
2039
2314
  function mapSource(registration) {
2040
2315
  const record = registration;
2041
2316
  return {
@@ -2049,11 +2324,60 @@ function mapSource(registration) {
2049
2324
  status: String(registration.status ?? "ready")
2050
2325
  };
2051
2326
  }
2327
+
2328
+ // src/types.ts
2329
+ var SQAI_CONNECTOR_TYPES = [
2330
+ "s3",
2331
+ "gcs",
2332
+ "azure",
2333
+ "clickhouse",
2334
+ "bigquery",
2335
+ "snowflake",
2336
+ "redshift",
2337
+ "postgres",
2338
+ "postgresql",
2339
+ "sqlite",
2340
+ "mysql",
2341
+ "mssql",
2342
+ "oracle",
2343
+ "neo4j",
2344
+ "redis",
2345
+ "elastic",
2346
+ "elasticsearch",
2347
+ "rest",
2348
+ "rest_api",
2349
+ "file",
2350
+ "file_upload",
2351
+ "github_repo",
2352
+ "gitlab_repo",
2353
+ "bitbucket_repo",
2354
+ "local_repo",
2355
+ "repo_archive"
2356
+ ];
2357
+ var SQAI_CONNECTOR_TYPE_ALIASES = {
2358
+ postgresql: "postgres",
2359
+ click_house: "clickhouse",
2360
+ redis_cache: "redis",
2361
+ elastic: "elasticsearch",
2362
+ rest_api: "rest",
2363
+ file_upload: "file",
2364
+ github: "github_repo",
2365
+ github_repository: "github_repo",
2366
+ gitlab: "gitlab_repo",
2367
+ gitlab_repository: "gitlab_repo",
2368
+ bitbucket: "bitbucket_repo",
2369
+ bitbucket_repository: "bitbucket_repo",
2370
+ local_repository: "local_repo",
2371
+ archive_repository: "repo_archive"
2372
+ };
2052
2373
  export {
2053
2374
  ContractIndex,
2375
+ LoginError,
2054
2376
  ResultStore,
2055
2377
  RuntimeProvisioner,
2056
2378
  SQAI,
2379
+ SQAI_CONNECTOR_TYPES,
2380
+ SQAI_CONNECTOR_TYPE_ALIASES,
2057
2381
  SQAI_ERROR_CODES,
2058
2382
  SqaiError,
2059
2383
  computationHash,
@@ -2061,8 +2385,11 @@ export {
2061
2385
  currentPlatformKey,
2062
2386
  invocationHash,
2063
2387
  isReadOnlyEligible,
2388
+ login,
2389
+ logout,
2064
2390
  lookupCapability,
2391
+ readCachedApiKey,
2065
2392
  runtimeCacheDir,
2393
+ sqaiHome,
2066
2394
  validateComputation
2067
2395
  };
2068
- //# sourceMappingURL=index.js.map