@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.
Files changed (57) hide show
  1. package/README.md +19 -1
  2. package/README.zh.md +19 -1
  3. package/dist/api/agent-chat.d.ts +7 -1
  4. package/dist/api/agent-chat.js +146 -40
  5. package/dist/api/agent-list.js +13 -13
  6. package/dist/api/business-domains.js +9 -5
  7. package/dist/api/context-loader.js +4 -1
  8. package/dist/api/conversations.js +4 -9
  9. package/dist/api/dataflow2.d.ts +95 -0
  10. package/dist/api/dataflow2.js +80 -0
  11. package/dist/api/headers.d.ts +2 -0
  12. package/dist/api/headers.js +7 -2
  13. package/dist/api/skills.js +2 -10
  14. package/dist/api/vega.d.ts +0 -16
  15. package/dist/api/vega.js +0 -33
  16. package/dist/auth/oauth.d.ts +7 -6
  17. package/dist/auth/oauth.js +170 -80
  18. package/dist/cli.js +21 -1
  19. package/dist/client.d.ts +9 -0
  20. package/dist/client.js +48 -8
  21. package/dist/commands/auth.js +103 -42
  22. package/dist/commands/bkn-schema.js +22 -0
  23. package/dist/commands/call.js +8 -5
  24. package/dist/commands/dataflow.d.ts +1 -0
  25. package/dist/commands/dataflow.js +251 -0
  26. package/dist/commands/explore-bkn.d.ts +79 -0
  27. package/dist/commands/explore-bkn.js +273 -0
  28. package/dist/commands/explore-chat.d.ts +3 -0
  29. package/dist/commands/explore-chat.js +193 -0
  30. package/dist/commands/explore-vega.d.ts +3 -0
  31. package/dist/commands/explore-vega.js +71 -0
  32. package/dist/commands/explore.d.ts +9 -0
  33. package/dist/commands/explore.js +258 -0
  34. package/dist/commands/vega.js +2 -104
  35. package/dist/config/no-auth.d.ts +3 -0
  36. package/dist/config/no-auth.js +5 -0
  37. package/dist/config/store.d.ts +8 -0
  38. package/dist/config/store.js +22 -0
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/kweaver.d.ts +5 -0
  42. package/dist/kweaver.js +32 -2
  43. package/dist/resources/bkn.js +2 -3
  44. package/dist/resources/knowledge-networks.js +3 -8
  45. package/dist/resources/vega.d.ts +0 -6
  46. package/dist/resources/vega.js +1 -10
  47. package/dist/templates/explorer/app.js +136 -0
  48. package/dist/templates/explorer/bkn.js +747 -0
  49. package/dist/templates/explorer/chat.js +980 -0
  50. package/dist/templates/explorer/dashboard.js +82 -0
  51. package/dist/templates/explorer/index.html +35 -0
  52. package/dist/templates/explorer/style.css +2440 -0
  53. package/dist/templates/explorer/vega.js +291 -0
  54. package/dist/utils/browser.js +33 -10
  55. package/dist/utils/http.d.ts +3 -0
  56. package/dist/utils/http.js +37 -1
  57. package/package.json +9 -5
@@ -1,5 +1,6 @@
1
- import { deleteClientConfig, getCurrentPlatform, loadClientConfig, loadTokenConfig, loadUserTokenConfig, resolveUserId, saveClientConfig, saveTokenConfig, setCurrentPlatform, } from "../config/store.js";
2
- import { HttpError, NetworkRequestError } from "../utils/http.js";
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=invalid_client") ||
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 valid = await isClientStillValid(baseUrl, client.clientId, redirectUri);
202
- if (valid)
203
- return client;
204
- process.stderr.write("Cached OAuth2 client is no longer valid on the server. Re-registering…\n");
205
- deleteClientConfig(baseUrl);
206
- client = null;
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
- * Parse a redirect URI to extract host, port, and pathname.
214
- * Returns null if the URI is not a valid HTTP(S) URL.
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 parseRedirectUri(uri) {
217
- try {
218
- const parsed = new URL(uri);
219
- const host = parsed.hostname;
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
- catch {
225
- return null;
232
+ if (!process.stderr.isTTY) {
233
+ return text;
226
234
  }
235
+ return `\x1b[1;33m${text}\x1b[0m`;
227
236
  }
228
237
  /**
229
- * Manual code flow for non-localhost redirect URIs.
230
- * Prints the auth URL, then reads the full callback URL from stdin
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 waitForManualCode(authUrl, state) {
241
+ async function promptForCode(authUrl, state, port, pasteMode = "explicit") {
234
242
  const { createInterface } = await import("node:readline");
235
- process.stderr.write("\nSince the redirect URI is not localhost, you need to complete login manually.\n" +
236
- "1. Open this URL in your browser:\n\n" +
237
- ` ${authUrl}\n\n` +
238
- "2. After login, the browser will redirect to your callback URL.\n" +
239
- "3. Copy the full callback URL and paste it here:\n\n");
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 callbackUrl = await new Promise((resolve) => {
242
- rl.question("Callback URL> ", (answer) => {
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
- const parsed = new URL(callbackUrl);
248
- const receivedState = parsed.searchParams.get("state");
249
- if (receivedState !== state) {
250
- throw new Error("OAuth2 state mismatch possible CSRF attack.");
251
- }
252
- const error = parsed.searchParams.get("error");
253
- if (error) {
254
- const desc = parsed.searchParams.get("error_description") ?? "";
255
- throw new Error(desc ? `Authorization failed: ${error} — ${desc}` : `Authorization failed: ${error}`);
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
- const code = parsed.searchParams.get("code");
258
- if (!code) {
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 code;
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 (or manual paste for non-localhost)
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
- // Determine redirect URI: explicit option > port-based default
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 = await resolveOrRegisterClient(base, redirectUri, scope, options);
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 (isLocalRedirect) {
309
- // Step 4a: Local callback — start HTTP server to receive the authorization code
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:${listenPort}`);
320
- if (url.pathname !== callbackPathname) {
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(listenPort, "127.0.0.1", () => {
380
- import("../utils/browser.js").then(({ openBrowser }) => {
381
- openBrowser(authUrl);
382
- });
383
- process.stderr.write(`If the wrong browser opens, copy this URL to your correct browser:\n ${authUrl}\n`);
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 = options?.redirectUri ?? `http://127.0.0.1:${port}/callback`;
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 = await resolveOrRegisterClient(base, redirectUri, scope);
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:${listenPort}`);
547
- if (url.pathname !== callbackPathname) {
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(listenPort, "127.0.0.1", async () => {
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://127.0.0.1:${DEFAULT_REDIRECT_PORT}/callback`;
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, () => fetch(url, {
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
- return `${error.message}: ${cause}\nHint: use --insecure (-k) to skip TLS verification for self-signed certificates.`;
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
- // Quick probe — if the token was revoked server-side, force refresh
144
- try {
145
- const probe = await fetch(`${token.baseUrl.replace(/\/+$/, "")}/api/ontology-manager/v1/knowledge-networks?limit=1`, { headers: { authorization: `Bearer ${token.accessToken}`, token: token.accessToken } });
146
- if (probe.status === 401) {
147
- throw new Error("Access token revoked. Run `kweaver auth login` to re-authenticate.");
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
  }