@kweaver-ai/kweaver-sdk 0.4.1 → 0.4.4

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.
@@ -1,12 +1,18 @@
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, validateNetwork } from "@kweaver-ai/bkn";
3
6
  import { ensureValidToken, formatHttpError } from "../auth/oauth.js";
7
+ import { HttpError } from "../utils/http.js";
4
8
  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
9
  import { objectTypeQuery, objectTypeProperties, subgraph, actionTypeQuery, actionTypeExecute, actionExecutionGet, actionLogsList, actionLogGet, actionLogCancel, } from "../api/ontology-query.js";
6
10
  import { semanticSearch } from "../api/semantic-search.js";
7
11
  import { listTablesWithColumns } from "../api/datasources.js";
8
12
  import { createDataView } from "../api/dataviews.js";
13
+ import { downloadBkn, uploadBkn } from "../api/bkn-backend.js";
9
14
  import { formatCallOutput } from "./call.js";
15
+ import { resolveBusinessDomain } from "../config/store.js";
10
16
  export function formatSimpleKnList(text, pretty, includeDetail = false) {
11
17
  const parsed = JSON.parse(text);
12
18
  const entries = Array.isArray(parsed.entries) ? parsed.entries : [];
@@ -23,7 +29,7 @@ export function parseKnListArgs(args) {
23
29
  let limit = 50;
24
30
  let sort = "update_time";
25
31
  let direction = "desc";
26
- let businessDomain = "bd_public";
32
+ let businessDomain = "";
27
33
  let detail = false;
28
34
  let pretty = true;
29
35
  let verbose = false;
@@ -94,13 +100,15 @@ export function parseKnListArgs(args) {
94
100
  }
95
101
  throw new Error(`Unsupported kn list argument: ${arg}`);
96
102
  }
103
+ if (!businessDomain)
104
+ businessDomain = resolveBusinessDomain();
97
105
  return { offset, limit, sort, direction, businessDomain, detail, pretty, verbose, name_pattern, tag };
98
106
  }
