@kweaver-ai/kweaver-sdk 0.6.3 → 0.6.5
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 +29 -4
- package/README.zh.md +7 -3
- package/dist/api/dataflow.d.ts +1 -1
- package/dist/api/dataflow.js +4 -1
- package/dist/api/toolboxes.d.ts +47 -0
- package/dist/api/toolboxes.js +90 -0
- package/dist/auth/oauth.d.ts +69 -0
- package/dist/auth/oauth.js +647 -1
- package/dist/cli.js +20 -1
- package/dist/commands/auth.js +145 -18
- package/dist/commands/bkn-ops.d.ts +1 -0
- package/dist/commands/bkn-ops.js +8 -1
- package/dist/commands/call.d.ts +10 -0
- package/dist/commands/call.js +61 -5
- package/dist/commands/config.js +19 -9
- package/dist/commands/context-loader.js +8 -2
- package/dist/commands/ds.d.ts +1 -0
- package/dist/commands/ds.js +11 -11
- package/dist/commands/import-csv.d.ts +1 -1
- package/dist/commands/import-csv.js +3 -1
- package/dist/commands/tool.d.ts +16 -0
- package/dist/commands/tool.js +208 -0
- package/dist/commands/toolbox.d.ts +14 -0
- package/dist/commands/toolbox.js +256 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12,6 +12,8 @@ import { runExploreCommand } from "./commands/explore.js";
|
|
|
12
12
|
import { runDataviewCommand } from "./commands/dataview.js";
|
|
13
13
|
import { runSkillCommand } from "./commands/skill.js";
|
|
14
14
|
import { runTokenCommand } from "./commands/token.js";
|
|
15
|
+
import { runToolboxCommand } from "./commands/toolbox.js";
|
|
16
|
+
import { runToolCommand } from "./commands/tool.js";
|
|
15
17
|
import { runVegaCommand } from "./commands/vega.js";
|
|
16
18
|
function printHelp() {
|
|
17
19
|
console.log(`kweaver
|
|
@@ -21,7 +23,7 @@ Usage:
|
|
|
21
23
|
kweaver --version | -V
|
|
22
24
|
kweaver --help | -h
|
|
23
25
|
|
|
24
|
-
kweaver auth <platform-url> [--alias name] [--no-auth] [--no-browser] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
26
|
+
kweaver auth <platform-url> [--alias name] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--playwright] [--insecure|-k]
|
|
25
27
|
kweaver auth login <platform-url> (alias for auth <url>)
|
|
26
28
|
kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (run on host without browser)
|
|
27
29
|
kweaver auth whoami [platform-url|alias] [--json]
|
|
@@ -99,6 +101,15 @@ Usage:
|
|
|
99
101
|
kweaver skill read-file <skill-id> <rel-path> [--raw] [--output file]
|
|
100
102
|
kweaver skill download|install <skill-id> [path] [options]
|
|
101
103
|
|
|
104
|
+
kweaver toolbox create --name <n> --service-url <url> [--description <d>] [-bd value]
|
|
105
|
+
kweaver toolbox list [--keyword X] [--limit N] [--offset N] [-bd value]
|
|
106
|
+
kweaver toolbox publish|unpublish <box-id> [-bd value]
|
|
107
|
+
kweaver toolbox delete <box-id> [-y] [-bd value]
|
|
108
|
+
|
|
109
|
+
kweaver tool upload --toolbox <box-id> <openapi-spec-path> [--metadata-type openapi]
|
|
110
|
+
kweaver tool list --toolbox <box-id> [-bd value]
|
|
111
|
+
kweaver tool enable|disable --toolbox <box-id> <tool-id>... [-bd value]
|
|
112
|
+
|
|
102
113
|
kweaver vega health|stats|inspect
|
|
103
114
|
kweaver vega catalog list|get|health|test-connection|discover|resources [options]
|
|
104
115
|
kweaver vega resource list|get|query [options]
|
|
@@ -129,6 +140,8 @@ Commands:
|
|
|
129
140
|
object-type, relation-type, subgraph, action-type, action-execution, action-log)
|
|
130
141
|
config Per-platform configuration (business domain)
|
|
131
142
|
skill Skill registry and market (register, search, progressive read, download/install)
|
|
143
|
+
toolbox Agent toolbox lifecycle (create, list, publish, delete)
|
|
144
|
+
tool Tools inside a toolbox (upload OpenAPI spec, list, enable/disable)
|
|
132
145
|
vega Vega observability (catalog, resource, query/sql, connector-type, health/stats/inspect)
|
|
133
146
|
context-loader Context-loader MCP (config, tools, resources, prompts, kn-search, query-*, etc.)
|
|
134
147
|
help Show this message`);
|
|
@@ -195,6 +208,12 @@ export async function run(argv) {
|
|
|
195
208
|
if (command === "skill") {
|
|
196
209
|
return runSkillCommand(rest);
|
|
197
210
|
}
|
|
211
|
+
if (command === "toolbox") {
|
|
212
|
+
return runToolboxCommand(rest);
|
|
213
|
+
}
|
|
214
|
+
if (command === "tool") {
|
|
215
|
+
return runToolCommand(rest);
|
|
216
|
+
}
|
|
198
217
|
if (command === "context-loader" || command === "context") {
|
|
199
218
|
return runContextLoaderCommand(rest);
|
|
200
219
|
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import { isNoAuth } from "../config/no-auth.js";
|
|
2
2
|
import { autoSelectBusinessDomain, clearPlatformSession, deletePlatform, deleteUser, getActiveUser, getConfigDir, getCurrentPlatform, getPlatformAlias, hasPlatform, listPlatforms, listUserProfiles, loadClientConfig, loadTokenConfig, resolveBusinessDomain, resolvePlatformIdentifier, resolveUserId, saveNoAuthPlatform, setActiveUser, setCurrentPlatform, setPlatformAlias, } from "../config/store.js";
|
|
3
3
|
import { decodeJwtPayload } from "../config/jwt.js";
|
|
4
|
-
import { buildCopyCommand, formatHttpError, normalizeBaseUrl, oauth2Login, playwrightLogin, refreshTokenLogin, } from "../auth/oauth.js";
|
|
4
|
+
import { buildCopyCommand, formatHttpError, isStudiowebShellUnavailableError, normalizeBaseUrl, oauth2Login, oauth2PasswordSigninLogin, playwrightLogin, refreshTokenLogin, } from "../auth/oauth.js";
|
|
5
|
+
/** True when the `playwright` npm package can be imported (browser binaries may still need `npx playwright install`). */
|
|
6
|
+
async function isPlaywrightPackageResolvable() {
|
|
7
|
+
try {
|
|
8
|
+
const modName = "playwright";
|
|
9
|
+
await import(/* webpackIgnore: true */ modName);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
5
16
|
export async function runAuthCommand(args) {
|
|
6
17
|
const target = args[0];
|
|
7
18
|
const rest = args.slice(1);
|
|
8
19
|
if (!target || target === "--help" || target === "-h") {
|
|
9
20
|
console.log(`kweaver auth login <url> [options] Login to a platform (browser OAuth2 by default)
|
|
10
21
|
kweaver auth <url> Login (shorthand; same options as login)
|
|
11
|
-
kweaver auth whoami [url|alias]
|
|
22
|
+
kweaver auth whoami [url|alias] [--json] Show current user identity (from id_token)
|
|
12
23
|
kweaver auth export [url|alias] [--json] Export credentials; run printed command on a headless host
|
|
13
24
|
kweaver auth status [url|alias] Show current auth status
|
|
14
25
|
kweaver auth list List all platforms and users (tree view)
|
|
@@ -30,16 +41,17 @@ Login options:
|
|
|
30
41
|
--port <n> Local callback port (default: 9010). Use when 9010 is occupied.
|
|
31
42
|
--no-browser Do not open a browser; print the auth URL and prompt for the callback URL or code (stdin).
|
|
32
43
|
Use on headless servers or when automatic browser launch fails.
|
|
33
|
-
-u, --username Username (with -p
|
|
34
|
-
-p, --password Password
|
|
35
|
-
--
|
|
44
|
+
-u, --username Username (with -p: tries HTTP /oauth2/signin first when the Studio web shell is available)
|
|
45
|
+
-p, --password Password (with -u: falls back to Playwright headless only when studioweb is unavailable and Playwright is installed)
|
|
46
|
+
--http-signin With -u/-p: HTTP POST /oauth2/signin only (no Playwright fallback). Uses the built-in RSA public key.
|
|
47
|
+
--playwright With -u/-p: force Playwright (skip HTTP sign-in). Without -u/-p: open Playwright for manual login.
|
|
36
48
|
--insecure, -k Skip TLS certificate verification (self-signed / dev HTTPS only)
|
|
37
49
|
--no-auth Save platform without OAuth (servers with no authentication). Same as detecting OAuth 404 during login.`);
|
|
38
50
|
return 0;
|
|
39
51
|
}
|
|
40
52
|
if (target === "login") {
|
|
41
53
|
if (rest[0] === "--help" || rest[0] === "-h") {
|
|
42
|
-
console.log(`kweaver auth login <platform-url> [--alias <name>] [--no-auth] [--no-browser] [-u user] [-p pass] [--playwright] [--refresh-token T --client-id ID --client-secret S]`);
|
|
54
|
+
console.log(`kweaver auth login <platform-url> [--alias <name>] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--playwright] [--refresh-token T --client-id ID --client-secret S]`);
|
|
43
55
|
return 0;
|
|
44
56
|
}
|
|
45
57
|
const url = rest[0];
|
|
@@ -69,6 +81,9 @@ Login options:
|
|
|
69
81
|
const username = readOption(args, "--username") ?? readOption(args, "-u");
|
|
70
82
|
const password = readOption(args, "--password") ?? readOption(args, "-p");
|
|
71
83
|
const usePlaywright = args.includes("--playwright");
|
|
84
|
+
const httpSignin = args.includes("--http-signin");
|
|
85
|
+
const oauthProduct = readOption(args, "--oauth-product");
|
|
86
|
+
const signinPublicKeyFile = readOption(args, "--signin-public-key-file");
|
|
72
87
|
const clientId = readOption(args, "--client-id");
|
|
73
88
|
const clientSecret = readOption(args, "--client-secret");
|
|
74
89
|
const refreshToken = readOption(args, "--refresh-token");
|
|
@@ -83,11 +98,16 @@ Login options:
|
|
|
83
98
|
const KNOWN_LOGIN_FLAGS = new Set([
|
|
84
99
|
"--alias", "--client-id", "--client-secret", "--refresh-token",
|
|
85
100
|
"--port", "--no-browser", "--username", "-u", "--password", "-p",
|
|
101
|
+
"--http-signin",
|
|
102
|
+
"--oauth-product",
|
|
103
|
+
"--signin-public-key-file",
|
|
86
104
|
"--playwright", "--insecure", "-k", "--no-auth", "--redirect-uri",
|
|
87
105
|
]);
|
|
88
106
|
const KNOWN_VALUE_FLAGS = new Set([
|
|
89
107
|
"--alias", "--client-id", "--client-secret", "--refresh-token",
|
|
90
108
|
"--port", "--username", "-u", "--password", "-p", "--redirect-uri",
|
|
109
|
+
"--oauth-product",
|
|
110
|
+
"--signin-public-key-file",
|
|
91
111
|
]);
|
|
92
112
|
for (let i = 0; i < args.length; i++) {
|
|
93
113
|
const a = args[i];
|
|
@@ -110,12 +130,24 @@ Login options:
|
|
|
110
130
|
if (noAuth && noBrowser) {
|
|
111
131
|
console.error("--no-auth does not require a browser; --no-browser is ignored.");
|
|
112
132
|
}
|
|
113
|
-
if (noAuth && (username || password || usePlaywright)) {
|
|
114
|
-
console.error("--no-auth cannot be used with Playwright login or -u/-p.");
|
|
133
|
+
if (noAuth && (username || password || usePlaywright || httpSignin)) {
|
|
134
|
+
console.error("--no-auth cannot be used with Playwright login, HTTP sign-in, or -u/-p.");
|
|
135
|
+
return 1;
|
|
136
|
+
}
|
|
137
|
+
if (noBrowser && (username || password || usePlaywright || httpSignin)) {
|
|
138
|
+
console.error("--no-browser cannot be used with Playwright login, HTTP sign-in, or -u/-p.");
|
|
115
139
|
return 1;
|
|
116
140
|
}
|
|
117
|
-
if (
|
|
118
|
-
console.error("--
|
|
141
|
+
if (httpSignin && usePlaywright) {
|
|
142
|
+
console.error("--http-signin cannot be used with --playwright.");
|
|
143
|
+
return 1;
|
|
144
|
+
}
|
|
145
|
+
if (httpSignin && refreshToken) {
|
|
146
|
+
console.error("--http-signin cannot be used with --refresh-token.");
|
|
147
|
+
return 1;
|
|
148
|
+
}
|
|
149
|
+
if (httpSignin && (!username || !password)) {
|
|
150
|
+
console.error("--http-signin requires -u/--username and -p/--password.");
|
|
119
151
|
return 1;
|
|
120
152
|
}
|
|
121
153
|
if (noBrowser && refreshToken) {
|
|
@@ -137,12 +169,67 @@ Login options:
|
|
|
137
169
|
clientId, clientSecret, refreshToken, tlsInsecure,
|
|
138
170
|
});
|
|
139
171
|
}
|
|
140
|
-
else if (username && password) {
|
|
141
|
-
console.log("Logging in (
|
|
172
|
+
else if (username && password && httpSignin) {
|
|
173
|
+
console.log("Logging in (HTTP /oauth2/signin)...");
|
|
174
|
+
token = await oauth2PasswordSigninLogin(normalizedTarget, {
|
|
175
|
+
username,
|
|
176
|
+
password,
|
|
177
|
+
tlsInsecure,
|
|
178
|
+
port: customPort,
|
|
179
|
+
clientId: clientId ?? undefined,
|
|
180
|
+
clientSecret: clientSecret ?? undefined,
|
|
181
|
+
oauthProduct: oauthProduct ?? undefined,
|
|
182
|
+
signinPublicKeyPemPath: signinPublicKeyFile ?? undefined,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else if (username && password && usePlaywright) {
|
|
186
|
+
console.log("Logging in (headless, Playwright)...");
|
|
142
187
|
token = await playwrightLogin(normalizedTarget, {
|
|
143
|
-
username,
|
|
188
|
+
username,
|
|
189
|
+
password,
|
|
190
|
+
tlsInsecure,
|
|
191
|
+
port: customPort,
|
|
144
192
|
});
|
|
145
193
|
}
|
|
194
|
+
else if (username && password) {
|
|
195
|
+
const signinOpts = {
|
|
196
|
+
username,
|
|
197
|
+
password,
|
|
198
|
+
tlsInsecure,
|
|
199
|
+
port: customPort,
|
|
200
|
+
clientId: clientId ?? undefined,
|
|
201
|
+
clientSecret: clientSecret ?? undefined,
|
|
202
|
+
oauthProduct: oauthProduct ?? undefined,
|
|
203
|
+
signinPublicKeyPemPath: signinPublicKeyFile ?? undefined,
|
|
204
|
+
};
|
|
205
|
+
console.log("Logging in (HTTP /oauth2/signin)...");
|
|
206
|
+
try {
|
|
207
|
+
token = await oauth2PasswordSigninLogin(normalizedTarget, signinOpts);
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
if (!isStudiowebShellUnavailableError(err)) {
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
const playwrightOk = await isPlaywrightPackageResolvable();
|
|
214
|
+
if (playwrightOk) {
|
|
215
|
+
process.stderr.write("Studio web sign-in shell is not available; falling back to Playwright headless login.\n");
|
|
216
|
+
console.log("Logging in (headless, Playwright)...");
|
|
217
|
+
token = await playwrightLogin(normalizedTarget, {
|
|
218
|
+
username,
|
|
219
|
+
password,
|
|
220
|
+
tlsInsecure,
|
|
221
|
+
port: customPort,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
console.error("Studio web sign-in shell is not available on this platform, and the Playwright package is not installed.");
|
|
226
|
+
console.error("Install Playwright for headless browser login: npm install playwright && npx playwright install chromium");
|
|
227
|
+
console.error("Alternatively, use OAuth without credentials:");
|
|
228
|
+
console.error(` kweaver auth login ${normalizedTarget} --no-browser`);
|
|
229
|
+
throw err;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
146
233
|
else if (usePlaywright) {
|
|
147
234
|
console.log("Opening browser for login (Playwright)...");
|
|
148
235
|
token = await playwrightLogin(normalizedTarget, {
|
|
@@ -217,8 +304,19 @@ Login options:
|
|
|
217
304
|
const statusTarget = resolvedTarget && /^https?:\/\//.test(resolvedTarget) ? normalizeBaseUrl(resolvedTarget) : resolvedTarget ?? undefined;
|
|
218
305
|
const platform = statusTarget ?? getCurrentPlatform();
|
|
219
306
|
if (!platform) {
|
|
220
|
-
|
|
221
|
-
|
|
307
|
+
const envRaw = process.env.KWEAVER_BASE_URL?.trim();
|
|
308
|
+
const envUrl = envRaw ? normalizeBaseUrl(envRaw) : undefined;
|
|
309
|
+
const envToken = process.env.KWEAVER_TOKEN?.trim();
|
|
310
|
+
if (!envUrl || !envToken) {
|
|
311
|
+
console.error("No active platform. Run `kweaver auth login <platform-url>` first.\n" +
|
|
312
|
+
" Tip: set KWEAVER_BASE_URL and KWEAVER_TOKEN to use this command without a saved login.");
|
|
313
|
+
return 1;
|
|
314
|
+
}
|
|
315
|
+
console.log(`Config directory: ${getConfigDir()}`);
|
|
316
|
+
console.log(`Platform: ${envUrl} (KWEAVER_BASE_URL)`);
|
|
317
|
+
console.log(`Token present: yes (KWEAVER_TOKEN)`);
|
|
318
|
+
console.log(`Refresh token: n/a (env)`);
|
|
319
|
+
return 0;
|
|
222
320
|
}
|
|
223
321
|
const token = loadTokenConfig(platform);
|
|
224
322
|
if (!token) {
|
|
@@ -359,7 +457,7 @@ Login options:
|
|
|
359
457
|
return 0;
|
|
360
458
|
}
|
|
361
459
|
console.error("Usage: kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass] [--playwright]");
|
|
362
|
-
console.error(" kweaver auth whoami [platform-url|alias]");
|
|
460
|
+
console.error(" kweaver auth whoami [platform-url|alias] [--json]");
|
|
363
461
|
console.error(" kweaver auth export [platform-url|alias] [--json]");
|
|
364
462
|
console.error(" kweaver auth status [platform-url|alias]");
|
|
365
463
|
console.error(" kweaver auth list");
|
|
@@ -456,8 +554,37 @@ Options:
|
|
|
456
554
|
const resolved = positional ? resolvePlatformIdentifier(positional) : null;
|
|
457
555
|
const platform = resolved && /^https?:\/\//.test(resolved) ? normalizeBaseUrl(resolved) : resolved ?? getCurrentPlatform();
|
|
458
556
|
if (!platform) {
|
|
459
|
-
|
|
460
|
-
|
|
557
|
+
const envRaw = process.env.KWEAVER_BASE_URL?.trim();
|
|
558
|
+
const envUrl = envRaw ? normalizeBaseUrl(envRaw) : undefined;
|
|
559
|
+
const envToken = process.env.KWEAVER_TOKEN?.trim();
|
|
560
|
+
if (!envUrl || !envToken) {
|
|
561
|
+
console.error("No active platform. Run `kweaver auth login <platform-url>` first.");
|
|
562
|
+
return 1;
|
|
563
|
+
}
|
|
564
|
+
const accessToken = envToken.replace(/^Bearer\s+/i, "");
|
|
565
|
+
const payload = decodeJwtPayload(accessToken);
|
|
566
|
+
if (jsonOutput) {
|
|
567
|
+
console.log(JSON.stringify({ platform: envUrl, source: "env", ...(payload ?? {}) }, null, 2));
|
|
568
|
+
return 0;
|
|
569
|
+
}
|
|
570
|
+
console.log(`Platform: ${envUrl}`);
|
|
571
|
+
console.log(`Source: env (KWEAVER_TOKEN)`);
|
|
572
|
+
if (payload) {
|
|
573
|
+
const uname = payload.preferred_username ?? payload.name;
|
|
574
|
+
if (uname)
|
|
575
|
+
console.log(`Username: ${uname}`);
|
|
576
|
+
console.log(`User ID: ${payload.sub ?? "(unknown)"}`);
|
|
577
|
+
console.log(`Issuer: ${payload.iss ?? "(unknown)"}`);
|
|
578
|
+
if (payload.iat)
|
|
579
|
+
console.log(`Issued: ${new Date(payload.iat * 1000).toISOString()}`);
|
|
580
|
+
if (payload.exp)
|
|
581
|
+
console.log(`Expires: ${new Date(payload.exp * 1000).toISOString()}`);
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
console.log(`User info unavailable: opaque access token.`);
|
|
585
|
+
console.log(`Hint: run \`kweaver auth login ${envUrl}\` to obtain a full session.`);
|
|
586
|
+
}
|
|
587
|
+
return 0;
|
|
461
588
|
}
|
|
462
589
|
const token = loadTokenConfig(platform);
|
|
463
590
|
if (!token) {
|
package/dist/commands/bkn-ops.js
CHANGED
|
@@ -706,6 +706,7 @@ Options:
|
|
|
706
706
|
--tables <a,b> Tables to include in KN (default: all imported)
|
|
707
707
|
--build (default) Build after creation
|
|
708
708
|
--no-build Skip build
|
|
709
|
+
--recreate Use "insert" mode on first batch (only effective for new tables)
|
|
709
710
|
--timeout <n> Build timeout in seconds (default: 300)
|
|
710
711
|
-bd, --biz-domain Business domain (default: bd_public)`;
|
|
711
712
|
export function parseKnCreateFromCsvArgs(args) {
|
|
@@ -716,6 +717,7 @@ export function parseKnCreateFromCsvArgs(args) {
|
|
|
716
717
|
let batchSize = 500;
|
|
717
718
|
let tablesStr = "";
|
|
718
719
|
let build = true;
|
|
720
|
+
let recreate = false;
|
|
719
721
|
let timeout = 300;
|
|
720
722
|
let businessDomain = "";
|
|
721
723
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -752,6 +754,10 @@ export function parseKnCreateFromCsvArgs(args) {
|
|
|
752
754
|
build = false;
|
|
753
755
|
continue;
|
|
754
756
|
}
|
|
757
|
+
if (arg === "--recreate") {
|
|
758
|
+
recreate = true;
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
755
761
|
if (arg === "--timeout" && args[i + 1]) {
|
|
756
762
|
timeout = parseInt(args[++i], 10);
|
|
757
763
|
if (Number.isNaN(timeout) || timeout < 1)
|
|
@@ -772,7 +778,7 @@ export function parseKnCreateFromCsvArgs(args) {
|
|
|
772
778
|
}
|
|
773
779
|
if (!businessDomain)
|
|
774
780
|
businessDomain = resolveBusinessDomain();
|
|
775
|
-
return { dsId, files, name, tablePrefix, batchSize, tables, build, timeout, businessDomain };
|
|
781
|
+
return { dsId, files, name, tablePrefix, batchSize, tables, build, recreate, timeout, businessDomain };
|
|
776
782
|
}
|
|
777
783
|
export async function runKnCreateFromCsvCommand(args) {
|
|
778
784
|
let options;
|
|
@@ -795,6 +801,7 @@ export async function runKnCreateFromCsvCommand(args) {
|
|
|
795
801
|
"--table-prefix", options.tablePrefix,
|
|
796
802
|
"--batch-size", String(options.batchSize),
|
|
797
803
|
"-bd", options.businessDomain,
|
|
804
|
+
...(options.recreate ? ["--recreate"] : []),
|
|
798
805
|
];
|
|
799
806
|
const importResult = await runDsImportCsv(importArgs);
|
|
800
807
|
if (importResult.code !== 0) {
|
package/dist/commands/call.d.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
export type FormField = {
|
|
2
|
+
name: string;
|
|
3
|
+
kind: "string";
|
|
4
|
+
value: string;
|
|
5
|
+
} | {
|
|
6
|
+
name: string;
|
|
7
|
+
kind: "file";
|
|
8
|
+
path: string;
|
|
9
|
+
};
|
|
1
10
|
export interface CallInvocation {
|
|
2
11
|
url: string;
|
|
3
12
|
method: string;
|
|
4
13
|
headers: Headers;
|
|
5
14
|
body?: string;
|
|
15
|
+
formFields?: FormField[];
|
|
6
16
|
pretty: boolean;
|
|
7
17
|
verbose: boolean;
|
|
8
18
|
businessDomain: string;
|
package/dist/commands/call.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { basename } from "node:path";
|
|
1
3
|
import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/oauth.js";
|
|
2
4
|
import { isNoAuth } from "../config/no-auth.js";
|
|
3
5
|
import { HttpError } from "../utils/http.js";
|
|
@@ -6,6 +8,7 @@ export function parseCallArgs(args) {
|
|
|
6
8
|
const headers = new Headers();
|
|
7
9
|
let method = "GET";
|
|
8
10
|
let body;
|
|
11
|
+
const formFields = [];
|
|
9
12
|
let url;
|
|
10
13
|
let pretty = true;
|
|
11
14
|
let verbose = false;
|
|
@@ -40,6 +43,26 @@ export function parseCallArgs(args) {
|
|
|
40
43
|
index += 1;
|
|
41
44
|
continue;
|
|
42
45
|
}
|
|
46
|
+
if (arg === "-F" || arg === "--form") {
|
|
47
|
+
const raw = args[index + 1];
|
|
48
|
+
if (!raw)
|
|
49
|
+
throw new Error("Missing value for -F flag");
|
|
50
|
+
const eq = raw.indexOf("=");
|
|
51
|
+
if (eq === -1)
|
|
52
|
+
throw new Error(`Invalid -F format: ${raw} (expected key=value or key=@path)`);
|
|
53
|
+
const name = raw.slice(0, eq);
|
|
54
|
+
const rhs = raw.slice(eq + 1);
|
|
55
|
+
if (rhs.startsWith("@")) {
|
|
56
|
+
formFields.push({ name, kind: "file", path: rhs.slice(1) });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
formFields.push({ name, kind: "string", value: rhs });
|
|
60
|
+
}
|
|
61
|
+
if (method === "GET")
|
|
62
|
+
method = "POST";
|
|
63
|
+
index += 1;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
43
66
|
if (arg === "--pretty") {
|
|
44
67
|
pretty = true;
|
|
45
68
|
continue;
|
|
@@ -70,9 +93,21 @@ export function parseCallArgs(args) {
|
|
|
70
93
|
if (!url) {
|
|
71
94
|
throw new Error("Missing request URL");
|
|
72
95
|
}
|
|
96
|
+
if (formFields.length > 0 && body !== undefined) {
|
|
97
|
+
throw new Error("-F and -d are mutually exclusive");
|
|
98
|
+
}
|
|
73
99
|
if (!businessDomain)
|
|
74
100
|
businessDomain = resolveBusinessDomain();
|
|
75
|
-
return {
|
|
101
|
+
return {
|
|
102
|
+
url,
|
|
103
|
+
method,
|
|
104
|
+
headers,
|
|
105
|
+
body,
|
|
106
|
+
formFields: formFields.length > 0 ? formFields : undefined,
|
|
107
|
+
pretty,
|
|
108
|
+
verbose,
|
|
109
|
+
businessDomain,
|
|
110
|
+
};
|
|
76
111
|
}
|
|
77
112
|
function injectAuthHeaders(headers, accessToken, businessDomain) {
|
|
78
113
|
if (!isNoAuth(accessToken)) {
|
|
@@ -115,12 +150,17 @@ export function formatVerboseRequest(invocation) {
|
|
|
115
150
|
for (const [name, value] of entries) {
|
|
116
151
|
lines.push(` ${name}: ${value}`);
|
|
117
152
|
}
|
|
118
|
-
|
|
153
|
+
const bodyDesc = invocation.formFields && invocation.formFields.length > 0
|
|
154
|
+
? `multipart (${invocation.formFields.length} field${invocation.formFields.length > 1 ? "s" : ""})`
|
|
155
|
+
: invocation.body
|
|
156
|
+
? "present"
|
|
157
|
+
: "empty";
|
|
158
|
+
lines.push(`Body: ${bodyDesc}`);
|
|
119
159
|
return lines;
|
|
120
160
|
}
|
|
121
161
|
export async function runCallCommand(args) {
|
|
122
162
|
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
123
|
-
console.log(`kweaver call <url> [-X METHOD] [-H "Name: value"] [-d BODY] [--pretty] [--verbose] [-bd value]
|
|
163
|
+
console.log(`kweaver call <url> [-X METHOD] [-H "Name: value"] [-d BODY] [-F key=value] [--pretty] [--verbose] [-bd value]
|
|
124
164
|
|
|
125
165
|
Call an API with curl-style flags and auto-injected token headers.
|
|
126
166
|
|
|
@@ -129,6 +169,7 @@ Options:
|
|
|
129
169
|
-X, --request HTTP method (default: GET)
|
|
130
170
|
-H, --header Extra header (repeatable)
|
|
131
171
|
-d, --data, --data-raw JSON request body (sets Content-Type: application/json if not set)
|
|
172
|
+
-F, --form Multipart form field. -F key=value or -F key=@/path/to/file. Repeatable. Mutually exclusive with -d.
|
|
132
173
|
-bd, --biz-domain Override x-business-domain (default: bd_public)
|
|
133
174
|
-v, --verbose Print request info to stderr
|
|
134
175
|
--pretty Pretty-print JSON output (default)`);
|
|
@@ -150,7 +191,22 @@ Options:
|
|
|
150
191
|
: invocation.url;
|
|
151
192
|
const headers = new Headers(invocation.headers);
|
|
152
193
|
injectAuthHeaders(headers, token.accessToken, invocation.businessDomain);
|
|
153
|
-
|
|
194
|
+
let requestBody = invocation.body;
|
|
195
|
+
if (invocation.formFields && invocation.formFields.length > 0) {
|
|
196
|
+
const form = new FormData();
|
|
197
|
+
for (const field of invocation.formFields) {
|
|
198
|
+
if (field.kind === "string") {
|
|
199
|
+
form.append(field.name, field.value);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const buf = await readFile(field.path);
|
|
203
|
+
form.append(field.name, new Blob([buf]), basename(field.path));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
requestBody = form;
|
|
207
|
+
// do not set content-type — fetch sets multipart boundary
|
|
208
|
+
}
|
|
209
|
+
else if (invocation.body !== undefined &&
|
|
154
210
|
invocation.body.length > 0 &&
|
|
155
211
|
!headers.has("content-type") &&
|
|
156
212
|
!headers.has("Content-Type")) {
|
|
@@ -164,7 +220,7 @@ Options:
|
|
|
164
220
|
const response = await fetch(url, {
|
|
165
221
|
method: invocation.method,
|
|
166
222
|
headers,
|
|
167
|
-
body:
|
|
223
|
+
body: requestBody,
|
|
168
224
|
});
|
|
169
225
|
const rawText = await response.text();
|
|
170
226
|
const text = stripSseDoneMarker(rawText, response.headers.get("content-type"));
|
package/dist/commands/config.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { listBusinessDomains } from "../api/business-domains.js";
|
|
2
|
-
import { withTokenRetry } from "../auth/oauth.js";
|
|
2
|
+
import { normalizeBaseUrl, withTokenRetry } from "../auth/oauth.js";
|
|
3
|
+
// Resolve platform URL: saved current platform > KWEAVER_BASE_URL (normalized to
|
|
4
|
+
// match what `auth login` writes, so env users share the same platforms/<key>/ dir).
|
|
5
|
+
function resolvePlatformUrl() {
|
|
6
|
+
const saved = getCurrentPlatform();
|
|
7
|
+
if (saved)
|
|
8
|
+
return saved;
|
|
9
|
+
const env = process.env.KWEAVER_BASE_URL?.trim();
|
|
10
|
+
return env ? normalizeBaseUrl(env) : undefined;
|
|
11
|
+
}
|
|
3
12
|
import { getCurrentPlatform, loadPlatformBusinessDomain, resolveBusinessDomain, savePlatformBusinessDomain, } from "../config/store.js";
|
|
4
13
|
const HELP = `kweaver config
|
|
5
14
|
|
|
@@ -20,9 +29,9 @@ export async function runConfigCommand(args) {
|
|
|
20
29
|
return 0;
|
|
21
30
|
}
|
|
22
31
|
if (sub === "show") {
|
|
23
|
-
const platform =
|
|
32
|
+
const platform = resolvePlatformUrl();
|
|
24
33
|
if (!platform) {
|
|
25
|
-
console.error("No active platform. Run `kweaver auth login <url>` first.");
|
|
34
|
+
console.error("No active platform. Run `kweaver auth login <url>` first.\n Tip: set KWEAVER_BASE_URL to use this command without a saved login.");
|
|
26
35
|
return 1;
|
|
27
36
|
}
|
|
28
37
|
const bd = resolveBusinessDomain(platform);
|
|
@@ -31,7 +40,8 @@ export async function runConfigCommand(args) {
|
|
|
31
40
|
: loadPlatformBusinessDomain(platform)
|
|
32
41
|
? "config"
|
|
33
42
|
: "default";
|
|
34
|
-
|
|
43
|
+
const platformSource = getCurrentPlatform() ? "" : " (KWEAVER_BASE_URL)";
|
|
44
|
+
console.log(`Platform: ${platform}${platformSource}`);
|
|
35
45
|
console.log(`Business Domain: ${bd} (${source})`);
|
|
36
46
|
return 0;
|
|
37
47
|
}
|
|
@@ -41,19 +51,19 @@ export async function runConfigCommand(args) {
|
|
|
41
51
|
console.error("Usage: kweaver config set-bd <value>");
|
|
42
52
|
return 1;
|
|
43
53
|
}
|
|
44
|
-
const platform =
|
|
54
|
+
const platform = resolvePlatformUrl();
|
|
45
55
|
if (!platform) {
|
|
46
|
-
console.error("No active platform. Run `kweaver auth login <url>` first.");
|
|
56
|
+
console.error("No active platform. Run `kweaver auth login <url>` first.\n Tip: set KWEAVER_BASE_URL to write the business domain for that platform.");
|
|
47
57
|
return 1;
|
|
48
58
|
}
|
|
49
59
|
savePlatformBusinessDomain(platform, value);
|
|
50
|
-
console.log(`Business domain set to: ${value}`);
|
|
60
|
+
console.log(`Business domain set to: ${value} (${getCurrentPlatform() ? platform : `${platform} via KWEAVER_BASE_URL`})`);
|
|
51
61
|
return 0;
|
|
52
62
|
}
|
|
53
63
|
if (sub === "list-bd") {
|
|
54
|
-
const platform =
|
|
64
|
+
const platform = resolvePlatformUrl();
|
|
55
65
|
if (!platform) {
|
|
56
|
-
console.error("No active platform. Run `kweaver auth login <url>` first.");
|
|
66
|
+
console.error("No active platform. Run `kweaver auth login <url>` first.\n Tip: set KWEAVER_BASE_URL and KWEAVER_TOKEN to use this command without a saved login.");
|
|
57
67
|
return 1;
|
|
58
68
|
}
|
|
59
69
|
try {
|
|
@@ -281,20 +281,26 @@ async function runGetPrompt(options, args, pretty) {
|
|
|
281
281
|
async function runKnSearch(options, args, pretty) {
|
|
282
282
|
let query;
|
|
283
283
|
let onlySchema = false;
|
|
284
|
+
let knIdOverride;
|
|
284
285
|
for (let i = 0; i < args.length; i += 1) {
|
|
285
286
|
const arg = args[i];
|
|
286
287
|
if (arg === "--only-schema") {
|
|
287
288
|
onlySchema = true;
|
|
288
289
|
}
|
|
290
|
+
else if ((arg === "--kn-id" || arg === "-k") && args[i + 1]) {
|
|
291
|
+
knIdOverride = args[i + 1];
|
|
292
|
+
i += 1;
|
|
293
|
+
}
|
|
289
294
|
else if (!arg.startsWith("-") && !query) {
|
|
290
295
|
query = arg;
|
|
291
296
|
}
|
|
292
297
|
}
|
|
293
298
|
if (!query) {
|
|
294
|
-
console.error("Usage: kweaver context-loader kn-search <query> [--only-schema]");
|
|
299
|
+
console.error("Usage: kweaver context-loader kn-search <query> [--kn-id <id>] [--only-schema]");
|
|
295
300
|
return 1;
|
|
296
301
|
}
|
|
297
|
-
const
|
|
302
|
+
const effectiveOptions = knIdOverride ? { ...options, knId: knIdOverride } : options;
|
|
303
|
+
const result = await knSearch(effectiveOptions, { query, only_schema: onlySchema });
|
|
298
304
|
console.log(formatOutput(result, pretty));
|
|
299
305
|
return 0;
|
|
300
306
|
}
|
package/dist/commands/ds.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare function resolveFiles(pattern: string): Promise<string[]>;
|
|
|
17
17
|
export interface ImportCsvResult {
|
|
18
18
|
code: number;
|
|
19
19
|
tables: string[];
|
|
20
|
+
failed: string[];
|
|
20
21
|
tableColumns: Record<string, string[]>;
|
|
21
22
|
sampleRows: Record<string, Array<Record<string, string | null>>>;
|
|
22
23
|
}
|
package/dist/commands/ds.js
CHANGED
|
@@ -384,17 +384,17 @@ export async function runDsImportCsv(args) {
|
|
|
384
384
|
catch (error) {
|
|
385
385
|
if (error instanceof Error && error.message === "help") {
|
|
386
386
|
console.log(IMPORT_CSV_HELP);
|
|
387
|
-
return { code: 0, tables: [], tableColumns: {}, sampleRows: {} };
|
|
387
|
+
return { code: 0, tables: [], failed: [], tableColumns: {}, sampleRows: {} };
|
|
388
388
|
}
|
|
389
389
|
throw error;
|
|
390
390
|
}
|
|
391
391
|
if (!options.datasourceId) {
|
|
392
392
|
console.error("Usage: kweaver ds import-csv <ds-id> --files <glob_or_list> [options]");
|
|
393
|
-
return { code: 1, tables: [], tableColumns: {}, sampleRows: {} };
|
|
393
|
+
return { code: 1, tables: [], failed: [], tableColumns: {}, sampleRows: {} };
|
|
394
394
|
}
|
|
395
395
|
if (!options.files) {
|
|
396
396
|
console.error("Error: --files is required");
|
|
397
|
-
return { code: 1, tables: [], tableColumns: {}, sampleRows: {} };
|
|
397
|
+
return { code: 1, tables: [], failed: [], tableColumns: {}, sampleRows: {} };
|
|
398
398
|
}
|
|
399
399
|
// 1. Get credentials
|
|
400
400
|
const token = await ensureValidToken();
|
|
@@ -429,7 +429,7 @@ export async function runDsImportCsv(args) {
|
|
|
429
429
|
}
|
|
430
430
|
if (parsed.length === 0) {
|
|
431
431
|
console.error("All files were skipped — nothing to import");
|
|
432
|
-
return { code: 1, tables: [], tableColumns: {}, sampleRows: {} };
|
|
432
|
+
return { code: 1, tables: [], failed: [], tableColumns: {}, sampleRows: {} };
|
|
433
433
|
}
|
|
434
434
|
// Phase 2: Import each file in batches
|
|
435
435
|
const succeeded = [];
|
|
@@ -466,7 +466,7 @@ export async function runDsImportCsv(args) {
|
|
|
466
466
|
process.stderr.write(`${elapsed}s\n`);
|
|
467
467
|
}
|
|
468
468
|
catch (err) {
|
|
469
|
-
const msg =
|
|
469
|
+
const msg = formatHttpError(err);
|
|
470
470
|
process.stderr.write(`FAILED\n`);
|
|
471
471
|
console.error(`[${tableName}] batch ${batchLabel} error: ${msg}`);
|
|
472
472
|
batchFailed = true;
|
|
@@ -487,14 +487,14 @@ export async function runDsImportCsv(args) {
|
|
|
487
487
|
if (failed.length > 0) {
|
|
488
488
|
console.error(`Failed tables: ${failed.join(", ")}`);
|
|
489
489
|
}
|
|
490
|
-
|
|
491
|
-
tables: succeeded,
|
|
492
|
-
failed,
|
|
493
|
-
summary: { succeeded: succeeded.length, failed: failed.length },
|
|
494
|
-
}, null, 2));
|
|
495
|
-
return { code: failed.length > 0 ? 1 : 0, tables: succeeded, tableColumns, sampleRows };
|
|
490
|
+
return { code: failed.length > 0 ? 1 : 0, tables: succeeded, failed, tableColumns, sampleRows };
|
|
496
491
|
}
|
|
497
492
|
export async function runDsImportCsvCommand(args) {
|
|
498
493
|
const result = await runDsImportCsv(args);
|
|
494
|
+
console.log(JSON.stringify({
|
|
495
|
+
tables: result.tables,
|
|
496
|
+
failed: result.failed,
|
|
497
|
+
summary: { succeeded: result.tables.length, failed: result.failed.length },
|
|
498
|
+
}, null, 2));
|
|
499
499
|
return result.code;
|
|
500
500
|
}
|