@kweaver-ai/kweaver-sdk 0.8.3 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/agent-chat.d.ts +10 -2
- package/dist/api/agent-chat.js +19 -5
- package/dist/api/datasources.d.ts +14 -0
- package/dist/api/datasources.js +14 -0
- package/dist/cli.js +2 -14
- package/dist/client.d.ts +7 -1
- package/dist/client.js +7 -1
- package/dist/commands/bkn-ops.d.ts +1 -1
- package/dist/commands/bkn-ops.js +42 -21
- package/dist/commands/bkn.js +6 -3
- package/dist/commands/ds.d.ts +0 -31
- package/dist/commands/ds.js +18 -448
- package/dist/commands/explore-bkn.d.ts +7 -1
- package/dist/commands/explore-bkn.js +32 -3
- package/dist/resources/datasources.d.ts +7 -0
- package/dist/resources/datasources.js +7 -0
- package/dist/templates/explorer/bkn.js +860 -9
- package/dist/templates/explorer/index.html +1 -0
- package/dist/templates/explorer/style.css +225 -0
- package/dist/templates/explorer/vendor/g6.min.js +68 -0
- package/dist/trace-ai/eval-set/schemas.d.ts +1 -0
- package/dist/trace-ai/eval-set/schemas.js +4 -0
- package/dist/trace-ai/eval-set/types.d.ts +2 -0
- package/dist/trace-ai/exp/capture-fingerprint.d.ts +10 -0
- package/dist/trace-ai/exp/capture-fingerprint.js +12 -0
- package/dist/trace-ai/exp/context/context-assembler.d.ts +18 -0
- package/dist/trace-ai/exp/context/context-assembler.js +42 -0
- package/dist/trace-ai/exp/context/failure-analyzer.d.ts +22 -0
- package/dist/trace-ai/exp/context/failure-analyzer.js +59 -0
- package/dist/trace-ai/exp/context/kn-data-prober.d.ts +13 -0
- package/dist/trace-ai/exp/context/kn-data-prober.js +38 -0
- package/dist/trace-ai/exp/context/kn-schema-client.d.ts +14 -0
- package/dist/trace-ai/exp/context/kn-schema-client.js +41 -0
- package/dist/trace-ai/exp/context/retrieval-health.d.ts +32 -0
- package/dist/trace-ai/exp/context/retrieval-health.js +138 -0
- package/dist/trace-ai/exp/context/vega-catalog-client.d.ts +14 -0
- package/dist/trace-ai/exp/context/vega-catalog-client.js +15 -0
- package/dist/trace-ai/exp/coordinator.d.ts +34 -21
- package/dist/trace-ai/exp/coordinator.js +246 -24
- package/dist/trace-ai/exp/eval-runner.js +4 -2
- package/dist/trace-ai/exp/exp-store/events-jsonl.d.ts +1 -0
- package/dist/trace-ai/exp/exp-store/events-jsonl.js +18 -0
- package/dist/trace-ai/exp/exp-store/expected-fingerprint.d.ts +3 -0
- package/dist/trace-ai/exp/exp-store/expected-fingerprint.js +31 -0
- package/dist/trace-ai/exp/exp-store/index.d.ts +63 -2
- package/dist/trace-ai/exp/exp-store/index.js +2 -1
- package/dist/trace-ai/exp/exp-store/rollback-yaml.d.ts +12 -0
- package/dist/trace-ai/exp/exp-store/rollback-yaml.js +29 -0
- package/dist/trace-ai/exp/index.d.ts +2 -0
- package/dist/trace-ai/exp/index.js +68 -3
- package/dist/trace-ai/exp/info.js +1 -1
- package/dist/trace-ai/exp/patch/index.d.ts +13 -2
- package/dist/trace-ai/exp/patch/index.js +65 -10
- package/dist/trace-ai/exp/patch/kn-api-client.d.ts +40 -0
- package/dist/trace-ai/exp/patch/kn-api-client.js +14 -0
- package/dist/trace-ai/exp/patch/kn.d.ts +8 -0
- package/dist/trace-ai/exp/patch/kn.js +36 -0
- package/dist/trace-ai/exp/patch/skill-api-client.d.ts +17 -0
- package/dist/trace-ai/exp/patch/skill-api-client.js +14 -0
- package/dist/trace-ai/exp/patch/skill-content.d.ts +9 -0
- package/dist/trace-ai/exp/patch/skill-content.js +12 -0
- package/dist/trace-ai/exp/preflight.d.ts +77 -0
- package/dist/trace-ai/exp/preflight.js +148 -0
- package/dist/trace-ai/exp/providers/synthesizer-client.d.ts +3 -14
- package/dist/trace-ai/exp/providers/synthesizer-client.js +53 -35
- package/dist/trace-ai/exp/providers/triage-client.d.ts +15 -2
- package/dist/trace-ai/exp/providers/triage-client.js +143 -28
- package/dist/trace-ai/exp/run-preflight.d.ts +19 -0
- package/dist/trace-ai/exp/run-preflight.js +56 -0
- package/dist/trace-ai/exp/schemas.d.ts +402 -44
- package/dist/trace-ai/exp/schemas.js +131 -18
- package/dist/utils/deprecation.d.ts +1 -0
- package/dist/utils/deprecation.js +18 -0
- package/package.json +2 -1
package/dist/commands/ds.js
CHANGED
|
@@ -1,443 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
// Internal CSV-import helpers — formerly the `kweaver ds` command. The user-facing
|
|
2
|
+
// `ds` command group has been removed; these helpers are now consumed by
|
|
3
|
+
// `kweaver bkn create-from-csv` and by the import-csv tests.
|
|
4
|
+
//
|
|
5
|
+
// The flow still talks to the legacy `/datasources` backend endpoints. SDK-level
|
|
6
|
+
// shims for those endpoints remain available for users on older backends; see
|
|
7
|
+
// `api/datasources.ts` and `resources/datasources.ts` (both @deprecated).
|
|
2
8
|
import { statSync } from "node:fs";
|
|
3
9
|
import { glob } from "node:fs/promises";
|
|
4
10
|
import { resolve as resolvePath } from "node:path";
|
|
5
|
-
import { ensureValidToken, formatHttpError
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { formatCallOutput } from "./call.js";
|
|
11
|
+
import { ensureValidToken, formatHttpError } from "../auth/oauth.js";
|
|
12
|
+
import { getDatasource } from "../api/datasources.js";
|
|
13
|
+
import { scanMetadata } from "../api/vega.js";
|
|
9
14
|
import { resolveBusinessDomain } from "../config/store.js";
|
|
10
|
-
import { assertVegaCatalogId } from "./bkn-utils.js";
|
|
11
15
|
import { parseCsvFile, buildTableName, splitBatches, buildFieldMappings, buildDagBody, } from "./import-csv.js";
|
|
12
16
|
import { executeDataflow } from "../api/dataflow.js";
|
|
13
|
-
import { renderHelp } from "../help/format.js";
|
|
14
|
-
const DS_HELP = renderHelp({
|
|
15
|
-
tagline: "Manage datasources — list, get, delete, tables, connect, import-csv",
|
|
16
|
-
usage: "kweaver ds <subcommand> [flags]",
|
|
17
|
-
sections: [
|
|
18
|
-
{
|
|
19
|
-
title: "AVAILABLE COMMANDS",
|
|
20
|
-
items: [
|
|
21
|
-
{ name: "list", desc: "List datasources" },
|
|
22
|
-
{ name: "get", desc: "Get datasource details" },
|
|
23
|
-
{ name: "delete", desc: "Delete a datasource" },
|
|
24
|
-
{ name: "tables", desc: "List tables with columns" },
|
|
25
|
-
{ name: "connect", desc: "Test, register and discover; reuses by (type,host,port,db,account) unless --force-new" },
|
|
26
|
-
{ name: "import-csv", desc: "Import CSV files into datasource tables via dataflow API" },
|
|
27
|
-
],
|
|
28
|
-
},
|
|
29
|
-
],
|
|
30
|
-
inheritedFlags: "--base-url, --token, --user, --help",
|
|
31
|
-
examples: [
|
|
32
|
-
"kweaver ds list --keyword mysql",
|
|
33
|
-
"kweaver ds connect mysql 127.0.0.1 3306 mydb --account root --password ******",
|
|
34
|
-
"kweaver ds tables <id> --keyword users",
|
|
35
|
-
],
|
|
36
|
-
learnMore: ["Use `kweaver ds <subcommand> --help` for flag details"],
|
|
37
|
-
});
|
|
38
|
-
function confirmYes(prompt) {
|
|
39
|
-
return new Promise((resolve) => {
|
|
40
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
41
|
-
rl.question(`${prompt} [y/N] `, (answer) => {
|
|
42
|
-
rl.close();
|
|
43
|
-
const trimmed = answer.trim().toLowerCase();
|
|
44
|
-
resolve(trimmed === "y" || trimmed === "yes");
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
function extractDatasourceId(body) {
|
|
49
|
-
const parsed = JSON.parse(body);
|
|
50
|
-
const item = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
51
|
-
if (!item || typeof item !== "object")
|
|
52
|
-
return "";
|
|
53
|
-
const id = item.id ?? item.ds_id;
|
|
54
|
-
return id != null ? String(id) : "";
|
|
55
|
-
}
|
|
56
|
-
export async function runDsCommand(args) {
|
|
57
|
-
const [subcommand, ...rest] = args;
|
|
58
|
-
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
59
|
-
console.log(DS_HELP);
|
|
60
|
-
return 0;
|
|
61
|
-
}
|
|
62
|
-
const dispatch = () => {
|
|
63
|
-
if (subcommand === "list")
|
|
64
|
-
return runDsListCommand(rest);
|
|
65
|
-
if (subcommand === "get")
|
|
66
|
-
return runDsGetCommand(rest);
|
|
67
|
-
if (subcommand === "delete")
|
|
68
|
-
return runDsDeleteCommand(rest);
|
|
69
|
-
if (subcommand === "tables")
|
|
70
|
-
return runDsTablesCommand(rest);
|
|
71
|
-
if (subcommand === "connect")
|
|
72
|
-
return runDsConnectCommand(rest);
|
|
73
|
-
if (subcommand === "import-csv")
|
|
74
|
-
return runDsImportCsvCommand(rest);
|
|
75
|
-
return Promise.resolve(-1);
|
|
76
|
-
};
|
|
77
|
-
try {
|
|
78
|
-
return await with401RefreshRetry(async () => {
|
|
79
|
-
const code = await dispatch();
|
|
80
|
-
if (code === -1) {
|
|
81
|
-
console.error(`Unknown ds subcommand: ${subcommand}`);
|
|
82
|
-
return 1;
|
|
83
|
-
}
|
|
84
|
-
return code;
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
catch (error) {
|
|
88
|
-
console.error(formatHttpError(error));
|
|
89
|
-
return 1;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
export function parseDsListArgs(args) {
|
|
93
|
-
let keyword;
|
|
94
|
-
let type;
|
|
95
|
-
let businessDomain = "";
|
|
96
|
-
let pretty = true;
|
|
97
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
98
|
-
const arg = args[i];
|
|
99
|
-
if (arg === "--help" || arg === "-h")
|
|
100
|
-
throw new Error("help");
|
|
101
|
-
if (arg === "--keyword" && args[i + 1]) {
|
|
102
|
-
keyword = args[++i];
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
if (arg === "--type" && args[i + 1]) {
|
|
106
|
-
type = args[++i];
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if ((arg === "-bd" || arg === "--biz-domain") && args[i + 1]) {
|
|
110
|
-
businessDomain = args[++i];
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (arg === "--pretty") {
|
|
114
|
-
pretty = true;
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
if (!businessDomain)
|
|
119
|
-
businessDomain = resolveBusinessDomain();
|
|
120
|
-
return { keyword, type, businessDomain, pretty };
|
|
121
|
-
}
|
|
122
|
-
async function runDsListCommand(args) {
|
|
123
|
-
try {
|
|
124
|
-
const opts = parseDsListArgs(args);
|
|
125
|
-
const token = await ensureValidToken();
|
|
126
|
-
const body = await listDatasources({
|
|
127
|
-
baseUrl: token.baseUrl,
|
|
128
|
-
accessToken: token.accessToken,
|
|
129
|
-
keyword: opts.keyword,
|
|
130
|
-
type: opts.type,
|
|
131
|
-
businessDomain: opts.businessDomain,
|
|
132
|
-
});
|
|
133
|
-
console.log(formatCallOutput(body, opts.pretty));
|
|
134
|
-
return 0;
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
if (error instanceof Error && error.message === "help") {
|
|
138
|
-
console.log(renderHelp({
|
|
139
|
-
tagline: "List datasources",
|
|
140
|
-
usage: "kweaver ds list [options]",
|
|
141
|
-
flags: [
|
|
142
|
-
{ name: "--keyword <s>", desc: "Filter by keyword" },
|
|
143
|
-
{ name: "--type <s>", desc: "Filter by database type" },
|
|
144
|
-
{ name: "-bd, --biz-domain", desc: "Business domain (default: bd_public)" },
|
|
145
|
-
{ name: "--pretty", desc: "Pretty-print JSON (default)" },
|
|
146
|
-
],
|
|
147
|
-
inheritedFlags: "--base-url, --token, --user, --help",
|
|
148
|
-
}));
|
|
149
|
-
return 0;
|
|
150
|
-
}
|
|
151
|
-
throw error;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
async function runDsGetCommand(args) {
|
|
155
|
-
const id = args.find((a) => !a.startsWith("-"));
|
|
156
|
-
if (!id) {
|
|
157
|
-
console.error("Usage: kweaver ds get <id>");
|
|
158
|
-
return 1;
|
|
159
|
-
}
|
|
160
|
-
const token = await ensureValidToken();
|
|
161
|
-
const body = await getDatasource({
|
|
162
|
-
baseUrl: token.baseUrl,
|
|
163
|
-
accessToken: token.accessToken,
|
|
164
|
-
id,
|
|
165
|
-
});
|
|
166
|
-
console.log(formatCallOutput(body, true));
|
|
167
|
-
return 0;
|
|
168
|
-
}
|
|
169
|
-
async function runDsDeleteCommand(args) {
|
|
170
|
-
let id = "";
|
|
171
|
-
let yes = false;
|
|
172
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
173
|
-
const arg = args[i];
|
|
174
|
-
if (arg === "--yes" || arg === "-y")
|
|
175
|
-
yes = true;
|
|
176
|
-
else if (!arg.startsWith("-"))
|
|
177
|
-
id = arg;
|
|
178
|
-
}
|
|
179
|
-
if (!id) {
|
|
180
|
-
console.error("Usage: kweaver ds delete <id> [-y]");
|
|
181
|
-
return 1;
|
|
182
|
-
}
|
|
183
|
-
if (!yes) {
|
|
184
|
-
const confirmed = await confirmYes("Are you sure you want to delete this datasource?");
|
|
185
|
-
if (!confirmed) {
|
|
186
|
-
console.error("Aborted.");
|
|
187
|
-
return 1;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
const token = await ensureValidToken();
|
|
191
|
-
await deleteDatasource({
|
|
192
|
-
baseUrl: token.baseUrl,
|
|
193
|
-
accessToken: token.accessToken,
|
|
194
|
-
id,
|
|
195
|
-
});
|
|
196
|
-
console.error(`Deleted ${id}`);
|
|
197
|
-
return 0;
|
|
198
|
-
}
|
|
199
|
-
async function runDsTablesCommand(args) {
|
|
200
|
-
let id = "";
|
|
201
|
-
let keyword;
|
|
202
|
-
let pretty = true;
|
|
203
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
204
|
-
const arg = args[i];
|
|
205
|
-
if (arg === "--keyword" && args[i + 1]) {
|
|
206
|
-
keyword = args[++i];
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
if (arg === "--pretty") {
|
|
210
|
-
pretty = true;
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
if (!arg.startsWith("-"))
|
|
214
|
-
id = arg;
|
|
215
|
-
}
|
|
216
|
-
if (!id) {
|
|
217
|
-
console.error("Usage: kweaver ds tables <vega-catalog-id> [--keyword X]\n" +
|
|
218
|
-
" <vega-catalog-id> is a vega catalog id (use `kweaver vega catalog list` to find one;\n" +
|
|
219
|
-
" legacy data-connection datasource UUIDs are no longer accepted).");
|
|
220
|
-
return 1;
|
|
221
|
-
}
|
|
222
|
-
assertVegaCatalogId(id);
|
|
223
|
-
const token = await ensureValidToken();
|
|
224
|
-
const body = await listTablesWithColumns({
|
|
225
|
-
baseUrl: token.baseUrl,
|
|
226
|
-
accessToken: token.accessToken,
|
|
227
|
-
id,
|
|
228
|
-
keyword,
|
|
229
|
-
});
|
|
230
|
-
console.log(formatCallOutput(body, pretty));
|
|
231
|
-
return 0;
|
|
232
|
-
}
|
|
233
|
-
export function findExistingDatasource(listBody, sig) {
|
|
234
|
-
const parsed = JSON.parse(listBody);
|
|
235
|
-
const entries = Array.isArray(parsed) ? parsed : (parsed.entries ?? []);
|
|
236
|
-
const tupleMatch = entries.find((e) => e.id &&
|
|
237
|
-
e.type === sig.type &&
|
|
238
|
-
e.bin_data?.host === sig.host &&
|
|
239
|
-
Number(e.bin_data?.port) === Number(sig.port) &&
|
|
240
|
-
e.bin_data?.database_name === sig.database &&
|
|
241
|
-
e.bin_data?.account === sig.account);
|
|
242
|
-
if (tupleMatch) {
|
|
243
|
-
return {
|
|
244
|
-
id: String(tupleMatch.id),
|
|
245
|
-
name: String(tupleMatch.name ?? ""),
|
|
246
|
-
matchedByName: tupleMatch.name === sig.name,
|
|
247
|
-
matchedByTuple: true,
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
if (sig.name) {
|
|
251
|
-
const nameMatch = entries.find((e) => e.id && e.name === sig.name);
|
|
252
|
-
if (nameMatch) {
|
|
253
|
-
return {
|
|
254
|
-
id: String(nameMatch.id),
|
|
255
|
-
name: String(nameMatch.name),
|
|
256
|
-
matchedByName: true,
|
|
257
|
-
matchedByTuple: false,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
return undefined;
|
|
262
|
-
}
|
|
263
|
-
export function findDatasourceIdByName(listBody, name) {
|
|
264
|
-
const parsed = JSON.parse(listBody);
|
|
265
|
-
const entries = Array.isArray(parsed) ? parsed : (parsed.entries ?? []);
|
|
266
|
-
const hit = entries.find((e) => e.id && e.name === name);
|
|
267
|
-
return hit?.id ? String(hit.id) : undefined;
|
|
268
|
-
}
|
|
269
|
-
function isDuplicateNameError(err) {
|
|
270
|
-
if (!err || typeof err !== "object")
|
|
271
|
-
return false;
|
|
272
|
-
// HttpError.message is just "HTTP 400 ..."; the description lives in body.
|
|
273
|
-
const status = "status" in err ? Number(err.status) : NaN;
|
|
274
|
-
const body = "body" in err ? String(err.body) : "";
|
|
275
|
-
if (status !== 400)
|
|
276
|
-
return false;
|
|
277
|
-
return /数据源名称已存在|datasource name.*exist|already exists/i.test(body);
|
|
278
|
-
}
|
|
279
|
-
async function runDsConnectCommand(args) {
|
|
280
|
-
let dbType = "";
|
|
281
|
-
let host = "";
|
|
282
|
-
let port = 0;
|
|
283
|
-
let database = "";
|
|
284
|
-
let account = "";
|
|
285
|
-
let password = "";
|
|
286
|
-
let schema;
|
|
287
|
-
let name;
|
|
288
|
-
let forceNew = false;
|
|
289
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
290
|
-
const arg = args[i];
|
|
291
|
-
if (arg === "--account" && args[i + 1]) {
|
|
292
|
-
account = args[++i];
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
if (arg === "--password" && args[i + 1]) {
|
|
296
|
-
password = args[++i];
|
|
297
|
-
continue;
|
|
298
|
-
}
|
|
299
|
-
if (arg === "--schema" && args[i + 1]) {
|
|
300
|
-
schema = args[++i];
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
if (arg === "--name" && args[i + 1]) {
|
|
304
|
-
name = args[++i];
|
|
305
|
-
continue;
|
|
306
|
-
}
|
|
307
|
-
if (arg === "--force-new") {
|
|
308
|
-
forceNew = true;
|
|
309
|
-
continue;
|
|
310
|
-
}
|
|
311
|
-
if (arg === "--reuse-existing") {
|
|
312
|
-
forceNew = false;
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
if (!arg.startsWith("-")) {
|
|
316
|
-
if (!dbType)
|
|
317
|
-
dbType = arg;
|
|
318
|
-
else if (!host)
|
|
319
|
-
host = arg;
|
|
320
|
-
else if (port === 0)
|
|
321
|
-
port = parseInt(arg, 10);
|
|
322
|
-
else if (!database)
|
|
323
|
-
database = arg;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
if (!dbType || !host || !database || !account || !password) {
|
|
327
|
-
console.error("Usage: kweaver ds connect <db_type> <host> <port> <database> --account X --password Y [--schema Z] [--name N] [--reuse-existing|--force-new]");
|
|
328
|
-
return 1;
|
|
329
|
-
}
|
|
330
|
-
if (Number.isNaN(port) || port < 1) {
|
|
331
|
-
console.error("Invalid port");
|
|
332
|
-
return 1;
|
|
333
|
-
}
|
|
334
|
-
const token = await ensureValidToken();
|
|
335
|
-
const base = { baseUrl: token.baseUrl, accessToken: token.accessToken };
|
|
336
|
-
const dsName = name ?? database;
|
|
337
|
-
// Pre-flight dedup: connection-tuple match is the silent-orphan vector.
|
|
338
|
-
// Backend already rejects duplicate names with 400, but won't notice
|
|
339
|
-
// tuple collisions, so we own that check.
|
|
340
|
-
if (!forceNew) {
|
|
341
|
-
const listBody = await listDatasources({ ...base });
|
|
342
|
-
const hit = findExistingDatasource(listBody, {
|
|
343
|
-
type: dbType,
|
|
344
|
-
host,
|
|
345
|
-
port,
|
|
346
|
-
database,
|
|
347
|
-
account,
|
|
348
|
-
name: dsName,
|
|
349
|
-
});
|
|
350
|
-
if (hit) {
|
|
351
|
-
const why = hit.matchedByTuple
|
|
352
|
-
? "matched by (type,host,port,database,account)"
|
|
353
|
-
: "matched by --name";
|
|
354
|
-
console.error(`Reusing existing datasource ${hit.id} (${hit.name}); ${why}. Use --force-new to override.`);
|
|
355
|
-
return printDsConnectOutput(base, hit.id);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
console.error("Testing connectivity ...");
|
|
359
|
-
await testDatasource({
|
|
360
|
-
...base,
|
|
361
|
-
type: dbType,
|
|
362
|
-
host,
|
|
363
|
-
port,
|
|
364
|
-
database,
|
|
365
|
-
account,
|
|
366
|
-
password,
|
|
367
|
-
schema,
|
|
368
|
-
});
|
|
369
|
-
let dsId = "";
|
|
370
|
-
try {
|
|
371
|
-
const createBody = await createDatasource({
|
|
372
|
-
...base,
|
|
373
|
-
name: dsName,
|
|
374
|
-
type: dbType,
|
|
375
|
-
host,
|
|
376
|
-
port,
|
|
377
|
-
database,
|
|
378
|
-
account,
|
|
379
|
-
password,
|
|
380
|
-
schema,
|
|
381
|
-
});
|
|
382
|
-
dsId = extractDatasourceId(createBody);
|
|
383
|
-
}
|
|
384
|
-
catch (err) {
|
|
385
|
-
// Backend checks name uniqueness but not tuple. If we raced another caller
|
|
386
|
-
// (or tuple match got disabled by --force-new and the name still collides),
|
|
387
|
-
// turn the raw 400 into a useful pointer to the existing id.
|
|
388
|
-
if (isDuplicateNameError(err)) {
|
|
389
|
-
// Backend rejected the name; look it up specifically (not by tuple —
|
|
390
|
-
// sibling ds sharing the same connection would mislead the pointer).
|
|
391
|
-
const listBody = await listDatasources({ ...base });
|
|
392
|
-
const existingId = findDatasourceIdByName(listBody, dsName);
|
|
393
|
-
if (existingId) {
|
|
394
|
-
console.error(`Datasource name '${dsName}' already exists as ${existingId}. Re-run without --force-new to reuse it, or pick a different --name.`);
|
|
395
|
-
return 1;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
throw err;
|
|
399
|
-
}
|
|
400
|
-
if (!dsId) {
|
|
401
|
-
console.error("Failed to get datasource ID from create response");
|
|
402
|
-
return 1;
|
|
403
|
-
}
|
|
404
|
-
return printDsConnectOutput(base, dsId);
|
|
405
|
-
}
|
|
406
|
-
async function printDsConnectOutput(base, dsId) {
|
|
407
|
-
const tablesBody = await listTablesWithColumns({ ...base, id: dsId });
|
|
408
|
-
const tables = JSON.parse(tablesBody);
|
|
409
|
-
const output = {
|
|
410
|
-
datasource_id: dsId,
|
|
411
|
-
tables: tables.map((t) => ({
|
|
412
|
-
name: t.name,
|
|
413
|
-
...(t.primaryKeys && t.primaryKeys.length > 0 ? { primary_keys: t.primaryKeys } : {}),
|
|
414
|
-
columns: t.columns.map((c) => ({
|
|
415
|
-
name: c.name,
|
|
416
|
-
type: c.type,
|
|
417
|
-
comment: c.comment,
|
|
418
|
-
...(c.isPrimaryKey ? { is_primary_key: true } : {}),
|
|
419
|
-
})),
|
|
420
|
-
})),
|
|
421
|
-
};
|
|
422
|
-
console.log(JSON.stringify(output, null, 2));
|
|
423
|
-
return 0;
|
|
424
|
-
}
|
|
425
|
-
// ── import-csv ────────────────────────────────────────────────────────────────
|
|
426
|
-
const IMPORT_CSV_HELP = renderHelp({
|
|
427
|
-
tagline: "Import CSV files into datasource tables via dataflow API",
|
|
428
|
-
usage: "kweaver ds import-csv <ds-id> --files <glob_or_list> [flags]",
|
|
429
|
-
flags: [
|
|
430
|
-
{ name: "--files <s>", desc: "CSV file paths — comma-separated or glob pattern (required)" },
|
|
431
|
-
{ name: "--table-prefix <s>", desc: "Table name prefix (default: none)" },
|
|
432
|
-
{ name: "--batch-size <n>", desc: "Rows per batch (default: 500, range: 1-10000)" },
|
|
433
|
-
{ name: "-bd, --biz-domain <s>", desc: "Business domain (default: bd_public)" },
|
|
434
|
-
],
|
|
435
|
-
inheritedFlags: "--base-url, --token, --user, --help",
|
|
436
|
-
examples: [
|
|
437
|
-
"kweaver ds import-csv ds-123 --files './data/*.csv'",
|
|
438
|
-
"kweaver ds import-csv ds-123 --files ./a.csv,./b.csv --table-prefix raw_ --batch-size 1000",
|
|
439
|
-
],
|
|
440
|
-
});
|
|
441
17
|
export function parseImportCsvArgs(args) {
|
|
442
18
|
let datasourceId = "";
|
|
443
19
|
let files = "";
|
|
@@ -508,13 +84,15 @@ export async function runDsImportCsv(args) {
|
|
|
508
84
|
}
|
|
509
85
|
catch (error) {
|
|
510
86
|
if (error instanceof Error && error.message === "help") {
|
|
511
|
-
|
|
87
|
+
// The user-facing `ds import-csv` CLI is gone; this path is only reachable
|
|
88
|
+
// if a caller explicitly passes --help into the helper. Print a redirect.
|
|
89
|
+
console.error("CSV import is now an internal helper. Use `kweaver bkn create-from-csv` instead.");
|
|
512
90
|
return { code: 0, tables: [], failed: [], tableColumns: {}, sampleRows: {} };
|
|
513
91
|
}
|
|
514
92
|
throw error;
|
|
515
93
|
}
|
|
516
94
|
if (!options.datasourceId) {
|
|
517
|
-
console.error("
|
|
95
|
+
console.error("runDsImportCsv: missing datasource/catalog id (positional argument required)");
|
|
518
96
|
return { code: 1, tables: [], failed: [], tableColumns: {}, sampleRows: {} };
|
|
519
97
|
}
|
|
520
98
|
if (!options.files) {
|
|
@@ -611,9 +189,10 @@ export async function runDsImportCsv(args) {
|
|
|
611
189
|
if (failed.length > 0) {
|
|
612
190
|
console.error(`Failed tables: ${failed.join(", ")}`);
|
|
613
191
|
}
|
|
614
|
-
// Refresh the platform metadata catalog so the freshly imported tables
|
|
615
|
-
//
|
|
616
|
-
// Best-effort: scan failures
|
|
192
|
+
// Refresh the platform metadata catalog so the freshly imported tables are
|
|
193
|
+
// visible to downstream commands (e.g. `bkn create-from-catalog`) without
|
|
194
|
+
// requiring a manual scan. Best-effort: scan failures don't mask a
|
|
195
|
+
// successful import.
|
|
617
196
|
if (succeeded.length > 0) {
|
|
618
197
|
process.stderr.write("Scanning datasource metadata ...\n");
|
|
619
198
|
try {
|
|
@@ -630,12 +209,3 @@ export async function runDsImportCsv(args) {
|
|
|
630
209
|
}
|
|
631
210
|
return { code: failed.length > 0 ? 1 : 0, tables: succeeded, failed, tableColumns, sampleRows };
|
|
632
211
|
}
|
|
633
|
-
export async function runDsImportCsvCommand(args) {
|
|
634
|
-
const result = await runDsImportCsv(args);
|
|
635
|
-
console.log(JSON.stringify({
|
|
636
|
-
tables: result.tables,
|
|
637
|
-
failed: result.failed,
|
|
638
|
-
summary: { succeeded: result.tables.length, failed: result.failed.length },
|
|
639
|
-
}, null, 2));
|
|
640
|
-
return result.code;
|
|
641
|
-
}
|
|
@@ -30,6 +30,12 @@ export interface ExploreMeta {
|
|
|
30
30
|
id: string;
|
|
31
31
|
name: string;
|
|
32
32
|
}>;
|
|
33
|
+
conceptGroups: Array<{
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
color?: string;
|
|
37
|
+
objectTypeIds: string[];
|
|
38
|
+
}>;
|
|
33
39
|
}
|
|
34
40
|
export interface ExploreOt {
|
|
35
41
|
id: string;
|
|
@@ -63,7 +69,7 @@ export interface ExploreStats {
|
|
|
63
69
|
}
|
|
64
70
|
export declare const EXPLORE_BOOTSTRAP_RETRY_DELAY_MS = 300;
|
|
65
71
|
export declare const EXPLORE_BOOTSTRAP_MAX_ATTEMPTS = 2;
|
|
66
|
-
export declare function buildMeta(knRaw: string, otRaw: string, rtRaw: string, atRaw: string): ExploreMeta;
|
|
72
|
+
export declare function buildMeta(knRaw: string, otRaw: string, rtRaw: string, atRaw: string, cgRaw?: string): ExploreMeta;
|
|
67
73
|
export declare function isRetryableExploreBootstrapError(error: unknown): boolean;
|
|
68
74
|
export declare function loadExploreMetaWithRetry(token: {
|
|
69
75
|
baseUrl: string;
|
|
@@ -2,11 +2,12 @@ import { HttpError } from "../utils/http.js";
|
|
|
2
2
|
import { getKnowledgeNetwork, listObjectTypes, listRelationTypes, listActionTypes, } from "../api/knowledge-networks.js";
|
|
3
3
|
import { objectTypeQuery, objectTypeProperties, subgraph } from "../api/ontology-query.js";
|
|
4
4
|
import { semanticSearch } from "../api/semantic-search.js";
|
|
5
|
+
import { listConceptGroups } from "../api/bkn-backend.js";
|
|
5
6
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
6
7
|
export const EXPLORE_BOOTSTRAP_RETRY_DELAY_MS = 300;
|
|
7
8
|
export const EXPLORE_BOOTSTRAP_MAX_ATTEMPTS = 2;
|
|
8
9
|
// ── Meta builder ────────────────────────────────────────────────────────────
|
|
9
|
-
export function buildMeta(knRaw, otRaw, rtRaw, atRaw) {
|
|
10
|
+
export function buildMeta(knRaw, otRaw, rtRaw, atRaw, cgRaw) {
|
|
10
11
|
const kn = JSON.parse(knRaw);
|
|
11
12
|
const otParsed = JSON.parse(otRaw);
|
|
12
13
|
const otItems = (Array.isArray(otParsed) ? otParsed
|
|
@@ -54,8 +55,29 @@ export function buildMeta(knRaw, otRaw, rtRaw, atRaw) {
|
|
|
54
55
|
id: a.id,
|
|
55
56
|
name: a.name,
|
|
56
57
|
})),
|
|
58
|
+
conceptGroups: parseConceptGroups(cgRaw),
|
|
57
59
|
};
|
|
58
60
|
}
|
|
61
|
+
function parseConceptGroups(cgRaw) {
|
|
62
|
+
if (!cgRaw)
|
|
63
|
+
return [];
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(cgRaw);
|
|
66
|
+
const items = (Array.isArray(parsed) ? parsed
|
|
67
|
+
: Array.isArray(parsed.entries) ? parsed.entries
|
|
68
|
+
: Array.isArray(parsed.concept_groups) ? parsed.concept_groups
|
|
69
|
+
: []);
|
|
70
|
+
return items.map((g) => ({
|
|
71
|
+
id: g.id,
|
|
72
|
+
name: g.name,
|
|
73
|
+
...(g.color ? { color: g.color } : {}),
|
|
74
|
+
objectTypeIds: Array.isArray(g.object_type_ids) ? g.object_type_ids : [],
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
59
81
|
// ── Bootstrap helpers ───────────────────────────────────────────────────────
|
|
60
82
|
function getErrorMessage(error) {
|
|
61
83
|
const parts = [];
|
|
@@ -100,7 +122,7 @@ function sleep(ms) {
|
|
|
100
122
|
export async function loadExploreMetaWithRetry(token, knId, businessDomain) {
|
|
101
123
|
for (let attempt = 1; attempt <= EXPLORE_BOOTSTRAP_MAX_ATTEMPTS; attempt++) {
|
|
102
124
|
try {
|
|
103
|
-
const [knRaw, otRaw, rtRaw, atRaw] = await Promise.all([
|
|
125
|
+
const [knRaw, otRaw, rtRaw, atRaw, cgRaw] = await Promise.all([
|
|
104
126
|
getKnowledgeNetwork({
|
|
105
127
|
baseUrl: token.baseUrl,
|
|
106
128
|
accessToken: token.accessToken,
|
|
@@ -126,8 +148,15 @@ export async function loadExploreMetaWithRetry(token, knId, businessDomain) {
|
|
|
126
148
|
knId,
|
|
127
149
|
businessDomain,
|
|
128
150
|
}),
|
|
151
|
+
// Concept groups are optional — don't fail the whole load if missing
|
|
152
|
+
listConceptGroups({
|
|
153
|
+
baseUrl: token.baseUrl,
|
|
154
|
+
accessToken: token.accessToken,
|
|
155
|
+
knId,
|
|
156
|
+
businessDomain,
|
|
157
|
+
}).catch(() => ""),
|
|
129
158
|
]);
|
|
130
|
-
return buildMeta(knRaw, otRaw, rtRaw, atRaw);
|
|
159
|
+
return buildMeta(knRaw, otRaw, rtRaw, atRaw, cgRaw);
|
|
131
160
|
}
|
|
132
161
|
catch (error) {
|
|
133
162
|
if (attempt >= EXPLORE_BOOTSTRAP_MAX_ATTEMPTS || !isRetryableExploreBootstrapError(error)) {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { ClientContext } from "../client.js";
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated Datasource is a legacy concept. The platform has migrated
|
|
4
|
+
* data-connection management to vega catalogs. Use `client.vega.catalogs`
|
|
5
|
+
* for catalog CRUD and `client.resources` for resource queries instead.
|
|
6
|
+
* This class is kept as a thin back-compat layer and may be removed in a
|
|
7
|
+
* future major release.
|
|
8
|
+
*/
|
|
2
9
|
export declare class DataSourcesResource {
|
|
3
10
|
private readonly ctx;
|
|
4
11
|
constructor(ctx: ClientContext);
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { testDatasource, createDatasource, listDatasources, getDatasource, deleteDatasource, listTables, } from "../api/datasources.js";
|
|
2
2
|
import { listTablesWithColumns, scanMetadata } from "../api/vega.js";
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Datasource is a legacy concept. The platform has migrated
|
|
5
|
+
* data-connection management to vega catalogs. Use `client.vega.catalogs`
|
|
6
|
+
* for catalog CRUD and `client.resources` for resource queries instead.
|
|
7
|
+
* This class is kept as a thin back-compat layer and may be removed in a
|
|
8
|
+
* future major release.
|
|
9
|
+
*/
|
|
3
10
|
export class DataSourcesResource {
|
|
4
11
|
ctx;
|
|
5
12
|
constructor(ctx) {
|