@kweaver-ai/kweaver-sdk 0.4.5 → 0.4.7
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/bin/kweaver.js +17 -2
- package/dist/auth/oauth.d.ts +16 -0
- package/dist/auth/oauth.js +159 -1
- package/dist/cli.js +16 -2
- package/dist/commands/auth.js +23 -3
- package/dist/commands/bkn.js +58 -3
- package/package.json +1 -1
package/bin/kweaver.js
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
function exit(code) {
|
|
4
|
+
if (process.stdout.writableNeedDrain || process.stderr.writableNeedDrain) {
|
|
5
|
+
const done = () => {
|
|
6
|
+
if (!process.stdout.writableNeedDrain && !process.stderr.writableNeedDrain) {
|
|
7
|
+
process.exit(code);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
process.stdout.once("drain", done);
|
|
11
|
+
process.stderr.once("drain", done);
|
|
12
|
+
} else {
|
|
13
|
+
process.exit(code);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
2
17
|
import("../dist/cli.js").then(({ run }) => {
|
|
3
18
|
run(process.argv.slice(2))
|
|
4
|
-
.then((code) =>
|
|
19
|
+
.then((code) => exit(code))
|
|
5
20
|
.catch((err) => {
|
|
6
21
|
console.error(err instanceof Error ? err.message : String(err));
|
|
7
|
-
|
|
22
|
+
exit(1);
|
|
8
23
|
});
|
|
9
24
|
});
|
package/dist/auth/oauth.d.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { type TokenConfig } from "../config/store.js";
|
|
2
2
|
export declare function normalizeBaseUrl(value: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* OAuth2 Authorization Code login flow.
|
|
5
|
+
* 1. Register client (if not already registered)
|
|
6
|
+
* 2. Open browser to /oauth2/auth
|
|
7
|
+
* 3. Receive authorization code via local HTTP callback
|
|
8
|
+
* 4. Exchange code for access_token + refresh_token
|
|
9
|
+
* 5. Save token.json + client.json to ~/.kweaver/
|
|
10
|
+
*/
|
|
11
|
+
export declare function oauth2Login(baseUrl: string, options?: {
|
|
12
|
+
port?: number;
|
|
13
|
+
scope?: string;
|
|
14
|
+
}): Promise<TokenConfig>;
|
|
15
|
+
/**
|
|
16
|
+
* Playwright cookie login (legacy fallback).
|
|
17
|
+
* Does NOT produce a refresh_token — token expires in 1 hour with no auto-refresh.
|
|
18
|
+
*/
|
|
3
19
|
export declare function playwrightLogin(baseUrl: string, options?: {
|
|
4
20
|
username?: string;
|
|
5
21
|
password?: string;
|
package/dist/auth/oauth.js
CHANGED
|
@@ -1,11 +1,169 @@
|
|
|
1
|
-
import { getCurrentPlatform, loadClientConfig, loadTokenConfig, saveTokenConfig, setCurrentPlatform, } from "../config/store.js";
|
|
1
|
+
import { getCurrentPlatform, loadClientConfig, loadTokenConfig, saveClientConfig, saveTokenConfig, setCurrentPlatform, } from "../config/store.js";
|
|
2
2
|
import { HttpError, NetworkRequestError } from "../utils/http.js";
|
|
3
3
|
const TOKEN_TTL_SECONDS = 3600;
|
|
4
4
|
/** Seconds before access token expiry to trigger refresh (matches Python ConfigAuth). */
|
|
5
5
|
const REFRESH_THRESHOLD_SEC = 60;
|
|
6
|
+
const DEFAULT_REDIRECT_PORT = 9010;
|
|
7
|
+
const DEFAULT_SCOPE = "openid offline all";
|
|
6
8
|
export function normalizeBaseUrl(value) {
|
|
7
9
|
return value.replace(/\/+$/, "");
|
|
8
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* OAuth2 Authorization Code login flow.
|
|
13
|
+
* 1. Register client (if not already registered)
|
|
14
|
+
* 2. Open browser to /oauth2/auth
|
|
15
|
+
* 3. Receive authorization code via local HTTP callback
|
|
16
|
+
* 4. Exchange code for access_token + refresh_token
|
|
17
|
+
* 5. Save token.json + client.json to ~/.kweaver/
|
|
18
|
+
*/
|
|
19
|
+
export async function oauth2Login(baseUrl, options) {
|
|
20
|
+
const { createServer } = await import("node:http");
|
|
21
|
+
const { randomBytes } = await import("node:crypto");
|
|
22
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
23
|
+
const port = options?.port ?? DEFAULT_REDIRECT_PORT;
|
|
24
|
+
const scope = options?.scope ?? DEFAULT_SCOPE;
|
|
25
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
26
|
+
// Step 1: Ensure registered client
|
|
27
|
+
let client = loadClientConfig(base);
|
|
28
|
+
if (!client?.clientId) {
|
|
29
|
+
client = await registerOAuth2Client(base, redirectUri, scope);
|
|
30
|
+
saveClientConfig(base, client);
|
|
31
|
+
}
|
|
32
|
+
// Step 2: Generate CSRF state
|
|
33
|
+
const state = randomBytes(12).toString("hex");
|
|
34
|
+
// Step 3: Build authorization URL
|
|
35
|
+
const authParams = new URLSearchParams({
|
|
36
|
+
redirect_uri: redirectUri,
|
|
37
|
+
"x-forwarded-prefix": "",
|
|
38
|
+
client_id: client.clientId,
|
|
39
|
+
scope,
|
|
40
|
+
response_type: "code",
|
|
41
|
+
state,
|
|
42
|
+
lang: "zh-cn",
|
|
43
|
+
product: "adp",
|
|
44
|
+
});
|
|
45
|
+
const authUrl = `${base}/oauth2/auth?${authParams.toString()}`;
|
|
46
|
+
// Step 4: Start local callback server, wait for code
|
|
47
|
+
const code = await new Promise((resolve, reject) => {
|
|
48
|
+
const timeoutId = setTimeout(() => {
|
|
49
|
+
server.close();
|
|
50
|
+
reject(new Error("OAuth2 login timed out (120s). No authorization code received."));
|
|
51
|
+
}, 120_000);
|
|
52
|
+
const server = createServer((req, res) => {
|
|
53
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
54
|
+
if (url.pathname === "/callback") {
|
|
55
|
+
const receivedState = url.searchParams.get("state");
|
|
56
|
+
const receivedCode = url.searchParams.get("code");
|
|
57
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
58
|
+
res.end("<html><body><h2>Login successful. You can close this tab.</h2></body></html>");
|
|
59
|
+
clearTimeout(timeoutId);
|
|
60
|
+
server.close();
|
|
61
|
+
if (receivedState !== state) {
|
|
62
|
+
reject(new Error("OAuth2 state mismatch — possible CSRF attack."));
|
|
63
|
+
}
|
|
64
|
+
else if (!receivedCode) {
|
|
65
|
+
reject(new Error("No authorization code received in callback."));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
resolve(receivedCode);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
res.writeHead(404);
|
|
73
|
+
res.end();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
server.listen(port, "127.0.0.1", () => {
|
|
77
|
+
// Step 5: Open browser
|
|
78
|
+
import("node:child_process").then(({ exec }) => {
|
|
79
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
80
|
+
exec(`${cmd} "${authUrl}"`);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
// Step 6: Exchange code for tokens
|
|
85
|
+
const token = await exchangeCodeForToken(base, code, client.clientId, client.clientSecret, redirectUri);
|
|
86
|
+
setCurrentPlatform(base);
|
|
87
|
+
return token;
|
|
88
|
+
}
|
|
89
|
+
async function registerOAuth2Client(baseUrl, redirectUri, scope) {
|
|
90
|
+
const logoutUri = redirectUri.replace("/callback", "/successful-logout");
|
|
91
|
+
const response = await fetch(`${baseUrl}/oauth2/clients`, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
94
|
+
body: JSON.stringify({
|
|
95
|
+
client_name: "kweaver-sdk",
|
|
96
|
+
grant_types: ["authorization_code", "implicit", "refresh_token"],
|
|
97
|
+
response_types: ["token id_token", "code", "token"],
|
|
98
|
+
scope: "openid offline all",
|
|
99
|
+
redirect_uris: [redirectUri],
|
|
100
|
+
post_logout_redirect_uris: [logoutUri],
|
|
101
|
+
metadata: {
|
|
102
|
+
device: {
|
|
103
|
+
name: "kweaver-sdk",
|
|
104
|
+
client_type: "web",
|
|
105
|
+
description: "KWeaver TypeScript SDK",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
const text = await response.text();
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
throw new HttpError(response.status, response.statusText, text);
|
|
113
|
+
}
|
|
114
|
+
const data = JSON.parse(text);
|
|
115
|
+
return {
|
|
116
|
+
baseUrl,
|
|
117
|
+
clientId: data.client_id,
|
|
118
|
+
clientSecret: data.client_secret,
|
|
119
|
+
redirectUri,
|
|
120
|
+
logoutRedirectUri: logoutUri,
|
|
121
|
+
scope,
|
|
122
|
+
lang: "zh-cn",
|
|
123
|
+
product: "adp",
|
|
124
|
+
xForwardedPrefix: "",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function exchangeCodeForToken(baseUrl, code, clientId, clientSecret, redirectUri) {
|
|
128
|
+
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
129
|
+
const response = await fetch(`${baseUrl}/oauth2/token`, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
headers: {
|
|
132
|
+
Authorization: `Basic ${credentials}`,
|
|
133
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
134
|
+
Accept: "application/json",
|
|
135
|
+
},
|
|
136
|
+
body: new URLSearchParams({
|
|
137
|
+
grant_type: "authorization_code",
|
|
138
|
+
code,
|
|
139
|
+
redirect_uri: redirectUri,
|
|
140
|
+
}).toString(),
|
|
141
|
+
});
|
|
142
|
+
const text = await response.text();
|
|
143
|
+
if (!response.ok) {
|
|
144
|
+
throw new HttpError(response.status, response.statusText, text);
|
|
145
|
+
}
|
|
146
|
+
const data = JSON.parse(text);
|
|
147
|
+
const now = new Date();
|
|
148
|
+
const expiresIn = typeof data.expires_in === "number" ? data.expires_in : 3600;
|
|
149
|
+
const token = {
|
|
150
|
+
baseUrl,
|
|
151
|
+
accessToken: data.access_token,
|
|
152
|
+
tokenType: data.token_type ?? "Bearer",
|
|
153
|
+
scope: data.scope ?? "",
|
|
154
|
+
expiresIn,
|
|
155
|
+
expiresAt: new Date(now.getTime() + expiresIn * 1000).toISOString(),
|
|
156
|
+
refreshToken: data.refresh_token ?? "",
|
|
157
|
+
idToken: data.id_token ?? "",
|
|
158
|
+
obtainedAt: now.toISOString(),
|
|
159
|
+
};
|
|
160
|
+
saveTokenConfig(token);
|
|
161
|
+
return token;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Playwright cookie login (legacy fallback).
|
|
165
|
+
* Does NOT produce a refresh_token — token expires in 1 hour with no auto-refresh.
|
|
166
|
+
*/
|
|
9
167
|
export async function playwrightLogin(baseUrl, options) {
|
|
10
168
|
let chromium;
|
|
11
169
|
try {
|
package/dist/cli.js
CHANGED
|
@@ -87,14 +87,28 @@ export async function run(argv) {
|
|
|
87
87
|
printHelp();
|
|
88
88
|
return 1;
|
|
89
89
|
}
|
|
90
|
+
function safeExit(code) {
|
|
91
|
+
if (process.stdout.writableNeedDrain || process.stderr.writableNeedDrain) {
|
|
92
|
+
const done = () => {
|
|
93
|
+
if (!process.stdout.writableNeedDrain && !process.stderr.writableNeedDrain) {
|
|
94
|
+
process.exit(code);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
process.stdout.once("drain", done);
|
|
98
|
+
process.stderr.once("drain", done);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
process.exit(code);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
90
104
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
91
105
|
run(process.argv.slice(2))
|
|
92
106
|
.then((code) => {
|
|
93
|
-
|
|
107
|
+
safeExit(code);
|
|
94
108
|
})
|
|
95
109
|
.catch((error) => {
|
|
96
110
|
const message = error instanceof Error ? error.message : String(error);
|
|
97
111
|
console.error(message);
|
|
98
|
-
|
|
112
|
+
safeExit(1);
|
|
99
113
|
});
|
|
100
114
|
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { clearPlatformSession, deletePlatform, getConfigDir, getCurrentPlatform, getPlatformAlias, hasPlatform, listPlatforms, loadTokenConfig, resolvePlatformIdentifier, setCurrentPlatform, setPlatformAlias, } from "../config/store.js";
|
|
2
|
-
import { formatHttpError, normalizeBaseUrl, playwrightLogin, } from "../auth/oauth.js";
|
|
2
|
+
import { formatHttpError, normalizeBaseUrl, oauth2Login, playwrightLogin, } from "../auth/oauth.js";
|
|
3
3
|
export async function runAuthCommand(args) {
|
|
4
4
|
const target = args[0];
|
|
5
5
|
const rest = args.slice(1);
|
|
@@ -27,13 +27,23 @@ kweaver auth delete <url> Delete saved credentials`);
|
|
|
27
27
|
const alias = readOption(args, "--alias");
|
|
28
28
|
const username = readOption(args, "--username") ?? readOption(args, "-u");
|
|
29
29
|
const password = readOption(args, "--password") ?? readOption(args, "-p");
|
|
30
|
+
const usePlaywright = args.includes("--playwright");
|
|
31
|
+
let token;
|
|
30
32
|
if (username && password) {
|
|
33
|
+
// Headless Playwright login with credentials
|
|
31
34
|
console.log("Logging in (headless)...");
|
|
35
|
+
token = await playwrightLogin(normalizedTarget, { username, password });
|
|
36
|
+
}
|
|
37
|
+
else if (usePlaywright) {
|
|
38
|
+
// Explicit Playwright fallback
|
|
39
|
+
console.log("Opening browser for login (Playwright)...");
|
|
40
|
+
token = await playwrightLogin(normalizedTarget);
|
|
32
41
|
}
|
|
33
42
|
else {
|
|
34
|
-
|
|
43
|
+
// Default: OAuth2 authorization code flow (supports refresh_token)
|
|
44
|
+
console.log("Opening browser for OAuth2 login...");
|
|
45
|
+
token = await oauth2Login(normalizedTarget);
|
|
35
46
|
}
|
|
36
|
-
const token = await playwrightLogin(normalizedTarget, username && password ? { username, password } : undefined);
|
|
37
47
|
if (alias) {
|
|
38
48
|
setPlatformAlias(normalizedTarget, alias);
|
|
39
49
|
}
|
|
@@ -49,6 +59,12 @@ kweaver auth delete <url> Delete saved credentials`);
|
|
|
49
59
|
}
|
|
50
60
|
console.log(`Current platform: ${normalizedTarget}`);
|
|
51
61
|
console.log(`Access token saved: yes`);
|
|
62
|
+
if (token.refreshToken) {
|
|
63
|
+
console.log(`Refresh token: yes (auto-refresh enabled)`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log(`Refresh token: no (token will expire in 1 hour)`);
|
|
67
|
+
}
|
|
52
68
|
if (token.expiresAt) {
|
|
53
69
|
console.log(`Token expires at: ${token.expiresAt}`);
|
|
54
70
|
}
|
|
@@ -79,6 +95,7 @@ kweaver auth delete <url> Delete saved credentials`);
|
|
|
79
95
|
`Current platform: ${token.baseUrl === currentPlatform ? "yes" : "no"}`,
|
|
80
96
|
`Token present: yes`,
|
|
81
97
|
];
|
|
98
|
+
lines.push(`Refresh token: ${token.refreshToken ? "yes (auto-refresh enabled)" : "no"}`);
|
|
82
99
|
if (token.expiresAt) {
|
|
83
100
|
const expiry = new Date(token.expiresAt);
|
|
84
101
|
const remainingMs = expiry.getTime() - Date.now();
|
|
@@ -86,6 +103,9 @@ kweaver auth delete <url> Delete saved credentials`);
|
|
|
86
103
|
const remainingMin = Math.ceil(remainingMs / 60_000);
|
|
87
104
|
lines.push(`Token status: active (expires in ${remainingMin} min)`);
|
|
88
105
|
}
|
|
106
|
+
else if (token.refreshToken) {
|
|
107
|
+
lines.push(`Token status: expired (will auto-refresh on next command)`);
|
|
108
|
+
}
|
|
89
109
|
else {
|
|
90
110
|
lines.push(`Token status: expired (run \`kweaver auth login ${token.baseUrl}\` again)`);
|
|
91
111
|
}
|
package/dist/commands/bkn.js
CHANGED
|
@@ -456,6 +456,57 @@ function parseJsonObject(text, errorMessage) {
|
|
|
456
456
|
}
|
|
457
457
|
return parsed;
|
|
458
458
|
}
|
|
459
|
+
const MAX_OUTPUT_BYTES = 100_000;
|
|
460
|
+
/**
|
|
461
|
+
* If a query response exceeds MAX_OUTPUT_BYTES, trim the datas array
|
|
462
|
+
* to fit, preserving valid JSON and the search_after cursor for pagination.
|
|
463
|
+
*/
|
|
464
|
+
function truncateQueryResult(raw) {
|
|
465
|
+
if (raw.length <= MAX_OUTPUT_BYTES) {
|
|
466
|
+
return raw;
|
|
467
|
+
}
|
|
468
|
+
let parsed;
|
|
469
|
+
try {
|
|
470
|
+
parsed = JSON.parse(raw);
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
return raw;
|
|
474
|
+
}
|
|
475
|
+
const datas = parsed.datas;
|
|
476
|
+
if (!Array.isArray(datas) || datas.length === 0) {
|
|
477
|
+
return raw;
|
|
478
|
+
}
|
|
479
|
+
const originalCount = datas.length;
|
|
480
|
+
while (datas.length > 1) {
|
|
481
|
+
datas.pop();
|
|
482
|
+
const candidate = JSON.stringify(parsed);
|
|
483
|
+
if (candidate.length <= MAX_OUTPUT_BYTES) {
|
|
484
|
+
const remaining = originalCount - datas.length;
|
|
485
|
+
const sa = parsed.search_after;
|
|
486
|
+
parsed._truncated = {
|
|
487
|
+
returned: datas.length,
|
|
488
|
+
total_fetched: originalCount,
|
|
489
|
+
remaining,
|
|
490
|
+
next_search_after: sa ?? null,
|
|
491
|
+
hint: sa
|
|
492
|
+
? `Pass --search-after '${JSON.stringify(sa)}' --limit ${datas.length} to fetch the next page.`
|
|
493
|
+
: `Reduce --limit to ${datas.length} or less to avoid truncation.`,
|
|
494
|
+
};
|
|
495
|
+
console.error(`[warn] Truncated ${originalCount} → ${datas.length} records (output exceeded ${Math.round(MAX_OUTPUT_BYTES / 1024)}KB). ${parsed._truncated.hint}`);
|
|
496
|
+
return JSON.stringify(parsed);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
const sa = parsed.search_after;
|
|
500
|
+
parsed._truncated = {
|
|
501
|
+
returned: 1,
|
|
502
|
+
total_fetched: originalCount,
|
|
503
|
+
remaining: originalCount - 1,
|
|
504
|
+
next_search_after: sa ?? null,
|
|
505
|
+
hint: `Single record is very large. Use --limit 1 and --search-after to iterate.`,
|
|
506
|
+
};
|
|
507
|
+
console.error(`[warn] Truncated ${originalCount} → 1 record. Single record is very large. Use --limit 1 and --search-after to iterate.`);
|
|
508
|
+
return JSON.stringify(parsed);
|
|
509
|
+
}
|
|
459
510
|
function parseSearchAfterArray(text) {
|
|
460
511
|
let parsed;
|
|
461
512
|
try {
|
|
@@ -528,7 +579,7 @@ export function parseKnObjectTypeQueryArgs(args) {
|
|
|
528
579
|
body.search_after = searchAfter;
|
|
529
580
|
}
|
|
530
581
|
if (typeof body.limit !== "number" || !Number.isFinite(body.limit) || body.limit < 1) {
|
|
531
|
-
|
|
582
|
+
body.limit = 30;
|
|
532
583
|
}
|
|
533
584
|
if (!businessDomain)
|
|
534
585
|
businessDomain = resolveBusinessDomain();
|
|
@@ -916,7 +967,8 @@ kweaver bkn object-type properties <kn-id> <ot-id> '<json>' [--pretty] [-bd valu
|
|
|
916
967
|
list: List object types (schema) from ontology-manager.
|
|
917
968
|
get: Get single object type details.
|
|
918
969
|
create/update/delete: Schema CRUD (create requires dataview-id).
|
|
919
|
-
query
|
|
970
|
+
query: Query via ontology-query API. Default limit is 30 if not specified. Use --search-after for pagination.
|
|
971
|
+
properties: Query instance properties by primary key.
|
|
920
972
|
|
|
921
973
|
properties JSON format: {"_instance_identities":[{"<primary-key>":"<value>"}],"properties":["prop1","prop2"]}`);
|
|
922
974
|
return 0;
|
|
@@ -1016,7 +1068,7 @@ properties JSON format: {"_instance_identities":[{"<primary-key>":"<value>"}],"p
|
|
|
1016
1068
|
body: options.body,
|
|
1017
1069
|
businessDomain: options.businessDomain,
|
|
1018
1070
|
});
|
|
1019
|
-
console.log(formatCallOutput(result, options.pretty));
|
|
1071
|
+
console.log(formatCallOutput(truncateQueryResult(result), options.pretty));
|
|
1020
1072
|
return 0;
|
|
1021
1073
|
}
|
|
1022
1074
|
if (action === "properties") {
|
|
@@ -1343,6 +1395,9 @@ Query subgraph via ontology-query API. JSON body format see references/json-form
|
|
|
1343
1395
|
businessDomain,
|
|
1344
1396
|
queryType,
|
|
1345
1397
|
});
|
|
1398
|
+
if (result.length > 100_000) {
|
|
1399
|
+
console.error(`[warn] Response is ${(result.length / 1024).toFixed(0)}KB. Consider narrowing the subgraph query.`);
|
|
1400
|
+
}
|
|
1346
1401
|
console.log(formatCallOutput(result, pretty));
|
|
1347
1402
|
return 0;
|
|
1348
1403
|
}
|
package/package.json
CHANGED