@kweaver-ai/kweaver-sdk 0.4.0 → 0.4.2

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.
Files changed (41) hide show
  1. package/README.md +139 -0
  2. package/README.zh.md +139 -0
  3. package/dist/api/agent-list.d.ts +51 -0
  4. package/dist/api/agent-list.js +116 -7
  5. package/dist/api/bkn-backend.d.ts +16 -0
  6. package/dist/api/bkn-backend.js +46 -0
  7. package/dist/api/datasources.d.ts +73 -0
  8. package/dist/api/datasources.js +218 -0
  9. package/dist/api/dataviews.d.ts +20 -0
  10. package/dist/api/dataviews.js +72 -0
  11. package/dist/api/knowledge-networks.d.ts +84 -0
  12. package/dist/api/knowledge-networks.js +167 -0
  13. package/dist/api/ontology-query.d.ts +1 -1
  14. package/dist/api/ontology-query.js +1 -0
  15. package/dist/api/vega.d.ts +110 -0
  16. package/dist/api/vega.js +251 -0
  17. package/dist/auth/oauth.d.ts +3 -1
  18. package/dist/auth/oauth.js +12 -9
  19. package/dist/cli.js +15 -0
  20. package/dist/client.d.ts +19 -0
  21. package/dist/client.js +76 -13
  22. package/dist/commands/agent.d.ts +7 -0
  23. package/dist/commands/agent.js +392 -13
  24. package/dist/commands/bkn.d.ts +22 -0
  25. package/dist/commands/bkn.js +1057 -41
  26. package/dist/commands/call.js +20 -1
  27. package/dist/commands/context-loader.js +4 -4
  28. package/dist/commands/ds.d.ts +7 -0
  29. package/dist/commands/ds.js +283 -0
  30. package/dist/commands/vega.d.ts +1 -0
  31. package/dist/commands/vega.js +663 -0
  32. package/dist/index.d.ts +3 -2
  33. package/dist/index.js +1 -1
  34. package/dist/resources/agents.d.ts +83 -9
  35. package/dist/resources/agents.js +46 -10
  36. package/dist/resources/bkn.d.ts +12 -0
  37. package/dist/resources/bkn.js +12 -0
  38. package/dist/resources/knowledge-networks.js +19 -58
  39. package/dist/utils/crypto.d.ts +10 -0
  40. package/dist/utils/crypto.js +31 -0
  41. package/package.json +4 -2
@@ -1,9 +1,15 @@
1
1
  import { createInterface } from "node:readline";
2
- import { readFileSync } from "node:fs";
2
+ import { spawnSync } from "node:child_process";
3
+ import { mkdirSync, readFileSync, readdirSync, statSync } from "node:fs";
4
+ import { resolve } from "node:path";
5
+ import { loadNetwork, allObjects, allRelations, allActions, generateChecksum } from "@kweaver-ai/bkn";
3
6
  import { ensureValidToken, formatHttpError } from "../auth/oauth.js";
4
- import { listKnowledgeNetworks, getKnowledgeNetwork, createKnowledgeNetwork, updateKnowledgeNetwork, deleteKnowledgeNetwork, listObjectTypes, listRelationTypes, listActionTypes, } from "../api/knowledge-networks.js";
7
+ import { listKnowledgeNetworks, getKnowledgeNetwork, createKnowledgeNetwork, updateKnowledgeNetwork, deleteKnowledgeNetwork, listObjectTypes, listRelationTypes, listActionTypes, getObjectType, createObjectTypes, updateObjectType, deleteObjectTypes, getRelationType, createRelationTypes, updateRelationType, deleteRelationTypes, buildKnowledgeNetwork, getBuildStatus, } from "../api/knowledge-networks.js";
5
8
  import { objectTypeQuery, objectTypeProperties, subgraph, actionTypeQuery, actionTypeExecute, actionExecutionGet, actionLogsList, actionLogGet, actionLogCancel, } from "../api/ontology-query.js";
6
9
  import { semanticSearch } from "../api/semantic-search.js";
10
+ import { listTablesWithColumns } from "../api/datasources.js";
11
+ import { createDataView } from "../api/dataviews.js";
12
+ import { downloadBkn, uploadBkn } from "../api/bkn-backend.js";
7
13
  import { formatCallOutput } from "./call.js";
8
14
  export function formatSimpleKnList(text, pretty, includeDetail = false) {
9
15
  const parsed = JSON.parse(text);
@@ -336,6 +342,92 @@ export function parseKnDeleteArgs(args) {
336
342
  }
337
343
  return { knId, businessDomain, yes };
338
344
  }
