@kweaver-ai/kweaver-sdk 0.5.2 → 0.6.1
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 +19 -1
- package/README.zh.md +19 -1
- package/dist/api/agent-chat.d.ts +7 -1
- package/dist/api/agent-chat.js +146 -40
- package/dist/api/agent-list.js +13 -13
- package/dist/api/business-domains.js +9 -5
- package/dist/api/context-loader.js +4 -1
- package/dist/api/conversations.js +4 -9
- package/dist/api/dataflow2.d.ts +95 -0
- package/dist/api/dataflow2.js +80 -0
- package/dist/api/headers.d.ts +2 -0
- package/dist/api/headers.js +7 -2
- package/dist/api/skills.js +2 -10
- package/dist/api/vega.d.ts +0 -16
- package/dist/api/vega.js +0 -33
- package/dist/auth/oauth.d.ts +7 -6
- package/dist/auth/oauth.js +170 -80
- package/dist/cli.js +21 -1
- package/dist/client.d.ts +9 -0
- package/dist/client.js +48 -8
- package/dist/commands/auth.js +103 -42
- package/dist/commands/bkn-schema.js +22 -0
- package/dist/commands/call.js +8 -5
- package/dist/commands/dataflow.d.ts +1 -0
- package/dist/commands/dataflow.js +251 -0
- package/dist/commands/explore-bkn.d.ts +79 -0
- package/dist/commands/explore-bkn.js +273 -0
- package/dist/commands/explore-chat.d.ts +3 -0
- package/dist/commands/explore-chat.js +193 -0
- package/dist/commands/explore-vega.d.ts +3 -0
- package/dist/commands/explore-vega.js +71 -0
- package/dist/commands/explore.d.ts +9 -0
- package/dist/commands/explore.js +258 -0
- package/dist/commands/vega.js +2 -104
- package/dist/config/no-auth.d.ts +3 -0
- package/dist/config/no-auth.js +5 -0
- package/dist/config/store.d.ts +8 -0
- package/dist/config/store.js +22 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/kweaver.d.ts +5 -0
- package/dist/kweaver.js +32 -2
- package/dist/resources/bkn.js +2 -3
- package/dist/resources/knowledge-networks.js +3 -8
- package/dist/resources/vega.d.ts +0 -6
- package/dist/resources/vega.js +1 -10
- package/dist/templates/explorer/app.js +136 -0
- package/dist/templates/explorer/bkn.js +747 -0
- package/dist/templates/explorer/chat.js +980 -0
- package/dist/templates/explorer/dashboard.js +82 -0
- package/dist/templates/explorer/index.html +35 -0
- package/dist/templates/explorer/style.css +2440 -0
- package/dist/templates/explorer/vega.js +291 -0
- package/dist/utils/browser.js +33 -10
- package/dist/utils/http.d.ts +3 -0
- package/dist/utils/http.js +37 -1
- package/package.json +9 -5
package/dist/auth/oauth.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isNoAuth } from "../config/no-auth.js";
|
|
2
|
+
import { deleteClientConfig, getCurrentPlatform, loadClientConfig, loadTokenConfig, loadUserTokenConfig, resolveUserId, saveClientConfig, saveNoAuthPlatform, saveTokenConfig, setCurrentPlatform, } from "../config/store.js";
|
|
3
|
+
import { HttpError, NetworkRequestError, fetchWithRetry } from "../utils/http.js";
|
|
3
4
|
const TOKEN_TTL_SECONDS = 3600;
|
|
4
5
|
/** Seconds before access token expiry to trigger refresh (matches Python ConfigAuth). */
|
|
5
6
|
const REFRESH_THRESHOLD_SEC = 60;
|
|
@@ -159,8 +160,7 @@ async function isClientStillValid(baseUrl, clientId, redirectUri) {
|
|
|
159
160
|
});
|
|
160
161
|
if (resp.status === 302) {
|
|
161
162
|
const location = resp.headers.get("location") ?? "";
|
|
162
|
-
if (location.includes("error=
|
|
163
|
-
location.includes("error=error")) {
|
|
163
|
+
if (location.includes("error=")) {
|
|
164
164
|
return false;
|
|
165
165
|
}
|
|
166
166
|
return true;
|
|
@@ -198,73 +198,99 @@ async function resolveOrRegisterClient(baseUrl, redirectUri, scope, options) {
|
|
|
198
198
|
}
|
|
199
199
|
let client = loadClientConfig(baseUrl);
|
|
200
200
|
if (client?.clientId) {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
201
|
+
const storedUri = client.redirectUri ?? redirectUri;
|
|
202
|
+
const valid = await isClientStillValid(baseUrl, client.clientId, storedUri);
|
|
203
|
+
if (valid) {
|
|
204
|
+
if (storedUri !== redirectUri) {
|
|
205
|
+
process.stderr.write("Redirect URI changed. Re-registering OAuth2 client…\n");
|
|
206
|
+
deleteClientConfig(baseUrl);
|
|
207
|
+
client = null;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
return client;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
process.stderr.write("Cached OAuth2 client is no longer valid on the server. Re-registering…\n");
|
|
215
|
+
deleteClientConfig(baseUrl);
|
|
216
|
+
client = null;
|
|
217
|
+
}
|
|
207
218
|
}
|
|
208
219
|
const registered = await registerOAuth2Client(baseUrl, redirectUri, scope);
|
|
209
220
|
saveClientConfig(baseUrl, registered);
|
|
210
221
|
return registered;
|
|
211
222
|
}
|
|
212
223
|
/**
|
|
213
|
-
*
|
|
214
|
-
*
|
|
224
|
+
* Emphasize text on stderr (bold + bright yellow) when stderr is a TTY and `NO_COLOR` is unset.
|
|
225
|
+
* See https://no-color.org/
|
|
215
226
|
*/
|
|
216
|
-
function
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const port = parsed.port ? Number(parsed.port) : (parsed.protocol === "https:" ? 443 : 80);
|
|
221
|
-
const isLocalhost = host === "127.0.0.1" || host === "localhost" || host === "::1";
|
|
222
|
-
return { host, port, pathname: parsed.pathname, isLocalhost };
|
|
227
|
+
function stderrEmphasis(text) {
|
|
228
|
+
const noColor = process.env.NO_COLOR;
|
|
229
|
+
if (noColor != null && noColor !== "") {
|
|
230
|
+
return text;
|
|
223
231
|
}
|
|
224
|
-
|
|
225
|
-
return
|
|
232
|
+
if (!process.stderr.isTTY) {
|
|
233
|
+
return text;
|
|
226
234
|
}
|
|
235
|
+
return `\x1b[1;33m${text}\x1b[0m`;
|
|
227
236
|
}
|
|
228
237
|
/**
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* to extract the authorization code.
|
|
238
|
+
* Headless login: read authorization code from stdin (full callback URL or raw code).
|
|
239
|
+
* Used with `--no-browser` or when automatic browser launch fails.
|
|
232
240
|
*/
|
|
233
|
-
async function
|
|
241
|
+
async function promptForCode(authUrl, state, port, pasteMode = "explicit") {
|
|
234
242
|
const { createInterface } = await import("node:readline");
|
|
235
|
-
|
|
236
|
-
"
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
"
|
|
243
|
+
const intro = pasteMode === "explicit"
|
|
244
|
+
? "Open this URL on any device (use a private/incognito window if you need the full sign-in form):\n\n"
|
|
245
|
+
: "Could not open a browser automatically. Open this URL on any device:\n\n";
|
|
246
|
+
const pasteInstructions = "After login, the browser may show an error page (this is expected if nothing listens on localhost).\n" +
|
|
247
|
+
"Copy the FULL URL from the address bar and paste it here, or paste only the authorization code.\n" +
|
|
248
|
+
`The URL looks like: http://127.0.0.1:${port}/callback?code=THIS_PART&state=...\n\n`;
|
|
249
|
+
process.stderr.write("\n" +
|
|
250
|
+
intro +
|
|
251
|
+
` ${authUrl}\n\n` +
|
|
252
|
+
stderrEmphasis(pasteInstructions));
|
|
240
253
|
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
241
|
-
const
|
|
242
|
-
rl.
|
|
254
|
+
const input = await new Promise((resolve, reject) => {
|
|
255
|
+
rl.on("close", () => reject(new Error("Login cancelled.")));
|
|
256
|
+
rl.question("Paste URL or code> ", (answer) => {
|
|
243
257
|
rl.close();
|
|
244
258
|
resolve(answer.trim());
|
|
245
259
|
});
|
|
246
260
|
});
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
261
|
+
if (input.includes("code=")) {
|
|
262
|
+
let url;
|
|
263
|
+
try {
|
|
264
|
+
url = new URL(input.startsWith("http") ? input : `http://x/?${input}`);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
throw new Error("Could not parse the pasted URL. Paste the full callback URL or the code value.");
|
|
268
|
+
}
|
|
269
|
+
const receivedState = url.searchParams.get("state");
|
|
270
|
+
if (receivedState && receivedState !== state) {
|
|
271
|
+
throw new Error("OAuth2 state mismatch — possible CSRF attack.");
|
|
272
|
+
}
|
|
273
|
+
const err = url.searchParams.get("error");
|
|
274
|
+
if (err) {
|
|
275
|
+
const desc = url.searchParams.get("error_description") ?? "";
|
|
276
|
+
throw new Error(desc ? `Authorization failed: ${err} — ${desc}` : `Authorization failed: ${err}`);
|
|
277
|
+
}
|
|
278
|
+
const code = url.searchParams.get("code");
|
|
279
|
+
if (!code) {
|
|
280
|
+
throw new Error("No authorization code found in the pasted URL.");
|
|
281
|
+
}
|
|
282
|
+
return code;
|
|
256
283
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
throw new Error("No authorization code found in the callback URL.");
|
|
284
|
+
if (!input) {
|
|
285
|
+
throw new Error("No authorization code entered.");
|
|
260
286
|
}
|
|
261
|
-
return
|
|
287
|
+
return input;
|
|
262
288
|
}
|
|
263
289
|
/**
|
|
264
290
|
* OAuth2 Authorization Code login flow.
|
|
265
291
|
* 1. Register client (if not already registered), OR use a provided client ID
|
|
266
|
-
* 2. Open browser to /oauth2/auth
|
|
267
|
-
* 3. Receive authorization code via local HTTP callback
|
|
292
|
+
* 2. Open browser to /oauth2/auth (unless `noBrowser` or browser launch fails)
|
|
293
|
+
* 3. Receive authorization code via local HTTP callback, or stdin paste (`noBrowser` / fallback)
|
|
268
294
|
* 4. Exchange code for access_token + refresh_token
|
|
269
295
|
* 5. Save token.json + client.json to ~/.kweaver/
|
|
270
296
|
*/
|
|
@@ -275,14 +301,19 @@ export async function oauth2Login(baseUrl, options) {
|
|
|
275
301
|
const base = normalizeBaseUrl(baseUrl);
|
|
276
302
|
const port = options?.port ?? DEFAULT_REDIRECT_PORT;
|
|
277
303
|
const scope = options?.scope ?? DEFAULT_SCOPE;
|
|
278
|
-
|
|
279
|
-
const redirectUri = options?.redirectUri ?? `http://127.0.0.1:${port}/callback`;
|
|
280
|
-
const parsedRedirect = parseRedirectUri(redirectUri);
|
|
281
|
-
const isLocalRedirect = parsedRedirect?.isLocalhost ?? true;
|
|
282
|
-
const listenPort = parsedRedirect?.port ?? port;
|
|
283
|
-
const callbackPathname = parsedRedirect?.pathname ?? "/callback";
|
|
304
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
284
305
|
// Step 1: Determine client — use provided client ID or fall back to dynamic registration
|
|
285
|
-
let client
|
|
306
|
+
let client;
|
|
307
|
+
try {
|
|
308
|
+
client = await resolveOrRegisterClient(base, redirectUri, scope, options);
|
|
309
|
+
}
|
|
310
|
+
catch (e) {
|
|
311
|
+
if (e instanceof HttpError && e.status === 404) {
|
|
312
|
+
process.stderr.write("OAuth2 endpoint not found (404). Saving platform in no-auth mode.\n");
|
|
313
|
+
return saveNoAuthPlatform(base, { tlsInsecure: options?.tlsInsecure });
|
|
314
|
+
}
|
|
315
|
+
throw e;
|
|
316
|
+
}
|
|
286
317
|
// Use PKCE when no client secret is available (public client / platform client).
|
|
287
318
|
const usePkce = !client.clientSecret;
|
|
288
319
|
const pkce = usePkce ? await generatePkce() : null;
|
|
@@ -304,9 +335,19 @@ export async function oauth2Login(baseUrl, options) {
|
|
|
304
335
|
authParams.set("code_challenge_method", "S256");
|
|
305
336
|
}
|
|
306
337
|
const authUrl = `${base}/oauth2/auth?${authParams.toString()}`;
|
|
338
|
+
const runPasteCodeFlow = async (pasteMode) => {
|
|
339
|
+
const code = await promptForCode(authUrl, state, port, pasteMode);
|
|
340
|
+
const exchanged = await exchangeCodeForToken(base, code, client.clientId, client.clientSecret, redirectUri, pkce?.verifier, options?.tlsInsecure);
|
|
341
|
+
const copyCommand = buildCopyCommand(base, client.clientId, client.clientSecret, exchanged.refreshToken, options?.tlsInsecure);
|
|
342
|
+
process.stderr.write("\nOn a machine without a browser, run:\n\n " + copyCommand + "\n\n");
|
|
343
|
+
return exchanged;
|
|
344
|
+
};
|
|
307
345
|
let token;
|
|
308
|
-
if (
|
|
309
|
-
|
|
346
|
+
if (options?.noBrowser) {
|
|
347
|
+
token = await runPasteCodeFlow("explicit");
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
// Step 4: Local HTTP callback, or paste-code if browser cannot be opened
|
|
310
351
|
token = await new Promise((resolve, reject) => {
|
|
311
352
|
let server;
|
|
312
353
|
const timeoutId = setTimeout(() => {
|
|
@@ -316,8 +357,8 @@ export async function oauth2Login(baseUrl, options) {
|
|
|
316
357
|
server = createServer((req, res) => {
|
|
317
358
|
void (async () => {
|
|
318
359
|
try {
|
|
319
|
-
const url = new URL(req.url ?? "/", `http://127.0.0.1:${
|
|
320
|
-
if (url.pathname !==
|
|
360
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
361
|
+
if (url.pathname !== "/callback") {
|
|
321
362
|
res.writeHead(404);
|
|
322
363
|
res.end();
|
|
323
364
|
return;
|
|
@@ -376,19 +417,25 @@ export async function oauth2Login(baseUrl, options) {
|
|
|
376
417
|
}
|
|
377
418
|
})();
|
|
378
419
|
});
|
|
379
|
-
server.listen(
|
|
380
|
-
|
|
381
|
-
openBrowser(
|
|
382
|
-
|
|
383
|
-
|
|
420
|
+
server.listen(port, "127.0.0.1", () => {
|
|
421
|
+
void (async () => {
|
|
422
|
+
const { openBrowser } = await import("../utils/browser.js");
|
|
423
|
+
const opened = await openBrowser(authUrl);
|
|
424
|
+
process.stderr.write(`If the wrong browser opens, copy this URL to your correct browser:\n ${authUrl}\n`);
|
|
425
|
+
if (!opened) {
|
|
426
|
+
clearTimeout(timeoutId);
|
|
427
|
+
server.close();
|
|
428
|
+
try {
|
|
429
|
+
resolve(await runPasteCodeFlow("fallback"));
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
})();
|
|
384
436
|
});
|
|
385
437
|
});
|
|
386
438
|
}
|
|
387
|
-
else {
|
|
388
|
-
// Step 4b: Non-localhost redirect — manual code entry flow
|
|
389
|
-
const code = await waitForManualCode(authUrl, state);
|
|
390
|
-
token = await exchangeCodeForToken(base, code, client.clientId, client.clientSecret, redirectUri, pkce?.verifier, options?.tlsInsecure);
|
|
391
|
-
}
|
|
392
439
|
setCurrentPlatform(base);
|
|
393
440
|
return token;
|
|
394
441
|
});
|
|
@@ -509,13 +556,20 @@ export async function playwrightLogin(baseUrl, options) {
|
|
|
509
556
|
const base = normalizeBaseUrl(baseUrl);
|
|
510
557
|
const port = options?.port ?? DEFAULT_REDIRECT_PORT;
|
|
511
558
|
const scope = options?.scope ?? DEFAULT_SCOPE;
|
|
512
|
-
const redirectUri =
|
|
513
|
-
const parsedRedirect = parseRedirectUri(redirectUri);
|
|
514
|
-
const listenPort = parsedRedirect?.port ?? port;
|
|
515
|
-
const callbackPathname = parsedRedirect?.pathname ?? "/callback";
|
|
559
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
516
560
|
const hasCredentials = !!(options?.username && options?.password);
|
|
517
561
|
// Step 1: Ensure registered OAuth2 client (with stale-client auto-recovery)
|
|
518
|
-
let client
|
|
562
|
+
let client;
|
|
563
|
+
try {
|
|
564
|
+
client = await resolveOrRegisterClient(base, redirectUri, scope);
|
|
565
|
+
}
|
|
566
|
+
catch (e) {
|
|
567
|
+
if (e instanceof HttpError && e.status === 404) {
|
|
568
|
+
process.stderr.write("OAuth2 endpoint not found (404). Saving platform in no-auth mode.\n");
|
|
569
|
+
return saveNoAuthPlatform(base, { tlsInsecure: options?.tlsInsecure });
|
|
570
|
+
}
|
|
571
|
+
throw e;
|
|
572
|
+
}
|
|
519
573
|
// Step 2: Generate CSRF state
|
|
520
574
|
const state = randomBytes(12).toString("hex");
|
|
521
575
|
// Step 3: Build authorization URL
|
|
@@ -543,8 +597,8 @@ export async function playwrightLogin(baseUrl, options) {
|
|
|
543
597
|
server = createServer((req, res) => {
|
|
544
598
|
void (async () => {
|
|
545
599
|
try {
|
|
546
|
-
const url = new URL(req.url ?? "/", `http://127.0.0.1:${
|
|
547
|
-
if (url.pathname !==
|
|
600
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
601
|
+
if (url.pathname !== "/callback") {
|
|
548
602
|
res.writeHead(404);
|
|
549
603
|
res.end();
|
|
550
604
|
return;
|
|
@@ -608,7 +662,7 @@ export async function playwrightLogin(baseUrl, options) {
|
|
|
608
662
|
}
|
|
609
663
|
})();
|
|
610
664
|
});
|
|
611
|
-
server.listen(
|
|
665
|
+
server.listen(port, "127.0.0.1", async () => {
|
|
612
666
|
try {
|
|
613
667
|
browser = await chromium.launch({ headless: hasCredentials });
|
|
614
668
|
const context = await browser.newContext({ ignoreHTTPSErrors: !!options?.tlsInsecure });
|
|
@@ -649,7 +703,7 @@ export async function playwrightLogin(baseUrl, options) {
|
|
|
649
703
|
*/
|
|
650
704
|
export async function refreshTokenLogin(baseUrl, options) {
|
|
651
705
|
const base = normalizeBaseUrl(baseUrl);
|
|
652
|
-
const redirectUri = `http://
|
|
706
|
+
const redirectUri = `http://localhost:${DEFAULT_REDIRECT_PORT}/callback`;
|
|
653
707
|
const client = {
|
|
654
708
|
baseUrl: base,
|
|
655
709
|
clientId: options.clientId,
|
|
@@ -676,6 +730,9 @@ export async function refreshTokenLogin(baseUrl, options) {
|
|
|
676
730
|
return token;
|
|
677
731
|
}
|
|
678
732
|
function tokenNeedsRefresh(token) {
|
|
733
|
+
if (isNoAuth(token.accessToken)) {
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
679
736
|
if (!token.expiresAt) {
|
|
680
737
|
return false;
|
|
681
738
|
}
|
|
@@ -692,6 +749,9 @@ function tokenNeedsRefresh(token) {
|
|
|
692
749
|
*/
|
|
693
750
|
export async function refreshAccessToken(token) {
|
|
694
751
|
const baseUrl = normalizeBaseUrl(token.baseUrl);
|
|
752
|
+
if (isNoAuth(token.accessToken)) {
|
|
753
|
+
throw new Error(`Cannot refresh no-auth session for ${baseUrl}.`);
|
|
754
|
+
}
|
|
695
755
|
const refreshToken = token.refreshToken?.trim();
|
|
696
756
|
if (!refreshToken) {
|
|
697
757
|
throw new Error(`Token expired and no refresh_token available for ${baseUrl}. Run \`kweaver auth login ${baseUrl}\` again.`);
|
|
@@ -710,7 +770,7 @@ export async function refreshAccessToken(token) {
|
|
|
710
770
|
});
|
|
711
771
|
let response;
|
|
712
772
|
try {
|
|
713
|
-
response = await runWithTlsInsecure(token.tlsInsecure, () =>
|
|
773
|
+
response = await runWithTlsInsecure(token.tlsInsecure, () => fetchWithRetry(url, {
|
|
714
774
|
method: "POST",
|
|
715
775
|
headers: {
|
|
716
776
|
Authorization: `Basic ${credentials}`,
|
|
@@ -777,11 +837,24 @@ export async function ensureValidToken(opts) {
|
|
|
777
837
|
return {
|
|
778
838
|
baseUrl: normalizeBaseUrl(envBaseUrl),
|
|
779
839
|
accessToken: rawToken,
|
|
780
|
-
tokenType: "bearer",
|
|
840
|
+
tokenType: isNoAuth(rawToken) ? "none" : "bearer",
|
|
781
841
|
scope: "",
|
|
782
842
|
obtainedAt: new Date().toISOString(),
|
|
783
843
|
};
|
|
784
844
|
}
|
|
845
|
+
if (!opts?.forceRefresh && envToken && !envBaseUrl) {
|
|
846
|
+
const currentPlatformForEnv = getCurrentPlatform();
|
|
847
|
+
if (currentPlatformForEnv) {
|
|
848
|
+
const rawToken = envToken.replace(/^Bearer\s+/i, "");
|
|
849
|
+
return {
|
|
850
|
+
baseUrl: normalizeBaseUrl(currentPlatformForEnv),
|
|
851
|
+
accessToken: rawToken,
|
|
852
|
+
tokenType: isNoAuth(rawToken) ? "none" : "bearer",
|
|
853
|
+
scope: "",
|
|
854
|
+
obtainedAt: new Date().toISOString(),
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
}
|
|
785
858
|
const currentPlatform = getCurrentPlatform();
|
|
786
859
|
if (!currentPlatform) {
|
|
787
860
|
throw new Error("No active platform selected. Run `kweaver auth login <platform-url>` first.");
|
|
@@ -803,6 +876,9 @@ export async function ensureValidToken(opts) {
|
|
|
803
876
|
if (!token) {
|
|
804
877
|
throw new Error(`No saved token for ${currentPlatform}. Run \`kweaver auth login ${currentPlatform}\` first.`);
|
|
805
878
|
}
|
|
879
|
+
if (isNoAuth(token.accessToken)) {
|
|
880
|
+
return token;
|
|
881
|
+
}
|
|
806
882
|
if (opts?.forceRefresh) {
|
|
807
883
|
return refreshAccessToken(token);
|
|
808
884
|
}
|
|
@@ -845,6 +921,9 @@ export async function with401RefreshRetry(fn) {
|
|
|
845
921
|
if (!latest) {
|
|
846
922
|
throw error;
|
|
847
923
|
}
|
|
924
|
+
if (isNoAuth(latest.accessToken)) {
|
|
925
|
+
throw error;
|
|
926
|
+
}
|
|
848
927
|
try {
|
|
849
928
|
await refreshAccessToken(latest);
|
|
850
929
|
}
|
|
@@ -869,6 +948,9 @@ export async function withTokenRetry(fn) {
|
|
|
869
948
|
}
|
|
870
949
|
catch (error) {
|
|
871
950
|
if (error instanceof HttpError && error.status === 401) {
|
|
951
|
+
if (isNoAuth(token.accessToken)) {
|
|
952
|
+
throw error;
|
|
953
|
+
}
|
|
872
954
|
const platformUrl = normalizeBaseUrl(token.baseUrl);
|
|
873
955
|
const envUser = process.env.KWEAVER_USER;
|
|
874
956
|
let latest;
|
|
@@ -918,6 +1000,11 @@ function formatOAuthErrorBody(body) {
|
|
|
918
1000
|
}
|
|
919
1001
|
return lines.join("\n");
|
|
920
1002
|
}
|
|
1003
|
+
function isTlsVerificationDisabledForProcess() {
|
|
1004
|
+
return (process.env.NODE_TLS_REJECT_UNAUTHORIZED === "0" ||
|
|
1005
|
+
process.env.KWEAVER_TLS_INSECURE === "1" ||
|
|
1006
|
+
process.env.KWEAVER_TLS_INSECURE === "true");
|
|
1007
|
+
}
|
|
921
1008
|
export function formatHttpError(error) {
|
|
922
1009
|
if (error instanceof HttpError) {
|
|
923
1010
|
const oauthMessage = formatOAuthErrorBody(error.body);
|
|
@@ -938,7 +1025,10 @@ export function formatHttpError(error) {
|
|
|
938
1025
|
if (error instanceof Error) {
|
|
939
1026
|
const cause = "cause" in error && error.cause instanceof Error ? error.cause.message : "";
|
|
940
1027
|
if (cause && error.message === "fetch failed") {
|
|
941
|
-
|
|
1028
|
+
const hint = isTlsVerificationDisabledForProcess()
|
|
1029
|
+
? "Hint: TLS verification is already disabled for this process. Check network reachability, TLS termination, or proxy stability."
|
|
1030
|
+
: "Hint: use --insecure (-k) to skip TLS verification for self-signed certificates.";
|
|
1031
|
+
return `${error.message}: ${cause}\n${hint}`;
|
|
942
1032
|
}
|
|
943
1033
|
return error.message;
|
|
944
1034
|
}
|
package/dist/cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NO_AUTH_TOKEN } from "./config/no-auth.js";
|
|
1
2
|
import { applyTlsEnvFromSavedTokens } from "./config/tls-env.js";
|
|
2
3
|
import { runAgentCommand } from "./commands/agent.js";
|
|
3
4
|
import { runAuthCommand } from "./commands/auth.js";
|
|
@@ -5,7 +6,9 @@ import { runKnCommand } from "./commands/bkn.js";
|
|
|
5
6
|
import { runCallCommand } from "./commands/call.js";
|
|
6
7
|
import { runConfigCommand } from "./commands/config.js";
|
|
7
8
|
import { runContextLoaderCommand } from "./commands/context-loader.js";
|
|
9
|
+
import { runDataflowCommand } from "./commands/dataflow.js";
|
|
8
10
|
import { runDsCommand } from "./commands/ds.js";
|
|
11
|
+
import { runExploreCommand } from "./commands/explore.js";
|
|
9
12
|
import { runDataviewCommand } from "./commands/dataview.js";
|
|
10
13
|
import { runSkillCommand } from "./commands/skill.js";
|
|
11
14
|
import { runTokenCommand } from "./commands/token.js";
|
|
@@ -18,7 +21,7 @@ Usage:
|
|
|
18
21
|
kweaver --version | -V
|
|
19
22
|
kweaver --help | -h
|
|
20
23
|
|
|
21
|
-
kweaver auth <platform-url> [--alias name] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
24
|
+
kweaver auth <platform-url> [--alias name] [--no-auth] [--no-browser] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
22
25
|
kweaver auth login <platform-url> (alias for auth <url>)
|
|
23
26
|
kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (run on host without browser)
|
|
24
27
|
kweaver auth whoami [platform-url|alias] [--json]
|
|
@@ -55,6 +58,11 @@ Usage:
|
|
|
55
58
|
kweaver ds tables <id> [--keyword X] [--pretty]
|
|
56
59
|
kweaver ds connect <db_type> <host> <port> <database> --account X --password Y [--schema S] [--name N]
|
|
57
60
|
|
|
61
|
+
kweaver dataflow list [-bd value]
|
|
62
|
+
kweaver dataflow run <dagId> (--file <path> | --url <remote-url> --name <filename>) [-bd value]
|
|
63
|
+
kweaver dataflow runs <dagId> [--since <date-like>] [-bd value]
|
|
64
|
+
kweaver dataflow logs <dagId> <instanceId> [--detail] [-bd value]
|
|
65
|
+
|
|
58
66
|
kweaver dataview list [--datasource-id id] [--type atomic|custom] [--limit n] [-bd value] [--pretty]
|
|
59
67
|
kweaver dataview find --name <name> [--exact] [--datasource-id id] [--wait] [--timeout ms] [-bd value] [--pretty]
|
|
60
68
|
kweaver dataview get <id> [-bd value] [--pretty]
|
|
@@ -114,6 +122,7 @@ Commands:
|
|
|
114
122
|
call (curl) Call an API with curl-style flags and auto-injected token headers
|
|
115
123
|
agent Agent CRUD, chat, sessions, history, publish/unpublish
|
|
116
124
|
ds Manage datasources (list, get, delete, tables, connect)
|
|
125
|
+
dataflow Dataflow document workflows (list, run, runs, logs)
|
|
117
126
|
dataview|dv List, find, get, query (SQL), delete data views (atomic / custom)
|
|
118
127
|
bkn Knowledge network (CRUD, build, validate, export, stats, push/pull,
|
|
119
128
|
object-type, relation-type, subgraph, action-type, action-execution, action-log)
|
|
@@ -125,6 +134,11 @@ Commands:
|
|
|
125
134
|
}
|
|
126
135
|
export async function run(argv) {
|
|
127
136
|
applyTlsEnvFromSavedTokens();
|
|
137
|
+
const noAuthEnv = process.env.KWEAVER_NO_AUTH;
|
|
138
|
+
if ((noAuthEnv === "1" || noAuthEnv === "true" || noAuthEnv === "yes") &&
|
|
139
|
+
!process.env.KWEAVER_TOKEN) {
|
|
140
|
+
process.env.KWEAVER_TOKEN = NO_AUTH_TOKEN;
|
|
141
|
+
}
|
|
128
142
|
// Global --user flag: override active user for this invocation
|
|
129
143
|
const userIdx = argv.indexOf("--user");
|
|
130
144
|
let filteredArgv = argv;
|
|
@@ -153,6 +167,9 @@ export async function run(argv) {
|
|
|
153
167
|
if (command === "ds") {
|
|
154
168
|
return runDsCommand(rest);
|
|
155
169
|
}
|
|
170
|
+
if (command === "dataflow") {
|
|
171
|
+
return runDataflowCommand(rest);
|
|
172
|
+
}
|
|
156
173
|
if (command === "dataview" || command === "dv") {
|
|
157
174
|
return runDataviewCommand(rest);
|
|
158
175
|
}
|
|
@@ -162,6 +179,9 @@ export async function run(argv) {
|
|
|
162
179
|
if (command === "agent") {
|
|
163
180
|
return runAgentCommand(rest);
|
|
164
181
|
}
|
|
182
|
+
if (command === "explore") {
|
|
183
|
+
return runExploreCommand(rest);
|
|
184
|
+
}
|
|
165
185
|
if (command === "bkn") {
|
|
166
186
|
return runKnCommand(rest);
|
|
167
187
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -40,8 +40,17 @@ export interface KWeaverClientOptions {
|
|
|
40
40
|
* When true, read credentials exclusively from ~/.kweaver/ (saved by
|
|
41
41
|
* `kweaver auth login`), ignoring KWEAVER_BASE_URL / KWEAVER_TOKEN env vars.
|
|
42
42
|
* Useful when env vars hold stale tokens or are intended for other tooling.
|
|
43
|
+
* Incompatible with `auth: false` — the constructor throws if both are set.
|
|
43
44
|
*/
|
|
44
45
|
config?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* When false, use no-auth mode: API requests omit Authorization / token headers.
|
|
48
|
+
* Requires a resolvable base URL: `baseUrl`, `KWEAVER_BASE_URL`, or the active
|
|
49
|
+
* platform from `kweaver auth login`. Incompatible with `config: true` — use
|
|
50
|
+
* saved `~/.kweaver/` credentials (including `__NO_AUTH__`) via `config: true`
|
|
51
|
+
* alone instead of passing `auth: false`.
|
|
52
|
+
*/
|
|
53
|
+
auth?: boolean;
|
|
45
54
|
}
|
|
46
55
|
/**
|
|
47
56
|
* Main entry point for the KWeaver TypeScript SDK.
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { applyTlsEnvFromSavedTokens } from "./config/tls-env.js";
|
|
2
|
+
import { NO_AUTH_TOKEN, isNoAuth } from "./config/no-auth.js";
|
|
2
3
|
import { getCurrentPlatform, loadTokenConfig, } from "./config/store.js";
|
|
4
|
+
import { buildHeaders } from "./api/headers.js";
|
|
3
5
|
import { ensureValidToken } from "./auth/oauth.js";
|
|
4
6
|
import { AgentsResource } from "./resources/agents.js";
|
|
5
7
|
import { ConversationsResource } from "./resources/conversations.js";
|
|
@@ -64,8 +66,39 @@ export class KWeaverClient {
|
|
|
64
66
|
skills;
|
|
65
67
|
constructor(opts = {}) {
|
|
66
68
|
const envDomain = process.env.KWEAVER_BUSINESS_DOMAIN;
|
|
69
|
+
if (opts.auth === false && opts.config) {
|
|
70
|
+
throw new Error("KWeaverClient: auth: false is incompatible with config: true.");
|
|
71
|
+
}
|
|
67
72
|
let baseUrl;
|
|
68
73
|
let accessToken;
|
|
74
|
+
if (opts.auth === false) {
|
|
75
|
+
{
|
|
76
|
+
const envUrl = process.env.KWEAVER_BASE_URL;
|
|
77
|
+
baseUrl = opts.baseUrl ?? envUrl;
|
|
78
|
+
if (!baseUrl) {
|
|
79
|
+
const platform = getCurrentPlatform();
|
|
80
|
+
if (platform)
|
|
81
|
+
baseUrl = platform;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!baseUrl) {
|
|
85
|
+
throw new Error("KWeaverClient: baseUrl is required when auth is false. " +
|
|
86
|
+
"Pass it explicitly, set KWEAVER_BASE_URL, or run `kweaver auth login`.");
|
|
87
|
+
}
|
|
88
|
+
this._baseUrl = baseUrl.replace(/\/+$/, "");
|
|
89
|
+
this._accessToken = NO_AUTH_TOKEN;
|
|
90
|
+
this._businessDomain = opts.businessDomain ?? envDomain ?? "bd_public";
|
|
91
|
+
this.knowledgeNetworks = new KnowledgeNetworksResource(this);
|
|
92
|
+
this.agents = new AgentsResource(this);
|
|
93
|
+
this.bkn = new BknResource(this);
|
|
94
|
+
this.conversations = new ConversationsResource(this);
|
|
95
|
+
this.dataflows = new DataflowsResource(this);
|
|
96
|
+
this.datasources = new DataSourcesResource(this);
|
|
97
|
+
this.dataviews = new DataViewsResource(this);
|
|
98
|
+
this.vega = new VegaResource(this);
|
|
99
|
+
this.skills = new SkillsResource(this);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
69
102
|
if (opts.config) {
|
|
70
103
|
// config: true — read exclusively from ~/.kweaver/, ignore env vars
|
|
71
104
|
const platform = getCurrentPlatform();
|
|
@@ -140,15 +173,22 @@ export class KWeaverClient {
|
|
|
140
173
|
accessToken: token.accessToken,
|
|
141
174
|
...opts,
|
|
142
175
|
});
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
176
|
+
if (!isNoAuth(token.accessToken)) {
|
|
177
|
+
// Quick probe — if the token was revoked server-side, force refresh
|
|
178
|
+
try {
|
|
179
|
+
const bd = client.base().businessDomain;
|
|
180
|
+
const probe = await fetch(`${token.baseUrl.replace(/\/+$/, "")}/api/ontology-manager/v1/knowledge-networks?limit=1`, { headers: buildHeaders(token.accessToken, bd) });
|
|
181
|
+
if (probe.status === 401) {
|
|
182
|
+
throw new Error("Access token revoked. Run `kweaver auth login` to re-authenticate.");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (e) {
|
|
186
|
+
if (e instanceof Error &&
|
|
187
|
+
e.message.startsWith("Access token revoked")) {
|
|
188
|
+
throw e;
|
|
189
|
+
}
|
|
190
|
+
// Network error — return client as-is, let the caller deal with it
|
|
148
191
|
}
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
// Network error — return client as-is, let the caller deal with it
|
|
152
192
|
}
|
|
153
193
|
return client;
|
|
154
194
|
}
|