@openbkn/bkn-sdk 0.1.1-alpha.0 → 0.1.1-alpha.2
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/dist/{chunk-DXA44XWY.js → chunk-5MOIXIMJ.js} +481 -374
- package/dist/chunk-5MOIXIMJ.js.map +1 -0
- package/dist/cli.js +320 -358
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +70 -34
- package/dist/index.js +1 -1
- package/package.json +2 -3
- package/dist/chunk-DXA44XWY.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,34 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
DEFAULT_BUSINESS_DOMAIN,
|
|
3
4
|
DEFAULT_LIST_LIMIT,
|
|
4
5
|
DEFAULT_QUERY_LIMIT,
|
|
6
|
+
HttpError,
|
|
5
7
|
InputError,
|
|
6
8
|
activePlatform,
|
|
7
9
|
attachToken,
|
|
8
|
-
changePassword,
|
|
9
10
|
createClient,
|
|
11
|
+
credentialDeviceLogin,
|
|
10
12
|
currentToken,
|
|
13
|
+
decodeJwt,
|
|
11
14
|
deletePlatform,
|
|
15
|
+
deviceLogin,
|
|
12
16
|
exportCreds,
|
|
13
17
|
formatError,
|
|
18
|
+
getUserSafe,
|
|
14
19
|
listPlatforms,
|
|
15
20
|
logout,
|
|
21
|
+
openBrowser,
|
|
16
22
|
parseEmbeddingFields,
|
|
17
23
|
parsePkMap,
|
|
18
24
|
rawCall,
|
|
19
25
|
readPlatformConfig,
|
|
20
|
-
renderOrgTree,
|
|
21
26
|
renderReportMarkdown,
|
|
27
|
+
request,
|
|
22
28
|
resolveContext,
|
|
23
29
|
setActivePlatform,
|
|
24
30
|
status,
|
|
25
31
|
switchUser,
|
|
26
32
|
toExitCode,
|
|
27
33
|
use,
|
|
28
|
-
usersOf,
|
|
29
34
|
whoami,
|
|
30
35
|
writePlatformConfig
|
|
31
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-5MOIXIMJ.js";
|
|
32
37
|
|
|
33
38
|
// src/cli.ts
|
|
34
39
|
import { Command as Command16 } from "commander";
|
|
@@ -36,12 +41,12 @@ import { Command as Command16 } from "commander";
|
|
|
36
41
|
// package.json
|
|
37
42
|
var package_default = {
|
|
38
43
|
name: "@openbkn/bkn-sdk",
|
|
39
|
-
version: "0.1.1-alpha.
|
|
44
|
+
version: "0.1.1-alpha.2",
|
|
40
45
|
description: "Unified TypeScript SDK + CLI for the BKN (Business Knowledge Network) platform.",
|
|
41
46
|
type: "module",
|
|
42
47
|
license: "Apache-2.0",
|
|
43
48
|
engines: {
|
|
44
|
-
node: ">=
|
|
49
|
+
node: ">=18"
|
|
45
50
|
},
|
|
46
51
|
bin: {
|
|
47
52
|
openbkn: "./dist/cli.js"
|
|
@@ -77,7 +82,6 @@ var package_default = {
|
|
|
77
82
|
dependencies: {
|
|
78
83
|
"@clack/prompts": "^0.9.1",
|
|
79
84
|
chalk: "^5.4.1",
|
|
80
|
-
"cli-table3": "^0.6.5",
|
|
81
85
|
commander: "^13.1.0",
|
|
82
86
|
"csv-parse": "^6.2.1",
|
|
83
87
|
"js-yaml": "^4.2.0",
|
|
@@ -149,8 +153,20 @@ function installGroupedHelp(root) {
|
|
|
149
153
|
apply(root);
|
|
150
154
|
}
|
|
151
155
|
|
|
156
|
+
// src/utils/org-tree.ts
|
|
157
|
+
function renderOrgTree(nodes, prefix = "") {
|
|
158
|
+
const lines = [];
|
|
159
|
+
nodes.forEach((node, i) => {
|
|
160
|
+
const last = i === nodes.length - 1;
|
|
161
|
+
lines.push(`${prefix}${last ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "}${node.name} (id: ${node.id})`);
|
|
162
|
+
if (node.children.length) {
|
|
163
|
+
lines.push(renderOrgTree(node.children, `${prefix}${last ? " " : "\u2502 "}`));
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
return lines.join("\n");
|
|
167
|
+
}
|
|
168
|
+
|
|
152
169
|
// src/utils/output.ts
|
|
153
|
-
import Table from "cli-table3";
|
|
154
170
|
function printJson(value, opts = {}) {
|
|
155
171
|
if (opts.json || opts.compact) {
|
|
156
172
|
process.stdout.write(`${JSON.stringify(value, null, opts.compact ? 0 : 2)}
|
|
@@ -159,16 +175,43 @@ function printJson(value, opts = {}) {
|
|
|
159
175
|
}
|
|
160
176
|
const rows = toRows(value);
|
|
161
177
|
if (rows) {
|
|
162
|
-
const
|
|
178
|
+
const fullColumns = columnsOf(rows).filter((c) => rows.some((r) => stringifyCell(r[c]) !== ""));
|
|
179
|
+
const columns = opts.full ? fullColumns : selectColumns(rows);
|
|
163
180
|
if (columns.length > 0) {
|
|
164
181
|
printTable(rows, columns);
|
|
182
|
+
const hidden = fullColumns.length - columns.length;
|
|
183
|
+
if (hidden > 0 && !opts.full) {
|
|
184
|
+
process.stdout.write(`\u2026 ${hidden} more column(s); use --full or --json for everything
|
|
185
|
+
`);
|
|
186
|
+
}
|
|
165
187
|
return;
|
|
166
188
|
}
|
|
167
189
|
}
|
|
190
|
+
if (isEmptyEnvelope(value)) {
|
|
191
|
+
process.stdout.write("(no results)\n");
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
168
194
|
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
169
195
|
`);
|
|
170
196
|
}
|
|
171
|
-
|
|
197
|
+
function isEmptyEnvelope(value) {
|
|
198
|
+
if (!value || typeof value !== "object") return false;
|
|
199
|
+
const o = value;
|
|
200
|
+
return ROW_ENVELOPES.some((k) => Array.isArray(o[k]) && o[k].length === 0);
|
|
201
|
+
}
|
|
202
|
+
var ROW_ENVELOPES = [
|
|
203
|
+
"entries",
|
|
204
|
+
"data",
|
|
205
|
+
"cases",
|
|
206
|
+
"reports",
|
|
207
|
+
"results",
|
|
208
|
+
"list",
|
|
209
|
+
"recurringRules",
|
|
210
|
+
"users",
|
|
211
|
+
"roles",
|
|
212
|
+
"departments",
|
|
213
|
+
"members"
|
|
214
|
+
];
|
|
172
215
|
function toRows(value) {
|
|
173
216
|
const isRowArray = (v) => Array.isArray(v) && v.length > 0 && v.every((x) => x !== null && typeof x === "object" && !Array.isArray(x));
|
|
174
217
|
if (isRowArray(value)) return value;
|
|
@@ -187,22 +230,68 @@ function columnsOf(rows) {
|
|
|
187
230
|
}
|
|
188
231
|
return seen;
|
|
189
232
|
}
|
|
233
|
+
var MAX_COLS = 8;
|
|
234
|
+
var NOISE_COLS = /* @__PURE__ */ new Set([
|
|
235
|
+
"creator",
|
|
236
|
+
"updater",
|
|
237
|
+
"create_by",
|
|
238
|
+
"update_by",
|
|
239
|
+
"create_user",
|
|
240
|
+
"update_user",
|
|
241
|
+
"operations",
|
|
242
|
+
"status_message",
|
|
243
|
+
"last_check_time",
|
|
244
|
+
"last_discover_status",
|
|
245
|
+
"health_check_result",
|
|
246
|
+
"health_check_enabled"
|
|
247
|
+
]);
|
|
248
|
+
var isNoiseCol = (c) => NOISE_COLS.has(c) || /_time$/.test(c);
|
|
249
|
+
var isKeyCol = (c) => /^(id|name|key|title|label)$/i.test(c) || /_(id|name|key)$/i.test(c) || /^(status|state|type|category|mode|enabled|version|branch)$/i.test(c);
|
|
250
|
+
function selectColumns(rows) {
|
|
251
|
+
const isObj = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
252
|
+
const kept = columnsOf(rows).filter((c) => {
|
|
253
|
+
if (isNoiseCol(c)) return false;
|
|
254
|
+
const vals = rows.map((r) => r[c]);
|
|
255
|
+
if (!vals.some((v) => stringifyCell(v) !== "")) return false;
|
|
256
|
+
if (vals.every((v) => v === null || v === void 0 || isObj(v))) return false;
|
|
257
|
+
return true;
|
|
258
|
+
});
|
|
259
|
+
const isLongText = (c) => rows.every((r) => {
|
|
260
|
+
const s = stringifyCell(r[c]);
|
|
261
|
+
return s === "" || s.length >= CELL_MAX - 1;
|
|
262
|
+
});
|
|
263
|
+
const rank = (c) => isKeyCol(c) ? 0 : isLongText(c) ? 2 : 1;
|
|
264
|
+
const ordered = kept.map((c, i) => ({ c, i, r: rank(c) })).sort((a, b) => a.r - b.r || a.i - b.i).map((x) => x.c);
|
|
265
|
+
return ordered.slice(0, MAX_COLS);
|
|
266
|
+
}
|
|
190
267
|
function printTable(rows, columns, opts = {}) {
|
|
191
268
|
if (opts.json || opts.compact) {
|
|
192
269
|
printJson(rows, opts);
|
|
193
270
|
return;
|
|
194
271
|
}
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
272
|
+
const cells = rows.map((row) => columns.map((c) => stringifyCell(row[c])));
|
|
273
|
+
const widths = columns.map(
|
|
274
|
+
(col, i) => Math.max(displayWidth(col), ...cells.map((r) => displayWidth(r[i] ?? "")))
|
|
275
|
+
);
|
|
276
|
+
const fmt = (parts) => parts.map((p, i) => i === parts.length - 1 ? p : pad(p, widths[i] ?? 0)).join(" ").trimEnd();
|
|
277
|
+
const lines = [fmt(columns), ...cells.map(fmt)];
|
|
278
|
+
process.stdout.write(`${lines.join("\n")}
|
|
200
279
|
`);
|
|
201
280
|
}
|
|
202
281
|
var CELL_MAX = 48;
|
|
282
|
+
function displayWidth(s) {
|
|
283
|
+
let w = 0;
|
|
284
|
+
for (const ch of s) w += /[ᄀ-ᅟ⺀-가-힣豈-︰-﹏-⦆¢-₩]/.test(ch) ? 2 : 1;
|
|
285
|
+
return w;
|
|
286
|
+
}
|
|
287
|
+
function pad(s, width) {
|
|
288
|
+
const gap = width - displayWidth(s);
|
|
289
|
+
return gap > 0 ? s + " ".repeat(gap) : s;
|
|
290
|
+
}
|
|
203
291
|
function stringifyCell(v) {
|
|
204
292
|
if (v === null || v === void 0) return "";
|
|
205
|
-
const
|
|
293
|
+
const raw = Array.isArray(v) && v.every((x) => x === null || typeof x !== "object") ? v.join(",") : typeof v === "object" ? JSON.stringify(v) : String(v);
|
|
294
|
+
const s = raw.replace(/\s+/g, " ").trim();
|
|
206
295
|
return s.length > CELL_MAX ? `${s.slice(0, CELL_MAX - 1)}\u2026` : s;
|
|
207
296
|
}
|
|
208
297
|
|
|
@@ -220,7 +309,7 @@ function clientFrom(cmd) {
|
|
|
220
309
|
}
|
|
221
310
|
function outputOptions(cmd) {
|
|
222
311
|
const o = cmd.optsWithGlobals();
|
|
223
|
-
return { json: Boolean(o.json), compact: Boolean(o.compact) };
|
|
312
|
+
return { json: Boolean(o.json), compact: Boolean(o.compact), full: Boolean(o.full) };
|
|
224
313
|
}
|
|
225
314
|
function csv(value) {
|
|
226
315
|
if (!value) return void 0;
|
|
@@ -237,340 +326,144 @@ function readBody(opts) {
|
|
|
237
326
|
}
|
|
238
327
|
|
|
239
328
|
// src/commands/auth.ts
|
|
240
|
-
import {
|
|
329
|
+
import { createInterface } from "readline";
|
|
241
330
|
import { Command } from "commander";
|
|
242
331
|
|
|
243
|
-
// src/
|
|
244
|
-
import {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
2XVy1v2bgSNkGHABgncR7seyIg81JQ3c/Axxd6GsTztjLnlvGAlmT1TphE84mi99
|
|
255
|
-
fUaGD2A1u1qdIuNc+XuisFeNcUW6fct0+x97eS2eEGRr/7qxWmO/P20sFVzXc2bF
|
|
256
|
-
1QIDAQAB
|
|
257
|
-
-----END PUBLIC KEY-----`;
|
|
258
|
-
function normalizeBaseUrl(value) {
|
|
259
|
-
return value.replace(/\/+$/, "");
|
|
260
|
-
}
|
|
261
|
-
function generatePkce() {
|
|
262
|
-
const verifier = randomBytes(48).toString("base64url");
|
|
263
|
-
return { verifier, challenge: createHash("sha256").update(verifier).digest("base64url") };
|
|
264
|
-
}
|
|
265
|
-
function buildAuthorizeUrl(base, clientId, redirectUri, state, codeChallenge, scope = DEFAULT_SCOPE) {
|
|
266
|
-
const params = new URLSearchParams({
|
|
267
|
-
response_type: "code",
|
|
268
|
-
client_id: clientId,
|
|
269
|
-
redirect_uri: redirectUri,
|
|
270
|
-
scope,
|
|
271
|
-
state,
|
|
272
|
-
"x-forwarded-prefix": "",
|
|
273
|
-
lang: "zh-cn",
|
|
274
|
-
product: "adp",
|
|
275
|
-
code_challenge: codeChallenge,
|
|
276
|
-
code_challenge_method: "S256"
|
|
277
|
-
});
|
|
278
|
-
return `${base}/oauth2/auth?${params.toString()}`;
|
|
279
|
-
}
|
|
280
|
-
function mapToken(data) {
|
|
281
|
-
return {
|
|
282
|
-
accessToken: data.access_token,
|
|
283
|
-
refreshToken: data.refresh_token,
|
|
284
|
-
idToken: data.id_token
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
async function registerClient(base, redirectUri, scope = DEFAULT_SCOPE) {
|
|
288
|
-
const res = await fetch(`${base}/oauth2/clients`, {
|
|
289
|
-
method: "POST",
|
|
290
|
-
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
291
|
-
body: JSON.stringify({
|
|
292
|
-
client_name: "openbkn-cli",
|
|
293
|
-
grant_types: ["authorization_code", "implicit", "refresh_token"],
|
|
294
|
-
response_types: ["token id_token", "code", "token"],
|
|
295
|
-
scope,
|
|
296
|
-
redirect_uris: [redirectUri],
|
|
297
|
-
post_logout_redirect_uris: [redirectUri.replace("/callback", "/successful-logout")],
|
|
298
|
-
metadata: { device: { name: "openbkn-cli", client_type: "web", description: "openbkn CLI" } }
|
|
299
|
-
})
|
|
300
|
-
});
|
|
301
|
-
if (!res.ok) {
|
|
302
|
-
throw new Error(
|
|
303
|
-
`Client registration failed (${res.status}): ${await res.text() || res.statusText}`
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
const data = await res.json();
|
|
307
|
-
return { clientId: data.client_id, clientSecret: data.client_secret };
|
|
308
|
-
}
|
|
309
|
-
async function exchangeCode(base, code, redirectUri, client, codeVerifier) {
|
|
310
|
-
const params = {
|
|
311
|
-
grant_type: "authorization_code",
|
|
312
|
-
code,
|
|
313
|
-
redirect_uri: redirectUri,
|
|
314
|
-
code_verifier: codeVerifier
|
|
315
|
-
};
|
|
316
|
-
const headers = {
|
|
317
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
318
|
-
Accept: "application/json"
|
|
319
|
-
};
|
|
320
|
-
if (client.clientSecret) {
|
|
321
|
-
headers.Authorization = `Basic ${Buffer.from(`${client.clientId}:${client.clientSecret}`).toString("base64")}`;
|
|
322
|
-
} else {
|
|
323
|
-
params.client_id = client.clientId;
|
|
324
|
-
}
|
|
325
|
-
const res = await fetch(`${base}/oauth2/token`, {
|
|
332
|
+
// src/api/eacp-crypto.ts
|
|
333
|
+
import {
|
|
334
|
+
constants,
|
|
335
|
+
createPrivateKey,
|
|
336
|
+
createPublicKey,
|
|
337
|
+
publicEncrypt
|
|
338
|
+
} from "crypto";
|
|
339
|
+
|
|
340
|
+
// src/api/admin.ts
|
|
341
|
+
async function changePasswordSafe(ctx, account, oldPassword, newPassword) {
|
|
342
|
+
await request(ctx, "/api/safe/v1/auth/change-password", {
|
|
326
343
|
method: "POST",
|
|
327
|
-
|
|
328
|
-
body: new URLSearchParams(params).toString()
|
|
329
|
-
});
|
|
330
|
-
if (!res.ok) {
|
|
331
|
-
throw new Error(
|
|
332
|
-
`Token exchange failed (${res.status}): ${await res.text() || res.statusText}`
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
return mapToken(await res.json());
|
|
336
|
-
}
|
|
337
|
-
function startCallbackServer(port) {
|
|
338
|
-
return new Promise((resolve2, reject) => {
|
|
339
|
-
const server = createServer((req2, res) => {
|
|
340
|
-
const u = new URL(req2.url ?? "/", `http://127.0.0.1:${port}`);
|
|
341
|
-
if (u.pathname !== "/callback") {
|
|
342
|
-
res.writeHead(404);
|
|
343
|
-
res.end();
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
const code = u.searchParams.get("code");
|
|
347
|
-
const error = u.searchParams.get("error");
|
|
348
|
-
if (error) {
|
|
349
|
-
res.writeHead(400, { "content-type": "text/html" });
|
|
350
|
-
res.end(`<h1>Login failed</h1><p>${error}</p>`);
|
|
351
|
-
server.close(() => reject(new Error(`OAuth error: ${error}`)));
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
if (!code) {
|
|
355
|
-
res.writeHead(400);
|
|
356
|
-
res.end("missing code");
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
360
|
-
res.end("<h1>Login successful</h1><p>You can close this window.</p>");
|
|
361
|
-
resolve2({
|
|
362
|
-
code,
|
|
363
|
-
state: u.searchParams.get("state") ?? void 0,
|
|
364
|
-
close: () => server.close()
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
|
-
server.on("error", reject);
|
|
368
|
-
server.listen(port, "127.0.0.1");
|
|
344
|
+
body: { account, old_password: oldPassword, new_password: newPassword }
|
|
369
345
|
});
|
|
346
|
+
return { ok: true };
|
|
370
347
|
}
|
|
371
|
-
|
|
372
|
-
|
|
348
|
+
|
|
349
|
+
// src/commands/auth.ts
|
|
350
|
+
async function resolveAccount(baseUrl, accessToken, insecure, idToken) {
|
|
351
|
+
const sub = decodeJwt(idToken ?? accessToken)?.sub;
|
|
352
|
+
if (!sub) return void 0;
|
|
373
353
|
try {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
354
|
+
const u = await getUserSafe(
|
|
355
|
+
{ baseUrl, token: accessToken, businessDomain: DEFAULT_BUSINESS_DOMAIN, insecure },
|
|
356
|
+
sub
|
|
357
|
+
);
|
|
358
|
+
return u.account;
|
|
379
359
|
} catch {
|
|
360
|
+
return void 0;
|
|
380
361
|
}
|
|
381
362
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
const authUrl = buildAuthorizeUrl(base, client.clientId, redirectUri, state, challenge, scope);
|
|
391
|
-
const waiter = startCallbackServer(port);
|
|
392
|
-
if (opts.noBrowser) {
|
|
393
|
-
process.stderr.write(`Open this URL to log in:
|
|
394
|
-
${authUrl}
|
|
395
|
-
`);
|
|
396
|
-
} else {
|
|
397
|
-
process.stderr.write(`Opening browser for login\u2026
|
|
398
|
-
If it doesn't open, visit:
|
|
399
|
-
${authUrl}
|
|
400
|
-
`);
|
|
401
|
-
openBrowser(authUrl);
|
|
402
|
-
}
|
|
403
|
-
const { code, state: returned, close } = await waiter;
|
|
404
|
-
close();
|
|
405
|
-
if (returned && returned !== state) throw new Error("OAuth state mismatch \u2014 possible CSRF.");
|
|
406
|
-
return exchangeCode(base, code, redirectUri, client, verifier);
|
|
407
|
-
}
|
|
408
|
-
function mergeCookies(existing, res) {
|
|
409
|
-
const setCookies = typeof res.headers.getSetCookie === "function" ? res.headers.getSetCookie() : res.headers.get("set-cookie") ? [res.headers.get("set-cookie")] : [];
|
|
410
|
-
const map = /* @__PURE__ */ new Map();
|
|
411
|
-
const add = (pair) => {
|
|
412
|
-
const eq = pair.indexOf("=");
|
|
413
|
-
if (eq > 0) map.set(pair.slice(0, eq), pair.slice(eq + 1));
|
|
414
|
-
};
|
|
415
|
-
for (const p of existing.split(";").map((s) => s.trim()).filter(Boolean))
|
|
416
|
-
add(p);
|
|
417
|
-
for (const sc of setCookies) add(sc.split(";")[0]?.trim() ?? "");
|
|
418
|
-
return [...map.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
|
|
419
|
-
}
|
|
420
|
-
function parseSigninProps(html) {
|
|
421
|
-
const m = html.match(/<script[^>]*\bid=["']__NEXT_DATA__["'][^>]*>([\s\S]*?)<\/script>/i);
|
|
422
|
-
if (!m?.[1]) throw new Error("Could not find __NEXT_DATA__ on /oauth2/signin.");
|
|
423
|
-
const data = JSON.parse(m[1]);
|
|
424
|
-
const pp = data.props?.pageProps;
|
|
425
|
-
const csrftoken = pp?.csrftoken ?? pp?._csrf;
|
|
426
|
-
if (typeof csrftoken !== "string") throw new Error("Sign-in page did not expose csrftoken.");
|
|
427
|
-
return {
|
|
428
|
-
csrftoken,
|
|
429
|
-
challenge: typeof pp?.challenge === "string" ? pp.challenge : void 0,
|
|
430
|
-
remember: pp?.remember === true || pp?.remember === "true"
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
async function followToCallback(startUrl, jar0, state, redirectUri) {
|
|
434
|
-
let url = startUrl;
|
|
435
|
-
let jar = jar0;
|
|
436
|
-
const cb = new URL(redirectUri);
|
|
437
|
-
for (let hop = 0; hop < 40; hop++) {
|
|
438
|
-
const resp = await fetch(url, {
|
|
439
|
-
headers: { Cookie: jar, Accept: "text/html,*/*;q=0.8" },
|
|
440
|
-
redirect: "manual"
|
|
441
|
-
});
|
|
442
|
-
jar = mergeCookies(jar, resp);
|
|
443
|
-
if (![302, 303, 307, 308].includes(resp.status)) {
|
|
444
|
-
throw new Error(`Unexpected OAuth response (HTTP ${resp.status}).`);
|
|
445
|
-
}
|
|
446
|
-
const loc = resp.headers.get("location");
|
|
447
|
-
if (!loc) throw new Error(`OAuth redirect missing Location (HTTP ${resp.status}).`);
|
|
448
|
-
const next = new URL(loc, url);
|
|
449
|
-
if (next.origin === cb.origin && next.pathname === cb.pathname) {
|
|
450
|
-
const err = next.searchParams.get("error");
|
|
451
|
-
if (err) throw new Error(`Authorization failed: ${err}`);
|
|
452
|
-
const code = next.searchParams.get("code");
|
|
453
|
-
if (next.searchParams.get("state") !== state) throw new Error("OAuth state mismatch.");
|
|
454
|
-
if (!code) throw new Error("Callback missing authorization code.");
|
|
455
|
-
return code;
|
|
363
|
+
function promptLine(query, hidden = false) {
|
|
364
|
+
return new Promise((resolve2) => {
|
|
365
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
366
|
+
if (hidden) {
|
|
367
|
+
const mutable = rl;
|
|
368
|
+
mutable._writeToOutput = (s) => {
|
|
369
|
+
if (s.startsWith(query)) process.stdout.write(query);
|
|
370
|
+
};
|
|
456
371
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
372
|
+
rl.question(query, (answer) => {
|
|
373
|
+
rl.close();
|
|
374
|
+
if (hidden) process.stdout.write("\n");
|
|
375
|
+
resolve2(answer.trim());
|
|
376
|
+
});
|
|
377
|
+
});
|
|
460
378
|
}
|
|
461
|
-
|
|
462
|
-
const
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const { verifier, challenge } = generatePkce();
|
|
468
|
-
const state = randomBytes(12).toString("hex");
|
|
469
|
-
let jar = "";
|
|
470
|
-
const authResp = await fetch(
|
|
471
|
-
buildAuthorizeUrl(base, client.clientId, redirectUri, state, challenge, scope),
|
|
472
|
-
{ redirect: "manual" }
|
|
473
|
-
);
|
|
474
|
-
jar = mergeCookies(jar, authResp);
|
|
475
|
-
const authLoc = authResp.headers.get("location");
|
|
476
|
-
if (!authLoc) throw new Error(`/oauth2/auth did not redirect (HTTP ${authResp.status}).`);
|
|
477
|
-
const signinUrl = new URL(authLoc, base);
|
|
478
|
-
if (!signinUrl.pathname.includes("signin")) {
|
|
479
|
-
throw new Error(`Expected a sign-in redirect, got: ${authLoc}`);
|
|
379
|
+
function renderSessions(items) {
|
|
380
|
+
const byPlatform = /* @__PURE__ */ new Map();
|
|
381
|
+
for (const it of items) {
|
|
382
|
+
const arr = byPlatform.get(it.baseUrl) ?? [];
|
|
383
|
+
arr.push(it);
|
|
384
|
+
byPlatform.set(it.baseUrl, arr);
|
|
480
385
|
}
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
});
|
|
485
|
-
jar = mergeCookies(jar, pageResp);
|
|
486
|
-
const props = parseSigninProps(await pageResp.text());
|
|
487
|
-
const loginChallenge = signinUrl.searchParams.get("login_challenge")?.trim() || props.challenge?.trim();
|
|
488
|
-
if (!loginChallenge) throw new Error("Could not resolve the login challenge.");
|
|
489
|
-
const cipher = publicEncrypt(
|
|
490
|
-
{
|
|
491
|
-
key: opts.signinPublicKeyPem ?? STUDIOWEB_LOGIN_PUBLIC_KEY_PEM,
|
|
492
|
-
padding: cryptoConstants.RSA_PKCS1_PADDING
|
|
493
|
-
},
|
|
494
|
-
Buffer.from(password, "utf8")
|
|
495
|
-
).toString("base64");
|
|
496
|
-
const postResp = await fetch(`${base}/oauth2/signin`, {
|
|
497
|
-
method: "POST",
|
|
498
|
-
headers: {
|
|
499
|
-
Cookie: jar,
|
|
500
|
-
"Content-Type": "application/json",
|
|
501
|
-
Accept: "application/json, text/plain, */*",
|
|
502
|
-
Origin: new URL(base).origin,
|
|
503
|
-
Referer: signinUrl.href
|
|
504
|
-
},
|
|
505
|
-
body: JSON.stringify({
|
|
506
|
-
_csrf: props.csrftoken,
|
|
507
|
-
challenge: loginChallenge,
|
|
508
|
-
account: username,
|
|
509
|
-
password: cipher,
|
|
510
|
-
vcode: { id: "", content: "" },
|
|
511
|
-
dualfactorauthinfo: { validcode: { vcode: "" }, OTP: { OTP: "" } },
|
|
512
|
-
remember: props.remember ?? false,
|
|
513
|
-
device: { name: "", description: "", client_type: "console_web", udids: [] }
|
|
514
|
-
}),
|
|
515
|
-
redirect: "manual"
|
|
516
|
-
});
|
|
517
|
-
jar = mergeCookies(jar, postResp);
|
|
518
|
-
let code;
|
|
519
|
-
if ([302, 303, 307].includes(postResp.status)) {
|
|
520
|
-
const loc = postResp.headers.get("location");
|
|
521
|
-
if (!loc) throw new Error("Sign-in response missing Location.");
|
|
522
|
-
code = await followToCallback(new URL(loc, base).href, jar, state, redirectUri);
|
|
523
|
-
} else if (postResp.status === 200) {
|
|
524
|
-
const text = await postResp.text();
|
|
525
|
-
let json = null;
|
|
526
|
-
try {
|
|
527
|
-
json = JSON.parse(text);
|
|
528
|
-
} catch {
|
|
529
|
-
}
|
|
530
|
-
const redir = json && typeof json.redirect === "string" ? json.redirect : "";
|
|
531
|
-
if (!redir) {
|
|
532
|
-
const msg = json && typeof json.message === "string" ? json.message : text.slice(0, 300);
|
|
533
|
-
throw new InputError(`Sign-in failed: ${msg}`);
|
|
534
|
-
}
|
|
535
|
-
code = await followToCallback(new URL(redir, base).href, jar, state, redirectUri);
|
|
536
|
-
} else {
|
|
537
|
-
throw new InputError(
|
|
538
|
-
`Sign-in failed (HTTP ${postResp.status}): ${(await postResp.text()).slice(0, 300)}`
|
|
539
|
-
);
|
|
386
|
+
const lines = [];
|
|
387
|
+
for (const [platform, users] of byPlatform) {
|
|
388
|
+
lines.push(platform);
|
|
389
|
+
for (const u of users) lines.push(` ${u.active ? "*" : " "} ${u.username ?? u.userId}`);
|
|
540
390
|
}
|
|
541
|
-
return
|
|
391
|
+
return lines.join("\n") || "(no saved sessions)";
|
|
542
392
|
}
|
|
543
|
-
|
|
544
|
-
// src/commands/auth.ts
|
|
545
393
|
function registerAuthLeaves(cmd) {
|
|
546
|
-
cmd.command("login <url>").description("Log in to a platform (attach a token, or browser/password OAuth)").option("-u, --username <name>", "username for password signin").option("-p, --password <pwd>", "password for password signin").option("--token <token>", "provide a token directly (CI / headless)").option("--client-id <id>", "use a fixed OAuth2 client id (skip dynamic registration)").option("--client-secret <secret>", "OAuth2 client secret (omit for public/PKCE)").option(
|
|
547
|
-
"--
|
|
548
|
-
"
|
|
549
|
-
|
|
394
|
+
cmd.command("login <url>").description("Log in to a platform (attach a token, or browser/password OAuth)").option("-u, --username <name>", "username for password signin").option("-p, --password <pwd>", "password for password signin").option("--token <token>", "provide a token directly (CI / headless)").option("--client-id <id>", "use a fixed OAuth2 client id (skip dynamic registration)").option("--client-secret <secret>", "OAuth2 client secret (omit for public/PKCE)").option(
|
|
395
|
+
"--port <n>",
|
|
396
|
+
"loopback redirect port for the auth_code flow",
|
|
397
|
+
(v) => Number.parseInt(v, 10)
|
|
398
|
+
).option("--device", "headless device-code login (RFC 8628) \u2014 no callback server, no password").option("--audience <aud>", "device-code token audience", "bkn-safe").option(
|
|
399
|
+
"--timeout <s>",
|
|
400
|
+
"device-login wait before timing out",
|
|
401
|
+
(v) => Number.parseInt(v, 10),
|
|
402
|
+
120
|
|
403
|
+
).option("--no-browser", "(legacy) print the URL instead of opening a browser").option("--product <name>", "(legacy) ISF OAuth product query").option("--signin-public-key-file <path>", "(legacy) RSA public key for ISF /oauth2/signin").action(async (url, opts, cmd2) => {
|
|
550
404
|
const g = cmd2.optsWithGlobals();
|
|
551
405
|
if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
406
|
+
const out = outputOptions(cmd2);
|
|
407
|
+
const report = (r) => {
|
|
408
|
+
if (out.json || out.compact) {
|
|
409
|
+
printJson({ loggedIn: true, ...r }, out);
|
|
410
|
+
} else {
|
|
411
|
+
process.stdout.write(`Logged in to ${r.baseUrl ?? url} as ${r.username ?? r.userId}
|
|
412
|
+
`);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
552
415
|
const token = opts.token ?? g.token;
|
|
553
416
|
if (token) {
|
|
554
|
-
|
|
555
|
-
printJson({ loggedIn: true, ...r2 }, outputOptions(cmd2));
|
|
417
|
+
report(attachToken(url, token, { insecure: g.insecure }));
|
|
556
418
|
return;
|
|
557
419
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
420
|
+
let tokens;
|
|
421
|
+
let account;
|
|
422
|
+
if (opts.username || opts.password) {
|
|
423
|
+
const username = opts.username ?? await promptLine("Username: ");
|
|
424
|
+
account = username;
|
|
425
|
+
const password = opts.password ?? await promptLine("Password: ", true);
|
|
426
|
+
tokens = await credentialDeviceLogin(url, username, password, {
|
|
427
|
+
clientId: opts.clientId,
|
|
428
|
+
audience: opts.audience,
|
|
429
|
+
timeoutMs: opts.timeout * 1e3
|
|
430
|
+
});
|
|
431
|
+
} else {
|
|
432
|
+
const openInBrowser = !opts.device && opts.browser !== false;
|
|
433
|
+
tokens = await deviceLogin(url, {
|
|
434
|
+
clientId: opts.clientId,
|
|
435
|
+
audience: opts.audience,
|
|
436
|
+
timeoutMs: opts.timeout * 1e3,
|
|
437
|
+
onPrompt: ({ userCode, verificationUri, verificationUriComplete }) => {
|
|
438
|
+
const target = verificationUriComplete ?? verificationUri;
|
|
439
|
+
process.stderr.write(
|
|
440
|
+
`
|
|
441
|
+
Open this URL to sign in and authorize:
|
|
442
|
+
${target}
|
|
443
|
+
User code: ${userCode}
|
|
444
|
+
`
|
|
445
|
+
);
|
|
446
|
+
if (openInBrowser) openBrowser(target);
|
|
447
|
+
process.stderr.write("Waiting for authorization\u2026\n");
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
if (!account) {
|
|
452
|
+
account = await resolveAccount(
|
|
453
|
+
url,
|
|
454
|
+
tokens.accessToken,
|
|
455
|
+
Boolean(g.insecure),
|
|
456
|
+
tokens.idToken
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
report(
|
|
460
|
+
attachToken(url, tokens.accessToken, {
|
|
461
|
+
refreshToken: tokens.refreshToken,
|
|
462
|
+
idToken: tokens.idToken,
|
|
463
|
+
insecure: g.insecure,
|
|
464
|
+
username: account
|
|
465
|
+
})
|
|
466
|
+
);
|
|
574
467
|
});
|
|
575
468
|
cmd.command("status").description("Show base URL and whether a token is configured").action((_opts, cmd2) => printJson(status(), outputOptions(cmd2)));
|
|
576
469
|
cmd.command("token").description("Print the current access token (keep secret)").action(() => {
|
|
@@ -580,7 +473,13 @@ function registerAuthLeaves(cmd) {
|
|
|
580
473
|
cmd.command("whoami [url]").description("Show current user identity (from the token)").option("--no-lookup", "skip the backend identity fallback (eacp/user/get)").action(
|
|
581
474
|
(_url, _opts, cmd2) => printJson(whoami(), outputOptions(cmd2))
|
|
582
475
|
);
|
|
583
|
-
cmd.command("list").alias("ls").description("List
|
|
476
|
+
cmd.command("list").alias("ls").description("List saved sessions (platform \u2192 users; * = active)").action((_opts, cmd2) => {
|
|
477
|
+
const items = listPlatforms();
|
|
478
|
+
const out = outputOptions(cmd2);
|
|
479
|
+
if (out.json || out.compact) printJson(items, out);
|
|
480
|
+
else process.stdout.write(`${renderSessions(items)}
|
|
481
|
+
`);
|
|
482
|
+
});
|
|
584
483
|
cmd.command("use <url>").description("Switch the active platform").action((url, _opts, cmd2) => {
|
|
585
484
|
use(url);
|
|
586
485
|
printJson(status(), outputOptions(cmd2));
|
|
@@ -589,28 +488,56 @@ function registerAuthLeaves(cmd) {
|
|
|
589
488
|
cmd.command("delete <url>").description("Delete saved credentials for a platform").action(
|
|
590
489
|
(url, _opts, cmd2) => printJson({ deleted: deletePlatform(url) }, outputOptions(cmd2))
|
|
591
490
|
);
|
|
592
|
-
cmd.command("switch <url> <user
|
|
593
|
-
|
|
491
|
+
cmd.command("switch <url> <user>").description("Switch the active user for a platform (by username or user id)").action((url, user, _opts, cmd2) => {
|
|
492
|
+
const r = switchUser(url, user);
|
|
493
|
+
const out = outputOptions(cmd2);
|
|
494
|
+
if (out.json || out.compact) printJson(r, out);
|
|
495
|
+
else process.stdout.write(`Switched to ${r.username ?? r.userId} on ${r.baseUrl}
|
|
496
|
+
`);
|
|
594
497
|
});
|
|
595
|
-
cmd.command("users <url>").description("List saved
|
|
596
|
-
|
|
498
|
+
cmd.command("users <url>").description("List saved users for a platform (* = active)").action((url, _opts, cmd2) => {
|
|
499
|
+
const norm = url.replace(/\/+$/, "");
|
|
500
|
+
const items = listPlatforms().filter((i) => i.baseUrl === norm);
|
|
501
|
+
const out = outputOptions(cmd2);
|
|
502
|
+
if (out.json || out.compact) printJson(items, out);
|
|
503
|
+
else process.stdout.write(`${renderSessions(items)}
|
|
504
|
+
`);
|
|
597
505
|
});
|
|
598
506
|
cmd.command("export").description("Export the active session's tokens (for a headless host)").action((_opts, cmd2) => {
|
|
599
507
|
printJson(exportCreds(), outputOptions(cmd2));
|
|
600
508
|
});
|
|
601
|
-
cmd.command("change-password [url]").description("Change your account password (
|
|
509
|
+
cmd.command("change-password [url]").description("Change your account password (bkn-safe self-service; no browser)").option("-a, --account <name>", "account / login name (the login column, e.g. admin)").option("--old-password <pwd>", "current password").option("--new-password <pwd>", "new password").option("--public-key-file <path>", "(legacy) RSA public key for ISF password encryption").action(async (url, opts, cmd2) => {
|
|
602
510
|
const g = cmd2.optsWithGlobals();
|
|
511
|
+
if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
603
512
|
const ctx = resolveContext({
|
|
604
|
-
baseUrl: g.baseUrl,
|
|
513
|
+
baseUrl: url ?? g.baseUrl,
|
|
605
514
|
token: g.token,
|
|
606
515
|
user: g.user,
|
|
607
516
|
businessDomain: g.bizDomain,
|
|
608
517
|
insecure: g.insecure
|
|
609
518
|
});
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
)
|
|
519
|
+
const account = opts.account ?? await promptLine("Account: ");
|
|
520
|
+
const oldPassword = opts.oldPassword ?? await promptLine("Current password: ", true);
|
|
521
|
+
let newPassword = opts.newPassword;
|
|
522
|
+
if (!newPassword) {
|
|
523
|
+
newPassword = await promptLine("New password: ", true);
|
|
524
|
+
const confirm = await promptLine("Confirm new password: ", true);
|
|
525
|
+
if (newPassword !== confirm) throw new Error("New passwords do not match.");
|
|
526
|
+
}
|
|
527
|
+
try {
|
|
528
|
+
printJson(
|
|
529
|
+
await changePasswordSafe(ctx, account, oldPassword, newPassword),
|
|
530
|
+
outputOptions(cmd2)
|
|
531
|
+
);
|
|
532
|
+
} catch (e) {
|
|
533
|
+
if (e instanceof HttpError && e.status === 401) {
|
|
534
|
+
throw new InputError("Wrong account or current password.");
|
|
535
|
+
}
|
|
536
|
+
if (e instanceof HttpError && e.status === 400) {
|
|
537
|
+
throw new InputError("New password must differ from the current one.");
|
|
538
|
+
}
|
|
539
|
+
throw e;
|
|
540
|
+
}
|
|
614
541
|
});
|
|
615
542
|
}
|
|
616
543
|
function authCommand() {
|
|
@@ -791,6 +718,41 @@ function adminCommand() {
|
|
|
791
718
|
outputOptions(cmd)
|
|
792
719
|
);
|
|
793
720
|
});
|
|
721
|
+
role.command("create").description("Create a custom role (bkn-safe; built-in roles are read-only)").requiredOption("--name <name>", "role name").option("--description <text>", "role description").action(async (opts, cmd) => {
|
|
722
|
+
printJson(
|
|
723
|
+
await clientFrom(cmd).admin.roleCreate(opts.name, opts.description),
|
|
724
|
+
outputOptions(cmd)
|
|
725
|
+
);
|
|
726
|
+
});
|
|
727
|
+
role.command("update <role>").description("Update a custom role's name/description (403 on built-in)").option("--name <name>", "new name").option("--description <text>", "new description").action(async (roleId, opts, cmd) => {
|
|
728
|
+
printJson(
|
|
729
|
+
await clientFrom(cmd).admin.roleUpdate(roleId, {
|
|
730
|
+
name: opts.name,
|
|
731
|
+
description: opts.description
|
|
732
|
+
}),
|
|
733
|
+
outputOptions(cmd)
|
|
734
|
+
);
|
|
735
|
+
});
|
|
736
|
+
role.command("delete <role>").description("Delete a custom role (403 on built-in)").option("-y, --yes", "skip confirmation").action(async (roleId, _opts, cmd) => {
|
|
737
|
+
printJson(await clientFrom(cmd).admin.roleDelete(roleId), outputOptions(cmd));
|
|
738
|
+
});
|
|
739
|
+
for (const [verb, grant] of [
|
|
740
|
+
["grant-perm", true],
|
|
741
|
+
["revoke-perm", false]
|
|
742
|
+
]) {
|
|
743
|
+
role.command(`${verb} <role>`).description(`${grant ? "Grant" : "Revoke"} a permission on a custom role (403 on built-in)`).requiredOption("--resource-type <t>", "resource type (e.g. catalog)").option("--resource-id <id>", "resource id ('*' = whole type)", "*").requiredOption("--operations <list>", "comma-separated operations").action(async (roleId, opts, cmd) => {
|
|
744
|
+
printJson(
|
|
745
|
+
await clientFrom(cmd).admin.rolePermission(
|
|
746
|
+
roleId,
|
|
747
|
+
grant,
|
|
748
|
+
opts.resourceType,
|
|
749
|
+
opts.resourceId,
|
|
750
|
+
csv(opts.operations) ?? []
|
|
751
|
+
),
|
|
752
|
+
outputOptions(cmd)
|
|
753
|
+
);
|
|
754
|
+
});
|
|
755
|
+
}
|
|
794
756
|
const modelBody = (opts) => {
|
|
795
757
|
if (opts.body || opts.bodyFile) return readBody(opts);
|
|
796
758
|
const mc = {};
|
|
@@ -1026,7 +988,7 @@ function agentCommand() {
|
|
|
1026
988
|
import { Command as Command4 } from "commander";
|
|
1027
989
|
|
|
1028
990
|
// src/utils/bkn-validate.ts
|
|
1029
|
-
import { existsSync, readFileSync as
|
|
991
|
+
import { existsSync, readFileSync as readFileSync2, readdirSync, statSync } from "fs";
|
|
1030
992
|
import { join, resolve } from "path";
|
|
1031
993
|
var BKN_OBJECT_NAME_MAX_LENGTH = 40;
|
|
1032
994
|
function parseFrontmatter(text) {
|
|
@@ -1079,7 +1041,7 @@ function validateBknDirectory(dirPath) {
|
|
|
1079
1041
|
if (!existsSync(networkPath)) {
|
|
1080
1042
|
errors.push("Missing network.bkn at the BKN root.");
|
|
1081
1043
|
} else {
|
|
1082
|
-
const fm = parseFrontmatter(
|
|
1044
|
+
const fm = parseFrontmatter(readFileSync2(networkPath, "utf8"));
|
|
1083
1045
|
if (!fm) errors.push("network.bkn has no frontmatter block.");
|
|
1084
1046
|
else {
|
|
1085
1047
|
if (fm.type !== "knowledge_network")
|
|
@@ -1091,7 +1053,7 @@ function validateBknDirectory(dirPath) {
|
|
|
1091
1053
|
const otIds = /* @__PURE__ */ new Set();
|
|
1092
1054
|
const otFiles = bknFiles(join(dir, "object_types"));
|
|
1093
1055
|
for (const file of otFiles) {
|
|
1094
|
-
const fm = parseFrontmatter(
|
|
1056
|
+
const fm = parseFrontmatter(readFileSync2(file, "utf8"));
|
|
1095
1057
|
const rel = file.slice(dir.length + 1);
|
|
1096
1058
|
if (!fm || fm.type !== "object_type") {
|
|
1097
1059
|
errors.push(`${rel}: not a valid object_type (missing/wrong frontmatter type).`);
|
|
@@ -1111,7 +1073,7 @@ function validateBknDirectory(dirPath) {
|
|
|
1111
1073
|
}
|
|
1112
1074
|
const rtFiles = bknFiles(join(dir, "relation_types"));
|
|
1113
1075
|
for (const file of rtFiles) {
|
|
1114
|
-
const text =
|
|
1076
|
+
const text = readFileSync2(file, "utf8");
|
|
1115
1077
|
const fm = parseFrontmatter(text);
|
|
1116
1078
|
const rel = file.slice(dir.length + 1);
|
|
1117
1079
|
if (!fm || fm.type !== "relation_type") {
|
|
@@ -1705,7 +1667,7 @@ function dataflowCommand() {
|
|
|
1705
1667
|
}
|
|
1706
1668
|
|
|
1707
1669
|
// src/commands/explore.ts
|
|
1708
|
-
import { createServer
|
|
1670
|
+
import { createServer } from "http";
|
|
1709
1671
|
import { Command as Command9 } from "commander";
|
|
1710
1672
|
var int6 = (v) => Number.parseInt(v, 10);
|
|
1711
1673
|
var ROUTES = {
|
|
@@ -1757,7 +1719,7 @@ function exploreCommand() {
|
|
|
1757
1719
|
);
|
|
1758
1720
|
cmd.option("--port <n>", "port to listen on", int6, 7777).option("--host <h>", "host to bind", "127.0.0.1").action(async (opts, command) => {
|
|
1759
1721
|
const client = clientFrom(command);
|
|
1760
|
-
const server =
|
|
1722
|
+
const server = createServer((reqMsg, res) => {
|
|
1761
1723
|
void handle(client, reqMsg, res);
|
|
1762
1724
|
});
|
|
1763
1725
|
server.listen(opts.port, opts.host, () => {
|
|
@@ -1872,18 +1834,18 @@ import { Command as Command11 } from "commander";
|
|
|
1872
1834
|
var int8 = (v) => Number.parseInt(v, 10);
|
|
1873
1835
|
function resourceCommand() {
|
|
1874
1836
|
const cmd = new Command11("resource").alias("res").description("Resources \u2014 list, find, get, query, delete");
|
|
1875
|
-
cmd.command("list").description("List resources under a
|
|
1837
|
+
cmd.command("list").description("List resources under a catalog").option("--catalog-id <id>", "filter by catalog id").option("--datasource-id <id>", "alias of --catalog-id").option("--category <c>", "resource category (table | logicview | dataset)").option("--type <c>", "alias of --category").option("--limit <n>", "page size", int8, DEFAULT_LIST_LIMIT).action(async (opts, cmd2) => {
|
|
1876
1838
|
const data = await clientFrom(cmd2).resource.list({
|
|
1877
|
-
datasourceId: opts.datasourceId,
|
|
1878
|
-
category: opts.type,
|
|
1839
|
+
datasourceId: opts.catalogId ?? opts.datasourceId,
|
|
1840
|
+
category: opts.category ?? opts.type,
|
|
1879
1841
|
limit: opts.limit
|
|
1880
1842
|
});
|
|
1881
1843
|
printJson(data, outputOptions(cmd2));
|
|
1882
1844
|
});
|
|
1883
|
-
cmd.command("find").description("Search resources by name (fuzzy; --exact for strict)").requiredOption("--name <name>", "resource name to search").option("--exact", "exact name match").option("--
|
|
1845
|
+
cmd.command("find").description("Search resources by name (fuzzy; --exact for strict)").requiredOption("--name <name>", "resource name to search").option("--exact", "exact name match").option("--catalog-id <id>", "limit to a catalog").option("--datasource-id <id>", "alias of --catalog-id").action(async (opts, cmd2) => {
|
|
1884
1846
|
const data = await clientFrom(cmd2).resource.find(opts.name, {
|
|
1885
1847
|
exact: opts.exact,
|
|
1886
|
-
datasourceId: opts.datasourceId
|
|
1848
|
+
datasourceId: opts.catalogId ?? opts.datasourceId
|
|
1887
1849
|
});
|
|
1888
1850
|
printJson(data, outputOptions(cmd2));
|
|
1889
1851
|
});
|
|
@@ -2098,11 +2060,11 @@ function toolCommand() {
|
|
|
2098
2060
|
}
|
|
2099
2061
|
|
|
2100
2062
|
// src/commands/trace.ts
|
|
2101
|
-
import { readFileSync as
|
|
2063
|
+
import { readFileSync as readFileSync4, writeFileSync } from "fs";
|
|
2102
2064
|
import { Command as Command14 } from "commander";
|
|
2103
2065
|
|
|
2104
2066
|
// src/trace-ai/schema-validate.ts
|
|
2105
|
-
import { readFileSync as
|
|
2067
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
2106
2068
|
import { extname } from "path";
|
|
2107
2069
|
import yaml from "js-yaml";
|
|
2108
2070
|
import { z } from "zod";
|
|
@@ -2139,7 +2101,7 @@ var DiagnosisRule = z.object({
|
|
|
2139
2101
|
params: z.record(z.string(), z.unknown()).optional()
|
|
2140
2102
|
});
|
|
2141
2103
|
function parseFile(file) {
|
|
2142
|
-
const text =
|
|
2104
|
+
const text = readFileSync3(file, "utf8");
|
|
2143
2105
|
const ext = extname(file).toLowerCase();
|
|
2144
2106
|
if (ext === ".yaml" || ext === ".yml") return yaml.load(text);
|
|
2145
2107
|
return JSON.parse(text);
|
|
@@ -2206,7 +2168,7 @@ function traceCommand() {
|
|
|
2206
2168
|
});
|
|
2207
2169
|
const evalSet = cmd.command("eval-set").description("Build + run trace eval sets");
|
|
2208
2170
|
evalSet.command("build <queries-file>").description("Build eval cases from a queries JSON file").option("--out <file>", "write the cases JSON here (default: stdout)").action(async (queriesFile, opts, cmd2) => {
|
|
2209
|
-
const raw = JSON.parse(
|
|
2171
|
+
const raw = JSON.parse(readFileSync4(queriesFile, "utf8"));
|
|
2210
2172
|
const cases = clientFrom(cmd2).trace.evalSetBuild(raw);
|
|
2211
2173
|
if (opts.out) {
|
|
2212
2174
|
writeFileSync(opts.out, JSON.stringify({ cases }, null, 2));
|
|
@@ -2216,7 +2178,7 @@ function traceCommand() {
|
|
|
2216
2178
|
}
|
|
2217
2179
|
});
|
|
2218
2180
|
evalSet.command("test <cases-file>").description("Run an eval set against an agent (--llm enables semantic_match)").requiredOption("--agent <id>", "agent id to run the queries against").option("--version <v>", "agent version", "v0").option("--llm", "enable semantic_match assertions via the local `claude` CLI").action(async (casesFile, opts, cmd2) => {
|
|
2219
|
-
const raw = JSON.parse(
|
|
2181
|
+
const raw = JSON.parse(readFileSync4(casesFile, "utf8"));
|
|
2220
2182
|
const cases = clientFrom(cmd2).trace.evalSetBuild(raw);
|
|
2221
2183
|
const result = await clientFrom(cmd2).trace.evalSetTest(opts.agent, cases, {
|
|
2222
2184
|
version: opts.version,
|
|
@@ -2338,7 +2300,7 @@ function vegaCommand() {
|
|
|
2338
2300
|
|
|
2339
2301
|
// src/cli.ts
|
|
2340
2302
|
var program = new Command16();
|
|
2341
|
-
program.name("openbkn").description("Operate the BKN platform from the CLI").version(package_default.version, "-V, --version", "output the version number").option("--base-url <url>", "platform base URL (env: BKN_BASE_URL)").option("--token <value>", "access token (env: BKN_TOKEN)").option("--user <id|name>", "use specific user credentials (env: BKN_USER)").option("--json", "machine-readable JSON output").option("--compact", "single-line JSON output").option("--biz-domain <s>", "business domain (alias: -bd)").option("-k, --insecure", "skip TLS verification (dev / self-signed only)").showHelpAfterError();
|
|
2303
|
+
program.name("openbkn").description("Operate the BKN platform from the CLI").version(package_default.version, "-V, --version", "output the version number").option("--base-url <url>", "platform base URL (env: BKN_BASE_URL)").option("--token <value>", "access token (env: BKN_TOKEN)").option("--user <id|name>", "use specific user credentials (env: BKN_USER)").option("--json", "machine-readable JSON output").option("--compact", "single-line JSON output").option("--full", "human view: show all columns (default trims to the key ones)").option("--biz-domain <s>", "business domain (alias: -bd)").option("-k, --insecure", "skip TLS verification (dev / self-signed only)").showHelpAfterError();
|
|
2342
2304
|
program.addCommand(authCommand());
|
|
2343
2305
|
program.addCommand(callCommand());
|
|
2344
2306
|
program.addCommand(configCommand());
|