@openbkn/bkn-sdk 0.1.1-alpha.8 → 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.
package/dist/cli.js CHANGED
@@ -6,8 +6,8 @@ import {
6
6
  HttpError,
7
7
  InputError,
8
8
  activePlatform,
9
- attachNoAuth,
10
9
  attachToken,
10
+ changePasswordSafe,
11
11
  createClient,
12
12
  credentialDeviceLogin,
13
13
  currentToken,
@@ -16,7 +16,6 @@ import {
16
16
  deletePlatform,
17
17
  deviceLogin,
18
18
  exportCreds,
19
- fetchAuthStatus,
20
19
  formatError,
21
20
  getUserSafe,
22
21
  isHeadless,
@@ -28,7 +27,6 @@ import {
28
27
  rawCall,
29
28
  readPlatformConfig,
30
29
  renderReportMarkdown,
31
- request,
32
30
  resolveContext,
33
31
  setActivePlatform,
34
32
  status,
@@ -37,15 +35,15 @@ import {
37
35
  use,
38
36
  whoami,
39
37
  writePlatformConfig
40
- } from "./chunk-DTU5DGJW.js";
38
+ } from "./chunk-NC6DZ2AU.js";
41
39
 
42
40
  // src/cli.ts
43
- import { Command as Command16 } from "commander";
41
+ import { Command as Command17 } from "commander";
44
42
 
45
43
  // package.json
46
44
  var package_default = {
47
45
  name: "@openbkn/bkn-sdk",
48
- version: "0.1.1-alpha.8",
46
+ version: "0.1.1",
49
47
  description: "Unified TypeScript SDK + CLI for the BKN (Business Knowledge Network) platform.",
50
48
  type: "module",
51
49
  license: "Apache-2.0",
@@ -63,7 +61,7 @@ var package_default = {
63
61
  import: "./dist/index.js"
64
62
  }
65
63
  },
66
- files: ["dist", "README.md", "README.zh.md", "LICENSE"],
64
+ files: ["dist", "!dist/**/*.map", "README.md", "README.zh.md", "LICENSE", "NOTICE"],
67
65
  publishConfig: {
68
66
  access: "public"
69
67
  },
@@ -76,7 +74,8 @@ var package_default = {
76
74
  build: "tsup",
77
75
  dev: "tsup --watch",
78
76
  typecheck: "tsc --noEmit",
79
- lint: "biome check . && tsc --noEmit",
77
+ "check:deps": "node scripts/check-no-self-dep.mjs",
78
+ lint: "npm run check:deps && biome check . && tsc --noEmit",
80
79
  format: "biome format --write .",
81
80
  test: "vitest run",
82
81
  "test:cover": "vitest run --coverage",
@@ -85,12 +84,12 @@ var package_default = {
85
84
  },
86
85
  dependencies: {
87
86
  "@clack/prompts": "^0.9.1",
88
- "@openbkn/bkn-sdk": "^0.1.1-alpha.3",
89
87
  chalk: "^5.4.1",
90
88
  commander: "^13.1.0",
91
89
  "csv-parse": "^6.2.1",
92
90
  "js-yaml": "^4.2.0",
93
91
  jszip: "^3.10.1",
92
+ undici: "^8.7.0",
94
93
  zod: "^3.24.1"
95
94
  },
96
95
  devDependencies: {
@@ -104,6 +103,7 @@ var package_default = {
104
103
  };
105
104
 
106
105
  // src/commands/admin.ts
106
+ import { readFileSync as readFileSync2 } from "fs";
107
107
  import { Command as Command2 } from "commander";
108
108
 
109
109
  // src/help/grouped-help.ts
@@ -215,7 +215,8 @@ var ROW_ENVELOPES = [
215
215
  "users",
216
216
  "roles",
217
217
  "departments",
218
- "members"
218
+ "members",
219
+ "keys"
219
220
  ];
220
221
  function toRows(value) {
221
222
  const isRowArray = (v) => Array.isArray(v) && v.length > 0 && v.every((x) => x !== null && typeof x === "object" && !Array.isArray(x));
@@ -300,6 +301,25 @@ function stringifyCell(v) {
300
301
  return s.length > CELL_MAX ? `${s.slice(0, CELL_MAX - 1)}\u2026` : s;
301
302
  }
302
303
 
304
+ // src/utils/prompt.ts
305
+ import { createInterface } from "readline";
306
+ function promptLine(query, hidden = false) {
307
+ return new Promise((resolve2) => {
308
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
309
+ if (hidden) {
310
+ const mutable = rl;
311
+ mutable._writeToOutput = (s) => {
312
+ if (s.startsWith(query)) process.stdout.write(query);
313
+ };
314
+ }
315
+ rl.question(query, (answer) => {
316
+ rl.close();
317
+ if (hidden) process.stdout.write("\n");
318
+ resolve2(answer.trim());
319
+ });
320
+ });
321
+ }
322
+
303
323
  // src/commands/_shared.ts
304
324
  import { readFileSync } from "fs";
305
325
  function clientFrom(cmd) {
@@ -331,27 +351,7 @@ function readBody(opts) {
331
351
  }
332
352
 
333
353
  // src/commands/auth.ts
334
- import { createInterface } from "readline";
335
354
  import { Command } from "commander";
336
-
337
- // src/api/eacp-crypto.ts
338
- import {
339
- constants,
340
- createPrivateKey,
341
- createPublicKey,
342
- publicEncrypt
343
- } from "crypto";
344
-
345
- // src/api/admin.ts
346
- async function changePasswordSafe(ctx, account, oldPassword, newPassword) {
347
- await request(ctx, "/api/safe/v1/auth/change-password", {
348
- method: "POST",
349
- body: { account, old_password: oldPassword, new_password: newPassword }
350
- });
351
- return { ok: true };
352
- }
353
-
354
- // src/commands/auth.ts
355
355
  async function resolveAccount(baseUrl, accessToken, insecure, idToken) {
356
356
  const sub = decodeJwt(idToken ?? accessToken)?.sub;
357
357
  if (!sub) return void 0;
@@ -365,22 +365,6 @@ async function resolveAccount(baseUrl, accessToken, insecure, idToken) {
365
365
  return void 0;
366
366
  }
367
367
  }
368
- function promptLine(query, hidden = false) {
369
- return new Promise((resolve2) => {
370
- const rl = createInterface({ input: process.stdin, output: process.stdout });
371
- if (hidden) {
372
- const mutable = rl;
373
- mutable._writeToOutput = (s) => {
374
- if (s.startsWith(query)) process.stdout.write(query);
375
- };
376
- }
377
- rl.question(query, (answer) => {
378
- rl.close();
379
- if (hidden) process.stdout.write("\n");
380
- resolve2(answer.trim());
381
- });
382
- });
383
- }
384
368
  function renderSessions(items) {
385
369
  const byPlatform = /* @__PURE__ */ new Map();
386
370
  for (const it of items) {
@@ -396,25 +380,17 @@ function renderSessions(items) {
396
380
  return lines.join("\n") || "(no saved sessions)";
397
381
  }
398
382
  function registerAuthLeaves(cmd) {
399
- cmd.command("login <url>").description("Log in to a platform (attach a token, or browser/password OAuth)").option("-u, --username <name>", "username for password signin").option("-p, --password <pwd>", "password for password signin").option("--token <token>", "provide a token directly (CI / headless)").option("--client-id <id>", "use a fixed OAuth2 client id (skip dynamic registration)").option("--client-secret <secret>", "OAuth2 client secret (omit for public/PKCE)").option(
400
- "--port <n>",
401
- "loopback redirect port for the auth_code flow",
402
- (v) => Number.parseInt(v, 10)
403
- ).option("--device", "headless device-code login (RFC 8628) \u2014 no callback server, no password").option("--audience <aud>", "device-code token audience", "bkn-safe").option(
383
+ cmd.command("login <url>").description("Log in to a platform (attach a token, or browser/password OAuth)").option("-u, --username <name>", "username for password signin").option("-p, --password <pwd>", "password for password signin").option("--token <token>", "provide a token directly (CI / headless)").option("--client-id <id>", "OAuth2 client id to authenticate as").option("--device", "headless device-code login (RFC 8628) \u2014 no callback server, no password").option("--audience <aud>", "device-code token audience", "bkn-safe").option(
404
384
  "--timeout <s>",
405
385
  "device-login wait before timing out",
406
386
  (v) => Number.parseInt(v, 10),
407
387
  120
408
- ).option("--no-browser", "(legacy) print the URL instead of opening a browser").option("--product <name>", "(legacy) ISF OAuth product query").option("--signin-public-key-file <path>", "(legacy) RSA public key for ISF /oauth2/signin").option("--no-auth", "register the platform with no authentication (no bkn-safe)").action(async (url, opts, cmd2) => {
388
+ ).option("--no-browser", "print the URL instead of opening a browser").action(async (url, opts, cmd2) => {
409
389
  const g = cmd2.optsWithGlobals();
410
- if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
411
390
  const out = outputOptions(cmd2);
412
391
  const report = (r) => {
413
392
  if (out.json || out.compact) {
414
393
  printJson({ loggedIn: true, ...r }, out);
415
- } else if (r.noAuth) {
416
- process.stdout.write(`Registered ${r.baseUrl ?? url} (no authentication)
417
- `);
418
394
  } else {
419
395
  process.stdout.write(`Logged in to ${r.baseUrl ?? url} as ${r.username ?? r.userId}
420
396
  `);
@@ -425,19 +401,6 @@ function registerAuthLeaves(cmd) {
425
401
  report(attachToken(url, token, { insecure: g.insecure }));
426
402
  return;
427
403
  }
428
- if (opts.auth === false) {
429
- report(attachNoAuth(url, { insecure: g.insecure }));
430
- return;
431
- }
432
- const authStatus = await fetchAuthStatus(url);
433
- if (authStatus && !authStatus.enabled) {
434
- process.stderr.write(
435
- `Platform auth is disabled (stack: ${authStatus.stack ?? "none"}) \u2014 registering without auth.
436
- `
437
- );
438
- report(attachNoAuth(url, { insecure: g.insecure }));
439
- return;
440
- }
441
404
  let tokens;
442
405
  let account;
443
406
  try {
@@ -448,7 +411,8 @@ function registerAuthLeaves(cmd) {
448
411
  tokens = await credentialDeviceLogin(url, username, password, {
449
412
  clientId: opts.clientId,
450
413
  audience: opts.audience,
451
- timeoutMs: opts.timeout * 1e3
414
+ timeoutMs: opts.timeout * 1e3,
415
+ insecure: g.insecure
452
416
  });
453
417
  } else {
454
418
  const headless = isHeadless();
@@ -457,6 +421,7 @@ function registerAuthLeaves(cmd) {
457
421
  clientId: opts.clientId,
458
422
  audience: opts.audience,
459
423
  timeoutMs: opts.timeout * 1e3,
424
+ insecure: g.insecure,
460
425
  onPrompt: ({ userCode, verificationUri, verificationUriComplete }) => {
461
426
  const target = verificationUriComplete ?? verificationUri;
462
427
  process.stderr.write(
@@ -475,9 +440,9 @@ User code: ${userCode}
475
440
  }
476
441
  } catch (e) {
477
442
  if (e instanceof Error && /Device auth failed \(404\)/.test(e.message)) {
478
- process.stderr.write("No auth endpoint found \u2014 registering platform without auth.\n");
479
- report(attachNoAuth(url, { insecure: g.insecure }));
480
- return;
443
+ throw new InputError(
444
+ `No auth endpoint on ${url} \u2014 the platform has no bkn-safe auth stack. Deploy bkn-safe, or log in elsewhere and pass the token with \`--token\`.`
445
+ );
481
446
  }
482
447
  throw e;
483
448
  }
@@ -493,29 +458,29 @@ User code: ${userCode}
493
458
  attachToken(url, tokens.accessToken, {
494
459
  refreshToken: tokens.refreshToken,
495
460
  idToken: tokens.idToken,
496
- insecure: g.insecure,
497
- username: account
461
+ username: account,
462
+ insecure: g.insecure
498
463
  })
499
464
  );
500
465
  });
501
- cmd.command("status").description("Show base URL and whether a token is configured").action((_opts, cmd2) => printJson(status(), outputOptions(cmd2)));
466
+ cmd.command("status").description("Show base URL and whether a token is configured").action(
467
+ (_opts, cmd2) => printJson(status({ user: cmd2.optsWithGlobals().user }), outputOptions(cmd2))
468
+ );
502
469
  cmd.command("token").description("Print the current access token, refreshing it if expired (keep secret)").option("--no-refresh", "print the stored token as-is, without refreshing").action(async (opts, cmd2) => {
503
470
  const g = cmd2.optsWithGlobals();
504
- if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
505
- const token = opts.refresh === false ? currentToken() : await currentTokenFresh();
471
+ const token = opts.refresh === false ? currentToken({ user: g.user }) : await currentTokenFresh({ insecure: g.insecure, user: g.user });
506
472
  process.stdout.write(`${token}
507
473
  `);
508
474
  });
509
475
  cmd.command("whoami [url]").description("Show current user identity (from the token)").option("--no-lookup", "skip the backend identity fallback (eacp/user/get)").action(async (_url, opts, cmd2) => {
510
476
  const g = cmd2.optsWithGlobals();
511
- if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
512
- const me = whoami();
477
+ const me = whoami({ user: g.user });
513
478
  if (opts.lookup !== false && me.baseUrl && me.sub) {
514
479
  try {
515
480
  const u = await getUserSafe(
516
481
  {
517
482
  baseUrl: me.baseUrl,
518
- token: currentToken(),
483
+ token: currentToken({ user: g.user }),
519
484
  businessDomain: DEFAULT_BUSINESS_DOMAIN,
520
485
  insecure: Boolean(g.insecure)
521
486
  },
@@ -580,9 +545,8 @@ User code: ${userCode}
580
545
  cmd.command("export").description("Export the active session's tokens (for a headless host)").action((_opts, cmd2) => {
581
546
  printJson(exportCreds(), outputOptions(cmd2));
582
547
  });
583
- cmd.command("change-password [url]").description("Change your account password (bkn-safe self-service; no browser)").option("-a, --account <name>", "account / login name (the login column, e.g. admin)").option("--old-password <pwd>", "current password").option("--new-password <pwd>", "new password").option("--public-key-file <path>", "(legacy) RSA public key for ISF password encryption").action(async (url, opts, cmd2) => {
548
+ cmd.command("change-password [url]").description("Change your account password (bkn-safe self-service; no browser)").option("-a, --account <name>", "account / login name (the login column, e.g. admin)").option("--old-password <pwd>", "current password").option("--new-password <pwd>", "new password").action(async (url, opts, cmd2) => {
584
549
  const g = cmd2.optsWithGlobals();
585
- if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
586
550
  const ctx = resolveContext({
587
551
  baseUrl: url ?? g.baseUrl,
588
552
  token: g.token,
@@ -623,10 +587,14 @@ function authCommand() {
623
587
  // src/commands/admin.ts
624
588
  var int = (v) => Number.parseInt(v, 10);
625
589
  var DEFAULT_RESET_PASSWORD = "openbkn";
590
+ async function importLicenseFile(cmd, file, receipt) {
591
+ const text = readFileSync2(file, "utf8");
592
+ const res = await clientFrom(cmd).admin.licenseImport(text, { receipt });
593
+ printJson(res, outputOptions(cmd));
594
+ if ("stored" in res && res.stored) process.exitCode = 1;
595
+ }
626
596
  function adminCommand() {
627
- const admin = new Command2("admin").description(
628
- "Operator CLI (kweaver-admin): org, user, role, models, audit"
629
- );
597
+ const admin = new Command2("admin").description("Operator CLI: org, user, role, models, audit");
630
598
  registerAuthLeaves(admin.command("auth").description("Operator authentication"));
631
599
  const org = admin.command("org").description("Departments and org structure");
632
600
  org.command("list").description("List departments").option("--role <r>", "role qualifier", "super_admin").option("--name <s>", "filter by name").option("--limit <n>", "page size", int, 100).option("--offset <n>", "page offset", int, 0).action(async (opts, cmd) => {
@@ -755,15 +723,19 @@ function adminCommand() {
755
723
  user.command("delete <id>").description("Delete a user").action(async (id, _opts, cmd) => {
756
724
  printJson(await clientFrom(cmd).admin.userDelete(id), outputOptions(cmd));
757
725
  });
758
- user.command("reset-password [id]").description("Reset a user's password (defaults to the platform initial password)").option("--id <userId>", "explicit user UUID (alt to the positional id)").option("--user <account>", "resolve the user by account / login name").option("--password <s>", "the new password (default: platform initial 'openbkn')").option("--new-password <s>", "the new password (alias of --password)").option("-y, --yes", "skip confirmation").action(async (id, opts, cmd) => {
726
+ user.command("reset-password [id]").description("Reset a user's password (defaults to the platform initial password)").option("--id <userId>", "explicit user UUID (alt to the positional id)").option("--user <account>", "resolve the user by account / login name").option("--password <s>", "the new password (default: platform initial 'openbkn')").option("--new-password <s>", "the new password (alias of --password)").option("--prompt-password", "type the new password interactively (input hidden)").option("-y, --yes", "skip confirmation").action(async (id, opts, cmd) => {
759
727
  const userId = id ?? opts.id ?? opts.user;
760
728
  if (!userId) throw new Error("Provide a user id (positional or --id).");
761
- const pwd = opts.password ?? opts.newPassword ?? DEFAULT_RESET_PASSWORD;
729
+ let explicit = opts.password ?? opts.newPassword;
730
+ if (!explicit && opts.promptPassword) {
731
+ explicit = await promptLine("New password: ", true);
732
+ if (!explicit) throw new InputError("No password entered.");
733
+ }
734
+ const pwd = explicit ?? DEFAULT_RESET_PASSWORD;
762
735
  const r = await clientFrom(cmd).admin.userResetPassword(userId, pwd);
763
736
  const out = outputOptions(cmd);
764
737
  if (out.json || out.compact) printJson(r, out);
765
- else if (opts.password || opts.newPassword)
766
- process.stdout.write(`Password reset for ${userId}.
738
+ else if (explicit) process.stdout.write(`Password reset for ${userId}.
767
739
  `);
768
740
  else
769
741
  process.stdout.write(
@@ -906,6 +878,27 @@ function adminCommand() {
906
878
  );
907
879
  });
908
880
  }
881
+ const license = admin.command("license").description("Cluster license management");
882
+ license.command("show").description("Current license detail (state/edition/expiry/features)").action(async (_opts, cmd) => {
883
+ printJson(await clientFrom(cmd).admin.licenseGet(), outputOptions(cmd));
884
+ });
885
+ license.command("import <file>").description("Import a .lic license file (online deployments auto-activate)").action(async (file, _opts, cmd) => {
886
+ await importLicenseFile(cmd, file, false);
887
+ });
888
+ license.command("receipt <file>").description("Import an offline activation receipt (.lic from the license portal)").action(async (file, _opts, cmd) => {
889
+ await importLicenseFile(cmd, file, true);
890
+ });
891
+ license.command("activate").description("Report the installed license to the issuer (online deployments)").action(async (_opts, cmd) => {
892
+ printJson(await clientFrom(cmd).admin.licenseActivate(), outputOptions(cmd));
893
+ });
894
+ license.command("remove").description("Remove the installed license (back to unactivated)").action(async (_opts, cmd) => {
895
+ printJson(await clientFrom(cmd).admin.licenseRemove(), outputOptions(cmd));
896
+ });
897
+ license.command("fingerprint").description(
898
+ "This cluster's machine code (paste into the license portal for offline activation)"
899
+ ).action(async (_opts, cmd) => {
900
+ printJson(await clientFrom(cmd).admin.licenseFingerprint(), outputOptions(cmd));
901
+ });
909
902
  admin.command("audit").description("Audit log queries").command("list").description("List login audit events").option("--user <name>", "filter by user").option("--start <time>", "start time").option("--end <time>", "end time").option("--page <n>", "page", int, 1).option("--size <n>", "page size", int, 30).action(async (opts, cmd) => {
910
903
  printJson(
911
904
  await clientFrom(cmd).admin.auditList({
@@ -966,7 +959,14 @@ function adminCommand() {
966
959
  import { Command as Command3 } from "commander";
967
960
  var int2 = (v) => Number.parseInt(v, 10);
968
961
  function agentCommand() {
969
- const cmd = new Command3("agent").description("Agent CRUD, chat, sessions, publish");
962
+ const cmd = new Command3("agent").description(
963
+ "[DEPRECATED] Decision Agent \u2014 CRUD, chat, sessions, publish (being phased out)"
964
+ );
965
+ cmd.hook("preAction", () => {
966
+ process.stderr.write(
967
+ "\u26A0\uFE0F `openbkn agent` is deprecated and may be removed in a future release.\n"
968
+ );
969
+ });
970
970
  cmd.command("list").description("List published agents").option("--name <s>", "filter by name").option("--limit <n>", "page size", int2, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int2, 0).option("--category-id <id>", "filter by category").action(async (opts, cmd2) => {
971
971
  const data = await clientFrom(cmd2).agents.list({
972
972
  name: opts.name,
@@ -1068,11 +1068,78 @@ function agentCommand() {
1068
1068
  return group(cmd, "DECISION AGENT");
1069
1069
  }
1070
1070
 
1071
- // src/commands/bkn.ts
1071
+ // src/commands/appkey.ts
1072
1072
  import { Command as Command4 } from "commander";
1073
+ var int3 = (v) => Number.parseInt(v, 10);
1074
+ var DAY_MS = 864e5;
1075
+ function printNewKey(created, out) {
1076
+ if (out.json || out.compact) {
1077
+ printJson(created, out);
1078
+ return;
1079
+ }
1080
+ process.stderr.write(
1081
+ "\u26A0\uFE0F Copy this key now \u2014 the plaintext is shown only once and cannot be retrieved again.\n\n"
1082
+ );
1083
+ const fields = [
1084
+ ["key", created.key],
1085
+ ["name", created.name],
1086
+ ["id", `${created.id} (use this to revoke/regenerate)`],
1087
+ ["key_id", created.key_id],
1088
+ ["expires_at", created.expires_at ?? "never"]
1089
+ ];
1090
+ const w = Math.max(...fields.map(([k]) => k.length));
1091
+ for (const [k, v] of fields) process.stdout.write(` ${k.padEnd(w)} ${v}
1092
+ `);
1093
+ }
1094
+ function appkeyCommand() {
1095
+ const appkey = new Command4("appkey").description(
1096
+ "AppKeys \u2014 user-issued long-lived credentials (bak_) for the Context Loader"
1097
+ );
1098
+ appkey.command("list").description("List your own AppKeys (no secrets)").action(async (_opts, cmd) => {
1099
+ printJson(await clientFrom(cmd).appKeys.list(), outputOptions(cmd));
1100
+ });
1101
+ appkey.command("create").description("Issue an AppKey \u2014 the plaintext key is shown ONCE, on create").requiredOption("--name <s>", "display name (to tell keys apart)").option("--expires-at <rfc3339>", "expiry as RFC3339 (e.g. 2027-01-01T00:00:00Z)").option("--expire-days <n>", "expiry in N days from now (alternative to --expires-at)", int3).option("--never-expire", "never expire (wins over --expires-at/--expire-days)").action(async (opts, cmd) => {
1102
+ let expiresAt = opts.expiresAt;
1103
+ if (opts.expireDays !== void 0) {
1104
+ if (!Number.isFinite(opts.expireDays) || opts.expireDays <= 0) {
1105
+ throw new InputError("--expire-days must be a positive integer.");
1106
+ }
1107
+ if (expiresAt) {
1108
+ throw new InputError("Use either --expires-at or --expire-days, not both.");
1109
+ }
1110
+ expiresAt = new Date(Date.now() + opts.expireDays * DAY_MS).toISOString();
1111
+ }
1112
+ const created = await clientFrom(cmd).appKeys.create({
1113
+ name: opts.name,
1114
+ expiresAt,
1115
+ neverExpire: Boolean(opts.neverExpire)
1116
+ });
1117
+ printNewKey(created, outputOptions(cmd));
1118
+ });
1119
+ appkey.command("regenerate <id>").alias("rotate").description("Rotate a key in place \u2014 mints a new plaintext; the old bak_ dies immediately").action(async (id, _opts, cmd) => {
1120
+ const created = await clientFrom(cmd).appKeys.regenerate(id);
1121
+ printNewKey(created, outputOptions(cmd));
1122
+ });
1123
+ appkey.command("revoke <id>").alias("delete").alias("rm").description("Revoke one of your AppKeys (immediate; use the `id`, not `key_id`)").action(async (id, _opts, cmd) => {
1124
+ await clientFrom(cmd).appKeys.revoke(id);
1125
+ printJson({ revoked: id }, outputOptions(cmd));
1126
+ });
1127
+ const admin = appkey.command("admin").description("Admin governance over all AppKeys");
1128
+ admin.command("list").description("List all AppKeys, or one owner's (adds owner_user_id)").option("--owner-id <id>", "filter to a single owner").action(async (opts, cmd) => {
1129
+ printJson(await clientFrom(cmd).appKeys.adminList(opts.ownerId), outputOptions(cmd));
1130
+ });
1131
+ admin.command("revoke <id>").alias("delete").alias("rm").description("Revoke any AppKey by id").action(async (id, _opts, cmd) => {
1132
+ await clientFrom(cmd).appKeys.adminRevoke(id);
1133
+ printJson({ revoked: id }, outputOptions(cmd));
1134
+ });
1135
+ return group(appkey, "AUTHENTICATION & CONFIG");
1136
+ }
1137
+
1138
+ // src/commands/bkn.ts
1139
+ import { Command as Command5 } from "commander";
1073
1140
 
1074
1141
  // src/utils/bkn-validate.ts
1075
- import { existsSync, readFileSync as readFileSync2, readdirSync, statSync } from "fs";
1142
+ import { existsSync, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
1076
1143
  import { join, resolve } from "path";
1077
1144
  var BKN_OBJECT_NAME_MAX_LENGTH = 40;
1078
1145
  function parseFrontmatter(text) {
@@ -1125,7 +1192,7 @@ function validateBknDirectory(dirPath) {
1125
1192
  if (!existsSync(networkPath)) {
1126
1193
  errors.push("Missing network.bkn at the BKN root.");
1127
1194
  } else {
1128
- const fm = parseFrontmatter(readFileSync2(networkPath, "utf8"));
1195
+ const fm = parseFrontmatter(readFileSync3(networkPath, "utf8"));
1129
1196
  if (!fm) errors.push("network.bkn has no frontmatter block.");
1130
1197
  else {
1131
1198
  if (fm.type !== "knowledge_network")
@@ -1137,7 +1204,7 @@ function validateBknDirectory(dirPath) {
1137
1204
  const otIds = /* @__PURE__ */ new Set();
1138
1205
  const otFiles = bknFiles(join(dir, "object_types"));
1139
1206
  for (const file of otFiles) {
1140
- const fm = parseFrontmatter(readFileSync2(file, "utf8"));
1207
+ const fm = parseFrontmatter(readFileSync3(file, "utf8"));
1141
1208
  const rel = file.slice(dir.length + 1);
1142
1209
  if (!fm || fm.type !== "object_type") {
1143
1210
  errors.push(`${rel}: not a valid object_type (missing/wrong frontmatter type).`);
@@ -1157,7 +1224,7 @@ function validateBknDirectory(dirPath) {
1157
1224
  }
1158
1225
  const rtFiles = bknFiles(join(dir, "relation_types"));
1159
1226
  for (const file of rtFiles) {
1160
- const text = readFileSync2(file, "utf8");
1227
+ const text = readFileSync3(file, "utf8");
1161
1228
  const fm = parseFrontmatter(text);
1162
1229
  const rel = file.slice(dir.length + 1);
1163
1230
  if (!fm || fm.type !== "relation_type") {
@@ -1187,10 +1254,10 @@ function validateBknDirectory(dirPath) {
1187
1254
  }
1188
1255
 
1189
1256
  // src/commands/bkn.ts
1190
- var int3 = (v) => Number.parseInt(v, 10);
1257
+ var int4 = (v) => Number.parseInt(v, 10);
1191
1258
  function bknCommand() {
1192
- const bkn = new Command4("bkn").description("Knowledge networks \u2014 list, query, schema, instances");
1193
- bkn.command("list").description("List knowledge networks").option("--limit <n>", "page size", int3, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int3, 0).option("--name-pattern <s>", "filter by name pattern").option("--tag <s>", "filter by tag").option("--sort <field>", "sort field", "update_time").option("--direction <dir>", "asc | desc", "desc").action(async (_opts, cmd) => {
1259
+ const bkn = new Command5("bkn").description("Knowledge networks \u2014 list, query, schema, instances");
1260
+ bkn.command("list").description("List knowledge networks").option("--limit <n>", "page size", int4, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int4, 0).option("--name-pattern <s>", "filter by name pattern").option("--tag <s>", "filter by tag").option("--sort <field>", "sort field", "update_time").option("--direction <dir>", "asc | desc", "desc").action(async (_opts, cmd) => {
1194
1261
  const o = cmd.optsWithGlobals();
1195
1262
  const data = await clientFrom(cmd).kn.list({
1196
1263
  limit: o.limit,
@@ -1209,7 +1276,7 @@ function bknCommand() {
1209
1276
  });
1210
1277
  printJson(data, outputOptions(cmd));
1211
1278
  });
1212
- bkn.command("search <kn-id> <query>").description("Semantic search within a knowledge network").option("--max-concepts <n>", "max concepts to return", int3, 10).option("--mode <mode>", "retrieval mode", "keyword_vector_retrieval").action(async (knId, query, opts, cmd) => {
1279
+ bkn.command("search <kn-id> <query>").description("Semantic search within a knowledge network").option("--max-concepts <n>", "max concepts to return", int4, 10).option("--mode <mode>", "retrieval mode", "keyword_vector_retrieval").action(async (knId, query, opts, cmd) => {
1213
1280
  const data = await clientFrom(cmd).kn.search(knId, query, {
1214
1281
  maxConcepts: opts.maxConcepts,
1215
1282
  mode: opts.mode
@@ -1298,7 +1365,7 @@ function bknCommand() {
1298
1365
  printJson(await clientFrom(cmd).kn.subgraph(knId, readBody(opts)), outputOptions(cmd));
1299
1366
  });
1300
1367
  const actionLog = bkn.command("action-log").description("Action logs \u2014 list/get/cancel");
1301
- actionLog.command("list <kn-id>").description("List action logs").option("--status <s>", "filter by status").option("--action-type-id <id>", "filter by action type").option("--limit <n>", "page size", int3, DEFAULT_LIST_LIMIT).action(async (knId, opts, cmd) => {
1368
+ actionLog.command("list <kn-id>").description("List action logs").option("--status <s>", "filter by status").option("--action-type-id <id>", "filter by action type").option("--limit <n>", "page size", int4, DEFAULT_LIST_LIMIT).action(async (knId, opts, cmd) => {
1302
1369
  printJson(
1303
1370
  await clientFrom(cmd).kn.actionLogs(knId, {
1304
1371
  status: opts.status,
@@ -1478,7 +1545,7 @@ function bknCommand() {
1478
1545
  printJson(result, outputOptions(cmd));
1479
1546
  if (!result.valid) process.exitCode = 1;
1480
1547
  });
1481
- bkn.command("create-from-csv <catalog-id>").description("Import CSV files into a Vega catalog, then build a KN from them").requiredOption("--files <glob>", "CSV paths (comma-separated or glob)").requiredOption("--name <name>", "knowledge network name").option("--table-prefix <s>", "prefix for derived table names", "").option("--batch-size <n>", "rows per insert batch", int3, 500).option("--tables <list>", "subset of imported tables to include in the KN").option("--pk-map <map>", "explicit primary keys: '<table>:<col>[,...]'").option("--build", "submit a Vega build task per resource after creation").option(
1548
+ bkn.command("create-from-csv <catalog-id>").description("Import CSV files into a Vega catalog, then build a KN from them").requiredOption("--files <glob>", "CSV paths (comma-separated or glob)").requiredOption("--name <name>", "knowledge network name").option("--table-prefix <s>", "prefix for derived table names", "").option("--batch-size <n>", "rows per insert batch", int4, 500).option("--tables <list>", "subset of imported tables to include in the KN").option("--pk-map <map>", "explicit primary keys: '<table>:<col>[,...]'").option("--build", "submit a Vega build task per resource after creation").option(
1482
1549
  "--embedding-fields <map>",
1483
1550
  "columns to vectorize per table (with --build): '<table>:<col>[+<col>...][,...]'"
1484
1551
  ).option("--embedding-model <id>", "embedding model id for the vector index (with --build)").option("--no-rollback", "keep a partially-created KN on failure").action(async (catalogId, opts, cmd) => {
@@ -1504,13 +1571,13 @@ function bknCommand() {
1504
1571
  }
1505
1572
 
1506
1573
  // src/commands/call.ts
1507
- import { Command as Command5 } from "commander";
1574
+ import { Command as Command6 } from "commander";
1508
1575
  function collect(value, prev) {
1509
1576
  prev.push(value);
1510
1577
  return prev;
1511
1578
  }
1512
1579
  function callCommand() {
1513
- const cmd = new Command5("call").alias("curl").description("Call an API with curl-style flags and auto-injected auth headers").argument("<url>", "API path (e.g. /api/...) or absolute URL").option("-X, --request <method>", "HTTP method").option("-H, --header <header>", 'extra header "Name: value" (repeatable)', collect, []).option("-d, --data <body>", "request body (sets JSON content-type if unset)").option("--data-raw <body>", "alias for --data").option(
1580
+ const cmd = new Command6("call").alias("curl").description("Call an API with curl-style flags and auto-injected auth headers").argument("<url>", "API path (e.g. /api/...) or absolute URL").option("-X, --request <method>", "HTTP method").option("-H, --header <header>", 'extra header "Name: value" (repeatable)', collect, []).option("-d, --data <body>", "request body (sets JSON content-type if unset)").option("--data-raw <body>", "alias for --data").option(
1514
1581
  "-F, --form <field>",
1515
1582
  "multipart field key=value or key=@file (repeatable)",
1516
1583
  collect,
@@ -1545,14 +1612,14 @@ function callCommand() {
1545
1612
  }
1546
1613
 
1547
1614
  // src/commands/config.ts
1548
- import { Command as Command6 } from "commander";
1615
+ import { Command as Command7 } from "commander";
1549
1616
  function requireActive() {
1550
1617
  const baseUrl = activePlatform();
1551
1618
  if (!baseUrl) throw new InputError("No active platform. Run `openbkn auth login <url>` first.");
1552
1619
  return baseUrl;
1553
1620
  }
1554
1621
  function configCommand() {
1555
- const config = new Command6("config").description("Per-platform CLI configuration");
1622
+ const config = new Command7("config").description("Per-platform CLI configuration");
1556
1623
  config.command("show").description("Show the active platform and business domain").action((_opts, cmd) => {
1557
1624
  const baseUrl = activePlatform();
1558
1625
  printJson(
@@ -1585,13 +1652,56 @@ function configCommand() {
1585
1652
  }
1586
1653
 
1587
1654
  // src/commands/context.ts
1588
- import { Command as Command7 } from "commander";
1589
- var int4 = (v) => Number.parseInt(v, 10);
1655
+ import { Command as Command8 } from "commander";
1656
+ var int5 = (v) => Number.parseInt(v, 10);
1657
+ var collectArg = (v, prev) => {
1658
+ prev.push(v);
1659
+ return prev;
1660
+ };
1661
+ function buildArgs(opts) {
1662
+ let out = {};
1663
+ if (opts.args) {
1664
+ try {
1665
+ out = JSON.parse(opts.args);
1666
+ } catch {
1667
+ throw new InputError("--args must be valid JSON");
1668
+ }
1669
+ }
1670
+ for (const pair of opts.arg ?? []) {
1671
+ const idx = pair.indexOf("=");
1672
+ if (idx <= 0) throw new InputError(`--arg must be key=value (got: ${pair})`);
1673
+ const key = pair.slice(0, idx);
1674
+ const raw = pair.slice(idx + 1);
1675
+ try {
1676
+ out[key] = JSON.parse(raw);
1677
+ } catch {
1678
+ out[key] = raw;
1679
+ }
1680
+ }
1681
+ return out;
1682
+ }
1683
+ function printToolList(res, out) {
1684
+ if (out.json || out.compact) {
1685
+ printJson(res, out);
1686
+ return;
1687
+ }
1688
+ const r = res ?? {};
1689
+ const arr = [res, r.tools, r.result?.tools, r.data].find(Array.isArray);
1690
+ if (!arr) {
1691
+ printJson(res, out);
1692
+ return;
1693
+ }
1694
+ const rows = arr.map((t) => ({
1695
+ name: t.name ?? t.tool_name ?? t.key ?? "",
1696
+ description: typeof t.description === "string" ? t.description : ""
1697
+ }));
1698
+ printJson(rows, out);
1699
+ }
1590
1700
  function contextCommand() {
1591
- const cmd = new Command7("context").description(
1701
+ const cmd = new Command8("context").description(
1592
1702
  "Context loader (MCP) \u2014 schema discovery, instance query, skill recall"
1593
1703
  );
1594
- cmd.command("search-schema <kn-id> <query>").description("Search object/relation/action/metric schemas").option("--scope <list>", "comma-separated scopes (object,relation,action,metric)").option("--max <n>", "max concepts", int4).action(async (knId, query, opts, cmd2) => {
1704
+ cmd.command("search-schema <kn-id> <query>").description("Search object/relation/action/metric schemas").option("--scope <list>", "comma-separated scopes (object,relation,action,metric)").option("--max <n>", "max concepts", int5).action(async (knId, query, opts, cmd2) => {
1595
1705
  const data = await clientFrom(cmd2).context.searchSchema(knId, query, {
1596
1706
  searchScope: opts.scope ? String(opts.scope).split(",") : void 0,
1597
1707
  maxConcepts: opts.max
@@ -1607,23 +1717,51 @@ function contextCommand() {
1607
1717
  }
1608
1718
  printJson(await clientFrom(cmd2).context.queryObjectInstance(knId, args), outputOptions(cmd2));
1609
1719
  });
1610
- cmd.command("find-skills <kn-id> <object-type-id>").description("Recall skills for an object type").option("--top-k <n>", "max skills (1-20)", int4).action(async (knId, otId, opts, cmd2) => {
1720
+ cmd.command("find-skills <kn-id> <object-type-id>").description("Recall skills for an object type").option("--top-k <n>", "max skills (1-20)", int5).action(async (knId, otId, opts, cmd2) => {
1611
1721
  printJson(
1612
1722
  await clientFrom(cmd2).context.findSkills(knId, otId, opts.topK),
1613
1723
  outputOptions(cmd2)
1614
1724
  );
1615
1725
  });
1616
- cmd.command("tools <kn-id>").description("List MCP tools").action(async (knId, _opts, cmd2) => {
1617
- printJson(await clientFrom(cmd2).context.tools(knId), outputOptions(cmd2));
1726
+ cmd.command("kn-detail <kn-id>").description("Get a KN's schema at a detail level (progressive: summary skeleton \u2192 drill down)").option("--detail-level <level>", "summary (default) | full", "summary").action(async (knId, opts, cmd2) => {
1727
+ const level = opts.detailLevel === "full" ? "full" : "summary";
1728
+ printJson(await clientFrom(cmd2).context.knDetail(knId, level), outputOptions(cmd2));
1618
1729
  });
1619
- cmd.command("tool-call <kn-id> <name>").description("Call any MCP tool directly (--args JSON)").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, name, opts, cmd2) => {
1620
- let args;
1621
- try {
1622
- args = JSON.parse(opts.args);
1623
- } catch {
1624
- throw new InputError("--args must be valid JSON");
1625
- }
1626
- printJson(await clientFrom(cmd2).context.toolCall(knId, name, args), outputOptions(cmd2));
1730
+ cmd.command("object-types <kn-id> <ids...>").description("Full definitions for the given object-type ids (unmatched \u2192 `missing`)").action(async (knId, ids, _opts, cmd2) => {
1731
+ printJson(await clientFrom(cmd2).context.objectTypes(knId, ids), outputOptions(cmd2));
1732
+ });
1733
+ cmd.command("relation-types <kn-id> <ids...>").description("Full definitions for the given relation-type ids (unmatched \u2192 `missing`)").action(async (knId, ids, _opts, cmd2) => {
1734
+ printJson(await clientFrom(cmd2).context.relationTypes(knId, ids), outputOptions(cmd2));
1735
+ });
1736
+ cmd.command("info").description("List the deploy's MCP tool catalog (global \u2014 no KN needed)").action(async (_opts, cmd2) => {
1737
+ printToolList(await clientFrom(cmd2).context.info(), outputOptions(cmd2));
1738
+ });
1739
+ cmd.command("tools <kn-id>").description("List MCP tools advertised for a KN session").action(async (knId, _opts, cmd2) => {
1740
+ printToolList(await clientFrom(cmd2).context.tools(knId), outputOptions(cmd2));
1741
+ });
1742
+ cmd.command("tool-call <kn-id> <name>").description("Call any MCP tool by name \u2014 current or future (use `tools` to discover)").option("--args <json>", "tool arguments as JSON").option(
1743
+ "--arg <key=value>",
1744
+ "one argument (repeatable; value parsed as JSON, else string)",
1745
+ collectArg,
1746
+ []
1747
+ ).action(async (knId, name, opts, cmd2) => {
1748
+ printJson(
1749
+ await clientFrom(cmd2).context.toolCall(knId, name, buildArgs(opts)),
1750
+ outputOptions(cmd2)
1751
+ );
1752
+ });
1753
+ cmd.command("call-method <kn-id> <method>").description(
1754
+ "Call any MCP method by name (e.g. tools/list, resources/read) \u2014 current or future"
1755
+ ).option("--args <json>", "method params as JSON").option(
1756
+ "--arg <key=value>",
1757
+ "one param (repeatable; value parsed as JSON, else string)",
1758
+ collectArg,
1759
+ []
1760
+ ).action(async (knId, method, opts, cmd2) => {
1761
+ printJson(
1762
+ await clientFrom(cmd2).context.callMethod(knId, method, buildArgs(opts)),
1763
+ outputOptions(cmd2)
1764
+ );
1627
1765
  });
1628
1766
  cmd.command("resources <kn-id>").description("List MCP resources").action(async (knId, _opts, cmd2) => {
1629
1767
  printJson(await clientFrom(cmd2).context.resources(knId), outputOptions(cmd2));
@@ -1655,19 +1793,19 @@ function contextCommand() {
1655
1793
  throw new InputError("--args must be valid JSON");
1656
1794
  }
1657
1795
  };
1658
- cmd.command("query-instance-subgraph <kn-id>").description("Layer-2: query an instance subgraph across relation-type paths").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
1796
+ cmd.command("query-instance-subgraph <kn-id>").description("Query an instance subgraph across relation-type paths").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
1659
1797
  printJson(
1660
1798
  await clientFrom(cmd2).context.queryInstanceSubgraph(knId, jsonArgs(opts.args)),
1661
1799
  outputOptions(cmd2)
1662
1800
  );
1663
1801
  });
1664
- cmd.command("get-logic-properties <kn-id>").description("Layer-3: compute logic-property values for instances").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
1802
+ cmd.command("get-logic-properties <kn-id>").description("Compute logic-property values for instances").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
1665
1803
  printJson(
1666
1804
  await clientFrom(cmd2).context.logicProperties(knId, jsonArgs(opts.args)),
1667
1805
  outputOptions(cmd2)
1668
1806
  );
1669
1807
  });
1670
- cmd.command("get-action-info <kn-id>").description("Layer-3: fetch action info / dynamic tools for an instance").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
1808
+ cmd.command("get-action-info <kn-id>").description("Fetch action info / dynamic tools for an instance").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
1671
1809
  printJson(
1672
1810
  await clientFrom(cmd2).context.actionInfo(knId, jsonArgs(opts.args)),
1673
1811
  outputOptions(cmd2)
@@ -1677,10 +1815,10 @@ function contextCommand() {
1677
1815
  }
1678
1816
 
1679
1817
  // src/commands/dataflow.ts
1680
- import { Command as Command8 } from "commander";
1681
- var int5 = (v) => Number.parseInt(v, 10);
1818
+ import { Command as Command9 } from "commander";
1819
+ var int6 = (v) => Number.parseInt(v, 10);
1682
1820
  function dataflowCommand() {
1683
- const cmd = new Command8("dataflow").description("Dataflow document workflows \u2014 list, runs, logs");
1821
+ const cmd = new Command9("dataflow").description("Dataflow document workflows \u2014 list, runs, logs");
1684
1822
  cmd.command("list").description("List all dataflows").action(async (_opts, cmd2) => {
1685
1823
  printJson(await clientFrom(cmd2).dataflows.list(), outputOptions(cmd2));
1686
1824
  });
@@ -1690,7 +1828,7 @@ function dataflowCommand() {
1690
1828
  outputOptions(cmd2)
1691
1829
  );
1692
1830
  });
1693
- cmd.command("logs <dagId> <instanceId>").description("Show logs for one run").option("--page <n>", "page", int5, 0).option("--limit <n>", "page size", int5, DEFAULT_LIST_LIMIT).action(async (dagId, instanceId, opts, cmd2) => {
1831
+ cmd.command("logs <dagId> <instanceId>").description("Show logs for one run").option("--page <n>", "page", int6, 0).option("--limit <n>", "page size", int6, DEFAULT_LIST_LIMIT).action(async (dagId, instanceId, opts, cmd2) => {
1694
1832
  printJson(
1695
1833
  await clientFrom(cmd2).dataflows.logs(dagId, instanceId, {
1696
1834
  page: opts.page,
@@ -1752,8 +1890,8 @@ function dataflowCommand() {
1752
1890
 
1753
1891
  // src/commands/explore.ts
1754
1892
  import { createServer } from "http";
1755
- import { Command as Command9 } from "commander";
1756
- var int6 = (v) => Number.parseInt(v, 10);
1893
+ import { Command as Command10 } from "commander";
1894
+ var int7 = (v) => Number.parseInt(v, 10);
1757
1895
  var ROUTES = {
1758
1896
  "GET /api/bkn/meta": (c, q) => c.kn.get(req(q, "knId")),
1759
1897
  "POST /api/bkn/search": (c, _q, b) => c.kn.search(str(b.knId), str(b.query), {
@@ -1798,10 +1936,10 @@ var INDEX = `<!doctype html><meta charset="utf-8"><title>openbkn explore</title>
1798
1936
  <p>Read-only JSON endpoints for bkn + vega:</p>
1799
1937
  <ul>${Object.keys(ROUTES).map((r) => `<li><code>${r}</code></li>`).join("")}</ul>`;
1800
1938
  function exploreCommand() {
1801
- const cmd = new Command9("explore").description(
1939
+ const cmd = new Command10("explore").description(
1802
1940
  "Start a local web server with read-only bkn + vega JSON endpoints"
1803
1941
  );
1804
- cmd.option("--port <n>", "port to listen on", int6, 7777).option("--host <h>", "host to bind", "127.0.0.1").action(async (opts, command) => {
1942
+ cmd.option("--port <n>", "port to listen on", int7, 7777).option("--host <h>", "host to bind", "127.0.0.1").action(async (opts, command) => {
1805
1943
  const client = clientFrom(command);
1806
1944
  const server = createServer((reqMsg, res) => {
1807
1945
  void handle(client, reqMsg, res);
@@ -1839,8 +1977,14 @@ async function handle(client, reqMsg, res) {
1839
1977
  }
1840
1978
 
1841
1979
  // src/commands/model.ts
1842
- import { Command as Command10 } from "commander";
1843
- var int7 = (v) => Number.parseInt(v, 10);
1980
+ import { Command as Command11 } from "commander";
1981
+ var int8 = (v) => Number.parseInt(v, 10);
1982
+ async function resolveLlmModelName(client, model) {
1983
+ if (!/^\d+$/.test(model)) return model;
1984
+ const detail = await client.models.llm.get(model);
1985
+ if (!detail?.model_name) throw new InputError(`No LLM found with id ${model}.`);
1986
+ return detail.model_name;
1987
+ }
1844
1988
  function addManagementCommands(parent, kind) {
1845
1989
  parent.command("add").description("Register a model (definition JSON via --body / --body-file)").option("--body <json>", "model definition JSON").option("--body-file <path>", "read model definition JSON from a file").action(async (opts, cmd) => {
1846
1990
  printJson(await clientFrom(cmd).models[kind].add(readBody(opts)), outputOptions(cmd));
@@ -1856,9 +2000,11 @@ function addManagementCommands(parent, kind) {
1856
2000
  });
1857
2001
  }
1858
2002
  function modelCommand() {
1859
- const model = new Command10("model").description("Model factory \u2014 LLM / small-model CRUD + chat");
2003
+ const model = new Command11("model").description(
2004
+ "Model factory \u2014 LLM / small-model CRUD, chat / embeddings / rerank, default selection"
2005
+ );
1860
2006
  const llm = model.command("llm").description("Large language models");
1861
- llm.command("list").description("List LLM models").option("--name <s>", "filter by name").option("--type <t>", "model type filter").option("--limit <n>", "page size", int7, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int7, 1).action(async (opts, cmd) => {
2007
+ llm.command("list").description("List LLM models").option("--name <s>", "filter by name").option("--type <t>", "model type filter").option("--limit <n>", "page size", int8, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int8, 1).action(async (opts, cmd) => {
1862
2008
  printJson(
1863
2009
  await clientFrom(cmd).models.llm.list({
1864
2010
  name: opts.name,
@@ -1872,18 +2018,26 @@ function modelCommand() {
1872
2018
  llm.command("get <modelId>").description("Get an LLM model").action(async (id, _opts, cmd) => {
1873
2019
  printJson(await clientFrom(cmd).models.llm.get(id), outputOptions(cmd));
1874
2020
  });
1875
- llm.command("chat <modelId>").description("OpenAI-compatible chat completion").requiredOption("-m, --message <text>", "user message").option("--stream", "stream the reply token-by-token to stdout").action(async (id, opts, cmd) => {
2021
+ llm.command("chat <model>").description("OpenAI-compatible chat completion (<model> = model name or numeric id)").requiredOption("-m, --message <text>", "user message").option("--stream", "stream the reply token-by-token to stdout").action(async (model2, opts, cmd) => {
2022
+ const client = clientFrom(cmd);
2023
+ const name = await resolveLlmModelName(client, model2);
1876
2024
  const messages = [{ role: "user", content: opts.message }];
1877
2025
  if (opts.stream) {
1878
- await clientFrom(cmd).models.llm.chatStream(id, messages, (t) => process.stdout.write(t));
2026
+ await client.models.llm.chatStream(name, messages, (t) => process.stdout.write(t));
1879
2027
  process.stdout.write("\n");
1880
2028
  return;
1881
2029
  }
1882
- printJson(await clientFrom(cmd).models.llm.chat(id, messages), outputOptions(cmd));
2030
+ printJson(await client.models.llm.chat(name, messages), outputOptions(cmd));
2031
+ });
2032
+ llm.command("set-default <modelId>").description("Set this LLM as the system default (admin)").action(async (id, _opts, cmd) => {
2033
+ printJson(await clientFrom(cmd).models.llm.setDefault(id, true), outputOptions(cmd));
2034
+ });
2035
+ llm.command("unset-default <modelId>").description("Clear this LLM as the system default (admin)").action(async (id, _opts, cmd) => {
2036
+ printJson(await clientFrom(cmd).models.llm.setDefault(id, false), outputOptions(cmd));
1883
2037
  });
1884
2038
  addManagementCommands(llm, "llm");
1885
2039
  const small = model.command("small").description("Small models (embedding / reranker)");
1886
- small.command("list").description("List small models").option("--name <s>", "filter by name").option("--type <t>", "model type filter").option("--limit <n>", "page size", int7, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int7, 1).action(async (opts, cmd) => {
2040
+ small.command("list").description("List small models").option("--name <s>", "filter by name").option("--type <t>", "model type filter").option("--limit <n>", "page size", int8, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int8, 1).action(async (opts, cmd) => {
1887
2041
  printJson(
1888
2042
  await clientFrom(cmd).models.small.list({
1889
2043
  name: opts.name,
@@ -1909,20 +2063,60 @@ function modelCommand() {
1909
2063
  outputOptions(cmd)
1910
2064
  );
1911
2065
  });
2066
+ small.command("get-default").description("Show the system default small model for a type").option("--type <t>", "model type: embedding | reranker", "embedding").action(async (opts, cmd) => {
2067
+ printJson(await clientFrom(cmd).models.small.getDefault(opts.type), outputOptions(cmd));
2068
+ });
2069
+ small.command("set-default <modelId>").description("Set this small model as the system default for its type (admin)").action(async (id, _opts, cmd) => {
2070
+ printJson(await clientFrom(cmd).models.small.setDefault(id, true), outputOptions(cmd));
2071
+ });
2072
+ small.command("unset-default <modelId>").description("Clear this small model as the system default (admin)").action(async (id, _opts, cmd) => {
2073
+ printJson(await clientFrom(cmd).models.small.setDefault(id, false), outputOptions(cmd));
2074
+ });
1912
2075
  addManagementCommands(small, "small");
2076
+ model.addHelpText(
2077
+ "after",
2078
+ `
2079
+ Identifiers:
2080
+ \u2022 get / set-default / delete take the numeric model id (e.g. 2071747547839467520).
2081
+ \u2022 chat takes a model NAME, but also accepts a numeric id (resolved to its name).
2082
+
2083
+ Examples:
2084
+ $ openbkn model llm list # ids + the 'default' flag
2085
+ $ openbkn model llm chat deepseek_v4_flash -m hi # by name
2086
+ $ openbkn model llm chat 2071747547839467520 -m hi --stream # by id, streamed
2087
+ $ openbkn model llm set-default 2071747547839467520 # system default LLM
2088
+ $ openbkn model small get-default --type embedding # current default
2089
+ $ openbkn model small set-default <id> # default embedding/reranker`
2090
+ );
1913
2091
  return group(model, "MODELS & SKILLS");
1914
2092
  }
1915
2093
 
1916
2094
  // src/commands/resource.ts
1917
- import { Command as Command11 } from "commander";
1918
- var int8 = (v) => Number.parseInt(v, 10);
2095
+ import { Command as Command12 } from "commander";
2096
+ var int9 = (v) => Number.parseInt(v, 10);
2097
+ var parsePairs = (raw) => {
2098
+ if (!raw) return void 0;
2099
+ return raw.split(",").map((part) => {
2100
+ const idx = part.indexOf("=");
2101
+ if (idx < 1) throw new Error("--extension must be key=value[,key=value]");
2102
+ return { key: part.slice(0, idx).trim(), value: part.slice(idx + 1).trim() };
2103
+ }).filter((p) => p.key.length > 0);
2104
+ };
1919
2105
  function resourceCommand() {
1920
- const cmd = new Command11("resource").alias("res").description("Resources \u2014 list, find, get, query, delete");
1921
- cmd.command("list").description("List resources under a catalog").option("--catalog-id <id>", "filter by catalog id").option("--datasource-id <id>", "alias of --catalog-id").option("--category <c>", "resource category (table | logicview | dataset)").option("--type <c>", "alias of --category").option("--limit <n>", "page size", int8, DEFAULT_LIST_LIMIT).action(async (opts, cmd2) => {
2106
+ const cmd = new Command12("resource").alias("res").description("Resources \u2014 list, find, get, query, delete");
2107
+ cmd.command("list").description("List resources under a catalog").option("--catalog-id <id>", "filter by catalog id").option("--datasource-id <id>", "alias of --catalog-id").option("--category <c>", "resource category (table | logicview | dataset)").option("--type <c>", "alias of --category").option("--status <status>", "filter by status").option("--database <name>", "filter by database").option("--limit <n>", "page size", int9, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int9, 0).option("--include-extensions", "include all extension key/value pairs").option("--include-extension-keys <keys>", "include selected extension keys").option("--extension <k=v,...>", "filter by extension key/value pairs").option("--sort <field>", "sort field: name | create_time | update_time").option("--direction <dir>", "sort direction: asc | desc").action(async (opts, cmd2) => {
1922
2108
  const data = await clientFrom(cmd2).resource.list({
1923
2109
  datasourceId: opts.catalogId ?? opts.datasourceId,
1924
2110
  category: opts.category ?? opts.type,
1925
- limit: opts.limit
2111
+ status: opts.status,
2112
+ database: opts.database,
2113
+ limit: opts.limit,
2114
+ offset: opts.offset,
2115
+ includeExtensions: opts.includeExtensions,
2116
+ includeExtensionKeys: opts.includeExtensionKeys,
2117
+ extensionPairs: parsePairs(opts.extension),
2118
+ sort: opts.sort,
2119
+ direction: opts.direction
1926
2120
  });
1927
2121
  printJson(data, outputOptions(cmd2));
1928
2122
  });
@@ -1936,7 +2130,7 @@ function resourceCommand() {
1936
2130
  cmd.command("get <id>").description("Get resource details").action(async (id, _opts, cmd2) => {
1937
2131
  printJson(await clientFrom(cmd2).resource.get(id), outputOptions(cmd2));
1938
2132
  });
1939
- cmd.command("query <id>").description("Fetch data rows from a resource").option("--limit <n>", "row limit", int8, DEFAULT_QUERY_LIMIT).option("--offset <n>", "row offset", int8, 0).option("--need-total", "include total count").action(async (id, opts, cmd2) => {
2133
+ cmd.command("query <id>").description("Fetch data rows from a resource").option("--limit <n>", "row limit", int9, DEFAULT_QUERY_LIMIT).option("--offset <n>", "row offset", int9, 0).option("--need-total", "include total count").action(async (id, opts, cmd2) => {
1940
2134
  const data = await clientFrom(cmd2).resource.query(id, {
1941
2135
  limit: opts.limit,
1942
2136
  offset: opts.offset,
@@ -1951,11 +2145,11 @@ function resourceCommand() {
1951
2145
  }
1952
2146
 
1953
2147
  // src/commands/skill.ts
1954
- import { Command as Command12 } from "commander";
1955
- var int9 = (v) => Number.parseInt(v, 10);
2148
+ import { Command as Command13 } from "commander";
2149
+ var int10 = (v) => Number.parseInt(v, 10);
1956
2150
  function skillCommand() {
1957
- const cmd = new Command12("skill").description("Skill registry and market");
1958
- const listOpts = (c) => c.option("--name <s>", "filter by name").option("--source <s>", "filter by source").option("--status <s>", "filter by status").option("--limit <n>", "page size", int9, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int9, 1);
2151
+ const cmd = new Command13("skill").description("Skill registry and market");
2152
+ const listOpts = (c) => c.option("--name <s>", "filter by name").option("--source <s>", "filter by source").option("--status <s>", "filter by status").option("--limit <n>", "page size", int10, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int10, 1);
1959
2153
  listOpts(cmd.command("list").description("List skills")).option("--create-user <s>", "filter by creator").action(async (opts, cmd2) => {
1960
2154
  printJson(
1961
2155
  await clientFrom(cmd2).skills.list({
@@ -2041,11 +2235,11 @@ function skillCommand() {
2041
2235
  }
2042
2236
 
2043
2237
  // src/commands/toolbox.ts
2044
- import { Command as Command13 } from "commander";
2045
- var int10 = (v) => Number.parseInt(v, 10);
2238
+ import { Command as Command14 } from "commander";
2239
+ var int11 = (v) => Number.parseInt(v, 10);
2046
2240
  function toolboxCommand() {
2047
- const cmd = new Command13("toolbox").description("Agent toolbox lifecycle");
2048
- cmd.command("list").description("List toolboxes").option("--keyword <s>", "filter by keyword").option("--limit <n>", "page size", int10, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int10, 0).action(async (opts, cmd2) => {
2241
+ const cmd = new Command14("toolbox").description("Agent toolbox lifecycle");
2242
+ cmd.command("list").description("List toolboxes").option("--keyword <s>", "filter by keyword").option("--limit <n>", "page size", int11, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int11, 0).action(async (opts, cmd2) => {
2049
2243
  printJson(
2050
2244
  await clientFrom(cmd2).toolboxes.list({
2051
2245
  keyword: opts.keyword,
@@ -2086,7 +2280,7 @@ function toolboxCommand() {
2086
2280
  return group(cmd, "DECISION AGENT");
2087
2281
  }
2088
2282
  function toolCommand() {
2089
- const cmd = new Command13("tool").description("Tools inside a toolbox");
2283
+ const cmd = new Command14("tool").description("Tools inside a toolbox");
2090
2284
  cmd.command("list").description("List tools in a toolbox").requiredOption("--toolbox <box-id>", "toolbox id").action(async (opts, cmd2) => {
2091
2285
  printJson(await clientFrom(cmd2).toolboxes.tools(opts.toolbox), outputOptions(cmd2));
2092
2286
  });
@@ -2102,7 +2296,7 @@ function toolCommand() {
2102
2296
  outputOptions(cmd2)
2103
2297
  );
2104
2298
  });
2105
- const invokeOpts = (c) => c.requiredOption("--toolbox <box-id>", "toolbox id").option("--body <json>", "request body JSON").option("--header <json>", "headers map JSON").option("--query <json>", "query params JSON").option("--path <json>", "path params JSON").option("--timeout <s>", "per-call timeout seconds", int10);
2299
+ const invokeOpts = (c) => c.requiredOption("--toolbox <box-id>", "toolbox id").option("--body <json>", "request body JSON").option("--header <json>", "headers map JSON").option("--query <json>", "query params JSON").option("--path <json>", "path params JSON").option("--timeout <s>", "per-call timeout seconds", int11);
2106
2300
  const parseJson = (s, label) => {
2107
2301
  if (!s) return void 0;
2108
2302
  try {
@@ -2144,11 +2338,11 @@ function toolCommand() {
2144
2338
  }
2145
2339
 
2146
2340
  // src/commands/trace.ts
2147
- import { readFileSync as readFileSync4, writeFileSync } from "fs";
2148
- import { Command as Command14 } from "commander";
2341
+ import { readFileSync as readFileSync5, writeFileSync } from "fs";
2342
+ import { Command as Command15 } from "commander";
2149
2343
 
2150
- // src/trace-ai/schema-validate.ts
2151
- import { readFileSync as readFileSync3 } from "fs";
2344
+ // src/bkn-trace/schema-validate.ts
2345
+ import { readFileSync as readFileSync4 } from "fs";
2152
2346
  import { extname } from "path";
2153
2347
  import yaml from "js-yaml";
2154
2348
  import { z } from "zod";
@@ -2185,7 +2379,7 @@ var DiagnosisRule = z.object({
2185
2379
  params: z.record(z.string(), z.unknown()).optional()
2186
2380
  });
2187
2381
  function parseFile(file) {
2188
- const text = readFileSync3(file, "utf8");
2382
+ const text = readFileSync4(file, "utf8");
2189
2383
  const ext = extname(file).toLowerCase();
2190
2384
  if (ext === ".yaml" || ext === ".yml") return yaml.load(text);
2191
2385
  return JSON.parse(text);
@@ -2223,8 +2417,8 @@ function validateSchemaFile(file, kind) {
2223
2417
 
2224
2418
  // src/commands/trace.ts
2225
2419
  function traceCommand() {
2226
- const cmd = new Command14("trace").description(
2227
- "Trace AI \u2014 fetch spans, diagnose (symbolic + LLM rubric), scan, eval-set, schema validate"
2420
+ const cmd = new Command15("trace").description(
2421
+ "BKN Trace \u2014 fetch spans, diagnose (symbolic + LLM rubric), scan, eval-set, schema validate"
2228
2422
  );
2229
2423
  cmd.command("get <conversation-id>").description("Fetch all trace spans for a conversation").option("--max-spans <n>", "max spans", (v) => Number.parseInt(v, 10)).action(async (conversationId, opts, cmd2) => {
2230
2424
  printJson(
@@ -2252,7 +2446,7 @@ function traceCommand() {
2252
2446
  });
2253
2447
  const evalSet = cmd.command("eval-set").description("Build + run trace eval sets");
2254
2448
  evalSet.command("build <queries-file>").description("Build eval cases from a queries JSON file").option("--out <file>", "write the cases JSON here (default: stdout)").action(async (queriesFile, opts, cmd2) => {
2255
- const raw = JSON.parse(readFileSync4(queriesFile, "utf8"));
2449
+ const raw = JSON.parse(readFileSync5(queriesFile, "utf8"));
2256
2450
  const cases = clientFrom(cmd2).trace.evalSetBuild(raw);
2257
2451
  if (opts.out) {
2258
2452
  writeFileSync(opts.out, JSON.stringify({ cases }, null, 2));
@@ -2262,7 +2456,7 @@ function traceCommand() {
2262
2456
  }
2263
2457
  });
2264
2458
  evalSet.command("test <cases-file>").description("Run an eval set against an agent (--llm enables semantic_match)").requiredOption("--agent <id>", "agent id to run the queries against").option("--version <v>", "agent version", "v0").option("--llm", "enable semantic_match assertions via the local `claude` CLI").action(async (casesFile, opts, cmd2) => {
2265
- const raw = JSON.parse(readFileSync4(casesFile, "utf8"));
2459
+ const raw = JSON.parse(readFileSync5(casesFile, "utf8"));
2266
2460
  const cases = clientFrom(cmd2).trace.evalSetBuild(raw);
2267
2461
  const result = await clientFrom(cmd2).trace.evalSetTest(opts.agent, cases, {
2268
2462
  version: opts.version,
@@ -2281,15 +2475,37 @@ function traceCommand() {
2281
2475
  }
2282
2476
 
2283
2477
  // src/commands/vega.ts
2284
- import { Command as Command15 } from "commander";
2478
+ import { Command as Command16 } from "commander";
2479
+ var int12 = (v) => Number.parseInt(v, 10);
2480
+ var parsePairs2 = (raw) => {
2481
+ if (!raw) return void 0;
2482
+ return raw.split(",").map((part) => {
2483
+ const idx = part.indexOf("=");
2484
+ if (idx < 1) throw new InputError("--extension must be key=value[,key=value]");
2485
+ return { key: part.slice(0, idx).trim(), value: part.slice(idx + 1).trim() };
2486
+ }).filter((p) => p.key.length > 0);
2487
+ };
2285
2488
  function vegaCommand() {
2286
- const vega = new Command15("vega").description(
2489
+ const vega = new Command16("vega").description(
2287
2490
  "Vega observability \u2014 catalog, resources, index build tasks"
2288
2491
  );
2289
2492
  const catalog = vega.command("catalog").description("Catalog entries");
2290
- catalog.command("list").description("List catalog entries").option("--limit <n>", "page size", (v) => Number.parseInt(v, 10), DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", (v) => Number.parseInt(v, 10), 0).action(async (_opts, cmd) => {
2493
+ catalog.command("list").description("List catalog entries").option("--limit <n>", "page size", (v) => Number.parseInt(v, 10), DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", (v) => Number.parseInt(v, 10), 0).option("--name <s>", "filter by name").option("--tag <s>", "filter by tag").option("--type <type>", "filter by catalog type: physical | logical").option("--enabled <bool>", "filter by enabled state").option("--health-check-status <s>", "filter by health status").option("--include-extensions", "include all extension key/value pairs").option("--include-extension-keys <keys>", "include selected extension keys").option("--extension <k=v,...>", "filter by extension key/value pairs").option("--sort <field>", "sort field: name | create_time | update_time").option("--direction <dir>", "sort direction: asc | desc").action(async (_opts, cmd) => {
2291
2494
  const o = cmd.optsWithGlobals();
2292
- const data = await clientFrom(cmd).vega.catalogs({ limit: o.limit, offset: o.offset });
2495
+ const data = await clientFrom(cmd).vega.catalogs({
2496
+ limit: o.limit,
2497
+ offset: o.offset,
2498
+ name: o.name,
2499
+ tag: o.tag,
2500
+ type: o.type,
2501
+ enabled: o.enabled === void 0 ? void 0 : o.enabled === "true",
2502
+ healthCheckStatus: o.healthCheckStatus,
2503
+ includeExtensions: o.includeExtensions,
2504
+ includeExtensionKeys: o.includeExtensionKeys,
2505
+ extensionPairs: parsePairs2(o.extension),
2506
+ sort: o.sort,
2507
+ direction: o.direction
2508
+ });
2293
2509
  printJson(data, outputOptions(cmd));
2294
2510
  });
2295
2511
  catalog.command("get <id>").description("Get a catalog by id").action(async (id, _opts, cmd) => {
@@ -2301,21 +2517,41 @@ function vegaCommand() {
2301
2517
  catalog.command("health <ids...>").description("Health-status for one or more catalogs").action(async (ids, _opts, cmd) => {
2302
2518
  printJson(await clientFrom(cmd).vega.catalogHealth(ids), outputOptions(cmd));
2303
2519
  });
2304
- catalog.command("create").description("Create a catalog (data source)").requiredOption("--name <s>", "catalog name").requiredOption("--connector-type <s>", "connector type (e.g. mysql)").requiredOption("--connector-config <json>", "connector config JSON").option("--tags <t1,t2>", "comma-separated tags").option("--description <s>", "description").option("--enabled", "create enabled (default: disabled)").action(async (opts, cmd) => {
2520
+ catalog.command("create").description("Create a catalog (data source)").requiredOption("--name <s>", "catalog name").requiredOption("--connector-type <s>", "connector type (e.g. mysql)").requiredOption("--connector-config <json>", "connector config JSON").option("--id <id>", "explicit catalog id").option("--tags <t1,t2>", "comma-separated tags").option("--description <s>", "description").option("--enabled", "create enabled (default: disabled)").option("--internal", "create an internal catalog").option("--extensions <json>", "extension key/value JSON object").action(async (opts, cmd) => {
2305
2521
  let connectorConfig;
2306
2522
  try {
2307
2523
  connectorConfig = JSON.parse(opts.connectorConfig);
2308
2524
  } catch {
2309
2525
  throw new Error("--connector-config must be valid JSON");
2310
2526
  }
2527
+ const extensions = opts.extensions ? JSON.parse(opts.extensions) : void 0;
2311
2528
  printJson(
2312
2529
  await clientFrom(cmd).vega.createCatalog({
2530
+ id: opts.id,
2531
+ name: opts.name,
2532
+ connectorType: opts.connectorType,
2533
+ connectorConfig,
2534
+ tags: opts.tags ? String(opts.tags).split(",").map((t) => t.trim()).filter(Boolean) : void 0,
2535
+ description: opts.description,
2536
+ enabled: opts.enabled ? true : void 0,
2537
+ internal: opts.internal ? true : void 0,
2538
+ extensions
2539
+ }),
2540
+ outputOptions(cmd)
2541
+ );
2542
+ });
2543
+ catalog.command("update <id>").description("Update a catalog").option("--name <s>", "catalog name").option("--connector-type <s>", "connector type").option("--connector-config <json>", "connector config JSON").option("--tags <t1,t2>", "comma-separated tags").option("--description <s>", "description").option("--enabled <bool>", "enabled state").option("--extensions <json>", "extension key/value JSON object").action(async (id, opts, cmd) => {
2544
+ const connectorConfig = opts.connectorConfig ? JSON.parse(opts.connectorConfig) : void 0;
2545
+ const extensions = opts.extensions ? JSON.parse(opts.extensions) : void 0;
2546
+ printJson(
2547
+ await clientFrom(cmd).vega.updateCatalog(id, {
2313
2548
  name: opts.name,
2314
2549
  connectorType: opts.connectorType,
2315
2550
  connectorConfig,
2316
2551
  tags: opts.tags ? String(opts.tags).split(",").map((t) => t.trim()).filter(Boolean) : void 0,
2317
2552
  description: opts.description,
2318
- enabled: opts.enabled ? true : void 0
2553
+ enabled: opts.enabled === void 0 ? void 0 : opts.enabled === "true",
2554
+ extensions
2319
2555
  }),
2320
2556
  outputOptions(cmd)
2321
2557
  );
@@ -2323,6 +2559,15 @@ function vegaCommand() {
2323
2559
  catalog.command("enable <id>").description("Enable a catalog (required before discovery)").action(async (id, _opts, cmd) => {
2324
2560
  printJson(await clientFrom(cmd).vega.enableCatalog(id), outputOptions(cmd));
2325
2561
  });
2562
+ catalog.command("disable <id>").description("Disable a catalog").action(async (id, _opts, cmd) => {
2563
+ printJson(await clientFrom(cmd).vega.disableCatalog(id), outputOptions(cmd));
2564
+ });
2565
+ catalog.command("delete <id>").description("Delete a catalog").action(async (id, _opts, cmd) => {
2566
+ printJson(await clientFrom(cmd).vega.deleteCatalog(id), outputOptions(cmd));
2567
+ });
2568
+ catalog.command("test-connection <id>").description("Test a catalog connection").action(async (id, _opts, cmd) => {
2569
+ printJson(await clientFrom(cmd).vega.testCatalogConnection(id), outputOptions(cmd));
2570
+ });
2326
2571
  catalog.command("discover <id>").description("Trigger catalog resource discovery").option("--wait", "wait for discovery to complete").action(async (id, opts, cmd) => {
2327
2572
  printJson(
2328
2573
  await clientFrom(cmd).vega.discoverCatalog(id, Boolean(opts.wait)),
@@ -2336,13 +2581,49 @@ function vegaCommand() {
2336
2581
  connector.command("get <type>").description("Get a connector type").action(async (type, _opts, cmd) => {
2337
2582
  printJson(await clientFrom(cmd).vega.connectorType(type), outputOptions(cmd));
2338
2583
  });
2584
+ vega.command("sql").description("Run SQL / OpenSearch DSL directly against a vega-backend data source").requiredOption("--resource-type <type>", "source type (mysql | postgresql | opensearch | \u2026)").option("--query-type <type>", "query mode: standard | stream").option(
2585
+ "--query <sql>",
2586
+ "SQL string; reference a resource with a {{<resource-id>}} placeholder"
2587
+ ).option("--stream-size <n>", "streaming batch size (100\u201310000)", int12).option("--query-timeout <s>", "query timeout in seconds (1\u20133600)", int12).option(
2588
+ "-d, --data <json>",
2589
+ "full request body as JSON (advanced; wins over --query/--resource-type)"
2590
+ ).action(async (opts, cmd) => {
2591
+ let body;
2592
+ if (opts.data) {
2593
+ try {
2594
+ body = JSON.parse(opts.data);
2595
+ } catch {
2596
+ throw new InputError("--data must be valid JSON");
2597
+ }
2598
+ } else {
2599
+ if (!opts.query) {
2600
+ throw new InputError("Provide --query (and optionally --resource-type), or --data.");
2601
+ }
2602
+ body = {
2603
+ query: opts.query,
2604
+ resource_type: opts.resourceType,
2605
+ ...opts.queryType ? { query_type: opts.queryType } : {},
2606
+ ...opts.streamSize !== void 0 ? { stream_size: opts.streamSize } : {},
2607
+ ...opts.queryTimeout !== void 0 ? { query_timeout: opts.queryTimeout } : {}
2608
+ };
2609
+ }
2610
+ printJson(await clientFrom(cmd).vega.sql(body), outputOptions(cmd));
2611
+ });
2339
2612
  const resource = vega.command("resource").description("Vega-backend resources");
2340
- resource.command("list").description("List resources").option("--datasource-id <id>", "filter by catalog/datasource id").option("--catalog-id <id>", "alias of --datasource-id").option("--type <category>", "resource category").option("--category <category>", "alias of --type").option("--limit <n>", "page size", (v) => Number.parseInt(v, 10), DEFAULT_LIST_LIMIT).action(async (opts, cmd) => {
2613
+ resource.command("list").description("List resources").option("--datasource-id <id>", "filter by catalog/datasource id").option("--catalog-id <id>", "alias of --datasource-id").option("--type <category>", "resource category").option("--category <category>", "alias of --type").option("--status <status>", "filter by status").option("--database <name>", "filter by database").option("--limit <n>", "page size", (v) => Number.parseInt(v, 10), DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int12, 0).option("--include-extensions", "include all extension key/value pairs").option("--include-extension-keys <keys>", "include selected extension keys").option("--extension <k=v,...>", "filter by extension key/value pairs").option("--sort <field>", "sort field: name | create_time | update_time").option("--direction <dir>", "sort direction: asc | desc").action(async (opts, cmd) => {
2341
2614
  printJson(
2342
2615
  await clientFrom(cmd).resource.list({
2343
2616
  datasourceId: opts.datasourceId ?? opts.catalogId,
2344
2617
  category: opts.type ?? opts.category,
2345
- limit: opts.limit
2618
+ status: opts.status,
2619
+ database: opts.database,
2620
+ limit: opts.limit,
2621
+ offset: opts.offset,
2622
+ includeExtensions: opts.includeExtensions,
2623
+ includeExtensionKeys: opts.includeExtensionKeys,
2624
+ extensionPairs: parsePairs2(opts.extension),
2625
+ sort: opts.sort,
2626
+ direction: opts.direction
2346
2627
  }),
2347
2628
  outputOptions(cmd)
2348
2629
  );
@@ -2360,16 +2641,25 @@ function vegaCommand() {
2360
2641
  dataset.command("build <resource-id>").description("Build a resource's index (creates a BuildTask)").requiredOption("--mode <mode>", "build mode: batch | streaming").option("--embedding-fields <list>", "comma-separated fields to vectorize").option(
2361
2642
  "--build-key-fields <list>",
2362
2643
  "comma-separated key fields (batch: time; streaming: row id)"
2363
- ).option("--embedding-model <id>", "embedding model id (default if omitted)").option("--model-dimensions <n>", "vector dimensions", (v) => Number.parseInt(v, 10)).option("--wait", "poll until the build reaches a terminal state").option("--timeout <s>", "wait timeout in seconds", (v) => Number.parseInt(v, 10), 300).action(async (resourceId, _opts, cmd) => {
2644
+ ).option("--embedding-model <id>", "default embedding model name/id").option("--fulltext-fields <list>", "comma-separated fields for fulltext index").option("--fulltext-analyzer <name>", "fulltext analyzer").option("--execute-type <type>", "batch execution type: incremental | full").option("--wait", "poll until the build reaches a terminal state").option("--timeout <s>", "wait timeout in seconds", (v) => Number.parseInt(v, 10), 300).action(async (resourceId, _opts, cmd) => {
2364
2645
  const o = cmd.optsWithGlobals();
2646
+ const embeddingFields = csv(o.embeddingFields);
2647
+ const buildKeyFields = csv(o.buildKeyFields);
2648
+ const fulltextFields = csv(o.fulltextFields);
2649
+ if (embeddingFields || buildKeyFields || o.embeddingModel || fulltextFields || o.fulltextAnalyzer) {
2650
+ await clientFrom(cmd).resource.configureIndex(resourceId, {
2651
+ embeddingFields,
2652
+ buildKeyFields,
2653
+ embeddingModel: o.embeddingModel,
2654
+ fulltextFields,
2655
+ fulltextAnalyzer: o.fulltextAnalyzer
2656
+ });
2657
+ }
2365
2658
  const task = await clientFrom(cmd).vega.build(
2366
2659
  {
2367
2660
  resource_id: resourceId,
2368
2661
  mode: o.mode,
2369
- embedding_fields: csv(o.embeddingFields),
2370
- build_key_fields: csv(o.buildKeyFields),
2371
- embedding_model: o.embeddingModel,
2372
- model_dimensions: o.modelDimensions
2662
+ execute_type: o.executeType
2373
2663
  },
2374
2664
  { wait: Boolean(o.wait), timeoutMs: o.timeout * 1e3 }
2375
2665
  );
@@ -2379,15 +2669,50 @@ function vegaCommand() {
2379
2669
  const task = await clientFrom(cmd).vega.buildStatus(taskId);
2380
2670
  printJson(task, outputOptions(cmd));
2381
2671
  });
2672
+ dataset.command("build-list").description("List BuildTasks").option("--limit <n>", "page size", int12, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int12, 0).option("--resource-id <id>", "filter by resource id").option("--catalog-id <id>", "filter by catalog id").option("--status <status>", "comma-separated statuses").option("--active", "only running/init tasks").option("--mode <mode>", "filter by mode: batch | streaming").option("--order-by <field>", "default | created_at | updated_at | status | mode").option("--order <dir>", "asc | desc").action(async (opts, cmd) => {
2673
+ printJson(
2674
+ await clientFrom(cmd).vega.buildTasks({
2675
+ limit: opts.limit,
2676
+ offset: opts.offset,
2677
+ resourceId: opts.resourceId,
2678
+ catalogId: opts.catalogId,
2679
+ status: opts.status,
2680
+ active: opts.active,
2681
+ mode: opts.mode,
2682
+ orderBy: opts.orderBy,
2683
+ order: opts.order
2684
+ }),
2685
+ outputOptions(cmd)
2686
+ );
2687
+ });
2688
+ dataset.command("build-start <task-id>").description("Start a BuildTask").option("--reset", "restart from the beginning").action(async (taskId, opts, cmd) => {
2689
+ printJson(
2690
+ await clientFrom(cmd).vega.startBuildTask(taskId, { reset: opts.reset }),
2691
+ outputOptions(cmd)
2692
+ );
2693
+ });
2694
+ dataset.command("build-stop <task-id>").description("Stop a BuildTask").action(async (taskId, _opts, cmd) => {
2695
+ printJson(await clientFrom(cmd).vega.stopBuildTask(taskId), outputOptions(cmd));
2696
+ });
2697
+ dataset.command("build-delete <ids...>").description("Delete one or more BuildTasks").option("--ignore-missing", "ignore missing task ids").option("--delete-active-index", "delete active indexes too").action(async (ids, opts, cmd) => {
2698
+ printJson(
2699
+ await clientFrom(cmd).vega.deleteBuildTasks(ids, {
2700
+ ignoreMissing: opts.ignoreMissing,
2701
+ deleteActiveIndex: opts.deleteActiveIndex
2702
+ }),
2703
+ outputOptions(cmd)
2704
+ );
2705
+ });
2382
2706
  return group(vega, "AI DATA PLATFORM");
2383
2707
  }
2384
2708
 
2385
2709
  // src/cli.ts
2386
- var program = new Command16();
2710
+ var program = new Command17();
2387
2711
  program.name("openbkn").description("Operate the BKN platform from the CLI").version(package_default.version, "-V, --version", "output the version number").option("--base-url <url>", "platform base URL (env: BKN_BASE_URL)").option("--token <value>", "access token (env: BKN_TOKEN)").option("--user <id|name>", "use specific user credentials (env: BKN_USER)").option("--json", "machine-readable JSON output").option("--compact", "single-line JSON output").option("--full", "human view: show all columns (default trims to the key ones)").option("--biz-domain <s>", "business domain (alias: -bd)").option("-k, --insecure", "skip TLS verification (dev / self-signed only)").showHelpAfterError();
2388
2712
  program.addCommand(authCommand());
2389
2713
  program.addCommand(callCommand());
2390
2714
  program.addCommand(configCommand());
2715
+ program.addCommand(appkeyCommand());
2391
2716
  program.addCommand(vegaCommand());
2392
2717
  program.addCommand(bknCommand());
2393
2718
  program.addCommand(resourceCommand());