345
+ export function parseKnPushArgs(args) {
346
+ let directory = "";
347
+ let branch = "main";
348
+ let businessDomain = "bd_public";
349
+ let pretty = true;
350
+ for (let i = 0; i < args.length; i += 1) {
351
+ const arg = args[i];
352
+ if (arg === "--help" || arg === "-h") {
353
+ throw new Error("help");
354
+ }
355
+ if (arg === "--branch") {
356
+ branch = args[i + 1] ?? "main";
357
+ if (!branch || branch.startsWith("-")) {
358
+ throw new Error("Missing value for --branch");
359
+ }
360
+ i += 1;
361
+ continue;
362
+ }
363
+ if (arg === "-bd" || arg === "--biz-domain") {
364
+ businessDomain = args[i + 1] ?? "bd_public";
365
+ if (!businessDomain || businessDomain.startsWith("-")) {
366
+ throw new Error("Missing value for biz-domain flag");
367
+ }
368
+ i += 1;
369
+ continue;
370
+ }
371
+ if (arg === "--pretty") {
372
+ pretty = true;
373
+ continue;
374
+ }
375
+ if (!arg.startsWith("-") && !directory) {
376
+ directory = arg;
377
+ continue;
378
+ }
379
+ throw new Error(`Unsupported bkn push argument: ${arg}`);
380
+ }
381
+ if (!directory) {
382
+ throw new Error("Missing directory. Usage: kweaver bkn push <directory> [--branch main] [-bd value]");
383
+ }
384
+ return { directory, branch, businessDomain, pretty };
385
+ }
386
+ export function parseKnPullArgs(args) {
387
+ let knId = "";
388
+ let directory = "";
389
+ let branch = "main";
390
+ let businessDomain = "bd_public";
391
+ for (let i = 0; i < args.length; i += 1) {
392
+ const arg = args[i];
393
+ if (arg === "--help" || arg === "-h") {
394
+ throw new Error("help");
395
+ }
396
+ if (arg === "--branch") {
397
+ branch = args[i + 1] ?? "main";
398
+ if (!branch || branch.startsWith("-")) {
399
+ throw new Error("Missing value for --branch");
400
+ }
401
+ i += 1;
402
+ continue;
403
+ }
404
+ if (arg === "-bd" || arg === "--biz-domain") {
405
+ businessDomain = args[i + 1] ?? "bd_public";
406
+ if (!businessDomain || businessDomain.startsWith("-")) {
407
+ throw new Error("Missing value for biz-domain flag");
408
+ }
409
+ i += 1;
410
+ continue;
411
+ }
412
+ if (!arg.startsWith("-")) {
413
+ if (!knId) {
414
+ knId = arg;
415
+ }
416
+ else if (!directory) {
417
+ directory = arg;
418
+ }
419
+ else {
420
+ throw new Error(`Unexpected positional argument: ${arg}`);
421
+ }
422
+ continue;
423
+ }
424
+ throw new Error(`Unsupported bkn pull argument: ${arg}`);
425
+ }
426
+ if (!knId) {
427
+ throw new Error("Missing kn-id. Usage: kweaver bkn pull <kn-id> [<directory>] [--branch main] [-bd value]");
428
+ }
429
+ return { knId, directory: directory || knId, branch, businessDomain };
430
+ }
339
431
  function parseJsonObject(text, errorMessage) {
340
432
  let parsed;
341
433
  try {
@@ -436,16 +528,28 @@ const KN_HELP = `kweaver bkn
436
528
  Subcommands:
437
529
  list [options] List business knowledge networks
438
530
  get <kn-id> [options] Get knowledge network detail (use --stats or --export)
439
- create [options] Create a knowledge network
531
+ create [options] Create a knowledge network (empty or from --body-file)
532
+ create-from-ds <ds-id> --name X [--tables a,b] [--build] Create KN from datasource
440
533
  update <kn-id> [options] Update a knowledge network
441
534
  delete <kn-id> Delete a knowledge network
535
+ build <kn-id> [--wait|--no-wait] [--timeout n] Trigger full build
536
+ push <directory> [--branch main] Upload BKN directory as tar
537
+ pull <kn-id> [<directory>] [--branch main] Download BKN tar and extract
442
538
  export <kn-id> Export knowledge network (alias for get --export)
443
539
  stats <kn-id> Get statistics (alias for get --stats)
444
540
  search <kn-id> <query> [options] Semantic search within a knowledge network
445
541
  object-type list <kn-id> List object types (schema)
542
+ object-type get <kn-id> <ot-id> Get object type details
543
+ object-type create <kn-id> [options] Create object type (--name --dataview-id --primary-key --display-key)
544
+ object-type update <kn-id> <ot-id> [options] Update object type
545
+ object-type delete <kn-id> <ot-ids> [-y] Delete object type(s)
446
546
  object-type query <kn-id> <ot-id> ['<json>'] Query object instances (ontology-query; supports --limit/--search-after)
447
- object-type properties <kn-id> <ot-id> '<json>' Query object properties
547
+ object-type properties <kn-id> <ot-id> '<json>' Query instance properties (json: {"_instance_identities":[{pk:val}],"properties":[...]})
448
548
  relation-type list <kn-id> List relation types (schema)
549
+ relation-type get <kn-id> <rt-id> Get relation type details
550
+ relation-type create <kn-id> [options] Create relation type (--name --source --target [--mapping src:tgt])
551
+ relation-type update <kn-id> <rt-id> [options] Update relation type
552
+ relation-type delete <kn-id> <rt-ids> [-y] Delete relation type(s)
449
553
  subgraph <kn-id> '<json>' Query subgraph
450
554
  action-type list <kn-id> List action types (schema)
451
555
  action-type query <kn-id> <at-id> '<json>' Query action info
@@ -471,12 +575,24 @@ export async function runKnCommand(args) {
471
575
  if (subcommand === "create") {
472
576
  return runKnCreateCommand(rest);
473
577
  }
578
+ if (subcommand === "create-from-ds") {
579
+ return runKnCreateFromDsCommand(rest);
580
+ }
474
581
  if (subcommand === "update") {
475
582
  return runKnUpdateCommand(rest);
476
583
  }
477
584
  if (subcommand === "delete") {
478
585
  return runKnDeleteCommand(rest);
479
586
  }
587
+ if (subcommand === "build") {
588
+ return runKnBuildCommand(rest);
589
+ }
590
+ if (subcommand === "push") {
591
+ return runKnPushCommand(rest);
592
+ }
593
+ if (subcommand === "pull") {
594
+ return runKnPullCommand(rest);
595
+ }
480
596
  if (subcommand === "export") {
481
597
  return runKnGetCommand([...(rest[0] ? [rest[0]] : []), "--export", ...rest.slice(1)]);
482
598
  }
@@ -507,6 +623,150 @@ export async function runKnCommand(args) {
507
623
  console.error(`Unknown bkn subcommand: ${subcommand}`);
508
624
  return 1;
509
625
  }
626
+ /** Parse object-type create args: --name --dataview-id --primary-key --display-key [--property '<json>' ...] */
627
+ function parseObjectTypeCreateArgs(args) {
628
+ let name = "";
629
+ let dataviewId = "";
630
+ let primaryKey = "";
631
+ let displayKey = "";
632
+ let businessDomain = "bd_public";
633
+ let branch = "main";
634
+ let pretty = true;
635
+ const properties = [];
636
+ const positional = [];
637
+ for (let i = 0; i < args.length; i += 1) {
638
+ const arg = args[i];
639
+ if (arg === "--help" || arg === "-h")
640
+ throw new Error("help");
641
+ if (arg === "--name" && args[i + 1]) {
642
+ name = args[++i];
643
+ continue;
644
+ }
645
+ if (arg === "--dataview-id" && args[i + 1]) {
646
+ dataviewId = args[++i];
647
+ continue;
648
+ }
649
+ if (arg === "--primary-key" && args[i + 1]) {
650
+ primaryKey = args[++i];
651
+ continue;
652
+ }
653
+ if (arg === "--display-key" && args[i + 1]) {
654
+ displayKey = args[++i];
655
+ continue;
656
+ }
657
+ if (arg === "--property" && args[i + 1]) {
658
+ properties.push(args[++i]);
659
+ continue;
660
+ }
661
+ if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
662
+ businessDomain = args[++i];
663
+ continue;
664
+ }
665
+ if (arg === "--branch" && args[i + 1]) {
666
+ branch = args[++i];
667
+ continue;
668
+ }
669
+ if (arg === "--pretty") {
670
+ pretty = true;
671
+ continue;
672
+ }
673
+ if (!arg.startsWith("-"))
674
+ positional.push(arg);
675
+ }
676
+ const knId = positional[0];
677
+ if (!knId || !name || !dataviewId || !primaryKey || !displayKey) {
678
+ throw new Error("Usage: kweaver bkn object-type create <kn-id> --name X --dataview-id Y --primary-key Z --display-key W");
679
+ }
680
+ const entry = {
681
+ name,
682
+ data_source: { type: "data_view", id: dataviewId },
683
+ primary_keys: [primaryKey],
684
+ display_key: displayKey,
685
+ };
686
+ if (properties.length > 0) {
687
+ entry.data_properties = properties.map((p) => JSON.parse(p));
688
+ }
689
+ else {
690
+ const autoProps = new Set([primaryKey, displayKey]);
691
+ entry.data_properties = Array.from(autoProps).map((n) => ({
692
+ name: n,
693
+ display_name: n,
694
+ type: "string",
695
+ }));
696
+ }
697
+ const body = JSON.stringify({ entries: [entry], branch });
698
+ return { knId, body, businessDomain, branch, pretty };
699
+ }
700
+ /** Parse object-type update args: --name X [--display-key Y] */
701
+ function parseObjectTypeUpdateArgs(args) {
702
+ let name;
703
+ let displayKey;
704
+ let businessDomain = "bd_public";
705
+ let pretty = true;
706
+ const positional = [];
707
+ for (let i = 0; i < args.length; i += 1) {
708
+ const arg = args[i];
709
+ if (arg === "--help" || arg === "-h")
710
+ throw new Error("help");
711
+ if (arg === "--name" && args[i + 1]) {
712
+ name = args[++i];
713
+ continue;
714
+ }
715
+ if (arg === "--display-key" && args[i + 1]) {
716
+ displayKey = args[++i];
717
+ continue;
718
+ }
719
+ if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
720
+ businessDomain = args[++i];
721
+ continue;
722
+ }
723
+ if (arg === "--pretty") {
724
+ pretty = true;
725
+ continue;
726
+ }
727
+ if (!arg.startsWith("-"))
728
+ positional.push(arg);
729
+ }
730
+ const [knId, otId] = positional;
731
+ if (!knId || !otId) {
732
+ throw new Error("Usage: kweaver bkn object-type update <kn-id> <ot-id> [--name X] [--display-key Y]");
733
+ }
734
+ const payload = {};
735
+ if (name !== undefined)
736
+ payload.name = name;
737
+ if (displayKey !== undefined)
738
+ payload.display_key = displayKey;
739
+ if (Object.keys(payload).length === 0) {
740
+ throw new Error("No update fields. Use --name or --display-key.");
741
+ }
742
+ return { knId, otId, body: JSON.stringify(payload), businessDomain, pretty };
743
+ }
744
+ /** Parse object-type delete args: <kn-id> <ot-ids> [-y] */
745
+ function parseObjectTypeDeleteArgs(args) {
746
+ let businessDomain = "bd_public";
747
+ let yes = false;
748
+ const positional = [];
749
+ for (let i = 0; i < args.length; i += 1) {
750
+ const arg = args[i];
751
+ if (arg === "--help" || arg === "-h")
752
+ throw new Error("help");
753
+ if (arg === "--yes" || arg === "-y") {
754
+ yes = true;
755
+ continue;
756
+ }
757
+ if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
758
+ businessDomain = args[++i];
759
+ continue;
760
+ }
761
+ if (!arg.startsWith("-"))
762
+ positional.push(arg);
763
+ }
764
+ const [knId, otIds] = positional;
765
+ if (!knId || !otIds) {
766
+ throw new Error("Usage: kweaver bkn object-type delete <kn-id> <ot-ids> [-y]");
767
+ }
768
+ return { knId, otIds, businessDomain, yes };
769
+ }
510
770
  /** Parse common flags for ontology-query subcommands; returns { filteredArgs, pretty, businessDomain } */
511
771
  function parseOntologyQueryFlags(args) {
512
772
  let pretty = true;
@@ -581,18 +841,126 @@ export function parseKnActionTypeExecuteArgs(args) {
581
841
  timeout,
582
842
  };
583
843
  }
844
+ const PK_CANDIDATES = new Set(["id", "pk", "key"]);
845
+ const PK_TYPES = new Set(["integer", "unsigned integer", "string", "varchar", "bigint", "int"]);
846
+ const DISPLAY_HINTS = ["name", "title", "label", "display_name", "description"];
847
+ function detectPrimaryKey(table) {
848
+ for (const col of table.columns) {
849
+ if (PK_CANDIDATES.has(col.name.toLowerCase()) && PK_TYPES.has(col.type.toLowerCase())) {
850
+ return col.name;
851
+ }
852
+ }
853
+ for (const col of table.columns) {
854
+ if (PK_TYPES.has(col.type.toLowerCase())) {
855
+ return col.name;
856
+ }
857
+ }
858
+ return table.columns[0]?.name ?? "id";
859
+ }
860
+ function detectDisplayKey(table, primaryKey) {
861
+ for (const col of table.columns) {
862
+ if (DISPLAY_HINTS.some((h) => col.name.toLowerCase().includes(h))) {
863
+ return col.name;
864
+ }
865
+ }
866
+ return primaryKey;
867
+ }
868
+ function confirmYes(prompt) {
869
+ return new Promise((resolve) => {
870
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
871
+ rl.question(`${prompt} [y/N] `, (answer) => {
872
+ rl.close();
873
+ const trimmed = answer.trim().toLowerCase();
874
+ resolve(trimmed === "y" || trimmed === "yes");
875
+ });
876
+ });
877
+ }
584
878
  async function runKnObjectTypeCommand(args) {
585
879
  const [action, ...rest] = args;
586
880
  if (!action || action === "--help" || action === "-h") {
587
881
  console.log(`kweaver bkn object-type list <kn-id> [--pretty] [-bd value]
882
+ kweaver bkn object-type get <kn-id> <ot-id> [--pretty] [-bd value]
883
+ kweaver bkn object-type create <kn-id> --name X --dataview-id Y --primary-key Z --display-key W [--property '<json>' ...]
884
+ kweaver bkn object-type update <kn-id> <ot-id> [--name X] [--display-key Y]
885
+ kweaver bkn object-type delete <kn-id> <ot-ids> [-y]
588
886
  kweaver bkn object-type query <kn-id> <ot-id> ['<json>'] [--limit <n>] [--search-after '<json-array>'] [--pretty] [-bd value]
589
887
  kweaver bkn object-type properties <kn-id> <ot-id> '<json>' [--pretty] [-bd value]
590
888
 
591
889
  list: List object types (schema) from ontology-manager.
592
- query/properties: Query via ontology-query API. For query, --limit and --search-after are merged into the JSON body.`);
890
+ get: Get single object type details.
891
+ create/update/delete: Schema CRUD (create requires dataview-id).
892
+ query/properties: Query via ontology-query API. For query, --limit and --search-after are merged into the JSON body.
893
+
894
+ properties JSON format: {"_instance_identities":[{"<primary-key>":"<value>"}],"properties":["prop1","prop2"]}`);
593
895
  return 0;
594
896
  }
