@kweaver-ai/kweaver-sdk 0.5.0 → 0.5.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 (66) hide show
  1. package/README.md +6 -1
  2. package/README.zh.md +5 -0
  3. package/dist/api/agent-chat.d.ts +1 -1
  4. package/dist/api/agent-chat.js +4 -4
  5. package/dist/api/agent-list.d.ts +35 -0
  6. package/dist/api/agent-list.js +86 -12
  7. package/dist/api/bkn-backend.d.ts +60 -0
  8. package/dist/api/bkn-backend.js +103 -10
  9. package/dist/api/conversations.d.ts +6 -3
  10. package/dist/api/conversations.js +26 -27
  11. package/dist/api/dataflow.js +1 -10
  12. package/dist/api/datasources.js +1 -10
  13. package/dist/api/dataviews.js +1 -10
  14. package/dist/api/headers.d.ts +9 -0
  15. package/dist/api/headers.js +25 -0
  16. package/dist/api/knowledge-networks.d.ts +41 -0
  17. package/dist/api/knowledge-networks.js +69 -22
  18. package/dist/api/ontology-query.d.ts +14 -1
  19. package/dist/api/ontology-query.js +63 -49
  20. package/dist/api/semantic-search.js +2 -12
  21. package/dist/api/skills.d.ts +141 -0
  22. package/dist/api/skills.js +216 -0
  23. package/dist/api/vega.d.ts +63 -0
  24. package/dist/api/vega.js +131 -10
  25. package/dist/auth/oauth.d.ts +5 -1
  26. package/dist/auth/oauth.js +293 -94
  27. package/dist/cli.js +29 -4
  28. package/dist/client.d.ts +3 -0
  29. package/dist/client.js +4 -0
  30. package/dist/commands/agent.d.ts +33 -1
  31. package/dist/commands/agent.js +721 -49
  32. package/dist/commands/auth.js +211 -21
  33. package/dist/commands/bkn-ops.d.ts +77 -0
  34. package/dist/commands/bkn-ops.js +1056 -0
  35. package/dist/commands/bkn-query.d.ts +14 -0
  36. package/dist/commands/bkn-query.js +370 -0
  37. package/dist/commands/bkn-schema.d.ts +135 -0
  38. package/dist/commands/bkn-schema.js +1461 -0
  39. package/dist/commands/bkn-utils.d.ts +36 -0
  40. package/dist/commands/bkn-utils.js +102 -0
  41. package/dist/commands/bkn.d.ts +7 -113
  42. package/dist/commands/bkn.js +175 -2429
  43. package/dist/commands/dataview.d.ts +7 -0
  44. package/dist/commands/dataview.js +38 -2
  45. package/dist/commands/ds.d.ts +1 -0
  46. package/dist/commands/ds.js +8 -1
  47. package/dist/commands/import-csv.d.ts +2 -0
  48. package/dist/commands/import-csv.js +3 -2
  49. package/dist/commands/skill.d.ts +26 -0
  50. package/dist/commands/skill.js +524 -0
  51. package/dist/commands/vega.js +371 -14
  52. package/dist/config/jwt.d.ts +6 -0
  53. package/dist/config/jwt.js +21 -0
  54. package/dist/config/store.d.ts +37 -5
  55. package/dist/config/store.js +363 -30
  56. package/dist/index.d.ts +6 -1
  57. package/dist/index.js +5 -1
  58. package/dist/resources/bkn.d.ts +4 -0
  59. package/dist/resources/bkn.js +4 -0
  60. package/dist/resources/conversations.d.ts +5 -2
  61. package/dist/resources/conversations.js +17 -3
  62. package/dist/resources/skills.d.ts +47 -0
  63. package/dist/resources/skills.js +47 -0
  64. package/dist/resources/vega.d.ts +11 -0
  65. package/dist/resources/vega.js +37 -1
  66. package/package.json +1 -1
@@ -1,32 +1,20 @@
1
1
  import { createInterface } from "node:readline";
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";
6
- import { prepareBknDirectoryForImport, stripBknEncodingCliArgs, } from "../utils/bkn-encoding.js";
2
+ import { readFileSync } from "node:fs";
7
3
  import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/oauth.js";
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";
9
- import { objectTypeQuery, objectTypeProperties, subgraph, actionTypeQuery, actionTypeExecute, actionExecutionGet, actionLogsList, actionLogGet, actionLogCancel, } from "../api/ontology-query.js";
10
- import { semanticSearch } from "../api/semantic-search.js";
11
- import { listTablesWithColumns, scanMetadata, getDatasource } from "../api/datasources.js";
12
- import { createDataView, findDataView } from "../api/dataviews.js"; // used by runKnCreateFromDsCommand
13
- import { downloadBkn, uploadBkn } from "../api/bkn-backend.js";
4
+ import { listKnowledgeNetworks, getKnowledgeNetwork, createKnowledgeNetwork, updateKnowledgeNetwork, deleteKnowledgeNetwork, } from "../api/knowledge-networks.js";
14
5
  import { formatCallOutput } from "./call.js";
15
6
  import { resolveBusinessDomain } from "../config/store.js";