99
107
  export function parseKnGetArgs(args) {
100
108
  let knId = "";
101
109
  let stats = false;
102
110
  let exportMode = false;
103
- let businessDomain = "bd_public";
111
+ let businessDomain = "";
104
112
  let pretty = true;
105
113
  for (let i = 0; i < args.length; i += 1) {
106
114
  const arg = args[i];
@@ -136,6 +144,8 @@ export function parseKnGetArgs(args) {
136
144
  if (!knId) {
137
145
  throw new Error("Missing kn-id. Usage: kweaver bkn get <kn-id> [options]");
138
146
  }
147
+ if (!businessDomain)
148
+ businessDomain = resolveBusinessDomain();
139
149
  return { knId, stats, export: exportMode, businessDomain, pretty };
140
150
  }
141
151
  const BODY_FILE_FLAGS = [
@@ -151,7 +161,7 @@ export function parseKnCreateArgs(args) {
151
161
  let bodyFile;
152
162
  let import_mode;
153
163
  let validate_dependency;
154
- let businessDomain = "bd_public";
164
+ let businessDomain = "";
155
165
  let pretty = true;
156
166
  const flags = {};
157
167
  for (let i = 0; i < args.length; i += 1) {
@@ -229,12 +239,14 @@ export function parseKnCreateArgs(args) {
229
239
  payload.color = flags.color;
230
240
  body = JSON.stringify(payload);
231
241
  }
242
+ if (!businessDomain)
243
+ businessDomain = resolveBusinessDomain();
232
244
  return { body, import_mode, validate_dependency, businessDomain, pretty };
233
245
  }
234
246
  export function parseKnUpdateArgs(args) {
235
247
  let knId = "";
236
248
  let bodyFile;
237
- let businessDomain = "bd_public";
249
+ let businessDomain = "";
238
250
  let pretty = true;
239
251
  const flags = {};
240
252
  for (let i = 0; i < args.length; i += 1) {
@@ -304,11 +316,13 @@ export function parseKnUpdateArgs(args) {
304
316
  payload.color = flags.color;
305
317
  body = JSON.stringify(payload);
306
318
  }
319
+ if (!businessDomain)
320
+ businessDomain = resolveBusinessDomain();
307
321
  return { knId, body, businessDomain, pretty };
308
322
  }
309
323
  export function parseKnDeleteArgs(args) {
310
324
  let knId = "";
311
- let businessDomain = "bd_public";
325
+ let businessDomain = "";
312
326
  let yes = false;
313
327
  for (let i = 0; i < args.length; i += 1) {
314
328
  const arg = args[i];
@@ -336,8 +350,100 @@ export function parseKnDeleteArgs(args) {
336
350
  if (!knId) {
337
351
  throw new Error("Missing kn-id. Usage: kweaver bkn delete <kn-id>");
338
352
  }
353
+ if (!businessDomain)
354
+ businessDomain = resolveBusinessDomain();
339
355
  return { knId, businessDomain, yes };
340
356
  }
357
+ export function parseKnPushArgs(args) {
358
+ let directory = "";
359
+ let branch = "main";
360
+ let businessDomain = "";
361
+ let pretty = true;
362
+ for (let i = 0; i < args.length; i += 1) {
363
+ const arg = args[i];
364
+ if (arg === "--help" || arg === "-h") {
365
+ throw new Error("help");
366
+ }
367
+ if (arg === "--branch") {
368
+ branch = args[i + 1] ?? "main";
369
+ if (!branch || branch.startsWith("-")) {
370
+ throw new Error("Missing value for --branch");
371
+ }
372
+ i += 1;
373
+ continue;
374
+ }
375
+ if (arg === "-bd" || arg === "--biz-domain") {
376
+ businessDomain = args[i + 1] ?? "bd_public";
377
+ if (!businessDomain || businessDomain.startsWith("-")) {
378
+ throw new Error("Missing value for biz-domain flag");
379
+ }
380
+ i += 1;
381
+ continue;
382
+ }
383
+ if (arg === "--pretty") {
384
+ pretty = true;
385
+ continue;
386
+ }
387
+ if (!arg.startsWith("-") && !directory) {
388
+ directory = arg;
389
+ continue;
390
+ }
391
+ throw new Error(`Unsupported bkn push argument: ${arg}`);
392
+ }
393
+ if (!directory) {
394
+ throw new Error("Missing directory. Usage: kweaver bkn push <directory> [--branch main] [-bd value]");
395
+ }
396
+ if (!businessDomain)
397
+ businessDomain = resolveBusinessDomain();
398
+ return { directory, branch, businessDomain, pretty };
399
+ }
400
+ export function parseKnPullArgs(args) {
401
+ let knId = "";
402
+ let directory = "";
403
+ let branch = "main";
404
+ let businessDomain = "";
405
+ for (let i = 0; i < args.length; i += 1) {
406
+ const arg = args[i];
407
+ if (arg === "--help" || arg === "-h") {
408
+ throw new Error("help");
409
+ }
410
+ if (arg === "--branch") {
411
+ branch = args[i + 1] ?? "main";
412
+ if (!branch || branch.startsWith("-")) {
413
+ throw new Error("Missing value for --branch");
414
+ }
415
+ i += 1;
416
+ continue;
417
+ }
418
+ if (arg === "-bd" || arg === "--biz-domain") {
419
+ businessDomain = args[i + 1] ?? "bd_public";
420
+ if (!businessDomain || businessDomain.startsWith("-")) {
421
+ throw new Error("Missing value for biz-domain flag");
422
+ }
423
+ i += 1;
424
+ continue;
425
+ }
426
+ if (!arg.startsWith("-")) {
427
+ if (!knId) {
428
+ knId = arg;
429
+ }
430
+ else if (!directory) {
431
+ directory = arg;
432
+ }
433
+ else {
434
+ throw new Error(`Unexpected positional argument: ${arg}`);
435
+ }
436
+ continue;
437
+ }
438
+ throw new Error(`Unsupported bkn pull argument: ${arg}`);
439
+ }
440
+ if (!knId) {
441
+ throw new Error("Missing kn-id. Usage: kweaver bkn pull <kn-id> [<directory>] [--branch main] [-bd value]");
442
+ }
443
+ if (!businessDomain)
444
+ businessDomain = resolveBusinessDomain();
445
+ return { knId, directory: directory || knId, branch, businessDomain };
446
+ }
341
447
  function parseJsonObject(text, errorMessage) {
342
448
  let parsed;
343
449
  try {
@@ -366,7 +472,7 @@ function parseSearchAfterArray(text) {
366
472
  }
367
473
  export function parseKnObjectTypeQueryArgs(args) {
368
474
  let pretty = true;
369
- let businessDomain = "bd_public";
475
+ let businessDomain = "";
370
476
  let limit;
371
477
  let searchAfter;
372
478
  const positionalArgs = [];
@@ -425,6 +531,8 @@ export function parseKnObjectTypeQueryArgs(args) {
425
531
  if (typeof body.limit !== "number" || !Number.isFinite(body.limit) || body.limit < 1) {
426
532
  throw new Error("Missing limit. Provide it in body JSON or via --limit <n>.");
427
533
  }
534
+ if (!businessDomain)
535
+ businessDomain = resolveBusinessDomain();
428
536
  return {
429
537
  knId,
430
538
  otId,
@@ -443,6 +551,9 @@ Subcommands:
443
551
  update <kn-id> [options] Update a knowledge network
444
552
  delete <kn-id> Delete a knowledge network
445
553
  build <kn-id> [--wait|--no-wait] [--timeout n] Trigger full build
554
+ validate <directory> Validate a local BKN directory (no upload)
555
+ push <directory> [--branch main] Upload BKN directory as tar
556
+ pull <kn-id> [<directory>] [--branch main] Download BKN tar and extract
446
557
  export <kn-id> Export knowledge network (alias for get --export)
447
558
  stats <kn-id> Get statistics (alias for get --stats)
448
559
  search <kn-id> <query> [options] Semantic search within a knowledge network
@@ -452,7 +563,7 @@ Subcommands:
452
563
  object-type update <kn-id> <ot-id> [options] Update object type
453
564
  object-type delete <kn-id> <ot-ids> [-y] Delete object type(s)
454
565
  object-type query <kn-id> <ot-id> ['<json>'] Query object instances (ontology-query; supports --limit/--search-after)
455
- object-type properties <kn-id> <ot-id> '<json>' Query object properties
566
+ object-type properties <kn-id> <ot-id> '<json>' Query instance properties (json: {"_instance_identities":[{pk:val}],"properties":[...]})
456
567
  relation-type list <kn-id> List relation types (schema)
457
568
  relation-type get <kn-id> <rt-id> Get relation type details
458
569
  relation-type create <kn-id> [options] Create relation type (--name --source --target [--mapping src:tgt])
@@ -474,56 +585,71 @@ export async function runKnCommand(args) {
474
585
  console.log(KN_HELP);
475
586
  return 0;
476
587
  }
477
- if (subcommand === "list") {
478
- return runKnListCommand(rest);
479
- }
480
- if (subcommand === "get") {
481
- return runKnGetCommand(rest);
482
- }
483
- if (subcommand === "create") {
484
- return runKnCreateCommand(rest);
485
- }
486
- if (subcommand === "create-from-ds") {
487
- return runKnCreateFromDsCommand(rest);
488
- }
489
- if (subcommand === "update") {
490
- return runKnUpdateCommand(rest);
491
- }
492
- if (subcommand === "delete") {
493
- return runKnDeleteCommand(rest);
494
- }
495
- if (subcommand === "build") {
496
- return runKnBuildCommand(rest);
497
- }
498
- if (subcommand === "export") {
499
- return runKnGetCommand([...(rest[0] ? [rest[0]] : []), "--export", ...rest.slice(1)]);
500
- }
501
- if (subcommand === "stats") {
502
- return runKnGetCommand([...(rest[0] ? [rest[0]] : []), "--stats", ...rest.slice(1)]);
503
- }
504
- if (subcommand === "search") {
505
- return runKnSearchCommand(rest);
506
- }
507
- if (subcommand === "object-type") {
508
- return runKnObjectTypeCommand(rest);
509
- }
510
- if (subcommand === "relation-type") {
511
- return runKnRelationTypeCommand(rest);
512
- }
513
- if (subcommand === "subgraph") {
514
- return runKnSubgraphCommand(rest);
515
- }
516
- if (subcommand === "action-type") {
517
- return runKnActionTypeCommand(rest);
518
- }
519
- if (subcommand === "action-execution") {
520
- return runKnActionExecutionCommand(rest);
588
+ const dispatch = () => {
589
+ if (subcommand === "list")
590
+ return runKnListCommand(rest);
591
+ if (subcommand === "get")
592
+ return runKnGetCommand(rest);
593
+ if (subcommand === "create")
594
+ return runKnCreateCommand(rest);
595
+ if (subcommand === "create-from-ds")
596
+ return runKnCreateFromDsCommand(rest);
597
+ if (subcommand === "update")
598
+ return runKnUpdateCommand(rest);
599
+ if (subcommand === "delete")
600
+ return runKnDeleteCommand(rest);
601
+ if (subcommand === "build")
602
+ return runKnBuildCommand(rest);
603
+ if (subcommand === "validate")
604
+ return runKnValidateCommand(rest);
605
+ if (subcommand === "push")
606
+ return runKnPushCommand(rest);
607
+ if (subcommand === "pull")
608
+ return runKnPullCommand(rest);
609
+ if (subcommand === "export")
610
+ return runKnGetCommand([...(rest[0] ? [rest[0]] : []), "--export", ...rest.slice(1)]);
611
+ if (subcommand === "stats")
612
+ return runKnGetCommand([...(rest[0] ? [rest[0]] : []), "--stats", ...rest.slice(1)]);
613
+ if (subcommand === "search")
614
+ return runKnSearchCommand(rest);
615
+ if (subcommand === "object-type")
616
+ return runKnObjectTypeCommand(rest);
617
+ if (subcommand === "relation-type")
618
+ return runKnRelationTypeCommand(rest);
619
+ if (subcommand === "subgraph")
620
+ return runKnSubgraphCommand(rest);
621
+ if (subcommand === "action-type")
622
+ return runKnActionTypeCommand(rest);
623
+ if (subcommand === "action-execution")
624
+ return runKnActionExecutionCommand(rest);
625
+ if (subcommand === "action-log")
626
+ return runKnActionLogCommand(rest);
627
+ return Promise.resolve(-1);
628
+ };
629
+ try {
630
+ const code = await dispatch();
631
+ if (code === -1) {
632
+ console.error(`Unknown bkn subcommand: ${subcommand}`);
633
+ return 1;
634
+ }
635
+ return code;
521
636
  }
522
- if (subcommand === "action-log") {
523
- return runKnActionLogCommand(rest);
637
+ catch (error) {
638
+ // Auto-retry on 401: force-refresh the token and re-run the subcommand.
639
+ // The subcommand will call ensureValidToken() again and pick up the fresh token.
640
+ if (error instanceof HttpError && error.status === 401) {
641
+ try {
642
+ await ensureValidToken({ forceRefresh: true });
643
+ return await dispatch();
644
+ }
645
+ catch (retryError) {
646
+ console.error(formatHttpError(retryError));
647
+ return 1;
648
+ }
649
+ }
650
+ console.error(formatHttpError(error));
651
+ return 1;
524
652
  }
525
- console.error(`Unknown bkn subcommand: ${subcommand}`);
526
- return 1;
527
653
  }
528
654
  /** Parse object-type create args: --name --dataview-id --primary-key --display-key [--property '<json>' ...] */
529
655
  function parseObjectTypeCreateArgs(args) {
@@ -531,7 +657,7 @@ function parseObjectTypeCreateArgs(args) {
531
657
  let dataviewId = "";
532
658
  let primaryKey = "";
533
659
  let displayKey = "";
534
- let businessDomain = "bd_public";
660
+ let businessDomain = "";
535
661
  let branch = "main";
536
662
  let pretty = true;
537
663
  const properties = [];
@@ -597,13 +723,15 @@ function parseObjectTypeCreateArgs(args) {
597
723
  }));
598
724
  }
599
725
  const body = JSON.stringify({ entries: [entry], branch });
726
+ if (!businessDomain)
727
+ businessDomain = resolveBusinessDomain();
600
728
  return { knId, body, businessDomain, branch, pretty };
601
729
  }
602
730
  /** Parse object-type update args: --name X [--display-key Y] */
603
731
  function parseObjectTypeUpdateArgs(args) {
604
732
  let name;
605
733
  let displayKey;
606
- let businessDomain = "bd_public";
734
+ let businessDomain = "";
607
735
  let pretty = true;
608
736
  const positional = [];
609
737
  for (let i = 0; i < args.length; i += 1) {
@@ -641,11 +769,13 @@ function parseObjectTypeUpdateArgs(args) {
641
769
  if (Object.keys(payload).length === 0) {
642
770
  throw new Error("No update fields. Use --name or --display-key.");
643
771
  }
772
+ if (!businessDomain)
773
+ businessDomain = resolveBusinessDomain();
644
774
  return { knId, otId, body: JSON.stringify(payload), businessDomain, pretty };
645
775
  }
646
776
  /** Parse object-type delete args: <kn-id> <ot-ids> [-y] */
647
777
  function parseObjectTypeDeleteArgs(args) {
648
- let businessDomain = "bd_public";
778
+ let businessDomain = "";
649
779
  let yes = false;
650
780
  const positional = [];
651
781
  for (let i = 0; i < args.length; i += 1) {
@@ -667,12 +797,14 @@ function parseObjectTypeDeleteArgs(args) {
667
797
  if (!knId || !otIds) {
668
798
  throw new Error("Usage: kweaver bkn object-type delete <kn-id> <ot-ids> [-y]");
669
799
  }
800
+ if (!businessDomain)
801
+ businessDomain = resolveBusinessDomain();
670
802
  return { knId, otIds, businessDomain, yes };
671
803
  }
672
804
  /** Parse common flags for ontology-query subcommands; returns { filteredArgs, pretty, businessDomain } */
673
805
  function parseOntologyQueryFlags(args) {
674
806
  let pretty = true;
675
- let businessDomain = "bd_public";
807
+ let businessDomain = "";
676
808
  const filteredArgs = [];
677
809
  for (let i = 0; i < args.length; i += 1) {
678
810
  const arg = args[i];
@@ -690,11 +822,13 @@ function parseOntologyQueryFlags(args) {
690
822
  }
691
823
  filteredArgs.push(arg);
692
824
  }
825
+ if (!businessDomain)
826
+ businessDomain = resolveBusinessDomain();
693
827
  return { filteredArgs, pretty, businessDomain };
694
828
  }
695
829
  export function parseKnActionTypeExecuteArgs(args) {
696
830
  let pretty = true;
697
- let businessDomain = "bd_public";
831
+ let businessDomain = "";
698
832
  let wait = true;
699
833
  let timeout = 300;
700
834
  const positional = [];
@@ -733,6 +867,8 @@ export function parseKnActionTypeExecuteArgs(args) {
733
867
  if (!knId || !atId || !body) {
734
868
  throw new Error("Missing kn-id, at-id, or body. Usage: kweaver bkn action-type execute <kn-id> <at-id> '<json>' [options]");
735
869
  }
870
+ if (!businessDomain)
871
+ businessDomain = resolveBusinessDomain();
736
872
  return {
737
873
  knId,
738
874
  atId,
@@ -791,7 +927,9 @@ kweaver bkn object-type properties <kn-id> <ot-id> '<json>' [--pretty] [-bd valu
791
927
  list: List object types (schema) from ontology-manager.
792
928
  get: Get single object type details.
793
929
  create/update/delete: Schema CRUD (create requires dataview-id).
794
- query/properties: Query via ontology-query API. For query, --limit and --search-after are merged into the JSON body.`);
930
+ query/properties: Query via ontology-query API. For query, --limit and --search-after are merged into the JSON body.
931
+
932
+ properties JSON format: {"_instance_identities":[{"<primary-key>":"<value>"}],"properties":["prop1","prop2"]}`);
795
933
  return 0;
796
934
  }
797
935
  try {
@@ -896,7 +1034,8 @@ query/properties: Query via ontology-query API. For query, --limit and --search-
896
1034
  const parsed = parseOntologyQueryFlags(rest);
897
1035
  const [knId, otId, body] = parsed.filteredArgs;
898
1036
  if (!knId || !otId || !body) {
899
- console.error("Usage: kweaver bkn object-type properties <kn-id> <ot-id> '<json>' [options]");
1037
+ console.error(`Usage: kweaver bkn object-type properties <kn-id> <ot-id> '<json>' [options]
1038
+ JSON: {"_instance_identities":[{"<primary-key>":"<value>"}],"properties":["prop1","prop2"]}`);
900
1039
  return 1;
901
1040
  }
902
1041
  const token = await ensureValidToken();
@@ -930,7 +1069,7 @@ function parseRelationTypeCreateArgs(args) {
930
1069
  let name = "";
931
1070
  let source = "";
932
1071
  let target = "";
933
- let businessDomain = "bd_public";
1072
+ let businessDomain = "";
934
1073
  let branch = "main";
935
1074
  let pretty = true;
936
1075
  const mappings = [];
@@ -990,12 +1129,14 @@ function parseRelationTypeCreateArgs(args) {
990
1129
  })),
991
1130
  };
992
1131
  const body = JSON.stringify({ entries: [entry], branch });
1132
+ if (!businessDomain)
1133
+ businessDomain = resolveBusinessDomain();
993
1134
  return { knId, body, businessDomain, branch, pretty };
994
1135
  }
995
1136
  /** Parse relation-type update args: [--name X] */
996
1137
  function parseRelationTypeUpdateArgs(args) {
997
1138
  let name;
998
- let businessDomain = "bd_public";
1139
+ let businessDomain = "";
999
1140
  let pretty = true;
1000
1141
  const positional = [];
1001
1142
  for (let i = 0; i < args.length; i += 1) {
@@ -1024,11 +1165,13 @@ function parseRelationTypeUpdateArgs(args) {
1024
1165
  if (name === undefined) {
1025
1166
  throw new Error("No update fields. Use --name.");
1026
1167
  }
1168
+ if (!businessDomain)
1169
+ businessDomain = resolveBusinessDomain();
1027
1170
  return { knId, rtId, body: JSON.stringify({ name }), businessDomain, pretty };
1028
1171
  }
1029
1172
  /** Parse relation-type delete args: <kn-id> <rt-ids> [-y] */
1030
1173
  function parseRelationTypeDeleteArgs(args) {
1031
- let businessDomain = "bd_public";
1174
+ let businessDomain = "";
1032
1175
  let yes = false;
1033
1176
  const positional = [];
1034
1177
  for (let i = 0; i < args.length; i += 1) {
@@ -1050,6 +1193,8 @@ function parseRelationTypeDeleteArgs(args) {
1050
1193
  if (!knId || !rtIds) {
1051
1194
  throw new Error("Usage: kweaver bkn relation-type delete <kn-id> <rt-ids> [-y]");
1052
1195
  }
1196
+ if (!businessDomain)
1197
+ businessDomain = resolveBusinessDomain();
1053
1198
  return { knId, rtIds, businessDomain, yes };
1054
1199
  }
1055
1200
  async function runKnRelationTypeCommand(args) {
@@ -1189,6 +1334,17 @@ Query subgraph via ontology-query API. JSON body format see references/json-form
1189
1334
  return 1;
1190
1335
  }
1191
1336
  try {
1337
+ // Auto-detect query_type=relation_path when body contains source_object_type_id
1338
+ let queryType;
1339
+ try {
1340
+ const parsedBody = JSON.parse(body);
1341
+ if (parsedBody.source_object_type_id) {
1342
+ queryType = "relation_path";
1343
+ }
1344
+ }
1345
+ catch {
1346
+ // Not valid JSON — let the API return the error
1347
+ }
1192
1348
  const token = await ensureValidToken();
1193
1349
  const result = await subgraph({
1194
1350
  baseUrl: token.baseUrl,
@@ -1196,6 +1352,7 @@ Query subgraph via ontology-query API. JSON body format see references/json-form
1196
1352
  knId,
1197
1353
  body,
1198
1354
  businessDomain,
1355
+ queryType,
1199
1356
  });
1200
1357
  console.log(formatCallOutput(result, pretty));
1201
1358
  return 0;
@@ -1414,7 +1571,7 @@ Options for list: --limit, --need-total, --action-type-id, --status, --trigger-t
1414
1571
  return 0;
1415
1572
  }
1416
1573
  let pretty = true;
1417
- let businessDomain = "bd_public";
1574
+ let businessDomain = "";
1418
1575
  let limit;
1419
1576
  let needTotal;
1420
1577
  let actionTypeId;
@@ -1468,6 +1625,8 @@ Options for list: --limit, --need-total, --action-type-id, --status, --trigger-t
1468
1625
  }
1469
1626
  filteredArgs.push(arg);
1470
1627
  }
1628
+ if (!businessDomain)
1629
+ businessDomain = resolveBusinessDomain();
1471
1630
  try {
1472
1631
  const token = await ensureValidToken();
1473
1632
  const base = {
@@ -1707,7 +1866,7 @@ function parseKnCreateFromDsArgs(args) {
1707
1866
  let tablesStr = "";
1708
1867
  let build = true;
1709
1868
  let timeout = 300;
1710
- let businessDomain = "bd_public";
1869
+ let businessDomain = "";
1711
1870
  let pretty = true;
1712
1871
  for (let i = 0; i < args.length; i += 1) {
1713
1872
  const arg = args[i];
@@ -1751,6 +1910,8 @@ function parseKnCreateFromDsArgs(args) {
1751
1910
  if (!dsId || !name) {
1752
1911
  throw new Error("Usage: kweaver bkn create-from-ds <ds-id> --name X [options]");
1753
1912
  }
1913
+ if (!businessDomain)
1914
+ businessDomain = resolveBusinessDomain();
1754
1915
  return { dsId, name, tables, build, timeout, businessDomain, pretty };
1755
1916
  }
1756
1917
  async function runKnCreateFromDsCommand(args) {
@@ -1956,7 +2117,7 @@ export function parseKnBuildArgs(args) {
1956
2117
  let knId = "";
1957
2118
  let wait = true;
1958
2119
  let timeout = 300;
1959
- let businessDomain = "bd_public";
2120
+ let businessDomain = "";
1960
2121
  for (let i = 0; i < args.length; i += 1) {
1961
2122
  const arg = args[i];
1962
2123
  if (arg === "--help" || arg === "-h")
@@ -1988,6 +2149,8 @@ export function parseKnBuildArgs(args) {
1988
2149
  if (!knId) {
1989
2150
  throw new Error("Missing kn-id. Usage: kweaver bkn build <kn-id> [options]");
1990
2151
  }
2152
+ if (!businessDomain)
2153
+ businessDomain = resolveBusinessDomain();
1991
2154
  return { knId, wait, timeout, businessDomain };
1992
2155
  }
1993
2156
  async function runKnBuildCommand(args) {
@@ -2048,6 +2211,193 @@ async function runKnBuildCommand(args) {
2048
2211
  return 1;
2049
2212
  }
2050
2213
  }
2214
+ // ── push / pull (BKN tar import/export) ──────────────────────────────────────
2215
+ export function packDirectoryToTar(dirPath) {
2216
+ const absPath = resolve(dirPath);
2217
+ const entries = readdirSync(absPath);
2218
+ const args = ["cf", "-", "-C", absPath, ...entries];
2219
+ const result = spawnSync("tar", args, {
2220
+ encoding: "buffer",
2221
+ env: { ...process.env, COPYFILE_DISABLE: "1" },
2222
+ });
2223
+ if (result.error)
2224
+ throw result.error;
2225
+ if (result.status !== 0) {
2226
+ throw new Error(`tar pack failed: ${result.stderr?.toString() ?? result.status}`);
2227
+ }
2228
+ return result.stdout;
2229
+ }
2230
+ export function extractTarToDirectory(tarBuffer, dirPath) {
2231
+ const absPath = resolve(dirPath);
2232
+ mkdirSync(absPath, { recursive: true });
2233
+ const result = spawnSync("tar", ["xf", "-", "-C", absPath], {
2234
+ input: tarBuffer,
2235
+ });
2236
+ if (result.error) {
2237
+ throw result.error;
2238
+ }
2239
+ if (result.status !== 0) {
2240
+ throw new Error(`tar extract failed: ${result.stderr?.toString() ?? result.status}`);
2241
+ }
2242
+ }
2243
+ const KN_PUSH_HELP = `kweaver bkn push <directory> [options]
2244
+
2245
+ Pack a BKN directory into a tar and upload to import as a knowledge network.
2246
+
2247
+ Options:
2248
+ --branch <s> Branch name (default: main)
2249
+ -bd, --biz-domain Business domain (default: bd_public)
2250
+ --pretty Pretty-print JSON output`;
2251
+ const KN_PULL_HELP = `kweaver bkn pull <kn-id> [<directory>] [options]
2252
+
2253
+ Download a BKN tar from a knowledge network and extract to a local directory.
2254
+
2255
+ Options:
2256
+ <directory> Output directory (default: <kn-id>)
2257
+ --branch <s> Branch name (default: main)
2258
+ -bd, --biz-domain Business domain (default: bd_public)`;
2259
+ async function runKnValidateCommand(args) {
2260
+ if (args.includes("--help") || args.includes("-h")) {
2261
+ console.log("Usage: kweaver bkn validate <directory>\n\nValidate a local BKN directory without uploading.");
2262
+ return 0;
2263
+ }
2264
+ const directory = args.find((a) => !a.startsWith("-"));
2265
+ if (!directory) {
2266
+ console.error("Missing directory. Usage: kweaver bkn validate <directory>");
2267
+ return 1;
2268
+ }
2269
+ const absDir = resolve(directory);
2270
+ try {
2271
+ const stat = statSync(absDir);
2272
+ if (!stat.isDirectory()) {
2273
+ console.error(`Not a directory: ${directory}`);
2274
+ return 1;
2275
+ }
2276
+ }
2277
+ catch (err) {
2278
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
2279
+ console.error(`Directory not found: ${directory}`);
2280
+ return 1;
2281
+ }
2282
+ throw err;
2283
+ }
2284
+ try {
2285
+ const network = await loadNetwork(absDir);
2286
+ const result = validateNetwork(network);
2287
+ if (!result.ok) {
2288
+ for (const e of result.errors)
2289
+ console.error(` - ${e}`);
2290
+ console.error(`BKN validation failed: ${result.errors.length} error(s)`);
2291
+ return 1;
2292
+ }
2293
+ const objs = allObjects(network);
2294
+ const rels = allRelations(network);
2295
+ const acts = allActions(network);
2296
+ console.log(`Valid: ${objs.length} object types, ${rels.length} relation types, ${acts.length} action types`);
2297
+ return 0;
2298
+ }
2299
+ catch (error) {
2300
+ console.error(`BKN validation failed: ${error instanceof Error ? error.message : String(error)}`);
2301
+ return 1;
2302
+ }
2303
+ }
2304
+ async function runKnPushCommand(args) {
2305
+ let options;
2306
+ try {
2307
+ options = parseKnPushArgs(args);
2308
+ }
2309
+ catch (error) {
2310
+ if (error instanceof Error && error.message === "help") {
2311
+ console.log(KN_PUSH_HELP);
2312
+ return 0;
2313
+ }
2314
+ console.error(formatHttpError(error));
2315
+ return 1;
2316
+ }
2317
+ const absDir = resolve(options.directory);
2318
+ try {
2319
+ const stat = statSync(absDir);
2320
+ if (!stat.isDirectory()) {
2321
+ console.error(`Not a directory: ${options.directory}`);
2322
+ return 1;
2323
+ }
2324
+ }
2325
+ catch (err) {
2326
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
2327
+ console.error(`Directory not found: ${options.directory}`);
2328
+ return 1;
2329
+ }
2330
+ throw err;
2331
+ }
2332
+ try {
2333
+ const network = await loadNetwork(absDir);
2334
+ const objs = allObjects(network);
2335
+ const rels = allRelations(network);
2336
+ const acts = allActions(network);
2337
+ console.error(`Validated: ${objs.length} object types, ${rels.length} relation types, ${acts.length} action types`);
2338
+ }
2339
+ catch (error) {
2340
+ console.error(`BKN validation failed: ${error instanceof Error ? error.message : String(error)}`);
2341
+ return 1;
2342
+ }
2343
+ try {
2344
+ await generateChecksum(absDir);
2345
+ console.error("Checksum generated");
2346
+ }
2347
+ catch (error) {
2348
+ console.error(`Checksum generation failed: ${error instanceof Error ? error.message : String(error)}`);
2349
+ return 1;
2350
+ }
2351
+ try {
2352
+ const tarBuffer = packDirectoryToTar(absDir);
2353
+ const token = await ensureValidToken();
2354
+ const body = await uploadBkn({
2355
+ baseUrl: token.baseUrl,
2356
+ accessToken: token.accessToken,
2357
+ tarBuffer,
2358
+ businessDomain: options.businessDomain,
2359
+ branch: options.branch,
2360
+ });
2361
+ console.log(formatCallOutput(body, options.pretty));
2362
+ return 0;
2363
+ }
2364
+ catch (error) {
2365
+ console.error(formatHttpError(error));
2366
+ return 1;
2367
+ }
2368
+ }
2369
+ async function runKnPullCommand(args) {
2370
+ let options;
2371
+ try {
2372
+ options = parseKnPullArgs(args);
2373
+ }
2374
+ catch (error) {
2375
+ if (error instanceof Error && error.message === "help") {
2376
+ console.log(KN_PULL_HELP);
2377
+ return 0;
2378
+ }
2379
+ console.error(formatHttpError(error));
2380
+ return 1;
2381
+ }
2382
+ try {
2383
+ const token = await ensureValidToken();
2384
+ const tarBuffer = await downloadBkn({
2385
+ baseUrl: token.baseUrl,
2386
+ accessToken: token.accessToken,
2387
+ knId: options.knId,
2388
+ businessDomain: options.businessDomain,
2389
+ branch: options.branch,
2390
+ });
2391
+ const absDir = resolve(options.directory);
2392
+ extractTarToDirectory(tarBuffer, absDir);
2393
+ console.log(`Extracted to ${absDir}`);
2394
+ return 0;
2395
+ }
2396
+ catch (error) {
2397
+ console.error(formatHttpError(error));
2398
+ return 1;
2399
+ }
2400
+ }
2051
2401
  // ── search ──────────────────────────────────────────────────────────────────
2052
2402
  const KN_SEARCH_HELP = `kweaver bkn search <kn-id> <query> [--max-concepts <n>] [--mode <mode>] [--pretty] [-bd value]
2053
2403
 
@@ -2065,7 +2415,7 @@ export function parseKnSearchArgs(args) {
2065
2415
  let maxConcepts = 10;
2066
2416
  let mode = "keyword_vector_retrieval";
2067
2417
  let pretty = false;
2068
- let businessDomain = process.env.KWEAVER_BUSINESS_DOMAIN ?? "bd_public";
2418
+ let businessDomain = process.env.KWEAVER_BUSINESS_DOMAIN ?? "";
2069
2419
  const positional = [];
2070
2420
  for (let i = 0; i < args.length; i++) {
2071
2421
  const arg = args[i];
@@ -2096,6 +2446,8 @@ export function parseKnSearchArgs(args) {
2096
2446
  if (!knId || !query) {
2097
2447
  throw new Error("Usage: kweaver bkn search <kn-id> <query> [options]");
2098
2448
  }
2449
+ if (!businessDomain)
2450
+ businessDomain = resolveBusinessDomain();
2099
2451
  return { knId, query, maxConcepts, mode, pretty, businessDomain };
2100
2452
  }
2101
2453
  async function runKnSearchCommand(args) {