@openbkn/bkn-sdk 0.1.1-alpha.16 → 0.1.1-alpha.19

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,7 +35,7 @@ import {
37
35
  use,
38
36
  whoami,
39
37
  writePlatformConfig
40
- } from "./chunk-SH2KLES5.js";
38
+ } from "./chunk-ISJHU26W.js";
41
39
 
42
40
  // src/cli.ts
43
41
  import { Command as Command17 } from "commander";
@@ -45,10 +43,10 @@ import { Command as Command17 } from "commander";
45
43
  // package.json
46
44
  var package_default = {
47
45
  name: "@openbkn/bkn-sdk",
48
- version: "0.1.1-alpha.16",
46
+ version: "0.1.1-alpha.19",
49
47
  description: "Unified TypeScript SDK + CLI for the BKN (Business Knowledge Network) platform.",
50
48
  type: "module",
51
- license: "SEE LICENSE IN LICENSE",
49
+ license: "Apache-2.0",
52
50
  engines: {
53
51
  node: ">=18"
54
52
  },
@@ -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", "NOTICE"],
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
@@ -352,25 +352,6 @@ function readBody(opts) {
352
352
 
353
353
  // src/commands/auth.ts
354
354
  import { Command } from "commander";
355
-
356
- // src/api/eacp-crypto.ts
357
- import {
358
- constants,
359
- createPrivateKey,
360
- createPublicKey,
361
- publicEncrypt
362
- } from "crypto";
363
-
364
- // src/api/admin.ts
365
- async function changePasswordSafe(ctx, account, oldPassword, newPassword) {
366
- await request(ctx, "/api/safe/v1/auth/change-password", {
367
- method: "POST",
368
- body: { account, old_password: oldPassword, new_password: newPassword }
369
- });
370
- return { ok: true };
371
- }
372
-
373
- // src/commands/auth.ts
374
355
  async function resolveAccount(baseUrl, accessToken, insecure, idToken) {
375
356
  const sub = decodeJwt(idToken ?? accessToken)?.sub;
376
357
  if (!sub) return void 0;
@@ -399,25 +380,17 @@ function renderSessions(items) {
399
380
  return lines.join("\n") || "(no saved sessions)";
400
381
  }
401
382
  function registerAuthLeaves(cmd) {
402
- 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(
403
- "--port <n>",
404
- "loopback redirect port for the auth_code flow",
405
- (v) => Number.parseInt(v, 10)
406
- ).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(
407
384
  "--timeout <s>",
408
385
  "device-login wait before timing out",
409
386
  (v) => Number.parseInt(v, 10),
410
387
  120
411
- ).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) => {
412
389
  const g = cmd2.optsWithGlobals();
413
- if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
414
390
  const out = outputOptions(cmd2);
415
391
  const report = (r) => {
416
392
  if (out.json || out.compact) {
417
393
  printJson({ loggedIn: true, ...r }, out);
418
- } else if (r.noAuth) {
419
- process.stdout.write(`Registered ${r.baseUrl ?? url} (no authentication)
420
- `);
421
394
  } else {
422
395
  process.stdout.write(`Logged in to ${r.baseUrl ?? url} as ${r.username ?? r.userId}
423
396
  `);
@@ -425,20 +398,7 @@ function registerAuthLeaves(cmd) {
425
398
  };
426
399
  const token = opts.token ?? g.token;
427
400
  if (token) {
428
- report(attachToken(url, token, { insecure: g.insecure }));
429
- return;
430
- }
431
- if (opts.auth === false) {
432
- report(attachNoAuth(url, { insecure: g.insecure }));
433
- return;
434
- }
435
- const authStatus = await fetchAuthStatus(url);
436
- if (authStatus && !authStatus.enabled) {
437
- process.stderr.write(
438
- `Platform auth is disabled (stack: ${authStatus.stack ?? "none"}) \u2014 registering without auth.
439
- `
440
- );
441
- report(attachNoAuth(url, { insecure: g.insecure }));
401
+ report(attachToken(url, token));
442
402
  return;
443
403
  }
444
404
  let tokens;
@@ -451,7 +411,8 @@ function registerAuthLeaves(cmd) {
451
411
  tokens = await credentialDeviceLogin(url, username, password, {
452
412
  clientId: opts.clientId,
453
413
  audience: opts.audience,
454
- timeoutMs: opts.timeout * 1e3
414
+ timeoutMs: opts.timeout * 1e3,
415
+ insecure: g.insecure
455
416
  });
456
417
  } else {
457
418
  const headless = isHeadless();
@@ -460,6 +421,7 @@ function registerAuthLeaves(cmd) {
460
421
  clientId: opts.clientId,
461
422
  audience: opts.audience,
462
423
  timeoutMs: opts.timeout * 1e3,
424
+ insecure: g.insecure,
463
425
  onPrompt: ({ userCode, verificationUri, verificationUriComplete }) => {
464
426
  const target = verificationUriComplete ?? verificationUri;
465
427
  process.stderr.write(
@@ -478,9 +440,9 @@ User code: ${userCode}
478
440
  }
479
441
  } catch (e) {
480
442
  if (e instanceof Error && /Device auth failed \(404\)/.test(e.message)) {
481
- process.stderr.write("No auth endpoint found \u2014 registering platform without auth.\n");
482
- report(attachNoAuth(url, { insecure: g.insecure }));
483
- 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
+ );
484
446
  }
485
447
  throw e;
486
448
  }
@@ -496,29 +458,28 @@ User code: ${userCode}
496
458
  attachToken(url, tokens.accessToken, {
497
459
  refreshToken: tokens.refreshToken,
498
460
  idToken: tokens.idToken,
499
- insecure: g.insecure,
500
461
  username: account
501
462
  })
502
463
  );
503
464
  });
504
- cmd.command("status").description("Show base URL and whether a token is configured").action((_opts, cmd2) => printJson(status(), outputOptions(cmd2)));
465
+ cmd.command("status").description("Show base URL and whether a token is configured").action(
466
+ (_opts, cmd2) => printJson(status({ user: cmd2.optsWithGlobals().user }), outputOptions(cmd2))
467
+ );
505
468
  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) => {
506
469
  const g = cmd2.optsWithGlobals();
507
- if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
508
- const token = opts.refresh === false ? currentToken() : await currentTokenFresh();
470
+ const token = opts.refresh === false ? currentToken({ user: g.user }) : await currentTokenFresh({ insecure: g.insecure, user: g.user });
509
471
  process.stdout.write(`${token}
510
472
  `);
511
473
  });
512
474
  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) => {
513
475
  const g = cmd2.optsWithGlobals();
514
- if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
515
- const me = whoami();
476
+ const me = whoami({ user: g.user });
516
477
  if (opts.lookup !== false && me.baseUrl && me.sub) {
517
478
  try {
518
479
  const u = await getUserSafe(
519
480
  {
520
481
  baseUrl: me.baseUrl,
521
- token: currentToken(),
482
+ token: currentToken({ user: g.user }),
522
483
  businessDomain: DEFAULT_BUSINESS_DOMAIN,
523
484
  insecure: Boolean(g.insecure)
524
485
  },
@@ -583,9 +544,8 @@ User code: ${userCode}
583
544
  cmd.command("export").description("Export the active session's tokens (for a headless host)").action((_opts, cmd2) => {
584
545
  printJson(exportCreds(), outputOptions(cmd2));
585
546
  });
586
- 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) => {
547
+ 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) => {
587
548
  const g = cmd2.optsWithGlobals();
588
- if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
589
549
  const ctx = resolveContext({
590
550
  baseUrl: url ?? g.baseUrl,
591
551
  token: g.token,
@@ -626,6 +586,12 @@ function authCommand() {
626
586
  // src/commands/admin.ts
627
587
  var int = (v) => Number.parseInt(v, 10);
628
588
  var DEFAULT_RESET_PASSWORD = "openbkn";
589
+ async function importLicenseFile(cmd, file, receipt) {
590
+ const text = readFileSync2(file, "utf8");
591
+ const res = await clientFrom(cmd).admin.licenseImport(text, { receipt });
592
+ printJson(res, outputOptions(cmd));
593
+ if ("stored" in res && res.stored) process.exitCode = 1;
594
+ }
629
595
  function adminCommand() {
630
596
  const admin = new Command2("admin").description("Operator CLI: org, user, role, models, audit");
631
597
  registerAuthLeaves(admin.command("auth").description("Operator authentication"));
@@ -911,6 +877,27 @@ function adminCommand() {
911
877
  );
912
878
  });
913
879
  }
880
+ const license = admin.command("license").description("Cluster license management");
881
+ license.command("show").description("Current license detail (state/edition/expiry/features)").action(async (_opts, cmd) => {
882
+ printJson(await clientFrom(cmd).admin.licenseGet(), outputOptions(cmd));
883
+ });
884
+ license.command("import <file>").description("Import a .lic license file (online deployments auto-activate)").action(async (file, _opts, cmd) => {
885
+ await importLicenseFile(cmd, file, false);
886
+ });
887
+ license.command("receipt <file>").description("Import an offline activation receipt (.lic from the license portal)").action(async (file, _opts, cmd) => {
888
+ await importLicenseFile(cmd, file, true);
889
+ });
890
+ license.command("activate").description("Report the installed license to the issuer (online deployments)").action(async (_opts, cmd) => {
891
+ printJson(await clientFrom(cmd).admin.licenseActivate(), outputOptions(cmd));
892
+ });
893
+ license.command("remove").description("Remove the installed license (back to unactivated)").action(async (_opts, cmd) => {
894
+ printJson(await clientFrom(cmd).admin.licenseRemove(), outputOptions(cmd));
895
+ });
896
+ license.command("fingerprint").description(
897
+ "This cluster's machine code (paste into the license portal for offline activation)"
898
+ ).action(async (_opts, cmd) => {
899
+ printJson(await clientFrom(cmd).admin.licenseFingerprint(), outputOptions(cmd));
900
+ });
914
901
  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) => {
915
902
  printJson(
916
903
  await clientFrom(cmd).admin.auditList({
@@ -1151,7 +1138,7 @@ function appkeyCommand() {
1151
1138
  import { Command as Command5 } from "commander";
1152
1139
 
1153
1140
  // src/utils/bkn-validate.ts
1154
- import { existsSync, readFileSync as readFileSync2, readdirSync, statSync } from "fs";
1141
+ import { existsSync, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
1155
1142
  import { join, resolve } from "path";
1156
1143
  var BKN_OBJECT_NAME_MAX_LENGTH = 40;
1157
1144
  function parseFrontmatter(text) {
@@ -1204,7 +1191,7 @@ function validateBknDirectory(dirPath) {
1204
1191
  if (!existsSync(networkPath)) {
1205
1192
  errors.push("Missing network.bkn at the BKN root.");
1206
1193
  } else {
1207
- const fm = parseFrontmatter(readFileSync2(networkPath, "utf8"));
1194
+ const fm = parseFrontmatter(readFileSync3(networkPath, "utf8"));
1208
1195
  if (!fm) errors.push("network.bkn has no frontmatter block.");
1209
1196
  else {
1210
1197
  if (fm.type !== "knowledge_network")
@@ -1216,7 +1203,7 @@ function validateBknDirectory(dirPath) {
1216
1203
  const otIds = /* @__PURE__ */ new Set();
1217
1204
  const otFiles = bknFiles(join(dir, "object_types"));
1218
1205
  for (const file of otFiles) {
1219
- const fm = parseFrontmatter(readFileSync2(file, "utf8"));
1206
+ const fm = parseFrontmatter(readFileSync3(file, "utf8"));
1220
1207
  const rel = file.slice(dir.length + 1);
1221
1208
  if (!fm || fm.type !== "object_type") {
1222
1209
  errors.push(`${rel}: not a valid object_type (missing/wrong frontmatter type).`);
@@ -1236,7 +1223,7 @@ function validateBknDirectory(dirPath) {
1236
1223
  }
1237
1224
  const rtFiles = bknFiles(join(dir, "relation_types"));
1238
1225
  for (const file of rtFiles) {
1239
- const text = readFileSync2(file, "utf8");
1226
+ const text = readFileSync3(file, "utf8");
1240
1227
  const fm = parseFrontmatter(text);
1241
1228
  const rel = file.slice(dir.length + 1);
1242
1229
  if (!fm || fm.type !== "relation_type") {
@@ -2106,13 +2093,29 @@ Examples:
2106
2093
  // src/commands/resource.ts
2107
2094
  import { Command as Command12 } from "commander";
2108
2095
  var int9 = (v) => Number.parseInt(v, 10);
2096
+ var parsePairs = (raw) => {
2097
+ if (!raw) return void 0;
2098
+ return raw.split(",").map((part) => {
2099
+ const idx = part.indexOf("=");
2100
+ if (idx < 1) throw new Error("--extension must be key=value[,key=value]");
2101
+ return { key: part.slice(0, idx).trim(), value: part.slice(idx + 1).trim() };
2102
+ }).filter((p) => p.key.length > 0);
2103
+ };
2109
2104
  function resourceCommand() {
2110
2105
  const cmd = new Command12("resource").alias("res").description("Resources \u2014 list, find, get, query, delete");
2111
- 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", int9, DEFAULT_LIST_LIMIT).action(async (opts, cmd2) => {
2106
+ 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) => {
2112
2107
  const data = await clientFrom(cmd2).resource.list({
2113
2108
  datasourceId: opts.catalogId ?? opts.datasourceId,
2114
2109
  category: opts.category ?? opts.type,
2115
- limit: opts.limit
2110
+ status: opts.status,
2111
+ database: opts.database,
2112
+ limit: opts.limit,
2113
+ offset: opts.offset,
2114
+ includeExtensions: opts.includeExtensions,
2115
+ includeExtensionKeys: opts.includeExtensionKeys,
2116
+ extensionPairs: parsePairs(opts.extension),
2117
+ sort: opts.sort,
2118
+ direction: opts.direction
2116
2119
  });
2117
2120
  printJson(data, outputOptions(cmd2));
2118
2121
  });
@@ -2334,11 +2337,11 @@ function toolCommand() {
2334
2337
  }
2335
2338
 
2336
2339
  // src/commands/trace.ts
2337
- import { readFileSync as readFileSync4, writeFileSync } from "fs";
2340
+ import { readFileSync as readFileSync5, writeFileSync } from "fs";
2338
2341
  import { Command as Command15 } from "commander";
2339
2342
 
2340
- // src/trace-ai/schema-validate.ts
2341
- import { readFileSync as readFileSync3 } from "fs";
2343
+ // src/bkn-trace/schema-validate.ts
2344
+ import { readFileSync as readFileSync4 } from "fs";
2342
2345
  import { extname } from "path";
2343
2346
  import yaml from "js-yaml";
2344
2347
  import { z } from "zod";
@@ -2375,7 +2378,7 @@ var DiagnosisRule = z.object({
2375
2378
  params: z.record(z.string(), z.unknown()).optional()
2376
2379
  });
2377
2380
  function parseFile(file) {
2378
- const text = readFileSync3(file, "utf8");
2381
+ const text = readFileSync4(file, "utf8");
2379
2382
  const ext = extname(file).toLowerCase();
2380
2383
  if (ext === ".yaml" || ext === ".yml") return yaml.load(text);
2381
2384
  return JSON.parse(text);
@@ -2414,7 +2417,7 @@ function validateSchemaFile(file, kind) {
2414
2417
  // src/commands/trace.ts
2415
2418
  function traceCommand() {
2416
2419
  const cmd = new Command15("trace").description(
2417
- "Trace AI \u2014 fetch spans, diagnose (symbolic + LLM rubric), scan, eval-set, schema validate"
2420
+ "BKN Trace \u2014 fetch spans, diagnose (symbolic + LLM rubric), scan, eval-set, schema validate"
2418
2421
  );
2419
2422
  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) => {
2420
2423
  printJson(
@@ -2442,7 +2445,7 @@ function traceCommand() {
2442
2445
  });
2443
2446
  const evalSet = cmd.command("eval-set").description("Build + run trace eval sets");
2444
2447
  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) => {
2445
- const raw = JSON.parse(readFileSync4(queriesFile, "utf8"));
2448
+ const raw = JSON.parse(readFileSync5(queriesFile, "utf8"));
2446
2449
  const cases = clientFrom(cmd2).trace.evalSetBuild(raw);
2447
2450
  if (opts.out) {
2448
2451
  writeFileSync(opts.out, JSON.stringify({ cases }, null, 2));
@@ -2452,7 +2455,7 @@ function traceCommand() {
2452
2455
  }
2453
2456
  });
2454
2457
  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) => {
2455
- const raw = JSON.parse(readFileSync4(casesFile, "utf8"));
2458
+ const raw = JSON.parse(readFileSync5(casesFile, "utf8"));
2456
2459
  const cases = clientFrom(cmd2).trace.evalSetBuild(raw);
2457
2460
  const result = await clientFrom(cmd2).trace.evalSetTest(opts.agent, cases, {
2458
2461
  version: opts.version,
@@ -2473,14 +2476,35 @@ function traceCommand() {
2473
2476
  // src/commands/vega.ts
2474
2477
  import { Command as Command16 } from "commander";
2475
2478
  var int12 = (v) => Number.parseInt(v, 10);
2479
+ var parsePairs2 = (raw) => {
2480
+ if (!raw) return void 0;
2481
+ return raw.split(",").map((part) => {
2482
+ const idx = part.indexOf("=");
2483
+ if (idx < 1) throw new InputError("--extension must be key=value[,key=value]");
2484
+ return { key: part.slice(0, idx).trim(), value: part.slice(idx + 1).trim() };
2485
+ }).filter((p) => p.key.length > 0);
2486
+ };
2476
2487
  function vegaCommand() {
2477
2488
  const vega = new Command16("vega").description(
2478
2489
  "Vega observability \u2014 catalog, resources, index build tasks"
2479
2490
  );
2480
2491
  const catalog = vega.command("catalog").description("Catalog entries");
2481
- 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) => {
2492
+ 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) => {
2482
2493
  const o = cmd.optsWithGlobals();
2483
- const data = await clientFrom(cmd).vega.catalogs({ limit: o.limit, offset: o.offset });
2494
+ const data = await clientFrom(cmd).vega.catalogs({
2495
+ limit: o.limit,
2496
+ offset: o.offset,
2497
+ name: o.name,
2498
+ tag: o.tag,
2499
+ type: o.type,
2500
+ enabled: o.enabled === void 0 ? void 0 : o.enabled === "true",
2501
+ healthCheckStatus: o.healthCheckStatus,
2502
+ includeExtensions: o.includeExtensions,
2503
+ includeExtensionKeys: o.includeExtensionKeys,
2504
+ extensionPairs: parsePairs2(o.extension),
2505
+ sort: o.sort,
2506
+ direction: o.direction
2507
+ });
2484
2508
  printJson(data, outputOptions(cmd));
2485
2509
  });
2486
2510
  catalog.command("get <id>").description("Get a catalog by id").action(async (id, _opts, cmd) => {
@@ -2492,21 +2516,41 @@ function vegaCommand() {
2492
2516
  catalog.command("health <ids...>").description("Health-status for one or more catalogs").action(async (ids, _opts, cmd) => {
2493
2517
  printJson(await clientFrom(cmd).vega.catalogHealth(ids), outputOptions(cmd));
2494
2518
  });
2495
- 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) => {
2519
+ 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) => {
2496
2520
  let connectorConfig;
2497
2521
  try {
2498
2522
  connectorConfig = JSON.parse(opts.connectorConfig);
2499
2523
  } catch {
2500
2524
  throw new Error("--connector-config must be valid JSON");
2501
2525
  }
2526
+ const extensions = opts.extensions ? JSON.parse(opts.extensions) : void 0;
2502
2527
  printJson(
2503
2528
  await clientFrom(cmd).vega.createCatalog({
2529
+ id: opts.id,
2504
2530
  name: opts.name,
2505
2531
  connectorType: opts.connectorType,
2506
2532
  connectorConfig,
2507
2533
  tags: opts.tags ? String(opts.tags).split(",").map((t) => t.trim()).filter(Boolean) : void 0,
2508
2534
  description: opts.description,
2509
- enabled: opts.enabled ? true : void 0
2535
+ enabled: opts.enabled ? true : void 0,
2536
+ internal: opts.internal ? true : void 0,
2537
+ extensions
2538
+ }),
2539
+ outputOptions(cmd)
2540
+ );
2541
+ });
2542
+ 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) => {
2543
+ const connectorConfig = opts.connectorConfig ? JSON.parse(opts.connectorConfig) : void 0;
2544
+ const extensions = opts.extensions ? JSON.parse(opts.extensions) : void 0;
2545
+ printJson(
2546
+ await clientFrom(cmd).vega.updateCatalog(id, {
2547
+ name: opts.name,
2548
+ connectorType: opts.connectorType,
2549
+ connectorConfig,
2550
+ tags: opts.tags ? String(opts.tags).split(",").map((t) => t.trim()).filter(Boolean) : void 0,
2551
+ description: opts.description,
2552
+ enabled: opts.enabled === void 0 ? void 0 : opts.enabled === "true",
2553
+ extensions
2510
2554
  }),
2511
2555
  outputOptions(cmd)
2512
2556
  );
@@ -2514,6 +2558,15 @@ function vegaCommand() {
2514
2558
  catalog.command("enable <id>").description("Enable a catalog (required before discovery)").action(async (id, _opts, cmd) => {
2515
2559
  printJson(await clientFrom(cmd).vega.enableCatalog(id), outputOptions(cmd));
2516
2560
  });
2561
+ catalog.command("disable <id>").description("Disable a catalog").action(async (id, _opts, cmd) => {
2562
+ printJson(await clientFrom(cmd).vega.disableCatalog(id), outputOptions(cmd));
2563
+ });
2564
+ catalog.command("delete <id>").description("Delete a catalog").action(async (id, _opts, cmd) => {
2565
+ printJson(await clientFrom(cmd).vega.deleteCatalog(id), outputOptions(cmd));
2566
+ });
2567
+ catalog.command("test-connection <id>").description("Test a catalog connection").action(async (id, _opts, cmd) => {
2568
+ printJson(await clientFrom(cmd).vega.testCatalogConnection(id), outputOptions(cmd));
2569
+ });
2517
2570
  catalog.command("discover <id>").description("Trigger catalog resource discovery").option("--wait", "wait for discovery to complete").action(async (id, opts, cmd) => {
2518
2571
  printJson(
2519
2572
  await clientFrom(cmd).vega.discoverCatalog(id, Boolean(opts.wait)),
@@ -2527,10 +2580,7 @@ function vegaCommand() {
2527
2580
  connector.command("get <type>").description("Get a connector type").action(async (type, _opts, cmd) => {
2528
2581
  printJson(await clientFrom(cmd).vega.connectorType(type), outputOptions(cmd));
2529
2582
  });
2530
- vega.command("sql").description("Run SQL / OpenSearch DSL directly against a vega-backend data source").option(
2531
- "--resource-type <type>",
2532
- "source type (mysql | postgresql | opensearch | \u2026); optional \u2014 inferred from the {{<id>}} placeholder"
2533
- ).option(
2583
+ 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(
2534
2584
  "--query <sql>",
2535
2585
  "SQL string; reference a resource with a {{<resource-id>}} placeholder"
2536
2586
  ).option("--stream-size <n>", "streaming batch size (100\u201310000)", int12).option("--query-timeout <s>", "query timeout in seconds (1\u20133600)", int12).option(
@@ -2550,9 +2600,8 @@ function vegaCommand() {
2550
2600
  }
2551
2601
  body = {
2552
2602
  query: opts.query,
2553
- // Optional — the backend infers the type from the {{<id>}} placeholder's
2554
- // catalog connector when omitted.
2555
- ...opts.resourceType ? { resource_type: opts.resourceType } : {},
2603
+ resource_type: opts.resourceType,
2604
+ ...opts.queryType ? { query_type: opts.queryType } : {},
2556
2605
  ...opts.streamSize !== void 0 ? { stream_size: opts.streamSize } : {},
2557
2606
  ...opts.queryTimeout !== void 0 ? { query_timeout: opts.queryTimeout } : {}
2558
2607
  };
@@ -2560,12 +2609,20 @@ function vegaCommand() {
2560
2609
  printJson(await clientFrom(cmd).vega.sql(body), outputOptions(cmd));
2561
2610
  });
2562
2611
  const resource = vega.command("resource").description("Vega-backend resources");
2563
- 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) => {
2612
+ 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) => {
2564
2613
  printJson(
2565
2614
  await clientFrom(cmd).resource.list({
2566
2615
  datasourceId: opts.datasourceId ?? opts.catalogId,
2567
2616
  category: opts.type ?? opts.category,
2568
- limit: opts.limit
2617
+ status: opts.status,
2618
+ database: opts.database,
2619
+ limit: opts.limit,
2620
+ offset: opts.offset,
2621
+ includeExtensions: opts.includeExtensions,
2622
+ includeExtensionKeys: opts.includeExtensionKeys,
2623
+ extensionPairs: parsePairs2(opts.extension),
2624
+ sort: opts.sort,
2625
+ direction: opts.direction
2569
2626
  }),
2570
2627
  outputOptions(cmd)
2571
2628
  );
@@ -2583,16 +2640,25 @@ function vegaCommand() {
2583
2640
  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(
2584
2641
  "--build-key-fields <list>",
2585
2642
  "comma-separated key fields (batch: time; streaming: row id)"
2586
- ).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) => {
2643
+ ).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) => {
2587
2644
  const o = cmd.optsWithGlobals();
2645
+ const embeddingFields = csv(o.embeddingFields);
2646
+ const buildKeyFields = csv(o.buildKeyFields);
2647
+ const fulltextFields = csv(o.fulltextFields);
2648
+ if (embeddingFields || buildKeyFields || o.embeddingModel || fulltextFields || o.fulltextAnalyzer) {
2649
+ await clientFrom(cmd).resource.configureIndex(resourceId, {
2650
+ embeddingFields,
2651
+ buildKeyFields,
2652
+ embeddingModel: o.embeddingModel,
2653
+ fulltextFields,
2654
+ fulltextAnalyzer: o.fulltextAnalyzer
2655
+ });
2656
+ }
2588
2657
  const task = await clientFrom(cmd).vega.build(
2589
2658
  {
2590
2659
  resource_id: resourceId,
2591
2660
  mode: o.mode,
2592
- embedding_fields: csv(o.embeddingFields),
2593
- build_key_fields: csv(o.buildKeyFields),
2594
- embedding_model: o.embeddingModel,
2595
- model_dimensions: o.modelDimensions
2661
+ execute_type: o.executeType
2596
2662
  },
2597
2663
  { wait: Boolean(o.wait), timeoutMs: o.timeout * 1e3 }
2598
2664
  );
@@ -2602,6 +2668,40 @@ function vegaCommand() {
2602
2668
  const task = await clientFrom(cmd).vega.buildStatus(taskId);
2603
2669
  printJson(task, outputOptions(cmd));
2604
2670
  });
2671
+ 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) => {
2672
+ printJson(
2673
+ await clientFrom(cmd).vega.buildTasks({
2674
+ limit: opts.limit,
2675
+ offset: opts.offset,
2676
+ resourceId: opts.resourceId,
2677
+ catalogId: opts.catalogId,
2678
+ status: opts.status,
2679
+ active: opts.active,
2680
+ mode: opts.mode,
2681
+ orderBy: opts.orderBy,
2682
+ order: opts.order
2683
+ }),
2684
+ outputOptions(cmd)
2685
+ );
2686
+ });
2687
+ dataset.command("build-start <task-id>").description("Start a BuildTask").option("--reset", "restart from the beginning").action(async (taskId, opts, cmd) => {
2688
+ printJson(
2689
+ await clientFrom(cmd).vega.startBuildTask(taskId, { reset: opts.reset }),
2690
+ outputOptions(cmd)
2691
+ );
2692
+ });
2693
+ dataset.command("build-stop <task-id>").description("Stop a BuildTask").action(async (taskId, _opts, cmd) => {
2694
+ printJson(await clientFrom(cmd).vega.stopBuildTask(taskId), outputOptions(cmd));
2695
+ });
2696
+ 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) => {
2697
+ printJson(
2698
+ await clientFrom(cmd).vega.deleteBuildTasks(ids, {
2699
+ ignoreMissing: opts.ignoreMissing,
2700
+ deleteActiveIndex: opts.deleteActiveIndex
2701
+ }),
2702
+ outputOptions(cmd)
2703
+ );
2704
+ });
2605
2705
  return group(vega, "AI DATA PLATFORM");
2606
2706
  }
2607
2707