@kweaver-ai/kweaver-sdk 0.5.2 → 0.6.0

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 (56) 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 +1 -1
  17. package/dist/auth/oauth.js +64 -7
  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 +80 -32
  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/http.d.ts +3 -0
  55. package/dist/utils/http.js +37 -1
  56. package/package.json +9 -5
@@ -1,4 +1,5 @@
1
- import { autoSelectBusinessDomain, clearPlatformSession, deletePlatform, deleteUser, getActiveUser, getConfigDir, getCurrentPlatform, getPlatformAlias, hasPlatform, listPlatforms, listUserProfiles, loadClientConfig, loadTokenConfig, resolvePlatformIdentifier, resolveUserId, setActiveUser, setCurrentPlatform, setPlatformAlias, } from "../config/store.js";
1
+ import { isNoAuth } from "../config/no-auth.js";
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";
2
3
  import { decodeJwtPayload } from "../config/jwt.js";
3
4
  import { buildCopyCommand, formatHttpError, normalizeBaseUrl, oauth2Login, playwrightLogin, refreshTokenLogin, } from "../auth/oauth.js";
4
5
  export async function runAuthCommand(args) {
@@ -29,16 +30,17 @@ Login options:
29
30
  --port <n> Local callback port (default: 9010). Use when 9010 is occupied.
30
31
  --redirect-uri <uri> Full OAuth2 redirect URI override. Localhost URIs start a local server;
31
32
  non-localhost URIs use a manual paste-the-callback-URL flow.
32
- Overrides --port. Example: http://127.0.0.1:8080/callback
33
+ When set, --port is ignored. Example: --redirect-uri http://127.0.0.1:9010/callback
33
34
  -u, --username Username (with -p triggers Playwright headless login)
34
35
  -p, --password Password
35
36
  --playwright Force Playwright browser login even without -u/-p
36
- --insecure, -k Skip TLS certificate verification (self-signed / dev HTTPS only)`);
37
+ --insecure, -k Skip TLS certificate verification (self-signed / dev HTTPS only)
38
+ --no-auth Save platform without OAuth (servers with no authentication). Same as detecting OAuth 404 during login.`);
37
39
  return 0;
38
40
  }