16
- import { runDsImportCsv } from "./ds.js";
17
- export async function pollWithBackoff(opts) {
18
- const { fn, timeout, maxInterval = 15000, _sleep = (ms) => new Promise(r => setTimeout(r, ms)) } = opts;
19
- let currentInterval = opts.interval;
20
- const deadline = Date.now() + timeout;
21
- while (Date.now() < deadline) {
22
- const result = await fn();
23
- if (result.done)
24
- return result.value;
25
- await _sleep(currentInterval);
26
- currentInterval = Math.min(currentInterval * 2, maxInterval);
27
- }
28
- throw new Error(`Polling timed out after ${timeout}ms`);
29
- }
7
+ import { runKnObjectTypeCommand, runKnRelationTypeCommand, runKnActionTypeCommand, runKnConceptGroupCommand, } from "./bkn-schema.js";
8
+ import { runKnSubgraphCommand, runKnActionExecutionCommand, runKnActionLogCommand, runKnSearchCommand, runKnRelationTypePathsCommand, runKnResourcesCommand, } from "./bkn-query.js";
9
+ import { runKnBuildCommand, runKnValidateCommand, runKnPushCommand, runKnPullCommand, runKnCreateFromDsCommand, runKnCreateFromCsvCommand, runKnActionScheduleCommand, runKnJobCommand, } from "./bkn-ops.js";
10
+ // Re-export shared utils for backward compatibility (tests import from bkn.js)
11
+ export { pollWithBackoff, parseOntologyQueryFlags, parseJsonObject, parseSearchAfterArray, confirmYes, DISPLAY_HINTS, detectPrimaryKey, detectDisplayKey, } from "./bkn-utils.js";
12
+ // Re-export schema types and parse functions for backward compatibility
13
+ export { parseObjectTypeCreateArgs, finalizeObjectTypeCreateFromDataview, ensureMappedFieldOnDataProperty, normalizeAdpFieldType, parseKnObjectTypeQueryArgs, parseKnActionTypeExecuteArgs, parseRelationTypeCreateArgs, applyObjectTypeMerge, stripObjectTypeForPut, parseObjectTypeUpdateArgs, parseObjectTypeDeleteArgs, parseRelationTypeUpdateArgs, parseRelationTypeDeleteArgs, runKnObjectTypeCommand, runKnRelationTypeCommand, runKnActionTypeCommand, parseConceptGroupArgs, } from "./bkn-schema.js";
14
+ // Re-export query parse functions for backward compatibility (tests import from bkn.js)
15
+ export { parseKnSearchArgs } from "./bkn-query.js";
16
+ // Re-export ops types and parse functions for backward compatibility (tests import from bkn.js)
17
+ export { parseKnBuildArgs, parseKnPushArgs, parseKnPullArgs, packDirectoryToTar, extractTarToDirectory, parseActionScheduleArgs, parseJobArgs, } from "./bkn-ops.js";
30
18
  export function formatSimpleKnList(text, pretty, includeDetail = false) {
31
19
  const parsed = JSON.parse(text);
32
20
  const entries = Array.isArray(parsed.entries) ? parsed.entries : [];
@@ -368,245 +356,6 @@ export function parseKnDeleteArgs(args) {
368
356
  businessDomain = resolveBusinessDomain();
369
357
  return { knId, businessDomain, yes };
370
358
  }
371
- export function parseKnPushArgs(args) {
372
- const { rest, options: encodingOptions } = stripBknEncodingCliArgs(args);
373
- let directory = "";
374
- let branch = "main";
375
- let businessDomain = "";
376
- let pretty = true;
377
- for (let i = 0; i < rest.length; i += 1) {
378
- const arg = rest[i];
379
- if (arg === "--help" || arg === "-h") {
380
- throw new Error("help");
381
- }
382
- if (arg === "--branch") {
383
- branch = args[i + 1] ?? "main";
384
- if (!branch || branch.startsWith("-")) {
385
- throw new Error("Missing value for --branch");
386
- }
387
- i += 1;
388
- continue;
389
- }
390
- if (arg === "-bd" || arg === "--biz-domain") {
391
- businessDomain = args[i + 1] ?? "bd_public";
392
- if (!businessDomain || businessDomain.startsWith("-")) {
393
- throw new Error("Missing value for biz-domain flag");
394
- }
395
- i += 1;
396
- continue;
397
- }
398
- if (arg === "--pretty") {
399
- pretty = true;
400
- continue;
401
- }
402
- if (!arg.startsWith("-") && !directory) {
403
- directory = arg;
404
- continue;
405
- }
406
- throw new Error(`Unsupported bkn push argument: ${arg}`);
407
- }
408
- if (!directory) {
409
- throw new Error("Missing directory. Usage: kweaver bkn push <directory> [--branch main] [-bd value]");
410
- }
411
- if (!businessDomain)
412
- businessDomain = resolveBusinessDomain();
413
- return { directory, branch, businessDomain, pretty, encodingOptions };
414
- }
415
- export function parseKnPullArgs(args) {
416
- let knId = "";
417
- let directory = "";
418
- let branch = "main";
419
- let businessDomain = "";
420
- for (let i = 0; i < args.length; i += 1) {
421
- const arg = args[i];
422
- if (arg === "--help" || arg === "-h") {
423
- throw new Error("help");
424
- }
425
- if (arg === "--branch") {
426
- branch = args[i + 1] ?? "main";
427
- if (!branch || branch.startsWith("-")) {
428
- throw new Error("Missing value for --branch");
429
- }
430
- i += 1;
431
- continue;
432
- }
433
- if (arg === "-bd" || arg === "--biz-domain") {
434
- businessDomain = args[i + 1] ?? "bd_public";
435
- if (!businessDomain || businessDomain.startsWith("-")) {
436
- throw new Error("Missing value for biz-domain flag");
437
- }
438
- i += 1;
439
- continue;
440
- }
441
- if (!arg.startsWith("-")) {
442
- if (!knId) {
443
- knId = arg;
444
- }
445
- else if (!directory) {
446
- directory = arg;
447
- }
448
- else {
449
- throw new Error(`Unexpected positional argument: ${arg}`);
450
- }
451
- continue;
452
- }
453
- throw new Error(`Unsupported bkn pull argument: ${arg}`);
454
- }
455
- if (!knId) {
456
- throw new Error("Missing kn-id. Usage: kweaver bkn pull <kn-id> [<directory>] [--branch main] [-bd value]");
457
- }
458
- if (!businessDomain)
459
- businessDomain = resolveBusinessDomain();
460
- return { knId, directory: directory || knId, branch, businessDomain };
461
- }
462
- function parseJsonObject(text, errorMessage) {
463
- let parsed;
464
- try {
465
- parsed = JSON.parse(text);
466
- }
467
- catch {
468
- throw new Error(errorMessage);
469
- }
470
- if (!parsed || Array.isArray(parsed) || typeof parsed !== "object") {
471
- throw new Error(errorMessage);
472
- }
473
- return parsed;
474
- }
475
- const MAX_OUTPUT_BYTES = 100_000;
476
- /**
477
- * If a query response exceeds MAX_OUTPUT_BYTES, trim the datas array
478
- * to fit, preserving valid JSON and the search_after cursor for pagination.
479
- */
480
- function truncateQueryResult(raw) {
481
- if (raw.length <= MAX_OUTPUT_BYTES) {
482
- return raw;
483
- }
484
- let parsed;
485
- try {
486
- parsed = JSON.parse(raw);
487
- }
488
- catch {
489
- return raw;
490
- }
491
- const datas = parsed.datas;
492
- if (!Array.isArray(datas) || datas.length === 0) {
493
- return raw;
494
- }
495
- const originalCount = datas.length;
496
- while (datas.length > 1) {
497
- datas.pop();
498
- const candidate = JSON.stringify(parsed);
499
- if (candidate.length <= MAX_OUTPUT_BYTES) {
500
- const remaining = originalCount - datas.length;
501
- const sa = parsed.search_after;
502
- parsed._truncated = {
503
- returned: datas.length,
504
- total_fetched: originalCount,
505
- remaining,
506
- next_search_after: sa ?? null,
507
- hint: sa
508
- ? `Pass --search-after '${JSON.stringify(sa)}' --limit ${datas.length} to fetch the next page.`
509
- : `Reduce --limit to ${datas.length} or less to avoid truncation.`,
510
- };
511
- console.error(`[warn] Truncated ${originalCount} → ${datas.length} records (output exceeded ${Math.round(MAX_OUTPUT_BYTES / 1024)}KB). ${parsed._truncated.hint}`);
512
- return JSON.stringify(parsed);
513
- }
514
- }
515
- const sa = parsed.search_after;
516
- parsed._truncated = {
517
- returned: 1,
518
- total_fetched: originalCount,
519
- remaining: originalCount - 1,
520
- next_search_after: sa ?? null,
521
- hint: `Single record is very large. Use --limit 1 and --search-after to iterate.`,
522
- };
523
- console.error(`[warn] Truncated ${originalCount} → 1 record. Single record is very large. Use --limit 1 and --search-after to iterate.`);
524
- return JSON.stringify(parsed);
525
- }
526
- function parseSearchAfterArray(text) {
527
- let parsed;
528
- try {
529
- parsed = JSON.parse(text);
530
- }
531
- catch {
532
- throw new Error("Invalid value for --search-after. Expected a JSON array string.");
533
- }
534
- if (!Array.isArray(parsed)) {
535
- throw new Error("Invalid value for --search-after. Expected a JSON array string.");
536
- }
537
- return parsed;
538
- }
539
- export function parseKnObjectTypeQueryArgs(args) {
540
- let pretty = true;
541
- let businessDomain = "";
542
- let limit;
543
- let searchAfter;
544
- const positionalArgs = [];
545
- for (let i = 0; i < args.length; i += 1) {
546
- const arg = args[i];
547
- if (arg === "--help" || arg === "-h") {
548
- throw new Error("help");
549
- }
550
- if (arg === "--pretty") {
551
- pretty = true;
552
- continue;
553
- }
554
- if (arg === "-bd" || arg === "--biz-domain") {
555
- businessDomain = args[i + 1] ?? "bd_public";
556
- if (!businessDomain || businessDomain.startsWith("-")) {
557
- throw new Error("Missing value for biz-domain flag");
558
- }
559
- i += 1;
560
- continue;
561
- }
562
- if (arg === "--limit") {
563
- const rawLimit = args[i + 1];
564
- const parsedLimit = parseInt(rawLimit ?? "", 10);
565
- if (!rawLimit || rawLimit.startsWith("-") || Number.isNaN(parsedLimit) || parsedLimit < 1) {
566
- throw new Error("Invalid value for --limit. Expected a positive integer.");
567
- }
568
- limit = parsedLimit;
569
- i += 1;
570
- continue;
571
- }
572
- if (arg === "--search-after") {
573
- const rawSearchAfter = args[i + 1];
574
- if (!rawSearchAfter) {
575
- throw new Error("Missing value for --search-after. Expected a JSON array string.");
576
- }
577
- searchAfter = parseSearchAfterArray(rawSearchAfter);
578
- i += 1;
579
- continue;
580
- }
581
- positionalArgs.push(arg);
582
- }
583
- const [knId, otId, bodyText = "{}"] = positionalArgs;
584
- if (!knId || !otId) {
585
- throw new Error("Usage: kweaver bkn object-type query <kn-id> <ot-id> ['<json>'] [--limit <n>] [--search-after '<json-array>'] [--pretty] [-bd value]");
586
- }
587
- if (positionalArgs.length > 3) {
588
- throw new Error("Usage: kweaver bkn object-type query <kn-id> <ot-id> ['<json>'] [--limit <n>] [--search-after '<json-array>'] [--pretty] [-bd value]");
589
- }
590
- const body = parseJsonObject(bodyText, "object-type query body must be a JSON object.");
591
- if (limit !== undefined) {
592
- body.limit = limit;
593
- }
594
- if (searchAfter !== undefined) {
595
- body.search_after = searchAfter;
596
- }
597
- if (typeof body.limit !== "number" || !Number.isFinite(body.limit) || body.limit < 1) {
598
- body.limit = 50;
599
- }
600
- if (!businessDomain)
601
- businessDomain = resolveBusinessDomain();
602
- return {
603
- knId,
604
- otId,
605
- body: JSON.stringify(body),
606
- pretty,
607
- businessDomain,
608
- };
609
- }
610
359
  const KN_HELP = `kweaver bkn
611
360
 
612
361
  Subcommands:
@@ -635,16 +384,25 @@ Subcommands:
635
384
  relation-type list <kn-id> List relation types (schema)
636
385
  relation-type get <kn-id> <rt-id> Get relation type details
637
386
  relation-type create <kn-id> [options] Create relation type (--name --source --target [--mapping src:tgt])
638
- relation-type update <kn-id> <rt-id> [options] Update relation type
387
+ relation-type update <kn-id> <rt-id> --source <ot-id> --target <ot-id> [--name X] [--type direct|data_view] [--mapping src:tgt ...] Update relation type
639
388
  relation-type delete <kn-id> <rt-ids> [-y] Delete relation type(s)
640
389
  subgraph <kn-id> '<json>' Query subgraph
641
390
  action-type list <kn-id> List action types (schema)
391
+ action-type get <kn-id> <at-id> Get action type details
392
+ action-type create <kn-id> '<json>' Create action type
393
+ action-type update <kn-id> <at-id> '<json>' Update action type
394
+ action-type delete <kn-id> <at-ids> [-y] Delete action type(s)
642
395
  action-type query <kn-id> <at-id> '<json>' Query action info
643
396
  action-type execute <kn-id> <at-id> '<json>' Execute action (has side effects)
644
397
  action-execution get <kn-id> <execution-id> Get execution status
645
398
  action-log list <kn-id> [options] List action execution logs
646
399
  action-log get <kn-id> <log-id> Get single execution log
647
400
  action-log cancel <kn-id> <log-id> Cancel running execution (has side effects)
401
+ concept-group list|get|create|update|delete|add-members|remove-members <kn-id> ...
402
+ action-schedule list|get|create|update|set-status|delete <kn-id> ...
403
+ job list|get|tasks|delete <kn-id> ...
404
+ relation-type-paths <kn-id> '<json>' Query relation type paths between OTs
405
+ resources List available resources
648
406
 
649
407
  Use 'kweaver bkn <subcommand> --help' for subcommand options.`;
650
408
  export async function runKnCommand(args) {
@@ -694,6 +452,16 @@ export async function runKnCommand(args) {
694
452
  return runKnActionExecutionCommand(rest);
695
453
  if (subcommand === "action-log")
696
454
  return runKnActionLogCommand(rest);
455
+ if (subcommand === "concept-group")
456
+ return runKnConceptGroupCommand(rest);
457
+ if (subcommand === "action-schedule")
458
+ return runKnActionScheduleCommand(rest);
459
+ if (subcommand === "job")
460
+ return runKnJobCommand(rest);
461
+ if (subcommand === "relation-type-paths")
462
+ return runKnRelationTypePathsCommand(rest);
463
+ if (subcommand === "resources")
464
+ return runKnResourcesCommand(rest);
697
465
  return Promise.resolve(-1);
698
466
  };
699
467
  try {
@@ -711,1341 +479,119 @@ export async function runKnCommand(args) {
711
479
  return 1;
712
480
  }
713
481
  }
714
- /** Parse object-type create args: --name --dataview-id --primary-key --display-key [--property '<json>' ...] */
715
- export function parseObjectTypeCreateArgs(args) {
716
- let name = "";
717
- let dataviewId = "";
718
- let primaryKey = "";
719
- let displayKey = "";
720
- let businessDomain = "";
721
- let branch = "main";
722
- let pretty = true;
723
- const properties = [];
724
- const positional = [];
725
- for (let i = 0; i < args.length; i += 1) {
726
- const arg = args[i];
727
- if (arg === "--help" || arg === "-h")
728
- throw new Error("help");
729
- if (arg === "--name" && args[i + 1]) {
730
- name = args[++i];
731
- continue;
732
- }
733
- if (arg === "--dataview-id" && args[i + 1]) {
734
- dataviewId = args[++i];
735
- continue;
736
- }
737
- if (arg === "--primary-key" && args[i + 1]) {
738
- primaryKey = args[++i];
739
- continue;
740
- }
741
- if (arg === "--display-key" && args[i + 1]) {
742
- displayKey = args[++i];
743
- continue;
744
- }
745
- if (arg === "--property" && args[i + 1]) {
746
- properties.push(args[++i]);
747
- continue;
748
- }
749
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
750
- businessDomain = args[++i];
751
- continue;
752
- }
753
- if (arg === "--branch" && args[i + 1]) {
754
- branch = args[++i];
755
- continue;
756
- }
757
- if (arg === "--pretty") {
758
- pretty = true;
759
- continue;
760
- }
761
- if (!arg.startsWith("-"))
762
- positional.push(arg);
763
- }
764
- const knId = positional[0];
765
- if (!knId || !name || !dataviewId || !primaryKey || !displayKey) {
766
- throw new Error("Usage: kweaver bkn object-type create <kn-id> --name X --dataview-id Y --primary-key Z --display-key W");
482
+ async function runKnListCommand(args) {
483
+ let options;
484
+ try {
485
+ options = parseKnListArgs(args);
767
486
  }
768
- const entry = {
769
- branch,
770
- name,
771
- data_source: { type: "data_view", id: dataviewId },
772
- primary_keys: [primaryKey],
773
- display_key: displayKey,
774
- };
775
- if (properties.length > 0) {
776
- entry.data_properties = properties.map((p) => JSON.parse(p));
487
+ catch (error) {
488
+ if (error instanceof Error && error.message === "help") {
489
+ console.log(KN_LIST_HELP);
490
+ return 0;
491
+ }
492
+ console.error(formatHttpError(error));
493
+ return 1;
777
494
  }
778
- else {
779
- const autoProps = new Set([primaryKey, displayKey]);
780
- entry.data_properties = Array.from(autoProps).map((n) => ({
781
- name: n,
782
- display_name: n,
783
- type: "string",
784
- }));
495
+ try {
496
+ const token = await ensureValidToken();
497
+ const body = await listKnowledgeNetworks({
498
+ baseUrl: token.baseUrl,
499
+ accessToken: token.accessToken,
500
+ businessDomain: options.businessDomain,
501
+ offset: options.offset,
502
+ limit: options.limit,
503
+ sort: options.sort,
504
+ direction: options.direction,
505
+ name_pattern: options.name_pattern,
506
+ tag: options.tag,
507
+ });
508
+ if (body) {
509
+ console.log(options.verbose
510
+ ? formatCallOutput(body, options.pretty)
511
+ : formatSimpleKnList(body, options.pretty, options.detail));
512
+ }
513
+ return 0;
785
514
  }
786
- const body = JSON.stringify({ entries: [entry] });
787
- if (!businessDomain)
788
- businessDomain = resolveBusinessDomain();
789
- return { knId, body, businessDomain, branch, pretty };
790
- }
791
- const OBJECT_TYPE_PUT_STRIP_KEYS = new Set([
792
- "status",
793
- "creator",
794
- "updater",
795
- "create_time",
796
- "update_time",
797
- "module_type",
798
- "kn_id",
799
- ]);
800
- /** Prepare a GET response entry for PUT (drop read-only fields). */
801
- export function stripObjectTypeForPut(entry) {
802
- const out = { ...entry };
803
- for (const k of OBJECT_TYPE_PUT_STRIP_KEYS) {
804
- delete out[k];
515
+ catch (error) {
516
+ console.error(formatHttpError(error));
517
+ return 1;
805
518
  }
806
- return out;
807
519
  }
808
- /**
809
- * Apply merge flags onto a stripped object-type object (mutates copy).
810
- * - Add: property `name` not in list → append.
811
- * - Update: property `name` exists → replace entry (same as add; CLI also accepts `--update-property`).
812
- * - Delete: `--remove-property` removes by `name` before adds are applied.
813
- */
814
- export function applyObjectTypeMerge(target, merge) {
815
- if (merge.name !== undefined)
816
- target.name = merge.name;
817
- if (merge.displayKey !== undefined)
818
- target.display_key = merge.displayKey;
819
- if (merge.comment !== undefined)
820
- target.comment = merge.comment;
821
- if (merge.icon !== undefined)
822
- target.icon = merge.icon;
823
- if (merge.color !== undefined)
824
- target.color = merge.color;
825
- if (merge.tags !== undefined)
826
- target.tags = merge.tags;
827
- let props = target.data_properties;
828
- if (!Array.isArray(props)) {
829
- props = [];
830
- }
831
- else {
832
- props = props.map((p) => p && typeof p === "object" && !Array.isArray(p) ? { ...p } : p);
833
- }
834
- const list = props;
835
- for (const rm of merge.removeProperties) {
836
- for (let j = list.length - 1; j >= 0; j -= 1) {
837
- const n = list[j]?.name;
838
- if (typeof n === "string" && n === rm)
839
- list.splice(j, 1);
840
- }
520
+ const KN_LIST_HELP = `kweaver bkn list [options]
521
+
522
+ List business knowledge networks from the ontology-manager API.
523
+
524
+ Options:
525
+ --offset <n> Offset (default: 0)
526
+ --limit <n> Limit (default: 30)
527
+ --sort <key> Sort field (default: update_time)
528
+ --direction <asc|desc> Sort direction (default: desc)
529
+ --name-pattern <s> Filter by name pattern
530
+ --tag <s> Filter by tag
531
+ --detail Include the detail field in simplified output
532
+ --verbose, -v Show full JSON response
533
+ -bd, --biz-domain <value> Business domain (default: bd_public)
534
+ --pretty Pretty-print JSON output (applies to both modes)`;
535
+ const KN_GET_HELP = `kweaver bkn get <kn-id> [options]
536
+
537
+ Get knowledge network detail.
538
+
539
+ Options:
540
+ --stats Include statistics
541
+ --export Export mode (include sub-types)
542
+ -bd, --biz-domain <value> Business domain (default: bd_public)
543
+ --pretty Pretty-print JSON output`;
544
+ const KN_CREATE_HELP = `kweaver bkn create [options]
545
+
546
+ Create a knowledge network.
547
+
548
+ Options:
549
+ --name <s> Name (required unless --body-file)
550
+ --comment <s> Comment
551
+ --tags <t1,t2> Comma-separated tags
552
+ --icon <s> Icon
553
+ --color <s> Color
554
+ --branch <s> Branch (default: main)
555
+ --base-branch <s> Base branch (default: empty for main)
556
+ --body-file <path> Read full JSON body from file (cannot combine with flags above)
557
+ --import-mode <normal|ignore|overwrite> Import mode (default: normal)
558
+ --validate-dependency <true|false> Validate dependency (default: true)
559
+ -bd, --biz-domain <value> Business domain (default: bd_public)
560
+ --pretty Pretty-print JSON output`;
561
+ const KN_UPDATE_HELP = `kweaver bkn update <kn-id> [options]
562
+
563
+ Update a knowledge network.
564
+
565
+ Options:
566
+ --name <s> Name (required unless --body-file)
567
+ --comment <s> Comment
568
+ --tags <t1,t2> Comma-separated tags
569
+ --icon <s> Icon
570
+ --color <s> Color
571
+ --branch <s> Branch (default: main)
572
+ --base-branch <s> Base branch (default: empty for main)
573
+ --body-file <path> Read full JSON body from file (cannot combine with flags above)
574
+ -bd, --biz-domain <value> Business domain (default: bd_public)
575
+ --pretty Pretty-print JSON output`;
576
+ const KN_DELETE_HELP = `kweaver bkn delete <kn-id>
577
+
578
+ Delete a knowledge network and its object types, relation types, action types, and concept groups.
579
+
580
+ Options:
581
+ --yes, -y Skip confirmation prompt
582
+ -bd, --biz-domain <value> Business domain (default: bd_public)`;
583
+ async function runKnGetCommand(args) {
584
+ let options;
585
+ try {
586
+ options = parseKnGetArgs(args);
841
587
  }
842
- for (const add of merge.addProperties) {
843
- const nm = add.name;
844
- if (typeof nm !== "string" || !nm) {
845
- throw new Error("--add-property / --update-property JSON must include a non-empty string \"name\" field.");
588
+ catch (error) {
589
+ if (error instanceof Error && error.message === "help") {
590
+ console.log(KN_GET_HELP);
591
+ return 0;
846
592
  }
847
- const idx = list.findIndex((p) => p?.name === nm);
848
- if (idx >= 0)
849
- list[idx] = add;
850
- else
851
- list.push(add);
852
- }
853
- target.data_properties = list;
854
- return target;
855
- }
856
- /** Parse object-type update: raw JSON body OR merge flags (GET-merge-PUT). */
857
- function parseObjectTypeUpdateArgs(args) {
858
- let name;
859
- let displayKey;
860
- let businessDomain = "";
861
- let pretty = true;
862
- let branch = "main";
863
- let comment;
864
- let icon;
865
- let color;
866
- let tagsJson;
867
- const addProperties = [];
868
- const removeProperties = [];
869
- const positional = [];
870
- for (let i = 0; i < args.length; i += 1) {
871
- const arg = args[i];
872
- if (arg === "--help" || arg === "-h")
873
- throw new Error("help");
874
- if (arg === "--name" && args[i + 1]) {
875
- name = args[++i];
876
- continue;
877
- }
878
- if (arg === "--display-key" && args[i + 1]) {
879
- displayKey = args[++i];
880
- continue;
881
- }
882
- if ((arg === "--add-property" || arg === "--update-property") && args[i + 1]) {
883
- const raw = args[++i];
884
- addProperties.push(parseJsonObject(raw, `--add-property / --update-property must be valid JSON object: ${raw}`));
885
- continue;
886
- }
887
- if (arg === "--remove-property" && args[i + 1]) {
888
- removeProperties.push(args[++i]);
889
- continue;
890
- }
891
- if (arg === "--tags" && args[i + 1]) {
892
- tagsJson = args[++i];
893
- continue;
894
- }
895
- if (arg === "--comment" && args[i + 1]) {
896
- comment = args[++i];
897
- continue;
898
- }
899
- if (arg === "--icon" && args[i + 1]) {
900
- icon = args[++i];
901
- continue;
902
- }
903
- if (arg === "--color" && args[i + 1]) {
904
- color = args[++i];
905
- continue;
906
- }
907
- if (arg === "--branch" && args[i + 1]) {
908
- branch = args[++i];
909
- continue;
910
- }
911
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
912
- businessDomain = args[++i];
913
- continue;
914
- }
915
- if (arg === "--pretty") {
916
- pretty = true;
917
- continue;
918
- }
919
- if (!arg.startsWith("-"))
920
- positional.push(arg);
921
- }
922
- const [knId, otId, maybeBody] = positional;
923
- if (!knId || !otId) {
924
- throw new Error("Usage: kweaver bkn object-type update <kn-id> <ot-id> [ '<full-json-body>' ] [--name ...] [--add-property|--update-property '<json>' ...] [--remove-property <name> ...]");
925
- }
926
- const hasMergeFlags = name !== undefined ||
927
- displayKey !== undefined ||
928
- addProperties.length > 0 ||
929
- removeProperties.length > 0 ||
930
- tagsJson !== undefined ||
931
- comment !== undefined ||
932
- icon !== undefined ||
933
- color !== undefined;
934
- if (maybeBody !== undefined && maybeBody.trim().startsWith("{")) {
935
- if (hasMergeFlags) {
936
- throw new Error("Do not combine a raw JSON body with --name/--add-property/--update-property/--remove-property and other merge flags.");
937
- }
938
- if (!businessDomain)
939
- businessDomain = resolveBusinessDomain();
940
- return {
941
- mode: "body",
942
- knId,
943
- otId,
944
- body: maybeBody.trim(),
945
- businessDomain,
946
- pretty,
947
- };
948
- }
949
- if (maybeBody !== undefined) {
950
- throw new Error(`Unexpected third argument "${maybeBody}". For raw PUT body, pass a single JSON object starting with "{".`);
951
- }
952
- let tags;
953
- if (tagsJson !== undefined) {
954
- try {
955
- const t = JSON.parse(tagsJson);
956
- if (!Array.isArray(t) || !t.every((x) => typeof x === "string")) {
957
- throw new Error("invalid");
958
- }
959
- tags = t;
960
- }
961
- catch {
962
- throw new Error(`--tags must be a JSON array of strings, e.g. '["足球","球员"]'`);
963
- }
964
- }
965
- const merge = {
966
- addProperties,
967
- removeProperties,
968
- ...(name !== undefined ? { name } : {}),
969
- ...(displayKey !== undefined ? { displayKey } : {}),
970
- ...(tags !== undefined ? { tags } : {}),
971
- ...(comment !== undefined ? { comment } : {}),
972
- ...(icon !== undefined ? { icon } : {}),
973
- ...(color !== undefined ? { color } : {}),
974
- };
975
- if (merge.name === undefined &&
976
- merge.displayKey === undefined &&
977
- merge.addProperties.length === 0 &&
978
- merge.removeProperties.length === 0 &&
979
- merge.tags === undefined &&
980
- merge.comment === undefined &&
981
- merge.icon === undefined &&
982
- merge.color === undefined) {
983
- throw new Error("No update fields. Use --name, --display-key, --add-property (new), --update-property (same as add; replaces by name), --remove-property, --tags, --comment, --icon, --color, or pass a full JSON object as the third argument.");
984
- }
985
- if (!businessDomain)
986
- businessDomain = resolveBusinessDomain();
987
- return { mode: "merge", knId, otId, merge, businessDomain, pretty, branch };
988
- }
989
- /** Parse object-type delete args: <kn-id> <ot-ids> [-y] */
990
- function parseObjectTypeDeleteArgs(args) {
991
- let businessDomain = "";
992
- let yes = false;
993
- const positional = [];
994
- for (let i = 0; i < args.length; i += 1) {
995
- const arg = args[i];
996
- if (arg === "--help" || arg === "-h")
997
- throw new Error("help");
998
- if (arg === "--yes" || arg === "-y") {
999
- yes = true;
1000
- continue;
1001
- }
1002
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
1003
- businessDomain = args[++i];
1004
- continue;
1005
- }
1006
- if (!arg.startsWith("-"))
1007
- positional.push(arg);
1008
- }
1009
- const [knId, otIds] = positional;
1010
- if (!knId || !otIds) {
1011
- throw new Error("Usage: kweaver bkn object-type delete <kn-id> <ot-ids> [-y]");
1012
- }
1013
- if (!businessDomain)
1014
- businessDomain = resolveBusinessDomain();
1015
- return { knId, otIds, businessDomain, yes };
1016
- }
1017
- /** Parse common flags for ontology-query subcommands; returns { filteredArgs, pretty, businessDomain } */
1018
- function parseOntologyQueryFlags(args) {
1019
- let pretty = true;
1020
- let businessDomain = "";
1021
- const filteredArgs = [];
1022
- for (let i = 0; i < args.length; i += 1) {
1023
- const arg = args[i];
1024
- if (arg === "--help" || arg === "-h") {
1025
- throw new Error("help");
1026
- }
1027
- if (arg === "--pretty") {
1028
- pretty = true;
1029
- continue;
1030
- }
1031
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
1032
- businessDomain = args[i + 1];
1033
- i += 1;
1034
- continue;
1035
- }
1036
- filteredArgs.push(arg);
1037
- }
1038
- if (!businessDomain)
1039
- businessDomain = resolveBusinessDomain();
1040
- return { filteredArgs, pretty, businessDomain };
1041
- }
1042
- export function parseKnActionTypeExecuteArgs(args) {
1043
- let pretty = true;
1044
- let businessDomain = "";
1045
- let wait = true;
1046
- let timeout = 300;
1047
- const positional = [];
1048
- for (let i = 0; i < args.length; i += 1) {
1049
- const arg = args[i];
1050
- if (arg === "--help" || arg === "-h") {
1051
- throw new Error("help");
1052
- }
1053
- if (arg === "--pretty") {
1054
- pretty = true;
1055
- continue;
1056
- }
1057
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
1058
- businessDomain = args[i + 1];
1059
- i += 1;
1060
- continue;
1061
- }
1062
- if (arg === "--wait") {
1063
- wait = true;
1064
- continue;
1065
- }
1066
- if (arg === "--no-wait") {
1067
- wait = false;
1068
- continue;
1069
- }
1070
- if (arg === "--timeout" && args[i + 1]) {
1071
- timeout = parseInt(args[i + 1], 10);
1072
- if (Number.isNaN(timeout) || timeout < 1)
1073
- timeout = 300;
1074
- i += 1;
1075
- continue;
1076
- }
1077
- positional.push(arg);
1078
- }
1079
- const [knId, atId, body] = positional;
1080
- if (!knId || !atId || !body) {
1081
- throw new Error("Missing kn-id, at-id, or body. Usage: kweaver bkn action-type execute <kn-id> <at-id> '<json>' [options]");
1082
- }
1083
- if (!businessDomain)
1084
- businessDomain = resolveBusinessDomain();
1085
- return {
1086
- knId,
1087
- atId,
1088
- body,
1089
- pretty,
1090
- businessDomain,
1091
- wait,
1092
- timeout,
1093
- };
1094
- }
1095
- const DISPLAY_HINTS = ["name", "title", "label", "display_name", "description"];
1096
- /** Detect primary key: first column (left-to-right) with all unique values in the sample. */
1097
- function detectPrimaryKey(table, rows) {
1098
- if (rows && rows.length > 0) {
1099
- for (const col of table.columns) {
1100
- const values = rows.map((r) => r[col.name]);
1101
- const unique = new Set(values);
1102
- if (unique.size === rows.length)
1103
- return col.name;
1104
- }
1105
- }
1106
- // Fallback: first column
1107
- return table.columns[0]?.name ?? "id";
1108
- }
1109
- function detectDisplayKey(table, primaryKey) {
1110
- for (const col of table.columns) {
1111
- if (DISPLAY_HINTS.some((h) => col.name.toLowerCase().includes(h))) {
1112
- return col.name;
1113
- }
1114
- }
1115
- return primaryKey;
1116
- }
1117
- function confirmYes(prompt) {
1118
- return new Promise((resolve) => {
1119
- const rl = createInterface({ input: process.stdin, output: process.stdout });
1120
- rl.question(`${prompt} [y/N] `, (answer) => {
1121
- rl.close();
1122
- const trimmed = answer.trim().toLowerCase();
1123
- resolve(trimmed === "y" || trimmed === "yes");
1124
- });
1125
- });
1126
- }
1127
- async function runKnObjectTypeCommand(args) {
1128
- const [action, ...rest] = args;
1129
- if (!action || action === "--help" || action === "-h") {
1130
- console.log(`kweaver bkn object-type list <kn-id> [--pretty] [-bd value]
1131
- kweaver bkn object-type get <kn-id> <ot-id> [--pretty] [-bd value]
1132
- kweaver bkn object-type create <kn-id> --name X --dataview-id Y --primary-key Z --display-key W [--property '<json>' ...]
1133
- kweaver bkn object-type update <kn-id> <ot-id> [--name X] [--display-key Y] [--add-property|--update-property '<json>' ...] [--remove-property N ...] [--tags '["a","b"]'] [--comment S] [--icon I] [--color C] [--branch main]
1134
- kweaver bkn object-type update <kn-id> <ot-id> '<full-json-body>'
1135
- kweaver bkn object-type delete <kn-id> <ot-ids> [-y]
1136
- kweaver bkn object-type query <kn-id> <ot-id> ['<json>'] [--limit <n>] [--search-after '<json-array>'] [--pretty] [-bd value]
1137
- kweaver bkn object-type properties <kn-id> <ot-id> '<json>' [--pretty] [-bd value]
1138
-
1139
- list: List object types (schema) from ontology-manager.
1140
- get: Get single object type details.
1141
- create/update/delete: Schema CRUD (create requires dataview-id). update: merge flags (--add-property / --update-property / --remove-property, etc.) GET-merge-PUT; or full JSON as third arg.
1142
- query: Query via ontology-query API. Default limit is 50 if not specified. Use --search-after for pagination.
1143
- properties: Query instance properties by primary key.
1144
-
1145
- properties JSON format: {"_instance_identities":[{"<primary-key>":"<value>"}],"properties":["prop1","prop2"]}`);
1146
- return 0;
1147
- }
1148
- try {
1149
- if (action === "get") {
1150
- const parsed = parseOntologyQueryFlags(rest);
1151
- const [knId, otId] = parsed.filteredArgs;
1152
- if (!knId || !otId) {
1153
- console.error("Usage: kweaver bkn object-type get <kn-id> <ot-id> [options]");
1154
- return 1;
1155
- }
1156
- const token = await ensureValidToken();
1157
- const body = await getObjectType({
1158
- baseUrl: token.baseUrl,
1159
- accessToken: token.accessToken,
1160
- knId,
1161
- otId,
1162
- businessDomain: parsed.businessDomain,
1163
- });
1164
- console.log(formatCallOutput(body, parsed.pretty));
1165
- return 0;
1166
- }
1167
- if (action === "create") {
1168
- const opts = parseObjectTypeCreateArgs(rest);
1169
- const token = await ensureValidToken();
1170
- const body = await createObjectTypes({
1171
- baseUrl: token.baseUrl,
1172
- accessToken: token.accessToken,
1173
- knId: opts.knId,
1174
- body: opts.body,
1175
- businessDomain: opts.businessDomain,
1176
- branch: opts.branch,
1177
- });
1178
- console.log(formatCallOutput(body, opts.pretty));
1179
- return 0;
1180
- }
1181
- if (action === "update") {
1182
- const opts = parseObjectTypeUpdateArgs(rest);
1183
- const token = await ensureValidToken();
1184
- let putBody;
1185
- if (opts.mode === "body") {
1186
- putBody = opts.body;
1187
- }
1188
- else {
1189
- const raw = await getObjectType({
1190
- baseUrl: token.baseUrl,
1191
- accessToken: token.accessToken,
1192
- knId: opts.knId,
1193
- otId: opts.otId,
1194
- businessDomain: opts.businessDomain,
1195
- branch: opts.branch,
1196
- });
1197
- const parsed = JSON.parse(raw);
1198
- const entryUnknown = parsed.entries;
1199
- const entry = Array.isArray(entryUnknown) && entryUnknown.length > 0 && entryUnknown[0] && typeof entryUnknown[0] === "object"
1200
- ? entryUnknown[0]
1201
- : parsed;
1202
- if (!entry || typeof entry !== "object") {
1203
- throw new Error("Unexpected object-type GET response shape.");
1204
- }
1205
- const stripped = stripObjectTypeForPut(entry);
1206
- applyObjectTypeMerge(stripped, opts.merge);
1207
- putBody = JSON.stringify(stripped);
1208
- }
1209
- const body = await updateObjectType({
1210
- baseUrl: token.baseUrl,
1211
- accessToken: token.accessToken,
1212
- knId: opts.knId,
1213
- otId: opts.otId,
1214
- body: putBody,
1215
- businessDomain: opts.businessDomain,
1216
- });
1217
- console.log(formatCallOutput(body, opts.pretty));
1218
- return 0;
1219
- }
1220
- if (action === "delete") {
1221
- const opts = parseObjectTypeDeleteArgs(rest);
1222
- if (!opts.yes) {
1223
- const confirmed = await confirmYes(`Delete object type(s) ${opts.otIds}?`);
1224
- if (!confirmed) {
1225
- console.error("Aborted.");
1226
- return 1;
1227
- }
1228
- }
1229
- const token = await ensureValidToken();
1230
- await deleteObjectTypes({
1231
- baseUrl: token.baseUrl,
1232
- accessToken: token.accessToken,
1233
- knId: opts.knId,
1234
- otIds: opts.otIds,
1235
- businessDomain: opts.businessDomain,
1236
- });
1237
- console.log(`Deleted ${opts.otIds}`);
1238
- return 0;
1239
- }
1240
- if (action === "list") {
1241
- const parsed = parseOntologyQueryFlags(rest);
1242
- const [knId] = parsed.filteredArgs;
1243
- if (!knId) {
1244
- console.error("Usage: kweaver bkn object-type list <kn-id> [options]");
1245
- return 1;
1246
- }
1247
- const token = await ensureValidToken();
1248
- const body = await listObjectTypes({
1249
- baseUrl: token.baseUrl,
1250
- accessToken: token.accessToken,
1251
- knId,
1252
- businessDomain: parsed.businessDomain,
1253
- });
1254
- console.log(formatCallOutput(body, parsed.pretty));
1255
- return 0;
1256
- }
1257
- if (action === "query") {
1258
- const options = parseKnObjectTypeQueryArgs(rest);
1259
- const token = await ensureValidToken();
1260
- const result = await objectTypeQuery({
1261
- baseUrl: token.baseUrl,
1262
- accessToken: token.accessToken,
1263
- knId: options.knId,
1264
- otId: options.otId,
1265
- body: options.body,
1266
- businessDomain: options.businessDomain,
1267
- });
1268
- console.log(formatCallOutput(truncateQueryResult(result), options.pretty));
1269
- return 0;
1270
- }
1271
- if (action === "properties") {
1272
- const parsed = parseOntologyQueryFlags(rest);
1273
- const [knId, otId, body] = parsed.filteredArgs;
1274
- if (!knId || !otId || !body) {
1275
- console.error(`Usage: kweaver bkn object-type properties <kn-id> <ot-id> '<json>' [options]
1276
- JSON: {"_instance_identities":[{"<primary-key>":"<value>"}],"properties":["prop1","prop2"]}`);
1277
- return 1;
1278
- }
1279
- const token = await ensureValidToken();
1280
- const result = await objectTypeProperties({
1281
- baseUrl: token.baseUrl,
1282
- accessToken: token.accessToken,
1283
- knId,
1284
- otId,
1285
- body,
1286
- businessDomain: parsed.businessDomain,
1287
- });
1288
- console.log(formatCallOutput(result, parsed.pretty));
1289
- return 0;
1290
- }
1291
- console.error(`Unknown object-type action: ${action}. Use list, get, create, update, delete, query, or properties.`);
1292
- return 1;
1293
- }
1294
- catch (error) {
1295
- if (error instanceof Error && error.message === "help") {
1296
- console.log(`kweaver bkn object-type create <kn-id> --name X --dataview-id Y --primary-key Z --display-key W [--property '<json>' ...]
1297
- kweaver bkn object-type update <kn-id> <ot-id> [--name X] [--display-key Y] [--add-property|--update-property '<json>' ...] [--remove-property N ...] [--tags '["a"]'] [--comment S] [--icon I] [--color C] [--branch main]
1298
- kweaver bkn object-type update <kn-id> <ot-id> '<full-json-body>'
1299
- kweaver bkn object-type delete <kn-id> <ot-ids> [-y]`);
1300
- return 0;
1301
- }
1302
- console.error(formatHttpError(error));
1303
- return 1;
1304
- }
1305
- }
1306
- /** Parse relation-type create args: --name --source --target [--mapping src:tgt ...] */
1307
- export function parseRelationTypeCreateArgs(args) {
1308
- let name = "";
1309
- let source = "";
1310
- let target = "";
1311
- let businessDomain = "";
1312
- let branch = "main";
1313
- let pretty = true;
1314
- const mappings = [];
1315
- const positional = [];
1316
- for (let i = 0; i < args.length; i += 1) {
1317
- const arg = args[i];
1318
- if (arg === "--help" || arg === "-h")
1319
- throw new Error("help");
1320
- if (arg === "--name" && args[i + 1]) {
1321
- name = args[++i];
1322
- continue;
1323
- }
1324
- if (arg === "--source" && args[i + 1]) {
1325
- source = args[++i];
1326
- continue;
1327
- }
1328
- if (arg === "--target" && args[i + 1]) {
1329
- target = args[++i];
1330
- continue;
1331
- }
1332
- if (arg === "--mapping" && args[i + 1]) {
1333
- const m = args[++i];
1334
- if (!m.includes(":")) {
1335
- throw new Error(`Invalid mapping format '${m}'. Expected source_prop:target_prop.`);
1336
- }
1337
- const [s, t] = m.split(":", 2);
1338
- mappings.push([s, t]);
1339
- continue;
1340
- }
1341
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
1342
- businessDomain = args[++i];
1343
- continue;
1344
- }
1345
- if (arg === "--branch" && args[i + 1]) {
1346
- branch = args[++i];
1347
- continue;
1348
- }
1349
- if (arg === "--pretty") {
1350
- pretty = true;
1351
- continue;
1352
- }
1353
- if (!arg.startsWith("-"))
1354
- positional.push(arg);
1355
- }
1356
- const knId = positional[0];
1357
- if (!knId || !name || !source || !target) {
1358
- throw new Error("Usage: kweaver bkn relation-type create <kn-id> --name X --source <ot-id> --target <ot-id> [--mapping src:tgt ...]");
1359
- }
1360
- const entry = {
1361
- branch,
1362
- name,
1363
- source_object_type_id: source,
1364
- target_object_type_id: target,
1365
- type: "direct",
1366
- mapping_rules: mappings.map(([s, t]) => ({
1367
- source_property: { name: s },
1368
- target_property: { name: t },
1369
- })),
1370
- };
1371
- const body = JSON.stringify({ entries: [entry] });
1372
- if (!businessDomain)
1373
- businessDomain = resolveBusinessDomain();
1374
- return { knId, body, businessDomain, branch, pretty };
1375
- }
1376
- /** Parse relation-type update args: [--name X] */
1377
- function parseRelationTypeUpdateArgs(args) {
1378
- let name;
1379
- let businessDomain = "";
1380
- let pretty = true;
1381
- const positional = [];
1382
- for (let i = 0; i < args.length; i += 1) {
1383
- const arg = args[i];
1384
- if (arg === "--help" || arg === "-h")
1385
- throw new Error("help");
1386
- if (arg === "--name" && args[i + 1]) {
1387
- name = args[++i];
1388
- continue;
1389
- }
1390
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
1391
- businessDomain = args[++i];
1392
- continue;
1393
- }
1394
- if (arg === "--pretty") {
1395
- pretty = true;
1396
- continue;
1397
- }
1398
- if (!arg.startsWith("-"))
1399
- positional.push(arg);
1400
- }
1401
- const [knId, rtId] = positional;
1402
- if (!knId || !rtId) {
1403
- throw new Error("Usage: kweaver bkn relation-type update <kn-id> <rt-id> [--name X]");
1404
- }
1405
- if (name === undefined) {
1406
- throw new Error("No update fields. Use --name.");
1407
- }
1408
- if (!businessDomain)
1409
- businessDomain = resolveBusinessDomain();
1410
- return { knId, rtId, body: JSON.stringify({ name }), businessDomain, pretty };
1411
- }
1412
- /** Parse relation-type delete args: <kn-id> <rt-ids> [-y] */
1413
- function parseRelationTypeDeleteArgs(args) {
1414
- let businessDomain = "";
1415
- let yes = false;
1416
- const positional = [];
1417
- for (let i = 0; i < args.length; i += 1) {
1418
- const arg = args[i];
1419
- if (arg === "--help" || arg === "-h")
1420
- throw new Error("help");
1421
- if (arg === "--yes" || arg === "-y") {
1422
- yes = true;
1423
- continue;
1424
- }
1425
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
1426
- businessDomain = args[++i];
1427
- continue;
1428
- }
1429
- if (!arg.startsWith("-"))
1430
- positional.push(arg);
1431
- }
1432
- const [knId, rtIds] = positional;
1433
- if (!knId || !rtIds) {
1434
- throw new Error("Usage: kweaver bkn relation-type delete <kn-id> <rt-ids> [-y]");
1435
- }
1436
- if (!businessDomain)
1437
- businessDomain = resolveBusinessDomain();
1438
- return { knId, rtIds, businessDomain, yes };
1439
- }
1440
- async function runKnRelationTypeCommand(args) {
1441
- const [action, ...rest] = args;
1442
- if (!action || action === "--help" || action === "-h") {
1443
- console.log(`kweaver bkn relation-type list <kn-id> [--pretty] [-bd value]
1444
- kweaver bkn relation-type get <kn-id> <rt-id> [--pretty] [-bd value]
1445
- kweaver bkn relation-type create <kn-id> --name X --source <ot-id> --target <ot-id> [--mapping src:tgt ...]
1446
- kweaver bkn relation-type update <kn-id> <rt-id> [--name X]
1447
- kweaver bkn relation-type delete <kn-id> <rt-ids> [-y]
1448
-
1449
- list: List relation types (schema) from ontology-manager.
1450
- get: Get single relation type details.
1451
- create/update/delete: Schema CRUD.`);
1452
- return 0;
1453
- }
1454
- try {
1455
- if (action === "get") {
1456
- const parsed = parseOntologyQueryFlags(rest);
1457
- const [knId, rtId] = parsed.filteredArgs;
1458
- if (!knId || !rtId) {
1459
- console.error("Usage: kweaver bkn relation-type get <kn-id> <rt-id> [options]");
1460
- return 1;
1461
- }
1462
- const token = await ensureValidToken();
1463
- const body = await getRelationType({
1464
- baseUrl: token.baseUrl,
1465
- accessToken: token.accessToken,
1466
- knId,
1467
- rtId,
1468
- businessDomain: parsed.businessDomain,
1469
- });
1470
- console.log(formatCallOutput(body, parsed.pretty));
1471
- return 0;
1472
- }
1473
- if (action === "create") {
1474
- const opts = parseRelationTypeCreateArgs(rest);
1475
- const token = await ensureValidToken();
1476
- const body = await createRelationTypes({
1477
- baseUrl: token.baseUrl,
1478
- accessToken: token.accessToken,
1479
- knId: opts.knId,
1480
- body: opts.body,
1481
- businessDomain: opts.businessDomain,
1482
- branch: opts.branch,
1483
- });
1484
- console.log(formatCallOutput(body, opts.pretty));
1485
- return 0;
1486
- }
1487
- if (action === "update") {
1488
- const opts = parseRelationTypeUpdateArgs(rest);
1489
- const token = await ensureValidToken();
1490
- const body = await updateRelationType({
1491
- baseUrl: token.baseUrl,
1492
- accessToken: token.accessToken,
1493
- knId: opts.knId,
1494
- rtId: opts.rtId,
1495
- body: opts.body,
1496
- businessDomain: opts.businessDomain,
1497
- });
1498
- console.log(formatCallOutput(body, opts.pretty));
1499
- return 0;
1500
- }
1501
- if (action === "delete") {
1502
- const opts = parseRelationTypeDeleteArgs(rest);
1503
- if (!opts.yes) {
1504
- const confirmed = await confirmYes(`Delete relation type(s) ${opts.rtIds}?`);
1505
- if (!confirmed) {
1506
- console.error("Aborted.");
1507
- return 1;
1508
- }
1509
- }
1510
- const token = await ensureValidToken();
1511
- await deleteRelationTypes({
1512
- baseUrl: token.baseUrl,
1513
- accessToken: token.accessToken,
1514
- knId: opts.knId,
1515
- rtIds: opts.rtIds,
1516
- businessDomain: opts.businessDomain,
1517
- });
1518
- console.log(`Deleted ${opts.rtIds}`);
1519
- return 0;
1520
- }
1521
- if (action === "list") {
1522
- const parsed = parseOntologyQueryFlags(rest);
1523
- const [knId] = parsed.filteredArgs;
1524
- if (!knId) {
1525
- console.error("Usage: kweaver bkn relation-type list <kn-id> [options]");
1526
- return 1;
1527
- }
1528
- const token = await ensureValidToken();
1529
- const body = await listRelationTypes({
1530
- baseUrl: token.baseUrl,
1531
- accessToken: token.accessToken,
1532
- knId,
1533
- businessDomain: parsed.businessDomain,
1534
- });
1535
- console.log(formatCallOutput(body, parsed.pretty));
1536
- return 0;
1537
- }
1538
- console.error(`Unknown relation-type action: ${action}. Use list, get, create, update, or delete.`);
1539
- return 1;
1540
- }
1541
- catch (error) {
1542
- if (error instanceof Error && error.message === "help") {
1543
- console.log(`kweaver bkn relation-type create <kn-id> --name X --source <ot-id> --target <ot-id> [--mapping src:tgt ...]
1544
- kweaver bkn relation-type update <kn-id> <rt-id> [--name X]
1545
- kweaver bkn relation-type delete <kn-id> <rt-ids> [-y]`);
1546
- return 0;
1547
- }
1548
- console.error(formatHttpError(error));
1549
- return 1;
1550
- }
1551
- }
1552
- async function runKnSubgraphCommand(args) {
1553
- let filteredArgs;
1554
- let pretty;
1555
- let businessDomain;
1556
- try {
1557
- const parsed = parseOntologyQueryFlags(args);
1558
- filteredArgs = parsed.filteredArgs;
1559
- pretty = parsed.pretty;
1560
- businessDomain = parsed.businessDomain;
1561
- }
1562
- catch (error) {
1563
- if (error instanceof Error && error.message === "help") {
1564
- console.log(`kweaver bkn subgraph <kn-id> '<json>' [--pretty] [-bd value]
1565
-
1566
- Query subgraph via ontology-query API. JSON body format see references/json-formats.md#subgraph.`);
1567
- return 0;
1568
- }
1569
- throw error;
1570
- }
1571
- const [knId, body] = filteredArgs;
1572
- if (!knId || !body) {
1573
- console.error("Usage: kweaver bkn subgraph <kn-id> '<json>' [options]");
1574
- return 1;
1575
- }
1576
- try {
1577
- // Auto-detect query_type=relation_path when body contains source_object_type_id
1578
- let queryType;
1579
- try {
1580
- const parsedBody = JSON.parse(body);
1581
- if (parsedBody.source_object_type_id) {
1582
- queryType = "relation_path";
1583
- }
1584
- }
1585
- catch {
1586
- // Not valid JSON — let the API return the error
1587
- }
1588
- const token = await ensureValidToken();
1589
- const result = await subgraph({
1590
- baseUrl: token.baseUrl,
1591
- accessToken: token.accessToken,
1592
- knId,
1593
- body,
1594
- businessDomain,
1595
- queryType,
1596
- });
1597
- if (result.length > 100_000) {
1598
- console.error(`[warn] Response is ${(result.length / 1024).toFixed(0)}KB. Consider narrowing the subgraph query.`);
1599
- }
1600
- console.log(formatCallOutput(result, pretty));
1601
- return 0;
1602
- }
1603
- catch (error) {
1604
- console.error(formatHttpError(error));
1605
- return 1;
1606
- }
1607
- }
1608
- const TERMINAL_STATUSES = ["SUCCESS", "FAILED", "CANCELLED"];
1609
- function extractExecutionId(body) {
1610
- try {
1611
- const data = JSON.parse(body);
1612
- const id = data.execution_id ?? data.id;
1613
- return typeof id === "string" ? id : null;
1614
- }
1615
- catch {
1616
- return null;
1617
- }
1618
- }
1619
- function extractStatus(body) {
1620
- try {
1621
- const data = JSON.parse(body);
1622
- const status = data.status;
1623
- return typeof status === "string" ? status : "";
1624
- }
1625
- catch {
1626
- return "";
1627
- }
1628
- }
1629
- async function runKnActionTypeCommand(args) {
1630
- const [action, ...rest] = args;
1631
- if (!action || action === "--help" || action === "-h") {
1632
- console.log(`kweaver bkn action-type list <kn-id> [--pretty] [-bd value]
1633
- kweaver bkn action-type query <kn-id> <at-id> '<json>' [--pretty] [-bd value]
1634
- kweaver bkn action-type execute <kn-id> <at-id> '<json>' [--pretty] [-bd value] [--wait|--no-wait] [--timeout n]
1635
-
1636
- list: List action types (schema) from ontology-manager.
1637
- query/execute: Query or execute actions. execute has side effects - only use when explicitly requested.
1638
- --wait (default) Poll until execution completes
1639
- --no-wait Return immediately after starting execution
1640
- --timeout <seconds> Max wait time when --wait (default: 300)`);
1641
- return 0;
1642
- }
1643
- if (action === "list") {
1644
- try {
1645
- const parsed = parseOntologyQueryFlags(rest);
1646
- const [knId] = parsed.filteredArgs;
1647
- if (!knId) {
1648
- console.error("Usage: kweaver bkn action-type list <kn-id> [options]");
1649
- return 1;
1650
- }
1651
- const token = await ensureValidToken();
1652
- const body = await listActionTypes({
1653
- baseUrl: token.baseUrl,
1654
- accessToken: token.accessToken,
1655
- knId,
1656
- businessDomain: parsed.businessDomain,
1657
- });
1658
- console.log(formatCallOutput(body, parsed.pretty));
1659
- return 0;
1660
- }
1661
- catch (error) {
1662
- console.error(formatHttpError(error));
1663
- return 1;
1664
- }
1665
- }
1666
- if (action === "query") {
1667
- let filteredArgs;
1668
- let pretty;
1669
- let businessDomain;
1670
- try {
1671
- const parsed = parseOntologyQueryFlags(rest);
1672
- filteredArgs = parsed.filteredArgs;
1673
- pretty = parsed.pretty;
1674
- businessDomain = parsed.businessDomain;
1675
- }
1676
- catch (error) {
1677
- if (error instanceof Error && error.message === "help")
1678
- return 0;
1679
- throw error;
1680
- }
1681
- const [knId, atId, body] = filteredArgs;
1682
- if (!knId || !atId || !body) {
1683
- console.error("Usage: kweaver bkn action-type query <kn-id> <at-id> '<json>' [options]");
1684
- return 1;
1685
- }
1686
- try {
1687
- const token = await ensureValidToken();
1688
- const result = await actionTypeQuery({
1689
- baseUrl: token.baseUrl,
1690
- accessToken: token.accessToken,
1691
- knId,
1692
- atId,
1693
- body,
1694
- businessDomain,
1695
- });
1696
- console.log(formatCallOutput(result, pretty));
1697
- return 0;
1698
- }
1699
- catch (error) {
1700
- console.error(formatHttpError(error));
1701
- return 1;
1702
- }
1703
- }
1704
- if (action === "execute") {
1705
- let options;
1706
- try {
1707
- options = parseKnActionTypeExecuteArgs(rest);
1708
- }
1709
- catch (error) {
1710
- if (error instanceof Error && error.message === "help")
1711
- return 0;
1712
- console.error(formatHttpError(error));
1713
- return 1;
1714
- }
1715
- try {
1716
- const token = await ensureValidToken();
1717
- const base = {
1718
- baseUrl: token.baseUrl,
1719
- accessToken: token.accessToken,
1720
- knId: options.knId,
1721
- atId: options.atId,
1722
- body: options.body,
1723
- businessDomain: options.businessDomain,
1724
- };
1725
- const result = await actionTypeExecute(base);
1726
- if (!options.wait) {
1727
- console.log(formatCallOutput(result, options.pretty));
1728
- return 0;
1729
- }
1730
- const executionId = extractExecutionId(result);
1731
- if (!executionId) {
1732
- console.log(formatCallOutput(result, options.pretty));
1733
- return 0;
1734
- }
1735
- let lastBody = result;
1736
- try {
1737
- lastBody = await pollWithBackoff({
1738
- fn: async () => {
1739
- const status = extractStatus(lastBody);
1740
- if (TERMINAL_STATUSES.includes(status.toUpperCase())) {
1741
- return { done: true, value: lastBody };
1742
- }
1743
- lastBody = await actionExecutionGet({
1744
- baseUrl: token.baseUrl,
1745
- accessToken: token.accessToken,
1746
- knId: options.knId,
1747
- executionId,
1748
- businessDomain: options.businessDomain,
1749
- });
1750
- return { done: false, value: lastBody };
1751
- },
1752
- interval: 2000,
1753
- timeout: options.timeout * 1000,
1754
- });
1755
- }
1756
- catch {
1757
- console.error(`Action execution did not complete within ${options.timeout}s`);
1758
- console.log(formatCallOutput(lastBody, options.pretty));
1759
- return 1;
1760
- }
1761
- const finalStatus = extractStatus(lastBody);
1762
- console.log(formatCallOutput(lastBody, options.pretty));
1763
- return finalStatus.toUpperCase() === "SUCCESS" ? 0 : 1;
1764
- }
1765
- catch (error) {
1766
- console.error(formatHttpError(error));
1767
- return 1;
1768
- }
1769
- }
1770
- console.error(`Unknown action-type action: ${action}. Use list, query, or execute.`);
1771
- return 1;
1772
- }
1773
- async function runKnActionExecutionCommand(args) {
1774
- let filteredArgs;
1775
- let pretty;
1776
- let businessDomain;
1777
- try {
1778
- const parsed = parseOntologyQueryFlags(args);
1779
- filteredArgs = parsed.filteredArgs;
1780
- pretty = parsed.pretty;
1781
- businessDomain = parsed.businessDomain;
1782
- }
1783
- catch (error) {
1784
- if (error instanceof Error && error.message === "help") {
1785
- console.log(`kweaver bkn action-execution get <kn-id> <execution-id> [--pretty] [-bd value]
1786
-
1787
- Get action execution status.`);
1788
- return 0;
1789
- }
1790
- throw error;
1791
- }
1792
- const [subAction, knId, executionId] = filteredArgs;
1793
- if (subAction !== "get" || !knId || !executionId) {
1794
- console.error("Usage: kweaver bkn action-execution get <kn-id> <execution-id> [options]");
1795
- return 1;
1796
- }
1797
- try {
1798
- const token = await ensureValidToken();
1799
- const result = await actionExecutionGet({
1800
- baseUrl: token.baseUrl,
1801
- accessToken: token.accessToken,
1802
- knId,
1803
- executionId,
1804
- businessDomain,
1805
- });
1806
- console.log(formatCallOutput(result, pretty));
1807
- return 0;
1808
- }
1809
- catch (error) {
1810
- console.error(formatHttpError(error));
1811
- return 1;
1812
- }
1813
- }
1814
- async function runKnActionLogCommand(args) {
1815
- const [action, ...rest] = args;
1816
- if (!action || action === "--help" || action === "-h") {
1817
- console.log(`kweaver bkn action-log list <kn-id> [options]
1818
- kweaver bkn action-log get <kn-id> <log-id> [options]
1819
- kweaver bkn action-log cancel <kn-id> <log-id> [options]
1820
-
1821
- List/get execution logs. cancel has side effects - only use when explicitly requested.
1822
- Options for list: --limit, --need-total, --action-type-id, --status, --trigger-type, --search-after`);
1823
- return 0;
1824
- }
1825
- let pretty = true;
1826
- let businessDomain = "";
1827
- let limit = 30;
1828
- let needTotal;
1829
- let actionTypeId;
1830
- let status;
1831
- let triggerType;
1832
- let searchAfter;
1833
- const filteredArgs = [];
1834
- for (let i = 0; i < rest.length; i += 1) {
1835
- const arg = rest[i];
1836
- if (arg === "--help" || arg === "-h") {
1837
- throw new Error("help");
1838
- }
1839
- if (arg === "--pretty") {
1840
- pretty = true;
1841
- continue;
1842
- }
1843
- if ((arg === "-bd" || arg === "--biz-domain") && rest[i + 1]) {
1844
- businessDomain = rest[i + 1];
1845
- i += 1;
1846
- continue;
1847
- }
1848
- if (arg === "--limit" && rest[i + 1]) {
1849
- limit = parseInt(rest[i + 1], 10);
1850
- i += 1;
1851
- continue;
1852
- }
1853
- if (arg === "--need-total" && rest[i + 1]) {
1854
- needTotal = rest[i + 1].toLowerCase() === "true";
1855
- i += 1;
1856
- continue;
1857
- }
1858
- if (arg === "--action-type-id" && rest[i + 1]) {
1859
- actionTypeId = rest[i + 1];
1860
- i += 1;
1861
- continue;
1862
- }
1863
- if (arg === "--status" && rest[i + 1]) {
1864
- status = rest[i + 1];
1865
- i += 1;
1866
- continue;
1867
- }
1868
- if (arg === "--trigger-type" && rest[i + 1]) {
1869
- triggerType = rest[i + 1];
1870
- i += 1;
1871
- continue;
1872
- }
1873
- if (arg === "--search-after" && rest[i + 1]) {
1874
- searchAfter = rest[i + 1];
1875
- i += 1;
1876
- continue;
1877
- }
1878
- filteredArgs.push(arg);
1879
- }
1880
- if (!businessDomain)
1881
- businessDomain = resolveBusinessDomain();
1882
- try {
1883
- const token = await ensureValidToken();
1884
- const base = {
1885
- baseUrl: token.baseUrl,
1886
- accessToken: token.accessToken,
1887
- businessDomain,
1888
- };
1889
- if (action === "list") {
1890
- const [knId] = filteredArgs;
1891
- if (!knId) {
1892
- console.error("Usage: kweaver bkn action-log list <kn-id> [options]");
1893
- return 1;
1894
- }
1895
- const result = await actionLogsList({
1896
- ...base,
1897
- knId,
1898
- limit,
1899
- needTotal,
1900
- actionTypeId,
1901
- status,
1902
- triggerType,
1903
- searchAfter,
1904
- });
1905
- console.log(formatCallOutput(result, pretty));
1906
- return 0;
1907
- }
1908
- if (action === "get") {
1909
- const [knId, logId] = filteredArgs;
1910
- if (!knId || !logId) {
1911
- console.error("Usage: kweaver bkn action-log get <kn-id> <log-id> [options]");
1912
- return 1;
1913
- }
1914
- const result = await actionLogGet({ ...base, knId, logId });
1915
- console.log(formatCallOutput(result, pretty));
1916
- return 0;
1917
- }
1918
- if (action === "cancel") {
1919
- const [knId, logId] = filteredArgs;
1920
- if (!knId || !logId) {
1921
- console.error("Usage: kweaver bkn action-log cancel <kn-id> <log-id> [options]");
1922
- return 1;
1923
- }
1924
- const result = await actionLogCancel({ ...base, knId, logId });
1925
- console.log(formatCallOutput(result, pretty));
1926
- return 0;
1927
- }
1928
- console.error(`Unknown action-log action: ${action}. Use list, get, or cancel.`);
1929
- return 1;
1930
- }
1931
- catch (error) {
1932
- console.error(formatHttpError(error));
1933
- return 1;
1934
- }
1935
- }
1936
- async function runKnListCommand(args) {
1937
- let options;
1938
- try {
1939
- options = parseKnListArgs(args);
1940
- }
1941
- catch (error) {
1942
- if (error instanceof Error && error.message === "help") {
1943
- console.log(KN_LIST_HELP);
1944
- return 0;
1945
- }
1946
- console.error(formatHttpError(error));
1947
- return 1;
1948
- }
1949
- try {
1950
- const token = await ensureValidToken();
1951
- const body = await listKnowledgeNetworks({
1952
- baseUrl: token.baseUrl,
1953
- accessToken: token.accessToken,
1954
- businessDomain: options.businessDomain,
1955
- offset: options.offset,
1956
- limit: options.limit,
1957
- sort: options.sort,
1958
- direction: options.direction,
1959
- name_pattern: options.name_pattern,
1960
- tag: options.tag,
1961
- });
1962
- if (body) {
1963
- console.log(options.verbose
1964
- ? formatCallOutput(body, options.pretty)
1965
- : formatSimpleKnList(body, options.pretty, options.detail));
1966
- }
1967
- return 0;
1968
- }
1969
- catch (error) {
1970
- console.error(formatHttpError(error));
1971
- return 1;
1972
- }
1973
- }
1974
- const KN_LIST_HELP = `kweaver bkn list [options]
1975
-
1976
- List business knowledge networks from the ontology-manager API.
1977
-
1978
- Options:
1979
- --offset <n> Offset (default: 0)
1980
- --limit <n> Limit (default: 30)
1981
- --sort <key> Sort field (default: update_time)
1982
- --direction <asc|desc> Sort direction (default: desc)
1983
- --name-pattern <s> Filter by name pattern
1984
- --tag <s> Filter by tag
1985
- --detail Include the detail field in simplified output
1986
- --verbose, -v Show full JSON response
1987
- -bd, --biz-domain <value> Business domain (default: bd_public)
1988
- --pretty Pretty-print JSON output (applies to both modes)`;
1989
- const KN_GET_HELP = `kweaver bkn get <kn-id> [options]
1990
-
1991
- Get knowledge network detail.
1992
-
1993
- Options:
1994
- --stats Include statistics
1995
- --export Export mode (include sub-types)
1996
- -bd, --biz-domain <value> Business domain (default: bd_public)
1997
- --pretty Pretty-print JSON output`;
1998
- const KN_CREATE_HELP = `kweaver bkn create [options]
1999
-
2000
- Create a knowledge network.
2001
-
2002
- Options:
2003
- --name <s> Name (required unless --body-file)
2004
- --comment <s> Comment
2005
- --tags <t1,t2> Comma-separated tags
2006
- --icon <s> Icon
2007
- --color <s> Color
2008
- --branch <s> Branch (default: main)
2009
- --base-branch <s> Base branch (default: empty for main)
2010
- --body-file <path> Read full JSON body from file (cannot combine with flags above)
2011
- --import-mode <normal|ignore|overwrite> Import mode (default: normal)
2012
- --validate-dependency <true|false> Validate dependency (default: true)
2013
- -bd, --biz-domain <value> Business domain (default: bd_public)
2014
- --pretty Pretty-print JSON output`;
2015
- const KN_UPDATE_HELP = `kweaver bkn update <kn-id> [options]
2016
-
2017
- Update a knowledge network.
2018
-
2019
- Options:
2020
- --name <s> Name (required unless --body-file)
2021
- --comment <s> Comment
2022
- --tags <t1,t2> Comma-separated tags
2023
- --icon <s> Icon
2024
- --color <s> Color
2025
- --branch <s> Branch (default: main)
2026
- --base-branch <s> Base branch (default: empty for main)
2027
- --body-file <path> Read full JSON body from file (cannot combine with flags above)
2028
- -bd, --biz-domain <value> Business domain (default: bd_public)
2029
- --pretty Pretty-print JSON output`;
2030
- const KN_DELETE_HELP = `kweaver bkn delete <kn-id>
2031
-
2032
- Delete a knowledge network and its object types, relation types, action types, and concept groups.
2033
-
2034
- Options:
2035
- --yes, -y Skip confirmation prompt
2036
- -bd, --biz-domain <value> Business domain (default: bd_public)`;
2037
- async function runKnGetCommand(args) {
2038
- let options;
2039
- try {
2040
- options = parseKnGetArgs(args);
2041
- }
2042
- catch (error) {
2043
- if (error instanceof Error && error.message === "help") {
2044
- console.log(KN_GET_HELP);
2045
- return 0;
2046
- }
2047
- console.error(formatHttpError(error));
2048
- return 1;
593
+ console.error(formatHttpError(error));
594
+ return 1;
2049
595
  }
2050
596
  try {
2051
597
  const token = await ensureValidToken();
@@ -2054,303 +600,8 @@ async function runKnGetCommand(args) {
2054
600
  accessToken: token.accessToken,
2055
601
  knId: options.knId,
2056
602
  businessDomain: options.businessDomain,
2057
- mode: options.export ? "export" : undefined,
2058
- include_statistics: options.stats ? true : undefined,
2059
- });
2060
- if (body) {
2061
- console.log(formatCallOutput(body, options.pretty));
2062
- }
2063
- return 0;
2064
- }
2065
- catch (error) {
2066
- console.error(formatHttpError(error));
2067
- return 1;
2068
- }
2069
- }
2070
- async function runKnCreateCommand(args) {
2071
- let options;
2072
- try {
2073
- options = parseKnCreateArgs(args);
2074
- }
2075
- catch (error) {
2076
- if (error instanceof Error && error.message === "help") {
2077
- console.log(KN_CREATE_HELP);
2078
- return 0;
2079
- }
2080
- console.error(formatHttpError(error));
2081
- return 1;
2082
- }
2083
- try {
2084
- const token = await ensureValidToken();
2085
- const body = await createKnowledgeNetwork({
2086
- baseUrl: token.baseUrl,
2087
- accessToken: token.accessToken,
2088
- body: options.body,
2089
- businessDomain: options.businessDomain,
2090
- import_mode: options.import_mode,
2091
- validate_dependency: options.validate_dependency,
2092
- });
2093
- if (body) {
2094
- console.log(formatCallOutput(body, options.pretty));
2095
- }
2096
- return 0;
2097
- }
2098
- catch (error) {
2099
- console.error(formatHttpError(error));
2100
- return 1;
2101
- }
2102
- }
2103
- const KN_CREATE_FROM_DS_HELP = `kweaver bkn create-from-ds <ds-id> --name X [options]
2104
-
2105
- Create a knowledge network from a datasource (dataviews + object types + optional build).
2106
-
2107
- Options:
2108
- --name <s> Knowledge network name (required)
2109
- --tables <a,b> Comma-separated table names (default: all)
2110
- --build (default) Build after creation
2111
- --no-build Skip build after creation
2112
- --timeout <n> Build timeout in seconds (default: 300)
2113
- -bd, --biz-domain Business domain (default: bd_public)
2114
- --pretty Pretty-print output (default)`;
2115
- function parseKnCreateFromDsArgs(args) {
2116
- let dsId = "";
2117
- let name = "";
2118
- let tablesStr = "";
2119
- let build = true;
2120
- let timeout = 300;
2121
- let businessDomain = "";
2122
- let pretty = true;
2123
- for (let i = 0; i < args.length; i += 1) {
2124
- const arg = args[i];
2125
- if (arg === "--help" || arg === "-h")
2126
- throw new Error("help");
2127
- if (arg === "--name" && args[i + 1]) {
2128
- name = args[++i];
2129
- continue;
2130
- }
2131
- if (arg === "--tables" && args[i + 1]) {
2132
- tablesStr = args[++i];
2133
- continue;
2134
- }
2135
- if (arg === "--build") {
2136
- build = true;
2137
- continue;
2138
- }
2139
- if (arg === "--no-build") {
2140
- build = false;
2141
- continue;
2142
- }
2143
- if (arg === "--timeout" && args[i + 1]) {
2144
- timeout = parseInt(args[++i], 10);
2145
- if (Number.isNaN(timeout) || timeout < 1)
2146
- timeout = 300;
2147
- continue;
2148
- }
2149
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
2150
- businessDomain = args[++i];
2151
- continue;
2152
- }
2153
- if (arg === "--pretty") {
2154
- pretty = true;
2155
- continue;
2156
- }
2157
- if (!arg.startsWith("-") && !dsId) {
2158
- dsId = arg;
2159
- }
2160
- }
2161
- const tables = tablesStr ? tablesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
2162
- if (!dsId || !name) {
2163
- throw new Error("Usage: kweaver bkn create-from-ds <ds-id> --name X [options]");
2164
- }
2165
- if (!businessDomain)
2166
- businessDomain = resolveBusinessDomain();
2167
- return { dsId, name, tables, build, timeout, businessDomain, pretty };
2168
- }
2169
- /** Sanitize a table name into a BKN-safe ID (alphanumeric + underscore). */
2170
- function sanitizeBknId(name) {
2171
- return name.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^(\d)/, "_$1");
2172
- }
2173
- /** Generate a BKN ObjectType YAML markdown file for a table. */
2174
- function generateObjectTypeBkn(tableName, dvId, pk, dk, columns) {
2175
- const safeId = sanitizeBknId(tableName);
2176
- const header = `## ObjectType: ${safeId}\n\n**${tableName}**\n`;
2177
- const dsTable = `### Data Source\n\n| Type | ID | Name |\n|------|-----|------|\n| data_view | ${dvId} | ${tableName} |\n`;
2178
- const dpHeader = `### Data Properties\n\n| Property | Display Name | Type | Primary Key | Display Key |\n|----------|-------------|------|-------------|-------------|\n`;
2179
- const dpRows = columns.map((c) => {
2180
- const isPk = c.name === pk ? "yes" : "no";
2181
- const isDk = c.name === dk ? "yes" : "no";
2182
- return `| ${c.name} | ${c.name} | string | ${isPk} | ${isDk} |`;
2183
- }).join("\n");
2184
- const frontmatter = `---\ntype: object_type\nid: ${safeId}\nname: ${tableName}\n---\n\n`;
2185
- return `${frontmatter}${header}\n${dsTable}\n${dpHeader}${dpRows}\n`;
2186
- }
2187
- async function runKnCreateFromDsCommand(args, sampleRows) {
2188
- let options;
2189
- try {
2190
- options = parseKnCreateFromDsArgs(args);
2191
- }
2192
- catch (error) {
2193
- if (error instanceof Error && error.message === "help") {
2194
- console.log(KN_CREATE_FROM_DS_HELP);
2195
- return 0;
2196
- }
2197
- console.error(formatHttpError(error));
2198
- return 1;
2199
- }
2200
- try {
2201
- const token = await ensureValidToken();
2202
- const base = {
2203
- baseUrl: token.baseUrl,
2204
- accessToken: token.accessToken,
2205
- businessDomain: options.businessDomain,
2206
- };
2207
- const tablesBody = await listTablesWithColumns({ ...base, id: options.dsId });
2208
- const allTables = JSON.parse(tablesBody);
2209
- const targetTables = options.tables.length > 0
2210
- ? allTables.filter((t) => options.tables.includes(t.name))
2211
- : allTables;
2212
- if (targetTables.length === 0) {
2213
- console.error("No tables available");
2214
- return 1;
2215
- }
2216
- // Phase 1: Create DataViews for each table
2217
- console.error(`Creating data views for ${targetTables.length} table(s) ...`);
2218
- const viewMap = {};
2219
- for (const t of targetTables) {
2220
- const found = await findDataView({
2221
- ...base,
2222
- name: t.name,
2223
- datasourceId: options.dsId,
2224
- exact: true,
2225
- wait: true,
2226
- });
2227
- const dvId = found[0]?.id ??
2228
- (await createDataView({
2229
- ...base,
2230
- name: t.name,
2231
- datasourceId: options.dsId,
2232
- table: t.name,
2233
- fields: t.columns.map((c) => ({ name: c.name, type: c.type })),
2234
- }));
2235
- viewMap[t.name] = dvId;
2236
- }
2237
- // Phase 2: Create the KN record
2238
- const knBody = JSON.stringify({
2239
- name: options.name,
2240
- branch: "main",
2241
- base_branch: "",
2242
- });
2243
- const knResponse = await createKnowledgeNetwork({
2244
- ...base,
2245
- body: knBody,
2246
- });
2247
- const knParsed = JSON.parse(knResponse);
2248
- const knItem = Array.isArray(knParsed) ? knParsed[0] : knParsed;
2249
- const knId = String(knItem?.id ?? "");
2250
- console.error(`Knowledge network created: ${knId}`);
2251
- // Phase 3: Create object types via REST API
2252
- console.error(`Creating ${targetTables.length} object type(s) ...`);
2253
- const otResults = [];
2254
- for (const t of targetTables) {
2255
- const pk = detectPrimaryKey(t, sampleRows?.[t.name]);
2256
- const dk = detectDisplayKey(t, pk);
2257
- const uniqueProps = [pk, dk].filter((x, i, a) => a.indexOf(x) === i);
2258
- const entry = {
2259
- branch: "main",
2260
- name: t.name,
2261
- data_source: { type: "data_view", id: viewMap[t.name] },
2262
- primary_keys: [pk],
2263
- display_key: dk,
2264
- data_properties: t.columns.map((c) => ({
2265
- name: c.name,
2266
- display_name: c.name,
2267
- type: "string",
2268
- mapped_field: { name: c.name, type: c.type || "varchar" },
2269
- })),
2270
- };
2271
- const otBody = JSON.stringify({ entries: [entry] });
2272
- const otResponse = await createObjectTypes({
2273
- ...base,
2274
- knId,
2275
- body: otBody,
2276
- });
2277
- const otParsed = JSON.parse(otResponse);
2278
- const otItem = otParsed.entries?.[0];
2279
- otResults.push({
2280
- name: t.name,
2281
- id: otItem?.id ?? "",
2282
- field_count: t.columns.length,
2283
- });
2284
- console.error(` Created: ${t.name} (${t.columns.length} fields, pk=${pk}, dk=${dk})`);
2285
- }
2286
- if (otResults.length === 0) {
2287
- const errorOutput = {
2288
- kn_id: knId,
2289
- kn_name: options.name,
2290
- error: "No object types were created",
2291
- };
2292
- console.log(JSON.stringify(errorOutput, null, options.pretty ? 2 : 0));
2293
- return 1;
2294
- }
2295
- let statusStr = "skipped";
2296
- if (options.build) {
2297
- console.error("Building ...");
2298
- await buildKnowledgeNetwork({ ...base, knId });
2299
- const TERMINAL = ["completed", "failed", "success"];
2300
- try {
2301
- statusStr = await pollWithBackoff({
2302
- fn: async () => {
2303
- const statusBody = await getBuildStatus({ ...base, knId });
2304
- const statusParsed = JSON.parse(statusBody);
2305
- const jobs = Array.isArray(statusParsed) ? statusParsed : (statusParsed.entries ?? []);
2306
- const state = (jobs[0]?.state ?? "running").toLowerCase();
2307
- if (TERMINAL.includes(state))
2308
- return { done: true, value: state };
2309
- return { done: false, value: "running" };
2310
- },
2311
- interval: 2000,
2312
- timeout: options.timeout * 1000,
2313
- });
2314
- }
2315
- catch {
2316
- // timeout — statusStr remains "skipped"
2317
- }
2318
- }
2319
- const output = {
2320
- kn_id: knId,
2321
- kn_name: options.name,
2322
- object_types: otResults,
2323
- status: statusStr,
2324
- };
2325
- console.log(JSON.stringify(output, null, options.pretty ? 2 : 0));
2326
- return 0;
2327
- }
2328
- catch (error) {
2329
- console.error(formatHttpError(error));
2330
- return 1;
2331
- }
2332
- }
2333
- async function runKnUpdateCommand(args) {
2334
- let options;
2335
- try {
2336
- options = parseKnUpdateArgs(args);
2337
- }
2338
- catch (error) {
2339
- if (error instanceof Error && error.message === "help") {
2340
- console.log(KN_UPDATE_HELP);
2341
- return 0;
2342
- }
2343
- console.error(formatHttpError(error));
2344
- return 1;
2345
- }
2346
- try {
2347
- const token = await ensureValidToken();
2348
- const body = await updateKnowledgeNetwork({
2349
- baseUrl: token.baseUrl,
2350
- accessToken: token.accessToken,
2351
- knId: options.knId,
2352
- body: options.body,
2353
- businessDomain: options.businessDomain,
603
+ mode: options.export ? "export" : undefined,
604
+ include_statistics: options.stats ? true : undefined,
2354
605
  });
2355
606
  if (body) {
2356
607
  console.log(formatCallOutput(body, options.pretty));
@@ -2362,44 +613,32 @@ async function runKnUpdateCommand(args) {
2362
613
  return 1;
2363
614
  }
2364
615
  }
2365
- function confirmDelete(knId) {
2366
- return new Promise((resolve) => {
2367
- const rl = createInterface({ input: process.stdin, output: process.stdout });
2368
- rl.question(`Delete knowledge network ${knId}? [y/N] `, (answer) => {
2369
- rl.close();
2370
- const trimmed = answer.trim().toLowerCase();
2371
- resolve(trimmed === "y" || trimmed === "yes");
2372
- });
2373
- });
2374
- }
2375
- async function runKnDeleteCommand(args) {
616
+ async function runKnCreateCommand(args) {
2376
617
  let options;
2377
618
  try {
2378
- options = parseKnDeleteArgs(args);
619
+ options = parseKnCreateArgs(args);
2379
620
  }
2380
621
  catch (error) {
2381
622
  if (error instanceof Error && error.message === "help") {
2382
- console.log(KN_DELETE_HELP);
623
+ console.log(KN_CREATE_HELP);
2383
624
  return 0;
2384
625
  }
2385
626
  console.error(formatHttpError(error));
2386
627
  return 1;
2387
628
  }
2388
- if (!options.yes) {
2389
- const confirmed = await confirmDelete(options.knId);
2390
- if (!confirmed) {
2391
- console.error("Aborted.");
2392
- return 1;
2393
- }
2394
- }
2395
629
  try {
2396
630
  const token = await ensureValidToken();
2397
- await deleteKnowledgeNetwork({
631
+ const body = await createKnowledgeNetwork({
2398
632
  baseUrl: token.baseUrl,
2399
633
  accessToken: token.accessToken,
2400
- knId: options.knId,
634
+ body: options.body,
2401
635
  businessDomain: options.businessDomain,
636
+ import_mode: options.import_mode,
637
+ validate_dependency: options.validate_dependency,
2402
638
  });
639
+ if (body) {
640
+ console.log(formatCallOutput(body, options.pretty));
641
+ }
2403
642
  return 0;
2404
643
  }
2405
644
  catch (error) {
@@ -2407,423 +646,76 @@ async function runKnDeleteCommand(args) {
2407
646
  return 1;
2408
647
  }
2409
648
  }
2410
- const KN_BUILD_HELP = `kweaver bkn build <kn-id> [options]
2411
-
2412
- Trigger a full build for a knowledge network.
2413
-
2414
- Options:
2415
- --wait (default) Poll until build completes
2416
- --no-wait Return immediately after triggering
2417
- --timeout <seconds> Max wait time when --wait (default: 300)
2418
- -bd, --biz-domain Business domain (default: bd_public)`;
2419
- export function parseKnBuildArgs(args) {
2420
- let knId = "";
2421
- let wait = true;
2422
- let timeout = 300;
2423
- let businessDomain = "";
2424
- for (let i = 0; i < args.length; i += 1) {
2425
- const arg = args[i];
2426
- if (arg === "--help" || arg === "-h")
2427
- throw new Error("help");
2428
- if (arg === "--wait") {
2429
- wait = true;
2430
- continue;
2431
- }
2432
- if (arg === "--no-wait") {
2433
- wait = false;
2434
- continue;
2435
- }
2436
- if (arg === "--timeout" && args[i + 1]) {
2437
- timeout = parseInt(args[i + 1], 10);
2438
- if (Number.isNaN(timeout) || timeout < 1)
2439
- timeout = 300;
2440
- i += 1;
2441
- continue;
2442
- }
2443
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
2444
- businessDomain = args[i + 1];
2445
- i += 1;
2446
- continue;
2447
- }
2448
- if (!arg.startsWith("-") && !knId) {
2449
- knId = arg;
2450
- }
2451
- }
2452
- if (!knId) {
2453
- throw new Error("Missing kn-id. Usage: kweaver bkn build <kn-id> [options]");
2454
- }
2455
- if (!businessDomain)
2456
- businessDomain = resolveBusinessDomain();
2457
- return { knId, wait, timeout, businessDomain };
2458
- }
2459
- async function runKnBuildCommand(args) {
649
+ async function runKnUpdateCommand(args) {
2460
650
  let options;
2461
651
  try {
2462
- options = parseKnBuildArgs(args);
652
+ options = parseKnUpdateArgs(args);
2463
653
  }
2464
654
  catch (error) {
2465
655
  if (error instanceof Error && error.message === "help") {
2466
- console.log(KN_BUILD_HELP);
656
+ console.log(KN_UPDATE_HELP);
2467
657
  return 0;
2468
658
  }
2469
659
  console.error(formatHttpError(error));
2470
660
  return 1;
2471
661
  }
2472
- const TERMINAL_STATES = ["completed", "failed", "success"];
2473
662
  try {
2474
663
  const token = await ensureValidToken();
2475
- await buildKnowledgeNetwork({
664
+ const body = await updateKnowledgeNetwork({
2476
665
  baseUrl: token.baseUrl,
2477
666
  accessToken: token.accessToken,
2478
667
  knId: options.knId,
668
+ body: options.body,
2479
669
  businessDomain: options.businessDomain,
2480
670
  });
2481
- console.error(`Build started for ${options.knId}`);
2482
- if (!options.wait) {
2483
- console.error("Build triggered (not waiting).");
2484
- return 0;
2485
- }
2486
- console.error("Waiting for build to complete ...");
2487
- try {
2488
- const { state, detail } = await pollWithBackoff({
2489
- fn: async () => {
2490
- const body = await getBuildStatus({
2491
- baseUrl: token.baseUrl,
2492
- accessToken: token.accessToken,
2493
- knId: options.knId,
2494
- businessDomain: options.businessDomain,
2495
- });
2496
- const parsed = JSON.parse(body);
2497
- const jobs = Array.isArray(parsed) ? parsed : (parsed.entries ?? parsed.data ?? []);
2498
- const job = jobs[0];
2499
- const st = (job?.state ?? "running").toLowerCase();
2500
- const dt = job?.state_detail;
2501
- if (TERMINAL_STATES.includes(st))
2502
- return { done: true, value: { state: st, detail: dt } };
2503
- return { done: false, value: { state: st } };
2504
- },
2505
- interval: 2000,
2506
- timeout: options.timeout * 1000,
2507
- });
2508
- console.log(state);
2509
- if (detail) {
2510
- console.log(`Detail: ${detail}`);
2511
- }
2512
- return state === "failed" ? 1 : 0;
2513
- }
2514
- catch {
2515
- console.error(`Build did not complete within ${options.timeout}s`);
2516
- return 1;
671
+ if (body) {
672
+ console.log(formatCallOutput(body, options.pretty));
2517
673
  }
674
+ return 0;
2518
675
  }
2519
676
  catch (error) {
2520
677
  console.error(formatHttpError(error));
2521
678
  return 1;
2522
679
  }
2523
680
  }
2524
- // ── push / pull (BKN tar import/export) ──────────────────────────────────────
2525
- export function packDirectoryToTar(dirPath) {
2526
- const absPath = resolve(dirPath);
2527
- const entries = readdirSync(absPath);
2528
- const args = ["cf", "-", "-C", absPath, ...entries];
2529
- const result = spawnSync("tar", args, {
2530
- encoding: "buffer",
2531
- env: { ...process.env, COPYFILE_DISABLE: "1" },
2532
- });
2533
- if (result.error) {
2534
- if ("code" in result.error && result.error.code === "ENOENT") {
2535
- throw new Error("tar executable not found. On Windows, ensure tar.exe is in PATH " +
2536
- "(ships with Windows 10 1803+) or install GNU tar via Git for Windows / scoop.");
2537
- }
2538
- throw result.error;
2539
- }
2540
- if (result.status !== 0) {
2541
- throw new Error(`tar pack failed: ${result.stderr?.toString() ?? result.status}`);
2542
- }
2543
- return result.stdout;
2544
- }
2545
- export function extractTarToDirectory(tarBuffer, dirPath) {
2546
- const absPath = resolve(dirPath);
2547
- mkdirSync(absPath, { recursive: true });
2548
- const result = spawnSync("tar", ["xf", "-", "-C", absPath], {
2549
- input: tarBuffer,
681
+ function confirmDelete(knId) {
682
+ return new Promise((resolve) => {
683
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
684
+ rl.question(`Delete knowledge network ${knId}? [y/N] `, (answer) => {
685
+ rl.close();
686
+ const trimmed = answer.trim().toLowerCase();
687
+ resolve(trimmed === "y" || trimmed === "yes");
688
+ });
2550
689
  });
2551
- if (result.error) {
2552
- if ("code" in result.error && result.error.code === "ENOENT") {
2553
- throw new Error("tar executable not found. On Windows, ensure tar.exe is in PATH " +
2554
- "(ships with Windows 10 1803+) or install GNU tar via Git for Windows / scoop.");
2555
- }
2556
- throw result.error;
2557
- }
2558
- if (result.status !== 0) {
2559
- throw new Error(`tar extract failed: ${result.stderr?.toString() ?? result.status}`);
2560
- }
2561
- }
2562
- const KN_PUSH_HELP = `kweaver bkn push <directory> [options]
2563
-
2564
- Pack a BKN directory into a tar and upload to import as a knowledge network.
2565
-
2566
- Options:
2567
- --branch <s> Branch name (default: main)
2568
- -bd, --biz-domain Business domain (default: bd_public)
2569
- --pretty Pretty-print JSON output
2570
- --detect-encoding Detect .bkn encoding and normalize to UTF-8 (default: on)
2571
- --no-detect-encoding Do not detect; require UTF-8 .bkn files
2572
- --source-encoding <name> Decode all .bkn files with this encoding (e.g. gb18030); overrides detection`;
2573
- const KN_PULL_HELP = `kweaver bkn pull <kn-id> [<directory>] [options]
2574
-
2575
- Download a BKN tar from a knowledge network and extract to a local directory.
2576
-
2577
- Options:
2578
- <directory> Output directory (default: <kn-id>)
2579
- --branch <s> Branch name (default: main)
2580
- -bd, --biz-domain Business domain (default: bd_public)`;
2581
- async function runKnValidateCommand(args) {
2582
- if (args.includes("--help") || args.includes("-h")) {
2583
- console.log("Usage: kweaver bkn validate <directory> [options]\n\n" +
2584
- "Validate a local BKN directory without uploading.\n\n" +
2585
- "Options:\n" +
2586
- " --detect-encoding Detect .bkn encoding and normalize to UTF-8 (default: on)\n" +
2587
- " --no-detect-encoding Require UTF-8 .bkn files\n" +
2588
- " --source-encoding <n> Decode all .bkn with this encoding (e.g. gb18030)");
2589
- return 0;
2590
- }
2591
- let encodingOptions;
2592
- let restArgs;
2593
- try {
2594
- const stripped = stripBknEncodingCliArgs(args);
2595
- encodingOptions = stripped.options;
2596
- restArgs = stripped.rest;
2597
- }
2598
- catch (e) {
2599
- console.error(e instanceof Error ? e.message : String(e));
2600
- return 1;
2601
- }
2602
- const directory = restArgs.find((a) => !a.startsWith("-"));
2603
- if (!directory) {
2604
- console.error("Missing directory. Usage: kweaver bkn validate <directory> [options]");
2605
- return 1;
2606
- }
2607
- const absDir = resolve(directory);
2608
- try {
2609
- const stat = statSync(absDir);
2610
- if (!stat.isDirectory()) {
2611
- console.error(`Not a directory: ${directory}`);
2612
- return 1;
2613
- }
2614
- }
2615
- catch (err) {
2616
- if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
2617
- console.error(`Directory not found: ${directory}`);
2618
- return 1;
2619
- }
2620
- throw err;
2621
- }
2622
- const prepared = prepareBknDirectoryForImport(absDir, encodingOptions);
2623
- try {
2624
- const network = await loadNetwork(prepared.dir);
2625
- const result = validateNetwork(network);
2626
- if (!result.ok) {
2627
- for (const e of result.errors)
2628
- console.error(` - ${e}`);
2629
- console.error(`BKN validation failed: ${result.errors.length} error(s)`);
2630
- return 1;
2631
- }
2632
- const objs = allObjects(network);
2633
- const rels = allRelations(network);
2634
- const acts = allActions(network);
2635
- console.log(`Valid: ${objs.length} object types, ${rels.length} relation types, ${acts.length} action types`);
2636
- return 0;
2637
- }
2638
- catch (error) {
2639
- console.error(`BKN validation failed: ${error instanceof Error ? error.message : String(error)}`);
2640
- return 1;
2641
- }
2642
- finally {
2643
- prepared.cleanup();
2644
- }
2645
690
  }
2646
- async function runKnPushCommand(args) {
691
+ async function runKnDeleteCommand(args) {
2647
692
  let options;
2648
693
  try {
2649
- options = parseKnPushArgs(args);
694
+ options = parseKnDeleteArgs(args);
2650
695
  }
2651
696
  catch (error) {
2652
697
  if (error instanceof Error && error.message === "help") {
2653
- console.log(KN_PUSH_HELP);
698
+ console.log(KN_DELETE_HELP);
2654
699
  return 0;
2655
700
  }
2656
701
  console.error(formatHttpError(error));
2657
702
  return 1;
2658
703
  }
2659
- const absDir = resolve(options.directory);
2660
- try {
2661
- const stat = statSync(absDir);
2662
- if (!stat.isDirectory()) {
2663
- console.error(`Not a directory: ${options.directory}`);
2664
- return 1;
2665
- }
2666
- }
2667
- catch (err) {
2668
- if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
2669
- console.error(`Directory not found: ${options.directory}`);
2670
- return 1;
2671
- }
2672
- throw err;
2673
- }
2674
- const prepared = prepareBknDirectoryForImport(absDir, options.encodingOptions);
2675
- const workDir = prepared.dir;
2676
- try {
2677
- try {
2678
- const network = await loadNetwork(workDir);
2679
- const objs = allObjects(network);
2680
- const rels = allRelations(network);
2681
- const acts = allActions(network);
2682
- console.error(`Validated: ${objs.length} object types, ${rels.length} relation types, ${acts.length} action types`);
2683
- }
2684
- catch (error) {
2685
- console.error(`BKN validation failed: ${error instanceof Error ? error.message : String(error)}`);
2686
- return 1;
2687
- }
2688
- try {
2689
- await generateChecksum(workDir);
2690
- console.error("Checksum generated");
2691
- }
2692
- catch (error) {
2693
- console.error(`Checksum generation failed: ${error instanceof Error ? error.message : String(error)}`);
2694
- return 1;
2695
- }
2696
- try {
2697
- const tarBuffer = packDirectoryToTar(workDir);
2698
- const token = await ensureValidToken();
2699
- const body = await uploadBkn({
2700
- baseUrl: token.baseUrl,
2701
- accessToken: token.accessToken,
2702
- tarBuffer,
2703
- businessDomain: options.businessDomain,
2704
- branch: options.branch,
2705
- });
2706
- console.log(formatCallOutput(body, options.pretty));
2707
- return 0;
2708
- }
2709
- catch (error) {
2710
- console.error(formatHttpError(error));
704
+ if (!options.yes) {
705
+ const confirmed = await confirmDelete(options.knId);
706
+ if (!confirmed) {
707
+ console.error("Aborted.");
2711
708
  return 1;
2712
709
  }
2713
710
  }
2714
- finally {
2715
- prepared.cleanup();
2716
- }
2717
- }
2718
- async function runKnPullCommand(args) {
2719
- let options;
2720
- try {
2721
- options = parseKnPullArgs(args);
2722
- }
2723
- catch (error) {
2724
- if (error instanceof Error && error.message === "help") {
2725
- console.log(KN_PULL_HELP);
2726
- return 0;
2727
- }
2728
- console.error(formatHttpError(error));
2729
- return 1;
2730
- }
2731
- try {
2732
- const token = await ensureValidToken();
2733
- const tarBuffer = await downloadBkn({
2734
- baseUrl: token.baseUrl,
2735
- accessToken: token.accessToken,
2736
- knId: options.knId,
2737
- businessDomain: options.businessDomain,
2738
- branch: options.branch,
2739
- });
2740
- const absDir = resolve(options.directory);
2741
- extractTarToDirectory(tarBuffer, absDir);
2742
- console.log(`Extracted to ${absDir}`);
2743
- return 0;
2744
- }
2745
- catch (error) {
2746
- console.error(formatHttpError(error));
2747
- return 1;
2748
- }
2749
- }
2750
- // ── search ──────────────────────────────────────────────────────────────────
2751
- const KN_SEARCH_HELP = `kweaver bkn search <kn-id> <query> [--max-concepts <n>] [--mode <mode>] [--pretty] [-bd value]
2752
-
2753
- Semantic search within a knowledge network via agent-retrieval API.
2754
- Returns matched concepts (object types, relation types, action types).
2755
-
2756
- Options:
2757
- --max-concepts <n> Max concepts to return (default: 10)
2758
- --mode <mode> Search mode (default: keyword_vector_retrieval)
2759
- --pretty Pretty-print JSON output
2760
- -bd, --biz-domain Override x-business-domain`;
2761
- export function parseKnSearchArgs(args) {
2762
- let knId = "";
2763
- let query = "";
2764
- let maxConcepts = 10;
2765
- let mode = "keyword_vector_retrieval";
2766
- let pretty = false;
2767
- let businessDomain = process.env.KWEAVER_BUSINESS_DOMAIN ?? "";
2768
- const positional = [];
2769
- for (let i = 0; i < args.length; i++) {
2770
- const arg = args[i];
2771
- if (arg === "--max-concepts") {
2772
- maxConcepts = Number(args[++i]);
2773
- }
2774
- else if (arg === "--mode") {
2775
- mode = args[++i];
2776
- }
2777
- else if (arg === "--pretty") {
2778
- pretty = true;
2779
- }
2780
- else if (arg === "-bd" || arg === "--biz-domain") {
2781
- businessDomain = args[++i];
2782
- }
2783
- else if (arg === "--help" || arg === "-h") {
2784
- throw Object.assign(new Error("help"), { isHelp: true });
2785
- }
2786
- else if (arg.startsWith("-")) {
2787
- throw new Error(`Unknown flag: ${arg}`);
2788
- }
2789
- else {
2790
- positional.push(arg);
2791
- }
2792
- }
2793
- knId = positional[0] ?? "";
2794
- query = positional.slice(1).join(" ");
2795
- if (!knId || !query) {
2796
- throw new Error("Usage: kweaver bkn search <kn-id> <query> [options]");
2797
- }
2798
- if (!businessDomain)
2799
- businessDomain = resolveBusinessDomain();
2800
- return { knId, query, maxConcepts, mode, pretty, businessDomain };
2801
- }
2802
- async function runKnSearchCommand(args) {
2803
- let options;
2804
- try {
2805
- options = parseKnSearchArgs(args);
2806
- }
2807
- catch (error) {
2808
- if (error instanceof Error && error.isHelp) {
2809
- console.log(KN_SEARCH_HELP);
2810
- return 0;
2811
- }
2812
- console.error(error instanceof Error ? error.message : String(error));
2813
- return 1;
2814
- }
2815
711
  try {
2816
712
  const token = await ensureValidToken();
2817
- const result = await semanticSearch({
713
+ await deleteKnowledgeNetwork({
2818
714
  baseUrl: token.baseUrl,
2819
715
  accessToken: token.accessToken,
2820
716
  knId: options.knId,
2821
- query: options.query,
2822
717
  businessDomain: options.businessDomain,
2823
- maxConcepts: options.maxConcepts,
2824
- mode: options.mode,
2825
718
  });
2826
- console.log(formatCallOutput(result, options.pretty));
2827
719
  return 0;
2828
720
  }
2829
721
  catch (error) {
@@ -2831,149 +723,3 @@ async function runKnSearchCommand(args) {
2831
723
  return 1;
2832
724
  }
2833
725
  }
2834
- const KN_CREATE_FROM_CSV_HELP = `kweaver bkn create-from-csv <ds-id> --files <glob> --name X [options]
2835
-
2836
- Import CSV files into datasource, then create a knowledge network.
2837
-
2838
- Options:
2839
- --files <s> CSV file paths (comma-separated or glob, required)
2840
- --name <s> Knowledge network name (required)
2841
- --table-prefix <s> Table name prefix (default: none)
2842
- --batch-size <n> Rows per batch (default: 500)
2843
- --tables <a,b> Tables to include in KN (default: all imported)
2844
- --build (default) Build after creation
2845
- --no-build Skip build
2846
- --timeout <n> Build timeout in seconds (default: 300)
2847
- -bd, --biz-domain Business domain (default: bd_public)`;
2848
- function parseKnCreateFromCsvArgs(args) {
2849
- let dsId = "";
2850
- let files = "";
2851
- let name = "";
2852
- let tablePrefix = "";
2853
- let batchSize = 500;
2854
- let tablesStr = "";
2855
- let build = true;
2856
- let timeout = 300;
2857
- let businessDomain = "";
2858
- for (let i = 0; i < args.length; i += 1) {
2859
- const arg = args[i];
2860
- if (arg === "--help" || arg === "-h")
2861
- throw new Error("help");
2862
- if (arg === "--files" && args[i + 1]) {
2863
- files = args[++i];
2864
- continue;
2865
- }
2866
- if (arg === "--name" && args[i + 1]) {
2867
- name = args[++i];
2868
- continue;
2869
- }
2870
- if (arg === "--table-prefix" && args[i + 1]) {
2871
- tablePrefix = args[++i];
2872
- continue;
2873
- }
2874
- if (arg === "--batch-size" && args[i + 1]) {
2875
- batchSize = parseInt(args[++i], 10);
2876
- if (Number.isNaN(batchSize) || batchSize < 1)
2877
- batchSize = 500;
2878
- continue;
2879
- }
2880
- if (arg === "--tables" && args[i + 1]) {
2881
- tablesStr = args[++i];
2882
- continue;
2883
- }
2884
- if (arg === "--build") {
2885
- build = true;
2886
- continue;
2887
- }
2888
- if (arg === "--no-build") {
2889
- build = false;
2890
- continue;
2891
- }
2892
- if (arg === "--timeout" && args[i + 1]) {
2893
- timeout = parseInt(args[++i], 10);
2894
- if (Number.isNaN(timeout) || timeout < 1)
2895
- timeout = 300;
2896
- continue;
2897
- }
2898
- if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
2899
- businessDomain = args[++i];
2900
- continue;
2901
- }
2902
- if (!arg.startsWith("-") && !dsId) {
2903
- dsId = arg;
2904
- }
2905
- }
2906
- const tables = tablesStr ? tablesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
2907
- if (!dsId || !files || !name) {
2908
- throw new Error("Usage: kweaver bkn create-from-csv <ds-id> --files <glob> --name X [options]");
2909
- }
2910
- if (!businessDomain)
2911
- businessDomain = resolveBusinessDomain();
2912
- return { dsId, files, name, tablePrefix, batchSize, tables, build, timeout, businessDomain };
2913
- }
2914
- async function runKnCreateFromCsvCommand(args) {
2915
- let options;
2916
- try {
2917
- options = parseKnCreateFromCsvArgs(args);
2918
- }
2919
- catch (error) {
2920
- if (error instanceof Error && error.message === "help") {
2921
- console.log(KN_CREATE_FROM_CSV_HELP);
2922
- return 0;
2923
- }
2924
- console.error(formatHttpError(error));
2925
- return 1;
2926
- }
2927
- // Phase 1: Import CSVs
2928
- console.error("Phase 1: Importing CSVs ...");
2929
- const importArgs = [
2930
- options.dsId,
2931
- "--files", options.files,
2932
- "--table-prefix", options.tablePrefix,
2933
- "--batch-size", String(options.batchSize),
2934
- "-bd", options.businessDomain,
2935
- ];
2936
- const importResult = await runDsImportCsv(importArgs);
2937
- if (importResult.code !== 0) {
2938
- console.error("CSV import failed — aborting KN creation");
2939
- return importResult.code;
2940
- }
2941
- // Phase 1.5: Scan datasource metadata so platform discovers newly imported tables
2942
- console.error("Scanning datasource metadata ...");
2943
- try {
2944
- const token = await ensureValidToken();
2945
- const dsBody = await getDatasource({
2946
- baseUrl: token.baseUrl,
2947
- accessToken: token.accessToken,
2948
- id: options.dsId,
2949
- businessDomain: options.businessDomain,
2950
- });
2951
- const dsParsed = JSON.parse(dsBody);
2952
- await scanMetadata({
2953
- baseUrl: token.baseUrl,
2954
- accessToken: token.accessToken,
2955
- id: options.dsId,
2956
- dsType: dsParsed.type ?? "mysql",
2957
- businessDomain: options.businessDomain,
2958
- });
2959
- }
2960
- catch (err) {
2961
- console.error(`Scan warning (continuing): ${String(err)}`);
2962
- }
2963
- // Phase 2: Create KN from datasource
2964
- console.error("Phase 2: Creating knowledge network ...");
2965
- const tableNames = options.tables.length > 0 ? options.tables : importResult.tables;
2966
- if (tableNames.length === 0) {
2967
- console.error("No tables available for KN creation — aborting");
2968
- return 1;
2969
- }
2970
- const knArgs = [
2971
- options.dsId,
2972
- "--name", options.name,
2973
- "--tables", tableNames.join(","),
2974
- options.build ? "--build" : "--no-build",
2975
- "--timeout", String(options.timeout),
2976
- "-bd", options.businessDomain,
2977
- ];
2978
- return runKnCreateFromDsCommand(knArgs, importResult.sampleRows);
2979
- }