@openbkn/bkn-sdk 0.1.4 → 0.1.5-rc.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
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ BuildTaskExecuteType,
3
4
  BuildTaskSort,
4
5
  BuildTaskStatus,
5
6
  DEFAULT_BUSINESS_DOMAIN,
@@ -9,6 +10,7 @@ import {
9
10
  DiscoverStrategy,
10
11
  DiscoverTaskSort,
11
12
  DiscoverTaskTriggerType,
13
+ DryRunSignal,
12
14
  HttpError,
13
15
  InputError,
14
16
  SemanticUnderstandingApplyMode,
@@ -27,6 +29,7 @@ import {
27
29
  decodeJwt,
28
30
  deletePlatform,
29
31
  deviceLogin,
32
+ enableDryRun,
30
33
  exportCreds,
31
34
  filesUnder,
32
35
  formatError,
@@ -50,24 +53,25 @@ import {
50
53
  stringifyBigIntJSON,
51
54
  switchUser,
52
55
  toExitCode,
56
+ trimTrailingSlashes,
53
57
  updatePlatformConfig,
54
58
  use,
55
59
  validateFixturePath,
56
60
  whoami
57
- } from "./chunk-Z2NRTUB3.js";
61
+ } from "./chunk-WIYRGBAB.js";
58
62
 
59
- // src/cli.ts
63
+ // src/cli-program.ts
60
64
  import { Command as Command16 } from "commander";
61
65
 
62
66
  // package.json
63
67
  var package_default = {
64
68
  name: "@openbkn/bkn-sdk",
65
- version: "0.1.4",
69
+ version: "0.1.5-rc.1",
66
70
  description: "Unified TypeScript SDK + CLI for the BKN (Business Knowledge Network) platform.",
67
71
  type: "module",
68
72
  license: "Apache-2.0",
69
73
  engines: {
70
- node: ">=24.19.0"
74
+ node: ">=22.19.0"
71
75
  },
72
76
  bin: {
73
77
  openbkn: "./dist/cli.js"
@@ -94,6 +98,7 @@ var package_default = {
94
98
  dev: "tsup --watch",
95
99
  typecheck: "tsc --noEmit",
96
100
  "check:deps": "node scripts/check-no-self-dep.mjs",
101
+ "capture:returns": "node scripts/capture-returns.mjs",
97
102
  lint: "npm run check:deps && biome check . && tsc --noEmit",
98
103
  format: "biome format --write .",
99
104
  test: "vitest run",
@@ -131,14 +136,72 @@ import { Command as Command2 } from "commander";
131
136
 
132
137
  // src/help/grouped-help.ts
133
138
  var GROUP = /* @__PURE__ */ Symbol("openbkn.group");
139
+ var GUIDE = /* @__PURE__ */ Symbol("openbkn.guide");
134
140
  var DEFAULT_GROUP = "COMMANDS";
135
141
  function group(cmd, name) {
136
142
  cmd[GROUP] = name;
137
143
  return cmd;
138
144
  }
145
+ function guide(cmd, text) {
146
+ cmd[GUIDE] = text;
147
+ return cmd;
148
+ }
149
+ var SECTION_ORDER = ["GROUPS", "READ", "RUN", "WRITE"];
150
+ function groupChildren(parent, sections) {
151
+ for (const [section, names] of Object.entries(sections)) {
152
+ for (const name of names) {
153
+ const child = parent.commands.find((c) => c.name() === name);
154
+ if (child) group(child, section);
155
+ }
156
+ }
157
+ }
158
+ var VERB_SECTIONS = [
159
+ [
160
+ /^(list|get|show|find|files|history|members|roles|tree|names|content|read-file|status|whoami|users|detail|spans|graph|health|resources|search|market|market-get|stats|export|pull|validate-fixture)$/,
161
+ "READ"
162
+ ],
163
+ [
164
+ /^(query|execute|debug|run|test|chat|embeddings|rerank|diagnose|scan|discover|build|dry-run|validate|token|download|install|call|sql|receipt|fingerprint|test-connection|test-connection-config|attempt|retry|start|resume|ensure-current|create-new-generation|close|complete|fail|cancel|handoff|operations|build-status|build-list)$/,
165
+ "RUN"
166
+ ],
167
+ [
168
+ /^(create|update|delete|add|edit|remove|set|register|upload|publish|unpublish|republish|import|activate|login|logout|use|switch|change-password|enable|disable|push|assign-role|revoke-role|add-member|remove-member|reset-password|grant-perm|revoke-perm|add-members|remove-members|set-status|regenerate|revoke|set-bd|list-bd|build-start|build-stop|build-delete|publish-history|update-metadata|update-package|create-from-catalog|export-config)$/,
169
+ "WRITE"
170
+ ]
171
+ ];
172
+ function autoGroup(parent) {
173
+ for (const child of parent.commands) {
174
+ if (child.name().startsWith("help")) continue;
175
+ if (child[GROUP] !== void 0) continue;
176
+ if (child.commands.filter((c) => !c.name().startsWith("help")).length > 0) {
177
+ group(child, "GROUPS");
178
+ continue;
179
+ }
180
+ const verb = VERB_SECTIONS.find(([re]) => re.test(child.name()));
181
+ if (verb) group(child, verb[1]);
182
+ }
183
+ }
184
+ function rankOf(name) {
185
+ if (name === DEFAULT_GROUP) return Number.MAX_SAFE_INTEGER;
186
+ const i = SECTION_ORDER.indexOf(name);
187
+ return i === -1 ? SECTION_ORDER.length : i;
188
+ }
139
189
  function groupOf(cmd) {
140
190
  return cmd[GROUP] ?? DEFAULT_GROUP;
141
191
  }
192
+ var SECTION_MEANINGS = {
193
+ GROUPS: "nested command groups \u2014 one level deeper",
194
+ READ: "changes nothing",
195
+ RUN: "acts without changing configuration (triggers a job, spends a model call, rotates a token)",
196
+ WRITE: "changes platform state \u2014 confirm with a person first",
197
+ [DEFAULT_GROUP]: "not sorted into a section yet"
198
+ };
199
+ function sectionOf(cmd) {
200
+ return groupOf(cmd);
201
+ }
202
+ function guideOf(cmd) {
203
+ return cmd[GUIDE];
204
+ }
142
205
  function formatHelp(cmd, helper) {
143
206
  const out = [];
144
207
  const desc = helper.commandDescription(cmd);
@@ -154,7 +217,8 @@ function formatHelp(cmd, helper) {
154
217
  if (bucket) bucket.push(c);
155
218
  else sections.set(g, [c]);
156
219
  }
157
- for (const [name, cmds] of sections) {
220
+ const ordered = [...sections.entries()].map((entry, index) => ({ entry, index })).sort((a, b) => rankOf(a.entry[0]) - rankOf(b.entry[0]) || a.index - b.index).map(({ entry }) => entry);
221
+ for (const [name, cmds] of ordered) {
158
222
  out.push(name);
159
223
  for (const c of cmds) {
160
224
  out.push(` ${helper.subcommandTerm(c).padEnd(width)} ${helper.subcommandDescription(c)}`);
@@ -162,6 +226,8 @@ function formatHelp(cmd, helper) {
162
226
  out.push("");
163
227
  }
164
228
  }
229
+ const extra = guideOf(cmd);
230
+ if (extra) out.push(extra.trim(), "");
165
231
  const opts = helper.visibleOptions(cmd);
166
232
  if (opts.length > 0) {
167
233
  const width = Math.max(...opts.map((o) => helper.optionTerm(o).length));
@@ -174,11 +240,15 @@ function formatHelp(cmd, helper) {
174
240
  return out.join("\n");
175
241
  }
176
242
  function installGroupedHelp(root) {
177
- const apply = (cmd) => {
243
+ const apply = (cmd, path) => {
178
244
  cmd.configureHelp({ formatHelp });
179
- for (const child of cmd.commands) apply(child);
245
+ if (cmd !== root) autoGroup(cmd);
246
+ cmd.showHelpAfterError(
247
+ path.length ? `Run \`openbkn describe ${path.join(" ")}\` to see its arguments and where their ids come from.` : "Run `openbkn describe --depth 1` for every command, or `openbkn --help` for the guide."
248
+ );
249
+ for (const child of cmd.commands) apply(child, [...path, child.name()]);
180
250
  };
181
- apply(root);
251
+ apply(root, []);
182
252
  }
183
253
 
184
254
  // src/utils/org-tree.ts
@@ -206,12 +276,12 @@ function printJson(value, opts = {}) {
206
276
  process.stdout.write("(ok)\n");
207
277
  return;
208
278
  }
209
- const rows = toRows(value);
210
- if (rows) {
211
- const fullColumns = columnsOf(rows).filter((c) => rows.some((r) => stringifyCell(r[c]) !== ""));
212
- const columns = opts.full ? fullColumns : selectColumns(rows);
279
+ const rows2 = toRows(value);
280
+ if (rows2) {
281
+ const fullColumns = columnsOf(rows2).filter((c) => rows2.some((r) => stringifyCell(r[c]) !== ""));
282
+ const columns = opts.full ? fullColumns : selectColumns(rows2);
213
283
  if (columns.length > 0) {
214
- printTable(rows, columns);
284
+ printTable(rows2, columns);
215
285
  const hidden = fullColumns.length - columns.length;
216
286
  if (hidden > 0 && !opts.full) {
217
287
  process.stdout.write(`\u2026 ${hidden} more column(s); use --full or --json for everything
@@ -257,9 +327,9 @@ function toRows(value) {
257
327
  }
258
328
  return null;
259
329
  }
260
- function columnsOf(rows) {
330
+ function columnsOf(rows2) {
261
331
  const seen = [];
262
- for (const row of rows) {
332
+ for (const row of rows2) {
263
333
  for (const k of Object.keys(row)) if (!seen.includes(k)) seen.push(k);
264
334
  }
265
335
  return seen;
@@ -280,16 +350,16 @@ var NOISE_COLS = /* @__PURE__ */ new Set([
280
350
  ]);
281
351
  var isNoiseCol = (c) => NOISE_COLS.has(c) || /_time$/.test(c);
282
352
  var isKeyCol = (c) => /^(id|name|key|title|label)$/i.test(c) || /_(id|name|key)$/i.test(c) || /^(status|state|type|category|mode|enabled|version|branch)$/i.test(c);
283
- function selectColumns(rows) {
353
+ function selectColumns(rows2) {
284
354
  const isObj = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
285
- const kept = columnsOf(rows).filter((c) => {
355
+ const kept = columnsOf(rows2).filter((c) => {
286
356
  if (isNoiseCol(c)) return false;
287
- const vals = rows.map((r) => r[c]);
357
+ const vals = rows2.map((r) => r[c]);
288
358
  if (!vals.some((v) => stringifyCell(v) !== "")) return false;
289
359
  if (vals.every((v) => v === null || v === void 0 || isObj(v))) return false;
290
360
  return true;
291
361
  });
292
- const isLongText = (c) => rows.every((r) => {
362
+ const isLongText = (c) => rows2.every((r) => {
293
363
  const s = stringifyCell(r[c]);
294
364
  return s === "" || s.length >= CELL_MAX - 1;
295
365
  });
@@ -297,12 +367,12 @@ function selectColumns(rows) {
297
367
  const ordered = kept.map((c, i) => ({ c, i, r: rank(c) })).sort((a, b) => a.r - b.r || a.i - b.i).map((x) => x.c);
298
368
  return ordered.slice(0, MAX_COLS);
299
369
  }
300
- function printTable(rows, columns, opts = {}) {
370
+ function printTable(rows2, columns, opts = {}) {
301
371
  if (opts.json || opts.compact) {
302
- printJson(rows, opts);
372
+ printJson(rows2, opts);
303
373
  return;
304
374
  }
305
- const cells = rows.map((row) => columns.map((c) => stringifyCell(row[c])));
375
+ const cells = rows2.map((row) => columns.map((c) => stringifyCell(row[c])));
306
376
  const widths = columns.map(
307
377
  (col, i) => Math.max(displayWidth(col), ...cells.map((r) => displayWidth(r[i] ?? "")))
308
378
  );
@@ -331,7 +401,7 @@ function stringifyCell(v) {
331
401
  // src/utils/prompt.ts
332
402
  import { createInterface } from "readline";
333
403
  function promptLine(query, hidden = false) {
334
- return new Promise((resolve2) => {
404
+ return new Promise((resolve3) => {
335
405
  const rl = createInterface({ input: process.stdin, output: process.stdout });
336
406
  if (hidden) {
337
407
  const mutable = rl;
@@ -342,7 +412,7 @@ function promptLine(query, hidden = false) {
342
412
  rl.question(query, (answer) => {
343
413
  rl.close();
344
414
  if (hidden) process.stdout.write("\n");
345
- resolve2(answer.trim());
415
+ resolve3(answer.trim());
346
416
  });
347
417
  });
348
418
  }
@@ -351,7 +421,7 @@ function promptLine(query, hidden = false) {
351
421
  import { readFileSync } from "fs";
352
422
  function platformOf(o) {
353
423
  const baseUrl = (typeof o.baseUrl === "string" ? o.baseUrl : void 0) ?? process.env.BKN_BASE_URL ?? activePlatform();
354
- return baseUrl?.replace(/\/+$/, "");
424
+ return baseUrl === void 0 ? void 0 : trimTrailingSlashes(baseUrl);
355
425
  }
356
426
  function transientIdentity(o) {
357
427
  return Boolean(o.user || process.env.BKN_USER || o.token || process.env.BKN_TOKEN);
@@ -576,16 +646,16 @@ User code: ${userCode}
576
646
  }
577
647
  const expMs = typeof me.exp === "number" ? me.exp * 1e3 : void 0;
578
648
  const expired = expMs !== void 0 && expMs < Date.now();
579
- const rows = [
649
+ const rows2 = [
580
650
  ["User", String(me.username ?? me.sub ?? "(unknown)")],
581
651
  ...me.name && me.name !== me.username ? [["Name", String(me.name)]] : [],
582
652
  ["ID", String(me.userId ?? me.sub ?? "-")],
583
653
  ["Platform", String(me.baseUrl ?? "-")],
584
654
  ...expMs !== void 0 ? [["Expires", `${new Date(expMs).toISOString()}${expired ? " (expired)" : ""}`]] : []
585
655
  ];
586
- const pad2 = Math.max(...rows.map(([k]) => k.length));
656
+ const pad2 = Math.max(...rows2.map(([k]) => k.length));
587
657
  process.stdout.write(
588
- `${rows.map(([k, v]) => `${k.padEnd(pad2)} ${v}`).join("\n")}
658
+ `${rows2.map(([k, v]) => `${k.padEnd(pad2)} ${v}`).join("\n")}
589
659
  \u2026 use --full or --json for all claims
590
660
  `
591
661
  );
@@ -613,7 +683,7 @@ User code: ${userCode}
613
683
  `);
614
684
  });
615
685
  cmd.command("users <url>").description("List saved users for a platform (* = active)").action((url, _opts, cmd2) => {
616
- const norm = url.replace(/\/+$/, "");
686
+ const norm = trimTrailingSlashes(url);
617
687
  const items = listPlatforms().filter((i) => i.baseUrl === norm);
618
688
  const out = outputOptions(cmd2);
619
689
  if (out.json || out.compact) printJson(items, out);
@@ -657,9 +727,33 @@ User code: ${userCode}
657
727
  });
658
728
  }
659
729
  function authCommand() {
660
- const cmd = new Command("auth").description("Login, session, and token management");
730
+ const cmd = new Command("auth").description("Log in; the token is saved and reused. Start here.");
661
731
  registerAuthLeaves(cmd);
662
- return group(cmd, "AUTHENTICATION & CONFIG");
732
+ groupChildren(cmd, {
733
+ READ: ["status", "whoami", "list", "users"],
734
+ RUN: ["token", "export"],
735
+ WRITE: ["login", "logout", "use", "switch", "delete", "change-password"]
736
+ });
737
+ guide(
738
+ cmd,
739
+ `WAYS IN
740
+ login <url> -u <user> -p <pass> password, no browser
741
+ login <url> --token <token> a token you already hold (CI)
742
+ login <url> --device device code, for a host with no browser
743
+ login <url> opens a browser; --no-browser prints the URL instead
744
+
745
+ MANY PLATFORMS, MANY USERS
746
+ A session is a platform plus a user. \`use <url>\` changes which platform is
747
+ active; \`switch <url> <user>\` changes which saved user is active on one.
748
+ \`list\` shows both, marking the active pair. \`--user\` on any command borrows a
749
+ saved user for that call alone.
750
+
751
+ TOKENS
752
+ \`token\` prints the access token, refreshing it first if it has expired;
753
+ \`--no-refresh\` prints what is stored. \`export\` hands the whole session to a
754
+ headless host. Both write a secret to stdout.`
755
+ );
756
+ return group(cmd, "SIGN IN & SETTINGS");
663
757
  }
664
758
 
665
759
  // src/commands/admin.ts
@@ -672,10 +766,10 @@ async function importLicenseFile(cmd, file, receipt) {
672
766
  if ("stored" in res && res.stored) process.exitCode = 1;
673
767
  }
674
768
  function adminCommand() {
675
- const admin = new Command2("admin").description("Operator CLI: org, user, role, models, audit");
769
+ const admin = new Command2("admin").description("Orgs, users, roles, license, audit log");
676
770
  registerAuthLeaves(admin.command("auth").description("Operator authentication"));
677
771
  const org = admin.command("org").description("Departments and org structure");
678
- 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) => {
772
+ org.command("list").description("List departments \u2192 {departments, total}").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) => {
679
773
  printJson(
680
774
  await clientFrom(cmd).admin.orgList({
681
775
  role: opts.role,
@@ -736,7 +830,7 @@ function adminCommand() {
736
830
  else console.log(renderOrgTree(tree));
737
831
  });
738
832
  const user = admin.command("user").description("User management");
739
- user.command("list").description("List users").option("--org <id>", "filter by department id").option("--keyword <s>", "filter by name").option("--limit <n>", "page size", int, 100).option("--offset <n>", "page offset", int, 0).action(async (opts, cmd) => {
833
+ user.command("list").description("List users \u2192 {users, total}").option("--org <id>", "filter by department id").option("--keyword <s>", "filter by name").option("--limit <n>", "page size", int, 100).option("--offset <n>", "page offset", int, 0).action(async (opts, cmd) => {
740
834
  printJson(
741
835
  await clientFrom(cmd).admin.userList({
742
836
  orgId: opts.org,
@@ -822,7 +916,7 @@ function adminCommand() {
822
916
  );
823
917
  });
824
918
  const role = admin.command("role").description("Role management");
825
- role.command("list").description("List roles").option("--keyword <s>", "filter by keyword").option("--limit <n>", "page size", int, 100).option("--offset <n>", "page offset", int, 0).option("--source <s>", "role source filter (business | user)").action(async (opts, cmd) => {
919
+ role.command("list").description("List roles \u2192 {roles}").option("--keyword <s>", "filter by keyword").option("--limit <n>", "page size", int, 100).option("--offset <n>", "page offset", int, 0).option("--source <s>", "role source filter (business | user)").action(async (opts, cmd) => {
826
920
  printJson(
827
921
  await clientFrom(cmd).admin.roleList({ keyword: opts.keyword, limit: opts.limit }),
828
922
  outputOptions(cmd)
@@ -924,7 +1018,10 @@ function adminCommand() {
924
1018
  m.command("get <modelid>").description(`Get a ${kind} model`).action(async (id, _opts, cmd) => {
925
1019
  printJson(await clientFrom(cmd).models[ns].get(id), outputOptions(cmd));
926
1020
  });
927
- const add = m.command("add").description(`Register a ${kind} model (granular flags or --body/--body-file)`).option("--name <s>", "model name").option("--api-model <s>", "upstream API model id").option("--api-key <s>", "upstream API key").option("--body <json>", "model config JSON (overrides flags)").option("--body-file <path>", "read config JSON from a file");
1021
+ const add = m.command("add").description(`Register a ${kind} model (granular flags or --body/--body-file)`).option("--name <s>", "model name").option("--api-model <s>", "upstream API model id").option("--api-key <s>", "upstream API key").option(
1022
+ "--body <json>",
1023
+ "model config JSON (overrides flags) \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (mf-model-manager)"
1024
+ ).option("--body-file <path>", "read config JSON from a file");
928
1025
  if (isLlm) {
929
1026
  add.option("--series <s>", "model series").option("--api-base <url>", "upstream API base URL").option("--icon <url>", "icon URL");
930
1027
  } else {
@@ -933,7 +1030,10 @@ function adminCommand() {
933
1030
  add.action(async (opts, cmd) => {
934
1031
  printJson(await clientFrom(cmd).models[ns].add(modelBody(opts)), outputOptions(cmd));
935
1032
  });
936
- const edit = m.command("edit <modelid>").description(`Edit a ${kind} model (granular flags or --body/--body-file)`).option("--name <s>", "model name").option("--body <json>", "model config JSON (overrides flags)").option("--body-file <path>", "read config JSON from a file");
1033
+ const edit = m.command("edit <modelid>").description(`Edit a ${kind} model (granular flags or --body/--body-file)`).option("--name <s>", "model name").option(
1034
+ "--body <json>",
1035
+ "model config JSON (overrides flags) \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (mf-model-manager)"
1036
+ ).option("--body-file <path>", "read config JSON from a file");
937
1037
  if (isLlm) {
938
1038
  edit.option("--icon <url>", "icon URL");
939
1039
  } else {
@@ -948,7 +1048,10 @@ function adminCommand() {
948
1048
  m.command("delete <modelid...>").description(`Delete ${kind} model(s)`).action(async (ids, _opts, cmd) => {
949
1049
  printJson(await clientFrom(cmd).models[ns].delete(ids), outputOptions(cmd));
950
1050
  });
951
- m.command("test <modelid>").description(`Test a ${kind} model`).option("--body <json>", "test request JSON").option("--body-file <path>", "read test request JSON from a file").action(async (id, opts, cmd) => {
1051
+ m.command("test <modelid>").description(`Test a ${kind} model`).option(
1052
+ "--body <json>",
1053
+ "test request JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (mf-model-manager)"
1054
+ ).option("--body-file <path>", "read test request JSON from a file").action(async (id, opts, cmd) => {
952
1055
  const body = opts.body || opts.bodyFile ? readBody(opts) : {};
953
1056
  printJson(
954
1057
  await clientFrom(cmd).models[ns].test({ model_id: id, ...body }),
@@ -997,10 +1100,12 @@ function adminCommand() {
997
1100
  if (key !== "baseUrl") {
998
1101
  throw new Error(`Unknown config key: ${key} (only baseUrl supported)`);
999
1102
  }
1000
- setActivePlatform(value.replace(/\/+$/, ""));
1103
+ setActivePlatform(trimTrailingSlashes(value));
1001
1104
  printJson({ ok: true, baseUrl: value }, outputOptions(cmd));
1002
1105
  });
1003
- admin.command("call <url>").description("Operator API passthrough (curl-style; auto-injected auth)").option("-X, --request <method>", "HTTP method").option(
1106
+ admin.command("call <url>").description(
1107
+ "Operator API passthrough (curl-style; auto-injected auth) \u2014 paths at https://openbkn-ai.github.io/bkn-foundry/"
1108
+ ).option("-X, --request <method>", "HTTP method").option(
1004
1109
  "-H, --header <header>",
1005
1110
  'extra header "Name: value" (repeatable)',
1006
1111
  (v, a) => {
@@ -1030,125 +1135,16 @@ function adminCommand() {
1030
1135
  }
1031
1136
  if (res.status >= 400) process.exitCode = 1;
1032
1137
  });
1033
- return group(admin, "OPERATOR");
1034
- }
1035
-
1036
- // src/commands/agent.ts
1037
- import { Command as Command3 } from "commander";
1038
- var int2 = (v) => Number.parseInt(v, 10);
1039
- function agentCommand() {
1040
- const cmd = new Command3("agent").description(
1041
- "[DEPRECATED] Decision Agent \u2014 CRUD, chat, sessions, publish (being phased out)"
1042
- );
1043
- cmd.hook("preAction", () => {
1044
- process.stderr.write(
1045
- "\u26A0\uFE0F `openbkn agent` is deprecated and may be removed in a future release.\n"
1046
- );
1047
- });
1048
- 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) => {
1049
- const data = await clientFrom(cmd2).agents.list({
1050
- name: opts.name,
1051
- limit: opts.limit,
1052
- offset: opts.offset,
1053
- categoryId: opts.categoryId
1054
- });
1055
- printJson(data, outputOptions(cmd2));
1056
- });
1057
- cmd.command("personal-list").description("List personal-space agents").option("--name <s>", "filter by name").option("--limit <n>", "page size", int2, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int2, 0).action(async (opts, cmd2) => {
1058
- const data = await clientFrom(cmd2).agents.personalList({
1059
- name: opts.name,
1060
- limit: opts.limit,
1061
- offset: opts.offset
1062
- });
1063
- printJson(data, outputOptions(cmd2));
1064
- });
1065
- cmd.command("category-list").description("List agent categories").action(async (_opts, cmd2) => {
1066
- printJson(await clientFrom(cmd2).agents.categoryList(), outputOptions(cmd2));
1067
- });
1068
- cmd.command("template-list").description("List published agent templates").option("--name <s>", "filter by name").option("--limit <n>", "page size", int2, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int2, 0).action(async (opts, cmd2) => {
1069
- const data = await clientFrom(cmd2).agents.templateList({
1070
- name: opts.name,
1071
- limit: opts.limit,
1072
- offset: opts.offset
1073
- });
1074
- printJson(data, outputOptions(cmd2));
1075
- });
1076
- cmd.command("template-get <id>").description("Get a published agent template").action(async (id, _opts, cmd2) => {
1077
- printJson(await clientFrom(cmd2).agents.templateGet(id), outputOptions(cmd2));
1078
- });
1079
- cmd.command("get <id>").description("Get agent details").action(async (id, _opts, cmd2) => {
1080
- printJson(await clientFrom(cmd2).agents.get(id), outputOptions(cmd2));
1081
- });
1082
- cmd.command("get-by-key <key>").description("Get an agent by key").action(async (key, _opts, cmd2) => {
1083
- printJson(await clientFrom(cmd2).agents.getByKey(key), outputOptions(cmd2));
1084
- });
1085
- cmd.command("create").description("Create an agent (--body-file <json> or --body '<json>')").option("--body <json>", "agent definition JSON").option("--body-file <path>", "read agent definition JSON from a file").action(async (opts, cmd2) => {
1086
- printJson(await clientFrom(cmd2).agents.create(readBody(opts)), outputOptions(cmd2));
1087
- });
1088
- cmd.command("update <id>").description("Update an agent (--body-file <json> or --body '<json>')").option("--body <json>", "agent definition JSON").option("--body-file <path>", "read agent definition JSON from a file").action(async (id, opts, cmd2) => {
1089
- printJson(await clientFrom(cmd2).agents.update(id, readBody(opts)), outputOptions(cmd2));
1138
+ groupChildren(admin, {
1139
+ GROUPS: ["org", "user", "role", "llm", "small-model", "license", "audit", "auth", "config"],
1140
+ RUN: ["call"]
1090
1141
  });
1091
- cmd.command("delete <id>").description("Delete an agent").option("-y, --yes", "skip confirmation").action(async (id, _opts, cmd2) => {
1092
- printJson(await clientFrom(cmd2).agents.delete(id), outputOptions(cmd2));
1093
- });
1094
- cmd.command("publish <id>").description("Publish an agent").action(async (id, _opts, cmd2) => {
1095
- printJson(await clientFrom(cmd2).agents.publish(id), outputOptions(cmd2));
1096
- });
1097
- cmd.command("unpublish <id>").description("Unpublish an agent").action(async (id, _opts, cmd2) => {
1098
- printJson(await clientFrom(cmd2).agents.unpublish(id), outputOptions(cmd2));
1099
- });
1100
- cmd.command("sessions <agent>").description("List conversations for an agent (by agent key)").option("--limit <n>", "page size", int2, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int2, 1).action(async (agentKey, opts, cmd2) => {
1101
- printJson(
1102
- await clientFrom(cmd2).agents.sessions(agentKey, { size: opts.limit, page: opts.page }),
1103
- outputOptions(cmd2)
1104
- );
1105
- });
1106
- cmd.command("history <agent> <conversation-id>").description("Show message history for a conversation").action(async (agentKey, conversationId, _opts, cmd2) => {
1107
- printJson(await clientFrom(cmd2).agents.history(agentKey, conversationId), outputOptions(cmd2));
1108
- });
1109
- cmd.command("chat <agent-id>").description("Chat with an agent (SSE streaming with --stream)").requiredOption("-m, --message <text>", "user message").option("--version <v>", "agent version", "v0").option("--conversation-id <id>", "continue an existing conversation").option("--stream", "stream the reply to stdout as it arrives").action(async (agentId, opts, cmd2) => {
1110
- const client = clientFrom(cmd2);
1111
- if (opts.stream) {
1112
- const res = await client.agents.chat(agentId, opts.message, {
1113
- version: opts.version,
1114
- conversationId: opts.conversationId,
1115
- stream: true,
1116
- onDelta: (t) => process.stdout.write(t)
1117
- });
1118
- process.stdout.write("\n");
1119
- if (res.conversationId) console.error(`conversation_id: ${res.conversationId}`);
1120
- return;
1121
- }
1122
- printJson(
1123
- await client.agents.chat(agentId, opts.message, {
1124
- version: opts.version,
1125
- conversationId: opts.conversationId
1126
- }),
1127
- outputOptions(cmd2)
1128
- );
1129
- });
1130
- cmd.command("trace <conversation-id>").description("Get trace spans for a conversation (agent-scoped alias of `trace get`)").action(async (conversationId, _opts, cmd2) => {
1131
- printJson(await clientFrom(cmd2).trace.spans(conversationId), outputOptions(cmd2));
1132
- });
1133
- const skill = cmd.command("skill").description("Manage skills attached to an agent");
1134
- skill.command("list <agent-id>").description("List skill ids attached to an agent").action(async (agentId, _opts, cmd2) => {
1135
- printJson(await clientFrom(cmd2).agents.skillList(agentId), outputOptions(cmd2));
1136
- });
1137
- skill.command("add <agent-id> <skill-ids>").description("Attach skill(s) to an agent (comma-joined ids)").action(async (agentId, ids, _opts, cmd2) => {
1138
- printJson(await clientFrom(cmd2).agents.skillAdd(agentId, csv(ids) ?? []), outputOptions(cmd2));
1139
- });
1140
- skill.command("remove <agent-id> <skill-ids>").description("Detach skill(s) from an agent (comma-joined ids)").action(async (agentId, ids, _opts, cmd2) => {
1141
- printJson(
1142
- await clientFrom(cmd2).agents.skillRemove(agentId, csv(ids) ?? []),
1143
- outputOptions(cmd2)
1144
- );
1145
- });
1146
- return group(cmd, "DECISION AGENT");
1142
+ return group(admin, "ADMINISTRATION");
1147
1143
  }
1148
1144
 
1149
1145
  // src/commands/appkey.ts
1150
- import { Command as Command4 } from "commander";
1151
- var int3 = (v) => Number.parseInt(v, 10);
1146
+ import { Command as Command3 } from "commander";
1147
+ var int2 = (v) => Number.parseInt(v, 10);
1152
1148
  var DAY_MS = 864e5;
1153
1149
  function printNewKey(created, out) {
1154
1150
  if (out.json || out.compact) {
@@ -1170,13 +1166,13 @@ function printNewKey(created, out) {
1170
1166
  `);
1171
1167
  }
1172
1168
  function appkeyCommand() {
1173
- const appkey = new Command4("appkey").description(
1174
- "AppKeys \u2014 user-issued long-lived credentials (bak_) for the Context Loader"
1169
+ const appkey = new Command3("appkey").description(
1170
+ "Issue long-lived `bak_` keys for scripts and services"
1175
1171
  );
1176
- appkey.command("list").description("List your own AppKeys (no secrets)").action(async (_opts, cmd) => {
1172
+ appkey.command("list").description("List your own AppKeys, no secrets \u2192 {keys}").action(async (_opts, cmd) => {
1177
1173
  printJson(await clientFrom(cmd).appKeys.list(), outputOptions(cmd));
1178
1174
  });
1179
- 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) => {
1175
+ 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)", int2).option("--never-expire", "never expire (wins over --expires-at/--expire-days)").action(async (opts, cmd) => {
1180
1176
  let expiresAt = opts.expiresAt;
1181
1177
  if (opts.expireDays !== void 0) {
1182
1178
  if (!Number.isFinite(opts.expireDays) || opts.expireDays <= 0) {
@@ -1210,11 +1206,18 @@ function appkeyCommand() {
1210
1206
  await clientFrom(cmd).appKeys.adminRevoke(id);
1211
1207
  printJson({ revoked: id }, outputOptions(cmd));
1212
1208
  });
1213
- return group(appkey, "AUTHENTICATION & CONFIG");
1209
+ groupChildren(appkey, {
1210
+ GROUPS: ["admin"],
1211
+ READ: ["list"],
1212
+ WRITE: ["create", "regenerate", "revoke"]
1213
+ });
1214
+ const appkeyAdmin = appkey.commands.find((c) => c.name() === "admin");
1215
+ if (appkeyAdmin) groupChildren(appkeyAdmin, { READ: ["list"], WRITE: ["revoke"] });
1216
+ return group(appkey, "SIGN IN & SETTINGS");
1214
1217
  }
1215
1218
 
1216
1219
  // src/commands/bkn.ts
1217
- import { Command as Command5 } from "commander";
1220
+ import { Command as Command4 } from "commander";
1218
1221
 
1219
1222
  // src/utils/bkn-validate.ts
1220
1223
  import { existsSync, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
@@ -1332,10 +1335,12 @@ function validateBknDirectory(dirPath) {
1332
1335
  }
1333
1336
 
1334
1337
  // src/commands/bkn.ts
1335
- var int4 = (v) => Number.parseInt(v, 10);
1338
+ var int3 = (v) => Number.parseInt(v, 10);
1336
1339
  function bknCommand() {
1337
- const bkn = new Command5("bkn").description("Knowledge networks \u2014 list, query, schema, instances");
1338
- 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) => {
1340
+ const bkn = new Command4("bkn").description(
1341
+ "Knowledge networks: schema, metrics, search, import/export"
1342
+ );
1343
+ 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) => {
1339
1344
  const o = cmd.optsWithGlobals();
1340
1345
  const data = await clientFrom(cmd).kn.list({
1341
1346
  limit: o.limit,
@@ -1354,10 +1359,17 @@ function bknCommand() {
1354
1359
  });
1355
1360
  printJson(data, outputOptions(cmd));
1356
1361
  });
1357
- 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) => {
1362
+ bkn.command("search <kn-id> <query>").description(
1363
+ "Recall instances from a plain sentence \u2014 no object type or field names needed \u2192 {nodes, object_types}"
1364
+ ).option("--object-types <ids>", "pin recall to these object-type ids (comma-separated)").option("--exclude-object-types <ids>", "drop these object-type ids (comma-separated)").option("--concept-groups <names>", "limit recall to these concept groups (comma-separated)").option("--max-object-types <n>", "how many object types may take part", int3).option("--max-instances <n>", "instances per object type", int3).option("--rerank", "re-rank hits with a cross-encoder (needs a rerank model deployed)").option("--no-object-types-detail", "omit the object-type definitions that come with hits").action(async (knId, query, opts, cmd) => {
1358
1365
  const data = await clientFrom(cmd).kn.search(knId, query, {
1359
- maxConcepts: opts.maxConcepts,
1360
- mode: opts.mode
1366
+ objectTypes: csv(opts.objectTypes),
1367
+ excludeObjectTypes: csv(opts.excludeObjectTypes),
1368
+ conceptGroups: csv(opts.conceptGroups),
1369
+ maxObjectTypes: opts.maxObjectTypes,
1370
+ maxInstancesPerType: opts.maxInstances,
1371
+ rerank: opts.rerank,
1372
+ includeObjectTypes: opts.objectTypesDetail === false ? false : void 0
1361
1373
  });
1362
1374
  printJson(data, outputOptions(cmd));
1363
1375
  });
@@ -1375,16 +1387,22 @@ function bknCommand() {
1375
1387
  );
1376
1388
  });
1377
1389
  if (crud) {
1378
- g.command("get <kn-id> <id>").description(`Get ${name}`).action(async (knId, id, _o, cmd) => {
1390
+ g.command("get <kn-id> <id>").description(`Get ${name} \u2192 {entries}`).action(async (knId, id, _o, cmd) => {
1379
1391
  printJson(await clientFrom(cmd).kn[`${crud}Get`](knId, id), outputOptions(cmd));
1380
1392
  });
1381
- g.command("create <kn-id>").description(`Create ${name} (--body / --body-file)`).option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1393
+ g.command("create <kn-id>").description(`Create ${name} (--body / --body-file)`).option(
1394
+ "--body <json>",
1395
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1396
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1382
1397
  printJson(
1383
1398
  await clientFrom(cmd).kn[`${crud}Create`](knId, readBody(opts)),
1384
1399
  outputOptions(cmd)
1385
1400
  );
1386
1401
  });
1387
- g.command("update <kn-id> <id>").description(`Update ${name} (--body / --body-file)`).option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, id, opts, cmd) => {
1402
+ g.command("update <kn-id> <id>").description(`Update ${name} (--body / --body-file)`).option(
1403
+ "--body <json>",
1404
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1405
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, id, opts, cmd) => {
1388
1406
  printJson(
1389
1407
  await clientFrom(cmd).kn[`${crud}Update`](knId, id, readBody(opts)),
1390
1408
  outputOptions(cmd)
@@ -1396,21 +1414,24 @@ function bknCommand() {
1396
1414
  }
1397
1415
  }
1398
1416
  const actionType = bkn.commands.find((c) => c.name() === "action-type");
1399
- actionType?.command("query <kn-id> <at-id>").description("Query an action type (--body / --body-file JSON)").option("--body <json>", "query JSON").option("--body-file <path>", "read query JSON from a file").action(async (knId, atId, opts, cmd) => {
1417
+ actionType?.command("query <kn-id> <at-id>").description("Query an action type (--body / --body-file JSON)").option(
1418
+ "--body <json>",
1419
+ "query JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (ontology-query)"
1420
+ ).option("--body-file <path>", "read query JSON from a file").action(async (knId, atId, opts, cmd) => {
1400
1421
  printJson(
1401
1422
  await clientFrom(cmd).kn.actionTypeQuery(knId, atId, readBody(opts)),
1402
1423
  outputOptions(cmd)
1403
1424
  );
1404
1425
  });
1405
- actionType?.command("execute <kn-id> <at-id>").description("Execute an action type (--body / --body-file envelope JSON)").option("--body <json>", "execution envelope JSON").option("--body-file <path>", "read envelope JSON from a file").action(async (knId, atId, opts, cmd) => {
1426
+ actionType?.command("execute <kn-id> <at-id>").description("Execute an action type (--body / --body-file envelope JSON)").option(
1427
+ "--body <json>",
1428
+ "execution envelope JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (ontology-query)"
1429
+ ).option("--body-file <path>", "read envelope JSON from a file").action(async (knId, atId, opts, cmd) => {
1406
1430
  printJson(
1407
1431
  await clientFrom(cmd).kn.actionTypeExecute(knId, atId, readBody(opts)),
1408
1432
  outputOptions(cmd)
1409
1433
  );
1410
1434
  });
1411
- actionType?.command("inputs <kn-id> <at-id>").description("Get an action type's input schema").action(async (knId, atId, _o, cmd) => {
1412
- printJson(await clientFrom(cmd).kn.actionTypeInputs(knId, atId), outputOptions(cmd));
1413
- });
1414
1435
  actionType?.command("get <kn-id> <at-id>").description("Get an action type").action(async (knId, atId, _o, cmd) => {
1415
1436
  printJson(await clientFrom(cmd).kn.actionTypeGet(knId, atId), outputOptions(cmd));
1416
1437
  });
@@ -1421,7 +1442,10 @@ function bknCommand() {
1421
1442
  printJson(await clientFrom(cmd).kn.get(knId, { exportMode: true }), outputOptions(cmd));
1422
1443
  });
1423
1444
  const objectType = bkn.commands.find((c) => c.name() === "object-type");
1424
- objectType?.command("query <kn-id> <ot-id>").description("Query instances of an object type (--body / --body-file JSON)").option("--body <json>", "query JSON").option("--body-file <path>", "read query JSON from a file").action(async (knId, otId, opts, cmd) => {
1445
+ objectType?.command("query <kn-id> <ot-id>").description("Query instances of an object type (--body / --body-file JSON)").option(
1446
+ "--body <json>",
1447
+ "query JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (ontology-query)"
1448
+ ).option("--body-file <path>", "read query JSON from a file").action(async (knId, otId, opts, cmd) => {
1425
1449
  printJson(
1426
1450
  await clientFrom(cmd).kn.objectTypeQuery(knId, otId, readBody(opts)),
1427
1451
  outputOptions(cmd)
@@ -1430,17 +1454,23 @@ function bknCommand() {
1430
1454
  bkn.command("create <name>").description("Create an (empty) knowledge network").option("--branch <b>", "branch", "main").action(async (name, opts, cmd) => {
1431
1455
  printJson(await clientFrom(cmd).kn.create({ name, branch: opts.branch }), outputOptions(cmd));
1432
1456
  });
1433
- bkn.command("update <kn-id>").description("Update a knowledge network (--body / --body-file)").option("--body <json>", "update body JSON").option("--body-file <path>", "read update body JSON from a file").action(async (knId, opts, cmd) => {
1457
+ bkn.command("update <kn-id>").description("Update a knowledge network (--body / --body-file)").option(
1458
+ "--body <json>",
1459
+ "update body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1460
+ ).option("--body-file <path>", "read update body JSON from a file").action(async (knId, opts, cmd) => {
1434
1461
  printJson(await clientFrom(cmd).kn.update(knId, readBody(opts)), outputOptions(cmd));
1435
1462
  });
1436
1463
  bkn.command("delete <kn-id>").description("Delete a knowledge network").option("-y, --yes", "skip confirmation").action(async (knId, _opts, cmd) => {
1437
1464
  printJson(await clientFrom(cmd).kn.delete(knId), outputOptions(cmd));
1438
1465
  });
1439
- bkn.command("subgraph <kn-id>").description("Query a subgraph (--body / --body-file JSON)").option("--body <json>", "subgraph query JSON").option("--body-file <path>", "read subgraph query JSON from a file").action(async (knId, opts, cmd) => {
1466
+ bkn.command("subgraph <kn-id>").description("Query a subgraph (--body / --body-file JSON)").option(
1467
+ "--body <json>",
1468
+ "subgraph query JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (ontology-query)"
1469
+ ).option("--body-file <path>", "read subgraph query JSON from a file").action(async (knId, opts, cmd) => {
1440
1470
  printJson(await clientFrom(cmd).kn.subgraph(knId, readBody(opts)), outputOptions(cmd));
1441
1471
  });
1442
- const actionLog = bkn.command("action-log").description("Action logs \u2014 list/get/cancel");
1443
- 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) => {
1472
+ const actionLog = bkn.command("action-log").description("Action logs \u2014 list/get/cancel; list pages with search_after");
1473
+ 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) => {
1444
1474
  printJson(
1445
1475
  await clientFrom(cmd).kn.actionLogs(knId, {
1446
1476
  status: opts.status,
@@ -1460,25 +1490,37 @@ function bknCommand() {
1460
1490
  printJson(await clientFrom(cmd).kn.actionExecution(knId, execId), outputOptions(cmd));
1461
1491
  });
1462
1492
  const metric = bkn.command("metric").description("Metrics \u2014 query / dry-run");
1463
- metric.command("query <kn-id> <metric-id>").description("Query a metric's data (--body / --body-file JSON)").option("--body <json>", "query JSON").option("--body-file <path>", "read query JSON from a file").action(async (knId, metricId, opts, cmd) => {
1493
+ metric.command("query <kn-id> <metric-id>").description("Query a metric's data (--body / --body-file JSON)").option(
1494
+ "--body <json>",
1495
+ "query JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (ontology-query)"
1496
+ ).option("--body-file <path>", "read query JSON from a file").action(async (knId, metricId, opts, cmd) => {
1464
1497
  printJson(
1465
1498
  await clientFrom(cmd).kn.metricQuery(knId, metricId, readBody(opts)),
1466
1499
  outputOptions(cmd)
1467
1500
  );
1468
1501
  });
1469
- metric.command("dry-run <kn-id>").description("Dry-run a metric definition (--body / --body-file JSON)").option("--body <json>", "metric definition JSON").option("--body-file <path>", "read metric definition JSON from a file").action(async (knId, opts, cmd) => {
1502
+ metric.command("dry-run <kn-id>").description("Dry-run a metric definition (--body / --body-file JSON)").option(
1503
+ "--body <json>",
1504
+ "metric definition JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (ontology-query)"
1505
+ ).option("--body-file <path>", "read metric definition JSON from a file").action(async (knId, opts, cmd) => {
1470
1506
  printJson(await clientFrom(cmd).kn.metricDryRun(knId, readBody(opts)), outputOptions(cmd));
1471
1507
  });
1472
1508
  metric.command("list <kn-id>").description("List metrics").action(async (knId, _o, cmd) => {
1473
1509
  printJson(await clientFrom(cmd).kn.metricList(knId), outputOptions(cmd));
1474
1510
  });
1475
- metric.command("get <kn-id> <metric-id>").description("Get a metric").action(async (knId, id, _o, cmd) => {
1511
+ metric.command("get <kn-id> <metric-id>").description("Get a metric \u2192 {entries}, since the route takes a list of ids").action(async (knId, id, _o, cmd) => {
1476
1512
  printJson(await clientFrom(cmd).kn.metricGet(knId, id), outputOptions(cmd));
1477
1513
  });
1478
- metric.command("create <kn-id>").description("Create a metric (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1514
+ metric.command("create <kn-id>").description("Create a metric (--body / --body-file)").option(
1515
+ "--body <json>",
1516
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1517
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1479
1518
  printJson(await clientFrom(cmd).kn.metricCreate(knId, readBody(opts)), outputOptions(cmd));
1480
1519
  });
1481
- metric.command("update <kn-id> <metric-id>").description("Update a metric (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, id, opts, cmd) => {
1520
+ metric.command("update <kn-id> <metric-id>").description("Update a metric (--body / --body-file)").option(
1521
+ "--body <json>",
1522
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1523
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, id, opts, cmd) => {
1482
1524
  printJson(
1483
1525
  await clientFrom(cmd).kn.metricUpdate(knId, id, readBody(opts)),
1484
1526
  outputOptions(cmd)
@@ -1487,10 +1529,10 @@ function bknCommand() {
1487
1529
  metric.command("delete <kn-id> <metric-id>").description("Delete a metric").action(async (knId, id, _o, cmd) => {
1488
1530
  printJson(await clientFrom(cmd).kn.metricDelete(knId, id), outputOptions(cmd));
1489
1531
  });
1490
- metric.command("search <kn-id>").description("Search metrics (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1491
- printJson(await clientFrom(cmd).kn.metricSearch(knId, readBody(opts)), outputOptions(cmd));
1492
- });
1493
- metric.command("validate <kn-id>").description("Validate a metric definition (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1532
+ metric.command("validate <kn-id>").description("Validate a metric definition (--body / --body-file)").option(
1533
+ "--body <json>",
1534
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1535
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1494
1536
  printJson(await clientFrom(cmd).kn.metricValidate(knId, readBody(opts)), outputOptions(cmd));
1495
1537
  });
1496
1538
  const cg = bkn.command("concept-group").description("Concept groups \u2014 list/get");
@@ -1500,13 +1542,19 @@ function bknCommand() {
1500
1542
  cg.command("get <kn-id> <cg-id>").description("Get a concept group").action(async (knId, cgId, _o, cmd) => {
1501
1543
  printJson(await clientFrom(cmd).kn.conceptGroup(knId, cgId), outputOptions(cmd));
1502
1544
  });
1503
- cg.command("create <kn-id>").description("Create a concept group (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1545
+ cg.command("create <kn-id>").description("Create a concept group (--body / --body-file)").option(
1546
+ "--body <json>",
1547
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1548
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1504
1549
  printJson(
1505
1550
  await clientFrom(cmd).kn.conceptGroupCreate(knId, readBody(opts)),
1506
1551
  outputOptions(cmd)
1507
1552
  );
1508
1553
  });
1509
- cg.command("update <kn-id> <cg-id>").description("Update a concept group (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, cgId, opts, cmd) => {
1554
+ cg.command("update <kn-id> <cg-id>").description("Update a concept group (--body / --body-file)").option(
1555
+ "--body <json>",
1556
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1557
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, cgId, opts, cmd) => {
1510
1558
  printJson(
1511
1559
  await clientFrom(cmd).kn.conceptGroupUpdate(knId, cgId, readBody(opts)),
1512
1560
  outputOptions(cmd)
@@ -1515,7 +1563,10 @@ function bknCommand() {
1515
1563
  cg.command("delete <kn-id> <cg-id>").description("Delete a concept group").action(async (knId, cgId, _o, cmd) => {
1516
1564
  printJson(await clientFrom(cmd).kn.conceptGroupDelete(knId, cgId), outputOptions(cmd));
1517
1565
  });
1518
- cg.command("add-members <kn-id> <cg-id>").description("Add object types to a concept group (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, cgId, opts, cmd) => {
1566
+ cg.command("add-members <kn-id> <cg-id>").description("Add object types to a concept group (--body / --body-file)").option(
1567
+ "--body <json>",
1568
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1569
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, cgId, opts, cmd) => {
1519
1570
  printJson(
1520
1571
  await clientFrom(cmd).kn.conceptGroupAddMembers(knId, cgId, readBody(opts)),
1521
1572
  outputOptions(cmd)
@@ -1534,19 +1585,28 @@ function bknCommand() {
1534
1585
  sched.command("get <kn-id> <schedule-id>").description("Get an action schedule").action(async (knId, sId, _o, cmd) => {
1535
1586
  printJson(await clientFrom(cmd).kn.actionSchedule(knId, sId), outputOptions(cmd));
1536
1587
  });
1537
- sched.command("create <kn-id>").description("Create an action schedule (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1588
+ sched.command("create <kn-id>").description("Create an action schedule (--body / --body-file)").option(
1589
+ "--body <json>",
1590
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1591
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
1538
1592
  printJson(
1539
1593
  await clientFrom(cmd).kn.actionScheduleCreate(knId, readBody(opts)),
1540
1594
  outputOptions(cmd)
1541
1595
  );
1542
1596
  });
1543
- sched.command("update <kn-id> <schedule-id>").description("Update an action schedule (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, sId, opts, cmd) => {
1597
+ sched.command("update <kn-id> <schedule-id>").description("Update an action schedule (--body / --body-file)").option(
1598
+ "--body <json>",
1599
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1600
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, sId, opts, cmd) => {
1544
1601
  printJson(
1545
1602
  await clientFrom(cmd).kn.actionScheduleUpdate(knId, sId, readBody(opts)),
1546
1603
  outputOptions(cmd)
1547
1604
  );
1548
1605
  });
1549
- sched.command("set-status <kn-id> <schedule-id>").description("Set an action schedule's status (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, sId, opts, cmd) => {
1606
+ sched.command("set-status <kn-id> <schedule-id>").description("Set an action schedule's status (--body / --body-file)").option(
1607
+ "--body <json>",
1608
+ "body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1609
+ ).option("--body-file <path>", "read body JSON from a file").action(async (knId, sId, opts, cmd) => {
1550
1610
  printJson(
1551
1611
  await clientFrom(cmd).kn.actionScheduleSetStatus(knId, sId, readBody(opts)),
1552
1612
  outputOptions(cmd)
@@ -1574,7 +1634,10 @@ function bknCommand() {
1574
1634
  outputOptions(cmd)
1575
1635
  );
1576
1636
  });
1577
- bkn.command("relation-type-paths <kn-id>").description("Query relation-type paths between object types (--body / --body-file JSON)").option("--body <json>", "request JSON").option("--body-file <path>", "read request JSON from a file").action(async (knId, opts, cmd) => {
1637
+ bkn.command("relation-type-paths <kn-id>").description("Query relation-type paths between object types (--body / --body-file JSON)").option(
1638
+ "--body <json>",
1639
+ "request JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (bkn-backend)"
1640
+ ).option("--body-file <path>", "read request JSON from a file").action(async (knId, opts, cmd) => {
1578
1641
  printJson(
1579
1642
  await clientFrom(cmd).kn.relationTypePaths(knId, readBody(opts)),
1580
1643
  outputOptions(cmd)
@@ -1610,17 +1673,73 @@ function bknCommand() {
1610
1673
  printJson(result, outputOptions(cmd));
1611
1674
  if (!result.valid) process.exitCode = 1;
1612
1675
  });
1613
- return group(bkn, "AI DATA PLATFORM");
1676
+ groupChildren(bkn, {
1677
+ GROUPS: [
1678
+ "object-type",
1679
+ "relation-type",
1680
+ "action-type",
1681
+ "metric",
1682
+ "concept-group",
1683
+ "action-log",
1684
+ "action-schedule"
1685
+ ],
1686
+ READ: [
1687
+ "pull",
1688
+ "list",
1689
+ "get",
1690
+ "stats",
1691
+ "export",
1692
+ "search",
1693
+ "subgraph",
1694
+ "relation-type-paths",
1695
+ "resources",
1696
+ "action-execution",
1697
+ "validate"
1698
+ ],
1699
+ WRITE: ["create", "update", "delete", "push", "create-from-catalog"]
1700
+ });
1701
+ groupChildren(metric, {
1702
+ READ: ["list", "get"],
1703
+ RUN: ["query", "dry-run", "validate"],
1704
+ WRITE: ["create", "update", "delete"]
1705
+ });
1706
+ guide(
1707
+ bkn,
1708
+ `WHERE IDS COME FROM
1709
+ \`list\` gives kn ids; \`object-type list <kn-id>\` and \`search <kn-id> "<q>"\` give the rest.
1710
+
1711
+ SEARCH VS THE MCP SIDE
1712
+ \`search\` recalls instance rows from one sentence and ships the object-type definitions
1713
+ needed to read them \u2014 start here when you do not know the schema yet. It only reaches
1714
+ properties indexed for match/knn, so an unindexed object type yields nothing, and no hits
1715
+ comes back as empty nodes with a message rather than an error. For schema alone use
1716
+ \`openbkn context search-schema\`; for a structured filter use \`object-type query\`.
1717
+
1718
+ EDITING SCHEMA AS FILES
1719
+ pull <kn-id> ./dir -> edit -> validate ./dir -> push ./dir
1720
+ \`validate\` is offline and catches structure errors before the upload.
1721
+
1722
+ REQUEST BODIES
1723
+ create/update take a definition; query/execute/dry-run take a query. Both are documented
1724
+ at https://openbkn-ai.github.io/bkn-foundry/ \u2014 definitions under bkn-backend, reads and
1725
+ executions under ontology-query. Each command's --body flag names its own module.
1726
+
1727
+ CREATING FROM DATA
1728
+ create-from-catalog <catalog-id> --name "<n>" builds a network from a Vega catalog,
1729
+ then \`openbkn vega dataset build <resource-id>\` produces the index. There is no
1730
+ whole-network build.`
1731
+ );
1732
+ return group(bkn, "DATA & KNOWLEDGE");
1614
1733
  }
1615
1734
 
1616
1735
  // src/commands/call.ts
1617
- import { Command as Command6 } from "commander";
1736
+ import { Command as Command5 } from "commander";
1618
1737
  function collect(value, prev) {
1619
1738
  prev.push(value);
1620
1739
  return prev;
1621
1740
  }
1622
1741
  function callCommand() {
1623
- 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(
1742
+ const cmd = new Command5("call").alias("curl").description("Call any platform API endpoint directly (auth added)").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(
1624
1743
  "-F, --form <field>",
1625
1744
  "multipart field key=value or key=@file (repeatable)",
1626
1745
  collect,
@@ -1657,18 +1776,30 @@ function callCommand() {
1657
1776
  process.exitCode = 1;
1658
1777
  }
1659
1778
  });
1660
- return group(cmd, "AUTHENTICATION & CONFIG");
1779
+ guide(
1780
+ cmd,
1781
+ `WHEN TO USE THIS
1782
+ Anything the named commands do not cover: services with no command group yet
1783
+ (bkn-agent, execution-factory operators and sandbox functions, MCP registration,
1784
+ skill index builds), and endpoints newer than this CLI.
1785
+
1786
+ FINDING THE PATH
1787
+ Every service's API is documented at https://openbkn-ai.github.io/bkn-foundry/ \u2014
1788
+ read the path and request body there rather than guessing. Auth, business domain
1789
+ and TLS flags are injected the same way as for any other command.`
1790
+ );
1791
+ return group(cmd, "RAW API");
1661
1792
  }
1662
1793
 
1663
1794
  // src/commands/config.ts
1664
- import { Command as Command7 } from "commander";
1795
+ import { Command as Command6 } from "commander";
1665
1796
  function requireActive() {
1666
1797
  const baseUrl = activePlatform();
1667
1798
  if (!baseUrl) throw new InputError("No active platform. Run `openbkn auth login <url>` first.");
1668
1799
  return baseUrl;
1669
1800
  }
1670
1801
  function configCommand() {
1671
- const config = new Command7("config").description("Per-platform CLI configuration");
1802
+ const config = new Command6("config").description("Remember a platform URL / business domain");
1672
1803
  config.command("show").description("Show the active platform and business domain").action((_opts, cmd) => {
1673
1804
  const baseUrl = activePlatform();
1674
1805
  printJson(
@@ -1681,7 +1812,7 @@ function configCommand() {
1681
1812
  });
1682
1813
  config.command("set <key> <value>").description("Set a config value (baseUrl | businessDomain)").action((key, value, _opts, cmd) => {
1683
1814
  if (key === "baseUrl") {
1684
- setActivePlatform(value.replace(/\/+$/, ""));
1815
+ setActivePlatform(trimTrailingSlashes(value));
1685
1816
  } else if (key === "businessDomain") {
1686
1817
  updatePlatformConfig(requireActive(), { businessDomain: value });
1687
1818
  } else {
@@ -1697,12 +1828,13 @@ function configCommand() {
1697
1828
  config.command("list-bd").description("List business domains (requires login)").action(() => {
1698
1829
  throw new InputError("Not yet implemented \u2014 requires backend business-domains API.");
1699
1830
  });
1700
- return group(config, "AUTHENTICATION & CONFIG");
1831
+ groupChildren(config, { READ: ["show", "list-bd"], WRITE: ["set", "set-bd"] });
1832
+ return group(config, "SIGN IN & SETTINGS");
1701
1833
  }
1702
1834
 
1703
1835
  // src/commands/context.ts
1704
- import { Command as Command8 } from "commander";
1705
- var int5 = (v) => Number.parseInt(v, 10);
1836
+ import { Command as Command7 } from "commander";
1837
+ var int4 = (v) => Number.parseInt(v, 10);
1706
1838
  var collectArg = (v, prev) => {
1707
1839
  prev.push(v);
1708
1840
  return prev;
@@ -1740,33 +1872,44 @@ function printToolList(res, out) {
1740
1872
  printJson(res, out);
1741
1873
  return;
1742
1874
  }
1743
- const rows = arr.map((t) => ({
1875
+ const rows2 = arr.map((t) => ({
1744
1876
  name: t.name ?? t.tool_name ?? t.key ?? "",
1745
1877
  description: typeof t.description === "string" ? t.description : ""
1746
1878
  }));
1747
- printJson(rows, out);
1879
+ printJson(rows2, out);
1748
1880
  }
1749
1881
  function contextCommand() {
1750
- const cmd = new Command8("context").description(
1751
- "Context loader (MCP) \u2014 schema discovery, instance query, skill recall"
1882
+ const cmd = new Command7("context").description(
1883
+ "Ask a network questions (the MCP interface agents use)"
1752
1884
  );
1753
- 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) => {
1885
+ const jsonArgs = (raw) => {
1886
+ if (!raw) throw new InputError("--args is required (run with --schema to see its shape)");
1887
+ try {
1888
+ return parseBigIntJSON(raw);
1889
+ } catch {
1890
+ throw new InputError("--args must be valid JSON");
1891
+ }
1892
+ };
1893
+ cmd.command("search-schema <kn-id> <query>").description(
1894
+ "Search object/relation/action/metric schemas \u2192 {object_types, relation_types, action_types, metric_types}"
1895
+ ).option("--scope <list>", "comma-separated scopes (object,relation,action,metric)").option("--max <n>", "max concepts", int4).action(async (knId, query, opts, cmd2) => {
1754
1896
  const data = await clientFrom(cmd2).context.searchSchema(knId, query, {
1755
1897
  searchScope: opts.scope ? String(opts.scope).split(",") : void 0,
1756
1898
  maxConcepts: opts.max
1757
1899
  });
1758
1900
  printJson(data, outputOptions(cmd2));
1759
1901
  });
1760
- cmd.command("query-object-instance <kn-id>").description("Query object instances (provide --args as JSON)").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
1761
- let args;
1762
- try {
1763
- args = parseBigIntJSON(opts.args);
1764
- } catch {
1765
- throw new InputError("--args must be valid JSON");
1766
- }
1902
+ cmd.command("query-object-instance <kn-id>").description(
1903
+ `Query one object type's instances \u2014 \`--args '{"ot_id":"<id>","limit":10}'\` \u2192 {datas, total_count}`
1904
+ ).option(
1905
+ "--args <json>",
1906
+ "tool arguments as JSON; kn_id is filled from <kn-id>; --schema prints the shape"
1907
+ ).option("--schema", "print this tool's argument schema from the deploy instead of calling it").action(async (knId, opts, cmd2) => {
1908
+ if (opts.schema) return printToolSchema(cmd2, knId, "query_object_instance");
1909
+ const args = jsonArgs(opts.args);
1767
1910
  printJson(await clientFrom(cmd2).context.queryObjectInstance(knId, args), outputOptions(cmd2));
1768
1911
  });
1769
- 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) => {
1912
+ 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) => {
1770
1913
  printJson(
1771
1914
  await clientFrom(cmd2).context.findSkills(knId, otId, opts.topK),
1772
1915
  outputOptions(cmd2)
@@ -1815,15 +1958,106 @@ function contextCommand() {
1815
1958
  cmd.command("info").description("List the deploy's MCP tool catalog (global \u2014 no KN needed)").action(async (_opts, cmd2) => {
1816
1959
  printToolList(await clientFrom(cmd2).context.info(), outputOptions(cmd2));
1817
1960
  });
1818
- cmd.command("tools <kn-id>").description("List MCP tools advertised for a KN session").action(async (knId, _opts, cmd2) => {
1961
+ const printToolSchema = async (cmd2, knId, tool) => {
1962
+ const listed = await clientFrom(cmd2).context.tools(knId);
1963
+ const found = listed.tools?.find((t) => t.name === tool);
1964
+ if (!found) throw new InputError(`this deploy does not advertise the ${tool} tool`);
1965
+ printJson(
1966
+ { tool: found.name, description: found.description, inputSchema: found.inputSchema },
1967
+ { ...outputOptions(cmd2), json: true }
1968
+ );
1969
+ };
1970
+ cmd.command("run-sql <kn-id>").description(
1971
+ "Aggregate, rank or join with read-only SQL \u2014 what query-object-instance cannot do"
1972
+ ).option("--sql <sql>", "read-only MySQL over data resources, tables named as {{<resource-id>}}").option("--timeout <sec>", "query timeout in seconds", (v) => Number.parseInt(v, 10)).option("--schema", "print this tool's argument schema from the deploy instead of calling it").addHelpText(
1973
+ "after",
1974
+ `
1975
+ A table is named by resource id, never by the name it carries on the source:
1976
+
1977
+ openbkn context search-schema <kn-id> "<what you are after>" --json
1978
+ \u2192 object_types[].data_source.id
1979
+
1980
+ openbkn context run-sql <kn-id> \\
1981
+ --sql "SELECT supplier_id, COUNT(*) c FROM {{d9g387peef0be1ifnurg}} GROUP BY supplier_id"
1982
+
1983
+ Joining two resources means two ids, one placeholder each. Column names are the
1984
+ physical ones. Answers {columns, entries, paging} plus a bkn_receipt recording
1985
+ the operation in BKN Trace. Row limits belong in the SQL \u2014 this tool takes no
1986
+ limit argument.
1987
+
1988
+ The same SQL runs without a knowledge network through \`openbkn vega sql\`, which
1989
+ adds paging and --need-total but records nothing in Trace.`
1990
+ ).action(async (knId, opts, cmd2) => {
1991
+ if (opts.schema) return printToolSchema(cmd2, knId, "run_sql");
1992
+ if (!opts.sql) throw new InputError("--sql is required (or use --schema to see the shape)");
1993
+ const args = { sql: opts.sql };
1994
+ if (opts.timeout !== void 0) args.query_timeout = opts.timeout;
1995
+ printJson(await clientFrom(cmd2).context.toolCall(knId, "run_sql", args), outputOptions(cmd2));
1996
+ });
1997
+ cmd.command("explore-subgraph <kn-id> <object-type-id>").description("Follow relations outward from one object type without naming a path first").option("--hops <n>", "how many hops to walk, 1-3", (v) => Number.parseInt(v, 10)).option(
1998
+ "--direction <d>",
1999
+ "forward | backward | bidirectional \u2014 pick bidirectional when unsure how the relation reads",
2000
+ "bidirectional"
2001
+ ).option(
2002
+ "--limit <n>",
2003
+ "instances of the STARTING type, not paths or total objects",
2004
+ (v) => Number.parseInt(v, 10)
2005
+ ).option("--args <json>", "extra tool arguments merged in (condition, sort, offset \u2026)").option("--schema", "print this tool's argument schema from the deploy instead of calling it").addHelpText(
2006
+ "after",
2007
+ `
2008
+ Use this when the topology is the question \u2014 "what does this supplier touch?" \u2014
2009
+ and \`query-instance-subgraph\` when you already know which relations to walk.
2010
+ Paths multiply with each hop, so start at 1 or 2.`
2011
+ ).action(async (knId, objectTypeId, opts, cmd2) => {
2012
+ if (opts.schema) return printToolSchema(cmd2, knId, "explore_subgraph");
2013
+ if (opts.hops === void 0) {
2014
+ throw new InputError("--hops is required (or use --schema to see the shape)");
2015
+ }
2016
+ const args = {
2017
+ ...opts.args ? jsonArgs(opts.args) : {},
2018
+ source_object_type_id: objectTypeId,
2019
+ direction: opts.direction,
2020
+ path_length: opts.hops
2021
+ };
2022
+ if (opts.limit !== void 0) args.limit = opts.limit;
2023
+ printJson(
2024
+ await clientFrom(cmd2).context.toolCall(knId, "explore_subgraph", args),
2025
+ outputOptions(cmd2)
2026
+ );
2027
+ });
2028
+ cmd.command("query-metric <kn-id> <metric-id>").description("Read a modelled metric through its own definition \u2014 do not restate it in SQL").option(
2029
+ "--args <json>",
2030
+ "tool arguments: analysis_dimensions, time, condition, having, order_by"
2031
+ ).option("--schema", "print this tool's argument schema from the deploy instead of calling it").addHelpText(
2032
+ "after",
2033
+ `
2034
+ Metric ids come from an object type: \`context object-types <kn-id> <ot-id>\`
2035
+ lists them under related_metrics. The definition owns the arithmetic, so
2036
+ rewriting it with \`run-sql\` produces a number the platform will not agree with.`
2037
+ ).action(async (knId, metricId, opts, cmd2) => {
2038
+ if (opts.schema) return printToolSchema(cmd2, knId, "query_metric");
2039
+ const args = { ...opts.args ? jsonArgs(opts.args) : {}, metric_id: metricId };
2040
+ printJson(
2041
+ await clientFrom(cmd2).context.toolCall(knId, "query_metric", args),
2042
+ outputOptions(cmd2)
2043
+ );
2044
+ });
2045
+ cmd.command("tools <kn-id>").description("List MCP tools advertised for a KN session \u2192 {tools} with each inputSchema").action(async (knId, _opts, cmd2) => {
1819
2046
  printToolList(await clientFrom(cmd2).context.tools(knId), outputOptions(cmd2));
1820
2047
  });
1821
- 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(
2048
+ cmd.command("tool-call <kn-id> <name>").description("Call any MCP tool by name \u2014 current or future (use `tools` to discover)").option(
2049
+ "--args <json>",
2050
+ "tool arguments as JSON; kn_id is filled from <kn-id> \u2014 input schema comes from `context tools <kn-id>`"
2051
+ ).option(
1822
2052
  "--arg <key=value>",
1823
2053
  "one argument (repeatable; value parsed as JSON, else string)",
1824
2054
  collectArg,
1825
2055
  []
2056
+ ).option(
2057
+ "--schema",
2058
+ "print the named tool's argument schema from the deploy instead of calling it"
1826
2059
  ).action(async (knId, name, opts, cmd2) => {
2060
+ if (opts.schema) return printToolSchema(cmd2, knId, name);
1827
2061
  printJson(
1828
2062
  await clientFrom(cmd2).context.toolCall(knId, name, buildArgs(opts)),
1829
2063
  outputOptions(cmd2)
@@ -1831,7 +2065,7 @@ function contextCommand() {
1831
2065
  });
1832
2066
  cmd.command("call-method <kn-id> <method>").description(
1833
2067
  "Call any MCP method by name (e.g. tools/list, resources/read) \u2014 current or future"
1834
- ).option("--args <json>", "method params as JSON").option(
2068
+ ).option("--args <json>", "method params as JSON \u2014 see `context call-method <kn-id> tools/list`").option(
1835
2069
  "--arg <key=value>",
1836
2070
  "one param (repeatable; value parsed as JSON, else string)",
1837
2071
  collectArg,
@@ -1854,7 +2088,10 @@ function contextCommand() {
1854
2088
  cmd.command("prompts <kn-id>").description("List MCP prompts").action(async (knId, _opts, cmd2) => {
1855
2089
  printJson(await clientFrom(cmd2).context.prompts(knId), outputOptions(cmd2));
1856
2090
  });
1857
- cmd.command("prompt <kn-id> <name>").description("Get one MCP prompt (--args JSON for prompt arguments)").option("--args <json>", "prompt arguments as JSON").action(async (knId, name, opts, cmd2) => {
2091
+ cmd.command("prompt <kn-id> <name>").description("Get one MCP prompt (--args JSON for prompt arguments)").option(
2092
+ "--args <json>",
2093
+ "prompt arguments as JSON \u2014 argument names come from `context prompts <kn-id>`"
2094
+ ).action(async (knId, name, opts, cmd2) => {
1858
2095
  let args;
1859
2096
  if (opts.args) {
1860
2097
  try {
@@ -1865,124 +2102,978 @@ function contextCommand() {
1865
2102
  }
1866
2103
  printJson(await clientFrom(cmd2).context.prompt(knId, name, args), outputOptions(cmd2));
1867
2104
  });
1868
- const jsonArgs = (raw) => {
1869
- try {
1870
- return parseBigIntJSON(raw);
1871
- } catch {
1872
- throw new InputError("--args must be valid JSON");
1873
- }
1874
- };
1875
- 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) => {
2105
+ cmd.command("query-instance-subgraph <kn-id>").description("Query an instance subgraph across relation-type paths").option(
2106
+ "--args <json>",
2107
+ "tool arguments as JSON; kn_id is filled from <kn-id>; --schema prints the shape"
2108
+ ).option("--schema", "print this tool's argument schema from the deploy instead of calling it").action(async (knId, opts, cmd2) => {
2109
+ if (opts.schema) return printToolSchema(cmd2, knId, "query_instance_subgraph");
1876
2110
  printJson(
1877
2111
  await clientFrom(cmd2).context.queryInstanceSubgraph(knId, jsonArgs(opts.args)),
1878
2112
  outputOptions(cmd2)
1879
2113
  );
1880
2114
  });
1881
- 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) => {
2115
+ cmd.command("get-logic-properties <kn-id>").description("Compute logic-property values for instances").option(
2116
+ "--args <json>",
2117
+ "tool arguments as JSON; kn_id is filled from <kn-id>; --schema prints the shape"
2118
+ ).option("--schema", "print this tool's argument schema from the deploy instead of calling it").action(async (knId, opts, cmd2) => {
2119
+ if (opts.schema) return printToolSchema(cmd2, knId, "get_logic_properties_values");
1882
2120
  printJson(
1883
2121
  await clientFrom(cmd2).context.logicProperties(knId, jsonArgs(opts.args)),
1884
2122
  outputOptions(cmd2)
1885
2123
  );
1886
2124
  });
1887
- 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) => {
2125
+ cmd.command("get-action-info <kn-id>").description("Fetch action info / dynamic tools for an instance").option(
2126
+ "--args <json>",
2127
+ "tool arguments as JSON; kn_id is filled from <kn-id>; --schema prints the shape"
2128
+ ).option("--schema", "print this tool's argument schema from the deploy instead of calling it").action(async (knId, opts, cmd2) => {
2129
+ if (opts.schema) return printToolSchema(cmd2, knId, "get_action_info");
1888
2130
  printJson(
1889
2131
  await clientFrom(cmd2).context.actionInfo(knId, jsonArgs(opts.args)),
1890
2132
  outputOptions(cmd2)
1891
2133
  );
1892
2134
  });
1893
- return group(cmd, "AI DATA PLATFORM");
2135
+ groupChildren(cmd, {
2136
+ GROUPS: ["conversation"],
2137
+ READ: [
2138
+ "search-schema",
2139
+ "kn-detail",
2140
+ "object-types",
2141
+ "relation-types",
2142
+ "info",
2143
+ "tools",
2144
+ "resources",
2145
+ "resource",
2146
+ "templates",
2147
+ "prompts",
2148
+ "prompt",
2149
+ "find-skills"
2150
+ ],
2151
+ RUN: [
2152
+ "query-object-instance",
2153
+ "run-sql",
2154
+ "explore-subgraph",
2155
+ "query-metric",
2156
+ "query-instance-subgraph",
2157
+ "get-logic-properties",
2158
+ "get-action-info",
2159
+ "tool-call",
2160
+ "call-method"
2161
+ ]
2162
+ });
2163
+ guide(
2164
+ cmd,
2165
+ `ORDER OF WORK
2166
+ 1. search-schema <kn-id> "<question>" find the object/relation/action ids that matter
2167
+ 2. kn-detail / object-types drill into the ones you picked
2168
+ 3. query-object-instance filter + sort + page one object type
2169
+ query-instance-subgraph follow a known relation path
2170
+ get-logic-properties / get-action-info computed values, runnable actions
2171
+
2172
+ PICKING THE RIGHT QUERY
2173
+ Aggregation, ranking, GROUP BY or joins are not query-object-instance \u2014 send SQL through
2174
+ \`tool-call <kn-id> run_sql\`. Unknown topology is \`tool-call <kn-id> explore_subgraph\`,
2175
+ not a hand-built path.
2176
+
2177
+ THE SAME ID, FOUR NAMES
2178
+ An object type's id is \`concept_id\` in search-schema output, \`id\` in kn-detail and
2179
+ get_object_types, \`ot_id\` in query-object-instance arguments, and \`object_type_id\` on
2180
+ an instance row. Same value throughout \u2014 carry it across, do not look it up again.
2181
+
2182
+ RAW MCP
2183
+ tools <kn-id> lists what this deploy advertises, with each tool's input schema.
2184
+ tool-call / call-method reach anything the named commands above do not cover.`
2185
+ );
2186
+ return group(cmd, "DATA & KNOWLEDGE");
1894
2187
  }
1895
2188
 
1896
- // src/commands/explore.ts
1897
- import { createServer } from "http";
1898
- import { Command as Command9 } from "commander";
1899
- var int6 = (v) => Number.parseInt(v, 10);
1900
- var ROUTES = {
1901
- "GET /api/bkn/meta": (c, q) => c.kn.get(req(q, "knId")),
1902
- "POST /api/bkn/search": (c, _q, b) => c.kn.search(str(b.knId), str(b.query), {
1903
- maxConcepts: typeof b.maxConcepts === "number" ? b.maxConcepts : void 0
1904
- }),
1905
- "POST /api/bkn/instances": (c, _q, b) => c.kn.objectTypeQuery(str(b.knId), str(b.objectTypeId), b.body ?? {}),
1906
- "POST /api/bkn/subgraph": (c, _q, b) => c.kn.subgraph(str(b.knId), b.body ?? b),
1907
- "GET /api/vega/catalogs": (c) => c.vega.catalogs(),
1908
- "GET /api/vega/catalog": (c, q) => c.vega.getCatalog(req(q, "catalogId")),
1909
- "GET /api/vega/catalog-resources": (c, q) => c.vega.catalogResources(req(q, "catalogId"), q.get("category") ?? void 0),
1910
- "GET /api/vega/connector-types": (c) => c.vega.connectorTypes(),
1911
- "POST /api/vega/query": (c, _q, b) => c.resource.query(str(b.resourceId), b.options ?? {})
2189
+ // src/commands/describe.ts
2190
+ import { Command as Command8 } from "commander";
2191
+
2192
+ // src/help/returns.json
2193
+ var returns_default = {
2194
+ note: "Top-level keys observed on a live deploy, not a contract. Regenerate with scripts/capture-returns.mjs.",
2195
+ observedAt: "2026-08-29",
2196
+ commands: {
2197
+ "auth status": ["baseUrl", "expired", "hasToken", "userId", "username"],
2198
+ "auth whoami": [
2199
+ "at_hash",
2200
+ "aud",
2201
+ "auth_time",
2202
+ "baseUrl",
2203
+ "exp",
2204
+ "iat",
2205
+ "iss",
2206
+ "jti",
2207
+ "rat",
2208
+ "sid",
2209
+ "sub",
2210
+ "userId",
2211
+ "username"
2212
+ ],
2213
+ "auth list": "array",
2214
+ "config show": ["baseUrl"],
2215
+ "appkey list": ["keys"],
2216
+ "appkey admin list": ["keys"],
2217
+ "bkn list": ["entries", "total_count"],
2218
+ "bkn get": [
2219
+ "branch",
2220
+ "business_domain",
2221
+ "color",
2222
+ "comment",
2223
+ "create_time",
2224
+ "creator",
2225
+ "icon",
2226
+ "id",
2227
+ "module_type",
2228
+ "name",
2229
+ "operations",
2230
+ "tags",
2231
+ "update_time",
2232
+ "updater"
2233
+ ],
2234
+ "bkn object-type list": ["entries", "total_count"],
2235
+ "bkn object-type get": ["entries"],
2236
+ "bkn relation-type list": ["entries", "total_count"],
2237
+ "bkn relation-type get": ["entries"],
2238
+ "bkn action-type list": ["entries", "total_count"],
2239
+ "bkn action-type get": ["entries"],
2240
+ "bkn stats": [
2241
+ "branch",
2242
+ "business_domain",
2243
+ "color",
2244
+ "comment",
2245
+ "create_time",
2246
+ "creator",
2247
+ "icon",
2248
+ "id",
2249
+ "module_type",
2250
+ "name",
2251
+ "operations",
2252
+ "statistics",
2253
+ "tags",
2254
+ "update_time",
2255
+ "updater"
2256
+ ],
2257
+ "bkn action-log list": ["entries"],
2258
+ "bkn metric list": ["entries", "total_count"],
2259
+ "bkn concept-group list": ["entries", "total_count"],
2260
+ "bkn concept-group get": [
2261
+ "branch",
2262
+ "color",
2263
+ "comment",
2264
+ "create_time",
2265
+ "creator",
2266
+ "icon",
2267
+ "id",
2268
+ "kn_id",
2269
+ "module_type",
2270
+ "name",
2271
+ "object_types",
2272
+ "relation_types",
2273
+ "tags",
2274
+ "update_time",
2275
+ "updater"
2276
+ ],
2277
+ "bkn action-schedule list": ["entries", "total_count"],
2278
+ "vega catalog list": ["entries", "total_count"],
2279
+ "vega catalog resources": ["entries", "total_count"],
2280
+ "vega catalog health": ["health_check_result", "health_check_status", "id", "last_check_time"],
2281
+ "vega discover-schedule list": ["entries", "total_count"],
2282
+ "vega discover-schedule get": [
2283
+ "catalog_id",
2284
+ "create_time",
2285
+ "creator",
2286
+ "cron_expr",
2287
+ "enabled",
2288
+ "end_time",
2289
+ "id",
2290
+ "last_run",
2291
+ "name",
2292
+ "next_run",
2293
+ "start_time",
2294
+ "strategy",
2295
+ "update_time",
2296
+ "updater"
2297
+ ],
2298
+ "vega discover-task list": ["entries", "total_count"],
2299
+ "vega discover-task get": [
2300
+ "catalog_id",
2301
+ "catalog_name",
2302
+ "create_time",
2303
+ "creator",
2304
+ "finish_time",
2305
+ "id",
2306
+ "last_progress_time",
2307
+ "message",
2308
+ "progress",
2309
+ "queue_priority",
2310
+ "result",
2311
+ "schedule_id",
2312
+ "start_time",
2313
+ "status",
2314
+ "strategy",
2315
+ "trigger_type"
2316
+ ],
2317
+ "vega semantic-task list": ["entries", "total_count"],
2318
+ "vega semantic-task get": [
2319
+ "agent_id",
2320
+ "agent_task_id",
2321
+ "applied",
2322
+ "apply_detail_json",
2323
+ "apply_mode",
2324
+ "catalog_id",
2325
+ "catalog_name",
2326
+ "confidence",
2327
+ "confidence_detail_json",
2328
+ "confidence_threshold",
2329
+ "create_time",
2330
+ "creator",
2331
+ "id",
2332
+ "input",
2333
+ "input_hash",
2334
+ "resource_id",
2335
+ "resource_name",
2336
+ "result_json",
2337
+ "scope",
2338
+ "status"
2339
+ ],
2340
+ "vega connector-type list": ["entries", "total_count"],
2341
+ "vega resource list": ["entries", "total_count"],
2342
+ "vega resource get": ["entries"],
2343
+ "resource list": ["entries", "total_count"],
2344
+ "resource get": ["entries"],
2345
+ "context kn-detail": [
2346
+ "action_types",
2347
+ "comment",
2348
+ "concept_groups",
2349
+ "id",
2350
+ "name",
2351
+ "object_types",
2352
+ "relation_types"
2353
+ ],
2354
+ "context info": [
2355
+ "auth",
2356
+ "client_config_example",
2357
+ "endpoint",
2358
+ "language",
2359
+ "protocol",
2360
+ "service",
2361
+ "supported_languages",
2362
+ "tool_count",
2363
+ "tools",
2364
+ "transport"
2365
+ ],
2366
+ "context tools": ["tools"],
2367
+ "model llm list": ["count", "data"],
2368
+ "model llm get": [
2369
+ "max_model_len",
2370
+ "model_config",
2371
+ "model_id",
2372
+ "model_name",
2373
+ "model_series",
2374
+ "model_type"
2375
+ ],
2376
+ "model small list": ["count", "data"],
2377
+ "model small get": [
2378
+ "adapter",
2379
+ "adapter_code",
2380
+ "batch_size",
2381
+ "create_time",
2382
+ "default",
2383
+ "embedding_dim",
2384
+ "max_tokens",
2385
+ "model_config",
2386
+ "model_id",
2387
+ "model_name",
2388
+ "model_type",
2389
+ "update_time"
2390
+ ],
2391
+ "model small get-default": [
2392
+ "batch_size",
2393
+ "default",
2394
+ "embedding_dim",
2395
+ "max_tokens",
2396
+ "model_config",
2397
+ "model_id",
2398
+ "model_name",
2399
+ "model_type"
2400
+ ],
2401
+ "skill list": ["data", "has_next", "has_prev", "page", "page_size", "total", "total_pages"],
2402
+ "skill get": [
2403
+ "business_domain_id",
2404
+ "category",
2405
+ "category_name",
2406
+ "create_time",
2407
+ "create_user",
2408
+ "description",
2409
+ "name",
2410
+ "skill_id",
2411
+ "source",
2412
+ "status",
2413
+ "update_time",
2414
+ "update_user",
2415
+ "version"
2416
+ ],
2417
+ "skill market": ["data", "has_next", "has_prev", "page", "page_size", "total", "total_pages"],
2418
+ "skill market-get": [
2419
+ "business_domain_id",
2420
+ "category",
2421
+ "category_name",
2422
+ "create_time",
2423
+ "create_user",
2424
+ "description",
2425
+ "name",
2426
+ "release_time",
2427
+ "release_user",
2428
+ "skill_id",
2429
+ "source",
2430
+ "status",
2431
+ "update_time",
2432
+ "update_user",
2433
+ "version"
2434
+ ],
2435
+ "skill content": ["files", "skill_id", "status", "url"],
2436
+ "skill files": ["entries", "path", "skillId", "totalFiles", "totalSize"],
2437
+ "skill names": ["entries"],
2438
+ "skill history": "array",
2439
+ "toolbox list": ["data", "has_next", "has_prev", "page", "page_size", "total", "total_pages"],
2440
+ "function deps": ["dependencies", "session_id"],
2441
+ "function template": ["code_template", "template_type"],
2442
+ "trace graph": [
2443
+ "data",
2444
+ "duration_nano",
2445
+ "page",
2446
+ "partial",
2447
+ "partial_reason",
2448
+ "status",
2449
+ "trace_id"
2450
+ ],
2451
+ "trace conversations list": ["entries"],
2452
+ "trace conversations get": [
2453
+ "agent_name",
2454
+ "conversation_id",
2455
+ "created_at",
2456
+ "creation_auth_method",
2457
+ "external_conversation_key",
2458
+ "generation",
2459
+ "one_shot",
2460
+ "owner",
2461
+ "row_version",
2462
+ "status",
2463
+ "updated_at"
2464
+ ],
2465
+ "trace interactions get": [
2466
+ "closure_manifest",
2467
+ "conversation_id",
2468
+ "created_at",
2469
+ "evidence_status",
2470
+ "execution_status",
2471
+ "interaction_id",
2472
+ "lease_epoch",
2473
+ "lease_expires_at",
2474
+ "lease_token",
2475
+ "lease_version",
2476
+ "ordinal",
2477
+ "row_version",
2478
+ "terminal_at",
2479
+ "updated_at"
2480
+ ],
2481
+ "trace operations get": [
2482
+ "attempt",
2483
+ "attempt_status",
2484
+ "conversation_id",
2485
+ "created_at",
2486
+ "interaction_id",
2487
+ "operation_id",
2488
+ "operation_key",
2489
+ "retryable",
2490
+ "row_version",
2491
+ "tool_name",
2492
+ "updated_at"
2493
+ ],
2494
+ "trace receipts get": [
2495
+ "artifact_refs",
2496
+ "attempt",
2497
+ "business_refs",
2498
+ "causation_event_ids",
2499
+ "conversation_id",
2500
+ "evidence_durability",
2501
+ "interaction_id",
2502
+ "issued_at",
2503
+ "observed_evidence_refs",
2504
+ "operation_id",
2505
+ "operation_key",
2506
+ "owner",
2507
+ "partial_reasons",
2508
+ "receipt_id",
2509
+ "receipt_status",
2510
+ "request_id",
2511
+ "required",
2512
+ "row_version",
2513
+ "schema_version",
2514
+ "terminal_at",
2515
+ "tool_name",
2516
+ "trace_id"
2517
+ ],
2518
+ "trace get": "array",
2519
+ "trace detail": ["graph", "operations", "partial", "summary"],
2520
+ "trace spans": "array",
2521
+ "trace search": [
2522
+ "entries",
2523
+ "next_cursor",
2524
+ "page",
2525
+ "page_size",
2526
+ "partial",
2527
+ "total",
2528
+ "truncated"
2529
+ ],
2530
+ "admin auth status": ["baseUrl", "expired", "hasToken", "userId", "username"],
2531
+ "admin auth whoami": [
2532
+ "at_hash",
2533
+ "aud",
2534
+ "auth_time",
2535
+ "baseUrl",
2536
+ "exp",
2537
+ "iat",
2538
+ "iss",
2539
+ "jti",
2540
+ "rat",
2541
+ "sid",
2542
+ "sub",
2543
+ "userId",
2544
+ "username"
2545
+ ],
2546
+ "admin auth list": "array",
2547
+ "admin auth export": ["accessToken", "baseUrl", "idToken", "refreshToken"],
2548
+ "admin org list": ["departments", "total"],
2549
+ "admin org get": [
2550
+ "code",
2551
+ "created_at",
2552
+ "id",
2553
+ "manager_id",
2554
+ "manager_name",
2555
+ "name",
2556
+ "parent_id",
2557
+ "type"
2558
+ ],
2559
+ "admin org members": ["total", "users"],
2560
+ "admin org tree": "array",
2561
+ "admin user list": ["total", "users"],
2562
+ "admin user get": [
2563
+ "account",
2564
+ "account_type",
2565
+ "departments",
2566
+ "email",
2567
+ "enabled",
2568
+ "id",
2569
+ "name",
2570
+ "roles",
2571
+ "telephone",
2572
+ "updated_at"
2573
+ ],
2574
+ "admin user roles": ["roles"],
2575
+ "admin role list": ["roles"],
2576
+ "admin role get": [
2577
+ "built_in",
2578
+ "created_at",
2579
+ "description",
2580
+ "id",
2581
+ "members",
2582
+ "name",
2583
+ "permissions",
2584
+ "source"
2585
+ ],
2586
+ "admin role members": ["members"],
2587
+ "admin llm list": ["count", "data"],
2588
+ "admin llm get": [
2589
+ "max_model_len",
2590
+ "model_config",
2591
+ "model_id",
2592
+ "model_name",
2593
+ "model_series",
2594
+ "model_type"
2595
+ ],
2596
+ "admin small-model list": ["count", "data"],
2597
+ "admin small-model get": [
2598
+ "adapter",
2599
+ "adapter_code",
2600
+ "batch_size",
2601
+ "create_time",
2602
+ "default",
2603
+ "embedding_dim",
2604
+ "max_tokens",
2605
+ "model_config",
2606
+ "model_id",
2607
+ "model_name",
2608
+ "model_type",
2609
+ "update_time"
2610
+ ],
2611
+ "admin license show": [
2612
+ "activated",
2613
+ "contract_expires_at",
2614
+ "customer",
2615
+ "edition",
2616
+ "expires_at",
2617
+ "features",
2618
+ "instance_fp",
2619
+ "issued_at",
2620
+ "lic_id",
2621
+ "limits",
2622
+ "state"
2623
+ ],
2624
+ "admin config show": ["baseUrl"]
2625
+ }
1912
2626
  };
1913
- function str(v) {
1914
- return typeof v === "string" ? v : String(v ?? "");
1915
- }
1916
- function req(q, key) {
1917
- const v = q.get(key);
1918
- if (!v) throw new Error(`missing query param: ${key}`);
1919
- return v;
1920
- }
1921
- function readBody2(reqMsg) {
1922
- return new Promise((resolve2, reject) => {
1923
- let data = "";
1924
- reqMsg.on("data", (chunk) => {
1925
- data += chunk;
1926
- });
1927
- reqMsg.on("end", () => {
1928
- if (!data.trim()) return resolve2({});
1929
- try {
1930
- resolve2(parseBigIntJSON(data));
1931
- } catch {
1932
- reject(new Error("invalid JSON body"));
1933
- }
1934
- });
1935
- reqMsg.on("error", reject);
1936
- });
2627
+
2628
+ // src/commands/probe.ts
2629
+ var SERVICE_PROBES = [
2630
+ { service: "bkn-backend", path: "/api/bkn-backend/v1/knowledge-networks?limit=1" },
2631
+ { service: "vega-backend", path: "/api/vega-backend/v1/catalogs?limit=1" },
2632
+ {
2633
+ service: "agent-operator-integration",
2634
+ path: "/api/agent-operator-integration/v1/tool-box/list?page=1&size=1"
2635
+ },
2636
+ { service: "agent-observability", path: "/api/agent-observability/v1/conversations?limit=1" },
2637
+ { service: "mf-model-manager", path: "/api/mf-model-manager/v1/llm/list" },
2638
+ { service: "safe", path: "/api/safe/v1/me/api-keys" },
2639
+ { service: "agent-retrieval", path: "/api/agent-retrieval/v1/mcp/info" }
2640
+ ];
2641
+ var COMMAND_SERVICE = [
2642
+ [/^bkn\b/, "bkn-backend"],
2643
+ [/^vega\b/, "vega-backend"],
2644
+ [/^resource\b/, "vega-backend"],
2645
+ [/^context\b/, "agent-retrieval"],
2646
+ [/^trace\b/, "agent-observability"],
2647
+ [/^(skill|toolbox|tool|function)\b/, "agent-operator-integration"],
2648
+ [/^model\b/, "mf-model-manager"],
2649
+ [/^(auth|appkey)\b/, "safe"],
2650
+ [/^admin (llm|small-model)\b/, "mf-model-manager"],
2651
+ [/^admin\b/, "safe"]
2652
+ ];
2653
+ var COMMAND_TOOL = {
2654
+ "context search-schema": "search_schema",
2655
+ "context query-object-instance": "query_object_instance",
2656
+ "context query-instance-subgraph": "query_instance_subgraph",
2657
+ "context explore-subgraph": "explore_subgraph",
2658
+ "context run-sql": "run_sql",
2659
+ "context query-metric": "query_metric",
2660
+ "context get-logic-properties": "get_logic_properties_values",
2661
+ "context get-action-info": "get_action_info",
2662
+ "context find-skills": "find_skills",
2663
+ "context kn-detail": "get_kn_detail",
2664
+ "context object-types": "get_object_types",
2665
+ "context relation-types": "get_relation_types"
2666
+ };
2667
+ async function probeDeploy(cmd, now) {
2668
+ const client = clientFrom(cmd);
2669
+ const services = {};
2670
+ for (const { service, path } of SERVICE_PROBES) {
2671
+ try {
2672
+ const res = await client.call(path);
2673
+ services[service] = res.status === 404 || res.status === 501 || res.status >= 502 ? { available: false, reason: `HTTP ${res.status} ${res.statusText}`.trim() } : { available: true };
2674
+ } catch (err) {
2675
+ services[service] = {
2676
+ available: false,
2677
+ reason: err instanceof Error ? firstLine(err.message) : "unreachable"
2678
+ };
2679
+ }
2680
+ }
2681
+ let mcpTools = [];
2682
+ if (services["agent-retrieval"]?.available) {
2683
+ try {
2684
+ const info = await client.context.info();
2685
+ mcpTools = (info.tools ?? []).map((t) => typeof t === "string" ? t : t.name ?? "").filter(Boolean);
2686
+ } catch {
2687
+ }
2688
+ }
2689
+ return { baseUrl: client.ctx.baseUrl, checkedAt: now, services, mcpTools };
2690
+ }
2691
+ function firstLine(message) {
2692
+ return (message.split("\n")[0] ?? message).slice(0, 160);
2693
+ }
2694
+ function availabilityOf(path, probe) {
2695
+ if (!probe) return { available: "unknown" };
2696
+ const tool = COMMAND_TOOL[path];
2697
+ if (tool && probe.mcpTools.length) {
2698
+ return probe.mcpTools.includes(tool) ? { available: true } : { available: false, reason: `this deploy's MCP server does not advertise ${tool}` };
2699
+ }
2700
+ const service = COMMAND_SERVICE.find(([re]) => re.test(path))?.[1];
2701
+ if (!service) return { available: "unknown", reason: "no single service to check" };
2702
+ const state = probe.services[service];
2703
+ if (!state) return { available: "unknown" };
2704
+ return state.available ? { available: true } : { available: false, reason: `${service}: ${state.reason ?? "unreachable"}` };
1937
2705
  }
1938
- var INDEX = `<!doctype html><meta charset="utf-8"><title>openbkn explore</title>
1939
- <h1>openbkn explore</h1>
1940
- <p>Read-only JSON endpoints for bkn + vega:</p>
1941
- <ul>${Object.keys(ROUTES).map((r) => `<li><code>${r}</code></li>`).join("")}</ul>`;
1942
- function exploreCommand() {
1943
- const cmd = new Command9("explore").description(
1944
- "Start a local web server with read-only bkn + vega JSON endpoints"
2706
+
2707
+ // src/commands/describe.ts
2708
+ var DEFAULT_SECTION = "COMMANDS";
2709
+ var ID_SOURCES = {
2710
+ "kn-id": "openbkn bkn list",
2711
+ "catalog-id": "openbkn vega catalog list",
2712
+ "resource-id": "openbkn vega catalog resources <catalog-id>",
2713
+ "ot-id": 'openbkn context search-schema <kn-id> "<q>"',
2714
+ "at-id": "openbkn bkn action-type list <kn-id>",
2715
+ "metric-id": "openbkn bkn metric list <kn-id>",
2716
+ "skill-id": "openbkn skill list",
2717
+ "box-id": "openbkn toolbox list",
2718
+ "tool-id": "openbkn tool list --toolbox <box-id>",
2719
+ "conversation-id": "openbkn trace conversations list",
2720
+ "trace-id": "openbkn trace search",
2721
+ "interaction-id": "openbkn trace search",
2722
+ "operation-id": "openbkn trace interactions operations <interaction-id>",
2723
+ "ot-ids": 'openbkn context search-schema <kn-id> "<q>"',
2724
+ "object-type-id": 'openbkn context search-schema <kn-id> "<q>"',
2725
+ "execution-id": "openbkn bkn action-log list <kn-id>",
2726
+ "receipt-id": "openbkn trace interactions operations <interaction-id>",
2727
+ "conversation-ids": "openbkn trace conversations list",
2728
+ "model-ids": "openbkn model llm list",
2729
+ "tool-ids": "openbkn tool list --toolbox <box-id>",
2730
+ "document-id": "openbkn vega resource document-get <resource-id> <document-id>",
2731
+ "document-ids": "openbkn vega resource document-get <resource-id> <document-id>",
2732
+ // Named entities, wherever they appear; the overrides below cover the cases
2733
+ // where the same word means something else.
2734
+ role: "openbkn admin role list",
2735
+ user: "openbkn admin user list"
2736
+ };
2737
+ var ARGUMENT_OVERRIDES = {
2738
+ "admin role add-member|id": "openbkn admin user list",
2739
+ "admin role remove-member|id": "openbkn admin user list",
2740
+ "admin user assign-role|role": "openbkn admin role list",
2741
+ "admin user revoke-role|role": "openbkn admin role list",
2742
+ "admin user roles|user": "openbkn admin user list",
2743
+ "admin role members|role": "openbkn admin role list",
2744
+ "auth switch|user": "openbkn auth users <url>",
2745
+ "admin auth switch|user": "openbkn admin auth users <url>"
2746
+ };
2747
+ var GROUP_ID_SOURCES = [
2748
+ [/^vega catalog\b/, "openbkn vega catalog list"],
2749
+ [/^vega resource\b/, "openbkn vega catalog resources <catalog-id>"],
2750
+ [/^vega discover-schedule\b/, "openbkn vega discover-schedule list"],
2751
+ [/^vega discover-task\b/, "openbkn vega discover-task list"],
2752
+ [/^vega semantic-task\b/, "openbkn vega semantic-task list"],
2753
+ [/^vega dataset\b/, "openbkn vega dataset build-list"],
2754
+ [/^bkn action-schedule\b/, "openbkn bkn action-schedule list <kn-id>"],
2755
+ [/^vega connector-type\b/, "openbkn vega connector-type list"],
2756
+ [/^resource\b/, "openbkn resource list"],
2757
+ [/^skill\b/, "openbkn skill list"],
2758
+ [/^toolbox\b/, "openbkn toolbox list"],
2759
+ [/^tool\b/, "openbkn tool list --toolbox <box-id>"],
2760
+ [/^appkey\b/, "openbkn appkey list"],
2761
+ [/^admin org\b/, "openbkn admin org list"],
2762
+ [/^auth\b/, "openbkn auth list"],
2763
+ [/^admin user\b/, "openbkn admin user list"],
2764
+ [/^admin role\b/, "openbkn admin role list"],
2765
+ [/^admin llm\b/, "openbkn admin llm list"],
2766
+ [/^admin small-model\b/, "openbkn admin small-model list"],
2767
+ [/^model llm\b/, "openbkn model llm list"],
2768
+ [/^model small\b/, "openbkn model small list"],
2769
+ [/^bkn object-type\b/, "openbkn bkn object-type list <kn-id>"],
2770
+ [/^bkn relation-type\b/, "openbkn bkn relation-type list <kn-id>"],
2771
+ [/^bkn action-type\b/, "openbkn bkn action-type list <kn-id>"],
2772
+ [/^bkn concept-group\b/, "openbkn bkn concept-group list <kn-id>"],
2773
+ [/^bkn action-schedule\b/, "openbkn bkn action-schedule list <kn-id>"],
2774
+ [/^bkn action-log\b/, "openbkn bkn action-log list <kn-id>"]
2775
+ ];
2776
+ function sourceOf(argName, path, isLast) {
2777
+ const override = ARGUMENT_OVERRIDES[`${path.join(" ")}|${argName}`];
2778
+ if (override !== void 0) return override ?? void 0;
2779
+ const byName = ID_SOURCES[argName];
2780
+ if (byName) return byName;
2781
+ if (!isLast) return void 0;
2782
+ const generic = /^(id|ids|cg-id|modelid|role|user|schedule-id|schedule-ids|task-id|log-id)$/i.test(argName);
2783
+ if (!generic) return void 0;
2784
+ const parent = path.slice(0, -1).join(" ");
2785
+ return GROUP_ID_SOURCES.find(([re]) => re.test(parent))?.[1];
2786
+ }
2787
+ function describeOption(opt) {
2788
+ return {
2789
+ flags: opt.flags,
2790
+ description: opt.description,
2791
+ ...opt.mandatory ? { mandatory: true } : {},
2792
+ ...opt.required || opt.optional ? { takesValue: true } : {},
2793
+ ...opt.defaultValue === void 0 ? {} : { default: opt.defaultValue }
2794
+ };
2795
+ }
2796
+ var RETURN_SHAPES = returns_default.commands;
2797
+ function shapeOf(path) {
2798
+ return RETURN_SHAPES[path];
2799
+ }
2800
+ var activeProbe;
2801
+ function describeNode(cmd, parentPath, depth) {
2802
+ const path = [...parentPath, cmd.name()];
2803
+ const children = cmd.commands.filter((c) => !c.name().startsWith("help"));
2804
+ const state = activeProbe ? availabilityOf(path.join(" "), activeProbe) : void 0;
2805
+ const skeleton = {
2806
+ path: path.join(" "),
2807
+ name: cmd.name(),
2808
+ section: sectionOf(cmd),
2809
+ summary: cmd.description(),
2810
+ ...shapeOf(path.join(" ")) ? { returns: shapeOf(path.join(" ")) } : {},
2811
+ ...state ? { available: state.available } : {},
2812
+ ...state?.reason ? { unavailable: state.reason } : {}
2813
+ };
2814
+ if (depth <= 0) return children.length ? { ...skeleton, hasCommands: true } : skeleton;
2815
+ const aliases = cmd.aliases();
2816
+ const options = cmd.options.filter((o) => o.long !== "--help");
2817
+ return {
2818
+ ...skeleton,
2819
+ ...aliases.length ? { aliases } : {},
2820
+ ...cmd.registeredArguments.length ? {
2821
+ arguments: cmd.registeredArguments.map((arg, index, all) => {
2822
+ const from = sourceOf(arg.name(), path, index === all.length - 1);
2823
+ return {
2824
+ name: arg.name(),
2825
+ required: arg.required,
2826
+ variadic: arg.variadic,
2827
+ ...arg.description ? { description: arg.description } : {},
2828
+ ...from ? { from } : {}
2829
+ };
2830
+ })
2831
+ } : {},
2832
+ ...options.length ? { options: options.map(describeOption) } : {},
2833
+ ...guideOf(cmd) ? { guide: guideOf(cmd) } : {},
2834
+ ...children.length ? { commands: children.map((child) => describeNode(child, path, depth - 1)) } : {}
2835
+ };
2836
+ }
2837
+ var FIELD_MEANINGS = {
2838
+ returns: "top-level keys observed on a live deploy \u2014 evidence for parsing, not a contract; absent where nothing was recorded",
2839
+ available: "whether this deploy can answer the command (only after --probe). Checked per service and per MCP tool, so a command can still be refused for a capability the probe cannot see",
2840
+ unavailable: "why it cannot; absent when it can",
2841
+ probe: "what the probe asked and found: one read per service, plus the MCP tool catalog",
2842
+ path: "full command path \u2014 run it as `openbkn <path>`",
2843
+ section: "which section the command sits in; see `sections`",
2844
+ summary: "what the command does; often names the shape it answers with",
2845
+ hasCommands: "this walk stopped here \u2014 ask for this path to see deeper",
2846
+ arguments: "positional arguments, in order",
2847
+ "arguments[].from": "the command that hands out this argument's value",
2848
+ options: "flags; `mandatory` and `takesValue` appear only when true",
2849
+ guide: "prose the command's own --help prints under its command list",
2850
+ commands: "nested commands"
2851
+ };
2852
+ function resolve2(program2, path) {
2853
+ let node = program2;
2854
+ for (const name of path) {
2855
+ const child = node.commands.find((c) => c.name() === name || c.aliases().includes(name));
2856
+ if (!child) throw new InputError(`no such command: ${path.join(" ")}`);
2857
+ node = child;
2858
+ }
2859
+ return node;
2860
+ }
2861
+ function describeCommandTree(program2, opts = {}) {
2862
+ activeProbe = opts.probe;
2863
+ const depth = opts.depth === void 0 ? Number.MAX_SAFE_INTEGER : opts.depth - 1;
2864
+ if (opts.path?.length) {
2865
+ return {
2866
+ sections: SECTION_MEANINGS,
2867
+ fields: FIELD_MEANINGS,
2868
+ ...describeNode(resolve2(program2, opts.path), opts.path.slice(0, -1), depth + 1)
2869
+ };
2870
+ }
2871
+ const top = program2.commands.filter(
2872
+ (c) => !c.name().startsWith("help") && c.name() !== "describe"
1945
2873
  );
1946
- cmd.option("--port <n>", "port to listen on", int6, 7777).option("--host <h>", "host to bind", "127.0.0.1").action(async (opts, command) => {
1947
- const client = clientFrom(command);
1948
- const server = createServer((reqMsg, res) => {
1949
- void handle(client, reqMsg, res);
1950
- });
1951
- server.listen(opts.port, opts.host, () => {
1952
- console.error(`openbkn explore running at http://${opts.host}:${opts.port}/`);
1953
- console.error("bkn + vega read endpoints only. Press Ctrl+C to stop.");
1954
- });
1955
- });
1956
- return group(cmd, "FOUNDATION");
2874
+ return {
2875
+ name: program2.name(),
2876
+ version: program2.version(),
2877
+ summary: program2.description(),
2878
+ sections: SECTION_MEANINGS,
2879
+ fields: FIELD_MEANINGS,
2880
+ ...opts.probe ? { probe: opts.probe } : {},
2881
+ commands: top.map((cmd) => describeNode(cmd, [], depth)),
2882
+ globalOptions: program2.options.filter((o) => o.long !== "--help").map(describeOption),
2883
+ guide: guideOf(program2)
2884
+ };
1957
2885
  }
1958
- async function handle(client, reqMsg, res) {
1959
- const url = new URL(reqMsg.url ?? "/", "http://localhost");
1960
- const method = reqMsg.method ?? "GET";
1961
- if (method === "GET" && url.pathname === "/") {
1962
- res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
1963
- res.end(INDEX);
1964
- return;
2886
+ function rows(node, indent = "") {
2887
+ const out = [
2888
+ {
2889
+ indent,
2890
+ name: `${node.path.split(" ").pop()}${node.hasCommands ? " \u2026" : ""}`,
2891
+ section: node.section === DEFAULT_SECTION ? "" : node.section,
2892
+ summary: node.available === false ? `[unavailable] ${node.unavailable ?? ""} \u2014 ${node.summary}` : node.summary
2893
+ }
2894
+ ];
2895
+ for (const child of node.commands ?? []) out.push(...rows(child, `${indent} `));
2896
+ return out;
2897
+ }
2898
+ function renderText(tree) {
2899
+ const root = tree;
2900
+ const out = [];
2901
+ if (root.sections) {
2902
+ const width = Math.max(...Object.keys(root.sections).map((k) => k.length));
2903
+ out.push("SECTIONS");
2904
+ for (const [name, meaning] of Object.entries(root.sections)) {
2905
+ out.push(` ${name.padEnd(width)} ${meaning}`);
2906
+ }
2907
+ out.push("");
1965
2908
  }
1966
- const handler = ROUTES[`${method} ${url.pathname}`];
1967
- if (!handler) {
1968
- res.writeHead(404, { "content-type": "application/json" });
1969
- res.end(JSON.stringify({ error: "not found" }));
1970
- return;
2909
+ const nodes = root.path ? [tree] : root.commands ?? [];
2910
+ const truncated = nodes.some(function deeper(n) {
2911
+ return Boolean(n.hasCommands) || (n.commands ?? []).some(deeper);
2912
+ });
2913
+ const table = nodes.flatMap((node) => rows(node));
2914
+ const nameWidth = Math.max(...table.map((r) => r.indent.length + r.name.length));
2915
+ const sectionWidth = Math.max(...table.map((r) => r.section.length));
2916
+ for (const r of table) {
2917
+ const name = `${r.indent}${r.name}`.padEnd(nameWidth);
2918
+ out.push(`${name} ${r.section.padEnd(sectionWidth)} ${r.summary}`.trimEnd());
1971
2919
  }
2920
+ if (truncated) {
2921
+ out.push("", "\u2026 has subcommands \u2014 `openbkn describe <command>`, or raise --depth");
2922
+ }
2923
+ return out.join("\n");
2924
+ }
2925
+ function describeCommand(program2) {
2926
+ const cmd = new Command8("describe").description("Print the command tree \u2014 paths, sections, flags (--json for the data)").argument("[command...]", "only this subtree, e.g. `describe bkn metric`").option(
2927
+ "--depth <n>",
2928
+ "how many levels to walk (1 = this level only)",
2929
+ (v) => Number.parseInt(v, 10)
2930
+ ).option("--pretty", "indent the JSON instead of packing it onto one line").option("--probe", "ask this deploy which commands it can answer (read-only, ~7 requests)").action(async (path, opts, self) => {
2931
+ const probe = opts.probe ? await probeDeploy(self, (/* @__PURE__ */ new Date()).toISOString()) : void 0;
2932
+ const tree = describeCommandTree(program2, { path, depth: opts.depth, probe });
2933
+ const out = outputOptions(self);
2934
+ if (!out.json && !out.compact && !opts.pretty) {
2935
+ process.stdout.write(`${renderText(tree)}
2936
+ `);
2937
+ return;
2938
+ }
2939
+ printJson(tree, { ...out, json: Boolean(opts.pretty), compact: !opts.pretty });
2940
+ });
2941
+ return group(cmd, "COMMANDS");
2942
+ }
2943
+
2944
+ // src/commands/function.ts
2945
+ import { readFileSync as readFileSync4 } from "fs";
2946
+ import { Command as Command9 } from "commander";
2947
+ var int5 = (v) => Number.parseInt(v, 10);
2948
+ function readCode(file) {
1972
2949
  try {
1973
- const body = method === "GET" ? {} : await readBody2(reqMsg);
1974
- const data = await handler(client, url.searchParams, body);
1975
- res.writeHead(200, { "content-type": "application/json" });
1976
- res.end(stringifyBigIntJSON(data ?? null));
2950
+ return readFileSync4(file === "-" ? 0 : file, "utf8");
1977
2951
  } catch (err) {
1978
- res.writeHead(500, { "content-type": "application/json" });
1979
- res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
2952
+ throw new InputError(
2953
+ `Cannot read ${file === "-" ? "stdin" : file}: ${err instanceof Error ? err.message : err}`
2954
+ );
1980
2955
  }
1981
2956
  }
2957
+ function collectDep(value, previous = []) {
2958
+ const at = value.lastIndexOf("@");
2959
+ const name = at > 0 ? value.slice(0, at) : value;
2960
+ const version = at > 0 ? value.slice(at + 1) : void 0;
2961
+ if (!name) throw new InputError("--dep takes <name> or <name>@<version>");
2962
+ return [...previous, version ? { name, version } : { name }];
2963
+ }
2964
+ function parseJsonOption(raw, label) {
2965
+ if (raw === void 0) return void 0;
2966
+ try {
2967
+ return parseBigIntJSON(raw);
2968
+ } catch {
2969
+ throw new InputError(`--${label} must be valid JSON`);
2970
+ }
2971
+ }
2972
+ function definitionFlags(c) {
2973
+ return c.option("--name <n>", "name; required when the definition is a function").option("--description <d>", "what it does \u2014 the model reads this to decide when to call it").option("--type <t>", "function | openapi", "function").option("--inputs <json>", "input parameters: [{name,type,required,description}]").option("--outputs <json>", "output parameters, same shape as --inputs").option("--dep <name@version>", "package to install before running (repeatable)", collectDep).option("--index-url <url>", "package index to install from");
2974
+ }
2975
+ function parameterList(raw, label) {
2976
+ const parsed = parseJsonOption(raw, label);
2977
+ if (parsed === void 0) return void 0;
2978
+ if (!Array.isArray(parsed)) throw new InputError(`--${label} must be a JSON array of parameters`);
2979
+ return parsed;
2980
+ }
2981
+ function functionDefinitionFrom(file, opts) {
2982
+ if (!opts.name) throw new InputError("--name is required for a function");
2983
+ return {
2984
+ name: opts.name,
2985
+ description: opts.description,
2986
+ code: readCode(file),
2987
+ inputs: parameterList(opts.inputs, "inputs"),
2988
+ outputs: parameterList(opts.outputs, "outputs"),
2989
+ dependencies: opts.dep,
2990
+ dependenciesUrl: opts.indexUrl
2991
+ };
2992
+ }
2993
+ function functionCommand() {
2994
+ const cmd = new Command9("function").description(
2995
+ "Sandbox functions: run Python on the platform without registering anything"
2996
+ );
2997
+ cmd.command("run <file>").description("Run a file (or `-` for stdin) in the sandbox; exits non-zero when the code does").option("--event <json>", "the single argument handler() receives", "{}").option("--timeout <s>", "sandbox timeout in seconds", int5).option("--dep <name@version>", "install a package first (repeatable)", collectDep).option("--index-url <url>", "package index to install from (default PyPI)").option(
2998
+ "--pass-token",
2999
+ "put your credential in the sandbox's BKN_TOKEN so `sandbox_sdk.bkn` calls BKN as you"
3000
+ ).action(async (file, opts, cmd2) => {
3001
+ const client = clientFrom(cmd2);
3002
+ const result = await client.functions.run({
3003
+ code: readCode(file),
3004
+ event: parseJsonOption(opts.event, "event") ?? {},
3005
+ timeout: opts.timeout,
3006
+ dependencies: opts.dep,
3007
+ dependenciesUrl: opts.indexUrl,
3008
+ source: "openbkn_cli",
3009
+ // The request headers carry these too, but they stop at the service:
3010
+ // the sandbox reads its own environment, which only these fields fill.
3011
+ conversationId: client.ctx.trace?.conversationId,
3012
+ interactionId: client.ctx.trace?.interactionId,
3013
+ ...opts.passToken ? { bknToken: client.ctx.token } : {}
3014
+ });
3015
+ printJson(result, outputOptions(cmd2));
3016
+ if (result.exit_code !== void 0 && result.exit_code !== 0) process.exitCode = 1;
3017
+ });
3018
+ cmd.command("infer-schema <file>").description(
3019
+ "Derive a tool contract from @tool-decorated code; runs it, answers supported:false when it cannot"
3020
+ ).action(async (file, _opts, cmd2) => {
3021
+ printJson(await clientFrom(cmd2).functions.inferSchema(readCode(file)), outputOptions(cmd2));
3022
+ });
3023
+ cmd.command("deps").description("Libraries already installed in the sandbox \u2014 import these without --dep").action(async (_opts, cmd2) => {
3024
+ printJson(await clientFrom(cmd2).functions.dependencies(), outputOptions(cmd2));
3025
+ });
3026
+ cmd.command("versions <package>").description("Versions of one package, asked of the package index live").option("--python <v>", "keep only versions compatible with this Python").option("--index-url <url>", "package index to ask (default PyPI)").action(async (pkg, opts, cmd2) => {
3027
+ printJson(
3028
+ await clientFrom(cmd2).functions.dependencyVersions(pkg, {
3029
+ pythonVersion: opts.python,
3030
+ pypiRepoUrl: opts.indexUrl
3031
+ }),
3032
+ outputOptions(cmd2)
3033
+ );
3034
+ });
3035
+ cmd.command("template").description("The handler() skeleton to start from").option("--type <t>", "template type (python)", "python").action(async (opts, cmd2) => {
3036
+ printJson(await clientFrom(cmd2).functions.template(opts.type), outputOptions(cmd2));
3037
+ });
3038
+ groupChildren(cmd, {
3039
+ READ: ["deps", "versions", "template"],
3040
+ RUN: ["run", "infer-schema"]
3041
+ });
3042
+ guide(
3043
+ cmd,
3044
+ `THE ONE HARD RULE
3045
+ The entry point must be a function named \`handler\`, taking one argument:
3046
+
3047
+ def handler(event: Dict[str, Any]) -> Any:
3048
+ return {"sum": event.get("a", 0) + event.get("b", 0)}
3049
+
3050
+ \`--event\` is that argument, the return value comes back as \`result\`, and
3051
+ \`print\` output as \`stdout\`. \`function template\` prints the skeleton.
3052
+
3053
+ READING THE ANSWER
3054
+ Code that raises still answers HTTP 200 \u2014 \`exit_code\` is the verdict and the
3055
+ traceback is in \`stderr\`. This command exits non-zero to match, so \`&&\` works.
3056
+
3057
+ CONTEXT INSIDE THE SANDBOX
3058
+ --conversation-id / --interaction-id reach the sandbox as BKN_CONVERSATION_ID
3059
+ and BKN_INTERACTION_ID, which is how \`sandbox_sdk.bkn\` hangs its own BKN calls
3060
+ under your interaction. The credential does not travel unless you say so:
3061
+ --pass-token puts it in BKN_TOKEN so that code runs as you.
3062
+
3063
+ ORDER OF WORK
3064
+ function deps what is already importable
3065
+ function run ./add.py --event ... iterate here; nothing is kept
3066
+ toolbox create --type function a box to keep it in
3067
+ tool create ./add.py --toolbox the same code, now a tool
3068
+ tool enable <tool-id> --toolbox a tool is off until enabled, then agents
3069
+ can call it`
3070
+ );
3071
+ return group(cmd, "TOOLS & SKILLS");
3072
+ }
1982
3073
 
1983
3074
  // src/commands/model.ts
1984
3075
  import { Command as Command10 } from "commander";
1985
- var int7 = (v) => Number.parseInt(v, 10);
3076
+ var int6 = (v) => Number.parseInt(v, 10);
1986
3077
  async function resolveLlmModelName(client, model) {
1987
3078
  if (!/^\d+$/.test(model)) return model;
1988
3079
  const detail = await client.models.llm.get(model);
@@ -1990,25 +3081,34 @@ async function resolveLlmModelName(client, model) {
1990
3081
  return detail.model_name;
1991
3082
  }
1992
3083
  function addManagementCommands(parent, kind) {
1993
- 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) => {
3084
+ parent.command("add").description("Register a model (definition JSON via --body / --body-file)").option(
3085
+ "--body <json>",
3086
+ "model definition JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (mf-model-manager)"
3087
+ ).option("--body-file <path>", "read model definition JSON from a file").action(async (opts, cmd) => {
1994
3088
  printJson(await clientFrom(cmd).models[kind].add(readBody(opts)), outputOptions(cmd));
1995
3089
  });
1996
- parent.command("edit").description("Update 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) => {
3090
+ parent.command("edit").description("Update a model definition (JSON via --body / --body-file)").option(
3091
+ "--body <json>",
3092
+ "model definition JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (mf-model-manager)"
3093
+ ).option("--body-file <path>", "read model definition JSON from a file").action(async (opts, cmd) => {
1997
3094
  printJson(await clientFrom(cmd).models[kind].edit(readBody(opts)), outputOptions(cmd));
1998
3095
  });
1999
3096
  parent.command("delete <model-ids>").description("Delete model(s) (comma-joined ids)").action(async (ids, _o, cmd) => {
2000
3097
  printJson(await clientFrom(cmd).models[kind].delete(csv(ids) ?? []), outputOptions(cmd));
2001
3098
  });
2002
- parent.command("test").description("Test a model's connectivity / inference (JSON via --body / --body-file)").option("--body <json>", "test request JSON").option("--body-file <path>", "read test request JSON from a file").action(async (opts, cmd) => {
3099
+ parent.command("test").description("Test a model's connectivity / inference (JSON via --body / --body-file)").option(
3100
+ "--body <json>",
3101
+ "test request JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (mf-model-manager)"
3102
+ ).option("--body-file <path>", "read test request JSON from a file").action(async (opts, cmd) => {
2003
3103
  printJson(await clientFrom(cmd).models[kind].test(readBody(opts)), outputOptions(cmd));
2004
3104
  });
2005
3105
  }
2006
3106
  function modelCommand() {
2007
3107
  const model = new Command10("model").description(
2008
- "Model factory \u2014 LLM / small-model CRUD, chat / embeddings / rerank, default selection"
3108
+ "Large and small models: chat, embeddings, rerank, defaults"
2009
3109
  );
2010
3110
  const llm = model.command("llm").description("Large language models");
2011
- 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) => {
3111
+ llm.command("list").description("List LLM models \u2192 {data, count}").option("--name <s>", "filter by name").option("--type <t>", "model type filter").option("--limit <n>", "page size", int6, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int6, 1).action(async (opts, cmd) => {
2012
3112
  printJson(
2013
3113
  await clientFrom(cmd).models.llm.list({
2014
3114
  name: opts.name,
@@ -2041,7 +3141,7 @@ function modelCommand() {
2041
3141
  });
2042
3142
  addManagementCommands(llm, "llm");
2043
3143
  const small = model.command("small").description("Small models (embedding / reranker)");
2044
- 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) => {
3144
+ small.command("list").description("List small models \u2192 {data, count}").option("--name <s>", "filter by name").option("--type <t>", "model type filter").option("--limit <n>", "page size", int6, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int6, 1).action(async (opts, cmd) => {
2045
3145
  printJson(
2046
3146
  await clientFrom(cmd).models.small.list({
2047
3147
  name: opts.name,
@@ -2092,15 +3192,22 @@ Examples:
2092
3192
  $ openbkn model small get-default --type embedding # current default
2093
3193
  $ openbkn model small set-default <id> # default embedding/reranker`
2094
3194
  );
2095
- return group(model, "MODELS & SKILLS");
3195
+ for (const kind of [llm, small]) {
3196
+ groupChildren(kind, {
3197
+ READ: ["list", "get", "get-default"],
3198
+ RUN: ["chat", "embeddings", "rerank", "test"],
3199
+ WRITE: ["add", "edit", "delete", "set-default", "unset-default"]
3200
+ });
3201
+ }
3202
+ return group(model, "MODELS");
2096
3203
  }
2097
3204
 
2098
3205
  // src/commands/resource.ts
2099
3206
  import { Command as Command11 } from "commander";
2100
- var int8 = (v) => Number.parseInt(v, 10);
3207
+ var int7 = (v) => Number.parseInt(v, 10);
2101
3208
  function resourceCommand() {
2102
- const cmd = new Command11("resource").alias("res").description("Resources \u2014 list, find, get, query, delete");
2103
- cmd.command("list").description("List resources under a catalog").option("--catalog-id <id>", "filter by catalog id").option("--category <c>", "resource category (table | logicview | dataset)").option("--type <c>", "alias of --category").option("--status <status>", "filter by status").option("--schema <name>", "filter by source schema").option("--limit <n>", "page size", int8, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int8, 0).option("--sort <field>", "sort field: name | create_time | update_time").option("--direction <dir>", "sort direction: asc | desc").action(async (opts, cmd2) => {
3209
+ const cmd = new Command11("resource").alias("res").description("Tables and views behind a network: find, inspect, sample, enable, disable");
3210
+ cmd.command("list").description("List resources under a catalog").option("--catalog-id <id>", "filter by catalog id").option("--category <c>", "resource category (table | logicview | dataset)").option("--type <c>", "alias of --category").option("--status <status>", "filter by status").option("--schema <name>", "filter by source schema").option("--limit <n>", "page size", int7, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int7, 0).option("--sort <field>", "sort field: name | create_time | update_time").option("--direction <dir>", "sort direction: asc | desc").action(async (opts, cmd2) => {
2104
3211
  const data = await clientFrom(cmd2).resource.list({
2105
3212
  catalogId: opts.catalogId,
2106
3213
  category: opts.category ?? opts.type,
@@ -2113,7 +3220,7 @@ function resourceCommand() {
2113
3220
  });
2114
3221
  printJson(data, outputOptions(cmd2));
2115
3222
  });
2116
- cmd.command("find").description("Search resources by name (fuzzy; --exact for strict)").requiredOption("--name <name>", "resource name to search").option("--exact", "exact name match").option("--catalog-id <id>", "limit to a catalog").option("--limit <n>", "rows to scan before filtering", int8, DEFAULT_LIST_LIMIT).action(async (opts, cmd2) => {
3223
+ cmd.command("find").description("Search resources by name, fuzzy unless --exact \u2192 a bare array, not an envelope").requiredOption("--name <name>", "resource name to search").option("--exact", "exact name match").option("--catalog-id <id>", "limit to a catalog").option("--limit <n>", "rows to scan before filtering", int7, DEFAULT_LIST_LIMIT).action(async (opts, cmd2) => {
2117
3224
  const data = await clientFrom(cmd2).resource.find(opts.name, {
2118
3225
  exact: opts.exact,
2119
3226
  catalogId: opts.catalogId,
@@ -2124,7 +3231,14 @@ function resourceCommand() {
2124
3231
  cmd.command("get <id>").description("Get resource details").action(async (id, _opts, cmd2) => {
2125
3232
  printJson(await clientFrom(cmd2).resource.get(id), outputOptions(cmd2));
2126
3233
  });
2127
- 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("--paging-mode <mode>", "paging mode: single | cursor").option("--keep-alive-sec <s>", "cursor keep-alive in seconds (60\u20133600)", int8).option("--cursor <cursor>", "opaque cursor returned by the previous page").option("--need-total", "include total count").action(async (id, opts, cmd2) => {
3234
+ for (const action of ["enable", "disable"]) {
3235
+ cmd.command(`${action} <id>`).description(`${action[0]?.toUpperCase()}${action.slice(1)} a resource`).action(async (id, _opts, cmd2) => {
3236
+ const api = clientFrom(cmd2).resource;
3237
+ const result = action === "enable" ? await api.enable(id) : await api.disable(id);
3238
+ printJson(result, outputOptions(cmd2));
3239
+ });
3240
+ }
3241
+ cmd.command("query <id>").description("Fetch data rows from a resource").option("--limit <n>", "row limit", int7, DEFAULT_QUERY_LIMIT).option("--offset <n>", "row offset", int7, 0).option("--paging-mode <mode>", "paging mode: single | cursor").option("--keep-alive-sec <s>", "cursor keep-alive in seconds (60\u20133600)", int7).option("--cursor <cursor>", "opaque cursor returned by the previous page").option("--need-total", "include total count").action(async (id, opts, cmd2) => {
2128
3242
  const data = await clientFrom(cmd2).resource.query(id, {
2129
3243
  limit: opts.limit,
2130
3244
  offset: opts.offset,
@@ -2138,12 +3252,17 @@ function resourceCommand() {
2138
3252
  cmd.command("delete <id>").description("Delete a resource").option("-y, --yes", "skip confirmation").action(async (id, _opts, cmd2) => {
2139
3253
  printJson(await clientFrom(cmd2).resource.delete(id), outputOptions(cmd2));
2140
3254
  });
2141
- return group(cmd, "AI DATA PLATFORM");
3255
+ groupChildren(cmd, {
3256
+ READ: ["list", "find", "get"],
3257
+ RUN: ["query"],
3258
+ WRITE: ["enable", "disable", "delete"]
3259
+ });
3260
+ return group(cmd, "DATA & KNOWLEDGE");
2142
3261
  }
2143
3262
 
2144
3263
  // src/commands/skill.ts
2145
3264
  import { Command as Command12 } from "commander";
2146
- var int9 = (v) => Number.parseInt(v, 10);
3265
+ var int8 = (v) => Number.parseInt(v, 10);
2147
3266
  var positiveInt = (flag) => (v) => {
2148
3267
  if (!/^\d+$/.test(v)) {
2149
3268
  throw new InputError(`${flag} must be a positive integer (got '${v}')`);
@@ -2168,9 +3287,15 @@ function checkSource(source) {
2168
3287
  }
2169
3288
  var draftOption = (c) => c.option("--draft", "read the draft (management) version instead of the published one");
2170
3289
  function skillCommand() {
2171
- const cmd = new Command12("skill").description("Skill registry and market");
2172
- 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);
2173
- listOpts(cmd.command("list").description("List skills")).option("--create-user <s>", "filter by creator").action(async (opts, cmd2) => {
3290
+ const cmd = new Command12("skill").description(
3291
+ "Skill packages (SKILL.md + files) agents load on demand"
3292
+ );
3293
+ 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", int8, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int8, 1);
3294
+ listOpts(
3295
+ cmd.command("list").description(
3296
+ "List skills \u2192 {data, total, page, page_size, has_next}; the id to reuse is `skill_id`"
3297
+ )
3298
+ ).option("--create-user <s>", "filter by creator").action(async (opts, cmd2) => {
2174
3299
  printJson(
2175
3300
  await clientFrom(cmd2).skills.list({
2176
3301
  name: opts.name,
@@ -2186,19 +3311,19 @@ function skillCommand() {
2186
3311
  cmd.command("get <skill-id>").description("Get a skill by id").action(async (id, _opts, cmd2) => {
2187
3312
  printJson(await clientFrom(cmd2).skills.get(id), outputOptions(cmd2));
2188
3313
  });
2189
- listOpts(cmd.command("market").description("Browse the skill market")).action(
2190
- async (opts, cmd2) => {
2191
- printJson(
2192
- await clientFrom(cmd2).skills.market({
2193
- name: opts.name,
2194
- source: opts.source,
2195
- pageSize: opts.limit,
2196
- page: opts.page
2197
- }),
2198
- outputOptions(cmd2)
2199
- );
2200
- }
2201
- );
3314
+ listOpts(
3315
+ cmd.command("market").description("Browse the skill market \u2192 {data, total, page, has_next}")
3316
+ ).action(async (opts, cmd2) => {
3317
+ printJson(
3318
+ await clientFrom(cmd2).skills.market({
3319
+ name: opts.name,
3320
+ source: opts.source,
3321
+ pageSize: opts.limit,
3322
+ page: opts.page
3323
+ }),
3324
+ outputOptions(cmd2)
3325
+ );
3326
+ });
2202
3327
  cmd.command("market-get <skill-id>").description("Get a market skill by id").action(async (id, _opts, cmd2) => {
2203
3328
  printJson(await clientFrom(cmd2).skills.marketGet(id), outputOptions(cmd2));
2204
3329
  });
@@ -2325,7 +3450,10 @@ ${files.length} files, ${bytes} B
2325
3450
  cmd.command("install <skill-id> [directory]").description("Download a skill archive and extract it locally").action(async (skillId, dir, _o, cmd2) => {
2326
3451
  printJson(await clientFrom(cmd2).skills.install(skillId, dir), outputOptions(cmd2));
2327
3452
  });
2328
- cmd.command("update-metadata <skill-id>").description("Update a skill's metadata (--body / --body-file JSON)").option("--body <json>", "metadata JSON").option("--body-file <path>", "read metadata JSON from a file").action(async (skillId, opts, cmd2) => {
3453
+ cmd.command("update-metadata <skill-id>").description("Update a skill's metadata (--body / --body-file JSON)").option(
3454
+ "--body <json>",
3455
+ "metadata JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (execution-factory)"
3456
+ ).option("--body-file <path>", "read metadata JSON from a file").action(async (skillId, opts, cmd2) => {
2329
3457
  printJson(
2330
3458
  await clientFrom(cmd2).skills.updateMetadata(skillId, readBody(opts)),
2331
3459
  outputOptions(cmd2)
@@ -2343,15 +3471,53 @@ ${files.length} files, ${bytes} B
2343
3471
  outputOptions(cmd2)
2344
3472
  );
2345
3473
  });
2346
- return group(cmd, "MODELS & SKILLS");
3474
+ groupChildren(cmd, {
3475
+ READ: [
3476
+ "list",
3477
+ "market",
3478
+ "get",
3479
+ "market-get",
3480
+ "names",
3481
+ "content",
3482
+ "read-file",
3483
+ "files",
3484
+ "history"
3485
+ ],
3486
+ RUN: ["execute", "download", "install"],
3487
+ WRITE: [
3488
+ "register",
3489
+ "update-metadata",
3490
+ "update-package",
3491
+ "set-status",
3492
+ "republish",
3493
+ "publish-history",
3494
+ "delete"
3495
+ ]
3496
+ });
3497
+ guide(
3498
+ cmd,
3499
+ `READING A SKILL
3500
+ content <id> gives the SKILL.md index; files <id> [path] walks the package; read-file
3501
+ pulls one file. Read progressively \u2014 do not download the whole archive to answer a question.
3502
+
3503
+ PUBLISHED VS DRAFT
3504
+ Read commands return the published version. --draft reads the editing copy instead, which
3505
+ is what a Studio user sees. The two differ whenever changes are unpublished.
3506
+
3507
+ AUTHORING
3508
+ register <dir> zips and registers; update-package replaces the files; update-metadata
3509
+ changes only the metadata. set-status and republish move versions around.`
3510
+ );
3511
+ return group(cmd, "TOOLS & SKILLS");
2347
3512
  }
2348
3513
 
2349
3514
  // src/commands/toolbox.ts
2350
3515
  import { Command as Command13 } from "commander";
2351
- var int10 = (v) => Number.parseInt(v, 10);
3516
+ import yaml from "js-yaml";
3517
+ var int9 = (v) => Number.parseInt(v, 10);
2352
3518
  function toolboxCommand() {
2353
- const cmd = new Command13("toolbox").description("Agent toolbox lifecycle");
2354
- 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) => {
3519
+ const cmd = new Command13("toolbox").description("Toolboxes: group tools into one publishable box");
3520
+ cmd.command("list").description("List toolboxes").option("--keyword <s>", "filter by keyword").option("--limit <n>", "page size", int9, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int9, 0).action(async (opts, cmd2) => {
2355
3521
  printJson(
2356
3522
  await clientFrom(cmd2).toolboxes.list({
2357
3523
  keyword: opts.keyword,
@@ -2361,12 +3527,21 @@ function toolboxCommand() {
2361
3527
  outputOptions(cmd2)
2362
3528
  );
2363
3529
  });
2364
- cmd.command("create").description("Create a toolbox").requiredOption("--name <name>", "toolbox name").requiredOption("--service-url <url>", "tool service URL").option("--description <d>", "description").action(async (opts, cmd2) => {
3530
+ cmd.command("create").description(
3531
+ "Create a toolbox \u2014 openapi proxies to a service, function holds platform functions"
3532
+ ).requiredOption("--name <name>", "toolbox name").option("--service-url <url>", "where an openapi box proxies its tools; required for that type").option("--type <t>", "openapi | function", "openapi").option("--description <d>", "description").action(async (opts, cmd2) => {
3533
+ if (opts.type !== "openapi" && opts.type !== "function") {
3534
+ throw new InputError("--type must be openapi or function");
3535
+ }
3536
+ if (opts.type === "openapi" && !opts.serviceUrl) {
3537
+ throw new InputError("--service-url is required for an openapi toolbox");
3538
+ }
2365
3539
  printJson(
2366
3540
  await clientFrom(cmd2).toolboxes.create({
2367
3541
  name: opts.name,
2368
3542
  serviceUrl: opts.serviceUrl,
2369
- description: opts.description
3543
+ description: opts.description,
3544
+ metadataType: opts.type
2370
3545
  }),
2371
3546
  outputOptions(cmd2)
2372
3547
  );
@@ -2389,11 +3564,36 @@ function toolboxCommand() {
2389
3564
  cmd.command("import <file>").description("Import a toolbox config from a local .adp file").option("--type <t>", "impex type: toolbox | mcp | operator", "toolbox").action(async (file, opts, cmd2) => {
2390
3565
  printJson(await clientFrom(cmd2).toolboxes.import(file, opts.type), outputOptions(cmd2));
2391
3566
  });
2392
- return group(cmd, "DECISION AGENT");
3567
+ groupChildren(cmd, {
3568
+ READ: ["list", "export"],
3569
+ WRITE: ["create", "publish", "unpublish", "delete", "import"]
3570
+ });
3571
+ guide(
3572
+ cmd,
3573
+ `TWO KINDS OF BOX
3574
+ --type openapi its tools proxy to --service-url; they come from a spec
3575
+ --type function its tools are platform functions, no service URL to give
3576
+
3577
+ ORDER OF WORK
3578
+ toolbox create --name "<n>" an empty box, in draft
3579
+ tool create ./add.py --toolbox a function tool, or --type openapi for a spec
3580
+ tool enable <tool-ids...> a tool is off until enabled
3581
+ toolbox publish <box-id> the box becomes visible in the market
3582
+ tool execute <tool-id> call an enabled tool
3583
+ tool debug <tool-id> call one that is not, while building it
3584
+
3585
+ Publishing the box is about the market, not about calling: an enabled tool in
3586
+ an unpublished box executes. \`tool enable\` is the gate.
3587
+
3588
+ export / import move a whole box between deploys as an .adp file.`
3589
+ );
3590
+ return group(cmd, "TOOLS & SKILLS");
2393
3591
  }
2394
3592
  function toolCommand() {
2395
- const cmd = new Command13("tool").description("Tools inside a toolbox");
2396
- cmd.command("list").description("List tools in a toolbox").requiredOption("--toolbox <box-id>", "toolbox id").option("--limit <n>", "page size (backend default 10, max 100)", int10).option("--page <n>", "page (1-based; backend default 1)", int10).option("--all", "return every tool, ignoring page size").action(async (opts, cmd2) => {
3593
+ const cmd = new Command13("tool").description(
3594
+ "Tools in a box: add one from code or a spec, enable it, call it"
3595
+ );
3596
+ cmd.command("list").description("List tools in a toolbox").requiredOption("--toolbox <box-id>", "toolbox id").option("--limit <n>", "page size (backend default 10, max 100)", int9).option("--page <n>", "page (1-based; backend default 1)", int9).option("--all", "return every tool, ignoring page size").action(async (opts, cmd2) => {
2397
3597
  printJson(
2398
3598
  await clientFrom(cmd2).toolboxes.tools(opts.toolbox, {
2399
3599
  page: opts.page,
@@ -2415,7 +3615,10 @@ function toolCommand() {
2415
3615
  outputOptions(cmd2)
2416
3616
  );
2417
3617
  });
2418
- 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);
3618
+ const invokeOpts = (c) => c.requiredOption("--toolbox <box-id>", "toolbox id").option(
3619
+ "--body <json>",
3620
+ "request body JSON \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (execution-factory)"
3621
+ ).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", int9);
2419
3622
  const parseJson = (s, label) => {
2420
3623
  if (!s) return void 0;
2421
3624
  try {
@@ -2431,39 +3634,101 @@ function toolCommand() {
2431
3634
  path: parseJson(opts.path, "path"),
2432
3635
  timeout: opts.timeout ? Number(opts.timeout) : void 0
2433
3636
  });
3637
+ invokeOpts(cmd.command("execute <tool-id>").description("Invoke an enabled tool")).action(
3638
+ async (toolId, opts, cmd2) => {
3639
+ printJson(
3640
+ await clientFrom(cmd2).toolboxes.execute(opts.toolbox, toolId, buildEnvelope(opts)),
3641
+ outputOptions(cmd2)
3642
+ );
3643
+ }
3644
+ );
2434
3645
  invokeOpts(
2435
- cmd.command("execute <tool-id>").description("Invoke a published+enabled tool")
3646
+ cmd.command("debug <tool-id>").description("Invoke a tool that is not enabled yet")
2436
3647
  ).action(async (toolId, opts, cmd2) => {
2437
3648
  printJson(
2438
- await clientFrom(cmd2).toolboxes.execute(opts.toolbox, toolId, buildEnvelope(opts)),
3649
+ await clientFrom(cmd2).toolboxes.debug(opts.toolbox, toolId, buildEnvelope(opts)),
2439
3650
  outputOptions(cmd2)
2440
3651
  );
2441
3652
  });
2442
- invokeOpts(
2443
- cmd.command("debug <tool-id>").description("Invoke a tool (draft/disabled too)")
2444
- ).action(async (toolId, opts, cmd2) => {
3653
+ const toolFrom = (file, opts) => {
3654
+ if (opts.type === "openapi") {
3655
+ let data;
3656
+ try {
3657
+ data = yaml.load(readCode(file));
3658
+ } catch (err) {
3659
+ throw new InputError(
3660
+ `${file} is not valid JSON or YAML: ${err instanceof Error ? err.message : err}`
3661
+ );
3662
+ }
3663
+ return { metadataType: "openapi", data, useRule: opts.useRule };
3664
+ }
3665
+ if (opts.type !== "function") throw new InputError("--type must be function or openapi");
3666
+ return {
3667
+ metadataType: "function",
3668
+ function: functionDefinitionFrom(file, opts),
3669
+ useRule: opts.useRule
3670
+ };
3671
+ };
3672
+ definitionFlags(
3673
+ cmd.command("create <file>").description(
3674
+ "Create a tool from code (or a spec) \u2014 the only way to add a function tool to a box"
3675
+ ).requiredOption("--toolbox <box-id>", "target toolbox id").option("--use-rule <s>", "usage rule carried onto the tool")
3676
+ ).action(async (file, opts, cmd2) => {
3677
+ const result = await clientFrom(cmd2).toolboxes.createTool(
3678
+ opts.toolbox,
3679
+ toolFrom(file, opts)
3680
+ );
3681
+ printJson(result, outputOptions(cmd2));
3682
+ if (result?.failure_count) process.exitCode = 1;
3683
+ });
3684
+ cmd.command("get <tool-id>").description("One tool in full: metadata, parameters, usage rule").requiredOption("--toolbox <box-id>", "toolbox id").action(async (toolId, opts, cmd2) => {
3685
+ printJson(await clientFrom(cmd2).toolboxes.getTool(opts.toolbox, toolId), outputOptions(cmd2));
3686
+ });
3687
+ definitionFlags(
3688
+ cmd.command("update <tool-id> <file>").description("Replace a tool's definition; the id survives and an enabled tool stays enabled").requiredOption("--toolbox <box-id>", "toolbox id").option("--use-rule <s>", "usage rule carried onto the tool")
3689
+ ).action(async (toolId, file, opts, cmd2) => {
3690
+ if (!opts.name || !opts.description) {
3691
+ throw new InputError(
3692
+ "--name and --description are required: update replaces the tool, it does not patch it"
3693
+ );
3694
+ }
2445
3695
  printJson(
2446
- await clientFrom(cmd2).toolboxes.debug(opts.toolbox, toolId, buildEnvelope(opts)),
3696
+ await clientFrom(cmd2).toolboxes.updateTool(opts.toolbox, toolId, {
3697
+ ...toolFrom(file, opts),
3698
+ name: opts.name,
3699
+ description: opts.description
3700
+ }),
2447
3701
  outputOptions(cmd2)
2448
3702
  );
2449
3703
  });
2450
- cmd.command("upload <file>").description("Upload a tool definition file (OpenAPI spec) into a toolbox").requiredOption("--toolbox <id>", "target toolbox id").option("--metadata-type <t>", "metadata type", "openapi").action(async (file, opts, cmd2) => {
3704
+ cmd.command("delete <tool-ids...>").description("Delete tools from a toolbox").requiredOption("--toolbox <box-id>", "toolbox id").option("-y, --yes", "skip confirmation").action(async (toolIds, opts, cmd2) => {
3705
+ printJson(
3706
+ await clientFrom(cmd2).toolboxes.deleteTools(opts.toolbox, toolIds),
3707
+ outputOptions(cmd2)
3708
+ );
3709
+ });
3710
+ cmd.command("upload <file>").description("Add tools from an OpenAPI file \u2014 `tool create` is the same endpoint, as JSON").requiredOption("--toolbox <id>", "target toolbox id").option("--metadata-type <t>", "metadata type", "openapi").action(async (file, opts, cmd2) => {
2451
3711
  printJson(
2452
3712
  await clientFrom(cmd2).toolboxes.upload(opts.toolbox, file, opts.metadataType),
2453
3713
  outputOptions(cmd2)
2454
3714
  );
2455
3715
  });
2456
- return group(cmd, "DECISION AGENT");
3716
+ groupChildren(cmd, {
3717
+ READ: ["list", "get"],
3718
+ RUN: ["execute", "debug"],
3719
+ WRITE: ["create", "update", "delete", "enable", "disable", "upload"]
3720
+ });
3721
+ return group(cmd, "TOOLS & SKILLS");
2457
3722
  }
2458
3723
 
2459
3724
  // src/commands/trace.ts
2460
- import { readFileSync as readFileSync5, writeFileSync } from "fs";
3725
+ import { readFileSync as readFileSync6, writeFileSync } from "fs";
2461
3726
  import { Command as Command14 } from "commander";
2462
3727
 
2463
3728
  // src/bkn-trace/schema-validate.ts
2464
- import { readFileSync as readFileSync4 } from "fs";
3729
+ import { readFileSync as readFileSync5 } from "fs";
2465
3730
  import { extname } from "path";
2466
- import yaml from "js-yaml";
3731
+ import yaml2 from "js-yaml";
2467
3732
  import { z } from "zod";
2468
3733
  var Assertion = z.object({
2469
3734
  type: z.enum([
@@ -2498,9 +3763,9 @@ var DiagnosisRule = z.object({
2498
3763
  params: z.record(z.string(), z.unknown()).optional()
2499
3764
  });
2500
3765
  function parseFile(file) {
2501
- const text = readFileSync4(file, "utf8");
3766
+ const text = readFileSync5(file, "utf8");
2502
3767
  const ext = extname(file).toLowerCase();
2503
- if (ext === ".yaml" || ext === ".yml") return yaml.load(text);
3768
+ if (ext === ".yaml" || ext === ".yml") return yaml2.load(text);
2504
3769
  return parseBigIntJSON(text);
2505
3770
  }
2506
3771
  function inferKind(data) {
@@ -2572,7 +3837,7 @@ function renderTechnicalTraceDetail(detail) {
2572
3837
  }
2573
3838
  function traceCommand() {
2574
3839
  const cmd = new Command14("trace").description(
2575
- "BKN Trace \u2014 fetch spans, diagnose (symbolic + LLM rubric), scan, eval-set, schema validate"
3840
+ "Inspect what an agent actually did, and diagnose bad answers"
2576
3841
  );
2577
3842
  cmd.command("graph <trace-id>").description("Fetch normalized trace graph by trace id").action(async (traceId, _opts, cmd2) => {
2578
3843
  printJson(await clientFrom(cmd2).trace.graph(traceId), outputOptions(cmd2));
@@ -2653,7 +3918,10 @@ function traceCommand() {
2653
3918
  );
2654
3919
  });
2655
3920
  for (const action of ["complete", "fail", "cancel", "handoff"]) {
2656
- interactions.command(`${action} <interaction-id>`).description(`${action} a managed interaction using a 3.0 completion manifest`).requiredOption("--body-file <path>", "read completion manifest JSON from a protected file").action(async (interactionId, opts, cmd2) => {
3921
+ interactions.command(`${action} <interaction-id>`).description(`${action} a managed interaction using a 3.0 completion manifest`).requiredOption(
3922
+ "--body-file <path>",
3923
+ "read completion manifest JSON from a protected file \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (agent-observability)"
3924
+ ).action(async (interactionId, opts, cmd2) => {
2657
3925
  const input = readBody(opts);
2658
3926
  const lifecycle = clientFrom(cmd2).trace.lifecycle;
2659
3927
  const terminal = {
@@ -2682,7 +3950,10 @@ function traceCommand() {
2682
3950
  outputOptions(cmd2)
2683
3951
  );
2684
3952
  });
2685
- operations.command("retry <operation-id>").description("Create the next retry attempt for an eligible failed operation").requiredOption("--body-file <path>", "read retry request JSON from a protected file").action(async (operationId, opts, cmd2) => {
3953
+ operations.command("retry <operation-id>").description("Create the next retry attempt for an eligible failed operation").requiredOption(
3954
+ "--body-file <path>",
3955
+ "read retry request JSON from a protected file \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (agent-observability)"
3956
+ ).action(async (operationId, opts, cmd2) => {
2686
3957
  printJson(
2687
3958
  await clientFrom(cmd2).trace.lifecycle.retryOperationAttempt(
2688
3959
  operationId,
@@ -2713,7 +3984,7 @@ function traceCommand() {
2713
3984
  outputOptions(cmd2)
2714
3985
  );
2715
3986
  });
2716
- cmd.command("search").description("List authorized technical traces").option("--limit <n>", "page size, 1..200", (value) => Number.parseInt(value, 10)).option("--cursor <cursor>", "opaque pagination cursor").option("--from <time>", "started at or after this RFC3339 timestamp").option("--to <time>", "started at or before this RFC3339 timestamp").option("--status <status>", "execution status").option("--service <service>", "exact producing service").option("--tool <tool>", "exact root tool").option("--trace-id <id>", "exact Trace ID").option("--error-keyword <text>", "case-insensitive error text").option("--conversation-id <id>", "exact conversation ID").option("--interaction-id <id>", "exact interaction ID").action(async (opts, cmd2) => {
3987
+ cmd.command("search").description("List authorized technical traces \u2192 {entries, total, next_cursor, partial}").option("--limit <n>", "page size, 1..200", (value) => Number.parseInt(value, 10)).option("--cursor <cursor>", "opaque pagination cursor").option("--from <time>", "started at or after this RFC3339 timestamp").option("--to <time>", "started at or before this RFC3339 timestamp").option("--status <status>", "execution status").option("--service <service>", "exact producing service").option("--tool <tool>", "exact root tool").option("--trace-id <id>", "exact Trace ID").option("--error-keyword <text>", "case-insensitive error text").option("--conversation-id <id>", "exact conversation ID").option("--interaction-id <id>", "exact interaction ID").action(async (opts, cmd2) => {
2717
3988
  printJson(
2718
3989
  await clientFrom(cmd2).trace.search({
2719
3990
  limit: opts.limit,
@@ -2746,9 +4017,9 @@ function traceCommand() {
2746
4017
  outputOptions(cmd2)
2747
4018
  );
2748
4019
  });
2749
- const evalSet = cmd.command("eval-set").description("Build + run trace eval sets");
4020
+ const evalSet = cmd.command("eval-set").description("Build trace eval sets");
2750
4021
  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) => {
2751
- const raw = parseBigIntJSON(readFileSync5(queriesFile, "utf8"));
4022
+ const raw = parseBigIntJSON(readFileSync6(queriesFile, "utf8"));
2752
4023
  const cases = clientFrom(cmd2).trace.evalSetBuild(raw);
2753
4024
  if (opts.out) {
2754
4025
  writeFileSync(opts.out, stringifyBigIntJSON({ cases }, 2));
@@ -2757,16 +4028,6 @@ function traceCommand() {
2757
4028
  printJson({ cases }, outputOptions(cmd2));
2758
4029
  }
2759
4030
  });
2760
- 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) => {
2761
- const raw = parseBigIntJSON(readFileSync5(casesFile, "utf8"));
2762
- const cases = clientFrom(cmd2).trace.evalSetBuild(raw);
2763
- const result = await clientFrom(cmd2).trace.evalSetTest(opts.agent, cases, {
2764
- version: opts.version,
2765
- llm: Boolean(opts.llm)
2766
- });
2767
- printJson(result, outputOptions(cmd2));
2768
- if (result.failed > 0) process.exitCode = 1;
2769
- });
2770
4031
  const schema = cmd.command("schema").description("Validate eval-set / diagnosis-rule files");
2771
4032
  schema.command("validate <file>").description("Validate an eval-set or diagnosis-rule file (JSON/YAML) against its schema").option("--kind <k>", "force schema kind: eval-set | rule (default: auto-detect)").action(async (file, opts, cmd2) => {
2772
4033
  const result = validateSchemaFile(file, opts.kind);
@@ -2778,12 +4039,33 @@ function traceCommand() {
2778
4039
  printJson(result, outputOptions(cmd2));
2779
4040
  if (!result.ok) process.exitCode = 1;
2780
4041
  });
2781
- return group(cmd, "TRACE AI");
4042
+ groupChildren(cmd, {
4043
+ GROUPS: ["conversations", "interactions", "operations", "receipts", "eval-set", "schema"],
4044
+ READ: ["graph", "get", "search", "detail", "spans"],
4045
+ RUN: ["diagnose", "scan", "validate-fixture"]
4046
+ });
4047
+ guide(
4048
+ cmd,
4049
+ `FINDING A CONVERSATION
4050
+ conversations list gives conversation ids; get <conversation-id> pulls its spans.
4051
+
4052
+ DIAGNOSING
4053
+ diagnose <conversation-id> symbolic rules only, no model needed
4054
+ diagnose <conversation-id> --llm adds rubric judging + a synthesized summary; needs a
4055
+ local \`claude\` CLI on PATH, silently degrades without it
4056
+ scan <id,id,...> the same over several conversations, aggregated
4057
+
4058
+ MANAGED LIFECYCLE
4059
+ conversations / interactions / operations / receipts are the write side: an agent opens an
4060
+ interaction, reports operations, then completes it. Those bodies are 3.0 manifests,
4061
+ documented at https://openbkn-ai.github.io/bkn-foundry/ (agent-observability).`
4062
+ );
4063
+ return group(cmd, "TRACING");
2782
4064
  }
2783
4065
 
2784
4066
  // src/commands/vega.ts
2785
4067
  import { Command as Command15 } from "commander";
2786
- var int11 = (value) => {
4068
+ var int10 = (value) => {
2787
4069
  const parsed = Number(value);
2788
4070
  if (!Number.isSafeInteger(parsed)) {
2789
4071
  throw new InputError(`expected an integer, received "${value}"`);
@@ -2878,6 +4160,16 @@ var buildTaskSort = (raw) => {
2878
4160
  }
2879
4161
  return parsed.data;
2880
4162
  };
4163
+ var buildTaskExecuteType = (raw) => {
4164
+ if (raw === void 0) return void 0;
4165
+ const parsed = BuildTaskExecuteType.safeParse(raw);
4166
+ if (!parsed.success) {
4167
+ throw new InputError(
4168
+ `invalid build task execute type "${raw}"; expected one of ${BuildTaskExecuteType.options.join(", ")}`
4169
+ );
4170
+ }
4171
+ return parsed.data;
4172
+ };
2881
4173
  var discoverStrategy = (raw) => {
2882
4174
  if (raw === void 0) return void 0;
2883
4175
  const parsed = DiscoverStrategy.safeParse(raw);
@@ -2975,10 +4267,10 @@ var sortDirection = (raw) => {
2975
4267
  };
2976
4268
  function vegaCommand() {
2977
4269
  const vega = new Command15("vega").description(
2978
- "Vega observability \u2014 catalog, resources, index build tasks"
4270
+ "Data sources: catalogs, connectors, SQL, index builds"
2979
4271
  );
2980
4272
  const catalog = vega.command("catalog").description("Catalog entries");
2981
- catalog.command("list").description("List catalog entries").option("--limit <n>", "page size", int11, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int11, 0).option("--name <s>", "filter by name").option("--tag <s>", "filter by tag").option("--type <type>", "filter by catalog type: physical | logical").option("--connector-type <type>", "filter by connector type").option("--enabled <bool>", "filter by enabled state").option("--health-check-status <s>", "filter by health status").option("--sort <field>", "sort field: name | create_time | update_time").option("--direction <dir>", "sort direction: asc | desc").action(async (_opts, cmd) => {
4273
+ catalog.command("list").description("List catalog entries").option("--limit <n>", "page size", int10, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int10, 0).option("--name <s>", "filter by name").option("--tag <s>", "filter by tag").option("--type <type>", "filter by catalog type: physical | logical").option("--connector-type <type>", "filter by connector type").option("--enabled <bool>", "filter by enabled state").option("--health-check-status <s>", "filter by health status").option("--sort <field>", "sort field: name | create_time | update_time").option("--direction <dir>", "sort direction: asc | desc").action(async (_opts, cmd) => {
2982
4274
  const o = cmd.optsWithGlobals();
2983
4275
  const data = await clientFrom(cmd).vega.catalogs({
2984
4276
  limit: o.limit,
@@ -2997,7 +4289,7 @@ function vegaCommand() {
2997
4289
  catalog.command("get <id>").description("Get a catalog by id").action(async (id, _opts, cmd) => {
2998
4290
  printJson(await clientFrom(cmd).vega.getCatalog(id), outputOptions(cmd));
2999
4291
  });
3000
- catalog.command("resources <id>").description("List resources under a catalog").option("--category <c>", "filter by category (e.g. table)").option("--limit <n>", "page size (default 30, max 1000; -1 = all)", int11).option("--offset <n>", "page offset", int11, 0).action(async (id, opts, cmd) => {
4292
+ catalog.command("resources <id>").description("List resources under a catalog").option("--category <c>", "filter by category (e.g. table)").option("--limit <n>", "page size (default 30, max 1000; -1 = all)", int10).option("--offset <n>", "page offset", int10, 0).action(async (id, opts, cmd) => {
3001
4293
  printJson(
3002
4294
  await clientFrom(cmd).vega.catalogResources(id, opts.category, opts.limit, opts.offset),
3003
4295
  outputOptions(cmd)
@@ -3098,7 +4390,7 @@ function vegaCommand() {
3098
4390
  );
3099
4391
  });
3100
4392
  const discoverSchedule = vega.command("discover-schedule").description("Resource discovery schedules");
3101
- discoverSchedule.command("list").description("List discovery schedules").option("--name <s>", "filter by name").option("--catalog-id <id>", "filter by catalog id").option("--enabled <bool>", "filter by enabled state", bool).option("--limit <n>", "page size", int11, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int11, 0).option("--sort <field>", "name | create_time | update_time | next_run").option("--direction <dir>", "asc | desc").action(async (opts, cmd) => {
4393
+ discoverSchedule.command("list").description("List discovery schedules").option("--name <s>", "filter by name").option("--catalog-id <id>", "filter by catalog id").option("--enabled <bool>", "filter by enabled state", bool).option("--limit <n>", "page size", int10, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int10, 0).option("--sort <field>", "name | create_time | update_time | next_run").option("--direction <dir>", "asc | desc").action(async (opts, cmd) => {
3102
4394
  printJson(
3103
4395
  await clientFrom(cmd).vega.discoverSchedules({
3104
4396
  name: opts.name,
@@ -3115,7 +4407,7 @@ function vegaCommand() {
3115
4407
  discoverSchedule.command("get <id>").description("Get a discovery schedule").action(async (id, _opts, cmd) => {
3116
4408
  printJson(await clientFrom(cmd).vega.getDiscoverSchedule(id), outputOptions(cmd));
3117
4409
  });
3118
- discoverSchedule.command("create").description("Create a discovery schedule").requiredOption("--name <s>", "schedule name").requiredOption("--catalog-id <id>", "catalog id").requiredOption("--cron <expr>", "five-field cron expression").option("--start-time <ms>", "start time", int11).option("--end-time <ms>", "end time", int11).option("--enabled", "create enabled").option("--strategy <strategy>", `strategy: ${DiscoverStrategy.options.join(" | ")}`).action(async (opts, cmd) => {
4410
+ discoverSchedule.command("create").description("Create a discovery schedule").requiredOption("--name <s>", "schedule name").requiredOption("--catalog-id <id>", "catalog id").requiredOption("--cron <expr>", "five-field cron expression").option("--start-time <ms>", "start time", int10).option("--end-time <ms>", "end time", int10).option("--enabled", "create enabled").option("--strategy <strategy>", `strategy: ${DiscoverStrategy.options.join(" | ")}`).action(async (opts, cmd) => {
3119
4411
  printJson(
3120
4412
  await clientFrom(cmd).vega.createDiscoverSchedule({
3121
4413
  name: opts.name,
@@ -3129,7 +4421,7 @@ function vegaCommand() {
3129
4421
  outputOptions(cmd)
3130
4422
  );
3131
4423
  });
3132
- discoverSchedule.command("update <id>").description("Fully update a discovery schedule").requiredOption("--name <s>", "schedule name").requiredOption("--catalog-id <id>", "current catalog id").requiredOption("--cron <expr>", "five-field cron expression").requiredOption("--enabled <bool>", "current enabled state", bool).requiredOption("--start-time <ms>", "start time (0 = no lower bound)", int11).requiredOption("--end-time <ms>", "end time (0 = no upper bound)", int11).requiredOption("--strategy <strategy>", `strategy: ${DiscoverStrategy.options.join(" | ")}`).requiredOption(
4424
+ discoverSchedule.command("update <id>").description("Fully update a discovery schedule").requiredOption("--name <s>", "schedule name").requiredOption("--catalog-id <id>", "current catalog id").requiredOption("--cron <expr>", "five-field cron expression").requiredOption("--enabled <bool>", "current enabled state", bool).requiredOption("--start-time <ms>", "start time (0 = no lower bound)", int10).requiredOption("--end-time <ms>", "end time (0 = no upper bound)", int10).requiredOption("--strategy <strategy>", `strategy: ${DiscoverStrategy.options.join(" | ")}`).requiredOption(
3133
4425
  "--expected-update-time <ms>",
3134
4426
  "optimistic-lock update time",
3135
4427
  expectedUpdateTime
@@ -3156,10 +4448,11 @@ function vegaCommand() {
3156
4448
  });
3157
4449
  }
3158
4450
  const discoverTask = vega.command("discover-task").description("Resource discovery tasks");
3159
- discoverTask.command("list").description("List discovery tasks").option("--catalog-id <id>", "filter by catalog id").option("--schedule-id <id>", "filter by schedule id").option("--status <status>", `comma-separated: ${VegaTaskStatus.options.join(" | ")}`).option("--strategy <strategy>", `strategy: ${DiscoverStrategy.options.join(" | ")}`).option("--trigger-type <type>", "manual | scheduled").option("--limit <n>", "page size", int11, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int11, 0).option("--sort <field>", "create_time | start_time | finish_time | last_progress_time").option("--direction <dir>", "asc | desc").action(async (opts, cmd) => {
4451
+ discoverTask.command("list").description("List discovery tasks").option("--catalog-id <id>", "filter by catalog id").option("--resource-id <id>", "filter by resource id").option("--schedule-id <id>", "filter by schedule id").option("--status <status>", `comma-separated: ${VegaTaskStatus.options.join(" | ")}`).option("--strategy <strategy>", `strategy: ${DiscoverStrategy.options.join(" | ")}`).option("--trigger-type <type>", "manual | scheduled").option("--limit <n>", "page size", int10, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int10, 0).option("--sort <field>", "create_time | start_time | finish_time | last_progress_time").option("--direction <dir>", "asc | desc").action(async (opts, cmd) => {
3160
4452
  printJson(
3161
4453
  await clientFrom(cmd).vega.discoverTasks({
3162
4454
  catalogId: opts.catalogId,
4455
+ resourceId: opts.resourceId,
3163
4456
  scheduleId: opts.scheduleId,
3164
4457
  status: taskStatuses(opts.status),
3165
4458
  strategy: discoverStrategy(opts.strategy),
@@ -3187,7 +4480,7 @@ function vegaCommand() {
3187
4480
  semanticTask.command("list").description("List semantic-understanding tasks").option("--scope <scope>", `scope: ${SemanticUnderstandingScope.options.join(" | ")}`).option("--catalog-id <id>", "filter by catalog id").option("--resource-id <id>", "filter by resource id").option("--status <status>", `comma-separated: ${VegaTaskStatus.options.join(" | ")}`).option(
3188
4481
  "--apply-mode <mode>",
3189
4482
  `apply mode: ${SemanticUnderstandingApplyMode.options.join(" | ")}`
3190
- ).option("--applied <bool>", "filter by applied state", bool).option("--limit <n>", "page size", int11, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int11, 0).option("--sort <field>", "create_time | start_time | finish_time").option("--direction <dir>", "asc | desc").action(async (opts, cmd) => {
4483
+ ).option("--applied <bool>", "filter by applied state", bool).option("--limit <n>", "page size", int10, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int10, 0).option("--sort <field>", "create_time | start_time | finish_time").option("--direction <dir>", "asc | desc").action(async (opts, cmd) => {
3191
4484
  printJson(
3192
4485
  await clientFrom(cmd).vega.semanticUnderstandingTasks({
3193
4486
  scope: semanticScope(opts.scope),
@@ -3204,7 +4497,7 @@ function vegaCommand() {
3204
4497
  outputOptions(cmd)
3205
4498
  );
3206
4499
  });
3207
- semanticTask.command("create").description("Create a semantic-understanding task").requiredOption("--scope <scope>", "resource | catalog").option("--catalog-id <id>", "catalog id").option("--resource-id <id>", "resource id").option("--apply-mode <mode>", "dry_run | fill_empty | force").option("--confidence-threshold <n>", "minimum confidence (0..1)", confidenceThreshold).option("--include-sample-rows", "include resource sample rows").option("--sample-max-rows <n>", "sample row limit", int11).action(async (opts, cmd) => {
4500
+ semanticTask.command("create").description("Create a semantic-understanding task").requiredOption("--scope <scope>", "resource | catalog").option("--catalog-id <id>", "catalog id").option("--resource-id <id>", "resource id").option("--apply-mode <mode>", "dry_run | fill_empty | force").option("--confidence-threshold <n>", "minimum confidence (0..1)", confidenceThreshold).option("--include-sample-rows", "include resource sample rows").option("--sample-max-rows <n>", "sample row limit", int10).action(async (opts, cmd) => {
3208
4501
  const scope = semanticScope(opts.scope);
3209
4502
  if (!scope) throw new InputError("--scope is required");
3210
4503
  const applyMode = semanticApplyMode(opts.applyMode);
@@ -3260,10 +4553,12 @@ function vegaCommand() {
3260
4553
  connector.command("get <type>").description("Get a connector type").action(async (type, _opts, cmd) => {
3261
4554
  printJson(await clientFrom(cmd).vega.connectorType(type), outputOptions(cmd));
3262
4555
  });
3263
- vega.command("sql").description("Run SQL / OpenSearch DSL directly against a vega-backend data source").option(
4556
+ vega.command("sql").description(
4557
+ "Read-only SQL straight against a data source \u2014 no knowledge network, no Trace record"
4558
+ ).option(
3264
4559
  "--query <sql>",
3265
4560
  "SQL string; reference a resource with a {{<resource-id>}} placeholder"
3266
- ).option("--input-dialect <dialect>", "SQL input dialect: postgres | mysql | trino | duckdb").option("--paging-mode <mode>", "paging mode: single | cursor").option("--limit <n>", "page size (cursor mode requires it)", int11).option("--offset <n>", "first-page offset", int11).option("--keep-alive-sec <s>", "cursor keep-alive in seconds (60\u20133600)", int11).option("--cursor <cursor>", "opaque cursor returned by the previous page").option("--need-total", "include the complete total count").option("--query-timeout-sec <s>", "query timeout in seconds (1\u20133600)", int11).option(
4561
+ ).option("--input-dialect <dialect>", "SQL input dialect: postgres | mysql | trino | duckdb").option("--paging-mode <mode>", "paging mode: single | cursor").option("--limit <n>", "page size (cursor mode requires it)", int10).option("--offset <n>", "first-page offset", int10).option("--keep-alive-sec <s>", "cursor keep-alive in seconds (60\u20133600)", int10).option("--cursor <cursor>", "opaque cursor returned by the previous page").option("--need-total", "include the complete total count").option("--query-timeout-sec <s>", "query timeout in seconds (1\u20133600)", int10).option(
3267
4562
  "-d, --data <json>",
3268
4563
  "full request body as JSON (advanced; wins over individual query flags)"
3269
4564
  ).action(async (opts, cmd) => {
@@ -3307,7 +4602,7 @@ function vegaCommand() {
3307
4602
  printJson(await clientFrom(cmd).vega.sql(body), outputOptions(cmd));
3308
4603
  });
3309
4604
  const resource = vega.command("resource").description("Vega-backend resources");
3310
- resource.command("list").description("List resources").option("--catalog-id <id>", "filter by catalog id").option("--type <category>", "resource category").option("--category <category>", "alias of --type").option("--status <status>", "filter by status").option("--schema <name>", "filter by source schema").option("--limit <n>", "page size", int11, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int11, 0).option("--sort <field>", "sort field: name | create_time | update_time").option("--direction <dir>", "sort direction: asc | desc").action(async (opts, cmd) => {
4605
+ resource.command("list").description("List resources").option("--catalog-id <id>", "filter by catalog id").option("--type <category>", "resource category").option("--category <category>", "alias of --type").option("--status <status>", "filter by status").option("--schema <name>", "filter by source schema").option("--limit <n>", "page size", int10, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int10, 0).option("--sort <field>", "sort field: name | create_time | update_time").option("--direction <dir>", "sort direction: asc | desc").action(async (opts, cmd) => {
3311
4606
  printJson(
3312
4607
  await clientFrom(cmd).resource.list({
3313
4608
  catalogId: opts.catalogId,
@@ -3325,7 +4620,17 @@ function vegaCommand() {
3325
4620
  resource.command("get <id>").description("Get a resource").action(async (id, _opts, cmd) => {
3326
4621
  printJson(await clientFrom(cmd).resource.get(id), outputOptions(cmd));
3327
4622
  });
3328
- resource.command("query <id>").description("Fetch data rows from a resource").option("--limit <n>", "row limit", int11, 50).option("--offset <n>", "row offset", int11, 0).action(async (id, opts, cmd) => {
4623
+ resource.command("discover <id>").description("Trigger metadata discovery for a resource").action(async (id, _opts, cmd) => {
4624
+ printJson(await clientFrom(cmd).vega.discoverResource(id), outputOptions(cmd));
4625
+ });
4626
+ for (const action of ["enable", "disable"]) {
4627
+ resource.command(`${action} <id>`).description(`${action[0]?.toUpperCase()}${action.slice(1)} a resource`).action(async (id, _opts, cmd) => {
4628
+ const api = clientFrom(cmd).resource;
4629
+ const result = action === "enable" ? await api.enable(id) : await api.disable(id);
4630
+ printJson(result, outputOptions(cmd));
4631
+ });
4632
+ }
4633
+ resource.command("query <id>").description("Fetch data rows from a resource").option("--limit <n>", "row limit", int10, 50).option("--offset <n>", "row offset", int10, 0).action(async (id, opts, cmd) => {
3329
4634
  printJson(
3330
4635
  await clientFrom(cmd).resource.query(id, { limit: opts.limit, offset: opts.offset }),
3331
4636
  outputOptions(cmd)
@@ -3337,7 +4642,10 @@ function vegaCommand() {
3337
4642
  outputOptions(cmd)
3338
4643
  );
3339
4644
  });
3340
- resource.command("document-create <resource-id>").description("Create dataset documents").requiredOption("--data <json>", "JSON array of documents").action(async (resourceId, opts, cmd) => {
4645
+ resource.command("document-create <resource-id>").description("Create dataset documents").requiredOption(
4646
+ "--data <json>",
4647
+ "JSON array of documents \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (vega-backend)"
4648
+ ).action(async (resourceId, opts, cmd) => {
3341
4649
  printJson(
3342
4650
  await clientFrom(cmd).resource.createDocuments(
3343
4651
  resourceId,
@@ -3346,7 +4654,10 @@ function vegaCommand() {
3346
4654
  outputOptions(cmd)
3347
4655
  );
3348
4656
  });
3349
- resource.command("document-upsert <resource-id>").description("Upsert dataset documents; every document must have an id").requiredOption("--data <json>", "JSON array of documents").action(async (resourceId, opts, cmd) => {
4657
+ resource.command("document-upsert <resource-id>").description("Upsert dataset documents; every document must have an id").requiredOption(
4658
+ "--data <json>",
4659
+ "JSON array of documents \u2014 docs: https://openbkn-ai.github.io/bkn-foundry/ (vega-backend)"
4660
+ ).action(async (resourceId, opts, cmd) => {
3350
4661
  const documents = parseJsonArray(opts.data, "--data");
3351
4662
  if (documents.some((document) => typeof document.id !== "string")) {
3352
4663
  throw new InputError("every document in --data must have a string id");
@@ -3405,7 +4716,10 @@ function vegaCommand() {
3405
4716
  const task = await clientFrom(cmd).vega.buildStatus(taskId);
3406
4717
  printJson(task, outputOptions(cmd));
3407
4718
  });
3408
- dataset.command("build-list").description("List BuildTasks").option("--limit <n>", "page size", int11, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int11, 0).option("--resource-id <id>", "filter by resource id").option("--catalog-id <id>", "filter by catalog id").option("--status <status>", `comma-separated statuses: ${BuildTaskStatus.options.join(" | ")}`).option("--mode <mode>", "filter by mode: batch | streaming").option("--sort <field>", `sort field: ${BuildTaskSort.options.join(" | ")}`).option("--direction <dir>", `sort direction: ${SortDirection.options.join(" | ")}`).action(async (opts, cmd) => {
4719
+ dataset.command("build-list").description("List BuildTasks").option("--limit <n>", "page size", int10, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int10, 0).option("--resource-id <id>", "filter by resource id").option("--catalog-id <id>", "filter by catalog id").option("--status <status>", `comma-separated statuses: ${BuildTaskStatus.options.join(" | ")}`).option("--mode <mode>", "filter by mode: batch | streaming").option(
4720
+ "--execute-type <type>",
4721
+ `filter by execution type: ${BuildTaskExecuteType.options.join(" | ")}`
4722
+ ).option("--sort <field>", `sort field: ${BuildTaskSort.options.join(" | ")}`).option("--direction <dir>", `sort direction: ${SortDirection.options.join(" | ")}`).action(async (opts, cmd) => {
3409
4723
  printJson(
3410
4724
  await clientFrom(cmd).vega.buildTasks({
3411
4725
  limit: opts.limit,
@@ -3414,6 +4728,7 @@ function vegaCommand() {
3414
4728
  catalogId: opts.catalogId,
3415
4729
  status: buildTaskStatuses(opts.status),
3416
4730
  mode: opts.mode,
4731
+ executeType: buildTaskExecuteType(opts.executeType),
3417
4732
  sort: buildTaskSort(opts.sort),
3418
4733
  direction: sortDirection(opts.direction)
3419
4734
  }),
@@ -3437,36 +4752,134 @@ function vegaCommand() {
3437
4752
  outputOptions(cmd)
3438
4753
  );
3439
4754
  });
3440
- return group(vega, "AI DATA PLATFORM");
4755
+ groupChildren(vega, {
4756
+ GROUPS: [
4757
+ "catalog",
4758
+ "resource",
4759
+ "connector-type",
4760
+ "dataset",
4761
+ "discover-schedule",
4762
+ "discover-task",
4763
+ "semantic-task"
4764
+ ],
4765
+ RUN: ["sql"]
4766
+ });
4767
+ groupChildren(resource, {
4768
+ READ: ["list", "get", "document-get"],
4769
+ RUN: ["query", "discover"],
4770
+ WRITE: [
4771
+ "enable",
4772
+ "disable",
4773
+ "document-create",
4774
+ "document-upsert",
4775
+ "document-delete",
4776
+ "document-delete-filter"
4777
+ ]
4778
+ });
4779
+ groupChildren(catalog, {
4780
+ READ: ["list", "get", "resources", "health", "health-check-schedule"],
4781
+ RUN: ["test-connection", "test-connection-config", "discover"],
4782
+ WRITE: ["create", "update", "enable", "disable", "delete", "set-health-check-schedule"]
4783
+ });
4784
+ guide(
4785
+ vega,
4786
+ `FINDING DATA
4787
+ catalog list -> catalog resources <catalog-id> -> resource get <id>. A physical catalog
4788
+ can be discovered and written; a logical one cannot.
4789
+
4790
+ QUERYING DIRECTLY
4791
+ sql --query "<sql>" runs against the source itself. Name a resource with a
4792
+ {{<resource-id>}} placeholder rather than the physical table it happens to have.
4793
+
4794
+ BUILDING AN INDEX
4795
+ dataset build <resource-id> creates a BuildTask; build-status / build-list follow it.
4796
+ Indexes are per resource \u2014 a knowledge network has no build of its own.`
4797
+ );
4798
+ return group(vega, "DATA & KNOWLEDGE");
4799
+ }
4800
+
4801
+ // src/cli-program.ts
4802
+ function buildProgram() {
4803
+ const program2 = new Command16();
4804
+ program2.name("openbkn").description(
4805
+ "openbkn \u2014 one CLI for the BKN platform: knowledge networks, the data behind them,\nthe tools and skills agents run on them, and the traces they leave."
4806
+ ).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("--conversation-id <id>", "BKN Trace conversation id (env: BKN_CONVERSATION_ID)").option("--interaction-id <id>", "BKN Trace interaction id (env: BKN_INTERACTION_ID)").option(
4807
+ "--new-conversation",
4808
+ "ignore the remembered conversation for this command (see `openbkn context conversation`)"
4809
+ ).option("-k, --insecure", "skip TLS verification (dev / self-signed only)").option("--dry-run", "print the request this command would send, and send nothing").showHelpAfterError();
4810
+ program2.addCommand(authCommand());
4811
+ program2.addCommand(configCommand());
4812
+ program2.addCommand(appkeyCommand());
4813
+ program2.addCommand(bknCommand());
4814
+ program2.addCommand(vegaCommand());
4815
+ program2.addCommand(resourceCommand());
4816
+ program2.addCommand(contextCommand());
4817
+ program2.addCommand(modelCommand());
4818
+ program2.addCommand(skillCommand());
4819
+ program2.addCommand(toolboxCommand());
4820
+ program2.addCommand(toolCommand());
4821
+ program2.addCommand(functionCommand());
4822
+ program2.addCommand(traceCommand());
4823
+ program2.addCommand(adminCommand());
4824
+ program2.addCommand(callCommand());
4825
+ program2.addCommand(describeCommand(program2));
4826
+ guide(
4827
+ program2,
4828
+ `FIRST STEPS
4829
+ openbkn auth login https://your-platform -u <user> -p <pass>
4830
+ openbkn bkn list # knowledge networks you can see
4831
+ openbkn bkn --help # every group has its own help
4832
+ openbkn describe --depth 1 # the whole map as one table; \`describe <command>\`
4833
+ # drills in, --json ships the same tree as data
4834
+
4835
+ COMMON TASKS
4836
+ Answer a question bkn search <kn-id> "<q>" -> context search-schema ->
4837
+ context query-object-instance --args '<json>'
4838
+ Look at the data vega catalog list -> resource find --name <t> -> resource query <id>
4839
+ Build from a catalog bkn create-from-catalog <catalog-id> --name "<n>" ->
4840
+ vega dataset build <resource-id>
4841
+ Edit as files bkn pull <kn-id> ./kn -> bkn validate ./kn -> bkn push ./kn
4842
+ Ship a capability skill register ./my-skill; toolbox create --name "<n>" ->
4843
+ tool upload ./api.yaml --toolbox <id> -> toolbox publish <id>
4844
+ Ship some code function run ./add.py -> toolbox create --name "<n>" --type function
4845
+ -> tool create ./add.py --toolbox <box-id> --name add ->
4846
+ tool enable <tool-id> --toolbox <box-id>
4847
+ Debug an answer trace conversations list -> trace diagnose <conversation-id> --llm
4848
+
4849
+ GOOD TO KNOW
4850
+ Every command group sorts its subcommands into the same four sections: GROUPS nests one
4851
+ level deeper, READ changes nothing, RUN acts without changing configuration (triggers a
4852
+ job, spends a model call, rotates a token), WRITE changes platform state \u2014 confirm those
4853
+ with a person first.
4854
+ Add --json to any command for machine-readable output (the default view trims columns,
4855
+ --full widens it). Most list commands answer {entries, total_count}; anything else says
4856
+ so in its own description. Ids come from list/search output \u2014 opaque, never guess one,
4857
+ and the key holding one is not always \`id\` (\`skill_id\`, \`conversation_id\`, \`ot_id\` \u2026).
4858
+ Multi-tenant deploys: --biz-domain picks the domain, --user switches saved logins.
4859
+ \`openbkn call\` reaches any endpoint a command does not cover, auth injected. Look the
4860
+ path up at https://openbkn-ai.github.io/bkn-foundry/ first \u2014 do not guess one.`
4861
+ );
4862
+ installGroupedHelp(program2);
4863
+ return program2;
3441
4864
  }
3442
4865
 
3443
4866
  // src/cli.ts
3444
- var program = new Command16();
3445
- 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("--conversation-id <id>", "BKN Trace conversation id (env: BKN_CONVERSATION_ID)").option("--interaction-id <id>", "BKN Trace interaction id (env: BKN_INTERACTION_ID)").option(
3446
- "--new-conversation",
3447
- "ignore the remembered conversation for this command (see `openbkn context conversation`)"
3448
- ).option("-k, --insecure", "skip TLS verification (dev / self-signed only)").showHelpAfterError();
3449
- program.addCommand(authCommand());
3450
- program.addCommand(callCommand());
3451
- program.addCommand(configCommand());
3452
- program.addCommand(appkeyCommand());
3453
- program.addCommand(vegaCommand());
3454
- program.addCommand(bknCommand());
3455
- program.addCommand(resourceCommand());
3456
- program.addCommand(contextCommand());
3457
- program.addCommand(agentCommand());
3458
- program.addCommand(modelCommand());
3459
- program.addCommand(skillCommand());
3460
- program.addCommand(toolboxCommand());
3461
- program.addCommand(toolCommand());
3462
- program.addCommand(traceCommand());
3463
- program.addCommand(adminCommand());
3464
- program.addCommand(exploreCommand());
3465
- installGroupedHelp(program);
4867
+ process.stdout.on("error", (err) => {
4868
+ if (err.code === "EPIPE") process.exit(0);
4869
+ throw err;
4870
+ });
4871
+ var program = buildProgram();
3466
4872
  var argv = process.argv.map((a) => a === "-bd" ? "--biz-domain" : a);
4873
+ if (argv.includes("--dry-run")) enableDryRun();
3467
4874
  try {
3468
4875
  await program.parseAsync(argv);
3469
4876
  } catch (err) {
4877
+ if (err instanceof DryRunSignal) {
4878
+ process.stdout.write(`${JSON.stringify(err.request, null, 2)}
4879
+ `);
4880
+ await releaseLifecycleSessions();
4881
+ process.exit(0);
4882
+ }
3470
4883
  console.error(formatError(err));
3471
4884
  await releaseLifecycleSessions();
3472
4885
  process.exit(toExitCode(err));