@kweaver-ai/kweaver-sdk 0.4.10 → 0.4.12
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/README.md +61 -3
- package/README.zh.md +42 -1
- package/dist/api/dataflow.d.ts +78 -0
- package/dist/api/dataflow.js +135 -0
- package/dist/api/dataviews.d.ts +58 -1
- package/dist/api/dataviews.js +150 -1
- package/dist/auth/oauth.d.ts +6 -1
- package/dist/auth/oauth.js +240 -166
- package/dist/cli.js +13 -1
- package/dist/client.d.ts +12 -0
- package/dist/client.js +18 -0
- package/dist/commands/auth.js +36 -16
- package/dist/commands/bkn.js +214 -21
- package/dist/commands/dataview.d.ts +1 -0
- package/dist/commands/dataview.js +244 -0
- package/dist/commands/ds.d.ts +16 -0
- package/dist/commands/ds.js +204 -1
- package/dist/commands/import-csv.d.ts +47 -0
- package/dist/commands/import-csv.js +111 -0
- package/dist/config/store.d.ts +2 -0
- package/dist/config/tls-env.d.ts +8 -0
- package/dist/config/tls-env.js +22 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/resources/dataflows.d.ts +17 -0
- package/dist/resources/dataflows.js +22 -0
- package/dist/resources/datasources.d.ts +52 -0
- package/dist/resources/datasources.js +54 -0
- package/dist/resources/dataviews.d.ts +28 -0
- package/dist/resources/dataviews.js +34 -0
- package/dist/resources/vega.d.ts +41 -0
- package/dist/resources/vega.js +80 -0
- package/package.json +2 -1
package/dist/commands/auth.js
CHANGED
|
@@ -4,19 +4,24 @@ export async function runAuthCommand(args) {
|
|
|
4
4
|
const target = args[0];
|
|
5
5
|
const rest = args.slice(1);
|
|
6
6
|
if (!target || target === "--help" || target === "-h") {
|
|
7
|
-
console.log(`kweaver auth login <url> [
|
|
8
|
-
kweaver auth <url>
|
|
9
|
-
kweaver auth status [url|alias]
|
|
10
|
-
kweaver auth list
|
|
11
|
-
kweaver auth use <url|alias>
|
|
12
|
-
kweaver auth logout [url|alias]
|
|
13
|
-
kweaver auth delete <url|alias>
|
|
7
|
+
console.log(`kweaver auth login <url> [options] Login to a platform (browser OAuth2 by default)
|
|
8
|
+
kweaver auth <url> Login (shorthand; same options as login)
|
|
9
|
+
kweaver auth status [url|alias] Show current auth status
|
|
10
|
+
kweaver auth list List saved platforms
|
|
11
|
+
kweaver auth use <url|alias> Switch active platform
|
|
12
|
+
kweaver auth logout [url|alias] Logout (clear local token)
|
|
13
|
+
kweaver auth delete <url|alias> Delete saved credentials
|
|
14
14
|
|
|
15
|
-
Login options
|
|
16
|
-
--alias <name>
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
Login options:
|
|
16
|
+
--alias <name> Save platform with a short alias (use with use / status / logout)
|
|
17
|
+
--client-id <id> Use an existing OAuth2 client ID instead of registering a new one.
|
|
18
|
+
Use the platform's web app client ID to get the same permissions
|
|
19
|
+
as the browser. Find it in DevTools: /oauth2/auth?client_id=<id>
|
|
20
|
+
--client-secret <s> Client secret (omit for public/PKCE clients)
|
|
21
|
+
-u, --username Username (with -p triggers Playwright headless login)
|
|
22
|
+
-p, --password Password
|
|
23
|
+
--playwright Force Playwright browser login even without -u/-p
|
|
24
|
+
--insecure, -k Skip TLS certificate verification (self-signed / dev HTTPS only)`);
|
|
20
25
|
return 0;
|
|
21
26
|
}
|
|
22
27
|
if (target === "login") {
|
|
@@ -38,21 +43,33 @@ Login options (browser OAuth2 by default; use -u/-p for headless Playwright):
|
|
|
38
43
|
const username = readOption(args, "--username") ?? readOption(args, "-u");
|
|
39
44
|
const password = readOption(args, "--password") ?? readOption(args, "-p");
|
|
40
45
|
const usePlaywright = args.includes("--playwright");
|
|
46
|
+
const clientId = readOption(args, "--client-id");
|
|
47
|
+
const clientSecret = readOption(args, "--client-secret");
|
|
48
|
+
const tlsInsecure = args.includes("--insecure") || args.includes("-k");
|
|
41
49
|
let token;
|
|
42
50
|
if (username && password) {
|
|
43
51
|
// Headless Playwright login with credentials
|
|
44
52
|
console.log("Logging in (headless)...");
|
|
45
|
-
token = await playwrightLogin(normalizedTarget, { username, password });
|
|
53
|
+
token = await playwrightLogin(normalizedTarget, { username, password, tlsInsecure });
|
|
46
54
|
}
|
|
47
55
|
else if (usePlaywright) {
|
|
48
56
|
// Explicit Playwright fallback
|
|
49
57
|
console.log("Opening browser for login (Playwright)...");
|
|
50
|
-
token = await playwrightLogin(normalizedTarget);
|
|
58
|
+
token = await playwrightLogin(normalizedTarget, { tlsInsecure });
|
|
51
59
|
}
|
|
52
60
|
else {
|
|
53
61
|
// Default: OAuth2 authorization code flow (supports refresh_token)
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
if (clientId) {
|
|
63
|
+
console.log(`Opening browser for OAuth2 login (client: ${clientId})...`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log("Opening browser for OAuth2 login...");
|
|
67
|
+
}
|
|
68
|
+
token = await oauth2Login(normalizedTarget, {
|
|
69
|
+
clientId: clientId ?? undefined,
|
|
70
|
+
clientSecret: clientSecret ?? undefined,
|
|
71
|
+
tlsInsecure,
|
|
72
|
+
});
|
|
56
73
|
}
|
|
57
74
|
if (alias) {
|
|
58
75
|
setPlatformAlias(normalizedTarget, alias);
|
|
@@ -106,6 +123,9 @@ Login options (browser OAuth2 by default; use -u/-p for headless Playwright):
|
|
|
106
123
|
`Token present: yes`,
|
|
107
124
|
];
|
|
108
125
|
lines.push(`Refresh token: ${token.refreshToken ? "yes (auto-refresh enabled)" : "no"}`);
|
|
126
|
+
if (token.tlsInsecure) {
|
|
127
|
+
lines.push(`TLS: certificate verification disabled (saved; dev only)`);
|
|
128
|
+
}
|
|
109
129
|
if (token.expiresAt) {
|
|
110
130
|
const expiry = new Date(token.expiresAt);
|
|
111
131
|
const remainingMs = expiry.getTime() - Date.now();
|
package/dist/commands/bkn.js
CHANGED
|
@@ -8,11 +8,12 @@ import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/
|
|
|
8
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
9
|
import { objectTypeQuery, objectTypeProperties, subgraph, actionTypeQuery, actionTypeExecute, actionExecutionGet, actionLogsList, actionLogGet, actionLogCancel, } from "../api/ontology-query.js";
|
|
10
10
|
import { semanticSearch } from "../api/semantic-search.js";
|
|
11
|
-
import { listTablesWithColumns } from "../api/datasources.js";
|
|
12
|
-
import { createDataView } from "../api/dataviews.js";
|
|
11
|
+
import { listTablesWithColumns, scanMetadata, getDatasource } from "../api/datasources.js";
|
|
12
|
+
import { createDataView, findDataView } from "../api/dataviews.js"; // used by runKnCreateFromDsCommand
|
|
13
13
|
import { downloadBkn, uploadBkn } from "../api/bkn-backend.js";
|
|
14
14
|
import { formatCallOutput } from "./call.js";
|
|
15
15
|
import { resolveBusinessDomain } from "../config/store.js";
|
|
16
|
+
import { runDsImportCsv } from "./ds.js";
|
|
16
17
|
export function formatSimpleKnList(text, pretty, includeDetail = false) {
|
|
17
18
|
const parsed = JSON.parse(text);
|
|
18
19
|
const entries = Array.isArray(parsed.entries) ? parsed.entries : [];
|
|
@@ -600,6 +601,8 @@ Subcommands:
|
|
|
600
601
|
get <kn-id> [options] Get knowledge network detail (use --stats or --export)
|
|
601
602
|
create [options] Create a knowledge network (empty or from --body-file)
|
|
602
603
|
create-from-ds <ds-id> --name X [--tables a,b] [--build] Create KN from datasource
|
|
604
|
+
create-from-csv <ds-id> --files <glob> --name X [--table-prefix P] [--build]
|
|
605
|
+
Import CSVs then create knowledge network
|
|
603
606
|
update <kn-id> [options] Update a knowledge network
|
|
604
607
|
delete <kn-id> Delete a knowledge network
|
|
605
608
|
build <kn-id> [--wait|--no-wait] [--timeout n] Trigger full build
|
|
@@ -646,6 +649,8 @@ export async function runKnCommand(args) {
|
|
|
646
649
|
return runKnCreateCommand(rest);
|
|
647
650
|
if (subcommand === "create-from-ds")
|
|
648
651
|
return runKnCreateFromDsCommand(rest);
|
|
652
|
+
if (subcommand === "create-from-csv")
|
|
653
|
+
return runKnCreateFromCsvCommand(rest);
|
|
649
654
|
if (subcommand === "update")
|
|
650
655
|
return runKnUpdateCommand(rest);
|
|
651
656
|
if (subcommand === "delete")
|
|
@@ -1074,20 +1079,18 @@ export function parseKnActionTypeExecuteArgs(args) {
|
|
|
1074
1079
|
timeout,
|
|
1075
1080
|
};
|
|
1076
1081
|
}
|
|
1077
|
-
const PK_CANDIDATES = new Set(["id", "pk", "key"]);
|
|
1078
|
-
const PK_TYPES = new Set(["integer", "unsigned integer", "string", "varchar", "bigint", "int"]);
|
|
1079
1082
|
const DISPLAY_HINTS = ["name", "title", "label", "display_name", "description"];
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
return col.name;
|
|
1083
|
+
/** Detect primary key: first column (left-to-right) with all unique values in the sample. */
|
|
1084
|
+
function detectPrimaryKey(table, rows) {
|
|
1085
|
+
if (rows && rows.length > 0) {
|
|
1086
|
+
for (const col of table.columns) {
|
|
1087
|
+
const values = rows.map((r) => r[col.name]);
|
|
1088
|
+
const unique = new Set(values);
|
|
1089
|
+
if (unique.size === rows.length)
|
|
1090
|
+
return col.name;
|
|
1089
1091
|
}
|
|
1090
1092
|
}
|
|
1093
|
+
// Fallback: first column
|
|
1091
1094
|
return table.columns[0]?.name ?? "id";
|
|
1092
1095
|
}
|
|
1093
1096
|
function detectDisplayKey(table, primaryKey) {
|
|
@@ -2141,7 +2144,25 @@ function parseKnCreateFromDsArgs(args) {
|
|
|
2141
2144
|
businessDomain = resolveBusinessDomain();
|
|
2142
2145
|
return { dsId, name, tables, build, timeout, businessDomain, pretty };
|
|
2143
2146
|
}
|
|
2144
|
-
|
|
2147
|
+
/** Sanitize a table name into a BKN-safe ID (alphanumeric + underscore). */
|
|
2148
|
+
function sanitizeBknId(name) {
|
|
2149
|
+
return name.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^(\d)/, "_$1");
|
|
2150
|
+
}
|
|
2151
|
+
/** Generate a BKN ObjectType YAML markdown file for a table. */
|
|
2152
|
+
function generateObjectTypeBkn(tableName, dvId, pk, dk, columns) {
|
|
2153
|
+
const safeId = sanitizeBknId(tableName);
|
|
2154
|
+
const header = `## ObjectType: ${safeId}\n\n**${tableName}**\n`;
|
|
2155
|
+
const dsTable = `### Data Source\n\n| Type | ID | Name |\n|------|-----|------|\n| data_view | ${dvId} | ${tableName} |\n`;
|
|
2156
|
+
const dpHeader = `### Data Properties\n\n| Property | Display Name | Type | Primary Key | Display Key |\n|----------|-------------|------|-------------|-------------|\n`;
|
|
2157
|
+
const dpRows = columns.map((c) => {
|
|
2158
|
+
const isPk = c.name === pk ? "yes" : "no";
|
|
2159
|
+
const isDk = c.name === dk ? "yes" : "no";
|
|
2160
|
+
return `| ${c.name} | ${c.name} | string | ${isPk} | ${isDk} |`;
|
|
2161
|
+
}).join("\n");
|
|
2162
|
+
const frontmatter = `---\ntype: object_type\nid: ${safeId}\nname: ${tableName}\n---\n\n`;
|
|
2163
|
+
return `${frontmatter}${header}\n${dsTable}\n${dpHeader}${dpRows}\n`;
|
|
2164
|
+
}
|
|
2165
|
+
async function runKnCreateFromDsCommand(args, sampleRows) {
|
|
2145
2166
|
let options;
|
|
2146
2167
|
try {
|
|
2147
2168
|
options = parseKnCreateFromDsArgs(args);
|
|
@@ -2170,17 +2191,28 @@ async function runKnCreateFromDsCommand(args) {
|
|
|
2170
2191
|
console.error("No tables available");
|
|
2171
2192
|
return 1;
|
|
2172
2193
|
}
|
|
2194
|
+
// Phase 1: Create DataViews for each table
|
|
2195
|
+
console.error(`Creating data views for ${targetTables.length} table(s) ...`);
|
|
2173
2196
|
const viewMap = {};
|
|
2174
2197
|
for (const t of targetTables) {
|
|
2175
|
-
const
|
|
2198
|
+
const found = await findDataView({
|
|
2176
2199
|
...base,
|
|
2177
2200
|
name: t.name,
|
|
2178
2201
|
datasourceId: options.dsId,
|
|
2179
|
-
|
|
2180
|
-
|
|
2202
|
+
exact: true,
|
|
2203
|
+
wait: true,
|
|
2181
2204
|
});
|
|
2205
|
+
const dvId = found[0]?.id ??
|
|
2206
|
+
(await createDataView({
|
|
2207
|
+
...base,
|
|
2208
|
+
name: t.name,
|
|
2209
|
+
datasourceId: options.dsId,
|
|
2210
|
+
table: t.name,
|
|
2211
|
+
fields: t.columns.map((c) => ({ name: c.name, type: c.type })),
|
|
2212
|
+
}));
|
|
2182
2213
|
viewMap[t.name] = dvId;
|
|
2183
2214
|
}
|
|
2215
|
+
// Phase 2: Create the KN record
|
|
2184
2216
|
const knBody = JSON.stringify({
|
|
2185
2217
|
name: options.name,
|
|
2186
2218
|
branch: "main",
|
|
@@ -2193,20 +2225,25 @@ async function runKnCreateFromDsCommand(args) {
|
|
|
2193
2225
|
const knParsed = JSON.parse(knResponse);
|
|
2194
2226
|
const knItem = Array.isArray(knParsed) ? knParsed[0] : knParsed;
|
|
2195
2227
|
const knId = String(knItem?.id ?? "");
|
|
2228
|
+
console.error(`Knowledge network created: ${knId}`);
|
|
2229
|
+
// Phase 3: Create object types via REST API
|
|
2230
|
+
console.error(`Creating ${targetTables.length} object type(s) ...`);
|
|
2196
2231
|
const otResults = [];
|
|
2197
2232
|
for (const t of targetTables) {
|
|
2198
|
-
const pk = detectPrimaryKey(t);
|
|
2233
|
+
const pk = detectPrimaryKey(t, sampleRows?.[t.name]);
|
|
2199
2234
|
const dk = detectDisplayKey(t, pk);
|
|
2235
|
+
const uniqueProps = [pk, dk].filter((x, i, a) => a.indexOf(x) === i);
|
|
2200
2236
|
const entry = {
|
|
2201
2237
|
branch: "main",
|
|
2202
2238
|
name: t.name,
|
|
2203
2239
|
data_source: { type: "data_view", id: viewMap[t.name] },
|
|
2204
2240
|
primary_keys: [pk],
|
|
2205
2241
|
display_key: dk,
|
|
2206
|
-
data_properties:
|
|
2207
|
-
name:
|
|
2208
|
-
display_name:
|
|
2242
|
+
data_properties: t.columns.map((c) => ({
|
|
2243
|
+
name: c.name,
|
|
2244
|
+
display_name: c.name,
|
|
2209
2245
|
type: "string",
|
|
2246
|
+
mapped_field: { name: c.name, type: c.type || "varchar" },
|
|
2210
2247
|
})),
|
|
2211
2248
|
};
|
|
2212
2249
|
const otBody = JSON.stringify({ entries: [entry] });
|
|
@@ -2222,6 +2259,16 @@ async function runKnCreateFromDsCommand(args) {
|
|
|
2222
2259
|
id: otItem?.id ?? "",
|
|
2223
2260
|
field_count: t.columns.length,
|
|
2224
2261
|
});
|
|
2262
|
+
console.error(` Created: ${t.name} (${t.columns.length} fields, pk=${pk}, dk=${dk})`);
|
|
2263
|
+
}
|
|
2264
|
+
if (otResults.length === 0) {
|
|
2265
|
+
const errorOutput = {
|
|
2266
|
+
kn_id: knId,
|
|
2267
|
+
kn_name: options.name,
|
|
2268
|
+
error: "No object types were created",
|
|
2269
|
+
};
|
|
2270
|
+
console.log(JSON.stringify(errorOutput, null, options.pretty ? 2 : 0));
|
|
2271
|
+
return 1;
|
|
2225
2272
|
}
|
|
2226
2273
|
let statusStr = "skipped";
|
|
2227
2274
|
if (options.build) {
|
|
@@ -2749,3 +2796,149 @@ async function runKnSearchCommand(args) {
|
|
|
2749
2796
|
return 1;
|
|
2750
2797
|
}
|
|
2751
2798
|
}
|
|
2799
|
+
const KN_CREATE_FROM_CSV_HELP = `kweaver bkn create-from-csv <ds-id> --files <glob> --name X [options]
|
|
2800
|
+
|
|
2801
|
+
Import CSV files into datasource, then create a knowledge network.
|
|
2802
|
+
|
|
2803
|
+
Options:
|
|
2804
|
+
--files <s> CSV file paths (comma-separated or glob, required)
|
|
2805
|
+
--name <s> Knowledge network name (required)
|
|
2806
|
+
--table-prefix <s> Table name prefix (default: none)
|
|
2807
|
+
--batch-size <n> Rows per batch (default: 500)
|
|
2808
|
+
--tables <a,b> Tables to include in KN (default: all imported)
|
|
2809
|
+
--build (default) Build after creation
|
|
2810
|
+
--no-build Skip build
|
|
2811
|
+
--timeout <n> Build timeout in seconds (default: 300)
|
|
2812
|
+
-bd, --biz-domain Business domain (default: bd_public)`;
|
|
2813
|
+
function parseKnCreateFromCsvArgs(args) {
|
|
2814
|
+
let dsId = "";
|
|
2815
|
+
let files = "";
|
|
2816
|
+
let name = "";
|
|
2817
|
+
let tablePrefix = "";
|
|
2818
|
+
let batchSize = 500;
|
|
2819
|
+
let tablesStr = "";
|
|
2820
|
+
let build = true;
|
|
2821
|
+
let timeout = 300;
|
|
2822
|
+
let businessDomain = "";
|
|
2823
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
2824
|
+
const arg = args[i];
|
|
2825
|
+
if (arg === "--help" || arg === "-h")
|
|
2826
|
+
throw new Error("help");
|
|
2827
|
+
if (arg === "--files" && args[i + 1]) {
|
|
2828
|
+
files = args[++i];
|
|
2829
|
+
continue;
|
|
2830
|
+
}
|
|
2831
|
+
if (arg === "--name" && args[i + 1]) {
|
|
2832
|
+
name = args[++i];
|
|
2833
|
+
continue;
|
|
2834
|
+
}
|
|
2835
|
+
if (arg === "--table-prefix" && args[i + 1]) {
|
|
2836
|
+
tablePrefix = args[++i];
|
|
2837
|
+
continue;
|
|
2838
|
+
}
|
|
2839
|
+
if (arg === "--batch-size" && args[i + 1]) {
|
|
2840
|
+
batchSize = parseInt(args[++i], 10);
|
|
2841
|
+
if (Number.isNaN(batchSize) || batchSize < 1)
|
|
2842
|
+
batchSize = 500;
|
|
2843
|
+
continue;
|
|
2844
|
+
}
|
|
2845
|
+
if (arg === "--tables" && args[i + 1]) {
|
|
2846
|
+
tablesStr = args[++i];
|
|
2847
|
+
continue;
|
|
2848
|
+
}
|
|
2849
|
+
if (arg === "--build") {
|
|
2850
|
+
build = true;
|
|
2851
|
+
continue;
|
|
2852
|
+
}
|
|
2853
|
+
if (arg === "--no-build") {
|
|
2854
|
+
build = false;
|
|
2855
|
+
continue;
|
|
2856
|
+
}
|
|
2857
|
+
if (arg === "--timeout" && args[i + 1]) {
|
|
2858
|
+
timeout = parseInt(args[++i], 10);
|
|
2859
|
+
if (Number.isNaN(timeout) || timeout < 1)
|
|
2860
|
+
timeout = 300;
|
|
2861
|
+
continue;
|
|
2862
|
+
}
|
|
2863
|
+
if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
|
|
2864
|
+
businessDomain = args[++i];
|
|
2865
|
+
continue;
|
|
2866
|
+
}
|
|
2867
|
+
if (!arg.startsWith("-") && !dsId) {
|
|
2868
|
+
dsId = arg;
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
const tables = tablesStr ? tablesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
2872
|
+
if (!dsId || !files || !name) {
|
|
2873
|
+
throw new Error("Usage: kweaver bkn create-from-csv <ds-id> --files <glob> --name X [options]");
|
|
2874
|
+
}
|
|
2875
|
+
if (!businessDomain)
|
|
2876
|
+
businessDomain = resolveBusinessDomain();
|
|
2877
|
+
return { dsId, files, name, tablePrefix, batchSize, tables, build, timeout, businessDomain };
|
|
2878
|
+
}
|
|
2879
|
+
async function runKnCreateFromCsvCommand(args) {
|
|
2880
|
+
let options;
|
|
2881
|
+
try {
|
|
2882
|
+
options = parseKnCreateFromCsvArgs(args);
|
|
2883
|
+
}
|
|
2884
|
+
catch (error) {
|
|
2885
|
+
if (error instanceof Error && error.message === "help") {
|
|
2886
|
+
console.log(KN_CREATE_FROM_CSV_HELP);
|
|
2887
|
+
return 0;
|
|
2888
|
+
}
|
|
2889
|
+
console.error(formatHttpError(error));
|
|
2890
|
+
return 1;
|
|
2891
|
+
}
|
|
2892
|
+
// Phase 1: Import CSVs
|
|
2893
|
+
console.error("Phase 1: Importing CSVs ...");
|
|
2894
|
+
const importArgs = [
|
|
2895
|
+
options.dsId,
|
|
2896
|
+
"--files", options.files,
|
|
2897
|
+
"--table-prefix", options.tablePrefix,
|
|
2898
|
+
"--batch-size", String(options.batchSize),
|
|
2899
|
+
"-bd", options.businessDomain,
|
|
2900
|
+
];
|
|
2901
|
+
const importResult = await runDsImportCsv(importArgs);
|
|
2902
|
+
if (importResult.code !== 0) {
|
|
2903
|
+
console.error("CSV import failed — aborting KN creation");
|
|
2904
|
+
return importResult.code;
|
|
2905
|
+
}
|
|
2906
|
+
// Phase 1.5: Scan datasource metadata so platform discovers newly imported tables
|
|
2907
|
+
console.error("Scanning datasource metadata ...");
|
|
2908
|
+
try {
|
|
2909
|
+
const token = await ensureValidToken();
|
|
2910
|
+
const dsBody = await getDatasource({
|
|
2911
|
+
baseUrl: token.baseUrl,
|
|
2912
|
+
accessToken: token.accessToken,
|
|
2913
|
+
id: options.dsId,
|
|
2914
|
+
businessDomain: options.businessDomain,
|
|
2915
|
+
});
|
|
2916
|
+
const dsParsed = JSON.parse(dsBody);
|
|
2917
|
+
await scanMetadata({
|
|
2918
|
+
baseUrl: token.baseUrl,
|
|
2919
|
+
accessToken: token.accessToken,
|
|
2920
|
+
id: options.dsId,
|
|
2921
|
+
dsType: dsParsed.type ?? "mysql",
|
|
2922
|
+
businessDomain: options.businessDomain,
|
|
2923
|
+
});
|
|
2924
|
+
}
|
|
2925
|
+
catch (err) {
|
|
2926
|
+
console.error(`Scan warning (continuing): ${String(err)}`);
|
|
2927
|
+
}
|
|
2928
|
+
// Phase 2: Create KN from datasource
|
|
2929
|
+
console.error("Phase 2: Creating knowledge network ...");
|
|
2930
|
+
const tableNames = options.tables.length > 0 ? options.tables : importResult.tables;
|
|
2931
|
+
if (tableNames.length === 0) {
|
|
2932
|
+
console.error("No tables available for KN creation — aborting");
|
|
2933
|
+
return 1;
|
|
2934
|
+
}
|
|
2935
|
+
const knArgs = [
|
|
2936
|
+
options.dsId,
|
|
2937
|
+
"--name", options.name,
|
|
2938
|
+
"--tables", tableNames.join(","),
|
|
2939
|
+
options.build ? "--build" : "--no-build",
|
|
2940
|
+
"--timeout", String(options.timeout),
|
|
2941
|
+
"-bd", options.businessDomain,
|
|
2942
|
+
];
|
|
2943
|
+
return runKnCreateFromDsCommand(knArgs, importResult.sampleRows);
|
|
2944
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runDataviewCommand(args: string[]): Promise<number>;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/oauth.js";
|
|
3
|
+
import { deleteDataView, findDataView, getDataView, listDataViews, } from "../api/dataviews.js";
|
|
4
|
+
import { formatCallOutput } from "./call.js";
|
|
5
|
+
import { resolveBusinessDomain } from "../config/store.js";
|
|
6
|
+
function confirmYes(prompt) {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
9
|
+
rl.question(`${prompt} [y/N] `, (answer) => {
|
|
10
|
+
rl.close();
|
|
11
|
+
const trimmed = answer.trim().toLowerCase();
|
|
12
|
+
resolve(trimmed === "y" || trimmed === "yes");
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export async function runDataviewCommand(args) {
|
|
17
|
+
const [subcommand, ...rest] = args;
|
|
18
|
+
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
19
|
+
console.log(`kweaver dataview
|
|
20
|
+
|
|
21
|
+
Subcommands:
|
|
22
|
+
list [--datasource-id <id>] [--type <atomic|custom>] [--limit <n>] [-bd value] [--pretty]
|
|
23
|
+
find --name <name> [--exact] [--datasource-id <id>] [--wait] [--no-wait] [--timeout <ms>] [-bd value] [--pretty]
|
|
24
|
+
get <id> [-bd value] [--pretty]
|
|
25
|
+
delete <id> [-y] [-bd value]
|
|
26
|
+
|
|
27
|
+
list — list all data views (no keyword search)
|
|
28
|
+
find — search by name; default fuzzy, --exact for strict match, --wait to poll`);
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
const dispatch = () => {
|
|
32
|
+
if (subcommand === "list")
|
|
33
|
+
return runDataviewListCommand(rest);
|
|
34
|
+
if (subcommand === "find")
|
|
35
|
+
return runDataviewFindCommand(rest);
|
|
36
|
+
if (subcommand === "get")
|
|
37
|
+
return runDataviewGetCommand(rest);
|
|
38
|
+
if (subcommand === "delete")
|
|
39
|
+
return runDataviewDeleteCommand(rest);
|
|
40
|
+
return Promise.resolve(-1);
|
|
41
|
+
};
|
|
42
|
+
try {
|
|
43
|
+
return await with401RefreshRetry(async () => {
|
|
44
|
+
const code = await dispatch();
|
|
45
|
+
if (code === -1) {
|
|
46
|
+
console.error(`Unknown dataview subcommand: ${subcommand}`);
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
return code;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.error(formatHttpError(error));
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function parseDataviewCommonArgs(args) {
|
|
58
|
+
let businessDomain = "";
|
|
59
|
+
let pretty = true;
|
|
60
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
61
|
+
const arg = args[i];
|
|
62
|
+
if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
|
|
63
|
+
businessDomain = args[++i];
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (arg === "--pretty") {
|
|
67
|
+
pretty = true;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!businessDomain)
|
|
72
|
+
businessDomain = resolveBusinessDomain();
|
|
73
|
+
return { businessDomain, pretty };
|
|
74
|
+
}
|
|
75
|
+
async function runDataviewListCommand(args) {
|
|
76
|
+
let datasourceId;
|
|
77
|
+
let type;
|
|
78
|
+
let limit;
|
|
79
|
+
const { businessDomain, pretty } = parseDataviewCommonArgs(args);
|
|
80
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
81
|
+
const arg = args[i];
|
|
82
|
+
if (arg === "-bd" || arg === "--biz-domain") {
|
|
83
|
+
i += 1;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (arg === "--pretty")
|
|
87
|
+
continue;
|
|
88
|
+
if (arg === "--datasource-id" && args[i + 1]) {
|
|
89
|
+
datasourceId = args[++i];
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (arg === "--type" && args[i + 1]) {
|
|
93
|
+
type = args[++i];
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (arg === "--limit" && args[i + 1]) {
|
|
97
|
+
const n = Number.parseInt(args[++i], 10);
|
|
98
|
+
if (!Number.isNaN(n))
|
|
99
|
+
limit = n;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const token = await ensureValidToken();
|
|
104
|
+
const views = await listDataViews({
|
|
105
|
+
baseUrl: token.baseUrl,
|
|
106
|
+
accessToken: token.accessToken,
|
|
107
|
+
businessDomain,
|
|
108
|
+
datasourceId,
|
|
109
|
+
type,
|
|
110
|
+
limit,
|
|
111
|
+
});
|
|
112
|
+
console.log(formatCallOutput(JSON.stringify(views), pretty));
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
async function runDataviewFindCommand(args) {
|
|
116
|
+
let datasourceId;
|
|
117
|
+
let name;
|
|
118
|
+
let exact = false;
|
|
119
|
+
let wait = false;
|
|
120
|
+
let timeoutMs = 30_000;
|
|
121
|
+
const { businessDomain, pretty } = parseDataviewCommonArgs(args);
|
|
122
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
123
|
+
const arg = args[i];
|
|
124
|
+
if (arg === "-bd" || arg === "--biz-domain") {
|
|
125
|
+
i += 1;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (arg === "--pretty")
|
|
129
|
+
continue;
|
|
130
|
+
if (arg === "--datasource-id" && args[i + 1]) {
|
|
131
|
+
datasourceId = args[++i];
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (arg === "--name" && args[i + 1]) {
|
|
135
|
+
name = args[++i];
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (arg === "--exact") {
|
|
139
|
+
exact = true;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (arg === "--wait") {
|
|
143
|
+
wait = true;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (arg === "--no-wait") {
|
|
147
|
+
wait = false;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (arg === "--timeout" && args[i + 1]) {
|
|
151
|
+
timeoutMs = Number(args[++i]);
|
|
152
|
+
if (Number.isNaN(timeoutMs) || timeoutMs < 0) {
|
|
153
|
+
console.error("Invalid --timeout value");
|
|
154
|
+
return 1;
|
|
155
|
+
}
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!name) {
|
|
160
|
+
console.error("Usage: kweaver dataview find --name <name> [--exact] [--datasource-id <id>] [--wait] [--timeout <ms>] [-bd value] [--pretty]");
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
const token = await ensureValidToken();
|
|
164
|
+
const views = await findDataView({
|
|
165
|
+
baseUrl: token.baseUrl,
|
|
166
|
+
accessToken: token.accessToken,
|
|
167
|
+
businessDomain,
|
|
168
|
+
name,
|
|
169
|
+
datasourceId,
|
|
170
|
+
exact,
|
|
171
|
+
wait,
|
|
172
|
+
timeoutMs,
|
|
173
|
+
});
|
|
174
|
+
console.log(formatCallOutput(JSON.stringify(views), pretty));
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
async function runDataviewGetCommand(args) {
|
|
178
|
+
const { businessDomain, pretty } = parseDataviewCommonArgs(args);
|
|
179
|
+
let id = "";
|
|
180
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
181
|
+
const arg = args[i];
|
|
182
|
+
if (arg === "-bd" || arg === "--biz-domain") {
|
|
183
|
+
i += 1;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (arg === "--pretty")
|
|
187
|
+
continue;
|
|
188
|
+
if (!arg.startsWith("-")) {
|
|
189
|
+
id = arg;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (!id) {
|
|
194
|
+
console.error("Usage: kweaver dataview get <id> [-bd value] [--pretty]");
|
|
195
|
+
return 1;
|
|
196
|
+
}
|
|
197
|
+
const token = await ensureValidToken();
|
|
198
|
+
const view = await getDataView({
|
|
199
|
+
baseUrl: token.baseUrl,
|
|
200
|
+
accessToken: token.accessToken,
|
|
201
|
+
businessDomain,
|
|
202
|
+
id,
|
|
203
|
+
});
|
|
204
|
+
console.log(formatCallOutput(JSON.stringify(view), pretty));
|
|
205
|
+
return 0;
|
|
206
|
+
}
|
|
207
|
+
async function runDataviewDeleteCommand(args) {
|
|
208
|
+
let id = "";
|
|
209
|
+
let yes = false;
|
|
210
|
+
let businessDomain = "";
|
|
211
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
212
|
+
const arg = args[i];
|
|
213
|
+
if (arg === "--yes" || arg === "-y")
|
|
214
|
+
yes = true;
|
|
215
|
+
else if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
|
|
216
|
+
businessDomain = args[++i];
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
else if (!arg.startsWith("-"))
|
|
220
|
+
id = arg;
|
|
221
|
+
}
|
|
222
|
+
if (!businessDomain)
|
|
223
|
+
businessDomain = resolveBusinessDomain();
|
|
224
|
+
if (!id) {
|
|
225
|
+
console.error("Usage: kweaver dataview delete <id> [-y] [-bd value]");
|
|
226
|
+
return 1;
|
|
227
|
+
}
|
|
228
|
+
if (!yes) {
|
|
229
|
+
const confirmed = await confirmYes("Are you sure you want to delete this data view?");
|
|
230
|
+
if (!confirmed) {
|
|
231
|
+
console.error("Aborted.");
|
|
232
|
+
return 1;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const token = await ensureValidToken();
|
|
236
|
+
await deleteDataView({
|
|
237
|
+
baseUrl: token.baseUrl,
|
|
238
|
+
accessToken: token.accessToken,
|
|
239
|
+
businessDomain,
|
|
240
|
+
id,
|
|
241
|
+
});
|
|
242
|
+
console.error(`Deleted ${id}`);
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
package/dist/commands/ds.d.ts
CHANGED
|
@@ -5,3 +5,19 @@ export declare function parseDsListArgs(args: string[]): {
|
|
|
5
5
|
businessDomain: string;
|
|
6
6
|
pretty: boolean;
|
|
7
7
|
};
|
|
8
|
+
export declare function parseImportCsvArgs(args: string[]): {
|
|
9
|
+
datasourceId: string;
|
|
10
|
+
files: string;
|
|
11
|
+
tablePrefix: string;
|
|
12
|
+
batchSize: number;
|
|
13
|
+
businessDomain: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function resolveFiles(pattern: string): Promise<string[]>;
|
|
16
|
+
export interface ImportCsvResult {
|
|
17
|
+
code: number;
|
|
18
|
+
tables: string[];
|
|
19
|
+
tableColumns: Record<string, string[]>;
|
|
20
|
+
sampleRows: Record<string, Array<Record<string, string | null>>>;
|
|
21
|
+
}
|
|
22
|
+
export declare function runDsImportCsv(args: string[]): Promise<ImportCsvResult>;
|
|
23
|
+
export declare function runDsImportCsvCommand(args: string[]): Promise<number>;
|