595
897
  try {
898
+ if (action === "get") {
899
+ const parsed = parseOntologyQueryFlags(rest);
900
+ const [knId, otId] = parsed.filteredArgs;
901
+ if (!knId || !otId) {
902
+ console.error("Usage: kweaver bkn object-type get <kn-id> <ot-id> [options]");
903
+ return 1;
904
+ }
905
+ const token = await ensureValidToken();
906
+ const body = await getObjectType({
907
+ baseUrl: token.baseUrl,
908
+ accessToken: token.accessToken,
909
+ knId,
910
+ otId,
911
+ businessDomain: parsed.businessDomain,
912
+ });
913
+ console.log(formatCallOutput(body, parsed.pretty));
914
+ return 0;
915
+ }
916
+ if (action === "create") {
917
+ const opts = parseObjectTypeCreateArgs(rest);
918
+ const token = await ensureValidToken();
919
+ const body = await createObjectTypes({
920
+ baseUrl: token.baseUrl,
921
+ accessToken: token.accessToken,
922
+ knId: opts.knId,
923
+ body: opts.body,
924
+ businessDomain: opts.businessDomain,
925
+ branch: opts.branch,
926
+ });
927
+ console.log(formatCallOutput(body, opts.pretty));
928
+ return 0;
929
+ }
930
+ if (action === "update") {
931
+ const opts = parseObjectTypeUpdateArgs(rest);
932
+ const token = await ensureValidToken();
933
+ const body = await updateObjectType({
934
+ baseUrl: token.baseUrl,
935
+ accessToken: token.accessToken,
936
+ knId: opts.knId,
937
+ otId: opts.otId,
938
+ body: opts.body,
939
+ businessDomain: opts.businessDomain,
940
+ });
941
+ console.log(formatCallOutput(body, opts.pretty));
942
+ return 0;
943
+ }
944
+ if (action === "delete") {
945
+ const opts = parseObjectTypeDeleteArgs(rest);
946
+ if (!opts.yes) {
947
+ const confirmed = await confirmYes(`Delete object type(s) ${opts.otIds}?`);
948
+ if (!confirmed) {
949
+ console.error("Aborted.");
950
+ return 1;
951
+ }
952
+ }
953
+ const token = await ensureValidToken();
954
+ await deleteObjectTypes({
955
+ baseUrl: token.baseUrl,
956
+ accessToken: token.accessToken,
957
+ knId: opts.knId,
958
+ otIds: opts.otIds,
959
+ businessDomain: opts.businessDomain,
960
+ });
961
+ console.log(`Deleted ${opts.otIds}`);
962
+ return 0;
963
+ }
596
964
  if (action === "list") {
597
965
  const parsed = parseOntologyQueryFlags(rest);
598
966
  const [knId] = parsed.filteredArgs;
@@ -628,7 +996,8 @@ query/properties: Query via ontology-query API. For query, --limit and --search-
628
996
  const parsed = parseOntologyQueryFlags(rest);
629
997
  const [knId, otId, body] = parsed.filteredArgs;
630
998
  if (!knId || !otId || !body) {
631
- console.error("Usage: kweaver bkn object-type properties <kn-id> <ot-id> '<json>' [options]");
999
+ console.error(`Usage: kweaver bkn object-type properties <kn-id> <ot-id> '<json>' [options]
1000
+ JSON: {"_instance_identities":[{"<primary-key>":"<value>"}],"properties":["prop1","prop2"]}`);
632
1001
  return 1;
633
1002
  }
634
1003
  const token = await ensureValidToken();
@@ -643,44 +1012,255 @@ query/properties: Query via ontology-query API. For query, --limit and --search-
643
1012
  console.log(formatCallOutput(result, parsed.pretty));
644
1013
  return 0;
645
1014
  }
646
- console.error(`Unknown object-type action: ${action}. Use list, query, or properties.`);
1015
+ console.error(`Unknown object-type action: ${action}. Use list, get, create, update, delete, query, or properties.`);
1016
+ return 1;
1017
+ }
1018
+ catch (error) {
1019
+ if (error instanceof Error && error.message === "help") {
1020
+ console.log(`kweaver bkn object-type create <kn-id> --name X --dataview-id Y --primary-key Z --display-key W [--property '<json>' ...]
1021
+ kweaver bkn object-type update <kn-id> <ot-id> [--name X] [--display-key Y]
1022
+ kweaver bkn object-type delete <kn-id> <ot-ids> [-y]`);
1023
+ return 0;
1024
+ }
1025
+ console.error(formatHttpError(error));
1026
+ return 1;
1027
+ }
1028
+ }
1029
+ /** Parse relation-type create args: --name --source --target [--mapping src:tgt ...] */
1030
+ function parseRelationTypeCreateArgs(args) {
1031
+ let name = "";
1032
+ let source = "";
1033
+ let target = "";
1034
+ let businessDomain = "bd_public";
1035
+ let branch = "main";
1036
+ let pretty = true;
1037
+ const mappings = [];
1038
+ const positional = [];
1039
+ for (let i = 0; i < args.length; i += 1) {
1040
+ const arg = args[i];
1041
+ if (arg === "--help" || arg === "-h")
1042
+ throw new Error("help");
1043
+ if (arg === "--name" && args[i + 1]) {
1044
+ name = args[++i];
1045
+ continue;
1046
+ }
1047
+ if (arg === "--source" && args[i + 1]) {
1048
+ source = args[++i];
1049
+ continue;
1050
+ }
1051
+ if (arg === "--target" && args[i + 1]) {
1052
+ target = args[++i];
1053
+ continue;
1054
+ }
1055
+ if (arg === "--mapping" && args[i + 1]) {
1056
+ const m = args[++i];
1057
+ if (!m.includes(":")) {
1058
+ throw new Error(`Invalid mapping format '${m}'. Expected source_prop:target_prop.`);
1059
+ }
1060
+ const [s, t] = m.split(":", 2);
1061
+ mappings.push([s, t]);
1062
+ continue;
1063
+ }
1064
+ if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
1065
+ businessDomain = args[++i];
1066
+ continue;
1067
+ }
1068
+ if (arg === "--branch" && args[i + 1]) {
1069
+ branch = args[++i];
1070
+ continue;
1071
+ }
1072
+ if (arg === "--pretty") {
1073
+ pretty = true;
1074
+ continue;
1075
+ }
1076
+ if (!arg.startsWith("-"))
1077
+ positional.push(arg);
1078
+ }
1079
+ const knId = positional[0];
1080
+ if (!knId || !name || !source || !target) {
1081
+ throw new Error("Usage: kweaver bkn relation-type create <kn-id> --name X --source <ot-id> --target <ot-id> [--mapping src:tgt ...]");
1082
+ }
1083
+ const entry = {
1084
+ name,
1085
+ source_object_type_id: source,
1086
+ target_object_type_id: target,
1087
+ type: "direct",
1088
+ mapping_rules: mappings.map(([s, t]) => ({
1089
+ source_property: { name: s },
1090
+ target_property: { name: t },
1091
+ })),
1092
+ };
1093
+ const body = JSON.stringify({ entries: [entry], branch });
1094
+ return { knId, body, businessDomain, branch, pretty };
1095
+ }
1096
+ /** Parse relation-type update args: [--name X] */
1097
+ function parseRelationTypeUpdateArgs(args) {
1098
+ let name;
1099
+ let businessDomain = "bd_public";
1100
+ let pretty = true;
1101
+ const positional = [];
1102
+ for (let i = 0; i < args.length; i += 1) {
1103
+ const arg = args[i];
1104
+ if (arg === "--help" || arg === "-h")
1105
+ throw new Error("help");
1106
+ if (arg === "--name" && args[i + 1]) {
1107
+ name = args[++i];
1108
+ continue;
1109
+ }
1110
+ if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
1111
+ businessDomain = args[++i];
1112
+ continue;
1113
+ }
1114
+ if (arg === "--pretty") {
1115
+ pretty = true;
1116
+ continue;
1117
+ }
1118
+ if (!arg.startsWith("-"))
1119
+ positional.push(arg);
1120
+ }
1121
+ const [knId, rtId] = positional;
1122
+ if (!knId || !rtId) {
1123
+ throw new Error("Usage: kweaver bkn relation-type update <kn-id> <rt-id> [--name X]");
1124
+ }
1125
+ if (name === undefined) {
1126
+ throw new Error("No update fields. Use --name.");
1127
+ }
1128
+ return { knId, rtId, body: JSON.stringify({ name }), businessDomain, pretty };
1129
+ }
1130
+ /** Parse relation-type delete args: <kn-id> <rt-ids> [-y] */
1131
+ function parseRelationTypeDeleteArgs(args) {
1132
+ let businessDomain = "bd_public";
1133
+ let yes = false;
1134
+ const positional = [];
1135
+ for (let i = 0; i < args.length; i += 1) {
1136
+ const arg = args[i];
1137
+ if (arg === "--help" || arg === "-h")
1138
+ throw new Error("help");
1139
+ if (arg === "--yes" || arg === "-y") {
1140
+ yes = true;
1141
+ continue;
1142
+ }
1143
+ if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
1144
+ businessDomain = args[++i];
1145
+ continue;
1146
+ }
1147
+ if (!arg.startsWith("-"))
1148
+ positional.push(arg);
1149
+ }
1150
+ const [knId, rtIds] = positional;
1151
+ if (!knId || !rtIds) {
1152
+ throw new Error("Usage: kweaver bkn relation-type delete <kn-id> <rt-ids> [-y]");
1153
+ }
1154
+ return { knId, rtIds, businessDomain, yes };
1155
+ }
1156
+ async function runKnRelationTypeCommand(args) {
1157
+ const [action, ...rest] = args;
1158
+ if (!action || action === "--help" || action === "-h") {
1159
+ console.log(`kweaver bkn relation-type list <kn-id> [--pretty] [-bd value]
1160
+ kweaver bkn relation-type get <kn-id> <rt-id> [--pretty] [-bd value]
1161
+ kweaver bkn relation-type create <kn-id> --name X --source <ot-id> --target <ot-id> [--mapping src:tgt ...]
1162
+ kweaver bkn relation-type update <kn-id> <rt-id> [--name X]
1163
+ kweaver bkn relation-type delete <kn-id> <rt-ids> [-y]
1164
+
1165
+ list: List relation types (schema) from ontology-manager.
1166
+ get: Get single relation type details.
1167
+ create/update/delete: Schema CRUD.`);
1168
+ return 0;
1169
+ }
1170
+ try {
1171
+ if (action === "get") {
1172
+ const parsed = parseOntologyQueryFlags(rest);
1173
+ const [knId, rtId] = parsed.filteredArgs;
1174
+ if (!knId || !rtId) {
1175
+ console.error("Usage: kweaver bkn relation-type get <kn-id> <rt-id> [options]");
1176
+ return 1;
1177
+ }
1178
+ const token = await ensureValidToken();
1179
+ const body = await getRelationType({
1180
+ baseUrl: token.baseUrl,
1181
+ accessToken: token.accessToken,
1182
+ knId,
1183
+ rtId,
1184
+ businessDomain: parsed.businessDomain,
1185
+ });
1186
+ console.log(formatCallOutput(body, parsed.pretty));
1187
+ return 0;
1188
+ }
1189
+ if (action === "create") {
1190
+ const opts = parseRelationTypeCreateArgs(rest);
1191
+ const token = await ensureValidToken();
1192
+ const body = await createRelationTypes({
1193
+ baseUrl: token.baseUrl,
1194
+ accessToken: token.accessToken,
1195
+ knId: opts.knId,
1196
+ body: opts.body,
1197
+ businessDomain: opts.businessDomain,
1198
+ branch: opts.branch,
1199
+ });
1200
+ console.log(formatCallOutput(body, opts.pretty));
1201
+ return 0;
1202
+ }
1203
+ if (action === "update") {
1204
+ const opts = parseRelationTypeUpdateArgs(rest);
1205
+ const token = await ensureValidToken();
1206
+ const body = await updateRelationType({
1207
+ baseUrl: token.baseUrl,
1208
+ accessToken: token.accessToken,
1209
+ knId: opts.knId,
1210
+ rtId: opts.rtId,
1211
+ body: opts.body,
1212
+ businessDomain: opts.businessDomain,
1213
+ });
1214
+ console.log(formatCallOutput(body, opts.pretty));
1215
+ return 0;
1216
+ }
1217
+ if (action === "delete") {
1218
+ const opts = parseRelationTypeDeleteArgs(rest);
1219
+ if (!opts.yes) {
1220
+ const confirmed = await confirmYes(`Delete relation type(s) ${opts.rtIds}?`);
1221
+ if (!confirmed) {
1222
+ console.error("Aborted.");
1223
+ return 1;
1224
+ }
1225
+ }
1226
+ const token = await ensureValidToken();
1227
+ await deleteRelationTypes({
1228
+ baseUrl: token.baseUrl,
1229
+ accessToken: token.accessToken,
1230
+ knId: opts.knId,
1231
+ rtIds: opts.rtIds,
1232
+ businessDomain: opts.businessDomain,
1233
+ });
1234
+ console.log(`Deleted ${opts.rtIds}`);
1235
+ return 0;
1236
+ }
1237
+ if (action === "list") {
1238
+ const parsed = parseOntologyQueryFlags(rest);
1239
+ const [knId] = parsed.filteredArgs;
1240
+ if (!knId) {
1241
+ console.error("Usage: kweaver bkn relation-type list <kn-id> [options]");
1242
+ return 1;
1243
+ }
1244
+ const token = await ensureValidToken();
1245
+ const body = await listRelationTypes({
1246
+ baseUrl: token.baseUrl,
1247
+ accessToken: token.accessToken,
1248
+ knId,
1249
+ businessDomain: parsed.businessDomain,
1250
+ });
1251
+ console.log(formatCallOutput(body, parsed.pretty));
1252
+ return 0;
1253
+ }
1254
+ console.error(`Unknown relation-type action: ${action}. Use list, get, create, update, or delete.`);
647
1255
  return 1;
648
1256
  }
649
1257
  catch (error) {
650
- console.error(formatHttpError(error));
651
- return 1;
652
- }
653
- }
654
- async function runKnRelationTypeCommand(args) {
655
- const [action, ...rest] = args;
656
- if (!action || action === "--help" || action === "-h") {
657
- console.log(`kweaver bkn relation-type list <kn-id> [--pretty] [-bd value]
658
-
659
- List relation types (schema) from ontology-manager.`);
660
- return 0;
661
- }
662
- if (action !== "list") {
663
- console.error(`Unknown relation-type action: ${action}. Use list.`);
664
- return 1;
665
- }
666
- try {
667
- const parsed = parseOntologyQueryFlags(rest);
668
- const [knId] = parsed.filteredArgs;
669
- if (!knId) {
670
- console.error("Usage: kweaver bkn relation-type list <kn-id> [options]");
671
- return 1;
1258
+ if (error instanceof Error && error.message === "help") {
1259
+ console.log(`kweaver bkn relation-type create <kn-id> --name X --source <ot-id> --target <ot-id> [--mapping src:tgt ...]
1260
+ kweaver bkn relation-type update <kn-id> <rt-id> [--name X]
1261
+ kweaver bkn relation-type delete <kn-id> <rt-ids> [-y]`);
1262
+ return 0;
672
1263
  }
673
- const token = await ensureValidToken();
674
- const body = await listRelationTypes({
675
- baseUrl: token.baseUrl,
676
- accessToken: token.accessToken,
677
- knId,
678
- businessDomain: parsed.businessDomain,
679
- });
680
- console.log(formatCallOutput(body, parsed.pretty));
681
- return 0;
682
- }
683
- catch (error) {
684
1264
  console.error(formatHttpError(error));
685
1265
  return 1;
686
1266
  }
@@ -699,7 +1279,7 @@ async function runKnSubgraphCommand(args) {
699
1279
  if (error instanceof Error && error.message === "help") {
700
1280
  console.log(`kweaver bkn subgraph <kn-id> '<json>' [--pretty] [-bd value]
701
1281
 
702
- Query subgraph via ontology-query API. JSON body format see ref/ontology/ontology-query.yaml.`);
1282
+ Query subgraph via ontology-query API. JSON body format see references/json-formats.md#subgraph.`);
703
1283
  return 0;
704
1284
  }
705
1285
  throw error;
@@ -710,6 +1290,17 @@ Query subgraph via ontology-query API. JSON body format see ref/ontology/ontolog
710
1290
  return 1;
711
1291
  }
712
1292
  try {
1293
+ // Auto-detect query_type=relation_path when body contains source_object_type_id
1294
+ let queryType;
1295
+ try {
1296
+ const parsedBody = JSON.parse(body);
1297
+ if (parsedBody.source_object_type_id) {
1298
+ queryType = "relation_path";
1299
+ }
1300
+ }
1301
+ catch {
1302
+ // Not valid JSON — let the API return the error
1303
+ }
713
1304
  const token = await ensureValidToken();
714
1305
  const result = await subgraph({
715
1306
  baseUrl: token.baseUrl,
@@ -717,6 +1308,7 @@ Query subgraph via ontology-query API. JSON body format see ref/ontology/ontolog
717
1308
  knId,
718
1309
  body,
719
1310
  businessDomain,
1311
+ queryType,
720
1312
  });
721
1313
  console.log(formatCallOutput(result, pretty));
722
1314
  return 0;
@@ -1210,6 +1802,183 @@ async function runKnCreateCommand(args) {
1210
1802
  return 1;
1211
1803
  }
1212
1804
  }
1805
+ const KN_CREATE_FROM_DS_HELP = `kweaver bkn create-from-ds <ds-id> --name X [options]
1806
+
1807
+ Create a knowledge network from a datasource (dataviews + object types + optional build).
1808
+
1809
+ Options:
1810
+ --name <s> Knowledge network name (required)
1811
+ --tables <a,b> Comma-separated table names (default: all)
1812
+ --build (default) Build after creation
1813
+ --no-build Skip build after creation
1814
+ --timeout <n> Build timeout in seconds (default: 300)
1815
+ -bd, --biz-domain Business domain (default: bd_public)
1816
+ --pretty Pretty-print output (default)`;
1817
+ function parseKnCreateFromDsArgs(args) {
1818
+ let dsId = "";
1819
+ let name = "";
1820
+ let tablesStr = "";
1821
+ let build = true;
1822
+ let timeout = 300;
1823
+ let businessDomain = "bd_public";
1824
+ let pretty = true;
1825
+ for (let i = 0; i < args.length; i += 1) {
1826
+ const arg = args[i];
1827
+ if (arg === "--help" || arg === "-h")
1828
+ throw new Error("help");
1829
+ if (arg === "--name" && args[i + 1]) {
1830
+ name = args[++i];
1831
+ continue;
1832
+ }
1833
+ if (arg === "--tables" && args[i + 1]) {
1834
+ tablesStr = args[++i];
1835
+ continue;
1836
+ }
1837
+ if (arg === "--build") {
1838
+ build = true;
1839
+ continue;
1840
+ }
1841
+ if (arg === "--no-build") {
1842
+ build = false;
1843
+ continue;
1844
+ }
1845
+ if (arg === "--timeout" && args[i + 1]) {
1846
+ timeout = parseInt(args[++i], 10);
1847
+ if (Number.isNaN(timeout) || timeout < 1)
1848
+ timeout = 300;
1849
+ continue;
1850
+ }
1851
+ if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
1852
+ businessDomain = args[++i];
1853
+ continue;
1854
+ }
1855
+ if (arg === "--pretty") {
1856
+ pretty = true;
1857
+ continue;
1858
+ }
1859
+ if (!arg.startsWith("-") && !dsId) {
1860
+ dsId = arg;
1861
+ }
1862
+ }
1863
+ const tables = tablesStr ? tablesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
1864
+ if (!dsId || !name) {
1865
+ throw new Error("Usage: kweaver bkn create-from-ds <ds-id> --name X [options]");
1866
+ }
1867
+ return { dsId, name, tables, build, timeout, businessDomain, pretty };
1868
+ }
1869
+ async function runKnCreateFromDsCommand(args) {
1870
+ let options;
1871
+ try {
1872
+ options = parseKnCreateFromDsArgs(args);
1873
+ }
1874
+ catch (error) {
1875
+ if (error instanceof Error && error.message === "help") {
1876
+ console.log(KN_CREATE_FROM_DS_HELP);
1877
+ return 0;
1878
+ }
1879
+ console.error(formatHttpError(error));
1880
+ return 1;
1881
+ }
1882
+ try {
1883
+ const token = await ensureValidToken();
1884
+ const base = {
1885
+ baseUrl: token.baseUrl,
1886
+ accessToken: token.accessToken,
1887
+ businessDomain: options.businessDomain,
1888
+ };
1889
+ const tablesBody = await listTablesWithColumns({ ...base, id: options.dsId });
1890
+ const allTables = JSON.parse(tablesBody);
1891
+ const targetTables = options.tables.length > 0
1892
+ ? allTables.filter((t) => options.tables.includes(t.name))
1893
+ : allTables;
1894
+ if (targetTables.length === 0) {
1895
+ console.error("No tables available");
1896
+ return 1;
1897
+ }
1898
+ const viewMap = {};
1899
+ for (const t of targetTables) {
1900
+ const dvId = await createDataView({
1901
+ ...base,
1902
+ name: t.name,
1903
+ datasourceId: options.dsId,
1904
+ table: t.name,
1905
+ fields: t.columns.map((c) => ({ name: c.name, type: c.type })),
1906
+ });
1907
+ viewMap[t.name] = dvId;
1908
+ }
1909
+ const knBody = JSON.stringify({
1910
+ name: options.name,
1911
+ branch: "main",
1912
+ base_branch: "",
1913
+ });
1914
+ const knResponse = await createKnowledgeNetwork({
1915
+ ...base,
1916
+ body: knBody,
1917
+ });
1918
+ const knParsed = JSON.parse(knResponse);
1919
+ const knItem = Array.isArray(knParsed) ? knParsed[0] : knParsed;
1920
+ const knId = String(knItem?.id ?? "");
1921
+ const otResults = [];
1922
+ for (const t of targetTables) {
1923
+ const pk = detectPrimaryKey(t);
1924
+ const dk = detectDisplayKey(t, pk);
1925
+ const entry = {
1926
+ name: t.name,
1927
+ data_source: { type: "data_view", id: viewMap[t.name] },
1928
+ primary_keys: [pk],
1929
+ display_key: dk,
1930
+ data_properties: [pk, dk].filter((x, i, a) => a.indexOf(x) === i).map((n) => ({
1931
+ name: n,
1932
+ display_name: n,
1933
+ type: "string",
1934
+ })),
1935
+ };
1936
+ const otBody = JSON.stringify({ entries: [entry], branch: "main" });
1937
+ const otResponse = await createObjectTypes({
1938
+ ...base,
1939
+ knId,
1940
+ body: otBody,
1941
+ });
1942
+ const otParsed = JSON.parse(otResponse);
1943
+ const otItem = otParsed.entries?.[0];
1944
+ otResults.push({
1945
+ name: t.name,
1946
+ id: otItem?.id ?? "",
1947
+ field_count: t.columns.length,
1948
+ });
1949
+ }
1950
+ let statusStr = "skipped";
1951
+ if (options.build) {
1952
+ console.error("Building ...");
1953
+ await buildKnowledgeNetwork({ ...base, knId });
1954
+ const deadline = Date.now() + options.timeout * 1000;
1955
+ const TERMINAL = ["completed", "failed", "success"];
1956
+ while (Date.now() < deadline) {
1957
+ await new Promise((r) => setTimeout(r, 2000));
1958
+ const statusBody = await getBuildStatus({ ...base, knId });
1959
+ const statusParsed = JSON.parse(statusBody);
1960
+ const jobs = Array.isArray(statusParsed) ? statusParsed : (statusParsed.entries ?? []);
1961
+ const state = (jobs[0]?.state ?? "running").toLowerCase();
1962
+ if (TERMINAL.includes(state)) {
1963
+ statusStr = state;
1964
+ break;
1965
+ }
1966
+ }
1967
+ }
1968
+ const output = {
1969
+ kn_id: knId,
1970
+ kn_name: options.name,
1971
+ object_types: otResults,
1972
+ status: statusStr,
1973
+ };
1974
+ console.log(JSON.stringify(output, null, options.pretty ? 2 : 0));
1975
+ return 0;
1976
+ }
1977
+ catch (error) {
1978
+ console.error(formatHttpError(error));
1979
+ return 1;
1980
+ }
1981
+ }
1213
1982
  async function runKnUpdateCommand(args) {
1214
1983
  let options;
1215
1984
  try {
@@ -1287,6 +2056,253 @@ async function runKnDeleteCommand(args) {
1287
2056
  return 1;
1288
2057
  }
1289
2058
  }
2059
+ const KN_BUILD_HELP = `kweaver bkn build <kn-id> [options]
2060
+
2061
+ Trigger a full build for a knowledge network.
2062
+
2063
+ Options:
2064
+ --wait (default) Poll until build completes
2065
+ --no-wait Return immediately after triggering
2066
+ --timeout <seconds> Max wait time when --wait (default: 300)
2067
+ -bd, --biz-domain Business domain (default: bd_public)`;
2068
+ export function parseKnBuildArgs(args) {
2069
+ let knId = "";
2070
+ let wait = true;
2071
+ let timeout = 300;
2072
+ let businessDomain = "bd_public";
2073
+ for (let i = 0; i < args.length; i += 1) {
2074
+ const arg = args[i];
2075
+ if (arg === "--help" || arg === "-h")
2076
+ throw new Error("help");
2077
+ if (arg === "--wait") {
2078
+ wait = true;
2079
+ continue;
2080
+ }
2081
+ if (arg === "--no-wait") {
2082
+ wait = false;
2083
+ continue;
2084
+ }
2085
+ if (arg === "--timeout" && args[i + 1]) {
2086
+ timeout = parseInt(args[i + 1], 10);
2087
+ if (Number.isNaN(timeout) || timeout < 1)
2088
+ timeout = 300;
2089
+ i += 1;
2090
+ continue;
2091
+ }
2092
+ if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
2093
+ businessDomain = args[i + 1];
2094
+ i += 1;
2095
+ continue;
2096
+ }
2097
+ if (!arg.startsWith("-") && !knId) {
2098
+ knId = arg;
2099
+ }
2100
+ }
2101
+ if (!knId) {
2102
+ throw new Error("Missing kn-id. Usage: kweaver bkn build <kn-id> [options]");
2103
+ }
2104
+ return { knId, wait, timeout, businessDomain };
2105
+ }
2106
+ async function runKnBuildCommand(args) {
2107
+ let options;
2108
+ try {
2109
+ options = parseKnBuildArgs(args);
2110
+ }
2111
+ catch (error) {
2112
+ if (error instanceof Error && error.message === "help") {
2113
+ console.log(KN_BUILD_HELP);
2114
+ return 0;
2115
+ }
2116
+ console.error(formatHttpError(error));
2117
+ return 1;
2118
+ }
2119
+ const TERMINAL_STATES = ["completed", "failed", "success"];
2120
+ try {
2121
+ const token = await ensureValidToken();
2122
+ await buildKnowledgeNetwork({
2123
+ baseUrl: token.baseUrl,
2124
+ accessToken: token.accessToken,
2125
+ knId: options.knId,
2126
+ businessDomain: options.businessDomain,
2127
+ });
2128
+ console.error(`Build started for ${options.knId}`);
2129
+ if (!options.wait) {
2130
+ console.error("Build triggered (not waiting).");
2131
+ return 0;
2132
+ }
2133
+ console.error("Waiting for build to complete ...");
2134
+ const deadline = Date.now() + options.timeout * 1000;
2135
+ while (Date.now() < deadline) {
2136
+ await new Promise((r) => setTimeout(r, 2000));
2137
+ const body = await getBuildStatus({
2138
+ baseUrl: token.baseUrl,
2139
+ accessToken: token.accessToken,
2140
+ knId: options.knId,
2141
+ businessDomain: options.businessDomain,
2142
+ });
2143
+ const parsed = JSON.parse(body);
2144
+ const jobs = Array.isArray(parsed) ? parsed : (parsed.entries ?? parsed.data ?? []);
2145
+ const job = jobs[0];
2146
+ const state = (job?.state ?? "running").toLowerCase();
2147
+ const detail = job?.state_detail;
2148
+ if (TERMINAL_STATES.includes(state)) {
2149
+ console.log(state);
2150
+ if (detail) {
2151
+ console.log(`Detail: ${detail}`);
2152
+ }
2153
+ return state === "failed" ? 1 : 0;
2154
+ }
2155
+ }
2156
+ console.error(`Build did not complete within ${options.timeout}s`);
2157
+ return 1;
2158
+ }
2159
+ catch (error) {
2160
+ console.error(formatHttpError(error));
2161
+ return 1;
2162
+ }
2163
+ }
2164
+ // ── push / pull (BKN tar import/export) ──────────────────────────────────────
2165
+ export function packDirectoryToTar(dirPath) {
2166
+ const absPath = resolve(dirPath);
2167
+ const entries = readdirSync(absPath);
2168
+ const args = ["cf", "-", "-C", absPath, ...entries];
2169
+ const result = spawnSync("tar", args, {
2170
+ encoding: "buffer",
2171
+ env: { ...process.env, COPYFILE_DISABLE: "1" },
2172
+ });
2173
+ if (result.error)
2174
+ throw result.error;
2175
+ if (result.status !== 0) {
2176
+ throw new Error(`tar pack failed: ${result.stderr?.toString() ?? result.status}`);
2177
+ }
2178
+ return result.stdout;
2179
+ }
2180
+ export function extractTarToDirectory(tarBuffer, dirPath) {
2181
+ const absPath = resolve(dirPath);
2182
+ mkdirSync(absPath, { recursive: true });
2183
+ const result = spawnSync("tar", ["xf", "-", "-C", absPath], {
2184
+ input: tarBuffer,
2185
+ });
2186
+ if (result.error) {
2187
+ throw result.error;
2188
+ }
2189
+ if (result.status !== 0) {
2190
+ throw new Error(`tar extract failed: ${result.stderr?.toString() ?? result.status}`);
2191
+ }
2192
+ }
2193
+ const KN_PUSH_HELP = `kweaver bkn push <directory> [options]
2194
+
2195
+ Pack a BKN directory into a tar and upload to import as a knowledge network.
2196
+
2197
+ Options:
2198
+ --branch <s> Branch name (default: main)
2199
+ -bd, --biz-domain Business domain (default: bd_public)
2200
+ --pretty Pretty-print JSON output`;
2201
+ const KN_PULL_HELP = `kweaver bkn pull <kn-id> [<directory>] [options]
2202
+
2203
+ Download a BKN tar from a knowledge network and extract to a local directory.
2204
+
2205
+ Options:
2206
+ <directory> Output directory (default: <kn-id>)
2207
+ --branch <s> Branch name (default: main)
2208
+ -bd, --biz-domain Business domain (default: bd_public)`;
2209
+ async function runKnPushCommand(args) {
2210
+ let options;
2211
+ try {
2212
+ options = parseKnPushArgs(args);
2213
+ }
2214
+ catch (error) {
2215
+ if (error instanceof Error && error.message === "help") {
2216
+ console.log(KN_PUSH_HELP);
2217
+ return 0;
2218
+ }
2219
+ console.error(formatHttpError(error));
2220
+ return 1;
2221
+ }
2222
+ const absDir = resolve(options.directory);
2223
+ try {
2224
+ const stat = statSync(absDir);
2225
+ if (!stat.isDirectory()) {
2226
+ console.error(`Not a directory: ${options.directory}`);
2227
+ return 1;
2228
+ }
2229
+ }
2230
+ catch (err) {
2231
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
2232
+ console.error(`Directory not found: ${options.directory}`);
2233
+ return 1;
2234
+ }
2235
+ throw err;
2236
+ }
2237
+ try {
2238
+ const network = await loadNetwork(absDir);
2239
+ const objs = allObjects(network);
2240
+ const rels = allRelations(network);
2241
+ const acts = allActions(network);
2242
+ console.error(`Validated: ${objs.length} object types, ${rels.length} relation types, ${acts.length} action types`);
2243
+ }
2244
+ catch (error) {
2245
+ console.error(`BKN validation failed: ${error instanceof Error ? error.message : String(error)}`);
2246
+ return 1;
2247
+ }
2248
+ try {
2249
+ await generateChecksum(absDir);
2250
+ console.error("Checksum generated");
2251
+ }
2252
+ catch (error) {
2253
+ console.error(`Checksum generation failed: ${error instanceof Error ? error.message : String(error)}`);
2254
+ return 1;
2255
+ }
2256
+ try {
2257
+ const tarBuffer = packDirectoryToTar(absDir);
2258
+ const token = await ensureValidToken();
2259
+ const body = await uploadBkn({
2260
+ baseUrl: token.baseUrl,
2261
+ accessToken: token.accessToken,
2262
+ tarBuffer,
2263
+ businessDomain: options.businessDomain,
2264
+ branch: options.branch,
2265
+ });
2266
+ console.log(formatCallOutput(body, options.pretty));
2267
+ return 0;
2268
+ }
2269
+ catch (error) {
2270
+ console.error(formatHttpError(error));
2271
+ return 1;
2272
+ }
2273
+ }
2274
+ async function runKnPullCommand(args) {
2275
+ let options;
2276
+ try {
2277
+ options = parseKnPullArgs(args);
2278
+ }
2279
+ catch (error) {
2280
+ if (error instanceof Error && error.message === "help") {
2281
+ console.log(KN_PULL_HELP);
2282
+ return 0;
2283
+ }
2284
+ console.error(formatHttpError(error));
2285
+ return 1;
2286
+ }
2287
+ try {
2288
+ const token = await ensureValidToken();
2289
+ const tarBuffer = await downloadBkn({
2290
+ baseUrl: token.baseUrl,
2291
+ accessToken: token.accessToken,
2292
+ knId: options.knId,
2293
+ businessDomain: options.businessDomain,
2294
+ branch: options.branch,
2295
+ });
2296
+ const absDir = resolve(options.directory);
2297
+ extractTarToDirectory(tarBuffer, absDir);
2298
+ console.log(`Extracted to ${absDir}`);
2299
+ return 0;
2300
+ }
2301
+ catch (error) {
2302
+ console.error(formatHttpError(error));
2303
+ return 1;
2304
+ }
2305
+ }
1290
2306
  // ── search ──────────────────────────────────────────────────────────────────
1291
2307
  const KN_SEARCH_HELP = `kweaver bkn search <kn-id> <query> [--max-concepts <n>] [--mode <mode>] [--pretty] [-bd value]
1292
2308