@kweaver-ai/kweaver-sdk 0.6.5 → 0.6.6
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 +2 -2
- package/README.zh.md +2 -2
- package/dist/auth/oauth.d.ts +52 -21
- package/dist/auth/oauth.js +178 -220
- package/dist/cli.js +1 -1
- package/dist/commands/auth.js +32 -81
- package/package.json +1 -11
package/README.md
CHANGED
|
@@ -153,8 +153,8 @@ const skillMd = await client.skills.fetchContent("skill-id");
|
|
|
153
153
|
## CLI Reference
|
|
154
154
|
|
|
155
155
|
```
|
|
156
|
-
kweaver auth login <url> [--alias name] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--
|
|
157
|
-
# -u/-p:
|
|
156
|
+
kweaver auth login <url> [--alias name] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--insecure|-k]
|
|
157
|
+
# -u/-p (with or without --http-signin): HTTP POST /oauth2/signin (yields refresh_token). Missing -u/-p are prompted from stdin (password hidden when TTY).
|
|
158
158
|
kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (headless login)
|
|
159
159
|
kweaver auth export [url|alias] [--json] (export command to run on a headless host)
|
|
160
160
|
kweaver auth status / whoami [url|alias] [--json] # whoami: --json; with KWEAVER_BASE_URL+KWEAVER_TOKEN when no ~/.kweaver/ platform
|
package/README.zh.md
CHANGED
|
@@ -146,8 +146,8 @@ const skillMd = await client.skills.fetchContent("skill-id");
|
|
|
146
146
|
## 命令速查
|
|
147
147
|
|
|
148
148
|
```
|
|
149
|
-
kweaver auth login <url> [--alias name] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--
|
|
150
|
-
# -u/-p
|
|
149
|
+
kweaver auth login <url> [--alias name] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--insecure|-k]
|
|
150
|
+
# -u/-p(无论是否带 --http-signin):HTTP POST /oauth2/signin(可拿 refresh_token);缺失的用户名/密码会从 stdin 提示输入(TTY 下密码隐藏)
|
|
151
151
|
kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (无浏览器登录)
|
|
152
152
|
kweaver auth export [url|alias] [--json] (导出在无浏览器机器上运行的命令)
|
|
153
153
|
kweaver auth status / whoami [url|alias] [--json] # whoami 支持 --json;无 ~/.kweaver/ 当前平台时可配 KWEAVER_BASE_URL+KWEAVER_TOKEN
|
package/dist/auth/oauth.d.ts
CHANGED
|
@@ -32,6 +32,40 @@ export declare function normalizeBaseUrl(value: string): string;
|
|
|
32
32
|
*/
|
|
33
33
|
/** @internal Exported for CLI env-only identity resolution (`env-snapshot.ts`). */
|
|
34
34
|
export declare function runWithTlsInsecure<T>(tlsInsecure: boolean | undefined, fn: () => Promise<T>): Promise<T>;
|
|
35
|
+
/**
|
|
36
|
+
* Headless login: read authorization code from stdin (full callback URL or raw code).
|
|
37
|
+
* Used with `--no-browser` or when automatic browser launch fails.
|
|
38
|
+
*
|
|
39
|
+
* `io` is injectable for tests; defaults to `process.stdin` / `process.stderr`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function promptForCode(authUrl: string, state: string, port: number, pasteMode?: "explicit" | "fallback", io?: {
|
|
42
|
+
input?: NodeJS.ReadableStream;
|
|
43
|
+
output?: NodeJS.WritableStream;
|
|
44
|
+
}): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Prompt the user for a username on stderr (input echoed).
|
|
47
|
+
*
|
|
48
|
+
* `io` is injectable for tests; defaults to `process.stdin` / `process.stderr`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function promptForUsername(promptLabel?: string, io?: {
|
|
51
|
+
input?: NodeJS.ReadableStream;
|
|
52
|
+
output?: NodeJS.WritableStream;
|
|
53
|
+
}): Promise<string>;
|
|
54
|
+
/**
|
|
55
|
+
* Prompt the user for a password on stderr without echoing keystrokes (TTY only).
|
|
56
|
+
*
|
|
57
|
+
* Falls back to a regular readline prompt when stdin is not a TTY (e.g. piped input
|
|
58
|
+
* during scripted use); callers needing strict no-echo should detect this case themselves.
|
|
59
|
+
*
|
|
60
|
+
* `io` is injectable for tests.
|
|
61
|
+
*/
|
|
62
|
+
export declare function promptForPassword(promptLabel?: string, io?: {
|
|
63
|
+
input?: NodeJS.ReadableStream & {
|
|
64
|
+
isTTY?: boolean;
|
|
65
|
+
setRawMode?: (mode: boolean) => unknown;
|
|
66
|
+
};
|
|
67
|
+
output?: NodeJS.WritableStream;
|
|
68
|
+
}): Promise<string>;
|
|
35
69
|
/**
|
|
36
70
|
* OAuth2 Authorization Code login flow.
|
|
37
71
|
* 1. Register client (if not already registered), OR use a provided client ID
|
|
@@ -53,24 +87,6 @@ export declare function oauth2Login(baseUrl: string, options?: {
|
|
|
53
87
|
*/
|
|
54
88
|
noBrowser?: boolean;
|
|
55
89
|
}): Promise<TokenConfig>;
|
|
56
|
-
/**
|
|
57
|
-
* Playwright-automated OAuth2 login.
|
|
58
|
-
*
|
|
59
|
-
* Uses the full OAuth2 authorization code flow (same as `oauth2Login`) but
|
|
60
|
-
* automates the browser interaction with Playwright. This produces a
|
|
61
|
-
* refresh_token so the CLI can auto-refresh without re-login.
|
|
62
|
-
*
|
|
63
|
-
* When `username` and `password` are provided the browser runs headless and
|
|
64
|
-
* fills the login form automatically. Otherwise it opens a visible browser
|
|
65
|
-
* window for manual login (same UX as the old cookie-based flow).
|
|
66
|
-
*/
|
|
67
|
-
export declare function playwrightLogin(baseUrl: string, options?: {
|
|
68
|
-
username?: string;
|
|
69
|
-
password?: string;
|
|
70
|
-
port?: number;
|
|
71
|
-
scope?: string;
|
|
72
|
-
tlsInsecure?: boolean;
|
|
73
|
-
}): Promise<TokenConfig>;
|
|
74
90
|
/**
|
|
75
91
|
* Parse Next.js `__NEXT_DATA__` from the OAuth2 sign-in HTML shell (CSRF + optional challenge/remember for POST /oauth2/signin).
|
|
76
92
|
* Hydra `login_challenge` may appear only in the sign-in URL; use that when `pageProps.challenge` is absent.
|
|
@@ -83,10 +99,25 @@ export declare function parseSigninPageHtmlProps(html: string): {
|
|
|
83
99
|
rsaPublicKeyMaterial?: string;
|
|
84
100
|
};
|
|
85
101
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
102
|
+
* Build the JSON body for `POST /oauth2/signin` (matches the browser `oauth2-ui` form).
|
|
103
|
+
*
|
|
104
|
+
* `device.client_type` MUST be a value present in the EACP whitelist defined by
|
|
105
|
+
* `kweaver/deploy/auto_cofig/auto_config.sh`. `console_web` is the canonical CLI value
|
|
106
|
+
* (also used by `kweaver-admin`); other values such as `unknown` are rejected by strict
|
|
107
|
+
* deployments with `管理员已禁止此类客户端登录` — surfaced upstream as a `request_forbidden`
|
|
108
|
+
* `No CSRF value available in the session cookie` error after Hydra discards the rejected
|
|
109
|
+
* login challenge.
|
|
110
|
+
*
|
|
111
|
+
* `vcode` and `dualfactorauthinfo` must be present even when empty; otherwise eachttpserver
|
|
112
|
+
* returns HTTP 400 (invalid parameter).
|
|
88
113
|
*/
|
|
89
|
-
export declare function
|
|
114
|
+
export declare function buildOauth2SigninPostBody(opts: {
|
|
115
|
+
csrftoken: string;
|
|
116
|
+
challenge: string;
|
|
117
|
+
account: string;
|
|
118
|
+
passwordCipher: string;
|
|
119
|
+
remember: boolean;
|
|
120
|
+
}): Record<string, unknown>;
|
|
90
121
|
/**
|
|
91
122
|
* OAuth2 Authorization Code login using HTTP **only**: `GET /oauth2/signin` (Next.js shell) and
|
|
92
123
|
* `POST /oauth2/signin` with an RSA PKCS#1 v1.5–encrypted password (same as the browser `rsa.min` / Studio
|
package/dist/auth/oauth.js
CHANGED
|
@@ -463,23 +463,36 @@ function stderrEmphasis(text) {
|
|
|
463
463
|
/**
|
|
464
464
|
* Headless login: read authorization code from stdin (full callback URL or raw code).
|
|
465
465
|
* Used with `--no-browser` or when automatic browser launch fails.
|
|
466
|
+
*
|
|
467
|
+
* `io` is injectable for tests; defaults to `process.stdin` / `process.stderr`.
|
|
466
468
|
*/
|
|
467
|
-
async function promptForCode(authUrl, state, port, pasteMode = "explicit") {
|
|
469
|
+
export async function promptForCode(authUrl, state, port, pasteMode = "explicit", io) {
|
|
468
470
|
const { createInterface } = await import("node:readline");
|
|
471
|
+
const stdin = io?.input ?? process.stdin;
|
|
472
|
+
const stderr = io?.output ?? process.stderr;
|
|
469
473
|
const intro = pasteMode === "explicit"
|
|
470
474
|
? "Open this URL on any device (use a private/incognito window if you need the full sign-in form):\n\n"
|
|
471
475
|
: "Could not open a browser automatically. Open this URL on any device:\n\n";
|
|
472
476
|
const pasteInstructions = "After login, the browser may show an error page (this is expected if nothing listens on localhost).\n" +
|
|
473
477
|
"Copy the FULL URL from the address bar and paste it here, or paste only the authorization code.\n" +
|
|
474
478
|
`The URL looks like: http://127.0.0.1:${port}/callback?code=THIS_PART&state=...\n\n`;
|
|
475
|
-
|
|
479
|
+
stderr.write("\n" +
|
|
476
480
|
intro +
|
|
477
481
|
` ${authUrl}\n\n` +
|
|
478
482
|
stderrEmphasis(pasteInstructions));
|
|
479
|
-
const rl = createInterface({ input:
|
|
483
|
+
const rl = createInterface({ input: stdin, output: stderr });
|
|
484
|
+
// The `close` listener exists to surface Ctrl-D / EOF before the user answers.
|
|
485
|
+
// It MUST be a no-op once the question callback has fired, because `rl.close()`
|
|
486
|
+
// emits `close` synchronously and would otherwise reject the promise before
|
|
487
|
+
// `resolve(answer)` runs (race condition that turns valid input into "Login cancelled.").
|
|
480
488
|
const input = await new Promise((resolve, reject) => {
|
|
481
|
-
|
|
489
|
+
let answered = false;
|
|
490
|
+
rl.on("close", () => {
|
|
491
|
+
if (!answered)
|
|
492
|
+
reject(new Error("Login cancelled."));
|
|
493
|
+
});
|
|
482
494
|
rl.question("Paste URL or code> ", (answer) => {
|
|
495
|
+
answered = true;
|
|
483
496
|
rl.close();
|
|
484
497
|
resolve(answer.trim());
|
|
485
498
|
});
|
|
@@ -512,6 +525,128 @@ async function promptForCode(authUrl, state, port, pasteMode = "explicit") {
|
|
|
512
525
|
}
|
|
513
526
|
return input;
|
|
514
527
|
}
|
|
528
|
+
/**
|
|
529
|
+
* Prompt the user for a username on stderr (input echoed).
|
|
530
|
+
*
|
|
531
|
+
* `io` is injectable for tests; defaults to `process.stdin` / `process.stderr`.
|
|
532
|
+
*/
|
|
533
|
+
export async function promptForUsername(promptLabel = "Username", io) {
|
|
534
|
+
const { createInterface } = await import("node:readline");
|
|
535
|
+
const stdin = io?.input ?? process.stdin;
|
|
536
|
+
const stderr = io?.output ?? process.stderr;
|
|
537
|
+
const rl = createInterface({ input: stdin, output: stderr });
|
|
538
|
+
const value = await new Promise((resolve, reject) => {
|
|
539
|
+
let answered = false;
|
|
540
|
+
rl.on("close", () => {
|
|
541
|
+
if (!answered)
|
|
542
|
+
reject(new Error("Login cancelled."));
|
|
543
|
+
});
|
|
544
|
+
rl.question(`${promptLabel}: `, (answer) => {
|
|
545
|
+
answered = true;
|
|
546
|
+
rl.close();
|
|
547
|
+
resolve(answer.trim());
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
if (!value) {
|
|
551
|
+
throw new Error(`${promptLabel} is required.`);
|
|
552
|
+
}
|
|
553
|
+
return value;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Prompt the user for a password on stderr without echoing keystrokes (TTY only).
|
|
557
|
+
*
|
|
558
|
+
* Falls back to a regular readline prompt when stdin is not a TTY (e.g. piped input
|
|
559
|
+
* during scripted use); callers needing strict no-echo should detect this case themselves.
|
|
560
|
+
*
|
|
561
|
+
* `io` is injectable for tests.
|
|
562
|
+
*/
|
|
563
|
+
export async function promptForPassword(promptLabel = "Password", io) {
|
|
564
|
+
const stdin = io?.input ?? process.stdin;
|
|
565
|
+
const stderr = io?.output ?? process.stderr;
|
|
566
|
+
// Non-TTY (piped, redirected, tests): use regular readline — no masking is possible
|
|
567
|
+
// without raw mode, so we accept echoed input rather than block forever.
|
|
568
|
+
if (!stdin.isTTY || typeof stdin.setRawMode !== "function") {
|
|
569
|
+
const { createInterface } = await import("node:readline");
|
|
570
|
+
const rl = createInterface({ input: stdin, output: stderr });
|
|
571
|
+
const value = await new Promise((resolve, reject) => {
|
|
572
|
+
let answered = false;
|
|
573
|
+
rl.on("close", () => {
|
|
574
|
+
if (!answered)
|
|
575
|
+
reject(new Error("Login cancelled."));
|
|
576
|
+
});
|
|
577
|
+
rl.question(`${promptLabel}: `, (answer) => {
|
|
578
|
+
answered = true;
|
|
579
|
+
rl.close();
|
|
580
|
+
resolve(answer);
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
if (!value)
|
|
584
|
+
throw new Error(`${promptLabel} is required.`);
|
|
585
|
+
return value;
|
|
586
|
+
}
|
|
587
|
+
// TTY: read byte-by-byte in raw mode so keystrokes are not echoed.
|
|
588
|
+
return new Promise((resolve, reject) => {
|
|
589
|
+
stderr.write(`${promptLabel}: `);
|
|
590
|
+
let buf = "";
|
|
591
|
+
const onData = (chunk) => {
|
|
592
|
+
const s = chunk.toString("utf8");
|
|
593
|
+
for (const ch of s) {
|
|
594
|
+
const code = ch.charCodeAt(0);
|
|
595
|
+
if (ch === "\n" || ch === "\r") {
|
|
596
|
+
cleanup();
|
|
597
|
+
stderr.write("\n");
|
|
598
|
+
if (!buf) {
|
|
599
|
+
reject(new Error(`${promptLabel} is required.`));
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
resolve(buf);
|
|
603
|
+
}
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
if (code === 3) {
|
|
607
|
+
// Ctrl-C
|
|
608
|
+
cleanup();
|
|
609
|
+
stderr.write("\n");
|
|
610
|
+
reject(new Error("Login cancelled."));
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
if (code === 4 && buf.length === 0) {
|
|
614
|
+
// Ctrl-D on empty buffer -> cancel
|
|
615
|
+
cleanup();
|
|
616
|
+
stderr.write("\n");
|
|
617
|
+
reject(new Error("Login cancelled."));
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
if (code === 8 || code === 127) {
|
|
621
|
+
// Backspace / DEL
|
|
622
|
+
buf = buf.slice(0, -1);
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
if (code < 32)
|
|
626
|
+
continue; // ignore other control chars
|
|
627
|
+
buf += ch;
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
const cleanup = () => {
|
|
631
|
+
try {
|
|
632
|
+
stdin.setRawMode(false);
|
|
633
|
+
}
|
|
634
|
+
catch { /* noop */ }
|
|
635
|
+
stdin.removeListener("data", onData);
|
|
636
|
+
if (typeof stdin.pause === "function") {
|
|
637
|
+
stdin.pause();
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
try {
|
|
641
|
+
stdin.setRawMode(true);
|
|
642
|
+
}
|
|
643
|
+
catch { /* noop */ }
|
|
644
|
+
if (typeof stdin.resume === "function") {
|
|
645
|
+
stdin.resume();
|
|
646
|
+
}
|
|
647
|
+
stdin.on("data", onData);
|
|
648
|
+
});
|
|
649
|
+
}
|
|
515
650
|
/**
|
|
516
651
|
* OAuth2 Authorization Code login flow.
|
|
517
652
|
* 1. Register client (if not already registered), OR use a provided client ID
|
|
@@ -755,174 +890,6 @@ async function exchangeCodeForToken(baseUrl, code, clientId, clientSecret, redir
|
|
|
755
890
|
saveTokenConfig(token);
|
|
756
891
|
return token;
|
|
757
892
|
}
|
|
758
|
-
/**
|
|
759
|
-
* Playwright-automated OAuth2 login.
|
|
760
|
-
*
|
|
761
|
-
* Uses the full OAuth2 authorization code flow (same as `oauth2Login`) but
|
|
762
|
-
* automates the browser interaction with Playwright. This produces a
|
|
763
|
-
* refresh_token so the CLI can auto-refresh without re-login.
|
|
764
|
-
*
|
|
765
|
-
* When `username` and `password` are provided the browser runs headless and
|
|
766
|
-
* fills the login form automatically. Otherwise it opens a visible browser
|
|
767
|
-
* window for manual login (same UX as the old cookie-based flow).
|
|
768
|
-
*/
|
|
769
|
-
export async function playwrightLogin(baseUrl, options) {
|
|
770
|
-
return runWithTlsInsecure(options?.tlsInsecure, async () => {
|
|
771
|
-
const { createServer } = await import("node:http");
|
|
772
|
-
const { randomBytes } = await import("node:crypto");
|
|
773
|
-
let chromium;
|
|
774
|
-
try {
|
|
775
|
-
const modName = "playwright";
|
|
776
|
-
const pw = await import(/* webpackIgnore: true */ modName);
|
|
777
|
-
chromium = pw.chromium;
|
|
778
|
-
}
|
|
779
|
-
catch {
|
|
780
|
-
throw new Error("Playwright is not installed. Run:\n npm install playwright && npx playwright install chromium");
|
|
781
|
-
}
|
|
782
|
-
const base = normalizeBaseUrl(baseUrl);
|
|
783
|
-
const port = options?.port ?? DEFAULT_REDIRECT_PORT;
|
|
784
|
-
const scope = options?.scope ?? DEFAULT_SCOPE;
|
|
785
|
-
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
786
|
-
const hasCredentials = !!(options?.username && options?.password);
|
|
787
|
-
// Step 1: Ensure registered OAuth2 client (with stale-client auto-recovery)
|
|
788
|
-
let client;
|
|
789
|
-
try {
|
|
790
|
-
client = await resolveOrRegisterClient(base, redirectUri, scope);
|
|
791
|
-
}
|
|
792
|
-
catch (e) {
|
|
793
|
-
if (e instanceof HttpError && e.status === 404) {
|
|
794
|
-
process.stderr.write("OAuth2 endpoint not found (404). Saving platform in no-auth mode.\n");
|
|
795
|
-
return saveNoAuthPlatform(base, { tlsInsecure: options?.tlsInsecure });
|
|
796
|
-
}
|
|
797
|
-
throw e;
|
|
798
|
-
}
|
|
799
|
-
// Step 2: Generate CSRF state
|
|
800
|
-
const state = randomBytes(12).toString("hex");
|
|
801
|
-
// Step 3: Build authorization URL
|
|
802
|
-
const authParams = new URLSearchParams({
|
|
803
|
-
redirect_uri: redirectUri,
|
|
804
|
-
"x-forwarded-prefix": "",
|
|
805
|
-
client_id: client.clientId,
|
|
806
|
-
scope,
|
|
807
|
-
response_type: "code",
|
|
808
|
-
state,
|
|
809
|
-
lang: "zh-cn",
|
|
810
|
-
product: "adp",
|
|
811
|
-
});
|
|
812
|
-
const authUrl = `${base}/oauth2/auth?${authParams.toString()}`;
|
|
813
|
-
// Step 4: Start local callback server; exchange code inside handler, then show credentials HTML
|
|
814
|
-
let browser;
|
|
815
|
-
const token = await new Promise((resolve, reject) => {
|
|
816
|
-
const TIMEOUT_MS = hasCredentials ? 30_000 : 120_000;
|
|
817
|
-
let server;
|
|
818
|
-
const timeoutId = setTimeout(() => {
|
|
819
|
-
server?.close();
|
|
820
|
-
browser?.close();
|
|
821
|
-
reject(new Error(`OAuth2 login timed out (${TIMEOUT_MS / 1000}s). No authorization code received.`));
|
|
822
|
-
}, TIMEOUT_MS);
|
|
823
|
-
server = createServer((req, res) => {
|
|
824
|
-
void (async () => {
|
|
825
|
-
try {
|
|
826
|
-
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
827
|
-
if (url.pathname !== "/callback") {
|
|
828
|
-
res.writeHead(404);
|
|
829
|
-
res.end();
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
const receivedState = url.searchParams.get("state");
|
|
833
|
-
const receivedCode = url.searchParams.get("code");
|
|
834
|
-
const callbackError = url.searchParams.get("error");
|
|
835
|
-
const callbackErrorDesc = url.searchParams.get("error_description");
|
|
836
|
-
if (receivedState !== state) {
|
|
837
|
-
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
838
|
-
res.end(buildCallbackExchangeErrorHtml("OAuth2 state mismatch — possible CSRF attack."));
|
|
839
|
-
clearTimeout(timeoutId);
|
|
840
|
-
server.close();
|
|
841
|
-
browser?.close();
|
|
842
|
-
reject(new Error("OAuth2 state mismatch — possible CSRF attack."));
|
|
843
|
-
return;
|
|
844
|
-
}
|
|
845
|
-
if (callbackError) {
|
|
846
|
-
const msg = callbackErrorDesc
|
|
847
|
-
? `Authorization failed: ${callbackError} — ${callbackErrorDesc}`
|
|
848
|
-
: `Authorization failed: ${callbackError}`;
|
|
849
|
-
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
850
|
-
res.end(buildCallbackExchangeErrorHtml(msg));
|
|
851
|
-
clearTimeout(timeoutId);
|
|
852
|
-
server.close();
|
|
853
|
-
browser?.close();
|
|
854
|
-
reject(new Error(msg));
|
|
855
|
-
return;
|
|
856
|
-
}
|
|
857
|
-
if (!receivedCode) {
|
|
858
|
-
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
859
|
-
res.end(buildCallbackExchangeErrorHtml("No authorization code received in callback."));
|
|
860
|
-
clearTimeout(timeoutId);
|
|
861
|
-
server.close();
|
|
862
|
-
browser?.close();
|
|
863
|
-
reject(new Error("No authorization code received in callback."));
|
|
864
|
-
return;
|
|
865
|
-
}
|
|
866
|
-
const exchanged = await exchangeCodeForToken(base, receivedCode, client.clientId, client.clientSecret, redirectUri, undefined, options?.tlsInsecure);
|
|
867
|
-
const copyCommand = buildCopyCommand(base, client.clientId, client.clientSecret, exchanged.refreshToken, options?.tlsInsecure);
|
|
868
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
869
|
-
res.end(buildCallbackHtml(copyCommand));
|
|
870
|
-
clearTimeout(timeoutId);
|
|
871
|
-
server.close();
|
|
872
|
-
browser?.close();
|
|
873
|
-
resolve(exchanged);
|
|
874
|
-
}
|
|
875
|
-
catch (err) {
|
|
876
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
877
|
-
try {
|
|
878
|
-
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
879
|
-
res.end(buildCallbackExchangeErrorHtml(message));
|
|
880
|
-
}
|
|
881
|
-
catch {
|
|
882
|
-
/* response may already be sent */
|
|
883
|
-
}
|
|
884
|
-
clearTimeout(timeoutId);
|
|
885
|
-
server.close();
|
|
886
|
-
browser?.close();
|
|
887
|
-
reject(err instanceof Error ? err : new Error(message));
|
|
888
|
-
}
|
|
889
|
-
})();
|
|
890
|
-
});
|
|
891
|
-
server.listen(port, "127.0.0.1", async () => {
|
|
892
|
-
try {
|
|
893
|
-
browser = await chromium.launch({ headless: hasCredentials });
|
|
894
|
-
const context = await browser.newContext({ ignoreHTTPSErrors: !!options?.tlsInsecure });
|
|
895
|
-
const page = await context.newPage();
|
|
896
|
-
// Navigate to OAuth2 auth URL — redirects to signin page
|
|
897
|
-
await page.goto(authUrl, { waitUntil: "networkidle", timeout: 30_000 });
|
|
898
|
-
if (hasCredentials) {
|
|
899
|
-
// Auto-fill credentials
|
|
900
|
-
await page.waitForSelector('input[name="account"]', { timeout: 10_000 });
|
|
901
|
-
await page.fill('input[name="account"]', options.username);
|
|
902
|
-
await page.fill('input[name="password"]', options.password);
|
|
903
|
-
await page.click("button.ant-btn-primary");
|
|
904
|
-
}
|
|
905
|
-
// else: visible browser — user logs in manually
|
|
906
|
-
// The OAuth2 callback will fire when login completes, resolving the promise above
|
|
907
|
-
}
|
|
908
|
-
catch (err) {
|
|
909
|
-
clearTimeout(timeoutId);
|
|
910
|
-
server.close();
|
|
911
|
-
browser?.close();
|
|
912
|
-
reject(err);
|
|
913
|
-
}
|
|
914
|
-
});
|
|
915
|
-
});
|
|
916
|
-
if (hasCredentials) {
|
|
917
|
-
const copyCommand = buildCopyCommand(base, client.clientId, client.clientSecret, token.refreshToken, options?.tlsInsecure);
|
|
918
|
-
process.stderr.write("\nHeadless login: copy this command and run it on a machine without a browser, or use `kweaver auth export`:\n\n" +
|
|
919
|
-
copyCommand +
|
|
920
|
-
"\n\n");
|
|
921
|
-
}
|
|
922
|
-
setCurrentPlatform(base);
|
|
923
|
-
return token;
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
893
|
function mergeCookieJarForSignin(existing, response) {
|
|
927
894
|
const setCookies = typeof response.headers.getSetCookie === "function"
|
|
928
895
|
? response.headers.getSetCookie()
|
|
@@ -1041,7 +1008,7 @@ async function followSigninRedirectsUntilCallback(startUrl, initialJar, state, r
|
|
|
1041
1008
|
return consentResult;
|
|
1042
1009
|
}
|
|
1043
1010
|
throw new Error(`Unexpected OAuth page (HTTP 200) at ${url.slice(0, 120)}… ` +
|
|
1044
|
-
`If this is a consent or MFA screen, use browser login
|
|
1011
|
+
`If this is a consent or MFA screen, use browser login (kweaver auth login <url>).`);
|
|
1045
1012
|
}
|
|
1046
1013
|
const text = await resp.text().catch(() => "");
|
|
1047
1014
|
throw new HttpError(resp.status, resp.statusText, text);
|
|
@@ -1094,17 +1061,38 @@ async function tryAcceptConsentAfterSignin(base, pageUrl, html, jar, scope, stat
|
|
|
1094
1061
|
}
|
|
1095
1062
|
return null;
|
|
1096
1063
|
}
|
|
1097
|
-
const STUDIOWEB_SHELL_UNAVAILABLE_SNIPPETS = [
|
|
1098
|
-
"Studioweb signin endpoint not available",
|
|
1099
|
-
"Cannot reach studioweb signin endpoint",
|
|
1100
|
-
];
|
|
1101
1064
|
/**
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
1065
|
+
* Build the JSON body for `POST /oauth2/signin` (matches the browser `oauth2-ui` form).
|
|
1066
|
+
*
|
|
1067
|
+
* `device.client_type` MUST be a value present in the EACP whitelist defined by
|
|
1068
|
+
* `kweaver/deploy/auto_cofig/auto_config.sh`. `console_web` is the canonical CLI value
|
|
1069
|
+
* (also used by `kweaver-admin`); other values such as `unknown` are rejected by strict
|
|
1070
|
+
* deployments with `管理员已禁止此类客户端登录` — surfaced upstream as a `request_forbidden`
|
|
1071
|
+
* `No CSRF value available in the session cookie` error after Hydra discards the rejected
|
|
1072
|
+
* login challenge.
|
|
1073
|
+
*
|
|
1074
|
+
* `vcode` and `dualfactorauthinfo` must be present even when empty; otherwise eachttpserver
|
|
1075
|
+
* returns HTTP 400 (invalid parameter).
|
|
1104
1076
|
*/
|
|
1105
|
-
export function
|
|
1106
|
-
|
|
1107
|
-
|
|
1077
|
+
export function buildOauth2SigninPostBody(opts) {
|
|
1078
|
+
return {
|
|
1079
|
+
_csrf: opts.csrftoken,
|
|
1080
|
+
challenge: opts.challenge,
|
|
1081
|
+
account: opts.account,
|
|
1082
|
+
password: opts.passwordCipher,
|
|
1083
|
+
vcode: { id: "", content: "" },
|
|
1084
|
+
dualfactorauthinfo: {
|
|
1085
|
+
validcode: { vcode: "" },
|
|
1086
|
+
OTP: { OTP: "" },
|
|
1087
|
+
},
|
|
1088
|
+
remember: opts.remember,
|
|
1089
|
+
device: {
|
|
1090
|
+
name: "",
|
|
1091
|
+
description: "",
|
|
1092
|
+
client_type: "console_web",
|
|
1093
|
+
udids: [],
|
|
1094
|
+
},
|
|
1095
|
+
};
|
|
1108
1096
|
}
|
|
1109
1097
|
/**
|
|
1110
1098
|
* OAuth2 Authorization Code login using HTTP **only**: `GET /oauth2/signin` (Next.js shell) and
|
|
@@ -1127,27 +1115,10 @@ export async function oauth2PasswordSigninLogin(baseUrl, options) {
|
|
|
1127
1115
|
(typeof process.env.KWEAVER_OAUTH_PRODUCT === "string" && process.env.KWEAVER_OAUTH_PRODUCT.trim()
|
|
1128
1116
|
? process.env.KWEAVER_OAUTH_PRODUCT.trim()
|
|
1129
1117
|
: "adp");
|
|
1130
|
-
//
|
|
1131
|
-
//
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
let probeResp;
|
|
1135
|
-
try {
|
|
1136
|
-
probeResp = await fetch(studiowebProbeUrl, { method: "GET", redirect: "manual" });
|
|
1137
|
-
}
|
|
1138
|
-
catch (cause) {
|
|
1139
|
-
throw new Error(`Cannot reach studioweb signin endpoint at ${base}/interface/studioweb/login. ` +
|
|
1140
|
-
`The deployment may not include studioweb. Use \`kweaver auth login ${base}\` ` +
|
|
1141
|
-
`(OAuth code flow) instead.\n Cause: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
1142
|
-
}
|
|
1143
|
-
const probeOk2xx = probeResp.status >= 200 && probeResp.status < 300;
|
|
1144
|
-
const probeOkRedirect = [301, 302, 303, 307, 308].includes(probeResp.status);
|
|
1145
|
-
await probeResp.text().catch(() => "");
|
|
1146
|
-
if (!probeOk2xx && !probeOkRedirect) {
|
|
1147
|
-
throw new Error(`Studioweb signin endpoint not available at ${base}/interface/studioweb/login ` +
|
|
1148
|
-
`(HTTP ${probeResp.status}). The deployment may not include studioweb. ` +
|
|
1149
|
-
`Use \`kweaver auth login ${base}\` (OAuth code flow) instead.`);
|
|
1150
|
-
}
|
|
1118
|
+
// Note: previously we pre-flighted `/interface/studioweb/login` to detect deployments
|
|
1119
|
+
// missing the Studio web shell. The probe added an extra round-trip and was unreliable
|
|
1120
|
+
// (see kweaver-admin which works fine without it). HTTP sign-in only needs `/oauth2/auth`
|
|
1121
|
+
// and `/oauth2/signin`; if either is missing the request below will surface a precise error.
|
|
1151
1122
|
let client;
|
|
1152
1123
|
try {
|
|
1153
1124
|
client = await resolveOrRegisterClient(base, redirectUri, scope, {
|
|
@@ -1231,26 +1202,13 @@ export async function oauth2PasswordSigninLogin(baseUrl, options) {
|
|
|
1231
1202
|
: options.signinPasswordBase64Plain === false
|
|
1232
1203
|
? false
|
|
1233
1204
|
: process.env.KWEAVER_SIGNIN_PASSWORD_B64_RSA_MIN !== "1";
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
const postBody = {
|
|
1237
|
-
_csrf: csrftoken,
|
|
1205
|
+
const postBody = buildOauth2SigninPostBody({
|
|
1206
|
+
csrftoken,
|
|
1238
1207
|
challenge: loginChallenge,
|
|
1239
1208
|
account: options.username,
|
|
1240
|
-
|
|
1241
|
-
vcode: { id: "", content: "" },
|
|
1242
|
-
dualfactorauthinfo: {
|
|
1243
|
-
validcode: { vcode: "" },
|
|
1244
|
-
OTP: { OTP: "" },
|
|
1245
|
-
},
|
|
1209
|
+
passwordCipher: "",
|
|
1246
1210
|
remember,
|
|
1247
|
-
|
|
1248
|
-
name: "",
|
|
1249
|
-
description: "",
|
|
1250
|
-
client_type: "unknown",
|
|
1251
|
-
udids: [],
|
|
1252
|
-
},
|
|
1253
|
-
};
|
|
1211
|
+
});
|
|
1254
1212
|
const origin = new URL(base).origin;
|
|
1255
1213
|
/** Some gateways (e.g. DIP) return HTTP 200 + `{"redirect":"..."}` instead of 3xx Location. */
|
|
1256
1214
|
let signinRedirectFromJson;
|
package/dist/cli.js
CHANGED
|
@@ -23,7 +23,7 @@ Usage:
|
|
|
23
23
|
kweaver --version | -V
|
|
24
24
|
kweaver --help | -h
|
|
25
25
|
|
|
26
|
-
kweaver auth <platform-url> [--alias name] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--
|
|
26
|
+
kweaver auth <platform-url> [--alias name] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--insecure|-k]
|
|
27
27
|
kweaver auth login <platform-url> (alias for auth <url>)
|
|
28
28
|
kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (run on host without browser)
|
|
29
29
|
kweaver auth whoami [platform-url|alias] [--json]
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,18 +1,7 @@
|
|
|
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,
|
|
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
|
-
}
|
|
4
|
+
import { buildCopyCommand, formatHttpError, normalizeBaseUrl, oauth2Login, oauth2PasswordSigninLogin, promptForUsername, promptForPassword, refreshTokenLogin, } from "../auth/oauth.js";
|
|
16
5
|
export async function runAuthCommand(args) {
|
|
17
6
|
const target = args[0];
|
|
18
7
|
const rest = args.slice(1);
|
|
@@ -39,24 +28,24 @@ Login options:
|
|
|
39
28
|
Requires --client-id and --client-secret.
|
|
40
29
|
Get these from the callback page after browser login or \`auth export\`.
|
|
41
30
|
--port <n> Local callback port (default: 9010). Use when 9010 is occupied.
|
|
42
|
-
--no-browser Do not open a browser
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
--
|
|
31
|
+
--no-browser Do not open a browser. Without -u/-p: print the auth URL and prompt for the
|
|
32
|
+
callback URL or code (stdin). With -u and/or -p: route through HTTP sign-in
|
|
33
|
+
(any missing credential is prompted; password is hidden when stdin is a TTY).
|
|
34
|
+
-u, --username Username for HTTP /oauth2/signin (POST). If -p is omitted, password is prompted.
|
|
35
|
+
-p, --password Password for HTTP /oauth2/signin (POST). If -u is omitted, username is prompted.
|
|
36
|
+
--http-signin Force HTTP /oauth2/signin (no browser). Missing -u/-p are prompted from stdin.
|
|
48
37
|
--insecure, -k Skip TLS certificate verification (self-signed / dev HTTPS only)
|
|
49
38
|
--no-auth Save platform without OAuth (servers with no authentication). Same as detecting OAuth 404 during login.`);
|
|
50
39
|
return 0;
|
|
51
40
|
}
|
|
52
41
|
if (target === "login") {
|
|
53
42
|
if (rest[0] === "--help" || rest[0] === "-h") {
|
|
54
|
-
console.log(`kweaver auth login <platform-url> [--alias <name>] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--
|
|
43
|
+
console.log(`kweaver auth login <platform-url> [--alias <name>] [--no-auth] [--no-browser] [-u user] [-p pass] [--http-signin] [--refresh-token T --client-id ID --client-secret S]`);
|
|
55
44
|
return 0;
|
|
56
45
|
}
|
|
57
46
|
const url = rest[0];
|
|
58
47
|
if (!url || url.startsWith("-")) {
|
|
59
|
-
console.error("Usage: kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass]
|
|
48
|
+
console.error("Usage: kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass]");
|
|
60
49
|
return 1;
|
|
61
50
|
}
|
|
62
51
|
return runAuthCommand([url, ...rest.slice(1)]);
|
|
@@ -78,9 +67,8 @@ Login options:
|
|
|
78
67
|
try {
|
|
79
68
|
const normalizedTarget = normalizeBaseUrl(target);
|
|
80
69
|
const alias = readOption(args, "--alias");
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const usePlaywright = args.includes("--playwright");
|
|
70
|
+
let username = readOption(args, "--username") ?? readOption(args, "-u");
|
|
71
|
+
let password = readOption(args, "--password") ?? readOption(args, "-p");
|
|
84
72
|
const httpSignin = args.includes("--http-signin");
|
|
85
73
|
const oauthProduct = readOption(args, "--oauth-product");
|
|
86
74
|
const signinPublicKeyFile = readOption(args, "--signin-public-key-file");
|
|
@@ -101,7 +89,7 @@ Login options:
|
|
|
101
89
|
"--http-signin",
|
|
102
90
|
"--oauth-product",
|
|
103
91
|
"--signin-public-key-file",
|
|
104
|
-
"--
|
|
92
|
+
"--insecure", "-k", "--no-auth", "--redirect-uri",
|
|
105
93
|
]);
|
|
106
94
|
const KNOWN_VALUE_FLAGS = new Set([
|
|
107
95
|
"--alias", "--client-id", "--client-secret", "--refresh-token",
|
|
@@ -130,30 +118,34 @@ Login options:
|
|
|
130
118
|
if (noAuth && noBrowser) {
|
|
131
119
|
console.error("--no-auth does not require a browser; --no-browser is ignored.");
|
|
132
120
|
}
|
|
133
|
-
if (noAuth && (username || password ||
|
|
134
|
-
console.error("--no-auth cannot be used with
|
|
121
|
+
if (noAuth && (username || password || httpSignin)) {
|
|
122
|
+
console.error("--no-auth cannot be used with HTTP sign-in or -u/-p.");
|
|
135
123
|
return 1;
|
|
136
124
|
}
|
|
137
|
-
if (noBrowser &&
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
if (httpSignin && usePlaywright) {
|
|
142
|
-
console.error("--http-signin cannot be used with --playwright.");
|
|
143
|
-
return 1;
|
|
125
|
+
if (noBrowser && httpSignin) {
|
|
126
|
+
// HTTP sign-in already runs without a browser; --no-browser is a no-op signal here.
|
|
127
|
+
console.error("--http-signin already runs without a browser; --no-browser is redundant and ignored.");
|
|
144
128
|
}
|
|
145
129
|
if (httpSignin && refreshToken) {
|
|
146
130
|
console.error("--http-signin cannot be used with --refresh-token.");
|
|
147
131
|
return 1;
|
|
148
132
|
}
|
|
149
|
-
if (httpSignin && (!username || !password)) {
|
|
150
|
-
console.error("--http-signin requires -u/--username and -p/--password.");
|
|
151
|
-
return 1;
|
|
152
|
-
}
|
|
153
133
|
if (noBrowser && refreshToken) {
|
|
154
134
|
console.error("--no-browser cannot be used with --refresh-token.");
|
|
155
135
|
return 1;
|
|
156
136
|
}
|
|
137
|
+
// Headless credential login: if the user signalled HTTP sign-in (--http-signin,
|
|
138
|
+
// or partial -u/-p, or --no-browser combined with -u/-p) but didn't provide both
|
|
139
|
+
// credentials inline, prompt for the missing one(s) on stderr. Password is read
|
|
140
|
+
// without echo when stdin is a TTY.
|
|
141
|
+
const wantsCredentialLogin = !noAuth && !refreshToken &&
|
|
142
|
+
(httpSignin || (noBrowser && (username || password)) || (!!username !== !!password));
|
|
143
|
+
if (wantsCredentialLogin) {
|
|
144
|
+
if (!username)
|
|
145
|
+
username = await promptForUsername("Username");
|
|
146
|
+
if (!password)
|
|
147
|
+
password = await promptForPassword("Password");
|
|
148
|
+
}
|
|
157
149
|
let token;
|
|
158
150
|
if (noAuth) {
|
|
159
151
|
token = saveNoAuthPlatform(normalizedTarget, { tlsInsecure });
|
|
@@ -182,17 +174,9 @@ Login options:
|
|
|
182
174
|
signinPublicKeyPemPath: signinPublicKeyFile ?? undefined,
|
|
183
175
|
});
|
|
184
176
|
}
|
|
185
|
-
else if (username && password && usePlaywright) {
|
|
186
|
-
console.log("Logging in (headless, Playwright)...");
|
|
187
|
-
token = await playwrightLogin(normalizedTarget, {
|
|
188
|
-
username,
|
|
189
|
-
password,
|
|
190
|
-
tlsInsecure,
|
|
191
|
-
port: customPort,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
177
|
else if (username && password) {
|
|
195
|
-
|
|
178
|
+
console.log("Logging in (HTTP /oauth2/signin)...");
|
|
179
|
+
token = await oauth2PasswordSigninLogin(normalizedTarget, {
|
|
196
180
|
username,
|
|
197
181
|
password,
|
|
198
182
|
tlsInsecure,
|
|
@@ -201,39 +185,6 @@ Login options:
|
|
|
201
185
|
clientSecret: clientSecret ?? undefined,
|
|
202
186
|
oauthProduct: oauthProduct ?? undefined,
|
|
203
187
|
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
|
-
}
|
|
233
|
-
else if (usePlaywright) {
|
|
234
|
-
console.log("Opening browser for login (Playwright)...");
|
|
235
|
-
token = await playwrightLogin(normalizedTarget, {
|
|
236
|
-
tlsInsecure, port: customPort,
|
|
237
188
|
});
|
|
238
189
|
}
|
|
239
190
|
else {
|
|
@@ -456,7 +407,7 @@ Login options:
|
|
|
456
407
|
console.log(`Run \`kweaver auth login ${logoutTarget}\` to sign in again.`);
|
|
457
408
|
return 0;
|
|
458
409
|
}
|
|
459
|
-
console.error("Usage: kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass]
|
|
410
|
+
console.error("Usage: kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass]");
|
|
460
411
|
console.error(" kweaver auth whoami [platform-url|alias] [--json]");
|
|
461
412
|
console.error(" kweaver auth export [platform-url|alias] [--json]");
|
|
462
413
|
console.error(" kweaver auth status [platform-url|alias]");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kweaver-ai/kweaver-sdk",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"description": "KWeaver TypeScript SDK — CLI tool and programmatic API for knowledge networks and Decision Agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -51,21 +51,11 @@
|
|
|
51
51
|
"@types/node": "^24.6.0",
|
|
52
52
|
"@types/react": "^19.2.14",
|
|
53
53
|
"@types/yargs": "^17.0.35",
|
|
54
|
-
"playwright": "^1.58.2",
|
|
55
54
|
"tsx": "^4.20.5",
|
|
56
55
|
"typescript": "^5.9.3"
|
|
57
56
|
},
|
|
58
|
-
"peerDependencies": {
|
|
59
|
-
"playwright": ">=1.40.0"
|
|
60
|
-
},
|
|
61
|
-
"peerDependenciesMeta": {
|
|
62
|
-
"playwright": {
|
|
63
|
-
"optional": true
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
57
|
"dependencies": {
|
|
67
58
|
"@kweaver-ai/bkn": "^0.1.0",
|
|
68
|
-
"@playwright/test": "^1.58.2",
|
|
69
59
|
"chardet": "^2.1.1",
|
|
70
60
|
"columnify": "^1.6.0",
|
|
71
61
|
"csv-parse": "^6.2.1",
|