@openbkn/bkn-sdk 0.1.1-alpha.9 → 0.1.1

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,12 +9,15 @@ var HttpError = class extends Error {
9
9
  status;
10
10
  statusText;
11
11
  body;
12
- constructor(status2, statusText, body) {
12
+ /** Optional next-step guidance, overriding the status default (e.g. AppKey re-issue). */
13
+ hint;
14
+ constructor(status2, statusText, body, hint) {
13
15
  super(`HTTP ${status2} ${statusText}`);
14
16
  this.name = "HttpError";
15
17
  this.status = status2;
16
18
  this.statusText = statusText;
17
19
  this.body = body;
20
+ this.hint = hint;
18
21
  }
19
22
  };
20
23
  var InputError = class extends Error {
@@ -35,7 +38,8 @@ function formatError(err) {
35
38
  if (err instanceof HttpError) {
36
39
  const serverMsg = serverError(err.body);
37
40
  if (err.status === 401) {
38
- return `Not authorized (HTTP 401)${serverMsg ? `: ${serverMsg}` : ""}. Run \`openbkn auth login\` and retry.`;
41
+ const next = err.hint ?? "Run `openbkn auth login` and retry.";
42
+ return `Not authorized (HTTP 401)${serverMsg ? `: ${serverMsg}` : ""}. ${next}`;
39
43
  }
40
44
  if (err.status === 403) {
41
45
  return `Forbidden (HTTP 403)${serverMsg ? `: ${serverMsg}` : " \u2014 admin privileges required"}.`;
@@ -74,6 +78,23 @@ function truncate(s, n) {
74
78
 
75
79
  // src/auth/oauth.ts
76
80
  import { spawn } from "child_process";
81
+
82
+ // src/api/tls.ts
83
+ import { Agent, fetch as undiciFetch } from "undici";
84
+ var insecureAgent;
85
+ function insecureDispatcher() {
86
+ insecureAgent ??= new Agent({ connect: { rejectUnauthorized: false } });
87
+ return insecureAgent;
88
+ }
89
+ function tlsFetch(insecure, url, init) {
90
+ if (!insecure) return fetch(url, init);
91
+ return undiciFetch(url, {
92
+ ...init,
93
+ dispatcher: insecureDispatcher()
94
+ });
95
+ }
96
+
97
+ // src/auth/oauth.ts
77
98
  function normalizeBaseUrl(value) {
78
99
  return value.replace(/\/+$/, "");
79
100
  }
@@ -114,22 +135,9 @@ function mergeCookies(existing, res) {
114
135
  for (const sc of setCookies) add(sc.split(";")[0]?.trim() ?? "");
115
136
  return [...map.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
116
137
  }
117
- async function fetchAuthStatus(baseUrl) {
118
- try {
119
- const res = await fetch(`${normalizeBaseUrl(baseUrl)}/install-status.json`, {
120
- headers: { Accept: "application/json" }
121
- });
122
- if (!res.ok) return null;
123
- const j = await res.json();
124
- if (!j.auth) return null;
125
- return { enabled: Boolean(j.auth.enabled), stack: j.auth.stack };
126
- } catch {
127
- return null;
128
- }
129
- }
130
- async function refreshAccessToken(baseUrl, refreshToken, clientId = "openbkn-sdk") {
138
+ async function refreshAccessToken(baseUrl, refreshToken, clientId = "openbkn-sdk", insecure) {
131
139
  const base = normalizeBaseUrl(baseUrl);
132
- const res = await fetch(`${base}/oauth2/token`, {
140
+ const res = await tlsFetch(insecure, `${base}/oauth2/token`, {
133
141
  method: "POST",
134
142
  headers: {
135
143
  "Content-Type": "application/x-www-form-urlencoded",
@@ -159,10 +167,10 @@ var form = (body) => ({
159
167
  },
160
168
  body: new URLSearchParams(body).toString()
161
169
  });
162
- async function requestDeviceCode(base, clientId, scope, audience) {
170
+ async function requestDeviceCode(base, clientId, scope, audience, insecure) {
163
171
  const params = { client_id: clientId, scope };
164
172
  if (audience) params.audience = audience;
165
- const res = await fetch(`${base}/oauth2/device/auth`, form(params));
173
+ const res = await tlsFetch(insecure, `${base}/oauth2/device/auth`, form(params));
166
174
  if (!res.ok) {
167
175
  throw new Error(`Device auth failed (${res.status}): ${await res.text() || res.statusText}`);
168
176
  }
@@ -174,12 +182,13 @@ async function requestDeviceCode(base, clientId, scope, audience) {
174
182
  }
175
183
  return da;
176
184
  }
177
- async function pollDeviceToken(base, deviceCode, clientId, intervalMs, windowMs) {
185
+ async function pollDeviceToken(base, deviceCode, clientId, intervalMs, windowMs, insecure) {
178
186
  let interval = intervalMs;
179
187
  const deadline = Date.now() + windowMs;
180
188
  while (Date.now() < deadline) {
181
189
  await new Promise((r) => setTimeout(r, interval));
182
- const tokRes = await fetch(
190
+ const tokRes = await tlsFetch(
191
+ insecure,
183
192
  `${base}/oauth2/token`,
184
193
  form({ grant_type: DEVICE_GRANT, device_code: deviceCode, client_id: clientId })
185
194
  );
@@ -205,14 +214,27 @@ async function pollDeviceToken(base, deviceCode, clientId, intervalMs, windowMs)
205
214
  async function deviceLogin(baseUrl, opts = {}) {
206
215
  const base = normalizeBaseUrl(baseUrl);
207
216
  const clientId = opts.clientId ?? DEFAULT_DEVICE_CLIENT_ID;
208
- const da = await requestDeviceCode(base, clientId, opts.scope ?? DEVICE_SCOPE, opts.audience);
217
+ const da = await requestDeviceCode(
218
+ base,
219
+ clientId,
220
+ opts.scope ?? DEVICE_SCOPE,
221
+ opts.audience,
222
+ opts.insecure
223
+ );
209
224
  opts.onPrompt?.({
210
225
  userCode: da.user_code,
211
226
  verificationUri: onBaseHost(base, da.verification_uri),
212
227
  verificationUriComplete: da.verification_uri_complete ? onBaseHost(base, da.verification_uri_complete) : void 0
213
228
  });
214
229
  const windowMs = Math.min(opts.timeoutMs ?? Number.POSITIVE_INFINITY, da.expires_in * 1e3);
215
- return pollDeviceToken(base, da.device_code, clientId, (da.interval ?? 5) * 1e3, windowMs);
230
+ return pollDeviceToken(
231
+ base,
232
+ da.device_code,
233
+ clientId,
234
+ (da.interval ?? 5) * 1e3,
235
+ windowMs,
236
+ opts.insecure
237
+ );
216
238
  }
217
239
  function onBaseHost(base, uri) {
218
240
  try {
@@ -226,10 +248,16 @@ function onBaseHost(base, uri) {
226
248
  async function credentialDeviceLogin(baseUrl, username, password, opts = {}) {
227
249
  const base = normalizeBaseUrl(baseUrl);
228
250
  const clientId = opts.clientId ?? DEFAULT_DEVICE_CLIENT_ID;
229
- const da = await requestDeviceCode(base, clientId, opts.scope ?? DEVICE_SCOPE, opts.audience);
251
+ const da = await requestDeviceCode(
252
+ base,
253
+ clientId,
254
+ opts.scope ?? DEVICE_SCOPE,
255
+ opts.audience,
256
+ opts.insecure
257
+ );
230
258
  let jar = "";
231
259
  const hop = async (url, init) => {
232
- const r = await fetch(url, {
260
+ const r = await tlsFetch(opts.insecure, url, {
233
261
  method: init?.method ?? "GET",
234
262
  headers: { Cookie: jar, Accept: "text/html,*/*;q=0.8", ...init?.headers ?? {} },
235
263
  body: init?.body,
@@ -267,38 +295,40 @@ async function credentialDeviceLogin(baseUrl, username, password, opts = {}) {
267
295
  loc = r.headers.get("location");
268
296
  }
269
297
  const windowMs = Math.min(opts.timeoutMs ?? Number.POSITIVE_INFINITY, da.expires_in * 1e3);
270
- return pollDeviceToken(base, da.device_code, clientId, (da.interval ?? 5) * 1e3, windowMs);
298
+ return pollDeviceToken(
299
+ base,
300
+ da.device_code,
301
+ clientId,
302
+ (da.interval ?? 5) * 1e3,
303
+ windowMs,
304
+ opts.insecure
305
+ );
271
306
  }
272
307
 
273
308
  // src/api/headers.ts
274
309
  function buildHeaders(ctx, extra) {
275
310
  return {
276
- // No token = a no-auth platform (no bkn-safe); send no Authorization.
277
- ...ctx.token ? { authorization: `Bearer ${ctx.token}`, token: ctx.token } : {},
311
+ authorization: `Bearer ${ctx.token}`,
278
312
  "x-business-domain": ctx.businessDomain,
279
313
  ...extra
280
314
  };
281
315
  }
282
316
 
283
- // src/api/tls.ts
284
- function applyTls(ctx) {
285
- if (ctx.insecure && process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0") {
286
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
287
- }
288
- }
289
-
290
317
  // src/api/http.ts
291
318
  var DEFAULT_TIMEOUT_MS = 3e4;
292
319
  async function request(ctx, path, init = {}) {
293
320
  const url = new URL(path.startsWith("http") ? path : `${ctx.baseUrl}${path}`);
294
321
  for (const [k, v] of Object.entries(init.query ?? {})) {
295
- if (v !== void 0) url.searchParams.set(k, String(v));
322
+ if (Array.isArray(v)) {
323
+ for (const item of v) url.searchParams.append(k, String(item));
324
+ } else if (v !== void 0) {
325
+ url.searchParams.set(k, String(v));
326
+ }
296
327
  }
297
- applyTls(ctx);
298
328
  const hasBody = init.body !== void 0;
299
329
  const controller = new AbortController();
300
330
  const timer = setTimeout(() => controller.abort(), init.timeoutMs ?? DEFAULT_TIMEOUT_MS);
301
- const send = () => fetch(url, {
331
+ const send = () => tlsFetch(ctx.insecure, url, {
302
332
  method: init.method ?? (hasBody ? "POST" : "GET"),
303
333
  headers: buildHeaders(ctx, {
304
334
  ...hasBody ? { "content-type": "application/json" } : {},
@@ -313,16 +343,27 @@ async function request(ctx, path, init = {}) {
313
343
  res = await send();
314
344
  }
315
345
  const text = await res.text();
316
- if (!res.ok) throw new HttpError(res.status, res.statusText, text);
346
+ if (!res.ok) throw new HttpError(res.status, res.statusText, text, hintFor(ctx, res.status));
317
347
  return text ? JSON.parse(text) : void 0;
318
348
  } finally {
319
349
  clearTimeout(timer);
320
350
  }
321
351
  }
352
+ function hintFor(ctx, status2) {
353
+ if (status2 === 401 && ctx.token.startsWith("bak_")) {
354
+ return "AppKey invalid / expired / revoked / owner disabled \u2014 re-issue with `openbkn appkey create` (or `appkey regenerate <id>`). Do not auto-retry.";
355
+ }
356
+ return void 0;
357
+ }
322
358
  async function tryRefresh(ctx) {
323
359
  if (!ctx.refresh) return false;
324
360
  try {
325
- const t = await refreshAccessToken(ctx.baseUrl, ctx.refresh.refreshToken, ctx.refresh.clientId);
361
+ const t = await refreshAccessToken(
362
+ ctx.baseUrl,
363
+ ctx.refresh.refreshToken,
364
+ ctx.refresh.clientId,
365
+ ctx.insecure
366
+ );
326
367
  ctx.token = t.accessToken;
327
368
  if (t.refreshToken) ctx.refresh.refreshToken = t.refreshToken;
328
369
  ctx.refresh.persist(t);
@@ -367,6 +408,7 @@ function isExpired(claims, nowMs = Date.now()) {
367
408
 
368
409
  // src/config/store.ts
369
410
  var PROFILE_RE = /^[A-Za-z0-9_-]{1,64}$/;
411
+ var USER_ID_RE = /^[A-Za-z0-9._@-]{1,128}$/;
370
412
  var IS_WIN = process.platform === "win32";
371
413
  function configDir() {
372
414
  return process.env.BKN_CONFIG_DIR ?? join(homedir(), ".bkn");
@@ -393,7 +435,14 @@ function userDir(baseUrl, userId) {
393
435
  return join(platformDir(baseUrl), "users", userId);
394
436
  }
395
437
  function userIdFromToken(token) {
396
- return decodeJwt(token.idToken ?? "")?.sub ?? decodeJwt(token.accessToken)?.sub ?? "default";
438
+ const sub = decodeJwt(token.idToken ?? "")?.sub ?? decodeJwt(token.accessToken)?.sub;
439
+ if (sub === void 0) return "default";
440
+ if (typeof sub !== "string" || !USER_ID_RE.test(sub) || sub === "." || sub === "..") {
441
+ throw new Error(
442
+ `Token subject '${String(sub)}' is not a usable user id (expected 1-128 chars from [A-Za-z0-9._@-]). Refusing to store this token.`
443
+ );
444
+ }
445
+ return sub;
397
446
  }
398
447
  function readState() {
399
448
  return readJson(statePath()) ?? {};
@@ -418,10 +467,10 @@ function readToken(baseUrl, userId = activeUserId(baseUrl)) {
418
467
  if (!userId) return void 0;
419
468
  return readJson(join(userDir(baseUrl, userId), "token.json")) ?? void 0;
420
469
  }
421
- function writeToken(baseUrl, token) {
470
+ function writeToken(baseUrl, token, opts = {}) {
422
471
  const userId = userIdFromToken(token);
423
472
  writeJson(join(userDir(baseUrl, userId), "token.json"), token, 384);
424
- setActiveUser(baseUrl, userId);
473
+ if (opts.setActive !== false) setActiveUser(baseUrl, userId);
425
474
  return userId;
426
475
  }
427
476
  function deleteToken(baseUrl, userId = activeUserId(baseUrl)) {
@@ -460,6 +509,14 @@ function listPlatforms() {
460
509
  }
461
510
  return out;
462
511
  }
512
+ function findUserId(baseUrl, userOrName) {
513
+ const users = listPlatforms().find((p) => p.baseUrl === baseUrl)?.users ?? [];
514
+ const match = users.find((u) => u.userId === userOrName) ?? users.find((u) => (u.username ?? u.displayName) === userOrName);
515
+ return match?.userId ?? null;
516
+ }
517
+ function usersOfPlatform(baseUrl) {
518
+ return listPlatforms().find((p) => p.baseUrl === baseUrl)?.users ?? [];
519
+ }
463
520
  function decodeKey(key) {
464
521
  try {
465
522
  const b64 = key.replace(/-/g, "+").replace(/_/g, "/");
@@ -485,6 +542,14 @@ function writeJson(path, value, mode = 384) {
485
542
  }
486
543
 
487
544
  // src/config/resolve.ts
545
+ function resolveUserId(baseUrl, userOrName) {
546
+ const id = findUserId(baseUrl, userOrName);
547
+ if (id) return id;
548
+ const known = usersOfPlatform(baseUrl).map((u) => u.username ?? u.userId).join(", ");
549
+ throw new InputError(
550
+ `No saved user '${userOrName}' on ${baseUrl}. Saved: ${known || "(none)"}. See \`openbkn auth users ${baseUrl}\`.`
551
+ );
552
+ }
488
553
  function resolveContext(opts = {}) {
489
554
  const baseUrl = opts.baseUrl ?? process.env.BKN_BASE_URL ?? activePlatform();
490
555
  if (!baseUrl) {
@@ -493,22 +558,29 @@ function resolveContext(opts = {}) {
493
558
  );
494
559
  }
495
560
  const normalized = baseUrl.replace(/\/+$/, "");
496
- const stored = readToken(normalized);
561
+ const user = opts.user ?? process.env.BKN_USER;
562
+ const stored = user ? readToken(normalized, resolveUserId(normalized, user)) : readToken(normalized);
497
563
  const explicit = opts.token ?? process.env.BKN_TOKEN;
498
564
  const token = explicit ?? stored?.accessToken ?? "";
499
- if (!token && !stored?.noAuth) {
565
+ if (!token) {
500
566
  throw new InputError("No access token. Set BKN_TOKEN or run `openbkn auth login`.");
501
567
  }
502
568
  const insecure = opts.insecure ?? stored?.tlsInsecure ?? false;
503
569
  const refresh = !explicit && stored?.refreshToken ? {
504
570
  refreshToken: stored.refreshToken,
505
571
  persist: (t) => {
506
- writeToken(normalized, {
507
- ...stored,
508
- accessToken: t.accessToken,
509
- refreshToken: t.refreshToken ?? stored.refreshToken,
510
- idToken: t.idToken ?? stored.idToken
511
- });
572
+ writeToken(
573
+ normalized,
574
+ {
575
+ ...stored,
576
+ accessToken: t.accessToken,
577
+ refreshToken: t.refreshToken ?? stored.refreshToken,
578
+ idToken: t.idToken ?? stored.idToken
579
+ },
580
+ // `--user` picks an identity for this command only; a refresh
581
+ // must not promote it to the default for the next one.
582
+ { setActive: !user }
583
+ );
512
584
  }
513
585
  } : void 0;
514
586
  return {
@@ -571,6 +643,13 @@ async function setUserPasswordSafe(ctx, userId, password) {
571
643
  });
572
644
  return { ok: true };
573
645
  }
646
+ async function changePasswordSafe(ctx, account, oldPassword, newPassword) {
647
+ await request(ctx, "/api/safe/v1/auth/change-password", {
648
+ method: "POST",
649
+ body: { account, old_password: oldPassword, new_password: newPassword }
650
+ });
651
+ return { ok: true };
652
+ }
574
653
  function getUserRolesSafe(ctx, userId) {
575
654
  return request(ctx, `${ADMIN}/role-bindings`, { query: { accessor_id: userId } });
576
655
  }
@@ -684,6 +763,43 @@ async function setRolePermissionSafe(ctx, roleId, grant, perm) {
684
763
  });
685
764
  return { ok: true };
686
765
  }
766
+ function getLicenseSafe(ctx) {
767
+ return request(ctx, `${ADMIN}/license`);
768
+ }
769
+ async function importLicenseSafe(ctx, licenseText, opts = {}) {
770
+ const text = licenseText.trim();
771
+ if (!text) throw new InputError("license text is empty");
772
+ try {
773
+ return await request(ctx, `${ADMIN}/license/${opts.receipt ? "receipt" : "import"}`, {
774
+ method: "POST",
775
+ body: { license: text }
776
+ });
777
+ } catch (err) {
778
+ if (err instanceof HttpError) {
779
+ const stored = storedImport(err.body);
780
+ if (stored) return stored;
781
+ }
782
+ throw err;
783
+ }
784
+ }
785
+ function storedImport(body) {
786
+ try {
787
+ const parsed = JSON.parse(body);
788
+ if (parsed && parsed.stored === true) return parsed;
789
+ } catch {
790
+ }
791
+ return null;
792
+ }
793
+ function activateLicenseSafe(ctx) {
794
+ return request(ctx, `${ADMIN}/license/activate`, { method: "POST" });
795
+ }
796
+ async function removeLicenseSafe(ctx) {
797
+ await request(ctx, `${ADMIN}/license`, { method: "DELETE" });
798
+ return { ok: true };
799
+ }
800
+ function getLicenseFingerprintSafe(ctx) {
801
+ return request(ctx, `${ADMIN}/license/fingerprint`);
802
+ }
687
803
 
688
804
  // src/resources/admin.ts
689
805
  var DEFAULT_NEW_USER_PASSWORD = "openbkn";
@@ -744,7 +860,13 @@ function admin(ctx) {
744
860
  roleUpdate: (roleId, input) => updateRoleSafe(ctx, roleId, input),
745
861
  roleDelete: (roleId) => deleteRoleSafe(ctx, roleId),
746
862
  rolePermission: (roleId, grant, resourceType, resourceId, operations) => setRolePermissionSafe(ctx, roleId, grant, { resourceType, resourceId, operations }),
747
- auditList: (_opts) => notOnSafe("audit list")
863
+ auditList: (_opts) => notOnSafe("audit list"),
864
+ // ── license (cluster license hub; weak judgements — display/ops only) ──
865
+ licenseGet: () => getLicenseSafe(ctx),
866
+ licenseImport: (licenseText, opts) => importLicenseSafe(ctx, licenseText, opts),
867
+ licenseActivate: () => activateLicenseSafe(ctx),
868
+ licenseRemove: () => removeLicenseSafe(ctx),
869
+ licenseFingerprint: () => getLicenseFingerprintSafe(ctx)
748
870
  };
749
871
  }
750
872
 
@@ -816,7 +938,6 @@ function extractText(result) {
816
938
  return "";
817
939
  }
818
940
  async function sendChat(ctx, info, query, opts = {}) {
819
- applyTls(ctx);
820
941
  const body = {
821
942
  agent_id: info.id,
822
943
  agent_key: info.key,
@@ -827,7 +948,7 @@ async function sendChat(ctx, info, query, opts = {}) {
827
948
  if (opts.conversationId) body.conversation_id = opts.conversationId;
828
949
  const res = await authFetch(
829
950
  ctx,
830
- () => fetch(`${ctx.baseUrl}${FACTORY}/v1/app/${info.key}/chat/completion`, {
951
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${FACTORY}/v1/app/${info.key}/chat/completion`, {
831
952
  method: "POST",
832
953
  headers: {
833
954
  ...buildHeaders(ctx),
@@ -1029,6 +1150,9 @@ function nextId() {
1029
1150
  function mcpUrl(ctx) {
1030
1151
  return `${ctx.baseUrl}${MCP_PATH}`;
1031
1152
  }
1153
+ function mcpInfo(ctx) {
1154
+ return request(ctx, `${MCP_PATH}/info`);
1155
+ }
1032
1156
  function headers(ctx, knId, sessionId) {
1033
1157
  const h = {
1034
1158
  "content-type": "application/json",
@@ -1050,10 +1174,9 @@ function parseBody(text) {
1050
1174
  }
1051
1175
  }
1052
1176
  async function post(ctx, knId, sessionId, body) {
1053
- applyTls(ctx);
1054
1177
  const res = await authFetch(
1055
1178
  ctx,
1056
- () => fetch(mcpUrl(ctx), {
1179
+ () => tlsFetch(ctx.insecure, mcpUrl(ctx), {
1057
1180
  method: "POST",
1058
1181
  headers: headers(ctx, knId, sessionId),
1059
1182
  body: JSON.stringify(body)
@@ -1135,6 +1258,17 @@ function findSkills(ctx, knId, objectTypeId, topK) {
1135
1258
  if (topK !== void 0) args.top_k = topK;
1136
1259
  return callTool(ctx, knId, "find_skills", args);
1137
1260
  }
1261
+ function getKnDetail(ctx, knId, detailLevel) {
1262
+ const args = { response_format: "json" };
1263
+ if (detailLevel) args.detail_level = detailLevel;
1264
+ return callTool(ctx, knId, "get_kn_detail", args);
1265
+ }
1266
+ function getObjectTypes(ctx, knId, ids) {
1267
+ return callTool(ctx, knId, "get_object_types", { ids, response_format: "json" });
1268
+ }
1269
+ function getRelationTypes(ctx, knId, ids) {
1270
+ return callTool(ctx, knId, "get_relation_types", { ids, response_format: "json" });
1271
+ }
1138
1272
  function listTools(ctx, knId) {
1139
1273
  return callMethod(ctx, knId, "tools/list");
1140
1274
  }
@@ -1169,8 +1303,16 @@ function context(ctx) {
1169
1303
  searchSchema: (knId, query, opts) => searchSchema(ctx, knId, query, opts),
1170
1304
  queryObjectInstance: (knId, args) => queryObjectInstance(ctx, knId, args),
1171
1305
  findSkills: (knId, objectTypeId, topK) => findSkills(ctx, knId, objectTypeId, topK),
1306
+ // Progressive schema disclosure: skeleton first (summary), then drill down.
1307
+ knDetail: (knId, detailLevel) => getKnDetail(ctx, knId, detailLevel),
1308
+ objectTypes: (knId, ids) => getObjectTypes(ctx, knId, ids),
1309
+ relationTypes: (knId, ids) => getRelationTypes(ctx, knId, ids),
1310
+ info: () => mcpInfo(ctx),
1172
1311
  tools: (knId) => listTools(ctx, knId),
1173
1312
  toolCall: (knId, name, args) => callTool(ctx, knId, name, args),
1313
+ // Generic MCP method passthrough — covers methods not yet wrapped, so the
1314
+ // surface doesn't have to grow every time the server adds one.
1315
+ callMethod: (knId, method, params) => callMethod(ctx, knId, method, params),
1174
1316
  queryInstanceSubgraph: (knId, args) => queryInstanceSubgraph(ctx, knId, args),
1175
1317
  logicProperties: (knId, args) => getLogicProperties(ctx, knId, args),
1176
1318
  actionInfo: (knId, args) => getActionInfo(ctx, knId, args),
@@ -1490,7 +1632,16 @@ function listResources2(ctx, opts = {}) {
1490
1632
  catalog_id: opts.datasourceId || void 0,
1491
1633
  name: opts.name || void 0,
1492
1634
  category: opts.category || void 0,
1493
- limit: opts.limit && opts.limit > 0 ? opts.limit : void 0
1635
+ status: opts.status || void 0,
1636
+ database: opts.database || void 0,
1637
+ limit: opts.limit && opts.limit > 0 ? opts.limit : void 0,
1638
+ offset: opts.offset,
1639
+ sort: opts.sort,
1640
+ direction: opts.direction,
1641
+ include_extensions: opts.includeExtensions === void 0 ? void 0 : String(opts.includeExtensions),
1642
+ include_extension_keys: opts.includeExtensionKeys || void 0,
1643
+ extension_key: opts.extensionPairs?.map((p) => p.key),
1644
+ extension_value: opts.extensionPairs?.map((p) => p.value)
1494
1645
  }
1495
1646
  });
1496
1647
  }
@@ -1500,18 +1651,103 @@ function getResource(ctx, id) {
1500
1651
  function createResourceRaw(ctx, body) {
1501
1652
  return request(ctx, BASE3, { method: "POST", body });
1502
1653
  }
1503
- function createResource(ctx, opts) {
1654
+ function updateResourceRaw(ctx, id, body) {
1655
+ return request(ctx, `${BASE3}/${encodeURIComponent(id)}`, { method: "PUT", body });
1656
+ }
1657
+ async function updateResource(ctx, id, patch) {
1658
+ const current = firstResource(await getResource(ctx, id));
1659
+ return updateResourceRaw(ctx, id, resourceUpdateBody(id, current, patch));
1660
+ }
1661
+ async function configureResourceIndex(ctx, id, opts) {
1662
+ const current = firstResource(await getResource(ctx, id));
1663
+ const schema = (current.schema_definition ?? []).map((prop) => ({ ...prop }));
1664
+ const indexConfig = {
1665
+ ...current.index_config ?? {},
1666
+ ...opts.buildKeyFields?.length ? { build_key_fields: opts.buildKeyFields } : {},
1667
+ ...opts.embeddingModel ? { default_embedding_model: opts.embeddingModel } : {},
1668
+ ...opts.fulltextAnalyzer ? { default_fulltext_analyzer: opts.fulltextAnalyzer } : {}
1669
+ };
1670
+ for (const field of opts.embeddingFields ?? []) {
1671
+ ensureFeature(
1672
+ schema,
1673
+ field,
1674
+ "vector",
1675
+ opts.embeddingModel ? { embedding_model: opts.embeddingModel } : void 0
1676
+ );
1677
+ }
1678
+ for (const field of opts.fulltextFields ?? []) {
1679
+ ensureFeature(
1680
+ schema,
1681
+ field,
1682
+ "fulltext",
1683
+ opts.fulltextAnalyzer ? { analyzer: opts.fulltextAnalyzer } : void 0
1684
+ );
1685
+ }
1686
+ return updateResourceRaw(
1687
+ ctx,
1688
+ id,
1689
+ resourceUpdateBody(id, current, { schemaDefinition: schema, indexConfig })
1690
+ );
1691
+ }
1692
+ function resourceUpdateBody(id, current, patch) {
1504
1693
  const body = {
1505
- name: opts.name,
1506
- catalog_id: opts.catalogId,
1507
- category: "table",
1508
- source_identifier: opts.sourceIdentifier
1694
+ id,
1695
+ name: patch.name ?? current.name,
1696
+ catalog_id: patch.catalogId ?? current.catalog_id,
1697
+ tags: patch.tags ?? current.tags ?? [],
1698
+ description: patch.description ?? current.description ?? "",
1699
+ category: patch.category ?? current.category,
1700
+ status: patch.status ?? current.status,
1701
+ database: patch.database ?? current.database,
1702
+ source_identifier: patch.sourceIdentifier ?? current.source_identifier,
1703
+ source_metadata: patch.sourceMetadata ?? current.source_metadata,
1704
+ schema_definition: patch.schemaDefinition ?? current.schema_definition,
1705
+ index_config: patch.indexConfig === void 0 ? current.index_config : patch.indexConfig,
1706
+ logic_definition: patch.logicDefinition ?? current.logic_definition
1509
1707
  };
1510
- if (opts.fields && opts.fields.length > 0) body.schema_definition = opts.fields;
1511
- return request(ctx, BASE3, { method: "POST", body });
1708
+ if (patch.extensions !== void 0 || current.extensions !== void 0) {
1709
+ body.extensions = patch.extensions ?? current.extensions;
1710
+ }
1711
+ return body;
1712
+ }
1713
+ function ensureFeature(schema, field, featureType, config) {
1714
+ const prop = schema.find((p) => p.name === field);
1715
+ if (!prop) throw new Error(`resource field '${field}' not found in schema_definition`);
1716
+ const features = [...prop.features ?? []];
1717
+ const existing = features.find(
1718
+ (f) => f.feature_type === featureType && (f.ref_property || field) === field
1719
+ );
1720
+ if (existing) {
1721
+ existing.ref_property = existing.ref_property || field;
1722
+ existing.config = { ...existing.config ?? {}, ...config ?? {} };
1723
+ } else {
1724
+ features.push({
1725
+ name: `${field}_${featureType}`,
1726
+ feature_type: featureType,
1727
+ ref_property: field,
1728
+ is_default: false,
1729
+ is_native: false,
1730
+ ...config ? { config } : {}
1731
+ });
1732
+ }
1733
+ prop.features = features;
1512
1734
  }
1513
- function deleteResource(ctx, id) {
1514
- return request(ctx, `${BASE3}/${encodeURIComponent(id)}`, { method: "DELETE" });
1735
+ function firstResource(result) {
1736
+ if (result && typeof result === "object") {
1737
+ const o = result;
1738
+ if (Array.isArray(o.entries)) return o.entries[0] ?? {};
1739
+ return o;
1740
+ }
1741
+ return {};
1742
+ }
1743
+ function deleteResource(ctx, id, opts = {}) {
1744
+ const ids = Array.isArray(id) ? id : [id];
1745
+ return request(ctx, `${BASE3}/${ids.map(encodeURIComponent).join(",")}`, {
1746
+ method: "DELETE",
1747
+ query: {
1748
+ ignore_missing: opts.ignoreMissing === void 0 ? void 0 : String(opts.ignoreMissing)
1749
+ }
1750
+ });
1515
1751
  }
1516
1752
  async function findResource(ctx, name, opts = {}) {
1517
1753
  const result = await listResources2(ctx, { name, datasourceId: opts.datasourceId });
@@ -2733,7 +2969,6 @@ function knPath(knId, path) {
2733
2969
  return `${BASE4}/${encodeURIComponent(knId)}/${path}`;
2734
2970
  }
2735
2971
  async function uploadBkn(ctx, tarBuffer, opts = {}) {
2736
- applyTls(ctx);
2737
2972
  const url = new URL(`${ctx.baseUrl}${BKNS}`);
2738
2973
  url.searchParams.set("branch", opts.branch ?? "main");
2739
2974
  const form2 = new FormData();
@@ -2744,17 +2979,19 @@ async function uploadBkn(ctx, tarBuffer, opts = {}) {
2744
2979
  );
2745
2980
  const res = await authFetch(
2746
2981
  ctx,
2747
- () => fetch(url, { method: "POST", headers: buildHeaders(ctx), body: form2 })
2982
+ () => tlsFetch(ctx.insecure, url, { method: "POST", headers: buildHeaders(ctx), body: form2 })
2748
2983
  );
2749
2984
  const text = await res.text();
2750
2985
  if (!res.ok) throw new HttpError(res.status, res.statusText, text);
2751
2986
  return text ? JSON.parse(text) : void 0;
2752
2987
  }
2753
2988
  async function downloadBkn(ctx, knId, opts = {}) {
2754
- applyTls(ctx);
2755
2989
  const url = new URL(`${ctx.baseUrl}${BKNS}/${encodeURIComponent(knId)}`);
2756
2990
  url.searchParams.set("branch", opts.branch ?? "main");
2757
- const res = await authFetch(ctx, () => fetch(url, { method: "GET", headers: buildHeaders(ctx) }));
2991
+ const res = await authFetch(
2992
+ ctx,
2993
+ () => tlsFetch(ctx.insecure, url, { method: "GET", headers: buildHeaders(ctx) })
2994
+ );
2758
2995
  if (!res.ok) throw new HttpError(res.status, res.statusText, await res.text());
2759
2996
  return Buffer.from(await res.arrayBuffer());
2760
2997
  }
@@ -2843,10 +3080,7 @@ var BuildMode = z.enum(["batch", "streaming"]);
2843
3080
  var CreateBuildTaskRequest = z.object({
2844
3081
  resource_id: z.string().min(1),
2845
3082
  mode: BuildMode,
2846
- embedding_fields: z.array(z.string()).optional(),
2847
- build_key_fields: z.array(z.string()).optional(),
2848
- embedding_model: z.string().optional(),
2849
- model_dimensions: z.number().int().positive().optional()
3083
+ execute_type: z.enum(["incremental", "full"]).optional()
2850
3084
  });
2851
3085
  var BuildTask = z.object({
2852
3086
  id: z.string(),
@@ -2857,31 +3091,83 @@ var BuildTask = z.object({
2857
3091
  total_count: z.number().optional(),
2858
3092
  synced_count: z.number().optional(),
2859
3093
  vectorized_count: z.number().optional(),
2860
- embedding_fields: z.string().optional(),
2861
- build_key_fields: z.string().optional(),
2862
- embedding_model: z.string().optional(),
2863
- model_dimensions: z.number().optional()
3094
+ index_config: z.unknown().optional(),
3095
+ catalog_id: z.string().optional(),
3096
+ index_health: z.object({
3097
+ embedding: z.string(),
3098
+ fulltext: z.string(),
3099
+ usable: z.boolean()
3100
+ }).passthrough().optional()
2864
3101
  }).passthrough();
2865
3102
  async function createBuildTask(ctx, req) {
2866
3103
  const p = CreateBuildTaskRequest.parse(req);
2867
3104
  const body = {
2868
3105
  resource_id: p.resource_id,
2869
3106
  mode: p.mode,
2870
- ...p.embedding_fields?.length ? { embedding_fields: p.embedding_fields.join(",") } : {},
2871
- ...p.build_key_fields?.length ? { build_key_fields: p.build_key_fields.join(",") } : {},
2872
- ...p.embedding_model ? { embedding_model: p.embedding_model } : {},
2873
- ...p.model_dimensions ? { model_dimensions: p.model_dimensions } : {}
3107
+ ...p.execute_type ? { execute_type: p.execute_type } : {}
2874
3108
  };
2875
3109
  const res = await request(ctx, `${VEGA_BASE}/build-tasks`, { method: "POST", body });
2876
3110
  return BuildTask.parse(res);
2877
3111
  }
3112
+ function listBuildTasks(ctx, opts = {}) {
3113
+ return request(ctx, `${VEGA_BASE}/build-tasks`, {
3114
+ query: {
3115
+ limit: opts.limit,
3116
+ offset: opts.offset,
3117
+ resource_id: opts.resourceId || void 0,
3118
+ catalog_id: opts.catalogId || void 0,
3119
+ status: Array.isArray(opts.status) ? opts.status.join(",") : opts.status || void 0,
3120
+ active: opts.active === void 0 ? void 0 : String(opts.active),
3121
+ mode: opts.mode,
3122
+ order_by: opts.orderBy,
3123
+ order: opts.order
3124
+ }
3125
+ });
3126
+ }
2878
3127
  async function getBuildTask(ctx, taskId) {
2879
3128
  const res = await request(ctx, `${VEGA_BASE}/build-tasks/${encodeURIComponent(taskId)}`);
2880
3129
  return BuildTask.parse(res);
2881
3130
  }
3131
+ function deleteBuildTasks(ctx, ids, opts = {}) {
3132
+ return request(ctx, `${VEGA_BASE}/build-tasks/${ids.map(encodeURIComponent).join(",")}`, {
3133
+ method: "DELETE",
3134
+ query: {
3135
+ ignore_missing: opts.ignoreMissing === void 0 ? void 0 : String(opts.ignoreMissing),
3136
+ delete_active_index: opts.deleteActiveIndex === void 0 ? void 0 : String(opts.deleteActiveIndex)
3137
+ }
3138
+ });
3139
+ }
3140
+ function startBuildTask(ctx, taskId, opts = {}) {
3141
+ return request(ctx, `${VEGA_BASE}/build-tasks/${encodeURIComponent(taskId)}/start`, {
3142
+ method: "POST",
3143
+ body: opts.reset === void 0 ? {} : { reset: opts.reset }
3144
+ });
3145
+ }
3146
+ function stopBuildTask(ctx, taskId) {
3147
+ return request(ctx, `${VEGA_BASE}/build-tasks/${encodeURIComponent(taskId)}/stop`, {
3148
+ method: "POST"
3149
+ });
3150
+ }
3151
+ function runSql(ctx, body) {
3152
+ return request(ctx, `${VEGA_BASE}/resources/query`, { method: "POST", body });
3153
+ }
2882
3154
  async function listCatalogs(ctx, opts = {}) {
2883
3155
  return request(ctx, `${VEGA_BASE}/catalogs`, {
2884
- query: { limit: opts.limit, offset: opts.offset }
3156
+ query: {
3157
+ limit: opts.limit,
3158
+ offset: opts.offset,
3159
+ name: opts.name || void 0,
3160
+ tag: opts.tag || void 0,
3161
+ type: opts.type || void 0,
3162
+ enabled: opts.enabled === void 0 ? void 0 : String(opts.enabled),
3163
+ health_check_status: opts.healthCheckStatus || void 0,
3164
+ include_extensions: opts.includeExtensions === void 0 ? void 0 : String(opts.includeExtensions),
3165
+ include_extension_keys: opts.includeExtensionKeys || void 0,
3166
+ extension_key: opts.extensionPairs?.map((p) => p.key),
3167
+ extension_value: opts.extensionPairs?.map((p) => p.value),
3168
+ sort: opts.sort,
3169
+ direction: opts.direction
3170
+ }
2885
3171
  });
2886
3172
  }
2887
3173
  function getCatalog(ctx, id) {
@@ -2891,18 +3177,49 @@ function createCatalog(ctx, req) {
2891
3177
  return request(ctx, `${VEGA_BASE}/catalogs`, {
2892
3178
  method: "POST",
2893
3179
  body: {
3180
+ ...req.id ? { id: req.id } : {},
2894
3181
  name: req.name,
2895
3182
  connector_type: req.connectorType,
2896
3183
  connector_config: req.connectorConfig,
2897
3184
  ...req.tags ? { tags: req.tags } : {},
2898
3185
  ...req.description ? { description: req.description } : {},
2899
- ...req.enabled !== void 0 ? { enabled: req.enabled } : {}
3186
+ ...req.enabled !== void 0 ? { enabled: req.enabled } : {},
3187
+ ...req.internal !== void 0 ? { internal: req.internal } : {},
3188
+ ...req.extensions ? { extensions: req.extensions } : {}
3189
+ }
3190
+ });
3191
+ }
3192
+ function updateCatalog(ctx, id, req) {
3193
+ return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}`, {
3194
+ method: "PUT",
3195
+ body: {
3196
+ ...req.id ? { id: req.id } : {},
3197
+ ...req.name ? { name: req.name } : {},
3198
+ ...req.connectorType ? { connector_type: req.connectorType } : {},
3199
+ ...req.connectorConfig !== void 0 ? { connector_config: req.connectorConfig } : {},
3200
+ ...req.tags ? { tags: req.tags } : {},
3201
+ ...req.description !== void 0 ? { description: req.description } : {},
3202
+ ...req.enabled !== void 0 ? { enabled: req.enabled } : {},
3203
+ ...req.extensions ? { extensions: req.extensions } : {}
2900
3204
  }
2901
3205
  });
2902
3206
  }
2903
3207
  function enableCatalog(ctx, id) {
2904
3208
  return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/enable`, { method: "POST" });
2905
3209
  }
3210
+ function disableCatalog(ctx, id) {
3211
+ return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/disable`, {
3212
+ method: "POST"
3213
+ });
3214
+ }
3215
+ function deleteCatalog(ctx, id) {
3216
+ return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}`, { method: "DELETE" });
3217
+ }
3218
+ function testCatalogConnection(ctx, id) {
3219
+ return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/test-connection`, {
3220
+ method: "POST"
3221
+ });
3222
+ }
2906
3223
  function discoverCatalog(ctx, id, wait = true) {
2907
3224
  return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/discover`, {
2908
3225
  method: "POST",
@@ -3372,7 +3689,7 @@ async function createFromCatalog(ctx, opts) {
3372
3689
  }
3373
3690
  tablePk[t.name] = res.pk;
3374
3691
  }
3375
- log(`Creating resources for ${targets.length} table(s)...`);
3692
+ log(`Resolving discovered resources for ${targets.length} table(s)...`);
3376
3693
  const viewMap = {};
3377
3694
  for (const t of targets) {
3378
3695
  const found = asArray(
@@ -3382,13 +3699,9 @@ async function createFromCatalog(ctx, opts) {
3382
3699
  if (existingId) {
3383
3700
  viewMap[t.name] = existingId;
3384
3701
  } else {
3385
- const created = await createResource(ctx, {
3386
- name: t.name,
3387
- catalogId: opts.catalogId,
3388
- sourceIdentifier: t.name,
3389
- fields: t.columns.map((c) => ({ name: c.name, type: c.type }))
3390
- });
3391
- viewMap[t.name] = String(created.id ?? "");
3702
+ throw new Error(
3703
+ `Table '${t.name}' has no discovered Vega resource. Run catalog discover and retry.`
3704
+ );
3392
3705
  }
3393
3706
  }
3394
3707
  const knCreated = await createKnowledgeNetwork(ctx, { name: opts.name });
@@ -3420,12 +3733,14 @@ async function createFromCatalog(ctx, opts) {
3420
3733
  log("Submitting build tasks...");
3421
3734
  for (const t of targets) {
3422
3735
  const embedding = opts.embeddingFields?.[t.name];
3736
+ await configureResourceIndex(ctx, viewMap[t.name], {
3737
+ buildKeyFields: [tablePk[t.name]],
3738
+ ...embedding && embedding.length > 0 ? { embeddingFields: embedding } : {},
3739
+ ...opts.embeddingModel ? { embeddingModel: opts.embeddingModel } : {}
3740
+ });
3423
3741
  const task = await createBuildTask(ctx, {
3424
3742
  resource_id: viewMap[t.name],
3425
- mode: "batch",
3426
- build_key_fields: [tablePk[t.name]],
3427
- ...embedding && embedding.length > 0 ? { embedding_fields: embedding } : {},
3428
- ...opts.embeddingModel ? { embedding_model: opts.embeddingModel } : {}
3743
+ mode: "batch"
3429
3744
  });
3430
3745
  builds.push({ table: t.name, taskId: String(task.id ?? "") });
3431
3746
  }
@@ -3603,12 +3918,19 @@ function kn(ctx) {
3603
3918
  const targets = collectIndexTargets(dir);
3604
3919
  const buildTasks = [];
3605
3920
  for (const t of targets) {
3921
+ if (!t.buildKey) {
3922
+ throw new Error(
3923
+ `Object type '${t.objectType}' declares a vector index but no build key; batch Vega builds require resource index_config.build_key_fields.`
3924
+ );
3925
+ }
3926
+ await configureResourceIndex(ctx, t.resourceId, {
3927
+ buildKeyFields: [t.buildKey],
3928
+ embeddingFields: t.embeddingFields,
3929
+ ...t.embeddingModel ?? opts.embeddingModel ? { embeddingModel: t.embeddingModel ?? opts.embeddingModel } : {}
3930
+ });
3606
3931
  const task = await createBuildTask(ctx, {
3607
3932
  resource_id: t.resourceId,
3608
- mode: "batch",
3609
- embedding_fields: t.embeddingFields,
3610
- ...t.buildKey ? { build_key_fields: [t.buildKey] } : {},
3611
- ...t.embeddingModel ?? opts.embeddingModel ? { embedding_model: t.embeddingModel ?? opts.embeddingModel } : {}
3933
+ mode: "batch"
3612
3934
  });
3613
3935
  buildTasks.push({
3614
3936
  objectType: t.objectType,
@@ -3663,10 +3985,9 @@ function deltaContent(chunk) {
3663
3985
  return typeof c === "string" ? c : "";
3664
3986
  }
3665
3987
  async function chatCompletionsStream(ctx, model, messages, onDelta) {
3666
- applyTls(ctx);
3667
3988
  const res = await authFetch(
3668
3989
  ctx,
3669
- () => fetch(`${ctx.baseUrl}${API}/chat/completions`, {
3990
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${API}/chat/completions`, {
3670
3991
  method: "POST",
3671
3992
  headers: {
3672
3993
  ...buildHeaders(ctx),
@@ -3725,6 +4046,23 @@ function deleteModels(ctx, kind, modelIds) {
3725
4046
  function testModel(ctx, kind, body) {
3726
4047
  return request(ctx, `${MANAGER}/${kind}/test`, { method: "POST", body });
3727
4048
  }
4049
+ function setDefaultLlm(ctx, modelId, isDefault = true) {
4050
+ return request(ctx, `${MANAGER}/llm/default/edit`, {
4051
+ method: "POST",
4052
+ body: { model_id: modelId, default: isDefault }
4053
+ });
4054
+ }
4055
+ function setDefaultSmallModel(ctx, modelId, isDefault = true) {
4056
+ return request(ctx, `${MANAGER}/small-model/set-default`, {
4057
+ method: "POST",
4058
+ body: { model_id: modelId, default: isDefault }
4059
+ });
4060
+ }
4061
+ function getDefaultSmallModel(ctx, modelType = "embedding") {
4062
+ return request(ctx, `${MANAGER}/small-model/get_default`, {
4063
+ query: { model_type: modelType }
4064
+ });
4065
+ }
3728
4066
  function rerank(ctx, model, query, documents) {
3729
4067
  return request(ctx, `${API}/small-model/reranker`, {
3730
4068
  method: "POST",
@@ -3743,7 +4081,9 @@ function models(ctx) {
3743
4081
  add: (body) => addModel(ctx, "llm", body),
3744
4082
  edit: (body) => editModel(ctx, "llm", body),
3745
4083
  delete: (modelIds) => deleteModels(ctx, "llm", modelIds),
3746
- test: (body) => testModel(ctx, "llm", body)
4084
+ test: (body) => testModel(ctx, "llm", body),
4085
+ /** Set (or clear) the system default LLM. */
4086
+ setDefault: (modelId, isDefault = true) => setDefaultLlm(ctx, modelId, isDefault)
3747
4087
  },
3748
4088
  small: {
3749
4089
  list: (opts) => listSmallModels(ctx, opts),
@@ -3753,7 +4093,11 @@ function models(ctx) {
3753
4093
  add: (body) => addModel(ctx, "small-model", body),
3754
4094
  edit: (body) => editModel(ctx, "small-model", body),
3755
4095
  delete: (modelIds) => deleteModels(ctx, "small-model", modelIds),
3756
- test: (body) => testModel(ctx, "small-model", body)
4096
+ test: (body) => testModel(ctx, "small-model", body),
4097
+ /** Set (or clear) the system default small model (type inferred from the model). */
4098
+ setDefault: (modelId, isDefault = true) => setDefaultSmallModel(ctx, modelId, isDefault),
4099
+ /** Get the system default small model for a type (default "embedding"). */
4100
+ getDefault: (modelType) => getDefaultSmallModel(ctx, modelType)
3757
4101
  }
3758
4102
  };
3759
4103
  }
@@ -3764,6 +4108,8 @@ function resources(ctx) {
3764
4108
  list: (opts) => listResources2(ctx, opts),
3765
4109
  get: (id) => getResource(ctx, id),
3766
4110
  delete: (id) => deleteResource(ctx, id),
4111
+ update: (id, patch) => updateResource(ctx, id, patch),
4112
+ configureIndex: (id, opts) => configureResourceIndex(ctx, id, opts),
3767
4113
  find: (name, opts) => findResource(ctx, name, opts),
3768
4114
  query: (id, opts) => queryResource(ctx, id, opts)
3769
4115
  };
@@ -3776,7 +4122,6 @@ import { basename as basename2, dirname as dirname3, resolve as resolve5 } from
3776
4122
  // src/api/skills.ts
3777
4123
  var BASE5 = "/api/agent-operator-integration/v1";
3778
4124
  async function registerSkillZip(ctx, bytes, opts = {}) {
3779
- applyTls(ctx);
3780
4125
  const form2 = new FormData();
3781
4126
  form2.set("file_type", "zip");
3782
4127
  form2.set("file", new Blob([bytes]), opts.filename ?? "skill.zip");
@@ -3784,7 +4129,7 @@ async function registerSkillZip(ctx, bytes, opts = {}) {
3784
4129
  if (opts.extendInfo) form2.set("extend_info", JSON.stringify(opts.extendInfo));
3785
4130
  const res = await authFetch(
3786
4131
  ctx,
3787
- () => fetch(`${ctx.baseUrl}${BASE5}/skills`, {
4132
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${BASE5}/skills`, {
3788
4133
  method: "POST",
3789
4134
  headers: buildHeaders(ctx),
3790
4135
  body: form2
@@ -3795,13 +4140,12 @@ async function registerSkillZip(ctx, bytes, opts = {}) {
3795
4140
  return text ? JSON.parse(text) : void 0;
3796
4141
  }
3797
4142
  async function updateSkillPackageZip(ctx, skillId, bytes, filename = "skill.zip") {
3798
- applyTls(ctx);
3799
4143
  const form2 = new FormData();
3800
4144
  form2.set("file_type", "zip");
3801
4145
  form2.set("file", new Blob([bytes]), filename);
3802
4146
  const res = await authFetch(
3803
4147
  ctx,
3804
- () => fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/package`, {
4148
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/package`, {
3805
4149
  method: "PUT",
3806
4150
  headers: buildHeaders(ctx),
3807
4151
  body: form2
@@ -3812,10 +4156,9 @@ async function updateSkillPackageZip(ctx, skillId, bytes, filename = "skill.zip"
3812
4156
  return text ? JSON.parse(text) : void 0;
3813
4157
  }
3814
4158
  async function downloadSkill(ctx, skillId) {
3815
- applyTls(ctx);
3816
4159
  const res = await authFetch(
3817
4160
  ctx,
3818
- () => fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/download`, {
4161
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/download`, {
3819
4162
  headers: buildHeaders(ctx)
3820
4163
  })
3821
4164
  );
@@ -3971,25 +4314,27 @@ import { basename as basename3 } from "path";
3971
4314
  var PATH = "/api/agent-operator-integration/v1/tool-box";
3972
4315
  var IMPEX = "/api/agent-operator-integration/v1/impex";
3973
4316
  async function exportConfig(ctx, id, type = "toolbox") {
3974
- applyTls(ctx);
3975
4317
  const res = await authFetch(
3976
4318
  ctx,
3977
- () => fetch(`${ctx.baseUrl}${IMPEX}/export/${encodeURIComponent(type)}/${encodeURIComponent(id)}`, {
3978
- headers: buildHeaders(ctx)
3979
- })
4319
+ () => tlsFetch(
4320
+ ctx.insecure,
4321
+ `${ctx.baseUrl}${IMPEX}/export/${encodeURIComponent(type)}/${encodeURIComponent(id)}`,
4322
+ {
4323
+ headers: buildHeaders(ctx)
4324
+ }
4325
+ )
3980
4326
  );
3981
4327
  const buf = new Uint8Array(await res.arrayBuffer());
3982
4328
  if (!res.ok) throw new HttpError(res.status, res.statusText, new TextDecoder().decode(buf));
3983
4329
  return buf;
3984
4330
  }
3985
4331
  async function importConfig(ctx, filePath, type = "toolbox") {
3986
- applyTls(ctx);
3987
4332
  const buf = await readFile2(filePath);
3988
4333
  const form2 = new FormData();
3989
4334
  form2.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
3990
4335
  const res = await authFetch(
3991
4336
  ctx,
3992
- () => fetch(`${ctx.baseUrl}${IMPEX}/import/${encodeURIComponent(type)}`, {
4337
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${IMPEX}/import/${encodeURIComponent(type)}`, {
3993
4338
  method: "POST",
3994
4339
  headers: buildHeaders(ctx),
3995
4340
  body: form2
@@ -4000,14 +4345,13 @@ async function importConfig(ctx, filePath, type = "toolbox") {
4000
4345
  return text ? JSON.parse(text) : text;
4001
4346
  }
4002
4347
  async function uploadTool(ctx, boxId, filePath, metadataType = "openapi") {
4003
- applyTls(ctx);
4004
4348
  const buf = await readFile2(filePath);
4005
4349
  const form2 = new FormData();
4006
4350
  form2.append("metadata_type", metadataType);
4007
4351
  form2.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
4008
4352
  const res = await authFetch(
4009
4353
  ctx,
4010
- () => fetch(`${ctx.baseUrl}${PATH}/${encodeURIComponent(boxId)}/tool`, {
4354
+ () => tlsFetch(ctx.insecure, `${ctx.baseUrl}${PATH}/${encodeURIComponent(boxId)}/tool`, {
4011
4355
  method: "POST",
4012
4356
  headers: buildHeaders(ctx),
4013
4357
  body: form2
@@ -4176,7 +4520,7 @@ async function getSpansByConversation(ctx, conversationId, opts = {}) {
4176
4520
  return (spans.hits?.hits ?? []).map((h) => h._source ?? {});
4177
4521
  }
4178
4522
 
4179
- // src/trace-ai/claude-judge.ts
4523
+ // src/bkn-trace/claude-judge.ts
4180
4524
  import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
4181
4525
  var ClaudeJudgeError = class extends Error {
4182
4526
  constructor(message, reason) {
@@ -4258,7 +4602,7 @@ async function judgeJson(prompt, opts = {}) {
4258
4602
  return JSON.parse(extractJsonObject(text));
4259
4603
  }
4260
4604
 
4261
- // src/trace-ai/diagnose.ts
4605
+ // src/bkn-trace/diagnose.ts
4262
4606
  var KIND_MAP = {
4263
4607
  chat: "llm",
4264
4608
  text_completion: "llm",
@@ -4652,7 +4996,7 @@ function renderReportMarkdown(r) {
4652
4996
  return lines.join("\n");
4653
4997
  }
4654
4998
 
4655
- // src/trace-ai/eval-set.ts
4999
+ // src/bkn-trace/eval-set.ts
4656
5000
  function hashId(s) {
4657
5001
  let h = 5381;
4658
5002
  for (let i = 0; i < s.length; i++) h = h * 33 ^ s.charCodeAt(i);
@@ -4916,19 +5260,29 @@ function vega(ctx) {
4916
5260
  catalogs: (opts) => listCatalogs(ctx, opts),
4917
5261
  getCatalog: (id) => getCatalog(ctx, id),
4918
5262
  createCatalog: (req) => createCatalog(ctx, req),
5263
+ updateCatalog: (id, req) => updateCatalog(ctx, id, req),
4919
5264
  enableCatalog: (id) => enableCatalog(ctx, id),
5265
+ disableCatalog: (id) => disableCatalog(ctx, id),
5266
+ deleteCatalog: (id) => deleteCatalog(ctx, id),
5267
+ testCatalogConnection: (id) => testCatalogConnection(ctx, id),
4920
5268
  discoverCatalog: (id, wait = false) => discoverCatalog(ctx, id, wait),
4921
5269
  catalogResources: (id, category) => listCatalogResources(ctx, id, category),
4922
5270
  catalogHealth: (ids) => catalogHealthStatus(ctx, ids),
4923
5271
  connectorTypes: () => listConnectorTypes(ctx),
4924
5272
  connectorType: (type) => getConnectorType(ctx, type),
5273
+ /** Run SQL / OpenSearch DSL directly against a data source. */
5274
+ sql: (body) => runSql(ctx, body),
4925
5275
  /** Build a resource's index. With `wait`, polls until terminal. */
4926
5276
  build: async (req, opts = {}) => {
4927
5277
  const task = await createBuildTask(ctx, req);
4928
5278
  if (!opts.wait) return task;
4929
5279
  return pollBuildTask(ctx, task.id, opts.timeoutMs ?? 3e5, opts.intervalMs ?? 2e3);
4930
5280
  },
4931
- buildStatus: (taskId) => getBuildTask(ctx, taskId)
5281
+ buildStatus: (taskId) => getBuildTask(ctx, taskId),
5282
+ buildTasks: (opts) => listBuildTasks(ctx, opts),
5283
+ deleteBuildTasks: (ids, opts) => deleteBuildTasks(ctx, ids, opts),
5284
+ startBuildTask: (taskId, opts) => startBuildTask(ctx, taskId, opts),
5285
+ stopBuildTask: (taskId) => stopBuildTask(ctx, taskId)
4932
5286
  };
4933
5287
  }
4934
5288
  async function pollBuildTask(ctx, taskId, timeoutMs, intervalMs) {
@@ -4964,7 +5318,6 @@ function resolveUrl(ctx, path) {
4964
5318
  return path.startsWith("http") ? path : `${ctx.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
4965
5319
  }
4966
5320
  async function rawCall(ctx, path, opts = {}) {
4967
- applyTls(ctx);
4968
5321
  const url = resolveUrl(ctx, path);
4969
5322
  const extra = {};
4970
5323
  for (const h of opts.header ?? []) {
@@ -4993,7 +5346,12 @@ async function rawCall(ctx, path, opts = {}) {
4993
5346
  try {
4994
5347
  const res = await authFetch(
4995
5348
  ctx,
4996
- () => fetch(url, { method, headers: headersFor(), body, signal: controller.signal })
5349
+ () => tlsFetch(ctx.insecure, url, {
5350
+ method,
5351
+ headers: headersFor(),
5352
+ body,
5353
+ signal: controller.signal
5354
+ })
4997
5355
  );
4998
5356
  return { status: res.status, statusText: res.statusText, body: await res.text() };
4999
5357
  } finally {
@@ -5001,6 +5359,53 @@ async function rawCall(ctx, path, opts = {}) {
5001
5359
  }
5002
5360
  }
5003
5361
 
5362
+ // src/api/app-keys.ts
5363
+ var ME = "/api/safe/v1/me/api-keys";
5364
+ var ADMIN2 = "/api/safe/v1/admin/api-keys";
5365
+ function listMyApiKeys(ctx) {
5366
+ return request(ctx, ME);
5367
+ }
5368
+ function createMyApiKey(ctx, input) {
5369
+ return request(ctx, ME, {
5370
+ method: "POST",
5371
+ body: {
5372
+ name: input.name,
5373
+ ...input.expiresAt ? { expires_at: input.expiresAt } : {},
5374
+ ...input.neverExpire ? { never_expire: true } : {}
5375
+ }
5376
+ });
5377
+ }
5378
+ async function revokeMyApiKey(ctx, id) {
5379
+ await request(ctx, `${ME}/${encodeURIComponent(id)}`, { method: "DELETE" });
5380
+ }
5381
+ function regenerateMyApiKey(ctx, id) {
5382
+ return request(ctx, `${ME}/${encodeURIComponent(id)}/regenerate`, { method: "POST" });
5383
+ }
5384
+ function listApiKeysAdmin(ctx, ownerId) {
5385
+ return request(ctx, ADMIN2, { query: { owner_id: ownerId || void 0 } });
5386
+ }
5387
+ async function revokeApiKeyAdmin(ctx, id) {
5388
+ await request(ctx, `${ADMIN2}/${encodeURIComponent(id)}`, { method: "DELETE" });
5389
+ }
5390
+
5391
+ // src/resources/app-keys.ts
5392
+ function appKeys(ctx) {
5393
+ return {
5394
+ /** List the caller's own keys (no secrets). */
5395
+ list: () => listMyApiKeys(ctx),
5396
+ /** Issue a key — the result's `key` is the plaintext, shown only once. */
5397
+ create: (input) => createMyApiKey(ctx, input),
5398
+ /** Revoke one of the caller's keys (immediate). */
5399
+ revoke: (id) => revokeMyApiKey(ctx, id),
5400
+ /** Rotate a key in place — new plaintext (shown once); old secret dies now. */
5401
+ regenerate: (id) => regenerateMyApiKey(ctx, id),
5402
+ /** Admin: list all keys, or one owner's (adds `owner_user_id`). */
5403
+ adminList: (ownerId) => listApiKeysAdmin(ctx, ownerId),
5404
+ /** Admin: revoke any key. */
5405
+ adminRevoke: (id) => revokeApiKeyAdmin(ctx, id)
5406
+ };
5407
+ }
5408
+
5004
5409
  // src/client.ts
5005
5410
  function createClient(opts = {}) {
5006
5411
  const ctx = resolveContext(opts);
@@ -5016,6 +5421,7 @@ function createClient(opts = {}) {
5016
5421
  toolboxes: toolboxes(ctx),
5017
5422
  trace: trace(ctx),
5018
5423
  admin: admin(ctx),
5424
+ appKeys: appKeys(ctx),
5019
5425
  vega: vega(ctx),
5020
5426
  call: (path, callOpts) => rawCall(ctx, path, callOpts)
5021
5427
  };
@@ -5024,7 +5430,6 @@ function createClient(opts = {}) {
5024
5430
  // src/resources/auth.ts
5025
5431
  var auth_exports = {};
5026
5432
  __export(auth_exports, {
5027
- attachNoAuth: () => attachNoAuth,
5028
5433
  attachToken: () => attachToken,
5029
5434
  currentToken: () => currentToken,
5030
5435
  currentTokenFresh: () => currentTokenFresh,
@@ -5062,7 +5467,8 @@ function attachToken(baseUrl, accessToken, opts = {}) {
5062
5467
  accessToken,
5063
5468
  refreshToken: opts.refreshToken,
5064
5469
  idToken: opts.idToken,
5065
- tlsInsecure: opts.insecure,
5470
+ // Remember `-k` so a self-signed platform needn't repeat it every command.
5471
+ tlsInsecure: opts.insecure ? true : void 0,
5066
5472
  // Prefer the account the user typed (-u); device tokens carry no username.
5067
5473
  username: opts.username ?? decodeJwt(opts.idToken ?? accessToken)?.preferred_username
5068
5474
  };
@@ -5070,33 +5476,40 @@ function attachToken(baseUrl, accessToken, opts = {}) {
5070
5476
  setActivePlatform(url);
5071
5477
  return { baseUrl: url, userId, username: usernameOf(token) };
5072
5478
  }
5073
- function attachNoAuth(baseUrl, opts = {}) {
5074
- const url = normalize(baseUrl);
5075
- writeToken(url, { baseUrl: url, accessToken: "", noAuth: true, tlsInsecure: opts.insecure });
5076
- setActivePlatform(url);
5077
- return { baseUrl: url, noAuth: true };
5479
+ function targetUser(baseUrl, userOrName) {
5480
+ if (!userOrName) return activeUserId(baseUrl);
5481
+ const id = findUserId(baseUrl, userOrName);
5482
+ if (!id) {
5483
+ const known = usersOfPlatform(baseUrl).map((u) => u.username ?? u.userId).join(", ");
5484
+ throw new InputError(
5485
+ `No saved user '${userOrName}' on ${baseUrl}. Saved: ${known || "(none)"}.`
5486
+ );
5487
+ }
5488
+ return id;
5078
5489
  }
5079
- function status() {
5490
+ function status(opts = {}) {
5080
5491
  const baseUrl = activePlatform();
5081
5492
  if (!baseUrl) return { hasToken: false };
5082
- const token = readToken(baseUrl);
5493
+ const userId = targetUser(baseUrl, opts.user);
5494
+ const token = readToken(baseUrl, userId);
5083
5495
  return {
5084
5496
  baseUrl,
5085
- userId: activeUserId(baseUrl),
5497
+ userId,
5086
5498
  hasToken: token !== void 0,
5087
5499
  username: usernameOf(token),
5088
5500
  expired: token ? isExpired(decodeJwt(token.accessToken)) : void 0
5089
5501
  };
5090
5502
  }
5091
- function currentToken() {
5503
+ function currentToken(opts = {}) {
5092
5504
  const baseUrl = activePlatform();
5093
- const token = baseUrl ? readToken(baseUrl) : void 0;
5505
+ const token = baseUrl ? readToken(baseUrl, targetUser(baseUrl, opts.user)) : void 0;
5094
5506
  if (!token) throw new InputError("Not logged in. Run `openbkn auth login <url> --token <t>`.");
5095
5507
  return token.accessToken;
5096
5508
  }
5097
- async function currentTokenFresh() {
5509
+ async function currentTokenFresh(opts = {}) {
5098
5510
  const baseUrl = activePlatform();
5099
- const token = baseUrl ? readToken(baseUrl) : void 0;
5511
+ const userId = baseUrl ? targetUser(baseUrl, opts.user) : void 0;
5512
+ const token = baseUrl ? readToken(baseUrl, userId) : void 0;
5100
5513
  if (!baseUrl || !token) {
5101
5514
  throw new InputError("Not logged in. Run `openbkn auth login <url> --token <t>`.");
5102
5515
  }
@@ -5105,22 +5518,27 @@ async function currentTokenFresh() {
5105
5518
  const needsRefresh = decodable ? isExpired(claims) : true;
5106
5519
  if (token.refreshToken && needsRefresh) {
5107
5520
  try {
5108
- const t = await refreshAccessToken(baseUrl, token.refreshToken);
5109
- writeToken(baseUrl, {
5110
- ...token,
5111
- accessToken: t.accessToken,
5112
- refreshToken: t.refreshToken ?? token.refreshToken,
5113
- idToken: t.idToken ?? token.idToken
5114
- });
5521
+ const t = await refreshAccessToken(baseUrl, token.refreshToken, void 0, opts.insecure);
5522
+ writeToken(
5523
+ baseUrl,
5524
+ {
5525
+ ...token,
5526
+ accessToken: t.accessToken,
5527
+ refreshToken: t.refreshToken ?? token.refreshToken,
5528
+ idToken: t.idToken ?? token.idToken
5529
+ },
5530
+ { setActive: !opts.user }
5531
+ );
5115
5532
  return t.accessToken;
5116
5533
  } catch {
5117
5534
  }
5118
5535
  }
5119
5536
  return token.accessToken;
5120
5537
  }
5121
- function whoami() {
5538
+ function whoami(opts = {}) {
5122
5539
  const baseUrl = activePlatform();
5123
- const token = baseUrl ? readToken(baseUrl) : void 0;
5540
+ const userId = baseUrl ? targetUser(baseUrl, opts.user) : void 0;
5541
+ const token = baseUrl ? readToken(baseUrl, userId) : void 0;
5124
5542
  const claims = decodeJwt(token?.idToken ?? "") ?? decodeJwt(token?.accessToken ?? "");
5125
5543
  if (!claims) {
5126
5544
  throw new InputError(
@@ -5130,7 +5548,7 @@ function whoami() {
5130
5548
  return {
5131
5549
  ...claims,
5132
5550
  baseUrl: baseUrl ?? void 0,
5133
- userId: baseUrl ? activeUserId(baseUrl) : void 0,
5551
+ userId,
5134
5552
  username: usernameOf(token)
5135
5553
  };
5136
5554
  }
@@ -5194,7 +5612,6 @@ export {
5194
5612
  formatError,
5195
5613
  isHeadless,
5196
5614
  openBrowser,
5197
- fetchAuthStatus,
5198
5615
  deviceLogin,
5199
5616
  credentialDeviceLogin,
5200
5617
  request,
@@ -5209,6 +5626,7 @@ export {
5209
5626
  writePlatformConfig,
5210
5627
  resolveContext,
5211
5628
  getUserSafe,
5629
+ changePasswordSafe,
5212
5630
  admin,
5213
5631
  agents,
5214
5632
  context,
@@ -5225,7 +5643,6 @@ export {
5225
5643
  vega,
5226
5644
  createClient,
5227
5645
  attachToken,
5228
- attachNoAuth,
5229
5646
  status,
5230
5647
  currentToken,
5231
5648
  currentTokenFresh,
@@ -5238,4 +5655,4 @@ export {
5238
5655
  exportCreds,
5239
5656
  auth_exports
5240
5657
  };
5241
- //# sourceMappingURL=chunk-BFE4SU3V.js.map
5658
+ //# sourceMappingURL=chunk-NC6DZ2AU.js.map