@kweaver-ai/kweaver-sdk 0.4.8 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/oauth.js +3 -4
- package/dist/cli.js +5 -3
- package/dist/commands/bkn.d.ts +2 -0
- package/dist/commands/bkn.js +83 -42
- package/dist/config/store.js +5 -3
- package/dist/utils/bkn-encoding.d.ts +32 -0
- package/dist/utils/bkn-encoding.js +152 -0
- package/dist/utils/browser.js +14 -11
- package/package.json +6 -1
package/dist/auth/oauth.js
CHANGED
|
@@ -74,10 +74,9 @@ export async function oauth2Login(baseUrl, options) {
|
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
76
|
server.listen(port, "127.0.0.1", () => {
|
|
77
|
-
// Step 5: Open browser
|
|
78
|
-
import("
|
|
79
|
-
|
|
80
|
-
exec(`${cmd} "${authUrl}"`);
|
|
77
|
+
// Step 5: Open browser (uses spawn with proper Windows quoting)
|
|
78
|
+
import("../utils/browser.js").then(({ openBrowser }) => {
|
|
79
|
+
openBrowser(authUrl);
|
|
81
80
|
});
|
|
82
81
|
});
|
|
83
82
|
});
|
package/dist/cli.js
CHANGED
|
@@ -54,10 +54,10 @@ Usage:
|
|
|
54
54
|
kweaver bkn update <kn-id> [options]
|
|
55
55
|
kweaver bkn delete <kn-id> [-y]
|
|
56
56
|
kweaver bkn build <kn-id> [--wait] [--no-wait] [--timeout N]
|
|
57
|
-
kweaver bkn validate <
|
|
57
|
+
kweaver bkn validate <directory> [--detect-encoding|--no-detect-encoding] [--source-encoding name]
|
|
58
58
|
kweaver bkn export <kn-id>
|
|
59
59
|
kweaver bkn stats <kn-id>
|
|
60
|
-
kweaver bkn push <directory> [--branch main] [-bd value]
|
|
60
|
+
kweaver bkn push <directory> [--branch main] [-bd value] [--detect-encoding|--no-detect-encoding] [--source-encoding name]
|
|
61
61
|
kweaver bkn pull <kn-id> [directory] [--branch main] [-bd value]
|
|
62
62
|
kweaver bkn object-type list|get|create|update|delete|query|properties <kn-id> ...
|
|
63
63
|
kweaver bkn relation-type list|get|create|update|delete <kn-id> ...
|
|
@@ -154,7 +154,9 @@ function safeExit(code) {
|
|
|
154
154
|
process.exit(code);
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
|
-
|
|
157
|
+
import { fileURLToPath } from "node:url";
|
|
158
|
+
import { resolve } from "node:path";
|
|
159
|
+
if (fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
158
160
|
run(process.argv.slice(2))
|
|
159
161
|
.then((code) => {
|
|
160
162
|
safeExit(code);
|
package/dist/commands/bkn.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type BknEncodingImportOptions } from "../utils/bkn-encoding.js";
|
|
1
2
|
export interface KnListOptions {
|
|
2
3
|
offset: number;
|
|
3
4
|
limit: number;
|
|
@@ -46,6 +47,7 @@ export interface KnPushOptions {
|
|
|
46
47
|
branch: string;
|
|
47
48
|
businessDomain: string;
|
|
48
49
|
pretty: boolean;
|
|
50
|
+
encodingOptions: BknEncodingImportOptions;
|
|
49
51
|
}
|
|
50
52
|
export declare function parseKnPushArgs(args: string[]): KnPushOptions;
|
|
51
53
|
export interface KnPullOptions {
|
package/dist/commands/bkn.js
CHANGED
|
@@ -3,6 +3,7 @@ import { spawnSync } from "node:child_process";
|
|
|
3
3
|
import { mkdirSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
import { loadNetwork, allObjects, allRelations, allActions, generateChecksum, validateNetwork } from "@kweaver-ai/bkn";
|
|
6
|
+
import { prepareBknDirectoryForImport, stripBknEncodingCliArgs, } from "../utils/bkn-encoding.js";
|
|
6
7
|
import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/oauth.js";
|
|
7
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";
|
|
8
9
|
import { objectTypeQuery, objectTypeProperties, subgraph, actionTypeQuery, actionTypeExecute, actionExecutionGet, actionLogsList, actionLogGet, actionLogCancel, } from "../api/ontology-query.js";
|
|
@@ -354,12 +355,13 @@ export function parseKnDeleteArgs(args) {
|
|
|
354
355
|
return { knId, businessDomain, yes };
|
|
355
356
|
}
|
|
356
357
|
export function parseKnPushArgs(args) {
|
|
358
|
+
const { rest, options: encodingOptions } = stripBknEncodingCliArgs(args);
|
|
357
359
|
let directory = "";
|
|
358
360
|
let branch = "main";
|
|
359
361
|
let businessDomain = "";
|
|
360
362
|
let pretty = true;
|
|
361
|
-
for (let i = 0; i <
|
|
362
|
-
const arg =
|
|
363
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
364
|
+
const arg = rest[i];
|
|
363
365
|
if (arg === "--help" || arg === "-h") {
|
|
364
366
|
throw new Error("help");
|
|
365
367
|
}
|
|
@@ -394,7 +396,7 @@ export function parseKnPushArgs(args) {
|
|
|
394
396
|
}
|
|
395
397
|
if (!businessDomain)
|
|
396
398
|
businessDomain = resolveBusinessDomain();
|
|
397
|
-
return { directory, branch, businessDomain, pretty };
|
|
399
|
+
return { directory, branch, businessDomain, pretty, encodingOptions };
|
|
398
400
|
}
|
|
399
401
|
export function parseKnPullArgs(args) {
|
|
400
402
|
let knId = "";
|
|
@@ -2443,8 +2445,13 @@ export function packDirectoryToTar(dirPath) {
|
|
|
2443
2445
|
encoding: "buffer",
|
|
2444
2446
|
env: { ...process.env, COPYFILE_DISABLE: "1" },
|
|
2445
2447
|
});
|
|
2446
|
-
if (result.error)
|
|
2448
|
+
if (result.error) {
|
|
2449
|
+
if ("code" in result.error && result.error.code === "ENOENT") {
|
|
2450
|
+
throw new Error("tar executable not found. On Windows, ensure tar.exe is in PATH " +
|
|
2451
|
+
"(ships with Windows 10 1803+) or install GNU tar via Git for Windows / scoop.");
|
|
2452
|
+
}
|
|
2447
2453
|
throw result.error;
|
|
2454
|
+
}
|
|
2448
2455
|
if (result.status !== 0) {
|
|
2449
2456
|
throw new Error(`tar pack failed: ${result.stderr?.toString() ?? result.status}`);
|
|
2450
2457
|
}
|
|
@@ -2457,6 +2464,10 @@ export function extractTarToDirectory(tarBuffer, dirPath) {
|
|
|
2457
2464
|
input: tarBuffer,
|
|
2458
2465
|
});
|
|
2459
2466
|
if (result.error) {
|
|
2467
|
+
if ("code" in result.error && result.error.code === "ENOENT") {
|
|
2468
|
+
throw new Error("tar executable not found. On Windows, ensure tar.exe is in PATH " +
|
|
2469
|
+
"(ships with Windows 10 1803+) or install GNU tar via Git for Windows / scoop.");
|
|
2470
|
+
}
|
|
2460
2471
|
throw result.error;
|
|
2461
2472
|
}
|
|
2462
2473
|
if (result.status !== 0) {
|
|
@@ -2470,7 +2481,10 @@ Pack a BKN directory into a tar and upload to import as a knowledge network.
|
|
|
2470
2481
|
Options:
|
|
2471
2482
|
--branch <s> Branch name (default: main)
|
|
2472
2483
|
-bd, --biz-domain Business domain (default: bd_public)
|
|
2473
|
-
--pretty Pretty-print JSON output
|
|
2484
|
+
--pretty Pretty-print JSON output
|
|
2485
|
+
--detect-encoding Detect .bkn encoding and normalize to UTF-8 (default: on)
|
|
2486
|
+
--no-detect-encoding Do not detect; require UTF-8 .bkn files
|
|
2487
|
+
--source-encoding <name> Decode all .bkn files with this encoding (e.g. gb18030); overrides detection`;
|
|
2474
2488
|
const KN_PULL_HELP = `kweaver bkn pull <kn-id> [<directory>] [options]
|
|
2475
2489
|
|
|
2476
2490
|
Download a BKN tar from a knowledge network and extract to a local directory.
|
|
@@ -2481,12 +2495,28 @@ Options:
|
|
|
2481
2495
|
-bd, --biz-domain Business domain (default: bd_public)`;
|
|
2482
2496
|
async function runKnValidateCommand(args) {
|
|
2483
2497
|
if (args.includes("--help") || args.includes("-h")) {
|
|
2484
|
-
console.log("Usage: kweaver bkn validate <directory
|
|
2498
|
+
console.log("Usage: kweaver bkn validate <directory> [options]\n\n" +
|
|
2499
|
+
"Validate a local BKN directory without uploading.\n\n" +
|
|
2500
|
+
"Options:\n" +
|
|
2501
|
+
" --detect-encoding Detect .bkn encoding and normalize to UTF-8 (default: on)\n" +
|
|
2502
|
+
" --no-detect-encoding Require UTF-8 .bkn files\n" +
|
|
2503
|
+
" --source-encoding <n> Decode all .bkn with this encoding (e.g. gb18030)");
|
|
2485
2504
|
return 0;
|
|
2486
2505
|
}
|
|
2487
|
-
|
|
2506
|
+
let encodingOptions;
|
|
2507
|
+
let restArgs;
|
|
2508
|
+
try {
|
|
2509
|
+
const stripped = stripBknEncodingCliArgs(args);
|
|
2510
|
+
encodingOptions = stripped.options;
|
|
2511
|
+
restArgs = stripped.rest;
|
|
2512
|
+
}
|
|
2513
|
+
catch (e) {
|
|
2514
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
2515
|
+
return 1;
|
|
2516
|
+
}
|
|
2517
|
+
const directory = restArgs.find((a) => !a.startsWith("-"));
|
|
2488
2518
|
if (!directory) {
|
|
2489
|
-
console.error("Missing directory. Usage: kweaver bkn validate <directory>");
|
|
2519
|
+
console.error("Missing directory. Usage: kweaver bkn validate <directory> [options]");
|
|
2490
2520
|
return 1;
|
|
2491
2521
|
}
|
|
2492
2522
|
const absDir = resolve(directory);
|
|
@@ -2504,8 +2534,9 @@ async function runKnValidateCommand(args) {
|
|
|
2504
2534
|
}
|
|
2505
2535
|
throw err;
|
|
2506
2536
|
}
|
|
2537
|
+
const prepared = prepareBknDirectoryForImport(absDir, encodingOptions);
|
|
2507
2538
|
try {
|
|
2508
|
-
const network = await loadNetwork(
|
|
2539
|
+
const network = await loadNetwork(prepared.dir);
|
|
2509
2540
|
const result = validateNetwork(network);
|
|
2510
2541
|
if (!result.ok) {
|
|
2511
2542
|
for (const e of result.errors)
|
|
@@ -2523,6 +2554,9 @@ async function runKnValidateCommand(args) {
|
|
|
2523
2554
|
console.error(`BKN validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2524
2555
|
return 1;
|
|
2525
2556
|
}
|
|
2557
|
+
finally {
|
|
2558
|
+
prepared.cleanup();
|
|
2559
|
+
}
|
|
2526
2560
|
}
|
|
2527
2561
|
async function runKnPushCommand(args) {
|
|
2528
2562
|
let options;
|
|
@@ -2552,41 +2586,48 @@ async function runKnPushCommand(args) {
|
|
|
2552
2586
|
}
|
|
2553
2587
|
throw err;
|
|
2554
2588
|
}
|
|
2589
|
+
const prepared = prepareBknDirectoryForImport(absDir, options.encodingOptions);
|
|
2590
|
+
const workDir = prepared.dir;
|
|
2555
2591
|
try {
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2592
|
+
try {
|
|
2593
|
+
const network = await loadNetwork(workDir);
|
|
2594
|
+
const objs = allObjects(network);
|
|
2595
|
+
const rels = allRelations(network);
|
|
2596
|
+
const acts = allActions(network);
|
|
2597
|
+
console.error(`Validated: ${objs.length} object types, ${rels.length} relation types, ${acts.length} action types`);
|
|
2598
|
+
}
|
|
2599
|
+
catch (error) {
|
|
2600
|
+
console.error(`BKN validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2601
|
+
return 1;
|
|
2602
|
+
}
|
|
2603
|
+
try {
|
|
2604
|
+
await generateChecksum(workDir);
|
|
2605
|
+
console.error("Checksum generated");
|
|
2606
|
+
}
|
|
2607
|
+
catch (error) {
|
|
2608
|
+
console.error(`Checksum generation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2609
|
+
return 1;
|
|
2610
|
+
}
|
|
2611
|
+
try {
|
|
2612
|
+
const tarBuffer = packDirectoryToTar(workDir);
|
|
2613
|
+
const token = await ensureValidToken();
|
|
2614
|
+
const body = await uploadBkn({
|
|
2615
|
+
baseUrl: token.baseUrl,
|
|
2616
|
+
accessToken: token.accessToken,
|
|
2617
|
+
tarBuffer,
|
|
2618
|
+
businessDomain: options.businessDomain,
|
|
2619
|
+
branch: options.branch,
|
|
2620
|
+
});
|
|
2621
|
+
console.log(formatCallOutput(body, options.pretty));
|
|
2622
|
+
return 0;
|
|
2623
|
+
}
|
|
2624
|
+
catch (error) {
|
|
2625
|
+
console.error(formatHttpError(error));
|
|
2626
|
+
return 1;
|
|
2627
|
+
}
|
|
2586
2628
|
}
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
return 1;
|
|
2629
|
+
finally {
|
|
2630
|
+
prepared.cleanup();
|
|
2590
2631
|
}
|
|
2591
2632
|
}
|
|
2592
2633
|
async function runKnPullCommand(args) {
|
package/dist/config/store.js
CHANGED
|
@@ -24,9 +24,10 @@ function getLegacyTokenFilePath() {
|
|
|
24
24
|
function getLegacyCallbackFilePath() {
|
|
25
25
|
return join(getConfigDirPath(), "callback.json");
|
|
26
26
|
}
|
|
27
|
+
const IS_WIN32 = process.platform === "win32";
|
|
27
28
|
function ensureDir(path) {
|
|
28
29
|
if (!existsSync(path)) {
|
|
29
|
-
mkdirSync(path, { recursive: true, mode: 0o700 });
|
|
30
|
+
mkdirSync(path, { recursive: true, ...(IS_WIN32 ? {} : { mode: 0o700 }) });
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
function ensureConfigDir() {
|
|
@@ -41,8 +42,9 @@ function readJsonFile(filePath) {
|
|
|
41
42
|
}
|
|
42
43
|
function writeJsonFile(filePath, value) {
|
|
43
44
|
ensureConfigDir();
|
|
44
|
-
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
45
|
-
|
|
45
|
+
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, IS_WIN32 ? {} : { mode: 0o600 });
|
|
46
|
+
if (!IS_WIN32)
|
|
47
|
+
chmodSync(filePath, 0o600);
|
|
46
48
|
}
|
|
47
49
|
function encodePlatformKey(baseUrl) {
|
|
48
50
|
return Buffer.from(baseUrl, "utf8")
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize .bkn file bytes to UTF-8 for BKN import (validate / push).
|
|
3
|
+
* Used when --detect-encoding (default) or --source-encoding is active.
|
|
4
|
+
*/
|
|
5
|
+
/** Minimum confidence (0–1) for charset detection before failing. */
|
|
6
|
+
export declare const BKN_DETECT_MIN_CONFIDENCE = 0.65;
|
|
7
|
+
export interface BknEncodingImportOptions {
|
|
8
|
+
/** When true (default), detect encoding for non-UTF-8 .bkn files. */
|
|
9
|
+
detectEncoding: boolean;
|
|
10
|
+
/** When set, decode all .bkn files with this encoding (overrides detection). */
|
|
11
|
+
sourceEncoding: string | null;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parse --no-detect-encoding, --detect-encoding, --source-encoding <name> from argv.
|
|
15
|
+
* Remaining args are returned for positional parsing (directory, etc.).
|
|
16
|
+
*/
|
|
17
|
+
export declare function stripBknEncodingCliArgs(args: string[]): {
|
|
18
|
+
rest: string[];
|
|
19
|
+
options: BknEncodingImportOptions;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Decode raw .bkn bytes to a UTF-8 Buffer (no BOM).
|
|
23
|
+
*/
|
|
24
|
+
export declare function normalizeBknFileBytes(raw: Buffer, options: BknEncodingImportOptions, fileLabel: string): Buffer;
|
|
25
|
+
/**
|
|
26
|
+
* When normalization is needed, copy the tree to a temp dir with .bkn files normalized to UTF-8.
|
|
27
|
+
* Returns the directory to pass to loadNetwork and a cleanup function.
|
|
28
|
+
*/
|
|
29
|
+
export declare function prepareBknDirectoryForImport(absDir: string, options: BknEncodingImportOptions): {
|
|
30
|
+
dir: string;
|
|
31
|
+
cleanup: () => void;
|
|
32
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize .bkn file bytes to UTF-8 for BKN import (validate / push).
|
|
3
|
+
* Used when --detect-encoding (default) or --source-encoding is active.
|
|
4
|
+
*/
|
|
5
|
+
import { copyFileSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { join, relative, resolve } from "node:path";
|
|
8
|
+
import chardet from "chardet";
|
|
9
|
+
import iconv from "iconv-lite";
|
|
10
|
+
/** Minimum confidence (0–1) for charset detection before failing. */
|
|
11
|
+
export const BKN_DETECT_MIN_CONFIDENCE = 0.65;
|
|
12
|
+
/**
|
|
13
|
+
* Parse --no-detect-encoding, --detect-encoding, --source-encoding <name> from argv.
|
|
14
|
+
* Remaining args are returned for positional parsing (directory, etc.).
|
|
15
|
+
*/
|
|
16
|
+
export function stripBknEncodingCliArgs(args) {
|
|
17
|
+
let detectEncoding = true;
|
|
18
|
+
let sourceEncoding = null;
|
|
19
|
+
const rest = [];
|
|
20
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
21
|
+
const arg = args[i];
|
|
22
|
+
if (arg === "--no-detect-encoding") {
|
|
23
|
+
detectEncoding = false;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (arg === "--detect-encoding") {
|
|
27
|
+
detectEncoding = true;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (arg === "--source-encoding") {
|
|
31
|
+
const v = args[i + 1];
|
|
32
|
+
if (!v || v.startsWith("-")) {
|
|
33
|
+
throw new Error("Missing value for --source-encoding (e.g. gb18030)");
|
|
34
|
+
}
|
|
35
|
+
sourceEncoding = v;
|
|
36
|
+
i += 1;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
rest.push(arg);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
rest,
|
|
43
|
+
options: { detectEncoding, sourceEncoding },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function isValidUtf8(buf) {
|
|
47
|
+
try {
|
|
48
|
+
new TextDecoder("utf-8", { fatal: true }).decode(buf);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function stripUtf8Bom(buf) {
|
|
56
|
+
if (buf.length >= 3 && buf[0] === 0xef && buf[1] === 0xbb && buf[2] === 0xbf) {
|
|
57
|
+
return buf.subarray(3);
|
|
58
|
+
}
|
|
59
|
+
return buf;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Decode raw .bkn bytes to a UTF-8 Buffer (no BOM).
|
|
63
|
+
*/
|
|
64
|
+
export function normalizeBknFileBytes(raw, options, fileLabel) {
|
|
65
|
+
if (options.sourceEncoding) {
|
|
66
|
+
const enc = options.sourceEncoding.trim().toLowerCase();
|
|
67
|
+
if (enc === "utf-8" || enc === "utf8") {
|
|
68
|
+
const body = stripUtf8Bom(raw);
|
|
69
|
+
if (!isValidUtf8(body)) {
|
|
70
|
+
throw new Error(`Invalid UTF-8 in ${fileLabel} despite --source-encoding utf-8`);
|
|
71
|
+
}
|
|
72
|
+
return Buffer.from(body.toString("utf8"), "utf8");
|
|
73
|
+
}
|
|
74
|
+
if (!iconv.encodingExists(enc)) {
|
|
75
|
+
throw new Error(`Unsupported --source-encoding: ${options.sourceEncoding}`);
|
|
76
|
+
}
|
|
77
|
+
const text = iconv.decode(raw, enc);
|
|
78
|
+
return Buffer.from(text, "utf8");
|
|
79
|
+
}
|
|
80
|
+
if (!options.detectEncoding) {
|
|
81
|
+
const body = stripUtf8Bom(raw);
|
|
82
|
+
if (!isValidUtf8(body)) {
|
|
83
|
+
throw new Error(`Invalid UTF-8 in ${fileLabel}. Use --detect-encoding (default) or --source-encoding (e.g. gb18030).`);
|
|
84
|
+
}
|
|
85
|
+
return Buffer.from(body.toString("utf8"), "utf8");
|
|
86
|
+
}
|
|
87
|
+
let work = stripUtf8Bom(raw);
|
|
88
|
+
if (isValidUtf8(work)) {
|
|
89
|
+
return Buffer.from(work.toString("utf8"), "utf8");
|
|
90
|
+
}
|
|
91
|
+
const matches = chardet.analyse(work);
|
|
92
|
+
const best = matches[0];
|
|
93
|
+
if (!best || best.confidence < BKN_DETECT_MIN_CONFIDENCE) {
|
|
94
|
+
throw new Error(`Could not detect encoding confidently for ${fileLabel} (best confidence ${best?.confidence ?? 0}). ` +
|
|
95
|
+
`Try --source-encoding gb18030 or save files as UTF-8.`);
|
|
96
|
+
}
|
|
97
|
+
const name = best.name ?? "utf-8";
|
|
98
|
+
if (!iconv.encodingExists(name)) {
|
|
99
|
+
throw new Error(`Detected encoding "${name}" is not supported for ${fileLabel}. Try --source-encoding.`);
|
|
100
|
+
}
|
|
101
|
+
const text = iconv.decode(work, name);
|
|
102
|
+
return Buffer.from(text, "utf8");
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* When normalization is needed, copy the tree to a temp dir with .bkn files normalized to UTF-8.
|
|
106
|
+
* Returns the directory to pass to loadNetwork and a cleanup function.
|
|
107
|
+
*/
|
|
108
|
+
export function prepareBknDirectoryForImport(absDir, options) {
|
|
109
|
+
const needWork = options.sourceEncoding != null || options.detectEncoding;
|
|
110
|
+
if (!needWork) {
|
|
111
|
+
return { dir: absDir, cleanup: () => { } };
|
|
112
|
+
}
|
|
113
|
+
const root = resolve(absDir);
|
|
114
|
+
const tmpRoot = mkdtempSync(join(tmpdir(), "kweaver-bkn-"));
|
|
115
|
+
function walk(srcDir, destDir) {
|
|
116
|
+
mkdirSync(destDir, { recursive: true });
|
|
117
|
+
const entries = readdirSync(srcDir, { withFileTypes: true });
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
if (entry.name === "." || entry.name === "..")
|
|
120
|
+
continue;
|
|
121
|
+
const srcPath = join(srcDir, entry.name);
|
|
122
|
+
const destPath = join(destDir, entry.name);
|
|
123
|
+
if (entry.isDirectory()) {
|
|
124
|
+
walk(srcPath, destPath);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (!entry.isFile())
|
|
128
|
+
continue;
|
|
129
|
+
if (entry.name.endsWith(".bkn")) {
|
|
130
|
+
const raw = readFileSync(srcPath);
|
|
131
|
+
const rel = relative(root, srcPath) || entry.name;
|
|
132
|
+
const out = normalizeBknFileBytes(raw, options, rel);
|
|
133
|
+
writeFileSync(destPath, out);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
copyFileSync(srcPath, destPath);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
walk(root, tmpRoot);
|
|
141
|
+
return {
|
|
142
|
+
dir: tmpRoot,
|
|
143
|
+
cleanup: () => {
|
|
144
|
+
try {
|
|
145
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
/* ignore */
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
package/dist/utils/browser.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
export function openBrowser(url) {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
const isWindows = process.platform === "win32";
|
|
4
|
+
const command = process.platform === "darwin" ? "open" : isWindows ? "rundll32.exe" : "xdg-open";
|
|
5
|
+
const args = isWindows
|
|
6
|
+
? [
|
|
7
|
+
"url.dll,FileProtocolHandler",
|
|
8
|
+
url,
|
|
9
|
+
]
|
|
10
10
|
: [url];
|
|
11
11
|
return new Promise((resolve) => {
|
|
12
12
|
const child = spawn(command, args, {
|
|
13
|
-
detached: true,
|
|
14
13
|
stdio: "ignore",
|
|
14
|
+
detached: !isWindows,
|
|
15
|
+
windowsHide: true,
|
|
15
16
|
});
|
|
16
|
-
child.
|
|
17
|
-
child.
|
|
18
|
-
|
|
17
|
+
child.once("error", () => resolve(false));
|
|
18
|
+
child.once("spawn", () => resolve(true));
|
|
19
|
+
if (!isWindows) {
|
|
20
|
+
child.unref();
|
|
21
|
+
}
|
|
19
22
|
});
|
|
20
23
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kweaver-ai/kweaver-sdk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"description": "KWeaver TypeScript SDK — CLI tool and programmatic API for knowledge networks and Decision Agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"lint": "tsc --noEmit -p tsconfig.json",
|
|
30
30
|
"test": "node --import tsx --test test/*.test.ts",
|
|
31
31
|
"test:e2e": "node --import tsx test/e2e/ensure-token.ts && node --import tsx --test --test-concurrency=1 test/e2e/**/*.test.ts",
|
|
32
|
+
"test:e2e:strict": "node test/e2e/run-e2e-strict.mjs",
|
|
32
33
|
"prepublishOnly": "npm run build"
|
|
33
34
|
},
|
|
34
35
|
"keywords": [
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@types/node": "^24.6.0",
|
|
52
53
|
"@types/react": "^19.2.14",
|
|
54
|
+
"playwright": "^1.58.2",
|
|
53
55
|
"tsx": "^4.20.5",
|
|
54
56
|
"typescript": "^5.9.3"
|
|
55
57
|
},
|
|
@@ -63,6 +65,9 @@
|
|
|
63
65
|
},
|
|
64
66
|
"dependencies": {
|
|
65
67
|
"@kweaver-ai/bkn": "^0.1.0",
|
|
68
|
+
"@playwright/test": "^1.58.2",
|
|
69
|
+
"chardet": "^2.1.1",
|
|
70
|
+
"iconv-lite": "^0.7.2",
|
|
66
71
|
"ink": "^6.8.0",
|
|
67
72
|
"ink-spinner": "^5.0.0",
|
|
68
73
|
"ink-text-input": "^6.0.0",
|