39
41
  if (target === "login") {
40
42
  if (rest[0] === "--help" || rest[0] === "-h") {
41
- console.log(`kweaver auth login <platform-url> [--alias <name>] [-u user] [-p pass] [--playwright] [--refresh-token T --client-id ID --client-secret S]`);
43
+ console.log(`kweaver auth login <platform-url> [--alias <name>] [--no-auth] [-u user] [-p pass] [--playwright] [--refresh-token T --client-id ID --client-secret S]`);
42
44
  return 0;
43
45
  }
44
46
  const url = rest[0];
@@ -75,12 +77,43 @@ Login options:
75
77
  const customPortStr = readOption(args, "--port");
76
78
  const customPort = customPortStr ? parseInt(customPortStr, 10) : undefined;
77
79
  const tlsInsecure = args.includes("--insecure") || args.includes("-k");
80
+ const noAuth = args.includes("--no-auth");
81
+ const KNOWN_LOGIN_FLAGS = new Set([
82
+ "--alias", "--client-id", "--client-secret", "--refresh-token",
83
+ "--port", "--redirect-uri", "--username", "-u", "--password", "-p",
84
+ "--playwright", "--insecure", "-k", "--no-auth",
85
+ ]);
86
+ const KNOWN_VALUE_FLAGS = new Set([
87
+ "--alias", "--client-id", "--client-secret", "--refresh-token",
88
+ "--port", "--redirect-uri", "--username", "-u", "--password", "-p",
89
+ ]);
90
+ for (let i = 0; i < args.length; i++) {
91
+ const a = args[i];
92
+ if (a.startsWith("-") && a !== target && !KNOWN_LOGIN_FLAGS.has(a)) {
93
+ console.error(`Unknown option: ${a}`);
94
+ console.error("Run 'kweaver auth --help' to see available options.");
95
+ return 1;
96
+ }
97
+ if (KNOWN_VALUE_FLAGS.has(a))
98
+ i++;
99
+ }
78
100
  if (customPort !== undefined && (Number.isNaN(customPort) || customPort < 1 || customPort > 65535)) {
79
101
  console.error("Invalid --port value. Expected a number between 1 and 65535.");
80
102
  return 1;
81
103
  }
104
+ if (noAuth && refreshToken) {
105
+ console.error("--no-auth cannot be used with --refresh-token.");
106
+ return 1;
107
+ }
108
+ if (noAuth && (username || password || usePlaywright)) {
109
+ console.error("--no-auth cannot be used with Playwright login or -u/-p.");
110
+ return 1;
111
+ }
82
112
  let token;
83
- if (refreshToken) {
113
+ if (noAuth) {
114
+ token = saveNoAuthPlatform(normalizedTarget, { tlsInsecure });
115
+ }
116
+ else if (refreshToken) {
84
117
  if (!clientId || !clientSecret) {
85
118
  console.error("--refresh-token requires --client-id and --client-secret.\n");
86
119
  console.error("Get these values from the callback page after a browser login or `kweaver auth export`.");
@@ -138,19 +171,26 @@ Login options:
138
171
  const userLabel = token.displayName ? `${token.displayName} (${activeUser})` : activeUser;
139
172
  console.log(`User: ${userLabel}`);
140
173
  }
141
- console.log(`Access token saved: yes`);
142
- if (token.refreshToken) {
143
- console.log(`Refresh token: yes (auto-refresh enabled)`);
174
+ if (isNoAuth(token.accessToken)) {
175
+ console.log(`Authentication: none (no-auth mode)`);
144
176
  }
145
177
  else {
178
+ console.log(`Access token saved: yes`);
179
+ }
180
+ if (!isNoAuth(token.accessToken) && token.refreshToken) {
181
+ console.log(`Refresh token: yes (auto-refresh enabled)`);
182
+ }
183
+ else if (!isNoAuth(token.accessToken)) {
146
184
  console.log(`Refresh token: no (token will expire in 1 hour)`);
147
185
  }
148
186
  if (token.expiresAt) {
149
187
  console.log(`Token expires at: ${token.expiresAt}`);
150
188
  }
151
- const selectedBd = await autoSelectBusinessDomain(normalizedTarget, token.accessToken, {
152
- tlsInsecure: token.tlsInsecure,
153
- });
189
+ const selectedBd = isNoAuth(token.accessToken)
190
+ ? resolveBusinessDomain(normalizedTarget)
191
+ : await autoSelectBusinessDomain(normalizedTarget, token.accessToken, {
192
+ tlsInsecure: token.tlsInsecure,
193
+ });
154
194
  console.log(`Business domain: ${selectedBd}`);
155
195
  return 0;
156
196
  }
@@ -178,29 +218,35 @@ Login options:
178
218
  `Platform: ${token.baseUrl}`,
179
219
  `Current platform: ${token.baseUrl === currentPlatform ? "yes" : "no"}`,
180
220
  ];
181
- const statusActiveUser = getActiveUser(platform);
182
- if (statusActiveUser) {
183
- const statusDisplayName = token.displayName;
184
- const userLabel = statusDisplayName ? `${statusDisplayName} (${statusActiveUser})` : statusActiveUser;
185
- lines.push(`User: ${userLabel}`);
221
+ if (isNoAuth(token.accessToken)) {
222
+ lines.push(`Authentication: none (no-auth mode)`);
223
+ lines.push(`User: default (built-in profile for no-auth platforms)`);
186
224
  }
187
- lines.push(`Token present: yes`);
188
- lines.push(`Refresh token: ${token.refreshToken ? "yes (auto-refresh enabled)" : "no"}`);
189
- if (token.tlsInsecure) {
190
- lines.push(`TLS: certificate verification disabled (saved; dev only)`);
191
- }
192
- if (token.expiresAt) {
193
- const expiry = new Date(token.expiresAt);
194
- const remainingMs = expiry.getTime() - Date.now();
195
- if (remainingMs > 0) {
196
- const remainingMin = Math.ceil(remainingMs / 60_000);
197
- lines.push(`Token status: active (expires in ${remainingMin} min)`);
225
+ else {
226
+ const statusActiveUser = getActiveUser(platform);
227
+ if (statusActiveUser) {
228
+ const statusDisplayName = token.displayName;
229
+ const userLabel = statusDisplayName ? `${statusDisplayName} (${statusActiveUser})` : statusActiveUser;
230
+ lines.push(`User: ${userLabel}`);
198
231
  }
199
- else if (token.refreshToken) {
200
- lines.push(`Token status: expired (will auto-refresh on next command)`);
232
+ lines.push(`Token present: yes`);
233
+ lines.push(`Refresh token: ${token.refreshToken ? "yes (auto-refresh enabled)" : "no"}`);
234
+ if (token.tlsInsecure) {
235
+ lines.push(`TLS: certificate verification disabled (saved; dev only)`);
201
236
  }
202
- else {
203
- lines.push(`Token status: expired (run \`kweaver auth login ${token.baseUrl}\` again)`);
237
+ if (token.expiresAt) {
238
+ const expiry = new Date(token.expiresAt);
239
+ const remainingMs = expiry.getTime() - Date.now();
240
+ if (remainingMs > 0) {
241
+ const remainingMin = Math.ceil(remainingMs / 60_000);
242
+ lines.push(`Token status: active (expires in ${remainingMin} min)`);
243
+ }
244
+ else if (token.refreshToken) {
245
+ lines.push(`Token status: expired (will auto-refresh on next command)`);
246
+ }
247
+ else {
248
+ lines.push(`Token status: expired (run \`kweaver auth login ${token.baseUrl}\` again)`);
249
+ }
204
250
  }
205
251
  }
206
252
  for (const line of lines) {
@@ -219,7 +265,9 @@ Login options:
219
265
  for (const platform of platforms) {
220
266
  const marker = platform.baseUrl === currentPlatform ? "*" : "-";
221
267
  const aliasPart = platform.alias ? ` (${platform.alias})` : "";
222
- console.log(`${marker} ${platform.baseUrl}${aliasPart}`);
268
+ const tok = loadTokenConfig(platform.baseUrl);
269
+ const noAuthPart = tok && isNoAuth(tok.accessToken) ? " (no-auth)" : "";
270
+ console.log(`${marker} ${platform.baseUrl}${aliasPart}${noAuthPart}`);
223
271
  const profiles = listUserProfiles(platform.baseUrl);
224
272
  const activeUser = getActiveUser(platform.baseUrl);
225
273
  for (let i = 0; i < profiles.length; i++) {
@@ -109,6 +109,28 @@ export function parseKnObjectTypeQueryArgs(args) {
109
109
  throw new Error("Usage: kweaver bkn object-type query <kn-id> <ot-id> ['<json>'] [--limit <n>] [--search-after '<json-array>'] [--pretty] [-bd value]");
110
110
  }
111
111
  const body = parseJsonObject(bodyText, "object-type query body must be a JSON object.");
112
+ // Detect likely misplaced filter fields in query body (#49)
113
+ // Instead of a brittle whitelist, detect the pattern: no "condition" key present,
114
+ // but there are keys with primitive values (string/number/boolean) — these are
115
+ // almost certainly field=value filters that belong inside a condition structure.
116
+ if (!("condition" in body)) {
117
+ const suspectKeys = Object.keys(body).filter((k) => {
118
+ const v = body[k];
119
+ return typeof v === "string" || typeof v === "number" || typeof v === "boolean";
120
+ });
121
+ // Exclude keys that are well-known query parameters with primitive values
122
+ const PRIMITIVE_QUERY_KEYS = new Set(["limit"]);
123
+ const misplacedKeys = suspectKeys.filter((k) => !PRIMITIVE_QUERY_KEYS.has(k));
124
+ if (misplacedKeys.length > 0) {
125
+ const keyList = misplacedKeys.map((k) => `"${k}"`).join(", ");
126
+ const hint = misplacedKeys.length === 1
127
+ ? `Example: {"limit":20,"condition":{"field":${JSON.stringify(misplacedKeys[0])},"operation":"==","value":"<your-value>"}}`
128
+ : `Example: {"limit":20,"condition":{"operation":"and","sub_conditions":[${misplacedKeys.map((k) => `{"field":${JSON.stringify(k)},"operation":"==","value":"<value>"}`).join(",")}]}}`;
129
+ throw new Error(`Likely misplaced filter field(s) ${keyList} in query body.\n` +
130
+ `Filter conditions must be wrapped in a "condition" structure.\n` +
131
+ hint);
132
+ }
133
+ }
112
134
  if (limit !== undefined) {
113
135
  body.limit = limit;
114
136
  }
@@ -1,4 +1,5 @@
1
1
  import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/oauth.js";
2
+ import { isNoAuth } from "../config/no-auth.js";
2
3
  import { HttpError } from "../utils/http.js";
3
4
  import { resolveBusinessDomain } from "../config/store.js";
4
5
  export function parseCallArgs(args) {
@@ -74,11 +75,13 @@ export function parseCallArgs(args) {
74
75
  return { url, method, headers, body, pretty, verbose, businessDomain };
75
76
  }
76
77
  function injectAuthHeaders(headers, accessToken, businessDomain) {
77
- if (!headers.has("authorization")) {
78
- headers.set("authorization", `Bearer ${accessToken}`);
79
- }
80
- if (!headers.has("token")) {
81
- headers.set("token", accessToken);
78
+ if (!isNoAuth(accessToken)) {
79
+ if (!headers.has("authorization")) {
80
+ headers.set("authorization", `Bearer ${accessToken}`);
81
+ }
82
+ if (!headers.has("token")) {
83
+ headers.set("token", accessToken);
84
+ }
82
85
  }
83
86
  if (!headers.has("x-business-domain")) {
84
87
  headers.set("x-business-domain", businessDomain);
@@ -0,0 +1 @@
1
+ export declare function runDataflowCommand(args: string[]): Promise<number>;
@@ -0,0 +1,251 @@
1
+ import { access, readFile } from "node:fs/promises";
2
+ import { constants } from "node:fs";
3
+ import columnify from "columnify";
4
+ import stringWidth from "string-width";
5
+ import yargs from "yargs";
6
+ import { ensureValidToken, formatHttpError, with401RefreshRetry } from "../auth/oauth.js";
7
+ import { resolveBusinessDomain } from "../config/store.js";
8
+ import { getDataflowLogsPage, listDataflowRuns, listDataflows, runDataflowWithFile, runDataflowWithRemoteUrl, } from "../api/dataflow2.js";
9
+ function renderTable(rows) {
10
+ if (rows.length === 0)
11
+ return "";
12
+ return columnify(rows, {
13
+ showHeaders: true,
14
+ preserveNewLines: true,
15
+ stringLength: stringWidth,
16
+ headingTransform: (heading) => heading,
17
+ });
18
+ }
19
+ function buildListTableRows(items) {
20
+ return items.map((item) => ({
21
+ "ID": item.id,
22
+ "Title": item.title ?? "",
23
+ "Status": item.status ?? "",
24
+ "Trigger": item.trigger ?? "",
25
+ "Creator": item.creator ?? "",
26
+ "Updated At": item.updated_at != null ? String(item.updated_at) : "",
27
+ "Version ID": item.version_id ?? "",
28
+ }));
29
+ }
30
+ function buildRunTableRows(items) {
31
+ return items.map((item) => ({
32
+ "ID": item.id,
33
+ "Status": item.status ?? "",
34
+ "Started At": item.started_at != null ? String(item.started_at) : "",
35
+ "Ended At": item.ended_at != null ? String(item.ended_at) : "",
36
+ "Source Name": item.source?.name != null ? String(item.source.name) : "",
37
+ "Content Type": item.source?.content_type != null ? String(item.source.content_type) : "",
38
+ "Size": item.source?.size != null ? String(item.source.size) : "",
39
+ "Reason": item.reason ?? "",
40
+ }));
41
+ }
42
+ function parseSinceToLocalDayRange(value) {
43
+ // 只支持 YYYY-MM-DD 格式,解析为本地时区的一整天范围
44
+ const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
45
+ if (!match)
46
+ return null;
47
+ const year = parseInt(match[1], 10);
48
+ const month = parseInt(match[2], 10) - 1; // Date month is 0-indexed
49
+ const day = parseInt(match[3], 10);
50
+ const start = new Date(year, month, day, 0, 0, 0);
51
+ const end = new Date(year, month, day, 23, 59, 59);
52
+ return {
53
+ startTime: Math.floor(start.getTime() / 1000),
54
+ endTime: Math.floor(end.getTime() / 1000),
55
+ };
56
+ }
57
+ function formatDataflowLogSummary(item) {
58
+ const duration = item.metadata?.duration ?? "-";
59
+ return [
60
+ `[${item.id}] ${item.taskId ?? ""} ${item.operator ?? ""}`,
61
+ `Status: ${item.status ?? ""}`,
62
+ `Started At: ${item.started_at ?? ""}`,
63
+ `Updated At: ${item.updated_at ?? ""}`,
64
+ `Duration: ${duration}`
65
+ ].join("\n");
66
+ }
67
+ function formatIndentedJsonBlock(label, value) {
68
+ const pretty = JSON.stringify(value ?? {}, null, 4) ?? "{}";
69
+ const indented = pretty
70
+ .split("\n")
71
+ .map((line) => ` ${line}`)
72
+ .join("\n");
73
+ return ` ${label}:\n${indented}`;
74
+ }
75
+ function formatDataflowLogOutput(item, detail) {
76
+ const parts = [formatDataflowLogSummary(item)];
77
+ if (detail) {
78
+ parts.push("");
79
+ parts.push(formatIndentedJsonBlock("input", item.inputs ?? {}));
80
+ parts.push("");
81
+ parts.push(formatIndentedJsonBlock("output", item.outputs ?? {}));
82
+ }
83
+ return parts.join("\n");
84
+ }
85
+ async function requireTokenAndBusinessDomain(businessDomain) {
86
+ const token = await ensureValidToken();
87
+ return {
88
+ baseUrl: token.baseUrl,
89
+ accessToken: token.accessToken,
90
+ businessDomain: businessDomain || resolveBusinessDomain(),
91
+ };
92
+ }
93
+ export async function runDataflowCommand(args) {
94
+ let exitCode = 0;
95
+ const parser = yargs(args)
96
+ .scriptName("kweaver dataflow")
97
+ .exitProcess(false)
98
+ .help()
99
+ .version(false)
100
+ .strict()
101
+ .fail((message, error) => {
102
+ throw error ?? new Error(message);
103
+ })
104
+ .command("list", "List all dataflows", (command) => command.option("biz-domain", {
105
+ alias: "bd",
106
+ type: "string",
107
+ }), async (argv) => {
108
+ exitCode = await with401RefreshRetry(async () => {
109
+ const base = await requireTokenAndBusinessDomain(argv.bizDomain);
110
+ const body = await listDataflows(base);
111
+ const table = renderTable(buildListTableRows(body.dags));
112
+ if (table) {
113
+ console.log(table);
114
+ }
115
+ return 0;
116
+ });
117
+ })
118
+ .command("run <dagId>", "Trigger one dataflow run", (command) => command
119
+ .positional("dagId", { type: "string" })
120
+ .option("file", { type: "string" })
121
+ .option("url", { type: "string" })
122
+ .option("name", { type: "string" })
123
+ .option("biz-domain", { alias: "bd", type: "string" })
124
+ .check((argv) => {
125
+ const hasFile = typeof argv.file === "string";
126
+ const hasUrl = typeof argv.url === "string";
127
+ if (hasFile === hasUrl) {
128
+ throw new Error("Exactly one of --file or --url is required.");
129
+ }
130
+ if (hasUrl && typeof argv.name !== "string") {
131
+ throw new Error("--url requires --name.");
132
+ }
133
+ return true;
134
+ }), async (argv) => {
135
+ exitCode = await with401RefreshRetry(async () => {
136
+ const base = await requireTokenAndBusinessDomain(argv.bizDomain);
137
+ if (typeof argv.file === "string") {
138
+ await access(argv.file, constants.R_OK);
139
+ const fileBytes = await readFile(argv.file);
140
+ const fileName = argv.file.split(/[\\/]/).pop() || "upload.bin";
141
+ const body = await runDataflowWithFile({
142
+ ...base,
143
+ dagId: argv.dagId,
144
+ fileName,
145
+ fileBytes,
146
+ });
147
+ console.log(body.dag_instance_id);
148
+ return 0;
149
+ }
150
+ const body = await runDataflowWithRemoteUrl({
151
+ ...base,
152
+ dagId: argv.dagId,
153
+ url: String(argv.url),
154
+ name: String(argv.name),
155
+ });
156
+ console.log(body.dag_instance_id);
157
+ return 0;
158
+ });
159
+ })
160
+ .command("runs <dagId>", "List run records for one dataflow", (command) => command
161
+ .positional("dagId", { type: "string" })
162
+ .option("since", { type: "string" })
163
+ .option("biz-domain", { alias: "bd", type: "string" }), async (argv) => {
164
+ exitCode = await with401RefreshRetry(async () => {
165
+ const base = await requireTokenAndBusinessDomain(argv.bizDomain);
166
+ const dayRange = typeof argv.since === "string" ? parseSinceToLocalDayRange(argv.since) : null;
167
+ let results = [];
168
+ if (!dayRange) {
169
+ const body = await listDataflowRuns({
170
+ ...base,
171
+ dagId: argv.dagId,
172
+ page: 0,
173
+ limit: 20,
174
+ sortBy: "started_at",
175
+ order: "desc",
176
+ });
177
+ results = body.results;
178
+ }
179
+ else {
180
+ const first = await listDataflowRuns({
181
+ ...base,
182
+ dagId: argv.dagId,
183
+ page: 0,
184
+ limit: 20,
185
+ sortBy: "started_at",
186
+ order: "desc",
187
+ startTime: dayRange.startTime,
188
+ endTime: dayRange.endTime,
189
+ });
190
+ results = [...first.results];
191
+ const total = first.total ?? first.results.length;
192
+ for (let page = 1; page * 20 < total; page += 1) {
193
+ const next = await listDataflowRuns({
194
+ ...base,
195
+ dagId: argv.dagId,
196
+ page,
197
+ limit: 20,
198
+ sortBy: "started_at",
199
+ order: "desc",
200
+ startTime: dayRange.startTime,
201
+ endTime: dayRange.endTime,
202
+ });
203
+ results = results.concat(next.results);
204
+ }
205
+ }
206
+ const table = renderTable(buildRunTableRows(results));
207
+ if (table) {
208
+ console.log(table);
209
+ }
210
+ return 0;
211
+ });
212
+ })
213
+ .command("logs <dagId> <instanceId>", "Show logs for one run in summary or detail mode", (command) => command
214
+ .positional("dagId", { type: "string" })
215
+ .positional("instanceId", { type: "string" })
216
+ .option("detail", { type: "boolean", default: false })
217
+ .option("biz-domain", { alias: "bd", type: "string" }), async (argv) => {
218
+ exitCode = await with401RefreshRetry(async () => {
219
+ const base = await requireTokenAndBusinessDomain(argv.bizDomain);
220
+ let seen = 0;
221
+ for (let page = 0;; page += 1) {
222
+ const body = await getDataflowLogsPage({
223
+ ...base,
224
+ dagId: argv.dagId,
225
+ instanceId: argv.instanceId,
226
+ page,
227
+ limit: 100,
228
+ });
229
+ if (body.results.length === 0)
230
+ break;
231
+ for (const item of body.results) {
232
+ console.log(formatDataflowLogOutput(item, argv.detail === true));
233
+ console.log("");
234
+ }
235
+ seen += body.results.length;
236
+ if ((body.total ?? 0) > 0 && seen >= (body.total ?? 0))
237
+ break;
238
+ }
239
+ return 0;
240
+ });
241
+ })
242
+ .demandCommand(1);
243
+ try {
244
+ await parser.parseAsync();
245
+ return exitCode;
246
+ }
247
+ catch (error) {
248
+ console.error(formatHttpError(error));
249
+ return 1;
250
+ }
251
+ }
@@ -0,0 +1,79 @@
1
+ import { IncomingMessage, ServerResponse } from "node:http";
2
+ export interface ExploreMeta {
3
+ bkn: {
4
+ id: string;
5
+ name: string;
6
+ };
7
+ statistics: {
8
+ object_count: number;
9
+ relation_count: number;
10
+ };
11
+ objectTypes: Array<{
12
+ id: string;
13
+ name: string;
14
+ displayKey: string;
15
+ propertyCount: number;
16
+ properties: Array<{
17
+ name: string;
18
+ type?: string;
19
+ }>;
20
+ }>;
21
+ relationTypes: Array<{
22
+ id: string;
23
+ name: string;
24
+ sourceOtId: string;
25
+ targetOtId: string;
26
+ sourceOtName: string;
27
+ targetOtName: string;
28
+ }>;
29
+ actionTypes: Array<{
30
+ id: string;
31
+ name: string;
32
+ }>;
33
+ }
34
+ export interface ExploreOt {
35
+ id: string;
36
+ name: string;
37
+ displayKey: string;
38
+ propertyCount: number;
39
+ properties: Array<{
40
+ name: string;
41
+ type?: string;
42
+ }>;
43
+ }
44
+ export interface ExploreRt {
45
+ id: string;
46
+ name: string;
47
+ sourceOtId: string;
48
+ targetOtId: string;
49
+ sourceOtName: string;
50
+ targetOtName: string;
51
+ }
52
+ export interface ExploreAt {
53
+ id: string;
54
+ name: string;
55
+ }
56
+ export interface ExploreBkn {
57
+ id: string;
58
+ name: string;
59
+ }
60
+ export interface ExploreStats {
61
+ object_count: number;
62
+ relation_count: number;
63
+ }
64
+ export declare const EXPLORE_BOOTSTRAP_RETRY_DELAY_MS = 300;
65
+ export declare const EXPLORE_BOOTSTRAP_MAX_ATTEMPTS = 2;
66
+ export declare function buildMeta(knRaw: string, otRaw: string, rtRaw: string, atRaw: string): ExploreMeta;
67
+ export declare function isRetryableExploreBootstrapError(error: unknown): boolean;
68
+ export declare function loadExploreMetaWithRetry(token: {
69
+ baseUrl: string;
70
+ accessToken: string;
71
+ }, knId: string, businessDomain: string): Promise<ExploreMeta>;
72
+ export declare function readBody(req: IncomingMessage): Promise<string>;
73
+ export declare function jsonResponse(res: ServerResponse, status: number, data: unknown): void;
74
+ export declare function handleApiError(res: ServerResponse, error: unknown): void;
75
+ export type TokenProvider = () => Promise<{
76
+ baseUrl: string;
77
+ accessToken: string;
78
+ }>;
79
+ export declare function registerBknRoutes(meta: ExploreMeta, getToken: TokenProvider, businessDomain: string): Map<string, (req: IncomingMessage, res: ServerResponse) => void>;