@kweaver-ai/kweaver-sdk 0.4.12 → 0.4.14
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 +40 -3
- package/README.zh.md +40 -2
- package/dist/api/business-domains.d.ts +20 -0
- package/dist/api/business-domains.js +54 -0
- package/dist/api/dataflow.d.ts +2 -0
- package/dist/api/dataflow.js +5 -3
- package/dist/api/dataviews.d.ts +41 -1
- package/dist/api/dataviews.js +58 -5
- package/dist/api/vega.js +1 -1
- package/dist/auth/oauth.d.ts +30 -0
- package/dist/auth/oauth.js +235 -54
- package/dist/cli.js +6 -2
- package/dist/commands/agent.js +15 -15
- package/dist/commands/auth.js +84 -6
- package/dist/commands/bkn.d.ts +11 -0
- package/dist/commands/bkn.js +90 -55
- package/dist/commands/config.js +34 -1
- package/dist/commands/dataview.js +61 -2
- package/dist/commands/vega.js +7 -7
- package/dist/config/store.d.ts +8 -0
- package/dist/config/store.js +39 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -2
- package/dist/resources/dataviews.d.ts +10 -1
- package/dist/resources/dataviews.js +14 -1
- package/package.json +1 -1
package/dist/auth/oauth.js
CHANGED
|
@@ -5,6 +5,87 @@ const TOKEN_TTL_SECONDS = 3600;
|
|
|
5
5
|
const REFRESH_THRESHOLD_SEC = 60;
|
|
6
6
|
const DEFAULT_REDIRECT_PORT = 9010;
|
|
7
7
|
const DEFAULT_SCOPE = "openid offline all";
|
|
8
|
+
/** POSIX shell single-quote escaping for copy-paste commands. */
|
|
9
|
+
export function shellQuoteForShell(value) {
|
|
10
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build a one-line `kweaver auth login ...` command for headless / other machines.
|
|
14
|
+
* Omits `--client-secret` when empty (PKCE-only client); headless refresh may still require a confidential client.
|
|
15
|
+
*/
|
|
16
|
+
export function buildCopyCommand(baseUrl, clientId, clientSecret, refreshToken, tlsInsecure) {
|
|
17
|
+
const parts = ["kweaver", "auth", "login", shellQuoteForShell(normalizeBaseUrl(baseUrl)), "--client-id", shellQuoteForShell(clientId)];
|
|
18
|
+
if (clientSecret) {
|
|
19
|
+
parts.push("--client-secret", shellQuoteForShell(clientSecret));
|
|
20
|
+
}
|
|
21
|
+
if (refreshToken) {
|
|
22
|
+
parts.push("--refresh-token", shellQuoteForShell(refreshToken));
|
|
23
|
+
}
|
|
24
|
+
if (tlsInsecure) {
|
|
25
|
+
parts.push("--insecure");
|
|
26
|
+
}
|
|
27
|
+
return parts.join(" ");
|
|
28
|
+
}
|
|
29
|
+
function escapeHtml(value) {
|
|
30
|
+
return value
|
|
31
|
+
.replace(/&/g, "&")
|
|
32
|
+
.replace(/</g, "<")
|
|
33
|
+
.replace(/>/g, ">")
|
|
34
|
+
.replace(/"/g, """);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* HTML shown after successful OAuth callback with a copyable headless login command.
|
|
38
|
+
*/
|
|
39
|
+
export function buildCallbackHtml(copyCommand) {
|
|
40
|
+
const safeCmd = escapeHtml(copyCommand);
|
|
41
|
+
return `<!DOCTYPE html>
|
|
42
|
+
<html lang="en">
|
|
43
|
+
<head>
|
|
44
|
+
<meta charset="utf-8"/>
|
|
45
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
46
|
+
<title>Login successful</title>
|
|
47
|
+
<style>
|
|
48
|
+
body { font-family: system-ui, sans-serif; max-width: 52rem; margin: 2rem auto; padding: 0 1rem; line-height: 1.5; }
|
|
49
|
+
pre { background: #f4f4f5; padding: 1rem; border-radius: 6px; overflow-x: auto; white-space: pre-wrap; word-break: break-all; }
|
|
50
|
+
button { margin-top: 0.75rem; padding: 0.5rem 1rem; cursor: pointer; }
|
|
51
|
+
.warn { color: #b45309; margin-top: 1.5rem; font-size: 0.9rem; }
|
|
52
|
+
</style>
|
|
53
|
+
</head>
|
|
54
|
+
<body>
|
|
55
|
+
<h2>Login successful</h2>
|
|
56
|
+
<p>You can close this tab.</p>
|
|
57
|
+
<h3>Headless machine</h3>
|
|
58
|
+
<p>On the computer that has no browser (SSH server, CI runner, container), run:</p>
|
|
59
|
+
<pre id="kw-cmd">${safeCmd}</pre>
|
|
60
|
+
<button type="button" id="kw-copy">Copy command</button>
|
|
61
|
+
<p class="warn">Keep these credentials secure. Anyone with the refresh token and client secret can obtain new access tokens.</p>
|
|
62
|
+
<script>
|
|
63
|
+
(function () {
|
|
64
|
+
var btn = document.getElementById("kw-copy");
|
|
65
|
+
var pre = document.getElementById("kw-cmd");
|
|
66
|
+
if (btn && pre) {
|
|
67
|
+
btn.addEventListener("click", function () {
|
|
68
|
+
var text = pre.textContent || "";
|
|
69
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
70
|
+
navigator.clipboard.writeText(text.trim()).then(function () {
|
|
71
|
+
btn.textContent = "Copied";
|
|
72
|
+
setTimeout(function () { btn.textContent = "Copy command"; }, 2000);
|
|
73
|
+
});
|
|
74
|
+
} else {
|
|
75
|
+
window.prompt("Copy this command:", text.trim());
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
</script>
|
|
81
|
+
</body>
|
|
82
|
+
</html>`;
|
|
83
|
+
}
|
|
84
|
+
function buildCallbackExchangeErrorHtml(message) {
|
|
85
|
+
return `<!DOCTYPE html>
|
|
86
|
+
<html lang="en"><head><meta charset="utf-8"/><title>Login error</title></head>
|
|
87
|
+
<body><h2>Login error</h2><pre>${escapeHtml(message)}</pre></body></html>`;
|
|
88
|
+
}
|
|
8
89
|
export function normalizeBaseUrl(value) {
|
|
9
90
|
return value.replace(/\/+$/, "");
|
|
10
91
|
}
|
|
@@ -96,35 +177,62 @@ export async function oauth2Login(baseUrl, options) {
|
|
|
96
177
|
authParams.set("code_challenge_method", "S256");
|
|
97
178
|
}
|
|
98
179
|
const authUrl = `${base}/oauth2/auth?${authParams.toString()}`;
|
|
99
|
-
// Step 4: Start local callback server,
|
|
100
|
-
const
|
|
180
|
+
// Step 4: Start local callback server; exchange code inside handler, then show credentials HTML
|
|
181
|
+
const token = await new Promise((resolve, reject) => {
|
|
182
|
+
let server;
|
|
101
183
|
const timeoutId = setTimeout(() => {
|
|
102
|
-
server
|
|
184
|
+
server?.close();
|
|
103
185
|
reject(new Error("OAuth2 login timed out (120s). No authorization code received."));
|
|
104
186
|
}, 120_000);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
187
|
+
server = createServer((req, res) => {
|
|
188
|
+
void (async () => {
|
|
189
|
+
try {
|
|
190
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
191
|
+
if (url.pathname !== "/callback") {
|
|
192
|
+
res.writeHead(404);
|
|
193
|
+
res.end();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const receivedState = url.searchParams.get("state");
|
|
197
|
+
const receivedCode = url.searchParams.get("code");
|
|
198
|
+
if (receivedState !== state) {
|
|
199
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
200
|
+
res.end(buildCallbackExchangeErrorHtml("OAuth2 state mismatch — possible CSRF attack."));
|
|
201
|
+
clearTimeout(timeoutId);
|
|
202
|
+
server.close();
|
|
203
|
+
reject(new Error("OAuth2 state mismatch — possible CSRF attack."));
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (!receivedCode) {
|
|
207
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
208
|
+
res.end(buildCallbackExchangeErrorHtml("No authorization code received in callback."));
|
|
209
|
+
clearTimeout(timeoutId);
|
|
210
|
+
server.close();
|
|
211
|
+
reject(new Error("No authorization code received in callback."));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const exchanged = await exchangeCodeForToken(base, receivedCode, client.clientId, client.clientSecret, redirectUri, pkce?.verifier, options?.tlsInsecure);
|
|
215
|
+
const copyCommand = buildCopyCommand(base, client.clientId, client.clientSecret, exchanged.refreshToken, options?.tlsInsecure);
|
|
216
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
217
|
+
res.end(buildCallbackHtml(copyCommand));
|
|
218
|
+
clearTimeout(timeoutId);
|
|
219
|
+
server.close();
|
|
220
|
+
resolve(exchanged);
|
|
119
221
|
}
|
|
120
|
-
|
|
121
|
-
|
|
222
|
+
catch (err) {
|
|
223
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
224
|
+
try {
|
|
225
|
+
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
226
|
+
res.end(buildCallbackExchangeErrorHtml(message));
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
/* response may already be sent */
|
|
230
|
+
}
|
|
231
|
+
clearTimeout(timeoutId);
|
|
232
|
+
server.close();
|
|
233
|
+
reject(err instanceof Error ? err : new Error(message));
|
|
122
234
|
}
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
res.writeHead(404);
|
|
126
|
-
res.end();
|
|
127
|
-
}
|
|
235
|
+
})();
|
|
128
236
|
});
|
|
129
237
|
server.listen(port, "127.0.0.1", () => {
|
|
130
238
|
// Step 5: Open browser (uses spawn with proper Windows quoting)
|
|
@@ -134,8 +242,6 @@ export async function oauth2Login(baseUrl, options) {
|
|
|
134
242
|
process.stderr.write(`If the wrong browser opens, copy this URL to your correct browser:\n ${authUrl}\n`);
|
|
135
243
|
});
|
|
136
244
|
});
|
|
137
|
-
// Step 6: Exchange code for tokens
|
|
138
|
-
const token = await exchangeCodeForToken(base, code, client.clientId, client.clientSecret, redirectUri, pkce?.verifier, options?.tlsInsecure);
|
|
139
245
|
setCurrentPlatform(base);
|
|
140
246
|
return token;
|
|
141
247
|
});
|
|
@@ -275,40 +381,70 @@ export async function playwrightLogin(baseUrl, options) {
|
|
|
275
381
|
product: "adp",
|
|
276
382
|
});
|
|
277
383
|
const authUrl = `${base}/oauth2/auth?${authParams.toString()}`;
|
|
278
|
-
// Step 4: Start local callback server
|
|
279
|
-
|
|
384
|
+
// Step 4: Start local callback server; exchange code inside handler, then show credentials HTML
|
|
385
|
+
let browser;
|
|
386
|
+
const token = await new Promise((resolve, reject) => {
|
|
280
387
|
const TIMEOUT_MS = hasCredentials ? 30_000 : 120_000;
|
|
388
|
+
let server;
|
|
281
389
|
const timeoutId = setTimeout(() => {
|
|
282
|
-
server
|
|
390
|
+
server?.close();
|
|
283
391
|
browser?.close();
|
|
284
392
|
reject(new Error(`OAuth2 login timed out (${TIMEOUT_MS / 1000}s). No authorization code received.`));
|
|
285
393
|
}, TIMEOUT_MS);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
394
|
+
server = createServer((req, res) => {
|
|
395
|
+
void (async () => {
|
|
396
|
+
try {
|
|
397
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
398
|
+
if (url.pathname !== "/callback") {
|
|
399
|
+
res.writeHead(404);
|
|
400
|
+
res.end();
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const receivedState = url.searchParams.get("state");
|
|
404
|
+
const receivedCode = url.searchParams.get("code");
|
|
405
|
+
if (receivedState !== state) {
|
|
406
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
407
|
+
res.end(buildCallbackExchangeErrorHtml("OAuth2 state mismatch — possible CSRF attack."));
|
|
408
|
+
clearTimeout(timeoutId);
|
|
409
|
+
server.close();
|
|
410
|
+
browser?.close();
|
|
411
|
+
reject(new Error("OAuth2 state mismatch — possible CSRF attack."));
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (!receivedCode) {
|
|
415
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
416
|
+
res.end(buildCallbackExchangeErrorHtml("No authorization code received in callback."));
|
|
417
|
+
clearTimeout(timeoutId);
|
|
418
|
+
server.close();
|
|
419
|
+
browser?.close();
|
|
420
|
+
reject(new Error("No authorization code received in callback."));
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const exchanged = await exchangeCodeForToken(base, receivedCode, client.clientId, client.clientSecret, redirectUri, undefined, options?.tlsInsecure);
|
|
424
|
+
const copyCommand = buildCopyCommand(base, client.clientId, client.clientSecret, exchanged.refreshToken, options?.tlsInsecure);
|
|
425
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
426
|
+
res.end(buildCallbackHtml(copyCommand));
|
|
427
|
+
clearTimeout(timeoutId);
|
|
428
|
+
server.close();
|
|
429
|
+
browser?.close();
|
|
430
|
+
resolve(exchanged);
|
|
301
431
|
}
|
|
302
|
-
|
|
303
|
-
|
|
432
|
+
catch (err) {
|
|
433
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
434
|
+
try {
|
|
435
|
+
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
436
|
+
res.end(buildCallbackExchangeErrorHtml(message));
|
|
437
|
+
}
|
|
438
|
+
catch {
|
|
439
|
+
/* response may already be sent */
|
|
440
|
+
}
|
|
441
|
+
clearTimeout(timeoutId);
|
|
442
|
+
server.close();
|
|
443
|
+
browser?.close();
|
|
444
|
+
reject(err instanceof Error ? err : new Error(message));
|
|
304
445
|
}
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
res.writeHead(404);
|
|
308
|
-
res.end();
|
|
309
|
-
}
|
|
446
|
+
})();
|
|
310
447
|
});
|
|
311
|
-
let browser;
|
|
312
448
|
server.listen(port, "127.0.0.1", async () => {
|
|
313
449
|
try {
|
|
314
450
|
browser = await chromium.launch({ headless: hasCredentials });
|
|
@@ -334,12 +470,48 @@ export async function playwrightLogin(baseUrl, options) {
|
|
|
334
470
|
}
|
|
335
471
|
});
|
|
336
472
|
});
|
|
337
|
-
|
|
338
|
-
|
|
473
|
+
if (hasCredentials) {
|
|
474
|
+
const copyCommand = buildCopyCommand(base, client.clientId, client.clientSecret, token.refreshToken, options?.tlsInsecure);
|
|
475
|
+
process.stderr.write("\nHeadless login: copy this command and run it on a machine without a browser, or use `kweaver auth export`:\n\n" +
|
|
476
|
+
copyCommand +
|
|
477
|
+
"\n\n");
|
|
478
|
+
}
|
|
339
479
|
setCurrentPlatform(base);
|
|
340
480
|
return token;
|
|
341
481
|
});
|
|
342
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* Log in on a headless machine using OAuth2 client credentials and a refresh token (no browser).
|
|
485
|
+
* Exchanges the refresh token for a new access token and persists ~/.kweaver/ state.
|
|
486
|
+
*/
|
|
487
|
+
export async function refreshTokenLogin(baseUrl, options) {
|
|
488
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
489
|
+
const redirectUri = `http://127.0.0.1:${DEFAULT_REDIRECT_PORT}/callback`;
|
|
490
|
+
const client = {
|
|
491
|
+
baseUrl: base,
|
|
492
|
+
clientId: options.clientId,
|
|
493
|
+
clientSecret: options.clientSecret,
|
|
494
|
+
redirectUri,
|
|
495
|
+
logoutRedirectUri: redirectUri.replace("/callback", "/successful-logout"),
|
|
496
|
+
scope: DEFAULT_SCOPE,
|
|
497
|
+
lang: "zh-cn",
|
|
498
|
+
product: "adp",
|
|
499
|
+
xForwardedPrefix: "",
|
|
500
|
+
};
|
|
501
|
+
saveClientConfig(base, client);
|
|
502
|
+
const synthetic = {
|
|
503
|
+
baseUrl: base,
|
|
504
|
+
accessToken: "",
|
|
505
|
+
tokenType: "Bearer",
|
|
506
|
+
scope: "",
|
|
507
|
+
refreshToken: options.refreshToken,
|
|
508
|
+
obtainedAt: new Date().toISOString(),
|
|
509
|
+
...(options.tlsInsecure ? { tlsInsecure: true } : {}),
|
|
510
|
+
};
|
|
511
|
+
const token = await runWithTlsInsecure(options.tlsInsecure, () => refreshAccessToken(synthetic));
|
|
512
|
+
setCurrentPlatform(base);
|
|
513
|
+
return token;
|
|
514
|
+
}
|
|
343
515
|
function tokenNeedsRefresh(token) {
|
|
344
516
|
if (!token.expiresAt) {
|
|
345
517
|
return false;
|
|
@@ -420,6 +592,15 @@ export async function refreshAccessToken(token) {
|
|
|
420
592
|
saveTokenConfig(newToken);
|
|
421
593
|
return newToken;
|
|
422
594
|
}
|
|
595
|
+
/**
|
|
596
|
+
* Resolve a usable access token for the current platform.
|
|
597
|
+
*
|
|
598
|
+
* **Default behavior** (saved `~/.kweaver/` session from OAuth2 code login): when the access
|
|
599
|
+
* token is expired or near expiry, automatically exchanges the saved **refresh_token** for a new
|
|
600
|
+
* access token (OAuth2 `refresh_token` grant) and persists it. No extra flags are required.
|
|
601
|
+
*
|
|
602
|
+
* Static env `KWEAVER_TOKEN` bypasses refresh (see implementation).
|
|
603
|
+
*/
|
|
423
604
|
export async function ensureValidToken(opts) {
|
|
424
605
|
const envToken = process.env.KWEAVER_TOKEN;
|
|
425
606
|
const envBaseUrl = process.env.KWEAVER_BASE_URL;
|
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,8 @@ Usage:
|
|
|
18
18
|
|
|
19
19
|
kweaver auth <platform-url> [--alias name] [-u user] [-p pass] [--playwright] [--insecure|-k]
|
|
20
20
|
kweaver auth login <platform-url> (alias for auth <url>)
|
|
21
|
+
kweaver auth login <url> --client-id ID --client-secret S --refresh-token T (run on host without browser)
|
|
22
|
+
kweaver auth export [platform-url|alias] [--json]
|
|
21
23
|
kweaver auth status [platform-url|alias]
|
|
22
24
|
kweaver auth list
|
|
23
25
|
kweaver auth use <platform-url|alias>
|
|
@@ -51,6 +53,7 @@ Usage:
|
|
|
51
53
|
kweaver dataview list [--datasource-id id] [--type atomic|custom] [--limit n] [-bd value] [--pretty]
|
|
52
54
|
kweaver dataview find --name <name> [--exact] [--datasource-id id] [--wait] [--timeout ms] [-bd value] [--pretty]
|
|
53
55
|
kweaver dataview get <id> [-bd value] [--pretty]
|
|
56
|
+
kweaver dataview query <id> [--sql sql] [--limit n] [--offset n] [--need-total] [-bd value] [--pretty]
|
|
54
57
|
kweaver dataview delete <id> [-y] [-bd value]
|
|
55
58
|
|
|
56
59
|
kweaver bkn list [options]
|
|
@@ -74,6 +77,7 @@ Usage:
|
|
|
74
77
|
kweaver bkn action-log list|get|cancel <kn-id> ...
|
|
75
78
|
|
|
76
79
|
kweaver config set-bd <value>
|
|
80
|
+
kweaver config list-bd
|
|
77
81
|
kweaver config show
|
|
78
82
|
|
|
79
83
|
kweaver vega health|stats|inspect
|
|
@@ -96,7 +100,7 @@ Commands:
|
|
|
96
100
|
call (curl) Call an API with curl-style flags and auto-injected token headers
|
|
97
101
|
agent Agent CRUD, chat, sessions, history, publish/unpublish
|
|
98
102
|
ds Manage datasources (list, get, delete, tables, connect)
|
|
99
|
-
dataview
|
|
103
|
+
dataview|dv List, find, get, query (SQL), delete data views (atomic / custom)
|
|
100
104
|
bkn Knowledge network (CRUD, build, validate, export, stats, push/pull,
|
|
101
105
|
object-type, relation-type, subgraph, action-type, action-execution, action-log)
|
|
102
106
|
config Per-platform configuration (business domain)
|
|
@@ -127,7 +131,7 @@ export async function run(argv) {
|
|
|
127
131
|
if (command === "ds") {
|
|
128
132
|
return runDsCommand(rest);
|
|
129
133
|
}
|
|
130
|
-
if (command === "dataview") {
|
|
134
|
+
if (command === "dataview" || command === "dv") {
|
|
131
135
|
return runDataviewCommand(rest);
|
|
132
136
|
}
|
|
133
137
|
if (command === "token") {
|
package/dist/commands/agent.js
CHANGED
|
@@ -45,7 +45,7 @@ export function formatSimpleAgentList(text, pretty) {
|
|
|
45
45
|
export function parseAgentListArgs(args) {
|
|
46
46
|
let name = "";
|
|
47
47
|
let offset = 0;
|
|
48
|
-
let limit =
|
|
48
|
+
let limit = 30;
|
|
49
49
|
let category_id = "";
|
|
50
50
|
let custom_space_id = "";
|
|
51
51
|
let is_to_square = 1;
|
|
@@ -70,9 +70,9 @@ export function parseAgentListArgs(args) {
|
|
|
70
70
|
continue;
|
|
71
71
|
}
|
|
72
72
|
if (arg === "--limit") {
|
|
73
|
-
limit = parseInt(args[i + 1] ?? "
|
|
73
|
+
limit = parseInt(args[i + 1] ?? "30", 10);
|
|
74
74
|
if (Number.isNaN(limit) || limit < 1)
|
|
75
|
-
limit =
|
|
75
|
+
limit = 30;
|
|
76
76
|
i += 1;
|
|
77
77
|
continue;
|
|
78
78
|
}
|
|
@@ -134,7 +134,7 @@ export function parseAgentSessionsArgs(args) {
|
|
|
134
134
|
throw new Error("Missing agent_id");
|
|
135
135
|
}
|
|
136
136
|
let businessDomain = "";
|
|
137
|
-
let limit;
|
|
137
|
+
let limit = 30;
|
|
138
138
|
let pretty = true;
|
|
139
139
|
for (let i = 1; i < args.length; i += 1) {
|
|
140
140
|
const arg = args[i];
|
|
@@ -150,9 +150,9 @@ export function parseAgentSessionsArgs(args) {
|
|
|
150
150
|
continue;
|
|
151
151
|
}
|
|
152
152
|
if (arg === "--limit") {
|
|
153
|
-
limit = parseInt(args[i + 1] ?? "
|
|
153
|
+
limit = parseInt(args[i + 1] ?? "30", 10);
|
|
154
154
|
if (Number.isNaN(limit) || limit < 1)
|
|
155
|
-
limit =
|
|
155
|
+
limit = 30;
|
|
156
156
|
i += 1;
|
|
157
157
|
continue;
|
|
158
158
|
}
|
|
@@ -176,7 +176,7 @@ export function parseAgentHistoryArgs(args) {
|
|
|
176
176
|
throw new Error("Missing conversation_id");
|
|
177
177
|
}
|
|
178
178
|
let businessDomain = "";
|
|
179
|
-
let limit;
|
|
179
|
+
let limit = 30;
|
|
180
180
|
let pretty = true;
|
|
181
181
|
for (let i = 1; i < args.length; i += 1) {
|
|
182
182
|
const arg = args[i];
|
|
@@ -192,9 +192,9 @@ export function parseAgentHistoryArgs(args) {
|
|
|
192
192
|
continue;
|
|
193
193
|
}
|
|
194
194
|
if (arg === "--limit") {
|
|
195
|
-
limit = parseInt(args[i + 1] ?? "
|
|
195
|
+
limit = parseInt(args[i + 1] ?? "30", 10);
|
|
196
196
|
if (Number.isNaN(limit) || limit < 1)
|
|
197
|
-
limit =
|
|
197
|
+
limit = 30;
|
|
198
198
|
i += 1;
|
|
199
199
|
continue;
|
|
200
200
|
}
|
|
@@ -309,7 +309,7 @@ List published agents from the agent-factory API.
|
|
|
309
309
|
Options:
|
|
310
310
|
--name <text> Filter by name
|
|
311
311
|
--offset <n> Pagination offset (default: 0)
|
|
312
|
-
--limit <n> Max items to return (default:
|
|
312
|
+
--limit <n> Max items to return (default: 30)
|
|
313
313
|
--category-id <id> Filter by category
|
|
314
314
|
--custom-space-id <id> Filter by custom space
|
|
315
315
|
--is-to-square <0|1> Is to square (default: 1)
|
|
@@ -326,7 +326,7 @@ Options:
|
|
|
326
326
|
List all conversations for an agent.
|
|
327
327
|
|
|
328
328
|
Options:
|
|
329
|
-
--limit <n> Max conversations to return
|
|
329
|
+
--limit <n> Max conversations to return (default: 30)
|
|
330
330
|
-bd, --biz-domain <value> Business domain (default: bd_public)
|
|
331
331
|
--pretty Pretty-print JSON output (default)`);
|
|
332
332
|
return 0;
|
|
@@ -339,7 +339,7 @@ Options:
|
|
|
339
339
|
Show message history for a conversation.
|
|
340
340
|
|
|
341
341
|
Options:
|
|
342
|
-
--limit <n> Max messages to return
|
|
342
|
+
--limit <n> Max messages to return (default: 30)
|
|
343
343
|
-bd, --biz-domain <value> Business domain (default: bd_public)
|
|
344
344
|
--pretty Pretty-print JSON output (default)`);
|
|
345
345
|
return 0;
|
|
@@ -462,7 +462,7 @@ List published agents from the agent-factory API.
|
|
|
462
462
|
Options:
|
|
463
463
|
--name <text> Filter by name
|
|
464
464
|
--offset <n> Pagination offset (default: 0)
|
|
465
|
-
--limit <n> Max items to return (default:
|
|
465
|
+
--limit <n> Max items to return (default: 30)
|
|
466
466
|
--category-id <id> Filter by category
|
|
467
467
|
--custom-space-id <id> Filter by custom space
|
|
468
468
|
--is-to-square <0|1> Is to square (default: 1)
|
|
@@ -509,7 +509,7 @@ async function runAgentSessionsCommand(args) {
|
|
|
509
509
|
List all conversations for an agent.
|
|
510
510
|
|
|
511
511
|
Options:
|
|
512
|
-
--limit <n> Max conversations to return
|
|
512
|
+
--limit <n> Max conversations to return (default: 30)
|
|
513
513
|
-bd, --biz-domain <value> Business domain (default: bd_public)
|
|
514
514
|
--pretty Pretty-print JSON output (default)`);
|
|
515
515
|
return 0;
|
|
@@ -546,7 +546,7 @@ async function runAgentHistoryCommand(args) {
|
|
|
546
546
|
Show message history for a conversation.
|
|
547
547
|
|
|
548
548
|
Options:
|
|
549
|
-
--limit <n> Max messages to return
|
|
549
|
+
--limit <n> Max messages to return (default: 30)
|
|
550
550
|
-bd, --biz-domain <value> Business domain (default: bd_public)
|
|
551
551
|
--pretty Pretty-print JSON output (default)`);
|
|
552
552
|
return 0;
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { clearPlatformSession, deletePlatform, getConfigDir, getCurrentPlatform, getPlatformAlias, hasPlatform, listPlatforms, loadTokenConfig, resolvePlatformIdentifier, setCurrentPlatform, setPlatformAlias, } from "../config/store.js";
|
|
2
|
-
import { formatHttpError, normalizeBaseUrl, oauth2Login, playwrightLogin, } from "../auth/oauth.js";
|
|
1
|
+
import { autoSelectBusinessDomain, clearPlatformSession, deletePlatform, getConfigDir, getCurrentPlatform, getPlatformAlias, hasPlatform, listPlatforms, loadClientConfig, loadTokenConfig, resolvePlatformIdentifier, setCurrentPlatform, setPlatformAlias, } from "../config/store.js";
|
|
2
|
+
import { buildCopyCommand, formatHttpError, normalizeBaseUrl, oauth2Login, playwrightLogin, refreshTokenLogin, } from "../auth/oauth.js";
|
|
3
3
|
export async function runAuthCommand(args) {
|
|
4
4
|
const target = args[0];
|
|
5
5
|
const rest = args.slice(1);
|
|
6
6
|
if (!target || target === "--help" || target === "-h") {
|
|
7
7
|
console.log(`kweaver auth login <url> [options] Login to a platform (browser OAuth2 by default)
|
|
8
8
|
kweaver auth <url> Login (shorthand; same options as login)
|
|
9
|
+
kweaver auth export [url|alias] [--json] Export credentials; run printed command on a headless host
|
|
9
10
|
kweaver auth status [url|alias] Show current auth status
|
|
10
11
|
kweaver auth list List saved platforms
|
|
11
12
|
kweaver auth use <url|alias> Switch active platform
|
|
@@ -18,6 +19,9 @@ Login options:
|
|
|
18
19
|
Use the platform's web app client ID to get the same permissions
|
|
19
20
|
as the browser. Find it in DevTools: /oauth2/auth?client_id=<id>
|
|
20
21
|
--client-secret <s> Client secret (omit for public/PKCE clients)
|
|
22
|
+
--refresh-token <t> Use on a machine without a browser: exchange refresh token for access token.
|
|
23
|
+
Requires --client-id and --client-secret.
|
|
24
|
+
Get these from the callback page after browser login or \`auth export\`.
|
|
21
25
|
-u, --username Username (with -p triggers Playwright headless login)
|
|
22
26
|
-p, --password Password
|
|
23
27
|
--playwright Force Playwright browser login even without -u/-p
|
|
@@ -26,7 +30,7 @@ Login options:
|
|
|
26
30
|
}
|
|
27
31
|
if (target === "login") {
|
|
28
32
|
if (rest[0] === "--help" || rest[0] === "-h") {
|
|
29
|
-
console.log(`kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass] [--playwright]`);
|
|
33
|
+
console.log(`kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass] [--playwright] [--refresh-token T --client-id ID --client-secret S]`);
|
|
30
34
|
return 0;
|
|
31
35
|
}
|
|
32
36
|
const url = rest[0];
|
|
@@ -36,7 +40,11 @@ Login options:
|
|
|
36
40
|
}
|
|
37
41
|
return runAuthCommand([url, ...rest.slice(1)]);
|
|
38
42
|
}
|
|
39
|
-
if (target
|
|
43
|
+
if (target === "export") {
|
|
44
|
+
return runAuthExportCommand(rest);
|
|
45
|
+
}
|
|
46
|
+
const LOGIN_SUBCOMMANDS = new Set(["status", "list", "use", "delete", "logout", "export"]);
|
|
47
|
+
if (target && !LOGIN_SUBCOMMANDS.has(target)) {
|
|
40
48
|
try {
|
|
41
49
|
const normalizedTarget = normalizeBaseUrl(target);
|
|
42
50
|
const alias = readOption(args, "--alias");
|
|
@@ -45,9 +53,21 @@ Login options:
|
|
|
45
53
|
const usePlaywright = args.includes("--playwright");
|
|
46
54
|
const clientId = readOption(args, "--client-id");
|
|
47
55
|
const clientSecret = readOption(args, "--client-secret");
|
|
56
|
+
const refreshToken = readOption(args, "--refresh-token");
|
|
48
57
|
const tlsInsecure = args.includes("--insecure") || args.includes("-k");
|
|
49
58
|
let token;
|
|
50
|
-
if (
|
|
59
|
+
if (refreshToken) {
|
|
60
|
+
if (!clientId || !clientSecret) {
|
|
61
|
+
console.error("--refresh-token requires --client-id and --client-secret.\n");
|
|
62
|
+
console.error("Get these values from the callback page after a browser login or `kweaver auth export`.");
|
|
63
|
+
return 1;
|
|
64
|
+
}
|
|
65
|
+
console.log("Logging in with refresh token (no browser)...");
|
|
66
|
+
token = await refreshTokenLogin(normalizedTarget, {
|
|
67
|
+
clientId, clientSecret, refreshToken, tlsInsecure,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
else if (username && password) {
|
|
51
71
|
// Headless Playwright login with credentials
|
|
52
72
|
console.log("Logging in (headless)...");
|
|
53
73
|
token = await playwrightLogin(normalizedTarget, { username, password, tlsInsecure });
|
|
@@ -95,6 +115,10 @@ Login options:
|
|
|
95
115
|
if (token.expiresAt) {
|
|
96
116
|
console.log(`Token expires at: ${token.expiresAt}`);
|
|
97
117
|
}
|
|
118
|
+
const selectedBd = await autoSelectBusinessDomain(normalizedTarget, token.accessToken, {
|
|
119
|
+
tlsInsecure: token.tlsInsecure,
|
|
120
|
+
});
|
|
121
|
+
console.log(`Business domain: ${selectedBd}`);
|
|
98
122
|
return 0;
|
|
99
123
|
}
|
|
100
124
|
catch (error) {
|
|
@@ -213,7 +237,7 @@ Login options:
|
|
|
213
237
|
return 0;
|
|
214
238
|
}
|
|
215
239
|
console.error("Usage: kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass] [--playwright]");
|
|
216
|
-
console.error(" kweaver auth
|
|
240
|
+
console.error(" kweaver auth export [platform-url|alias] [--json]");
|
|
217
241
|
console.error(" kweaver auth status [platform-url|alias]");
|
|
218
242
|
console.error(" kweaver auth list");
|
|
219
243
|
console.error(" kweaver auth use <platform-url|alias>");
|
|
@@ -221,6 +245,60 @@ Login options:
|
|
|
221
245
|
console.error(" kweaver auth delete <platform-url|alias>");
|
|
222
246
|
return 1;
|
|
223
247
|
}
|
|
248
|
+
async function runAuthExportCommand(args) {
|
|
249
|
+
if (args[0] === "--help" || args[0] === "-h") {
|
|
250
|
+
console.log(`kweaver auth export [platform-url|alias] [--json]
|
|
251
|
+
|
|
252
|
+
Export OAuth2 credentials for copying to a headless host (no browser there).
|
|
253
|
+
Prints clientId, clientSecret, refreshToken, and a command to run on that machine.
|
|
254
|
+
|
|
255
|
+
Options:
|
|
256
|
+
--json Output as JSON (machine-readable)`);
|
|
257
|
+
return 0;
|
|
258
|
+
}
|
|
259
|
+
const jsonOutput = args.includes("--json");
|
|
260
|
+
const positional = args.find((a) => !a.startsWith("-"));
|
|
261
|
+
const resolved = positional ? resolvePlatformIdentifier(positional) : null;
|
|
262
|
+
const platform = resolved && /^https?:\/\//.test(resolved) ? normalizeBaseUrl(resolved) : resolved ?? getCurrentPlatform();
|
|
263
|
+
if (!platform) {
|
|
264
|
+
console.error("No active platform. Run `kweaver auth login <platform-url>` first.");
|
|
265
|
+
return 1;
|
|
266
|
+
}
|
|
267
|
+
const client = loadClientConfig(platform);
|
|
268
|
+
const token = loadTokenConfig(platform);
|
|
269
|
+
const clientId = client?.clientId ?? "";
|
|
270
|
+
const clientSecret = client?.clientSecret ?? "";
|
|
271
|
+
const refreshToken = token?.refreshToken ?? "";
|
|
272
|
+
const tlsInsecure = token?.tlsInsecure;
|
|
273
|
+
if (!clientId || !refreshToken) {
|
|
274
|
+
console.error(`Incomplete credentials for ${platform}.\n` +
|
|
275
|
+
(!clientId ? " Missing: client registration (client.json)\n" : "") +
|
|
276
|
+
(!refreshToken ? " Missing: refresh token (token.json)\n" : "") +
|
|
277
|
+
`Run \`kweaver auth login ${platform}\` first.`);
|
|
278
|
+
return 1;
|
|
279
|
+
}
|
|
280
|
+
if (jsonOutput) {
|
|
281
|
+
console.log(JSON.stringify({
|
|
282
|
+
baseUrl: platform,
|
|
283
|
+
clientId,
|
|
284
|
+
clientSecret,
|
|
285
|
+
refreshToken,
|
|
286
|
+
...(tlsInsecure ? { tlsInsecure: true } : {}),
|
|
287
|
+
}));
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
const cmd = buildCopyCommand(platform, clientId, clientSecret, refreshToken, tlsInsecure);
|
|
291
|
+
console.log(`Platform: ${platform}`);
|
|
292
|
+
console.log(`Client ID: ${clientId}`);
|
|
293
|
+
console.log(`Client Secret: ${clientSecret || "(none)"}`);
|
|
294
|
+
console.log(`Refresh Token: ${refreshToken}`);
|
|
295
|
+
console.log("");
|
|
296
|
+
console.log("On a machine without a browser, run:\n");
|
|
297
|
+
console.log(` ${cmd}`);
|
|
298
|
+
console.log("");
|
|
299
|
+
console.log("Keep these credentials secure.");
|
|
300
|
+
return 0;
|
|
301
|
+
}
|
|
224
302
|
function readOption(args, name) {
|
|
225
303
|
const index = args.findIndex((arg) => arg === name);
|
|
226
304
|
if (index === -1) {
|