@openbkn/bkn-sdk 0.1.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.
- package/LICENSE +201 -0
- package/README.md +104 -0
- package/README.zh.md +88 -0
- package/dist/chunk-4NXAIG4G.js +4805 -0
- package/dist/chunk-4NXAIG4G.js.map +1 -0
- package/dist/cli.js +2317 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +955 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,2317 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_LIST_LIMIT,
|
|
4
|
+
DEFAULT_QUERY_LIMIT,
|
|
5
|
+
InputError,
|
|
6
|
+
activePlatform,
|
|
7
|
+
attachToken,
|
|
8
|
+
changePassword,
|
|
9
|
+
createClient,
|
|
10
|
+
currentToken,
|
|
11
|
+
deletePlatform,
|
|
12
|
+
exportCreds,
|
|
13
|
+
formatError,
|
|
14
|
+
listPlatforms,
|
|
15
|
+
logout,
|
|
16
|
+
parsePkMap,
|
|
17
|
+
rawCall,
|
|
18
|
+
readPlatformConfig,
|
|
19
|
+
renderOrgTree,
|
|
20
|
+
renderReportMarkdown,
|
|
21
|
+
resolveContext,
|
|
22
|
+
setActivePlatform,
|
|
23
|
+
status,
|
|
24
|
+
switchUser,
|
|
25
|
+
toExitCode,
|
|
26
|
+
use,
|
|
27
|
+
usersOf,
|
|
28
|
+
whoami,
|
|
29
|
+
writePlatformConfig
|
|
30
|
+
} from "./chunk-4NXAIG4G.js";
|
|
31
|
+
|
|
32
|
+
// src/cli.ts
|
|
33
|
+
import { Command as Command16 } from "commander";
|
|
34
|
+
|
|
35
|
+
// package.json
|
|
36
|
+
var package_default = {
|
|
37
|
+
name: "@openbkn/bkn-sdk",
|
|
38
|
+
version: "0.1.0",
|
|
39
|
+
description: "Unified TypeScript SDK + CLI for the BKN (Business Knowledge Network) platform.",
|
|
40
|
+
type: "module",
|
|
41
|
+
license: "Apache-2.0",
|
|
42
|
+
engines: {
|
|
43
|
+
node: ">=22"
|
|
44
|
+
},
|
|
45
|
+
bin: {
|
|
46
|
+
openbkn: "./dist/cli.js"
|
|
47
|
+
},
|
|
48
|
+
main: "./dist/index.js",
|
|
49
|
+
types: "./dist/index.d.ts",
|
|
50
|
+
exports: {
|
|
51
|
+
".": {
|
|
52
|
+
types: "./dist/index.d.ts",
|
|
53
|
+
import: "./dist/index.js"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
files: ["dist", "README.md", "README.zh.md", "LICENSE"],
|
|
57
|
+
publishConfig: {
|
|
58
|
+
access: "public"
|
|
59
|
+
},
|
|
60
|
+
repository: {
|
|
61
|
+
type: "git",
|
|
62
|
+
url: "git+https://github.com/openbkn-ai/bkn-sdk.git"
|
|
63
|
+
},
|
|
64
|
+
keywords: ["bkn", "openbkn", "knowledge-network", "cli", "sdk", "typescript"],
|
|
65
|
+
scripts: {
|
|
66
|
+
build: "tsup",
|
|
67
|
+
dev: "tsup --watch",
|
|
68
|
+
typecheck: "tsc --noEmit",
|
|
69
|
+
lint: "biome check . && tsc --noEmit",
|
|
70
|
+
format: "biome format --write .",
|
|
71
|
+
test: "vitest run",
|
|
72
|
+
"test:cover": "vitest run --coverage",
|
|
73
|
+
ci: "npm run lint && npm test",
|
|
74
|
+
prepublishOnly: "npm run ci && npm run build"
|
|
75
|
+
},
|
|
76
|
+
dependencies: {
|
|
77
|
+
"@clack/prompts": "^0.9.1",
|
|
78
|
+
chalk: "^5.4.1",
|
|
79
|
+
"cli-table3": "^0.6.5",
|
|
80
|
+
commander: "^13.1.0",
|
|
81
|
+
"csv-parse": "^6.2.1",
|
|
82
|
+
"js-yaml": "^4.2.0",
|
|
83
|
+
jszip: "^3.10.1",
|
|
84
|
+
zod: "^3.24.1"
|
|
85
|
+
},
|
|
86
|
+
devDependencies: {
|
|
87
|
+
"@biomejs/biome": "^1.9.4",
|
|
88
|
+
"@types/js-yaml": "^4.0.9",
|
|
89
|
+
"@types/node": "^22.13.0",
|
|
90
|
+
tsup: "^8.4.0",
|
|
91
|
+
typescript: "^5.8.0",
|
|
92
|
+
vitest: "^3.0.0"
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// src/commands/admin.ts
|
|
97
|
+
import { Command as Command2 } from "commander";
|
|
98
|
+
|
|
99
|
+
// src/help/grouped-help.ts
|
|
100
|
+
var GROUP = /* @__PURE__ */ Symbol("openbkn.group");
|
|
101
|
+
var DEFAULT_GROUP = "COMMANDS";
|
|
102
|
+
function group(cmd, name) {
|
|
103
|
+
cmd[GROUP] = name;
|
|
104
|
+
return cmd;
|
|
105
|
+
}
|
|
106
|
+
function groupOf(cmd) {
|
|
107
|
+
return cmd[GROUP] ?? DEFAULT_GROUP;
|
|
108
|
+
}
|
|
109
|
+
function formatHelp(cmd, helper) {
|
|
110
|
+
const out = [];
|
|
111
|
+
const desc = helper.commandDescription(cmd);
|
|
112
|
+
if (desc) out.push(desc, "");
|
|
113
|
+
out.push("USAGE", ` ${helper.commandUsage(cmd)}`, "");
|
|
114
|
+
const subs = helper.visibleCommands(cmd);
|
|
115
|
+
if (subs.length > 0) {
|
|
116
|
+
const width = Math.max(...subs.map((c) => helper.subcommandTerm(c).length));
|
|
117
|
+
const sections = /* @__PURE__ */ new Map();
|
|
118
|
+
for (const c of subs) {
|
|
119
|
+
const g = groupOf(c);
|
|
120
|
+
const bucket = sections.get(g);
|
|
121
|
+
if (bucket) bucket.push(c);
|
|
122
|
+
else sections.set(g, [c]);
|
|
123
|
+
}
|
|
124
|
+
for (const [name, cmds] of sections) {
|
|
125
|
+
out.push(name);
|
|
126
|
+
for (const c of cmds) {
|
|
127
|
+
out.push(` ${helper.subcommandTerm(c).padEnd(width)} ${helper.subcommandDescription(c)}`);
|
|
128
|
+
}
|
|
129
|
+
out.push("");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const opts = helper.visibleOptions(cmd);
|
|
133
|
+
if (opts.length > 0) {
|
|
134
|
+
const width = Math.max(...opts.map((o) => helper.optionTerm(o).length));
|
|
135
|
+
out.push("FLAGS");
|
|
136
|
+
for (const o of opts) {
|
|
137
|
+
out.push(` ${helper.optionTerm(o).padEnd(width)} ${helper.optionDescription(o)}`);
|
|
138
|
+
}
|
|
139
|
+
out.push("");
|
|
140
|
+
}
|
|
141
|
+
return out.join("\n");
|
|
142
|
+
}
|
|
143
|
+
function installGroupedHelp(root) {
|
|
144
|
+
const apply = (cmd) => {
|
|
145
|
+
cmd.configureHelp({ formatHelp });
|
|
146
|
+
for (const child of cmd.commands) apply(child);
|
|
147
|
+
};
|
|
148
|
+
apply(root);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/utils/output.ts
|
|
152
|
+
import Table from "cli-table3";
|
|
153
|
+
function printJson(value, opts = {}) {
|
|
154
|
+
if (opts.json || opts.compact) {
|
|
155
|
+
process.stdout.write(`${JSON.stringify(value, null, opts.compact ? 0 : 2)}
|
|
156
|
+
`);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const rows = toRows(value);
|
|
160
|
+
if (rows) {
|
|
161
|
+
const columns = columnsOf(rows);
|
|
162
|
+
if (columns.length > 0) {
|
|
163
|
+
printTable(rows, columns);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
168
|
+
`);
|
|
169
|
+
}
|
|
170
|
+
var ROW_ENVELOPES = ["entries", "data", "cases", "reports", "results", "list", "recurringRules"];
|
|
171
|
+
function toRows(value) {
|
|
172
|
+
const isRowArray = (v) => Array.isArray(v) && v.length > 0 && v.every((x) => x !== null && typeof x === "object" && !Array.isArray(x));
|
|
173
|
+
if (isRowArray(value)) return value;
|
|
174
|
+
if (value && typeof value === "object") {
|
|
175
|
+
for (const key of ROW_ENVELOPES) {
|
|
176
|
+
const inner = value[key];
|
|
177
|
+
if (isRowArray(inner)) return inner;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
function columnsOf(rows) {
|
|
183
|
+
const seen = [];
|
|
184
|
+
for (const row of rows) {
|
|
185
|
+
for (const k of Object.keys(row)) if (!seen.includes(k)) seen.push(k);
|
|
186
|
+
}
|
|
187
|
+
return seen;
|
|
188
|
+
}
|
|
189
|
+
function printTable(rows, columns, opts = {}) {
|
|
190
|
+
if (opts.json || opts.compact) {
|
|
191
|
+
printJson(rows, opts);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const table = new Table({ head: columns });
|
|
195
|
+
for (const row of rows) {
|
|
196
|
+
table.push(columns.map((c) => stringifyCell(row[c])));
|
|
197
|
+
}
|
|
198
|
+
process.stdout.write(`${table.toString()}
|
|
199
|
+
`);
|
|
200
|
+
}
|
|
201
|
+
var CELL_MAX = 48;
|
|
202
|
+
function stringifyCell(v) {
|
|
203
|
+
if (v === null || v === void 0) return "";
|
|
204
|
+
const s = (typeof v === "object" ? JSON.stringify(v) : String(v)).replace(/\s+/g, " ").trim();
|
|
205
|
+
return s.length > CELL_MAX ? `${s.slice(0, CELL_MAX - 1)}\u2026` : s;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/commands/_shared.ts
|
|
209
|
+
import { readFileSync } from "fs";
|
|
210
|
+
function clientFrom(cmd) {
|
|
211
|
+
const o = cmd.optsWithGlobals();
|
|
212
|
+
return createClient({
|
|
213
|
+
baseUrl: o.baseUrl,
|
|
214
|
+
token: o.token,
|
|
215
|
+
user: o.user,
|
|
216
|
+
businessDomain: o.bizDomain,
|
|
217
|
+
insecure: o.insecure
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
function outputOptions(cmd) {
|
|
221
|
+
const o = cmd.optsWithGlobals();
|
|
222
|
+
return { json: Boolean(o.json), compact: Boolean(o.compact) };
|
|
223
|
+
}
|
|
224
|
+
function csv(value) {
|
|
225
|
+
if (!value) return void 0;
|
|
226
|
+
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
227
|
+
}
|
|
228
|
+
function readBody(opts) {
|
|
229
|
+
const raw = opts.bodyFile ? readFileSync(opts.bodyFile, "utf8") : opts.body;
|
|
230
|
+
if (!raw) throw new InputError("Provide --body '<json>' or --body-file <path>.");
|
|
231
|
+
try {
|
|
232
|
+
return JSON.parse(raw);
|
|
233
|
+
} catch {
|
|
234
|
+
throw new InputError("Request body is not valid JSON.");
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/commands/auth.ts
|
|
239
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
240
|
+
import { Command } from "commander";
|
|
241
|
+
|
|
242
|
+
// src/auth/oauth.ts
|
|
243
|
+
import { spawn } from "child_process";
|
|
244
|
+
import { createHash, constants as cryptoConstants, publicEncrypt, randomBytes } from "crypto";
|
|
245
|
+
import { createServer } from "http";
|
|
246
|
+
var DEFAULT_REDIRECT_PORT = 9010;
|
|
247
|
+
var DEFAULT_SCOPE = "openid offline all";
|
|
248
|
+
var STUDIOWEB_LOGIN_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
249
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyOstgbYuubBi2PUqeVj
|
|
250
|
+
GKlkwVUY6w1Y8d4k116dI2SkZI8fxcjHALv77kItO4jYLVplk9gO4HAtsisnNE2o
|
|
251
|
+
wlYIqdmyEPMwupaeFFFcg751oiTXJiYbtX7ABzU5KQYPjRSEjMq6i5qu/mL67XTk
|
|
252
|
+
hvKwrC83zme66qaKApmKupDODPb0RRkutK/zHfd1zL7sciBQ6psnNadh8pE24w8O
|
|
253
|
+
2XVy1v2bgSNkGHABgncR7seyIg81JQ3c/Axxd6GsTztjLnlvGAlmT1TphE84mi99
|
|
254
|
+
fUaGD2A1u1qdIuNc+XuisFeNcUW6fct0+x97eS2eEGRr/7qxWmO/P20sFVzXc2bF
|
|
255
|
+
1QIDAQAB
|
|
256
|
+
-----END PUBLIC KEY-----`;
|
|
257
|
+
function normalizeBaseUrl(value) {
|
|
258
|
+
return value.replace(/\/+$/, "");
|
|
259
|
+
}
|
|
260
|
+
function generatePkce() {
|
|
261
|
+
const verifier = randomBytes(48).toString("base64url");
|
|
262
|
+
return { verifier, challenge: createHash("sha256").update(verifier).digest("base64url") };
|
|
263
|
+
}
|
|
264
|
+
function buildAuthorizeUrl(base, clientId, redirectUri, state, codeChallenge, scope = DEFAULT_SCOPE) {
|
|
265
|
+
const params = new URLSearchParams({
|
|
266
|
+
response_type: "code",
|
|
267
|
+
client_id: clientId,
|
|
268
|
+
redirect_uri: redirectUri,
|
|
269
|
+
scope,
|
|
270
|
+
state,
|
|
271
|
+
"x-forwarded-prefix": "",
|
|
272
|
+
lang: "zh-cn",
|
|
273
|
+
product: "adp",
|
|
274
|
+
code_challenge: codeChallenge,
|
|
275
|
+
code_challenge_method: "S256"
|
|
276
|
+
});
|
|
277
|
+
return `${base}/oauth2/auth?${params.toString()}`;
|
|
278
|
+
}
|
|
279
|
+
function mapToken(data) {
|
|
280
|
+
return {
|
|
281
|
+
accessToken: data.access_token,
|
|
282
|
+
refreshToken: data.refresh_token,
|
|
283
|
+
idToken: data.id_token
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
async function registerClient(base, redirectUri, scope = DEFAULT_SCOPE) {
|
|
287
|
+
const res = await fetch(`${base}/oauth2/clients`, {
|
|
288
|
+
method: "POST",
|
|
289
|
+
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
290
|
+
body: JSON.stringify({
|
|
291
|
+
client_name: "openbkn-cli",
|
|
292
|
+
grant_types: ["authorization_code", "implicit", "refresh_token"],
|
|
293
|
+
response_types: ["token id_token", "code", "token"],
|
|
294
|
+
scope,
|
|
295
|
+
redirect_uris: [redirectUri],
|
|
296
|
+
post_logout_redirect_uris: [redirectUri.replace("/callback", "/successful-logout")],
|
|
297
|
+
metadata: { device: { name: "openbkn-cli", client_type: "web", description: "openbkn CLI" } }
|
|
298
|
+
})
|
|
299
|
+
});
|
|
300
|
+
if (!res.ok) {
|
|
301
|
+
throw new Error(
|
|
302
|
+
`Client registration failed (${res.status}): ${await res.text() || res.statusText}`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
const data = await res.json();
|
|
306
|
+
return { clientId: data.client_id, clientSecret: data.client_secret };
|
|
307
|
+
}
|
|
308
|
+
async function exchangeCode(base, code, redirectUri, client, codeVerifier) {
|
|
309
|
+
const params = {
|
|
310
|
+
grant_type: "authorization_code",
|
|
311
|
+
code,
|
|
312
|
+
redirect_uri: redirectUri,
|
|
313
|
+
code_verifier: codeVerifier
|
|
314
|
+
};
|
|
315
|
+
const headers = {
|
|
316
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
317
|
+
Accept: "application/json"
|
|
318
|
+
};
|
|
319
|
+
if (client.clientSecret) {
|
|
320
|
+
headers.Authorization = `Basic ${Buffer.from(`${client.clientId}:${client.clientSecret}`).toString("base64")}`;
|
|
321
|
+
} else {
|
|
322
|
+
params.client_id = client.clientId;
|
|
323
|
+
}
|
|
324
|
+
const res = await fetch(`${base}/oauth2/token`, {
|
|
325
|
+
method: "POST",
|
|
326
|
+
headers,
|
|
327
|
+
body: new URLSearchParams(params).toString()
|
|
328
|
+
});
|
|
329
|
+
if (!res.ok) {
|
|
330
|
+
throw new Error(
|
|
331
|
+
`Token exchange failed (${res.status}): ${await res.text() || res.statusText}`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
return mapToken(await res.json());
|
|
335
|
+
}
|
|
336
|
+
function startCallbackServer(port) {
|
|
337
|
+
return new Promise((resolve2, reject) => {
|
|
338
|
+
const server = createServer((req2, res) => {
|
|
339
|
+
const u = new URL(req2.url ?? "/", `http://127.0.0.1:${port}`);
|
|
340
|
+
if (u.pathname !== "/callback") {
|
|
341
|
+
res.writeHead(404);
|
|
342
|
+
res.end();
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const code = u.searchParams.get("code");
|
|
346
|
+
const error = u.searchParams.get("error");
|
|
347
|
+
if (error) {
|
|
348
|
+
res.writeHead(400, { "content-type": "text/html" });
|
|
349
|
+
res.end(`<h1>Login failed</h1><p>${error}</p>`);
|
|
350
|
+
server.close(() => reject(new Error(`OAuth error: ${error}`)));
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (!code) {
|
|
354
|
+
res.writeHead(400);
|
|
355
|
+
res.end("missing code");
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
359
|
+
res.end("<h1>Login successful</h1><p>You can close this window.</p>");
|
|
360
|
+
resolve2({
|
|
361
|
+
code,
|
|
362
|
+
state: u.searchParams.get("state") ?? void 0,
|
|
363
|
+
close: () => server.close()
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
server.on("error", reject);
|
|
367
|
+
server.listen(port, "127.0.0.1");
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
function openBrowser(url) {
|
|
371
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
372
|
+
try {
|
|
373
|
+
spawn(cmd, [url], {
|
|
374
|
+
stdio: "ignore",
|
|
375
|
+
detached: true,
|
|
376
|
+
shell: process.platform === "win32"
|
|
377
|
+
}).unref();
|
|
378
|
+
} catch {
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
async function browserLogin(baseUrl, opts = {}) {
|
|
382
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
383
|
+
const port = opts.port ?? DEFAULT_REDIRECT_PORT;
|
|
384
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
385
|
+
const scope = opts.scope ?? DEFAULT_SCOPE;
|
|
386
|
+
const client = opts.clientId ? { clientId: opts.clientId } : await registerClient(base, redirectUri, scope);
|
|
387
|
+
const { verifier, challenge } = generatePkce();
|
|
388
|
+
const state = randomBytes(12).toString("hex");
|
|
389
|
+
const authUrl = buildAuthorizeUrl(base, client.clientId, redirectUri, state, challenge, scope);
|
|
390
|
+
const waiter = startCallbackServer(port);
|
|
391
|
+
if (opts.noBrowser) {
|
|
392
|
+
process.stderr.write(`Open this URL to log in:
|
|
393
|
+
${authUrl}
|
|
394
|
+
`);
|
|
395
|
+
} else {
|
|
396
|
+
process.stderr.write(`Opening browser for login\u2026
|
|
397
|
+
If it doesn't open, visit:
|
|
398
|
+
${authUrl}
|
|
399
|
+
`);
|
|
400
|
+
openBrowser(authUrl);
|
|
401
|
+
}
|
|
402
|
+
const { code, state: returned, close } = await waiter;
|
|
403
|
+
close();
|
|
404
|
+
if (returned && returned !== state) throw new Error("OAuth state mismatch \u2014 possible CSRF.");
|
|
405
|
+
return exchangeCode(base, code, redirectUri, client, verifier);
|
|
406
|
+
}
|
|
407
|
+
function mergeCookies(existing, res) {
|
|
408
|
+
const setCookies = typeof res.headers.getSetCookie === "function" ? res.headers.getSetCookie() : res.headers.get("set-cookie") ? [res.headers.get("set-cookie")] : [];
|
|
409
|
+
const map = /* @__PURE__ */ new Map();
|
|
410
|
+
const add = (pair) => {
|
|
411
|
+
const eq = pair.indexOf("=");
|
|
412
|
+
if (eq > 0) map.set(pair.slice(0, eq), pair.slice(eq + 1));
|
|
413
|
+
};
|
|
414
|
+
for (const p of existing.split(";").map((s) => s.trim()).filter(Boolean))
|
|
415
|
+
add(p);
|
|
416
|
+
for (const sc of setCookies) add(sc.split(";")[0]?.trim() ?? "");
|
|
417
|
+
return [...map.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
|
|
418
|
+
}
|
|
419
|
+
function parseSigninProps(html) {
|
|
420
|
+
const m = html.match(/<script[^>]*\bid=["']__NEXT_DATA__["'][^>]*>([\s\S]*?)<\/script>/i);
|
|
421
|
+
if (!m?.[1]) throw new Error("Could not find __NEXT_DATA__ on /oauth2/signin.");
|
|
422
|
+
const data = JSON.parse(m[1]);
|
|
423
|
+
const pp = data.props?.pageProps;
|
|
424
|
+
const csrftoken = pp?.csrftoken ?? pp?._csrf;
|
|
425
|
+
if (typeof csrftoken !== "string") throw new Error("Sign-in page did not expose csrftoken.");
|
|
426
|
+
return {
|
|
427
|
+
csrftoken,
|
|
428
|
+
challenge: typeof pp?.challenge === "string" ? pp.challenge : void 0,
|
|
429
|
+
remember: pp?.remember === true || pp?.remember === "true"
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
async function followToCallback(startUrl, jar0, state, redirectUri) {
|
|
433
|
+
let url = startUrl;
|
|
434
|
+
let jar = jar0;
|
|
435
|
+
const cb = new URL(redirectUri);
|
|
436
|
+
for (let hop = 0; hop < 40; hop++) {
|
|
437
|
+
const resp = await fetch(url, {
|
|
438
|
+
headers: { Cookie: jar, Accept: "text/html,*/*;q=0.8" },
|
|
439
|
+
redirect: "manual"
|
|
440
|
+
});
|
|
441
|
+
jar = mergeCookies(jar, resp);
|
|
442
|
+
if (![302, 303, 307, 308].includes(resp.status)) {
|
|
443
|
+
throw new Error(`Unexpected OAuth response (HTTP ${resp.status}).`);
|
|
444
|
+
}
|
|
445
|
+
const loc = resp.headers.get("location");
|
|
446
|
+
if (!loc) throw new Error(`OAuth redirect missing Location (HTTP ${resp.status}).`);
|
|
447
|
+
const next = new URL(loc, url);
|
|
448
|
+
if (next.origin === cb.origin && next.pathname === cb.pathname) {
|
|
449
|
+
const err = next.searchParams.get("error");
|
|
450
|
+
if (err) throw new Error(`Authorization failed: ${err}`);
|
|
451
|
+
const code = next.searchParams.get("code");
|
|
452
|
+
if (next.searchParams.get("state") !== state) throw new Error("OAuth state mismatch.");
|
|
453
|
+
if (!code) throw new Error("Callback missing authorization code.");
|
|
454
|
+
return code;
|
|
455
|
+
}
|
|
456
|
+
url = next.href;
|
|
457
|
+
}
|
|
458
|
+
throw new Error("Too many OAuth redirects.");
|
|
459
|
+
}
|
|
460
|
+
async function passwordLogin(baseUrl, username, password, opts = {}) {
|
|
461
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
462
|
+
const port = opts.port ?? DEFAULT_REDIRECT_PORT;
|
|
463
|
+
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
464
|
+
const scope = opts.scope ?? DEFAULT_SCOPE;
|
|
465
|
+
const client = opts.clientId ? { clientId: opts.clientId } : await registerClient(base, redirectUri, scope);
|
|
466
|
+
const { verifier, challenge } = generatePkce();
|
|
467
|
+
const state = randomBytes(12).toString("hex");
|
|
468
|
+
let jar = "";
|
|
469
|
+
const authResp = await fetch(
|
|
470
|
+
buildAuthorizeUrl(base, client.clientId, redirectUri, state, challenge, scope),
|
|
471
|
+
{ redirect: "manual" }
|
|
472
|
+
);
|
|
473
|
+
jar = mergeCookies(jar, authResp);
|
|
474
|
+
const authLoc = authResp.headers.get("location");
|
|
475
|
+
if (!authLoc) throw new Error(`/oauth2/auth did not redirect (HTTP ${authResp.status}).`);
|
|
476
|
+
const signinUrl = new URL(authLoc, base);
|
|
477
|
+
if (!signinUrl.pathname.includes("signin")) {
|
|
478
|
+
throw new Error(`Expected a sign-in redirect, got: ${authLoc}`);
|
|
479
|
+
}
|
|
480
|
+
const pageResp = await fetch(signinUrl.href, {
|
|
481
|
+
headers: { Cookie: jar, Accept: "text/html,*/*;q=0.8" },
|
|
482
|
+
redirect: "manual"
|
|
483
|
+
});
|
|
484
|
+
jar = mergeCookies(jar, pageResp);
|
|
485
|
+
const props = parseSigninProps(await pageResp.text());
|
|
486
|
+
const loginChallenge = signinUrl.searchParams.get("login_challenge")?.trim() || props.challenge?.trim();
|
|
487
|
+
if (!loginChallenge) throw new Error("Could not resolve the login challenge.");
|
|
488
|
+
const cipher = publicEncrypt(
|
|
489
|
+
{
|
|
490
|
+
key: opts.signinPublicKeyPem ?? STUDIOWEB_LOGIN_PUBLIC_KEY_PEM,
|
|
491
|
+
padding: cryptoConstants.RSA_PKCS1_PADDING
|
|
492
|
+
},
|
|
493
|
+
Buffer.from(password, "utf8")
|
|
494
|
+
).toString("base64");
|
|
495
|
+
const postResp = await fetch(`${base}/oauth2/signin`, {
|
|
496
|
+
method: "POST",
|
|
497
|
+
headers: {
|
|
498
|
+
Cookie: jar,
|
|
499
|
+
"Content-Type": "application/json",
|
|
500
|
+
Accept: "application/json, text/plain, */*",
|
|
501
|
+
Origin: new URL(base).origin,
|
|
502
|
+
Referer: signinUrl.href
|
|
503
|
+
},
|
|
504
|
+
body: JSON.stringify({
|
|
505
|
+
_csrf: props.csrftoken,
|
|
506
|
+
challenge: loginChallenge,
|
|
507
|
+
account: username,
|
|
508
|
+
password: cipher,
|
|
509
|
+
vcode: { id: "", content: "" },
|
|
510
|
+
dualfactorauthinfo: { validcode: { vcode: "" }, OTP: { OTP: "" } },
|
|
511
|
+
remember: props.remember ?? false,
|
|
512
|
+
device: { name: "", description: "", client_type: "console_web", udids: [] }
|
|
513
|
+
}),
|
|
514
|
+
redirect: "manual"
|
|
515
|
+
});
|
|
516
|
+
jar = mergeCookies(jar, postResp);
|
|
517
|
+
let code;
|
|
518
|
+
if ([302, 303, 307].includes(postResp.status)) {
|
|
519
|
+
const loc = postResp.headers.get("location");
|
|
520
|
+
if (!loc) throw new Error("Sign-in response missing Location.");
|
|
521
|
+
code = await followToCallback(new URL(loc, base).href, jar, state, redirectUri);
|
|
522
|
+
} else if (postResp.status === 200) {
|
|
523
|
+
const text = await postResp.text();
|
|
524
|
+
let json = null;
|
|
525
|
+
try {
|
|
526
|
+
json = JSON.parse(text);
|
|
527
|
+
} catch {
|
|
528
|
+
}
|
|
529
|
+
const redir = json && typeof json.redirect === "string" ? json.redirect : "";
|
|
530
|
+
if (!redir) {
|
|
531
|
+
const msg = json && typeof json.message === "string" ? json.message : text.slice(0, 300);
|
|
532
|
+
throw new InputError(`Sign-in failed: ${msg}`);
|
|
533
|
+
}
|
|
534
|
+
code = await followToCallback(new URL(redir, base).href, jar, state, redirectUri);
|
|
535
|
+
} else {
|
|
536
|
+
throw new InputError(
|
|
537
|
+
`Sign-in failed (HTTP ${postResp.status}): ${(await postResp.text()).slice(0, 300)}`
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
return exchangeCode(base, code, redirectUri, client, verifier);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// src/commands/auth.ts
|
|
544
|
+
function registerAuthLeaves(cmd) {
|
|
545
|
+
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("--port <n>", "local callback port", (v) => Number.parseInt(v, 10)).option(
|
|
546
|
+
"--signin-public-key-file <path>",
|
|
547
|
+
"override the RSA public key (PEM) for password signin"
|
|
548
|
+
).option("--product <name>", "OAuth product query (default 'adp')").option("--no-browser", "headless: print the authorize URL instead of opening a browser").action(async (url, opts, cmd2) => {
|
|
549
|
+
const g = cmd2.optsWithGlobals();
|
|
550
|
+
if (g.insecure) process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
551
|
+
const token = opts.token ?? g.token;
|
|
552
|
+
if (token) {
|
|
553
|
+
const r2 = attachToken(url, token, { insecure: g.insecure });
|
|
554
|
+
printJson({ loggedIn: true, ...r2 }, outputOptions(cmd2));
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
const signinKey = opts.signinPublicKeyFile ? readFileSync2(opts.signinPublicKeyFile, "utf8") : void 0;
|
|
558
|
+
const tokens = opts.username ? await passwordLogin(url, opts.username, opts.password ?? "", {
|
|
559
|
+
clientId: opts.clientId,
|
|
560
|
+
port: opts.port,
|
|
561
|
+
signinPublicKeyPem: signinKey
|
|
562
|
+
}) : await browserLogin(url, {
|
|
563
|
+
clientId: opts.clientId,
|
|
564
|
+
port: opts.port,
|
|
565
|
+
noBrowser: opts.browser === false
|
|
566
|
+
});
|
|
567
|
+
const r = attachToken(url, tokens.accessToken, {
|
|
568
|
+
refreshToken: tokens.refreshToken,
|
|
569
|
+
idToken: tokens.idToken,
|
|
570
|
+
insecure: g.insecure
|
|
571
|
+
});
|
|
572
|
+
printJson({ loggedIn: true, ...r }, outputOptions(cmd2));
|
|
573
|
+
});
|
|
574
|
+
cmd.command("status").description("Show base URL and whether a token is configured").action((_opts, cmd2) => printJson(status(), outputOptions(cmd2)));
|
|
575
|
+
cmd.command("token").description("Print the current access token (keep secret)").action(() => {
|
|
576
|
+
process.stdout.write(`${currentToken()}
|
|
577
|
+
`);
|
|
578
|
+
});
|
|
579
|
+
cmd.command("whoami [url]").description("Show current user identity (from the token)").option("--no-lookup", "skip the backend identity fallback (eacp/user/get)").action(
|
|
580
|
+
(_url, _opts, cmd2) => printJson(whoami(), outputOptions(cmd2))
|
|
581
|
+
);
|
|
582
|
+
cmd.command("list").alias("ls").description("List platforms with a saved session").action((_opts, cmd2) => printJson(listPlatforms(), outputOptions(cmd2)));
|
|
583
|
+
cmd.command("use <url>").description("Switch the active platform").action((url, _opts, cmd2) => {
|
|
584
|
+
use(url);
|
|
585
|
+
printJson(status(), outputOptions(cmd2));
|
|
586
|
+
});
|
|
587
|
+
cmd.command("logout").description("Remove the stored token for the active platform").action((_opts, cmd2) => printJson({ loggedOut: logout() }, outputOptions(cmd2)));
|
|
588
|
+
cmd.command("delete <url>").description("Delete saved credentials for a platform").action(
|
|
589
|
+
(url, _opts, cmd2) => printJson({ deleted: deletePlatform(url) }, outputOptions(cmd2))
|
|
590
|
+
);
|
|
591
|
+
cmd.command("switch <url> <user-id>").description("Switch the active user for a platform").action((url, userId, _opts, cmd2) => {
|
|
592
|
+
printJson(switchUser(url, userId), outputOptions(cmd2));
|
|
593
|
+
});
|
|
594
|
+
cmd.command("users <url>").description("List saved user profiles for a platform").action((url, _opts, cmd2) => {
|
|
595
|
+
printJson(usersOf(url), outputOptions(cmd2));
|
|
596
|
+
});
|
|
597
|
+
cmd.command("export").description("Export the active session's tokens (for a headless host)").action((_opts, cmd2) => {
|
|
598
|
+
printJson(exportCreds(), outputOptions(cmd2));
|
|
599
|
+
});
|
|
600
|
+
cmd.command("change-password [url]").description("Change your account password (EACP, RSA-encrypted in transit)").requiredOption("-a, --account <name>", "account / login name").requiredOption("--old-password <pwd>", "current password").requiredOption("--new-password <pwd>", "new password").option("--public-key-file <path>", "override the RSA public key (PEM) for password encryption").action(async (_url, opts, cmd2) => {
|
|
601
|
+
const g = cmd2.optsWithGlobals();
|
|
602
|
+
const ctx = resolveContext({
|
|
603
|
+
baseUrl: g.baseUrl,
|
|
604
|
+
token: g.token,
|
|
605
|
+
user: g.user,
|
|
606
|
+
businessDomain: g.bizDomain,
|
|
607
|
+
insecure: g.insecure
|
|
608
|
+
});
|
|
609
|
+
printJson(
|
|
610
|
+
await changePassword(ctx, opts.account, opts.oldPassword, opts.newPassword),
|
|
611
|
+
outputOptions(cmd2)
|
|
612
|
+
);
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
function authCommand() {
|
|
616
|
+
const cmd = new Command("auth").description("Login, session, and token management");
|
|
617
|
+
registerAuthLeaves(cmd);
|
|
618
|
+
return group(cmd, "AUTHENTICATION & CONFIG");
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// src/commands/admin.ts
|
|
622
|
+
var int = (v) => Number.parseInt(v, 10);
|
|
623
|
+
function adminCommand() {
|
|
624
|
+
const admin = new Command2("admin").description(
|
|
625
|
+
"Operator CLI (kweaver-admin): org, user, role, models, audit"
|
|
626
|
+
);
|
|
627
|
+
registerAuthLeaves(admin.command("auth").description("Operator authentication"));
|
|
628
|
+
const org = admin.command("org").description("Departments and org structure");
|
|
629
|
+
org.command("list").description("List departments").option("--role <r>", "role qualifier", "super_admin").option("--name <s>", "filter by name").option("--limit <n>", "page size", int, 100).option("--offset <n>", "page offset", int, 0).action(async (opts, cmd) => {
|
|
630
|
+
printJson(
|
|
631
|
+
await clientFrom(cmd).admin.orgList({
|
|
632
|
+
role: opts.role,
|
|
633
|
+
name: opts.name,
|
|
634
|
+
limit: opts.limit,
|
|
635
|
+
offset: opts.offset
|
|
636
|
+
}),
|
|
637
|
+
outputOptions(cmd)
|
|
638
|
+
);
|
|
639
|
+
});
|
|
640
|
+
org.command("get <id>").description("Get one department").action(async (id, _opts, cmd) => {
|
|
641
|
+
printJson(await clientFrom(cmd).admin.orgGet(id), outputOptions(cmd));
|
|
642
|
+
});
|
|
643
|
+
org.command("members <id>").description("List members of a department").option("--role <r>", "role qualifier", "super_admin").option("--fields <s>", "fields segment", "users").option("--limit <n>", "page size", int, 100).option("--offset <n>", "page offset", int, 0).action(async (id, opts, cmd) => {
|
|
644
|
+
printJson(
|
|
645
|
+
await clientFrom(cmd).admin.orgMembers(id, {
|
|
646
|
+
role: opts.role,
|
|
647
|
+
limit: opts.limit,
|
|
648
|
+
offset: opts.offset
|
|
649
|
+
}),
|
|
650
|
+
outputOptions(cmd)
|
|
651
|
+
);
|
|
652
|
+
});
|
|
653
|
+
org.command("create").description("Create a department").requiredOption("--name <s>", "department name").option("--parent <id>", "parent department id", "-1").option("--code <s>", "department code").option("--remark <s>", "remark").option("--email <s>", "email").option("--manager <id>", "manager user id").option("--status <n>", "status (1=enabled)", int).option("--oss-id <id>", "object-storage site id").action(async (opts, cmd) => {
|
|
654
|
+
printJson(
|
|
655
|
+
await clientFrom(cmd).admin.orgCreate({
|
|
656
|
+
name: opts.name,
|
|
657
|
+
parentId: opts.parent,
|
|
658
|
+
code: opts.code,
|
|
659
|
+
remark: opts.remark,
|
|
660
|
+
email: opts.email,
|
|
661
|
+
managerID: opts.manager,
|
|
662
|
+
status: opts.status
|
|
663
|
+
}),
|
|
664
|
+
outputOptions(cmd)
|
|
665
|
+
);
|
|
666
|
+
});
|
|
667
|
+
org.command("update <id>").description("Update a department (only provided fields change)").option("--name <s>", "new name").option("--code <s>", "department code").option("--remark <s>", "remark").option("--email <s>", "email").option("--manager <id>", "manager user id").option("--status <n>", "status (1=enabled)", int).option("--oss-id <id>", "object-storage site id").action(async (id, opts, cmd) => {
|
|
668
|
+
printJson(
|
|
669
|
+
await clientFrom(cmd).admin.orgUpdate(id, {
|
|
670
|
+
name: opts.name,
|
|
671
|
+
code: opts.code,
|
|
672
|
+
remark: opts.remark,
|
|
673
|
+
email: opts.email,
|
|
674
|
+
managerID: opts.manager,
|
|
675
|
+
status: opts.status
|
|
676
|
+
}),
|
|
677
|
+
outputOptions(cmd)
|
|
678
|
+
);
|
|
679
|
+
});
|
|
680
|
+
org.command("delete <id>").description("Delete a department").action(async (id, _opts, cmd) => {
|
|
681
|
+
printJson(await clientFrom(cmd).admin.orgDelete(id), outputOptions(cmd));
|
|
682
|
+
});
|
|
683
|
+
org.command("tree").description("Print the department hierarchy").option("--role <r>", "role qualifier", "super_admin").action(async (opts, cmd) => {
|
|
684
|
+
const tree = await clientFrom(cmd).admin.orgTree(opts.role);
|
|
685
|
+
const out = outputOptions(cmd);
|
|
686
|
+
if (out.json) printJson(tree, out);
|
|
687
|
+
else console.log(renderOrgTree(tree));
|
|
688
|
+
});
|
|
689
|
+
const user = admin.command("user").description("User management");
|
|
690
|
+
user.command("list").description("List users").option("--org <id>", "filter by department id").option("--keyword <s>", "filter by name").option("--limit <n>", "page size", int, 100).option("--offset <n>", "page offset", int, 0).action(async (opts, cmd) => {
|
|
691
|
+
printJson(
|
|
692
|
+
await clientFrom(cmd).admin.userList({
|
|
693
|
+
orgId: opts.org,
|
|
694
|
+
name: opts.keyword,
|
|
695
|
+
limit: opts.limit,
|
|
696
|
+
offset: opts.offset
|
|
697
|
+
}),
|
|
698
|
+
outputOptions(cmd)
|
|
699
|
+
);
|
|
700
|
+
});
|
|
701
|
+
user.command("assign-role <user> <role>").description("Grant a role to a user").action(async (userId, roleId, _opts, cmd) => {
|
|
702
|
+
printJson(
|
|
703
|
+
await clientFrom(cmd).admin.addRoleMember(roleId, userId, "user"),
|
|
704
|
+
outputOptions(cmd)
|
|
705
|
+
);
|
|
706
|
+
});
|
|
707
|
+
user.command("revoke-role <user> <role>").description("Revoke a role from a user").action(async (userId, roleId, _opts, cmd) => {
|
|
708
|
+
printJson(
|
|
709
|
+
await clientFrom(cmd).admin.removeRoleMember(roleId, userId, "user"),
|
|
710
|
+
outputOptions(cmd)
|
|
711
|
+
);
|
|
712
|
+
});
|
|
713
|
+
user.command("get <id>").description("Get one user").action(async (id, _opts, cmd) => {
|
|
714
|
+
printJson(await clientFrom(cmd).admin.userGet(id), outputOptions(cmd));
|
|
715
|
+
});
|
|
716
|
+
user.command("roles <user>").description("List roles granted to a user").action(async (userId, _opts, cmd) => {
|
|
717
|
+
printJson(await clientFrom(cmd).admin.userRoles(userId), outputOptions(cmd));
|
|
718
|
+
});
|
|
719
|
+
user.command("create").description("Create a user (gets the platform default password)").requiredOption("--login <name>", "login name").option("--display-name <s>", "display name (defaults to login name)").option("--email <s>", "email").option("--department <id>", "department id", "-1").option("--code <s>", "user code").option("--position <s>", "position").option("--remark <s>", "remark").option("--tel <s>", "telephone").option("--priority <n>", "priority", int).option("--csf-level <n>", "confidentiality level", int).action(async (opts, cmd) => {
|
|
720
|
+
printJson(
|
|
721
|
+
await clientFrom(cmd).admin.userCreate({
|
|
722
|
+
loginName: opts.login,
|
|
723
|
+
displayName: opts.displayName,
|
|
724
|
+
email: opts.email,
|
|
725
|
+
departmentIds: opts.department ? [opts.department] : void 0,
|
|
726
|
+
code: opts.code,
|
|
727
|
+
position: opts.position,
|
|
728
|
+
remark: opts.remark,
|
|
729
|
+
telNumber: opts.tel,
|
|
730
|
+
priority: opts.priority,
|
|
731
|
+
csfLevel: opts.csfLevel
|
|
732
|
+
}),
|
|
733
|
+
outputOptions(cmd)
|
|
734
|
+
);
|
|
735
|
+
});
|
|
736
|
+
user.command("update <id>").description("Update a user (only provided fields change)").option("--display-name <s>", "display name").option("--email <s>", "email").option("--code <s>", "user code").option("--position <s>", "position").option("--remark <s>", "remark").option("--tel <s>", "telephone").option("--manager <id>", "manager user id").option("--idcard <s>", "id-card number").option("--priority <n>", "priority", int).option("--csf-level <n>", "confidentiality level", int).option("--csf-level2 <n>", "secondary confidentiality level", int).option("--expire-time <n>", "account expiry (epoch, -1 = never)", int).action(async (id, opts, cmd) => {
|
|
737
|
+
printJson(
|
|
738
|
+
await clientFrom(cmd).admin.userUpdate(id, {
|
|
739
|
+
displayName: opts.displayName,
|
|
740
|
+
email: opts.email,
|
|
741
|
+
code: opts.code,
|
|
742
|
+
position: opts.position,
|
|
743
|
+
remark: opts.remark,
|
|
744
|
+
telNumber: opts.tel,
|
|
745
|
+
managerID: opts.manager,
|
|
746
|
+
priority: opts.priority,
|
|
747
|
+
csfLevel: opts.csfLevel
|
|
748
|
+
}),
|
|
749
|
+
outputOptions(cmd)
|
|
750
|
+
);
|
|
751
|
+
});
|
|
752
|
+
user.command("delete <id>").description("Delete a user").action(async (id, _opts, cmd) => {
|
|
753
|
+
printJson(await clientFrom(cmd).admin.userDelete(id), outputOptions(cmd));
|
|
754
|
+
});
|
|
755
|
+
user.command("reset-password [id]").description("Reset a user's password (RSA-encrypted in transit)").option("--id <userId>", "explicit user UUID (alt to the positional id)").option("--user <account>", "resolve the user by account / login name").option("--password <s>", "the new password").option("--new-password <s>", "the new password (alias of --password)").option("--prompt-password", "prompt for the new password interactively").option("-y, --yes", "skip confirmation").action(async (id, opts, cmd) => {
|
|
756
|
+
const userId = id ?? opts.id ?? opts.user;
|
|
757
|
+
const pwd = opts.password ?? opts.newPassword;
|
|
758
|
+
if (!userId) throw new Error("Provide a user id (positional or --id).");
|
|
759
|
+
if (!pwd) throw new Error("Provide --password / --new-password.");
|
|
760
|
+
printJson(await clientFrom(cmd).admin.userResetPassword(userId, pwd), outputOptions(cmd));
|
|
761
|
+
});
|
|
762
|
+
const role = admin.command("role").description("Role management");
|
|
763
|
+
role.command("list").description("List roles").option("--keyword <s>", "filter by keyword").option("--limit <n>", "page size", int, 100).option("--offset <n>", "page offset", int, 0).option("--source <s>", "role source filter (business | user)").action(async (opts, cmd) => {
|
|
764
|
+
printJson(
|
|
765
|
+
await clientFrom(cmd).admin.roleList({ keyword: opts.keyword, limit: opts.limit }),
|
|
766
|
+
outputOptions(cmd)
|
|
767
|
+
);
|
|
768
|
+
});
|
|
769
|
+
role.command("get <role>").description("Get a role by id").option("--view <mode>", "detail view mode").action(async (roleId, _opts, cmd) => {
|
|
770
|
+
printJson(await clientFrom(cmd).admin.roleGet(roleId), outputOptions(cmd));
|
|
771
|
+
});
|
|
772
|
+
role.command("members <role>").description("List members of a role").option("--keyword <s>", "filter by keyword").option("--type <t>", "filter by member type (user|department|group|app)").option("--member <spec...>", "filter to specific member(s) '<type>:<id-or-name>'").option("--limit <n>", "page size", int, 100).option("--offset <n>", "page offset", int, 0).action(async (roleId, opts, cmd) => {
|
|
773
|
+
printJson(
|
|
774
|
+
await clientFrom(cmd).admin.roleMembers(roleId, {
|
|
775
|
+
keyword: opts.keyword,
|
|
776
|
+
limit: opts.limit
|
|
777
|
+
}),
|
|
778
|
+
outputOptions(cmd)
|
|
779
|
+
);
|
|
780
|
+
});
|
|
781
|
+
role.command("add-member <role> <id>").description("Add a member to a role").option("--type <t>", "member type", "user").option("--member <spec...>", "member(s) as '<type>:<id-or-name>' (alt to <id>)").action(async (roleId, id, opts, cmd) => {
|
|
782
|
+
printJson(
|
|
783
|
+
await clientFrom(cmd).admin.addRoleMember(roleId, id, opts.type),
|
|
784
|
+
outputOptions(cmd)
|
|
785
|
+
);
|
|
786
|
+
});
|
|
787
|
+
role.command("remove-member <role> <id>").description("Remove a member from a role").option("--type <t>", "member type", "user").option("--member <spec...>", "member(s) as '<type>:<id-or-name>' (alt to <id>)").action(async (roleId, id, opts, cmd) => {
|
|
788
|
+
printJson(
|
|
789
|
+
await clientFrom(cmd).admin.removeRoleMember(roleId, id, opts.type),
|
|
790
|
+
outputOptions(cmd)
|
|
791
|
+
);
|
|
792
|
+
});
|
|
793
|
+
const modelBody = (opts) => {
|
|
794
|
+
if (opts.body || opts.bodyFile) return readBody(opts);
|
|
795
|
+
const mc = {};
|
|
796
|
+
if (opts.apiModel) mc.api_model = opts.apiModel;
|
|
797
|
+
if (opts.apiUrl) mc.api_url = opts.apiUrl;
|
|
798
|
+
if (opts.apiBase) mc.api_url = opts.apiBase;
|
|
799
|
+
if (opts.apiKey) mc.api_key = opts.apiKey;
|
|
800
|
+
const body = {};
|
|
801
|
+
if (opts.name) body.model_name = opts.name;
|
|
802
|
+
if (opts.series) body.model_series = opts.series;
|
|
803
|
+
if (opts.type) body.model_type = opts.type;
|
|
804
|
+
if (opts.icon) body.icon = opts.icon;
|
|
805
|
+
if (opts.embeddingDim !== void 0) body.model_dimensions = opts.embeddingDim;
|
|
806
|
+
if (opts.maxTokens !== void 0) body.max_model_len = opts.maxTokens;
|
|
807
|
+
if (opts.batchSize !== void 0) body.batch_size = opts.batchSize;
|
|
808
|
+
if (Object.keys(mc).length > 0) body.model_config = mc;
|
|
809
|
+
return body;
|
|
810
|
+
};
|
|
811
|
+
for (const kind of ["llm", "small-model"]) {
|
|
812
|
+
const isLlm = kind === "llm";
|
|
813
|
+
const m = admin.command(kind).description(`${kind} management`);
|
|
814
|
+
const ns = isLlm ? "llm" : "small";
|
|
815
|
+
const list = m.command("list").description(`List ${kind} models`).option("--name <s>", "filter by name").option("--page <n>", "page", int, 1).option("--size <n>", "page size", int, DEFAULT_LIST_LIMIT);
|
|
816
|
+
(isLlm ? list.option("--series <s>", "model series") : list.option("--type <t>", "model type")).action(async (opts, cmd) => {
|
|
817
|
+
printJson(
|
|
818
|
+
await clientFrom(cmd).models[ns].list({
|
|
819
|
+
name: opts.name,
|
|
820
|
+
page: opts.page,
|
|
821
|
+
limit: opts.size,
|
|
822
|
+
modelType: opts.type
|
|
823
|
+
}),
|
|
824
|
+
outputOptions(cmd)
|
|
825
|
+
);
|
|
826
|
+
});
|
|
827
|
+
m.command("get <modelid>").description(`Get a ${kind} model`).action(async (id, _opts, cmd) => {
|
|
828
|
+
printJson(await clientFrom(cmd).models[ns].get(id), outputOptions(cmd));
|
|
829
|
+
});
|
|
830
|
+
const add = m.command("add").description(`Register a ${kind} model (granular flags or --body/--body-file)`).option("--name <s>", "model name").option("--api-model <s>", "upstream API model id").option("--api-key <s>", "upstream API key").option("--body <json>", "model config JSON (overrides flags)").option("--body-file <path>", "read config JSON from a file");
|
|
831
|
+
if (isLlm) {
|
|
832
|
+
add.option("--series <s>", "model series").option("--api-base <url>", "upstream API base URL").option("--icon <url>", "icon URL");
|
|
833
|
+
} else {
|
|
834
|
+
add.option("--type <t>", "small-model type (embedding|reranker)").option("--api-url <url>", "upstream API URL").option("--embedding-dim <n>", "embedding dimensions", int).option("--max-tokens <n>", "max tokens", int).option("--batch-size <n>", "batch size", int);
|
|
835
|
+
}
|
|
836
|
+
add.action(async (opts, cmd) => {
|
|
837
|
+
printJson(await clientFrom(cmd).models[ns].add(modelBody(opts)), outputOptions(cmd));
|
|
838
|
+
});
|
|
839
|
+
const edit = m.command("edit <modelid>").description(`Edit a ${kind} model (granular flags or --body/--body-file)`).option("--name <s>", "model name").option("--body <json>", "model config JSON (overrides flags)").option("--body-file <path>", "read config JSON from a file");
|
|
840
|
+
if (isLlm) {
|
|
841
|
+
edit.option("--icon <url>", "icon URL");
|
|
842
|
+
} else {
|
|
843
|
+
edit.option("--api-model <s>", "upstream API model id").option("--api-url <url>", "upstream API URL");
|
|
844
|
+
}
|
|
845
|
+
edit.action(async (id, opts, cmd) => {
|
|
846
|
+
printJson(
|
|
847
|
+
await clientFrom(cmd).models[ns].edit({ model_id: id, ...modelBody(opts) }),
|
|
848
|
+
outputOptions(cmd)
|
|
849
|
+
);
|
|
850
|
+
});
|
|
851
|
+
m.command("delete <modelid...>").description(`Delete ${kind} model(s)`).action(async (ids, _opts, cmd) => {
|
|
852
|
+
printJson(await clientFrom(cmd).models[ns].delete(ids), outputOptions(cmd));
|
|
853
|
+
});
|
|
854
|
+
m.command("test <modelid>").description(`Test a ${kind} model`).option("--body <json>", "test request JSON").option("--body-file <path>", "read test request JSON from a file").action(async (id, opts, cmd) => {
|
|
855
|
+
const body = opts.body || opts.bodyFile ? readBody(opts) : {};
|
|
856
|
+
printJson(
|
|
857
|
+
await clientFrom(cmd).models[ns].test({ model_id: id, ...body }),
|
|
858
|
+
outputOptions(cmd)
|
|
859
|
+
);
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
admin.command("audit").description("Audit log queries").command("list").description("List login audit events").option("--user <name>", "filter by user").option("--start <time>", "start time").option("--end <time>", "end time").option("--page <n>", "page", int, 1).option("--size <n>", "page size", int, 30).action(async (opts, cmd) => {
|
|
863
|
+
printJson(
|
|
864
|
+
await clientFrom(cmd).admin.auditList({
|
|
865
|
+
user: opts.user,
|
|
866
|
+
start: opts.start,
|
|
867
|
+
end: opts.end,
|
|
868
|
+
page: opts.page,
|
|
869
|
+
size: opts.size
|
|
870
|
+
}),
|
|
871
|
+
outputOptions(cmd)
|
|
872
|
+
);
|
|
873
|
+
});
|
|
874
|
+
const adminConfig = admin.command("config").description("Admin CLI config (active platform)");
|
|
875
|
+
adminConfig.command("show").description("Show the active platform").action((_opts, cmd) => {
|
|
876
|
+
printJson({ baseUrl: activePlatform() }, outputOptions(cmd));
|
|
877
|
+
});
|
|
878
|
+
adminConfig.command("set <key> <value>").description("Set a config value (baseUrl)").action((key, value, _opts, cmd) => {
|
|
879
|
+
if (key !== "baseUrl") {
|
|
880
|
+
throw new Error(`Unknown config key: ${key} (only baseUrl supported)`);
|
|
881
|
+
}
|
|
882
|
+
setActivePlatform(value.replace(/\/+$/, ""));
|
|
883
|
+
printJson({ ok: true, baseUrl: value }, outputOptions(cmd));
|
|
884
|
+
});
|
|
885
|
+
admin.command("call <url>").description("Operator API passthrough (curl-style; auto-injected auth)").option("-X, --request <method>", "HTTP method").option(
|
|
886
|
+
"-H, --header <header>",
|
|
887
|
+
'extra header "Name: value" (repeatable)',
|
|
888
|
+
(v, a) => {
|
|
889
|
+
a.push(v);
|
|
890
|
+
return a;
|
|
891
|
+
},
|
|
892
|
+
[]
|
|
893
|
+
).option("-d, --data <body>", "request body (JSON content-type if unset)").action(async (url, opts, cmd) => {
|
|
894
|
+
const g = cmd.optsWithGlobals();
|
|
895
|
+
const res = await rawCall(
|
|
896
|
+
resolveContext({
|
|
897
|
+
baseUrl: g.baseUrl,
|
|
898
|
+
token: g.token,
|
|
899
|
+
user: g.user,
|
|
900
|
+
businessDomain: g.bizDomain,
|
|
901
|
+
insecure: g.insecure
|
|
902
|
+
}),
|
|
903
|
+
url,
|
|
904
|
+
{ method: opts.request, header: opts.header, data: opts.data, businessDomain: g.bizDomain }
|
|
905
|
+
);
|
|
906
|
+
const out = outputOptions(cmd);
|
|
907
|
+
try {
|
|
908
|
+
printJson(JSON.parse(res.body), out);
|
|
909
|
+
} catch {
|
|
910
|
+
process.stdout.write(res.body.endsWith("\n") ? res.body : `${res.body}
|
|
911
|
+
`);
|
|
912
|
+
}
|
|
913
|
+
if (res.status >= 400) process.exitCode = 1;
|
|
914
|
+
});
|
|
915
|
+
return group(admin, "OPERATOR");
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/commands/agent.ts
|
|
919
|
+
import { Command as Command3 } from "commander";
|
|
920
|
+
var int2 = (v) => Number.parseInt(v, 10);
|
|
921
|
+
function agentCommand() {
|
|
922
|
+
const cmd = new Command3("agent").description("Agent CRUD, chat, sessions, publish");
|
|
923
|
+
cmd.command("list").description("List published agents").option("--name <s>", "filter by name").option("--limit <n>", "page size", int2, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int2, 0).option("--category-id <id>", "filter by category").action(async (opts, cmd2) => {
|
|
924
|
+
const data = await clientFrom(cmd2).agents.list({
|
|
925
|
+
name: opts.name,
|
|
926
|
+
limit: opts.limit,
|
|
927
|
+
offset: opts.offset,
|
|
928
|
+
categoryId: opts.categoryId
|
|
929
|
+
});
|
|
930
|
+
printJson(data, outputOptions(cmd2));
|
|
931
|
+
});
|
|
932
|
+
cmd.command("personal-list").description("List personal-space agents").option("--name <s>", "filter by name").option("--limit <n>", "page size", int2, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int2, 0).action(async (opts, cmd2) => {
|
|
933
|
+
const data = await clientFrom(cmd2).agents.personalList({
|
|
934
|
+
name: opts.name,
|
|
935
|
+
limit: opts.limit,
|
|
936
|
+
offset: opts.offset
|
|
937
|
+
});
|
|
938
|
+
printJson(data, outputOptions(cmd2));
|
|
939
|
+
});
|
|
940
|
+
cmd.command("category-list").description("List agent categories").action(async (_opts, cmd2) => {
|
|
941
|
+
printJson(await clientFrom(cmd2).agents.categoryList(), outputOptions(cmd2));
|
|
942
|
+
});
|
|
943
|
+
cmd.command("template-list").description("List published agent templates").option("--name <s>", "filter by name").option("--limit <n>", "page size", int2, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int2, 0).action(async (opts, cmd2) => {
|
|
944
|
+
const data = await clientFrom(cmd2).agents.templateList({
|
|
945
|
+
name: opts.name,
|
|
946
|
+
limit: opts.limit,
|
|
947
|
+
offset: opts.offset
|
|
948
|
+
});
|
|
949
|
+
printJson(data, outputOptions(cmd2));
|
|
950
|
+
});
|
|
951
|
+
cmd.command("template-get <id>").description("Get a published agent template").action(async (id, _opts, cmd2) => {
|
|
952
|
+
printJson(await clientFrom(cmd2).agents.templateGet(id), outputOptions(cmd2));
|
|
953
|
+
});
|
|
954
|
+
cmd.command("get <id>").description("Get agent details").action(async (id, _opts, cmd2) => {
|
|
955
|
+
printJson(await clientFrom(cmd2).agents.get(id), outputOptions(cmd2));
|
|
956
|
+
});
|
|
957
|
+
cmd.command("get-by-key <key>").description("Get an agent by key").action(async (key, _opts, cmd2) => {
|
|
958
|
+
printJson(await clientFrom(cmd2).agents.getByKey(key), outputOptions(cmd2));
|
|
959
|
+
});
|
|
960
|
+
cmd.command("create").description("Create an agent (--body-file <json> or --body '<json>')").option("--body <json>", "agent definition JSON").option("--body-file <path>", "read agent definition JSON from a file").action(async (opts, cmd2) => {
|
|
961
|
+
printJson(await clientFrom(cmd2).agents.create(readBody(opts)), outputOptions(cmd2));
|
|
962
|
+
});
|
|
963
|
+
cmd.command("update <id>").description("Update an agent (--body-file <json> or --body '<json>')").option("--body <json>", "agent definition JSON").option("--body-file <path>", "read agent definition JSON from a file").action(async (id, opts, cmd2) => {
|
|
964
|
+
printJson(await clientFrom(cmd2).agents.update(id, readBody(opts)), outputOptions(cmd2));
|
|
965
|
+
});
|
|
966
|
+
cmd.command("delete <id>").description("Delete an agent").option("-y, --yes", "skip confirmation").action(async (id, _opts, cmd2) => {
|
|
967
|
+
printJson(await clientFrom(cmd2).agents.delete(id), outputOptions(cmd2));
|
|
968
|
+
});
|
|
969
|
+
cmd.command("publish <id>").description("Publish an agent").action(async (id, _opts, cmd2) => {
|
|
970
|
+
printJson(await clientFrom(cmd2).agents.publish(id), outputOptions(cmd2));
|
|
971
|
+
});
|
|
972
|
+
cmd.command("unpublish <id>").description("Unpublish an agent").action(async (id, _opts, cmd2) => {
|
|
973
|
+
printJson(await clientFrom(cmd2).agents.unpublish(id), outputOptions(cmd2));
|
|
974
|
+
});
|
|
975
|
+
cmd.command("sessions <agent>").description("List conversations for an agent (by agent key)").option("--limit <n>", "page size", int2, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int2, 1).action(async (agentKey, opts, cmd2) => {
|
|
976
|
+
printJson(
|
|
977
|
+
await clientFrom(cmd2).agents.sessions(agentKey, { size: opts.limit, page: opts.page }),
|
|
978
|
+
outputOptions(cmd2)
|
|
979
|
+
);
|
|
980
|
+
});
|
|
981
|
+
cmd.command("history <agent> <conversation-id>").description("Show message history for a conversation").action(async (agentKey, conversationId, _opts, cmd2) => {
|
|
982
|
+
printJson(await clientFrom(cmd2).agents.history(agentKey, conversationId), outputOptions(cmd2));
|
|
983
|
+
});
|
|
984
|
+
cmd.command("chat <agent-id>").description("Chat with an agent (SSE streaming with --stream)").requiredOption("-m, --message <text>", "user message").option("--version <v>", "agent version", "v0").option("--conversation-id <id>", "continue an existing conversation").option("--stream", "stream the reply to stdout as it arrives").action(async (agentId, opts, cmd2) => {
|
|
985
|
+
const client = clientFrom(cmd2);
|
|
986
|
+
if (opts.stream) {
|
|
987
|
+
const res = await client.agents.chat(agentId, opts.message, {
|
|
988
|
+
version: opts.version,
|
|
989
|
+
conversationId: opts.conversationId,
|
|
990
|
+
stream: true,
|
|
991
|
+
onDelta: (t) => process.stdout.write(t)
|
|
992
|
+
});
|
|
993
|
+
process.stdout.write("\n");
|
|
994
|
+
if (res.conversationId) console.error(`conversation_id: ${res.conversationId}`);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
printJson(
|
|
998
|
+
await client.agents.chat(agentId, opts.message, {
|
|
999
|
+
version: opts.version,
|
|
1000
|
+
conversationId: opts.conversationId
|
|
1001
|
+
}),
|
|
1002
|
+
outputOptions(cmd2)
|
|
1003
|
+
);
|
|
1004
|
+
});
|
|
1005
|
+
cmd.command("trace <conversation-id>").description("Get trace spans for a conversation (agent-scoped alias of `trace get`)").action(async (conversationId, _opts, cmd2) => {
|
|
1006
|
+
printJson(await clientFrom(cmd2).trace.spans(conversationId), outputOptions(cmd2));
|
|
1007
|
+
});
|
|
1008
|
+
const skill = cmd.command("skill").description("Manage skills attached to an agent");
|
|
1009
|
+
skill.command("list <agent-id>").description("List skill ids attached to an agent").action(async (agentId, _opts, cmd2) => {
|
|
1010
|
+
printJson(await clientFrom(cmd2).agents.skillList(agentId), outputOptions(cmd2));
|
|
1011
|
+
});
|
|
1012
|
+
skill.command("add <agent-id> <skill-ids>").description("Attach skill(s) to an agent (comma-joined ids)").action(async (agentId, ids, _opts, cmd2) => {
|
|
1013
|
+
printJson(await clientFrom(cmd2).agents.skillAdd(agentId, csv(ids) ?? []), outputOptions(cmd2));
|
|
1014
|
+
});
|
|
1015
|
+
skill.command("remove <agent-id> <skill-ids>").description("Detach skill(s) from an agent (comma-joined ids)").action(async (agentId, ids, _opts, cmd2) => {
|
|
1016
|
+
printJson(
|
|
1017
|
+
await clientFrom(cmd2).agents.skillRemove(agentId, csv(ids) ?? []),
|
|
1018
|
+
outputOptions(cmd2)
|
|
1019
|
+
);
|
|
1020
|
+
});
|
|
1021
|
+
return group(cmd, "DECISION AGENT");
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// src/commands/bkn.ts
|
|
1025
|
+
import { Command as Command4 } from "commander";
|
|
1026
|
+
|
|
1027
|
+
// src/utils/bkn-validate.ts
|
|
1028
|
+
import { existsSync, readFileSync as readFileSync3, readdirSync, statSync } from "fs";
|
|
1029
|
+
import { join, resolve } from "path";
|
|
1030
|
+
var BKN_OBJECT_NAME_MAX_LENGTH = 40;
|
|
1031
|
+
function parseFrontmatter(text) {
|
|
1032
|
+
if (!text.startsWith("---")) return null;
|
|
1033
|
+
const end = text.indexOf("\n---", 3);
|
|
1034
|
+
if (end === -1) return null;
|
|
1035
|
+
const block = text.slice(3, end);
|
|
1036
|
+
const fm = {};
|
|
1037
|
+
for (const line of block.split("\n")) {
|
|
1038
|
+
const m = line.match(/^\s*(type|id|name)\s*:\s*(.+?)\s*$/);
|
|
1039
|
+
if (m?.[1]) fm[m[1]] = m[2]?.replace(/^["']|["']$/g, "");
|
|
1040
|
+
}
|
|
1041
|
+
return fm;
|
|
1042
|
+
}
|
|
1043
|
+
function bknFiles(dir) {
|
|
1044
|
+
if (!existsSync(dir) || !statSync(dir).isDirectory()) return [];
|
|
1045
|
+
return readdirSync(dir).filter((f) => f.endsWith(".bkn")).map((f) => join(dir, f));
|
|
1046
|
+
}
|
|
1047
|
+
function endpointRefs(text) {
|
|
1048
|
+
const refs = [];
|
|
1049
|
+
const idx = text.indexOf("### Endpoint");
|
|
1050
|
+
if (idx === -1) return refs;
|
|
1051
|
+
const after = text.slice(idx + "### Endpoint".length);
|
|
1052
|
+
const next = after.indexOf("\n###");
|
|
1053
|
+
const section = next === -1 ? after : after.slice(0, next);
|
|
1054
|
+
for (const line of section.split("\n")) {
|
|
1055
|
+
const cells = line.split("|").map((c) => c.trim()).filter(Boolean);
|
|
1056
|
+
if (cells.length < 2) continue;
|
|
1057
|
+
const [source, target] = cells;
|
|
1058
|
+
if (!source || !target) continue;
|
|
1059
|
+
if (/^source$/i.test(source) || /^-+$/.test(source)) continue;
|
|
1060
|
+
refs.push({ source, target });
|
|
1061
|
+
}
|
|
1062
|
+
return refs;
|
|
1063
|
+
}
|
|
1064
|
+
function validateBknDirectory(dirPath) {
|
|
1065
|
+
const dir = resolve(dirPath);
|
|
1066
|
+
const errors = [];
|
|
1067
|
+
const warnings = [];
|
|
1068
|
+
if (!existsSync(dir) || !statSync(dir).isDirectory()) {
|
|
1069
|
+
return {
|
|
1070
|
+
valid: false,
|
|
1071
|
+
dir,
|
|
1072
|
+
counts: { objectTypes: 0, relationTypes: 0, conceptGroups: 0 },
|
|
1073
|
+
errors: [`Not a directory: ${dir}`],
|
|
1074
|
+
warnings: []
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
const networkPath = join(dir, "network.bkn");
|
|
1078
|
+
if (!existsSync(networkPath)) {
|
|
1079
|
+
errors.push("Missing network.bkn at the BKN root.");
|
|
1080
|
+
} else {
|
|
1081
|
+
const fm = parseFrontmatter(readFileSync3(networkPath, "utf8"));
|
|
1082
|
+
if (!fm) errors.push("network.bkn has no frontmatter block.");
|
|
1083
|
+
else {
|
|
1084
|
+
if (fm.type !== "knowledge_network")
|
|
1085
|
+
errors.push(`network.bkn type must be 'knowledge_network', got '${fm.type ?? ""}'.`);
|
|
1086
|
+
if (!fm.id) errors.push("network.bkn is missing 'id'.");
|
|
1087
|
+
if (!fm.name) errors.push("network.bkn is missing 'name'.");
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
const otIds = /* @__PURE__ */ new Set();
|
|
1091
|
+
const otFiles = bknFiles(join(dir, "object_types"));
|
|
1092
|
+
for (const file of otFiles) {
|
|
1093
|
+
const fm = parseFrontmatter(readFileSync3(file, "utf8"));
|
|
1094
|
+
const rel = file.slice(dir.length + 1);
|
|
1095
|
+
if (!fm || fm.type !== "object_type") {
|
|
1096
|
+
errors.push(`${rel}: not a valid object_type (missing/wrong frontmatter type).`);
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
if (!fm.id) errors.push(`${rel}: object_type missing 'id'.`);
|
|
1100
|
+
if (!fm.name) errors.push(`${rel}: object_type missing 'name'.`);
|
|
1101
|
+
if (fm.name && [...fm.name].length > BKN_OBJECT_NAME_MAX_LENGTH) {
|
|
1102
|
+
errors.push(
|
|
1103
|
+
`${rel}: object_type name exceeds ${BKN_OBJECT_NAME_MAX_LENGTH} codepoints ('${fm.name}').`
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
if (fm.id) {
|
|
1107
|
+
if (otIds.has(fm.id)) errors.push(`Duplicate object_type id '${fm.id}'.`);
|
|
1108
|
+
otIds.add(fm.id);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
const rtFiles = bknFiles(join(dir, "relation_types"));
|
|
1112
|
+
for (const file of rtFiles) {
|
|
1113
|
+
const text = readFileSync3(file, "utf8");
|
|
1114
|
+
const fm = parseFrontmatter(text);
|
|
1115
|
+
const rel = file.slice(dir.length + 1);
|
|
1116
|
+
if (!fm || fm.type !== "relation_type") {
|
|
1117
|
+
errors.push(`${rel}: not a valid relation_type (missing/wrong frontmatter type).`);
|
|
1118
|
+
continue;
|
|
1119
|
+
}
|
|
1120
|
+
if (!fm.id) errors.push(`${rel}: relation_type missing 'id'.`);
|
|
1121
|
+
for (const { source, target } of endpointRefs(text)) {
|
|
1122
|
+
if (otIds.size > 0 && !otIds.has(source))
|
|
1123
|
+
warnings.push(`${rel}: endpoint source '${source}' is not a known object_type.`);
|
|
1124
|
+
if (otIds.size > 0 && !otIds.has(target))
|
|
1125
|
+
warnings.push(`${rel}: endpoint target '${target}' is not a known object_type.`);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
const cgFiles = bknFiles(join(dir, "concept_groups"));
|
|
1129
|
+
return {
|
|
1130
|
+
valid: errors.length === 0,
|
|
1131
|
+
dir,
|
|
1132
|
+
counts: {
|
|
1133
|
+
objectTypes: otFiles.length,
|
|
1134
|
+
relationTypes: rtFiles.length,
|
|
1135
|
+
conceptGroups: cgFiles.length
|
|
1136
|
+
},
|
|
1137
|
+
errors,
|
|
1138
|
+
warnings
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// src/commands/bkn.ts
|
|
1143
|
+
var int3 = (v) => Number.parseInt(v, 10);
|
|
1144
|
+
function bknCommand() {
|
|
1145
|
+
const bkn = new Command4("bkn").description("Knowledge networks \u2014 list, query, schema, instances");
|
|
1146
|
+
bkn.command("list").description("List knowledge networks").option("--limit <n>", "page size", int3, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int3, 0).option("--name-pattern <s>", "filter by name pattern").option("--tag <s>", "filter by tag").option("--sort <field>", "sort field", "update_time").option("--direction <dir>", "asc | desc", "desc").action(async (_opts, cmd) => {
|
|
1147
|
+
const o = cmd.optsWithGlobals();
|
|
1148
|
+
const data = await clientFrom(cmd).kn.list({
|
|
1149
|
+
limit: o.limit,
|
|
1150
|
+
offset: o.offset,
|
|
1151
|
+
namePattern: o.namePattern,
|
|
1152
|
+
tag: o.tag,
|
|
1153
|
+
sort: o.sort,
|
|
1154
|
+
direction: o.direction
|
|
1155
|
+
});
|
|
1156
|
+
printJson(data, outputOptions(cmd));
|
|
1157
|
+
});
|
|
1158
|
+
bkn.command("get <kn-id>").description("Get a knowledge network (use --stats or --export)").option("--stats", "include statistics").option("--export", "return the full export payload").action(async (knId, opts, cmd) => {
|
|
1159
|
+
const data = await clientFrom(cmd).kn.get(knId, {
|
|
1160
|
+
stats: opts.stats,
|
|
1161
|
+
exportMode: opts.export
|
|
1162
|
+
});
|
|
1163
|
+
printJson(data, outputOptions(cmd));
|
|
1164
|
+
});
|
|
1165
|
+
bkn.command("search <kn-id> <query>").description("Semantic search within a knowledge network").option("--max-concepts <n>", "max concepts to return", int3, 10).option("--mode <mode>", "retrieval mode", "keyword_vector_retrieval").action(async (knId, query, opts, cmd) => {
|
|
1166
|
+
const data = await clientFrom(cmd).kn.search(knId, query, {
|
|
1167
|
+
maxConcepts: opts.maxConcepts,
|
|
1168
|
+
mode: opts.mode
|
|
1169
|
+
});
|
|
1170
|
+
printJson(data, outputOptions(cmd));
|
|
1171
|
+
});
|
|
1172
|
+
const schemaGroups = [
|
|
1173
|
+
["object-type", "objectTypes", "objectType"],
|
|
1174
|
+
["relation-type", "relationTypes", "relationType"],
|
|
1175
|
+
["action-type", "actionTypes", null]
|
|
1176
|
+
];
|
|
1177
|
+
for (const [name, listMethod, crud] of schemaGroups) {
|
|
1178
|
+
const g = bkn.command(name).description(`${name} list/get/...`);
|
|
1179
|
+
g.command("list <kn-id>").description(`List ${name}s`).option("--branch <b>", "branch", "main").action(async (knId, opts, cmd) => {
|
|
1180
|
+
printJson(
|
|
1181
|
+
await clientFrom(cmd).kn[listMethod](knId, { branch: opts.branch }),
|
|
1182
|
+
outputOptions(cmd)
|
|
1183
|
+
);
|
|
1184
|
+
});
|
|
1185
|
+
if (crud) {
|
|
1186
|
+
g.command("get <kn-id> <id>").description(`Get ${name}`).action(async (knId, id, _o, cmd) => {
|
|
1187
|
+
printJson(await clientFrom(cmd).kn[`${crud}Get`](knId, id), outputOptions(cmd));
|
|
1188
|
+
});
|
|
1189
|
+
g.command("create <kn-id>").description(`Create ${name} (--body / --body-file)`).option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
|
|
1190
|
+
printJson(
|
|
1191
|
+
await clientFrom(cmd).kn[`${crud}Create`](knId, readBody(opts)),
|
|
1192
|
+
outputOptions(cmd)
|
|
1193
|
+
);
|
|
1194
|
+
});
|
|
1195
|
+
g.command("update <kn-id> <id>").description(`Update ${name} (--body / --body-file)`).option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, id, opts, cmd) => {
|
|
1196
|
+
printJson(
|
|
1197
|
+
await clientFrom(cmd).kn[`${crud}Update`](knId, id, readBody(opts)),
|
|
1198
|
+
outputOptions(cmd)
|
|
1199
|
+
);
|
|
1200
|
+
});
|
|
1201
|
+
g.command("delete <kn-id> <id>").description(`Delete ${name}`).option("-y, --yes", "skip confirmation").action(async (knId, id, _o, cmd) => {
|
|
1202
|
+
printJson(await clientFrom(cmd).kn[`${crud}Delete`](knId, id), outputOptions(cmd));
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
const actionType = bkn.commands.find((c) => c.name() === "action-type");
|
|
1207
|
+
actionType?.command("query <kn-id> <at-id>").description("Query an action type (--body / --body-file JSON)").option("--body <json>", "query JSON").option("--body-file <path>", "read query JSON from a file").action(async (knId, atId, opts, cmd) => {
|
|
1208
|
+
printJson(
|
|
1209
|
+
await clientFrom(cmd).kn.actionTypeQuery(knId, atId, readBody(opts)),
|
|
1210
|
+
outputOptions(cmd)
|
|
1211
|
+
);
|
|
1212
|
+
});
|
|
1213
|
+
actionType?.command("execute <kn-id> <at-id>").description("Execute an action type (--body / --body-file envelope JSON)").option("--body <json>", "execution envelope JSON").option("--body-file <path>", "read envelope JSON from a file").action(async (knId, atId, opts, cmd) => {
|
|
1214
|
+
printJson(
|
|
1215
|
+
await clientFrom(cmd).kn.actionTypeExecute(knId, atId, readBody(opts)),
|
|
1216
|
+
outputOptions(cmd)
|
|
1217
|
+
);
|
|
1218
|
+
});
|
|
1219
|
+
actionType?.command("inputs <kn-id> <at-id>").description("Get an action type's input schema").action(async (knId, atId, _o, cmd) => {
|
|
1220
|
+
printJson(await clientFrom(cmd).kn.actionTypeInputs(knId, atId), outputOptions(cmd));
|
|
1221
|
+
});
|
|
1222
|
+
actionType?.command("get <kn-id> <at-id>").description("Get an action type").action(async (knId, atId, _o, cmd) => {
|
|
1223
|
+
printJson(await clientFrom(cmd).kn.actionTypeGet(knId, atId), outputOptions(cmd));
|
|
1224
|
+
});
|
|
1225
|
+
bkn.command("stats <kn-id>").description("Get knowledge-network statistics (alias for get --stats)").action(async (knId, _opts, cmd) => {
|
|
1226
|
+
printJson(await clientFrom(cmd).kn.get(knId, { stats: true }), outputOptions(cmd));
|
|
1227
|
+
});
|
|
1228
|
+
bkn.command("export <kn-id>").description("Export a knowledge network (alias for get --export)").action(async (knId, _opts, cmd) => {
|
|
1229
|
+
printJson(await clientFrom(cmd).kn.get(knId, { exportMode: true }), outputOptions(cmd));
|
|
1230
|
+
});
|
|
1231
|
+
const objectType = bkn.commands.find((c) => c.name() === "object-type");
|
|
1232
|
+
objectType?.command("query <kn-id> <ot-id>").description("Query instances of an object type (--body / --body-file JSON)").option("--body <json>", "query JSON").option("--body-file <path>", "read query JSON from a file").action(async (knId, otId, opts, cmd) => {
|
|
1233
|
+
printJson(
|
|
1234
|
+
await clientFrom(cmd).kn.objectTypeQuery(knId, otId, readBody(opts)),
|
|
1235
|
+
outputOptions(cmd)
|
|
1236
|
+
);
|
|
1237
|
+
});
|
|
1238
|
+
objectType?.command("properties <kn-id> <ot-id>").description("Get an object type's calculated properties").action(async (knId, otId, _opts, cmd) => {
|
|
1239
|
+
printJson(await clientFrom(cmd).kn.objectTypeProperties(knId, otId), outputOptions(cmd));
|
|
1240
|
+
});
|
|
1241
|
+
bkn.command("create <name>").description("Create an (empty) knowledge network").option("--branch <b>", "branch", "main").action(async (name, opts, cmd) => {
|
|
1242
|
+
printJson(await clientFrom(cmd).kn.create({ name, branch: opts.branch }), outputOptions(cmd));
|
|
1243
|
+
});
|
|
1244
|
+
bkn.command("update <kn-id>").description("Update a knowledge network (--body / --body-file)").option("--body <json>", "update body JSON").option("--body-file <path>", "read update body JSON from a file").action(async (knId, opts, cmd) => {
|
|
1245
|
+
printJson(await clientFrom(cmd).kn.update(knId, readBody(opts)), outputOptions(cmd));
|
|
1246
|
+
});
|
|
1247
|
+
bkn.command("delete <kn-id>").description("Delete a knowledge network").option("-y, --yes", "skip confirmation").action(async (knId, _opts, cmd) => {
|
|
1248
|
+
printJson(await clientFrom(cmd).kn.delete(knId), outputOptions(cmd));
|
|
1249
|
+
});
|
|
1250
|
+
bkn.command("subgraph <kn-id>").description("Query a subgraph (--body / --body-file JSON)").option("--body <json>", "subgraph query JSON").option("--body-file <path>", "read subgraph query JSON from a file").action(async (knId, opts, cmd) => {
|
|
1251
|
+
printJson(await clientFrom(cmd).kn.subgraph(knId, readBody(opts)), outputOptions(cmd));
|
|
1252
|
+
});
|
|
1253
|
+
const actionLog = bkn.command("action-log").description("Action logs \u2014 list/get/cancel");
|
|
1254
|
+
actionLog.command("list <kn-id>").description("List action logs").option("--status <s>", "filter by status").option("--action-type-id <id>", "filter by action type").option("--limit <n>", "page size", int3, DEFAULT_LIST_LIMIT).action(async (knId, opts, cmd) => {
|
|
1255
|
+
printJson(
|
|
1256
|
+
await clientFrom(cmd).kn.actionLogs(knId, {
|
|
1257
|
+
status: opts.status,
|
|
1258
|
+
actionTypeId: opts.actionTypeId,
|
|
1259
|
+
limit: opts.limit
|
|
1260
|
+
}),
|
|
1261
|
+
outputOptions(cmd)
|
|
1262
|
+
);
|
|
1263
|
+
});
|
|
1264
|
+
actionLog.command("get <kn-id> <log-id>").description("Get an action log").action(async (knId, logId, _opts, cmd) => {
|
|
1265
|
+
printJson(await clientFrom(cmd).kn.actionLog(knId, logId), outputOptions(cmd));
|
|
1266
|
+
});
|
|
1267
|
+
actionLog.command("cancel <kn-id> <log-id>").description("Cancel a running action").action(async (knId, logId, _opts, cmd) => {
|
|
1268
|
+
printJson(await clientFrom(cmd).kn.cancelActionLog(knId, logId), outputOptions(cmd));
|
|
1269
|
+
});
|
|
1270
|
+
bkn.command("action-execution <kn-id> <execution-id>").description("Get action execution status").action(async (knId, execId, _opts, cmd) => {
|
|
1271
|
+
printJson(await clientFrom(cmd).kn.actionExecution(knId, execId), outputOptions(cmd));
|
|
1272
|
+
});
|
|
1273
|
+
const metric = bkn.command("metric").description("Metrics \u2014 query / dry-run");
|
|
1274
|
+
metric.command("query <kn-id> <metric-id>").description("Query a metric's data (--body / --body-file JSON)").option("--body <json>", "query JSON").option("--body-file <path>", "read query JSON from a file").action(async (knId, metricId, opts, cmd) => {
|
|
1275
|
+
printJson(
|
|
1276
|
+
await clientFrom(cmd).kn.metricQuery(knId, metricId, readBody(opts)),
|
|
1277
|
+
outputOptions(cmd)
|
|
1278
|
+
);
|
|
1279
|
+
});
|
|
1280
|
+
metric.command("dry-run <kn-id>").description("Dry-run a metric definition (--body / --body-file JSON)").option("--body <json>", "metric definition JSON").option("--body-file <path>", "read metric definition JSON from a file").action(async (knId, opts, cmd) => {
|
|
1281
|
+
printJson(await clientFrom(cmd).kn.metricDryRun(knId, readBody(opts)), outputOptions(cmd));
|
|
1282
|
+
});
|
|
1283
|
+
metric.command("list <kn-id>").description("List metrics").action(async (knId, _o, cmd) => {
|
|
1284
|
+
printJson(await clientFrom(cmd).kn.metricList(knId), outputOptions(cmd));
|
|
1285
|
+
});
|
|
1286
|
+
metric.command("get <kn-id> <metric-id>").description("Get a metric").action(async (knId, id, _o, cmd) => {
|
|
1287
|
+
printJson(await clientFrom(cmd).kn.metricGet(knId, id), outputOptions(cmd));
|
|
1288
|
+
});
|
|
1289
|
+
metric.command("create <kn-id>").description("Create a metric (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
|
|
1290
|
+
printJson(await clientFrom(cmd).kn.metricCreate(knId, readBody(opts)), outputOptions(cmd));
|
|
1291
|
+
});
|
|
1292
|
+
metric.command("update <kn-id> <metric-id>").description("Update a metric (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, id, opts, cmd) => {
|
|
1293
|
+
printJson(
|
|
1294
|
+
await clientFrom(cmd).kn.metricUpdate(knId, id, readBody(opts)),
|
|
1295
|
+
outputOptions(cmd)
|
|
1296
|
+
);
|
|
1297
|
+
});
|
|
1298
|
+
metric.command("delete <kn-id> <metric-id>").description("Delete a metric").action(async (knId, id, _o, cmd) => {
|
|
1299
|
+
printJson(await clientFrom(cmd).kn.metricDelete(knId, id), outputOptions(cmd));
|
|
1300
|
+
});
|
|
1301
|
+
metric.command("search <kn-id>").description("Search metrics (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
|
|
1302
|
+
printJson(await clientFrom(cmd).kn.metricSearch(knId, readBody(opts)), outputOptions(cmd));
|
|
1303
|
+
});
|
|
1304
|
+
metric.command("validate <kn-id>").description("Validate a metric definition (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
|
|
1305
|
+
printJson(await clientFrom(cmd).kn.metricValidate(knId, readBody(opts)), outputOptions(cmd));
|
|
1306
|
+
});
|
|
1307
|
+
const cg = bkn.command("concept-group").description("Concept groups \u2014 list/get");
|
|
1308
|
+
cg.command("list <kn-id>").description("List concept groups").action(async (knId, _o, cmd) => {
|
|
1309
|
+
printJson(await clientFrom(cmd).kn.conceptGroups(knId), outputOptions(cmd));
|
|
1310
|
+
});
|
|
1311
|
+
cg.command("get <kn-id> <cg-id>").description("Get a concept group").action(async (knId, cgId, _o, cmd) => {
|
|
1312
|
+
printJson(await clientFrom(cmd).kn.conceptGroup(knId, cgId), outputOptions(cmd));
|
|
1313
|
+
});
|
|
1314
|
+
cg.command("create <kn-id>").description("Create a concept group (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
|
|
1315
|
+
printJson(
|
|
1316
|
+
await clientFrom(cmd).kn.conceptGroupCreate(knId, readBody(opts)),
|
|
1317
|
+
outputOptions(cmd)
|
|
1318
|
+
);
|
|
1319
|
+
});
|
|
1320
|
+
cg.command("update <kn-id> <cg-id>").description("Update a concept group (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, cgId, opts, cmd) => {
|
|
1321
|
+
printJson(
|
|
1322
|
+
await clientFrom(cmd).kn.conceptGroupUpdate(knId, cgId, readBody(opts)),
|
|
1323
|
+
outputOptions(cmd)
|
|
1324
|
+
);
|
|
1325
|
+
});
|
|
1326
|
+
cg.command("delete <kn-id> <cg-id>").description("Delete a concept group").action(async (knId, cgId, _o, cmd) => {
|
|
1327
|
+
printJson(await clientFrom(cmd).kn.conceptGroupDelete(knId, cgId), outputOptions(cmd));
|
|
1328
|
+
});
|
|
1329
|
+
cg.command("add-members <kn-id> <cg-id>").description("Add object types to a concept group (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, cgId, opts, cmd) => {
|
|
1330
|
+
printJson(
|
|
1331
|
+
await clientFrom(cmd).kn.conceptGroupAddMembers(knId, cgId, readBody(opts)),
|
|
1332
|
+
outputOptions(cmd)
|
|
1333
|
+
);
|
|
1334
|
+
});
|
|
1335
|
+
cg.command("remove-members <kn-id> <cg-id> <ot-ids>").description("Remove object types (comma-joined ids) from a concept group").action(async (knId, cgId, otIds, _o, cmd) => {
|
|
1336
|
+
printJson(
|
|
1337
|
+
await clientFrom(cmd).kn.conceptGroupRemoveMembers(knId, cgId, otIds),
|
|
1338
|
+
outputOptions(cmd)
|
|
1339
|
+
);
|
|
1340
|
+
});
|
|
1341
|
+
const sched = bkn.command("action-schedule").description("Action schedules \u2014 list/get");
|
|
1342
|
+
sched.command("list <kn-id>").description("List action schedules").action(async (knId, _o, cmd) => {
|
|
1343
|
+
printJson(await clientFrom(cmd).kn.actionSchedules(knId), outputOptions(cmd));
|
|
1344
|
+
});
|
|
1345
|
+
sched.command("get <kn-id> <schedule-id>").description("Get an action schedule").action(async (knId, sId, _o, cmd) => {
|
|
1346
|
+
printJson(await clientFrom(cmd).kn.actionSchedule(knId, sId), outputOptions(cmd));
|
|
1347
|
+
});
|
|
1348
|
+
sched.command("create <kn-id>").description("Create an action schedule (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, opts, cmd) => {
|
|
1349
|
+
printJson(
|
|
1350
|
+
await clientFrom(cmd).kn.actionScheduleCreate(knId, readBody(opts)),
|
|
1351
|
+
outputOptions(cmd)
|
|
1352
|
+
);
|
|
1353
|
+
});
|
|
1354
|
+
sched.command("update <kn-id> <schedule-id>").description("Update an action schedule (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, sId, opts, cmd) => {
|
|
1355
|
+
printJson(
|
|
1356
|
+
await clientFrom(cmd).kn.actionScheduleUpdate(knId, sId, readBody(opts)),
|
|
1357
|
+
outputOptions(cmd)
|
|
1358
|
+
);
|
|
1359
|
+
});
|
|
1360
|
+
sched.command("set-status <kn-id> <schedule-id>").description("Set an action schedule's status (--body / --body-file)").option("--body <json>", "body JSON").option("--body-file <path>", "read body JSON from a file").action(async (knId, sId, opts, cmd) => {
|
|
1361
|
+
printJson(
|
|
1362
|
+
await clientFrom(cmd).kn.actionScheduleSetStatus(knId, sId, readBody(opts)),
|
|
1363
|
+
outputOptions(cmd)
|
|
1364
|
+
);
|
|
1365
|
+
});
|
|
1366
|
+
sched.command("delete <kn-id> <schedule-ids>").description("Delete action schedule(s) (comma-joined ids)").action(async (knId, ids, _o, cmd) => {
|
|
1367
|
+
printJson(await clientFrom(cmd).kn.actionScheduleDelete(knId, ids), outputOptions(cmd));
|
|
1368
|
+
});
|
|
1369
|
+
const job = bkn.command("job").description("Build jobs \u2014 list/get/tasks");
|
|
1370
|
+
job.command("list <kn-id>").description("List jobs").action(async (knId, _o, cmd) => {
|
|
1371
|
+
printJson(await clientFrom(cmd).kn.jobs(knId), outputOptions(cmd));
|
|
1372
|
+
});
|
|
1373
|
+
job.command("get <kn-id> <job-id>").description("Get a job").action(async (knId, jobId, _o, cmd) => {
|
|
1374
|
+
printJson(await clientFrom(cmd).kn.job(knId, jobId), outputOptions(cmd));
|
|
1375
|
+
});
|
|
1376
|
+
job.command("tasks <kn-id> <job-id>").description("List a job's tasks").action(async (knId, jobId, _o, cmd) => {
|
|
1377
|
+
printJson(await clientFrom(cmd).kn.jobTasks(knId, jobId), outputOptions(cmd));
|
|
1378
|
+
});
|
|
1379
|
+
job.command("delete <kn-id> <job-ids>").description("Delete job(s) (comma-joined ids)").action(async (knId, ids, _o, cmd) => {
|
|
1380
|
+
printJson(await clientFrom(cmd).kn.jobDelete(knId, ids), outputOptions(cmd));
|
|
1381
|
+
});
|
|
1382
|
+
bkn.command("push <directory>").description("Pack a BKN directory into a tar and import it as a knowledge network").option("--branch <name>", "target branch", "main").action(async (dir, opts, cmd) => {
|
|
1383
|
+
printJson(await clientFrom(cmd).kn.push(dir, { branch: opts.branch }), outputOptions(cmd));
|
|
1384
|
+
});
|
|
1385
|
+
bkn.command("pull <kn-id> [directory]").description("Download a knowledge network as a BKN tar and extract it locally").option("--branch <name>", "source branch", "main").action(async (knId, dir, opts, cmd) => {
|
|
1386
|
+
printJson(
|
|
1387
|
+
await clientFrom(cmd).kn.pull(knId, dir ?? knId, { branch: opts.branch }),
|
|
1388
|
+
outputOptions(cmd)
|
|
1389
|
+
);
|
|
1390
|
+
});
|
|
1391
|
+
bkn.command("relation-type-paths <kn-id>").description("Query relation-type paths between object types (--body / --body-file JSON)").option("--body <json>", "request JSON").option("--body-file <path>", "read request JSON from a file").action(async (knId, opts, cmd) => {
|
|
1392
|
+
printJson(
|
|
1393
|
+
await clientFrom(cmd).kn.relationTypePaths(knId, readBody(opts)),
|
|
1394
|
+
outputOptions(cmd)
|
|
1395
|
+
);
|
|
1396
|
+
});
|
|
1397
|
+
bkn.command("resources").description("List BKN-backend resources").action(async (_opts, cmd) => {
|
|
1398
|
+
printJson(await clientFrom(cmd).kn.bknResources(), outputOptions(cmd));
|
|
1399
|
+
});
|
|
1400
|
+
bkn.command("create-from-catalog <catalog-id>").description("Build a knowledge network from a Vega catalog's tables").requiredOption("--name <name>", "knowledge network name").option("--tables <list>", "comma-separated table names (default: all)").option("--pk-map <map>", "explicit primary keys: '<table>:<col>[,<table>:<col>...]'").option("--build", "submit a Vega build task per resource after creation").option("--no-rollback", "keep a partially-created KN on failure").action(async (catalogId, opts, cmd) => {
|
|
1401
|
+
printJson(
|
|
1402
|
+
await clientFrom(cmd).kn.createFromCatalog({
|
|
1403
|
+
catalogId,
|
|
1404
|
+
name: opts.name,
|
|
1405
|
+
tables: csv(opts.tables),
|
|
1406
|
+
pkMap: opts.pkMap ? parsePkMap(opts.pkMap) : void 0,
|
|
1407
|
+
build: Boolean(opts.build),
|
|
1408
|
+
noRollback: opts.rollback === false,
|
|
1409
|
+
onProgress: (m) => console.error(m)
|
|
1410
|
+
}),
|
|
1411
|
+
outputOptions(cmd)
|
|
1412
|
+
);
|
|
1413
|
+
});
|
|
1414
|
+
bkn.command("validate <directory>").description("Validate a local BKN directory's structure (offline)").action(async (dir, _opts, cmd) => {
|
|
1415
|
+
const result = validateBknDirectory(dir);
|
|
1416
|
+
printJson(result, outputOptions(cmd));
|
|
1417
|
+
if (!result.valid) process.exitCode = 1;
|
|
1418
|
+
});
|
|
1419
|
+
bkn.command("create-from-csv <catalog-id>").description("Import CSV files into a Vega catalog, then build a KN from them").requiredOption("--files <glob>", "CSV paths (comma-separated or glob)").requiredOption("--name <name>", "knowledge network name").option("--table-prefix <s>", "prefix for derived table names", "").option("--batch-size <n>", "rows per insert batch", int3, 500).option("--tables <list>", "subset of imported tables to include in the KN").option("--pk-map <map>", "explicit primary keys: '<table>:<col>[,...]'").option("--build", "submit a Vega build task per resource after creation").option("--no-rollback", "keep a partially-created KN on failure").action(async (catalogId, opts, cmd) => {
|
|
1420
|
+
printJson(
|
|
1421
|
+
await clientFrom(cmd).kn.createFromCsv({
|
|
1422
|
+
catalogId,
|
|
1423
|
+
name: opts.name,
|
|
1424
|
+
files: opts.files,
|
|
1425
|
+
tablePrefix: opts.tablePrefix,
|
|
1426
|
+
batchSize: opts.batchSize,
|
|
1427
|
+
tables: csv(opts.tables),
|
|
1428
|
+
pkMap: opts.pkMap ? parsePkMap(opts.pkMap) : void 0,
|
|
1429
|
+
build: Boolean(opts.build),
|
|
1430
|
+
noRollback: opts.rollback === false,
|
|
1431
|
+
onProgress: (m) => console.error(m)
|
|
1432
|
+
}),
|
|
1433
|
+
outputOptions(cmd)
|
|
1434
|
+
);
|
|
1435
|
+
});
|
|
1436
|
+
return group(bkn, "AI DATA PLATFORM");
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// src/commands/call.ts
|
|
1440
|
+
import { Command as Command5 } from "commander";
|
|
1441
|
+
function collect(value, prev) {
|
|
1442
|
+
prev.push(value);
|
|
1443
|
+
return prev;
|
|
1444
|
+
}
|
|
1445
|
+
function callCommand() {
|
|
1446
|
+
const cmd = new Command5("call").alias("curl").description("Call an API with curl-style flags and auto-injected auth headers").argument("<url>", "API path (e.g. /api/...) or absolute URL").option("-X, --request <method>", "HTTP method").option("-H, --header <header>", 'extra header "Name: value" (repeatable)', collect, []).option("-d, --data <body>", "request body (sets JSON content-type if unset)").option("--data-raw <body>", "alias for --data").option(
|
|
1447
|
+
"-F, --form <field>",
|
|
1448
|
+
"multipart field key=value or key=@file (repeatable)",
|
|
1449
|
+
collect,
|
|
1450
|
+
[]
|
|
1451
|
+
).option("-v, --verbose", "print request line to stderr").action(async (url, opts, cmd2) => {
|
|
1452
|
+
const g = cmd2.optsWithGlobals();
|
|
1453
|
+
const ctx = resolveContext({
|
|
1454
|
+
baseUrl: g.baseUrl,
|
|
1455
|
+
token: g.token,
|
|
1456
|
+
user: g.user,
|
|
1457
|
+
businessDomain: g.bizDomain,
|
|
1458
|
+
insecure: g.insecure
|
|
1459
|
+
});
|
|
1460
|
+
const res = await rawCall(ctx, url, {
|
|
1461
|
+
method: opts.request,
|
|
1462
|
+
header: opts.header,
|
|
1463
|
+
data: opts.data ?? opts.dataRaw,
|
|
1464
|
+
form: opts.form,
|
|
1465
|
+
businessDomain: g.bizDomain,
|
|
1466
|
+
verbose: opts.verbose
|
|
1467
|
+
});
|
|
1468
|
+
const out = outputOptions(cmd2);
|
|
1469
|
+
try {
|
|
1470
|
+
printJson(JSON.parse(res.body), out);
|
|
1471
|
+
} catch {
|
|
1472
|
+
process.stdout.write(res.body.endsWith("\n") ? res.body : `${res.body}
|
|
1473
|
+
`);
|
|
1474
|
+
}
|
|
1475
|
+
if (res.status >= 400) process.exitCode = 1;
|
|
1476
|
+
});
|
|
1477
|
+
return group(cmd, "AUTHENTICATION & CONFIG");
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
// src/commands/config.ts
|
|
1481
|
+
import { Command as Command6 } from "commander";
|
|
1482
|
+
function requireActive() {
|
|
1483
|
+
const baseUrl = activePlatform();
|
|
1484
|
+
if (!baseUrl) throw new InputError("No active platform. Run `openbkn auth login <url>` first.");
|
|
1485
|
+
return baseUrl;
|
|
1486
|
+
}
|
|
1487
|
+
function configCommand() {
|
|
1488
|
+
const config = new Command6("config").description("Per-platform CLI configuration");
|
|
1489
|
+
config.command("show").description("Show the active platform and business domain").action((_opts, cmd) => {
|
|
1490
|
+
const baseUrl = activePlatform();
|
|
1491
|
+
printJson(
|
|
1492
|
+
{
|
|
1493
|
+
baseUrl,
|
|
1494
|
+
businessDomain: baseUrl ? readPlatformConfig(baseUrl).businessDomain : void 0
|
|
1495
|
+
},
|
|
1496
|
+
outputOptions(cmd)
|
|
1497
|
+
);
|
|
1498
|
+
});
|
|
1499
|
+
config.command("set <key> <value>").description("Set a config value (baseUrl | businessDomain)").action((key, value, _opts, cmd) => {
|
|
1500
|
+
if (key === "baseUrl") {
|
|
1501
|
+
setActivePlatform(value.replace(/\/+$/, ""));
|
|
1502
|
+
} else if (key === "businessDomain") {
|
|
1503
|
+
writePlatformConfig(requireActive(), { businessDomain: value });
|
|
1504
|
+
} else {
|
|
1505
|
+
throw new InputError(`Unknown config key: ${key} (expected baseUrl | businessDomain)`);
|
|
1506
|
+
}
|
|
1507
|
+
printJson({ ok: true, key, value }, outputOptions(cmd));
|
|
1508
|
+
});
|
|
1509
|
+
config.command("set-bd <value>").description("Set the default business domain for the active platform").action((value, _opts, cmd) => {
|
|
1510
|
+
const baseUrl = requireActive();
|
|
1511
|
+
writePlatformConfig(baseUrl, { businessDomain: value });
|
|
1512
|
+
printJson({ baseUrl, businessDomain: value }, outputOptions(cmd));
|
|
1513
|
+
});
|
|
1514
|
+
config.command("list-bd").description("List business domains (requires login)").action(() => {
|
|
1515
|
+
throw new InputError("Not yet implemented \u2014 requires backend business-domains API.");
|
|
1516
|
+
});
|
|
1517
|
+
return group(config, "AUTHENTICATION & CONFIG");
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// src/commands/context.ts
|
|
1521
|
+
import { Command as Command7 } from "commander";
|
|
1522
|
+
var int4 = (v) => Number.parseInt(v, 10);
|
|
1523
|
+
function contextCommand() {
|
|
1524
|
+
const cmd = new Command7("context").description(
|
|
1525
|
+
"Context loader (MCP) \u2014 schema discovery, instance query, skill recall"
|
|
1526
|
+
);
|
|
1527
|
+
cmd.command("search-schema <kn-id> <query>").description("Search object/relation/action/metric schemas").option("--scope <list>", "comma-separated scopes (object,relation,action,metric)").option("--max <n>", "max concepts", int4).action(async (knId, query, opts, cmd2) => {
|
|
1528
|
+
const data = await clientFrom(cmd2).context.searchSchema(knId, query, {
|
|
1529
|
+
searchScope: opts.scope ? String(opts.scope).split(",") : void 0,
|
|
1530
|
+
maxConcepts: opts.max
|
|
1531
|
+
});
|
|
1532
|
+
printJson(data, outputOptions(cmd2));
|
|
1533
|
+
});
|
|
1534
|
+
cmd.command("query-object-instance <kn-id>").description("Query object instances (provide --args as JSON)").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
|
|
1535
|
+
let args;
|
|
1536
|
+
try {
|
|
1537
|
+
args = JSON.parse(opts.args);
|
|
1538
|
+
} catch {
|
|
1539
|
+
throw new InputError("--args must be valid JSON");
|
|
1540
|
+
}
|
|
1541
|
+
printJson(await clientFrom(cmd2).context.queryObjectInstance(knId, args), outputOptions(cmd2));
|
|
1542
|
+
});
|
|
1543
|
+
cmd.command("find-skills <kn-id> <object-type-id>").description("Recall skills for an object type").option("--top-k <n>", "max skills (1-20)", int4).action(async (knId, otId, opts, cmd2) => {
|
|
1544
|
+
printJson(
|
|
1545
|
+
await clientFrom(cmd2).context.findSkills(knId, otId, opts.topK),
|
|
1546
|
+
outputOptions(cmd2)
|
|
1547
|
+
);
|
|
1548
|
+
});
|
|
1549
|
+
cmd.command("tools <kn-id>").description("List MCP tools").action(async (knId, _opts, cmd2) => {
|
|
1550
|
+
printJson(await clientFrom(cmd2).context.tools(knId), outputOptions(cmd2));
|
|
1551
|
+
});
|
|
1552
|
+
cmd.command("tool-call <kn-id> <name>").description("Call any MCP tool directly (--args JSON)").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, name, opts, cmd2) => {
|
|
1553
|
+
let args;
|
|
1554
|
+
try {
|
|
1555
|
+
args = JSON.parse(opts.args);
|
|
1556
|
+
} catch {
|
|
1557
|
+
throw new InputError("--args must be valid JSON");
|
|
1558
|
+
}
|
|
1559
|
+
printJson(await clientFrom(cmd2).context.toolCall(knId, name, args), outputOptions(cmd2));
|
|
1560
|
+
});
|
|
1561
|
+
cmd.command("resources <kn-id>").description("List MCP resources").action(async (knId, _opts, cmd2) => {
|
|
1562
|
+
printJson(await clientFrom(cmd2).context.resources(knId), outputOptions(cmd2));
|
|
1563
|
+
});
|
|
1564
|
+
cmd.command("resource <kn-id> <uri>").description("Read one MCP resource by uri").action(async (knId, uri, _opts, cmd2) => {
|
|
1565
|
+
printJson(await clientFrom(cmd2).context.resource(knId, uri), outputOptions(cmd2));
|
|
1566
|
+
});
|
|
1567
|
+
cmd.command("templates <kn-id>").description("List MCP resource templates").action(async (knId, _opts, cmd2) => {
|
|
1568
|
+
printJson(await clientFrom(cmd2).context.templates(knId), outputOptions(cmd2));
|
|
1569
|
+
});
|
|
1570
|
+
cmd.command("prompts <kn-id>").description("List MCP prompts").action(async (knId, _opts, cmd2) => {
|
|
1571
|
+
printJson(await clientFrom(cmd2).context.prompts(knId), outputOptions(cmd2));
|
|
1572
|
+
});
|
|
1573
|
+
cmd.command("prompt <kn-id> <name>").description("Get one MCP prompt (--args JSON for prompt arguments)").option("--args <json>", "prompt arguments as JSON").action(async (knId, name, opts, cmd2) => {
|
|
1574
|
+
let args;
|
|
1575
|
+
if (opts.args) {
|
|
1576
|
+
try {
|
|
1577
|
+
args = JSON.parse(opts.args);
|
|
1578
|
+
} catch {
|
|
1579
|
+
throw new InputError("--args must be valid JSON");
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
printJson(await clientFrom(cmd2).context.prompt(knId, name, args), outputOptions(cmd2));
|
|
1583
|
+
});
|
|
1584
|
+
const jsonArgs = (raw) => {
|
|
1585
|
+
try {
|
|
1586
|
+
return JSON.parse(raw);
|
|
1587
|
+
} catch {
|
|
1588
|
+
throw new InputError("--args must be valid JSON");
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
cmd.command("query-instance-subgraph <kn-id>").description("Layer-2: query an instance subgraph across relation-type paths").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
|
|
1592
|
+
printJson(
|
|
1593
|
+
await clientFrom(cmd2).context.queryInstanceSubgraph(knId, jsonArgs(opts.args)),
|
|
1594
|
+
outputOptions(cmd2)
|
|
1595
|
+
);
|
|
1596
|
+
});
|
|
1597
|
+
cmd.command("get-logic-properties <kn-id>").description("Layer-3: compute logic-property values for instances").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
|
|
1598
|
+
printJson(
|
|
1599
|
+
await clientFrom(cmd2).context.logicProperties(knId, jsonArgs(opts.args)),
|
|
1600
|
+
outputOptions(cmd2)
|
|
1601
|
+
);
|
|
1602
|
+
});
|
|
1603
|
+
cmd.command("get-action-info <kn-id>").description("Layer-3: fetch action info / dynamic tools for an instance").requiredOption("--args <json>", "tool arguments as JSON").action(async (knId, opts, cmd2) => {
|
|
1604
|
+
printJson(
|
|
1605
|
+
await clientFrom(cmd2).context.actionInfo(knId, jsonArgs(opts.args)),
|
|
1606
|
+
outputOptions(cmd2)
|
|
1607
|
+
);
|
|
1608
|
+
});
|
|
1609
|
+
return group(cmd, "AI DATA PLATFORM");
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
// src/commands/dataflow.ts
|
|
1613
|
+
import { Command as Command8 } from "commander";
|
|
1614
|
+
var int5 = (v) => Number.parseInt(v, 10);
|
|
1615
|
+
function dataflowCommand() {
|
|
1616
|
+
const cmd = new Command8("dataflow").description("Dataflow document workflows \u2014 list, runs, logs");
|
|
1617
|
+
cmd.command("list").description("List all dataflows").action(async (_opts, cmd2) => {
|
|
1618
|
+
printJson(await clientFrom(cmd2).dataflows.list(), outputOptions(cmd2));
|
|
1619
|
+
});
|
|
1620
|
+
cmd.command("runs <dagId>").description("List run records for one dataflow").option("--since <date>", "filter runs since a date").action(async (dagId, opts, cmd2) => {
|
|
1621
|
+
printJson(
|
|
1622
|
+
await clientFrom(cmd2).dataflows.runs(dagId, { since: opts.since }),
|
|
1623
|
+
outputOptions(cmd2)
|
|
1624
|
+
);
|
|
1625
|
+
});
|
|
1626
|
+
cmd.command("logs <dagId> <instanceId>").description("Show logs for one run").option("--page <n>", "page", int5, 0).option("--limit <n>", "page size", int5, DEFAULT_LIST_LIMIT).action(async (dagId, instanceId, opts, cmd2) => {
|
|
1627
|
+
printJson(
|
|
1628
|
+
await clientFrom(cmd2).dataflows.logs(dagId, instanceId, {
|
|
1629
|
+
page: opts.page,
|
|
1630
|
+
limit: opts.limit
|
|
1631
|
+
}),
|
|
1632
|
+
outputOptions(cmd2)
|
|
1633
|
+
);
|
|
1634
|
+
});
|
|
1635
|
+
cmd.command("run <dagId>").description("Trigger a dataflow run from a remote file URL").requiredOption("--url <url>", "remote file URL").requiredOption("--name <name>", "file name").action(async (dagId, opts, cmd2) => {
|
|
1636
|
+
printJson(
|
|
1637
|
+
await clientFrom(cmd2).dataflows.run(dagId, opts.url, opts.name),
|
|
1638
|
+
outputOptions(cmd2)
|
|
1639
|
+
);
|
|
1640
|
+
});
|
|
1641
|
+
cmd.command("create").description("Create a dataflow (DAG) from a full document body (--body / --body-file)").option("--body <json>", "dataflow document JSON").option("--body-file <path>", "read the dataflow document JSON from a file").action(async (opts, cmd2) => {
|
|
1642
|
+
printJson(await clientFrom(cmd2).dataflows.create(readBody(opts)), outputOptions(cmd2));
|
|
1643
|
+
});
|
|
1644
|
+
const parseSet = (pairs) => {
|
|
1645
|
+
const out = {};
|
|
1646
|
+
for (const item of pairs ?? []) {
|
|
1647
|
+
const i = item.indexOf("=");
|
|
1648
|
+
if (i > 0) out[item.slice(0, i)] = item.slice(i + 1);
|
|
1649
|
+
}
|
|
1650
|
+
return out;
|
|
1651
|
+
};
|
|
1652
|
+
cmd.command("templates").description("List available dataset/bkn/dataflow templates").action(async (_opts, cmd2) => {
|
|
1653
|
+
printJson(clientFrom(cmd2).dataflows.templates(), outputOptions(cmd2));
|
|
1654
|
+
});
|
|
1655
|
+
cmd.command("create-dataset").description("Create a dataset from a template (--template <name> --set k=v ...)").requiredOption("--template <name>", "template name").option(
|
|
1656
|
+
"--set <kv...>",
|
|
1657
|
+
"set a template argument (key=value); repeatable",
|
|
1658
|
+
(v, acc) => {
|
|
1659
|
+
acc.push(v);
|
|
1660
|
+
return acc;
|
|
1661
|
+
},
|
|
1662
|
+
[]
|
|
1663
|
+
).action(async (opts, cmd2) => {
|
|
1664
|
+
printJson(
|
|
1665
|
+
await clientFrom(cmd2).dataflows.createDataset(opts.template, parseSet(opts.set)),
|
|
1666
|
+
outputOptions(cmd2)
|
|
1667
|
+
);
|
|
1668
|
+
});
|
|
1669
|
+
cmd.command("create-bkn").description("Create a knowledge network from a template (--template <name> --set k=v ...)").requiredOption("--template <name>", "template name").option(
|
|
1670
|
+
"--set <kv...>",
|
|
1671
|
+
"set a template argument (key=value); repeatable",
|
|
1672
|
+
(v, acc) => {
|
|
1673
|
+
acc.push(v);
|
|
1674
|
+
return acc;
|
|
1675
|
+
},
|
|
1676
|
+
[]
|
|
1677
|
+
).action(async (opts, cmd2) => {
|
|
1678
|
+
printJson(
|
|
1679
|
+
await clientFrom(cmd2).dataflows.createBkn(opts.template, parseSet(opts.set)),
|
|
1680
|
+
outputOptions(cmd2)
|
|
1681
|
+
);
|
|
1682
|
+
});
|
|
1683
|
+
return group(cmd, "AI DATA PLATFORM");
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// src/commands/explore.ts
|
|
1687
|
+
import { createServer as createServer2 } from "http";
|
|
1688
|
+
import { Command as Command9 } from "commander";
|
|
1689
|
+
var int6 = (v) => Number.parseInt(v, 10);
|
|
1690
|
+
var ROUTES = {
|
|
1691
|
+
"GET /api/bkn/meta": (c, q) => c.kn.get(req(q, "knId")),
|
|
1692
|
+
"POST /api/bkn/search": (c, _q, b) => c.kn.search(str(b.knId), str(b.query), {
|
|
1693
|
+
maxConcepts: typeof b.maxConcepts === "number" ? b.maxConcepts : void 0
|
|
1694
|
+
}),
|
|
1695
|
+
"POST /api/bkn/instances": (c, _q, b) => c.kn.objectTypeQuery(str(b.knId), str(b.objectTypeId), b.body ?? {}),
|
|
1696
|
+
"POST /api/bkn/subgraph": (c, _q, b) => c.kn.subgraph(str(b.knId), b.body ?? b),
|
|
1697
|
+
"POST /api/bkn/properties": (c, _q, b) => c.kn.objectTypeProperties(str(b.knId), str(b.objectTypeId)),
|
|
1698
|
+
"GET /api/vega/catalogs": (c) => c.vega.catalogs(),
|
|
1699
|
+
"GET /api/vega/catalog": (c, q) => c.vega.getCatalog(req(q, "catalogId")),
|
|
1700
|
+
"GET /api/vega/catalog-resources": (c, q) => c.vega.catalogResources(req(q, "catalogId"), q.get("category") ?? void 0),
|
|
1701
|
+
"GET /api/vega/connector-types": (c) => c.vega.connectorTypes(),
|
|
1702
|
+
"POST /api/vega/query": (c, _q, b) => c.resource.query(str(b.resourceId), b.options ?? {})
|
|
1703
|
+
};
|
|
1704
|
+
function str(v) {
|
|
1705
|
+
return typeof v === "string" ? v : String(v ?? "");
|
|
1706
|
+
}
|
|
1707
|
+
function req(q, key) {
|
|
1708
|
+
const v = q.get(key);
|
|
1709
|
+
if (!v) throw new Error(`missing query param: ${key}`);
|
|
1710
|
+
return v;
|
|
1711
|
+
}
|
|
1712
|
+
function readBody2(reqMsg) {
|
|
1713
|
+
return new Promise((resolve2, reject) => {
|
|
1714
|
+
let data = "";
|
|
1715
|
+
reqMsg.on("data", (chunk) => {
|
|
1716
|
+
data += chunk;
|
|
1717
|
+
});
|
|
1718
|
+
reqMsg.on("end", () => {
|
|
1719
|
+
if (!data.trim()) return resolve2({});
|
|
1720
|
+
try {
|
|
1721
|
+
resolve2(JSON.parse(data));
|
|
1722
|
+
} catch {
|
|
1723
|
+
reject(new Error("invalid JSON body"));
|
|
1724
|
+
}
|
|
1725
|
+
});
|
|
1726
|
+
reqMsg.on("error", reject);
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
var INDEX = `<!doctype html><meta charset="utf-8"><title>openbkn explore</title>
|
|
1730
|
+
<h1>openbkn explore</h1>
|
|
1731
|
+
<p>Read-only JSON endpoints for bkn + vega:</p>
|
|
1732
|
+
<ul>${Object.keys(ROUTES).map((r) => `<li><code>${r}</code></li>`).join("")}</ul>`;
|
|
1733
|
+
function exploreCommand() {
|
|
1734
|
+
const cmd = new Command9("explore").description(
|
|
1735
|
+
"Start a local web server with read-only bkn + vega JSON endpoints"
|
|
1736
|
+
);
|
|
1737
|
+
cmd.option("--port <n>", "port to listen on", int6, 7777).option("--host <h>", "host to bind", "127.0.0.1").action(async (opts, command) => {
|
|
1738
|
+
const client = clientFrom(command);
|
|
1739
|
+
const server = createServer2((reqMsg, res) => {
|
|
1740
|
+
void handle(client, reqMsg, res);
|
|
1741
|
+
});
|
|
1742
|
+
server.listen(opts.port, opts.host, () => {
|
|
1743
|
+
console.error(`openbkn explore running at http://${opts.host}:${opts.port}/`);
|
|
1744
|
+
console.error("bkn + vega read endpoints only. Press Ctrl+C to stop.");
|
|
1745
|
+
});
|
|
1746
|
+
});
|
|
1747
|
+
return group(cmd, "FOUNDATION");
|
|
1748
|
+
}
|
|
1749
|
+
async function handle(client, reqMsg, res) {
|
|
1750
|
+
const url = new URL(reqMsg.url ?? "/", "http://localhost");
|
|
1751
|
+
const method = reqMsg.method ?? "GET";
|
|
1752
|
+
if (method === "GET" && url.pathname === "/") {
|
|
1753
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
1754
|
+
res.end(INDEX);
|
|
1755
|
+
return;
|
|
1756
|
+
}
|
|
1757
|
+
const handler = ROUTES[`${method} ${url.pathname}`];
|
|
1758
|
+
if (!handler) {
|
|
1759
|
+
res.writeHead(404, { "content-type": "application/json" });
|
|
1760
|
+
res.end(JSON.stringify({ error: "not found" }));
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
try {
|
|
1764
|
+
const body = method === "GET" ? {} : await readBody2(reqMsg);
|
|
1765
|
+
const data = await handler(client, url.searchParams, body);
|
|
1766
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
1767
|
+
res.end(JSON.stringify(data ?? null));
|
|
1768
|
+
} catch (err) {
|
|
1769
|
+
res.writeHead(500, { "content-type": "application/json" });
|
|
1770
|
+
res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
// src/commands/model.ts
|
|
1775
|
+
import { Command as Command10 } from "commander";
|
|
1776
|
+
var int7 = (v) => Number.parseInt(v, 10);
|
|
1777
|
+
function addManagementCommands(parent, kind) {
|
|
1778
|
+
parent.command("add").description("Register a model (definition JSON via --body / --body-file)").option("--body <json>", "model definition JSON").option("--body-file <path>", "read model definition JSON from a file").action(async (opts, cmd) => {
|
|
1779
|
+
printJson(await clientFrom(cmd).models[kind].add(readBody(opts)), outputOptions(cmd));
|
|
1780
|
+
});
|
|
1781
|
+
parent.command("edit").description("Update a model definition (JSON via --body / --body-file)").option("--body <json>", "model definition JSON").option("--body-file <path>", "read model definition JSON from a file").action(async (opts, cmd) => {
|
|
1782
|
+
printJson(await clientFrom(cmd).models[kind].edit(readBody(opts)), outputOptions(cmd));
|
|
1783
|
+
});
|
|
1784
|
+
parent.command("delete <model-ids>").description("Delete model(s) (comma-joined ids)").action(async (ids, _o, cmd) => {
|
|
1785
|
+
printJson(await clientFrom(cmd).models[kind].delete(csv(ids) ?? []), outputOptions(cmd));
|
|
1786
|
+
});
|
|
1787
|
+
parent.command("test").description("Test a model's connectivity / inference (JSON via --body / --body-file)").option("--body <json>", "test request JSON").option("--body-file <path>", "read test request JSON from a file").action(async (opts, cmd) => {
|
|
1788
|
+
printJson(await clientFrom(cmd).models[kind].test(readBody(opts)), outputOptions(cmd));
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
function modelCommand() {
|
|
1792
|
+
const model = new Command10("model").description("Model factory \u2014 LLM / small-model CRUD + chat");
|
|
1793
|
+
const llm = model.command("llm").description("Large language models");
|
|
1794
|
+
llm.command("list").description("List LLM models").option("--name <s>", "filter by name").option("--type <t>", "model type filter").option("--limit <n>", "page size", int7, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int7, 1).action(async (opts, cmd) => {
|
|
1795
|
+
printJson(
|
|
1796
|
+
await clientFrom(cmd).models.llm.list({
|
|
1797
|
+
name: opts.name,
|
|
1798
|
+
modelType: opts.type,
|
|
1799
|
+
limit: opts.limit,
|
|
1800
|
+
page: opts.page
|
|
1801
|
+
}),
|
|
1802
|
+
outputOptions(cmd)
|
|
1803
|
+
);
|
|
1804
|
+
});
|
|
1805
|
+
llm.command("get <modelId>").description("Get an LLM model").action(async (id, _opts, cmd) => {
|
|
1806
|
+
printJson(await clientFrom(cmd).models.llm.get(id), outputOptions(cmd));
|
|
1807
|
+
});
|
|
1808
|
+
llm.command("chat <modelId>").description("OpenAI-compatible chat completion").requiredOption("-m, --message <text>", "user message").option("--stream", "stream the reply token-by-token to stdout").action(async (id, opts, cmd) => {
|
|
1809
|
+
const messages = [{ role: "user", content: opts.message }];
|
|
1810
|
+
if (opts.stream) {
|
|
1811
|
+
await clientFrom(cmd).models.llm.chatStream(id, messages, (t) => process.stdout.write(t));
|
|
1812
|
+
process.stdout.write("\n");
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
printJson(await clientFrom(cmd).models.llm.chat(id, messages), outputOptions(cmd));
|
|
1816
|
+
});
|
|
1817
|
+
addManagementCommands(llm, "llm");
|
|
1818
|
+
const small = model.command("small").description("Small models (embedding / reranker)");
|
|
1819
|
+
small.command("list").description("List small models").option("--name <s>", "filter by name").option("--type <t>", "model type filter").option("--limit <n>", "page size", int7, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int7, 1).action(async (opts, cmd) => {
|
|
1820
|
+
printJson(
|
|
1821
|
+
await clientFrom(cmd).models.small.list({
|
|
1822
|
+
name: opts.name,
|
|
1823
|
+
modelType: opts.type,
|
|
1824
|
+
limit: opts.limit,
|
|
1825
|
+
page: opts.page
|
|
1826
|
+
}),
|
|
1827
|
+
outputOptions(cmd)
|
|
1828
|
+
);
|
|
1829
|
+
});
|
|
1830
|
+
small.command("get <modelId>").description("Get a small model").action(async (id, _opts, cmd) => {
|
|
1831
|
+
printJson(await clientFrom(cmd).models.small.get(id), outputOptions(cmd));
|
|
1832
|
+
});
|
|
1833
|
+
small.command("embeddings <modelId>").description("Compute embeddings").requiredOption("-i, --input <text>", "comma-separated input texts").action(async (id, opts, cmd) => {
|
|
1834
|
+
printJson(
|
|
1835
|
+
await clientFrom(cmd).models.small.embeddings(id, csv(opts.input) ?? []),
|
|
1836
|
+
outputOptions(cmd)
|
|
1837
|
+
);
|
|
1838
|
+
});
|
|
1839
|
+
small.command("rerank <modelId>").description("Rerank documents against a query").requiredOption("-q, --query <text>", "query").requiredOption("-d, --documents <list>", "comma-separated documents").action(async (id, opts, cmd) => {
|
|
1840
|
+
printJson(
|
|
1841
|
+
await clientFrom(cmd).models.small.rerank(id, opts.query, csv(opts.documents) ?? []),
|
|
1842
|
+
outputOptions(cmd)
|
|
1843
|
+
);
|
|
1844
|
+
});
|
|
1845
|
+
addManagementCommands(small, "small");
|
|
1846
|
+
return group(model, "MODELS & SKILLS");
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// src/commands/resource.ts
|
|
1850
|
+
import { Command as Command11 } from "commander";
|
|
1851
|
+
var int8 = (v) => Number.parseInt(v, 10);
|
|
1852
|
+
function resourceCommand() {
|
|
1853
|
+
const cmd = new Command11("resource").alias("res").description("Resources \u2014 list, find, get, query, delete");
|
|
1854
|
+
cmd.command("list").description("List resources under a datasource/catalog").option("--datasource-id <id>", "filter by datasource/catalog id").option("--type <category>", "resource category (table | logicview)").option("--limit <n>", "page size", int8, DEFAULT_LIST_LIMIT).action(async (opts, cmd2) => {
|
|
1855
|
+
const data = await clientFrom(cmd2).resource.list({
|
|
1856
|
+
datasourceId: opts.datasourceId,
|
|
1857
|
+
category: opts.type,
|
|
1858
|
+
limit: opts.limit
|
|
1859
|
+
});
|
|
1860
|
+
printJson(data, outputOptions(cmd2));
|
|
1861
|
+
});
|
|
1862
|
+
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("--datasource-id <id>", "limit to a datasource/catalog").action(async (opts, cmd2) => {
|
|
1863
|
+
const data = await clientFrom(cmd2).resource.find(opts.name, {
|
|
1864
|
+
exact: opts.exact,
|
|
1865
|
+
datasourceId: opts.datasourceId
|
|
1866
|
+
});
|
|
1867
|
+
printJson(data, outputOptions(cmd2));
|
|
1868
|
+
});
|
|
1869
|
+
cmd.command("get <id>").description("Get resource details").action(async (id, _opts, cmd2) => {
|
|
1870
|
+
printJson(await clientFrom(cmd2).resource.get(id), outputOptions(cmd2));
|
|
1871
|
+
});
|
|
1872
|
+
cmd.command("query <id>").description("Fetch data rows from a resource").option("--limit <n>", "row limit", int8, DEFAULT_QUERY_LIMIT).option("--offset <n>", "row offset", int8, 0).option("--need-total", "include total count").action(async (id, opts, cmd2) => {
|
|
1873
|
+
const data = await clientFrom(cmd2).resource.query(id, {
|
|
1874
|
+
limit: opts.limit,
|
|
1875
|
+
offset: opts.offset,
|
|
1876
|
+
needTotal: opts.needTotal
|
|
1877
|
+
});
|
|
1878
|
+
printJson(data, outputOptions(cmd2));
|
|
1879
|
+
});
|
|
1880
|
+
cmd.command("delete <id>").description("Delete a resource").option("-y, --yes", "skip confirmation").action(async (id, _opts, cmd2) => {
|
|
1881
|
+
printJson(await clientFrom(cmd2).resource.delete(id), outputOptions(cmd2));
|
|
1882
|
+
});
|
|
1883
|
+
return group(cmd, "AI DATA PLATFORM");
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
// src/commands/skill.ts
|
|
1887
|
+
import { Command as Command12 } from "commander";
|
|
1888
|
+
var int9 = (v) => Number.parseInt(v, 10);
|
|
1889
|
+
function skillCommand() {
|
|
1890
|
+
const cmd = new Command12("skill").description("Skill registry and market");
|
|
1891
|
+
const listOpts = (c) => c.option("--name <s>", "filter by name").option("--source <s>", "filter by source").option("--status <s>", "filter by status").option("--limit <n>", "page size", int9, DEFAULT_LIST_LIMIT).option("--page <n>", "page", int9, 1);
|
|
1892
|
+
listOpts(cmd.command("list").description("List skills")).option("--create-user <s>", "filter by creator").action(async (opts, cmd2) => {
|
|
1893
|
+
printJson(
|
|
1894
|
+
await clientFrom(cmd2).skills.list({
|
|
1895
|
+
name: opts.name,
|
|
1896
|
+
source: opts.source,
|
|
1897
|
+
status: opts.status,
|
|
1898
|
+
createUser: opts.createUser,
|
|
1899
|
+
pageSize: opts.limit,
|
|
1900
|
+
page: opts.page
|
|
1901
|
+
}),
|
|
1902
|
+
outputOptions(cmd2)
|
|
1903
|
+
);
|
|
1904
|
+
});
|
|
1905
|
+
cmd.command("get <skill-id>").description("Get a skill by id").action(async (id, _opts, cmd2) => {
|
|
1906
|
+
printJson(await clientFrom(cmd2).skills.get(id), outputOptions(cmd2));
|
|
1907
|
+
});
|
|
1908
|
+
listOpts(cmd.command("market").description("Browse the skill market")).action(
|
|
1909
|
+
async (opts, cmd2) => {
|
|
1910
|
+
printJson(
|
|
1911
|
+
await clientFrom(cmd2).skills.market({
|
|
1912
|
+
name: opts.name,
|
|
1913
|
+
source: opts.source,
|
|
1914
|
+
pageSize: opts.limit,
|
|
1915
|
+
page: opts.page
|
|
1916
|
+
}),
|
|
1917
|
+
outputOptions(cmd2)
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1920
|
+
);
|
|
1921
|
+
cmd.command("market-get <skill-id>").description("Get a market skill by id").action(async (id, _opts, cmd2) => {
|
|
1922
|
+
printJson(await clientFrom(cmd2).skills.marketGet(id), outputOptions(cmd2));
|
|
1923
|
+
});
|
|
1924
|
+
cmd.command("delete <skill-id>").description("Delete a skill").action(async (id, _opts, cmd2) => {
|
|
1925
|
+
printJson(await clientFrom(cmd2).skills.delete(id), outputOptions(cmd2));
|
|
1926
|
+
});
|
|
1927
|
+
cmd.command("content <skill-id>").description("Read a skill's SKILL.md content index").action(async (id, _opts, cmd2) => {
|
|
1928
|
+
printJson(await clientFrom(cmd2).skills.content(id), outputOptions(cmd2));
|
|
1929
|
+
});
|
|
1930
|
+
cmd.command("read-file <skill-id> <rel-path>").description("Read a file inside a skill (progressive)").action(async (id, relPath, _opts, cmd2) => {
|
|
1931
|
+
printJson(await clientFrom(cmd2).skills.readFile(id, relPath), outputOptions(cmd2));
|
|
1932
|
+
});
|
|
1933
|
+
cmd.command("history <skill-id>").description("Show a skill's version history").action(async (id, _opts, cmd2) => {
|
|
1934
|
+
printJson(await clientFrom(cmd2).skills.history(id), outputOptions(cmd2));
|
|
1935
|
+
});
|
|
1936
|
+
cmd.command("set-status <skill-id> <status>").description("Change status: unpublish | published | offline").action(async (id, status2, _opts, cmd2) => {
|
|
1937
|
+
printJson(
|
|
1938
|
+
await clientFrom(cmd2).skills.setStatus(id, status2),
|
|
1939
|
+
outputOptions(cmd2)
|
|
1940
|
+
);
|
|
1941
|
+
});
|
|
1942
|
+
cmd.command("register <directory>").description("Zip a local skill directory and register it").option("--source <s>", "source tag").option("--extend-info <json>", "extra metadata as JSON").action(async (dir, opts, cmd2) => {
|
|
1943
|
+
const extendInfo = opts.extendInfo ? JSON.parse(opts.extendInfo) : void 0;
|
|
1944
|
+
printJson(
|
|
1945
|
+
await clientFrom(cmd2).skills.register(dir, { source: opts.source, extendInfo }),
|
|
1946
|
+
outputOptions(cmd2)
|
|
1947
|
+
);
|
|
1948
|
+
});
|
|
1949
|
+
cmd.command("download <skill-id> [out-path]").description("Download a skill archive to a local .zip").action(async (skillId, outPath, _o, cmd2) => {
|
|
1950
|
+
printJson(await clientFrom(cmd2).skills.download(skillId, outPath), outputOptions(cmd2));
|
|
1951
|
+
});
|
|
1952
|
+
cmd.command("install <skill-id> [directory]").description("Download a skill archive and extract it locally").action(async (skillId, dir, _o, cmd2) => {
|
|
1953
|
+
printJson(await clientFrom(cmd2).skills.install(skillId, dir), outputOptions(cmd2));
|
|
1954
|
+
});
|
|
1955
|
+
cmd.command("update-metadata <skill-id>").description("Update a skill's metadata (--body / --body-file JSON)").option("--body <json>", "metadata JSON").option("--body-file <path>", "read metadata JSON from a file").action(async (skillId, opts, cmd2) => {
|
|
1956
|
+
printJson(
|
|
1957
|
+
await clientFrom(cmd2).skills.updateMetadata(skillId, readBody(opts)),
|
|
1958
|
+
outputOptions(cmd2)
|
|
1959
|
+
);
|
|
1960
|
+
});
|
|
1961
|
+
cmd.command("update-package <skill-id> <directory>").description("Replace a skill's package from a local directory").action(async (skillId, dir, _o, cmd2) => {
|
|
1962
|
+
printJson(await clientFrom(cmd2).skills.updatePackage(skillId, dir), outputOptions(cmd2));
|
|
1963
|
+
});
|
|
1964
|
+
cmd.command("republish <skill-id>").description("Republish a previous skill version").requiredOption("--version <v>", "version to republish").action(async (skillId, opts, cmd2) => {
|
|
1965
|
+
printJson(await clientFrom(cmd2).skills.republish(skillId, opts.version), outputOptions(cmd2));
|
|
1966
|
+
});
|
|
1967
|
+
cmd.command("publish-history <skill-id>").description("Publish a historical skill version").requiredOption("--version <v>", "version to publish").action(async (skillId, opts, cmd2) => {
|
|
1968
|
+
printJson(
|
|
1969
|
+
await clientFrom(cmd2).skills.publishHistory(skillId, opts.version),
|
|
1970
|
+
outputOptions(cmd2)
|
|
1971
|
+
);
|
|
1972
|
+
});
|
|
1973
|
+
return group(cmd, "MODELS & SKILLS");
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
// src/commands/toolbox.ts
|
|
1977
|
+
import { Command as Command13 } from "commander";
|
|
1978
|
+
var int10 = (v) => Number.parseInt(v, 10);
|
|
1979
|
+
function toolboxCommand() {
|
|
1980
|
+
const cmd = new Command13("toolbox").description("Agent toolbox lifecycle");
|
|
1981
|
+
cmd.command("list").description("List toolboxes").option("--keyword <s>", "filter by keyword").option("--limit <n>", "page size", int10, DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", int10, 0).action(async (opts, cmd2) => {
|
|
1982
|
+
printJson(
|
|
1983
|
+
await clientFrom(cmd2).toolboxes.list({
|
|
1984
|
+
keyword: opts.keyword,
|
|
1985
|
+
limit: opts.limit,
|
|
1986
|
+
offset: opts.offset
|
|
1987
|
+
}),
|
|
1988
|
+
outputOptions(cmd2)
|
|
1989
|
+
);
|
|
1990
|
+
});
|
|
1991
|
+
cmd.command("create").description("Create a toolbox").requiredOption("--name <name>", "toolbox name").requiredOption("--service-url <url>", "tool service URL").option("--description <d>", "description").action(async (opts, cmd2) => {
|
|
1992
|
+
printJson(
|
|
1993
|
+
await clientFrom(cmd2).toolboxes.create({
|
|
1994
|
+
name: opts.name,
|
|
1995
|
+
serviceUrl: opts.serviceUrl,
|
|
1996
|
+
description: opts.description
|
|
1997
|
+
}),
|
|
1998
|
+
outputOptions(cmd2)
|
|
1999
|
+
);
|
|
2000
|
+
});
|
|
2001
|
+
cmd.command("publish <box-id>").description("Publish a toolbox").action(async (id, _opts, cmd2) => {
|
|
2002
|
+
printJson(await clientFrom(cmd2).toolboxes.publish(id), outputOptions(cmd2));
|
|
2003
|
+
});
|
|
2004
|
+
cmd.command("unpublish <box-id>").description("Unpublish a toolbox (status=draft)").action(async (id, _opts, cmd2) => {
|
|
2005
|
+
printJson(await clientFrom(cmd2).toolboxes.unpublish(id), outputOptions(cmd2));
|
|
2006
|
+
});
|
|
2007
|
+
cmd.command("delete <box-id>").description("Delete a toolbox").option("-y, --yes", "skip confirmation").action(async (id, _opts, cmd2) => {
|
|
2008
|
+
printJson(await clientFrom(cmd2).toolboxes.delete(id), outputOptions(cmd2));
|
|
2009
|
+
});
|
|
2010
|
+
cmd.command("export <box-id>").description("Export a toolbox config to a local .adp file").requiredOption("-o, --out <file>", "output .adp path").option("--type <t>", "impex type: toolbox | mcp | operator", "toolbox").action(async (boxId, opts, cmd2) => {
|
|
2011
|
+
printJson(
|
|
2012
|
+
await clientFrom(cmd2).toolboxes.export(boxId, opts.out, opts.type),
|
|
2013
|
+
outputOptions(cmd2)
|
|
2014
|
+
);
|
|
2015
|
+
});
|
|
2016
|
+
cmd.command("import <file>").description("Import a toolbox config from a local .adp file").option("--type <t>", "impex type: toolbox | mcp | operator", "toolbox").action(async (file, opts, cmd2) => {
|
|
2017
|
+
printJson(await clientFrom(cmd2).toolboxes.import(file, opts.type), outputOptions(cmd2));
|
|
2018
|
+
});
|
|
2019
|
+
return group(cmd, "DECISION AGENT");
|
|
2020
|
+
}
|
|
2021
|
+
function toolCommand() {
|
|
2022
|
+
const cmd = new Command13("tool").description("Tools inside a toolbox");
|
|
2023
|
+
cmd.command("list").description("List tools in a toolbox").requiredOption("--toolbox <box-id>", "toolbox id").action(async (opts, cmd2) => {
|
|
2024
|
+
printJson(await clientFrom(cmd2).toolboxes.tools(opts.toolbox), outputOptions(cmd2));
|
|
2025
|
+
});
|
|
2026
|
+
cmd.command("enable <tool-ids...>").description("Enable one or more tools").requiredOption("--toolbox <box-id>", "toolbox id").action(async (toolIds, opts, cmd2) => {
|
|
2027
|
+
printJson(
|
|
2028
|
+
await clientFrom(cmd2).toolboxes.setToolStatus(opts.toolbox, toolIds, "enabled"),
|
|
2029
|
+
outputOptions(cmd2)
|
|
2030
|
+
);
|
|
2031
|
+
});
|
|
2032
|
+
cmd.command("disable <tool-ids...>").description("Disable one or more tools").requiredOption("--toolbox <box-id>", "toolbox id").action(async (toolIds, opts, cmd2) => {
|
|
2033
|
+
printJson(
|
|
2034
|
+
await clientFrom(cmd2).toolboxes.setToolStatus(opts.toolbox, toolIds, "disabled"),
|
|
2035
|
+
outputOptions(cmd2)
|
|
2036
|
+
);
|
|
2037
|
+
});
|
|
2038
|
+
const invokeOpts = (c) => c.requiredOption("--toolbox <box-id>", "toolbox id").option("--body <json>", "request body JSON").option("--header <json>", "headers map JSON").option("--query <json>", "query params JSON").option("--path <json>", "path params JSON").option("--timeout <s>", "per-call timeout seconds", int10);
|
|
2039
|
+
const parseJson = (s, label) => {
|
|
2040
|
+
if (!s) return void 0;
|
|
2041
|
+
try {
|
|
2042
|
+
return JSON.parse(s);
|
|
2043
|
+
} catch {
|
|
2044
|
+
throw new InputError(`--${label} must be valid JSON`);
|
|
2045
|
+
}
|
|
2046
|
+
};
|
|
2047
|
+
const buildEnvelope = (opts) => ({
|
|
2048
|
+
body: opts.body ? JSON.parse(opts.body) : void 0,
|
|
2049
|
+
header: parseJson(opts.header, "header"),
|
|
2050
|
+
query: parseJson(opts.query, "query"),
|
|
2051
|
+
path: parseJson(opts.path, "path"),
|
|
2052
|
+
timeout: opts.timeout ? Number(opts.timeout) : void 0
|
|
2053
|
+
});
|
|
2054
|
+
invokeOpts(
|
|
2055
|
+
cmd.command("execute <tool-id>").description("Invoke a published+enabled tool")
|
|
2056
|
+
).action(async (toolId, opts, cmd2) => {
|
|
2057
|
+
printJson(
|
|
2058
|
+
await clientFrom(cmd2).toolboxes.execute(opts.toolbox, toolId, buildEnvelope(opts)),
|
|
2059
|
+
outputOptions(cmd2)
|
|
2060
|
+
);
|
|
2061
|
+
});
|
|
2062
|
+
invokeOpts(
|
|
2063
|
+
cmd.command("debug <tool-id>").description("Invoke a tool (draft/disabled too)")
|
|
2064
|
+
).action(async (toolId, opts, cmd2) => {
|
|
2065
|
+
printJson(
|
|
2066
|
+
await clientFrom(cmd2).toolboxes.debug(opts.toolbox, toolId, buildEnvelope(opts)),
|
|
2067
|
+
outputOptions(cmd2)
|
|
2068
|
+
);
|
|
2069
|
+
});
|
|
2070
|
+
cmd.command("upload <file>").description("Upload a tool definition file (OpenAPI spec) into a toolbox").requiredOption("--toolbox <id>", "target toolbox id").option("--metadata-type <t>", "metadata type", "openapi").action(async (file, opts, cmd2) => {
|
|
2071
|
+
printJson(
|
|
2072
|
+
await clientFrom(cmd2).toolboxes.upload(opts.toolbox, file, opts.metadataType),
|
|
2073
|
+
outputOptions(cmd2)
|
|
2074
|
+
);
|
|
2075
|
+
});
|
|
2076
|
+
return group(cmd, "DECISION AGENT");
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
// src/commands/trace.ts
|
|
2080
|
+
import { readFileSync as readFileSync5, writeFileSync } from "fs";
|
|
2081
|
+
import { Command as Command14 } from "commander";
|
|
2082
|
+
|
|
2083
|
+
// src/trace-ai/schema-validate.ts
|
|
2084
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
2085
|
+
import { extname } from "path";
|
|
2086
|
+
import yaml from "js-yaml";
|
|
2087
|
+
import { z } from "zod";
|
|
2088
|
+
var Assertion = z.object({
|
|
2089
|
+
type: z.enum([
|
|
2090
|
+
"contains",
|
|
2091
|
+
"not_contains",
|
|
2092
|
+
"regex",
|
|
2093
|
+
"tool_call_count",
|
|
2094
|
+
"tool_call_order",
|
|
2095
|
+
"latency_ms",
|
|
2096
|
+
"semantic_match"
|
|
2097
|
+
])
|
|
2098
|
+
}).passthrough();
|
|
2099
|
+
var EvalCase = z.object({
|
|
2100
|
+
query_id: z.string().optional(),
|
|
2101
|
+
query: z.string().optional(),
|
|
2102
|
+
input: z.object({ user_message: z.string() }).optional(),
|
|
2103
|
+
reference: z.object({ answer: z.string() }).optional(),
|
|
2104
|
+
assertions: z.array(Assertion).optional(),
|
|
2105
|
+
tags: z.array(z.string()).optional()
|
|
2106
|
+
});
|
|
2107
|
+
var EvalSetFile = z.union([
|
|
2108
|
+
z.array(EvalCase),
|
|
2109
|
+
z.object({ cases: z.array(EvalCase) }),
|
|
2110
|
+
z.object({ queries: z.array(EvalCase) })
|
|
2111
|
+
]);
|
|
2112
|
+
var DiagnosisRule = z.object({
|
|
2113
|
+
id: z.string(),
|
|
2114
|
+
severity: z.enum(["low", "medium", "high"]),
|
|
2115
|
+
symptom: z.string(),
|
|
2116
|
+
predicate: z.string().optional(),
|
|
2117
|
+
rubric: z.unknown().optional(),
|
|
2118
|
+
params: z.record(z.string(), z.unknown()).optional()
|
|
2119
|
+
});
|
|
2120
|
+
function parseFile(file) {
|
|
2121
|
+
const text = readFileSync4(file, "utf8");
|
|
2122
|
+
const ext = extname(file).toLowerCase();
|
|
2123
|
+
if (ext === ".yaml" || ext === ".yml") return yaml.load(text);
|
|
2124
|
+
return JSON.parse(text);
|
|
2125
|
+
}
|
|
2126
|
+
function inferKind(data) {
|
|
2127
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
2128
|
+
const o = data;
|
|
2129
|
+
if ("predicate" in o || "rubric" in o || "symptom" in o && "severity" in o) return "rule";
|
|
2130
|
+
}
|
|
2131
|
+
return "eval-set";
|
|
2132
|
+
}
|
|
2133
|
+
function validateSchemaFile(file, kind) {
|
|
2134
|
+
let data;
|
|
2135
|
+
try {
|
|
2136
|
+
data = parseFile(file);
|
|
2137
|
+
} catch (e) {
|
|
2138
|
+
return {
|
|
2139
|
+
valid: false,
|
|
2140
|
+
kind: kind ?? "eval-set",
|
|
2141
|
+
file,
|
|
2142
|
+
errors: [`parse error: ${e instanceof Error ? e.message : String(e)}`]
|
|
2143
|
+
};
|
|
2144
|
+
}
|
|
2145
|
+
const resolved = kind ?? inferKind(data);
|
|
2146
|
+
const schema = resolved === "rule" ? DiagnosisRule : EvalSetFile;
|
|
2147
|
+
const result = schema.safeParse(data);
|
|
2148
|
+
if (result.success) return { valid: true, kind: resolved, file, errors: [] };
|
|
2149
|
+
return {
|
|
2150
|
+
valid: false,
|
|
2151
|
+
kind: resolved,
|
|
2152
|
+
file,
|
|
2153
|
+
errors: result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`)
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// src/commands/trace.ts
|
|
2158
|
+
function traceCommand() {
|
|
2159
|
+
const cmd = new Command14("trace").description(
|
|
2160
|
+
"Trace AI \u2014 fetch spans, diagnose (symbolic + LLM rubric), scan, eval-set, schema validate"
|
|
2161
|
+
);
|
|
2162
|
+
cmd.command("get <conversation-id>").description("Fetch all trace spans for a conversation").option("--max-spans <n>", "max spans", (v) => Number.parseInt(v, 10)).action(async (conversationId, opts, cmd2) => {
|
|
2163
|
+
printJson(
|
|
2164
|
+
await clientFrom(cmd2).trace.spans(conversationId, { maxSpans: opts.maxSpans }),
|
|
2165
|
+
outputOptions(cmd2)
|
|
2166
|
+
);
|
|
2167
|
+
});
|
|
2168
|
+
cmd.command("search").description("Raw trace search (--body / --body-file OpenSearch JSON)").option("--body <json>", "search body JSON").option("--body-file <path>", "read search body JSON from a file").action(async (opts, cmd2) => {
|
|
2169
|
+
printJson(await clientFrom(cmd2).trace.search(readBody(opts)), outputOptions(cmd2));
|
|
2170
|
+
});
|
|
2171
|
+
cmd.command("diagnose <conversation-id>").description("Diagnose a conversation's trace (symbolic rules; --llm adds rubric judging)").option("--llm", "also run LLM-judged rubric rules via the local `claude` CLI").action(async (conversationId, opts, cmd2) => {
|
|
2172
|
+
const report = await clientFrom(cmd2).trace.diagnose(conversationId, {
|
|
2173
|
+
llm: Boolean(opts.llm)
|
|
2174
|
+
});
|
|
2175
|
+
const out = outputOptions(cmd2);
|
|
2176
|
+
if (out.json) printJson(report, out);
|
|
2177
|
+
else console.log(renderReportMarkdown(report));
|
|
2178
|
+
});
|
|
2179
|
+
cmd.command("scan <conversation-ids>").description("Batch-diagnose several conversations (comma-joined) + aggregate findings").option("--llm", "hybrid mode (rubric + synthesizer) per trace via local `claude`").action(async (ids, opts, cmd2) => {
|
|
2180
|
+
const list = ids.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2181
|
+
printJson(
|
|
2182
|
+
await clientFrom(cmd2).trace.scan(list, { llm: Boolean(opts.llm) }),
|
|
2183
|
+
outputOptions(cmd2)
|
|
2184
|
+
);
|
|
2185
|
+
});
|
|
2186
|
+
const evalSet = cmd.command("eval-set").description("Build + run trace eval sets");
|
|
2187
|
+
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) => {
|
|
2188
|
+
const raw = JSON.parse(readFileSync5(queriesFile, "utf8"));
|
|
2189
|
+
const cases = clientFrom(cmd2).trace.evalSetBuild(raw);
|
|
2190
|
+
if (opts.out) {
|
|
2191
|
+
writeFileSync(opts.out, JSON.stringify({ cases }, null, 2));
|
|
2192
|
+
printJson({ out: opts.out, cases: cases.length }, outputOptions(cmd2));
|
|
2193
|
+
} else {
|
|
2194
|
+
printJson({ cases }, outputOptions(cmd2));
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
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) => {
|
|
2198
|
+
const raw = JSON.parse(readFileSync5(casesFile, "utf8"));
|
|
2199
|
+
const cases = clientFrom(cmd2).trace.evalSetBuild(raw);
|
|
2200
|
+
const result = await clientFrom(cmd2).trace.evalSetTest(opts.agent, cases, {
|
|
2201
|
+
version: opts.version,
|
|
2202
|
+
llm: Boolean(opts.llm)
|
|
2203
|
+
});
|
|
2204
|
+
printJson(result, outputOptions(cmd2));
|
|
2205
|
+
if (result.failed > 0) process.exitCode = 1;
|
|
2206
|
+
});
|
|
2207
|
+
const schema = cmd.command("schema").description("Validate eval-set / diagnosis-rule files");
|
|
2208
|
+
schema.command("validate <file>").description("Validate an eval-set or diagnosis-rule file (JSON/YAML) against its schema").option("--kind <k>", "force schema kind: eval-set | rule (default: auto-detect)").action(async (file, opts, cmd2) => {
|
|
2209
|
+
const result = validateSchemaFile(file, opts.kind);
|
|
2210
|
+
printJson(result, outputOptions(cmd2));
|
|
2211
|
+
if (!result.valid) process.exitCode = 1;
|
|
2212
|
+
});
|
|
2213
|
+
return group(cmd, "TRACE AI");
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
// src/commands/vega.ts
|
|
2217
|
+
import { Command as Command15 } from "commander";
|
|
2218
|
+
function vegaCommand() {
|
|
2219
|
+
const vega = new Command15("vega").description(
|
|
2220
|
+
"Vega observability \u2014 catalog, resources, index build tasks"
|
|
2221
|
+
);
|
|
2222
|
+
const catalog = vega.command("catalog").description("Catalog entries");
|
|
2223
|
+
catalog.command("list").description("List catalog entries").option("--limit <n>", "page size", (v) => Number.parseInt(v, 10), DEFAULT_LIST_LIMIT).option("--offset <n>", "page offset", (v) => Number.parseInt(v, 10), 0).action(async (_opts, cmd) => {
|
|
2224
|
+
const o = cmd.optsWithGlobals();
|
|
2225
|
+
const data = await clientFrom(cmd).vega.catalogs({ limit: o.limit, offset: o.offset });
|
|
2226
|
+
printJson(data, outputOptions(cmd));
|
|
2227
|
+
});
|
|
2228
|
+
catalog.command("get <id>").description("Get a catalog by id").action(async (id, _opts, cmd) => {
|
|
2229
|
+
printJson(await clientFrom(cmd).vega.getCatalog(id), outputOptions(cmd));
|
|
2230
|
+
});
|
|
2231
|
+
catalog.command("resources <id>").description("List resources under a catalog").option("--category <c>", "filter by category (e.g. table)").action(async (id, opts, cmd) => {
|
|
2232
|
+
printJson(await clientFrom(cmd).vega.catalogResources(id, opts.category), outputOptions(cmd));
|
|
2233
|
+
});
|
|
2234
|
+
catalog.command("health <ids...>").description("Health-status for one or more catalogs").action(async (ids, _opts, cmd) => {
|
|
2235
|
+
printJson(await clientFrom(cmd).vega.catalogHealth(ids), outputOptions(cmd));
|
|
2236
|
+
});
|
|
2237
|
+
const connector = vega.command("connector-type").description("Connector types");
|
|
2238
|
+
connector.command("list").description("List connector types").action(async (_opts, cmd) => {
|
|
2239
|
+
printJson(await clientFrom(cmd).vega.connectorTypes(), outputOptions(cmd));
|
|
2240
|
+
});
|
|
2241
|
+
connector.command("get <type>").description("Get a connector type").action(async (type, _opts, cmd) => {
|
|
2242
|
+
printJson(await clientFrom(cmd).vega.connectorType(type), outputOptions(cmd));
|
|
2243
|
+
});
|
|
2244
|
+
const resource = vega.command("resource").description("Vega-backend resources");
|
|
2245
|
+
resource.command("list").description("List resources").option("--datasource-id <id>", "filter by catalog/datasource id").option("--type <category>", "resource category").option("--limit <n>", "page size", (v) => Number.parseInt(v, 10), DEFAULT_LIST_LIMIT).action(async (opts, cmd) => {
|
|
2246
|
+
printJson(
|
|
2247
|
+
await clientFrom(cmd).resource.list({
|
|
2248
|
+
datasourceId: opts.datasourceId,
|
|
2249
|
+
category: opts.type,
|
|
2250
|
+
limit: opts.limit
|
|
2251
|
+
}),
|
|
2252
|
+
outputOptions(cmd)
|
|
2253
|
+
);
|
|
2254
|
+
});
|
|
2255
|
+
resource.command("get <id>").description("Get a resource").action(async (id, _opts, cmd) => {
|
|
2256
|
+
printJson(await clientFrom(cmd).resource.get(id), outputOptions(cmd));
|
|
2257
|
+
});
|
|
2258
|
+
resource.command("query <id>").description("Fetch data rows from a resource").option("--limit <n>", "row limit", (v) => Number.parseInt(v, 10), 50).option("--offset <n>", "row offset", (v) => Number.parseInt(v, 10), 0).action(async (id, opts, cmd) => {
|
|
2259
|
+
printJson(
|
|
2260
|
+
await clientFrom(cmd).resource.query(id, { limit: opts.limit, offset: opts.offset }),
|
|
2261
|
+
outputOptions(cmd)
|
|
2262
|
+
);
|
|
2263
|
+
});
|
|
2264
|
+
const dataset = vega.command("dataset").description("Dataset index build tasks");
|
|
2265
|
+
dataset.command("build <resource-id>").description("Build a resource's index (creates a BuildTask)").requiredOption("--mode <mode>", "build mode: batch | streaming").option("--embedding-fields <list>", "comma-separated fields to vectorize").option(
|
|
2266
|
+
"--build-key-fields <list>",
|
|
2267
|
+
"comma-separated key fields (batch: time; streaming: row id)"
|
|
2268
|
+
).option("--embedding-model <id>", "embedding model id (default if omitted)").option("--model-dimensions <n>", "vector dimensions", (v) => Number.parseInt(v, 10)).option("--wait", "poll until the build reaches a terminal state").option("--timeout <s>", "wait timeout in seconds", (v) => Number.parseInt(v, 10), 300).action(async (resourceId, _opts, cmd) => {
|
|
2269
|
+
const o = cmd.optsWithGlobals();
|
|
2270
|
+
const task = await clientFrom(cmd).vega.build(
|
|
2271
|
+
{
|
|
2272
|
+
resource_id: resourceId,
|
|
2273
|
+
mode: o.mode,
|
|
2274
|
+
embedding_fields: csv(o.embeddingFields),
|
|
2275
|
+
build_key_fields: csv(o.buildKeyFields),
|
|
2276
|
+
embedding_model: o.embeddingModel,
|
|
2277
|
+
model_dimensions: o.modelDimensions
|
|
2278
|
+
},
|
|
2279
|
+
{ wait: Boolean(o.wait), timeoutMs: o.timeout * 1e3 }
|
|
2280
|
+
);
|
|
2281
|
+
printJson(task, outputOptions(cmd));
|
|
2282
|
+
});
|
|
2283
|
+
dataset.command("build-status <resource-id> <task-id>").description("Show a BuildTask's state and progress").action(async (_resourceId, taskId, _opts, cmd) => {
|
|
2284
|
+
const task = await clientFrom(cmd).vega.buildStatus(taskId);
|
|
2285
|
+
printJson(task, outputOptions(cmd));
|
|
2286
|
+
});
|
|
2287
|
+
return group(vega, "AI DATA PLATFORM");
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
// src/cli.ts
|
|
2291
|
+
var program = new Command16();
|
|
2292
|
+
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();
|
|
2293
|
+
program.addCommand(authCommand());
|
|
2294
|
+
program.addCommand(callCommand());
|
|
2295
|
+
program.addCommand(configCommand());
|
|
2296
|
+
program.addCommand(vegaCommand());
|
|
2297
|
+
program.addCommand(bknCommand());
|
|
2298
|
+
program.addCommand(resourceCommand());
|
|
2299
|
+
program.addCommand(dataflowCommand());
|
|
2300
|
+
program.addCommand(contextCommand());
|
|
2301
|
+
program.addCommand(agentCommand());
|
|
2302
|
+
program.addCommand(modelCommand());
|
|
2303
|
+
program.addCommand(skillCommand());
|
|
2304
|
+
program.addCommand(toolboxCommand());
|
|
2305
|
+
program.addCommand(toolCommand());
|
|
2306
|
+
program.addCommand(traceCommand());
|
|
2307
|
+
program.addCommand(adminCommand());
|
|
2308
|
+
program.addCommand(exploreCommand());
|
|
2309
|
+
installGroupedHelp(program);
|
|
2310
|
+
var argv = process.argv.map((a) => a === "-bd" ? "--biz-domain" : a);
|
|
2311
|
+
try {
|
|
2312
|
+
await program.parseAsync(argv);
|
|
2313
|
+
} catch (err) {
|
|
2314
|
+
console.error(formatError(err));
|
|
2315
|
+
process.exit(toExitCode(err));
|
|
2316
|
+
}
|
|
2317
|
+
//# sourceMappingURL=cli.js.map
|