@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
|
@@ -0,0 +1,4805 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/types.ts
|
|
8
|
+
var DEFAULT_BUSINESS_DOMAIN = "bd_public";
|
|
9
|
+
var DEFAULT_LIST_LIMIT = 30;
|
|
10
|
+
var DEFAULT_QUERY_LIMIT = 50;
|
|
11
|
+
|
|
12
|
+
// src/utils/errors.ts
|
|
13
|
+
var HttpError = class extends Error {
|
|
14
|
+
status;
|
|
15
|
+
statusText;
|
|
16
|
+
body;
|
|
17
|
+
constructor(status2, statusText, body) {
|
|
18
|
+
super(`HTTP ${status2} ${statusText}`);
|
|
19
|
+
this.name = "HttpError";
|
|
20
|
+
this.status = status2;
|
|
21
|
+
this.statusText = statusText;
|
|
22
|
+
this.body = body;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var InputError = class extends Error {
|
|
26
|
+
constructor(message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = "InputError";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
function toExitCode(err) {
|
|
32
|
+
if (err instanceof InputError) return 2;
|
|
33
|
+
if (err instanceof HttpError) {
|
|
34
|
+
if (err.status === 401 || err.status === 403) return 3;
|
|
35
|
+
return 1;
|
|
36
|
+
}
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
function formatError(err) {
|
|
40
|
+
if (err instanceof HttpError) {
|
|
41
|
+
if (err.status === 401 || err.status === 403) {
|
|
42
|
+
return `Not authorized (HTTP ${err.status}). Run \`openbkn auth login\` and retry.`;
|
|
43
|
+
}
|
|
44
|
+
const detail = err.body ? `: ${truncate(err.body, 500)}` : "";
|
|
45
|
+
return `Request failed (HTTP ${err.status} ${err.statusText})${detail}`;
|
|
46
|
+
}
|
|
47
|
+
if (err instanceof Error) return err.message;
|
|
48
|
+
return String(err);
|
|
49
|
+
}
|
|
50
|
+
function truncate(s, n) {
|
|
51
|
+
return s.length > n ? `${s.slice(0, n)}\u2026` : s;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/config/store.ts
|
|
55
|
+
import {
|
|
56
|
+
chmodSync,
|
|
57
|
+
existsSync,
|
|
58
|
+
mkdirSync,
|
|
59
|
+
readFileSync,
|
|
60
|
+
readdirSync,
|
|
61
|
+
rmSync,
|
|
62
|
+
writeFileSync
|
|
63
|
+
} from "fs";
|
|
64
|
+
import { homedir } from "os";
|
|
65
|
+
import { join } from "path";
|
|
66
|
+
|
|
67
|
+
// src/auth/jwt.ts
|
|
68
|
+
function decodeJwt(token) {
|
|
69
|
+
const parts = token.split(".");
|
|
70
|
+
if (parts.length < 2 || !parts[1]) return void 0;
|
|
71
|
+
try {
|
|
72
|
+
const json = Buffer.from(parts[1], "base64url").toString("utf8");
|
|
73
|
+
return JSON.parse(json);
|
|
74
|
+
} catch {
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function isExpired(claims, nowMs = Date.now()) {
|
|
79
|
+
return claims?.exp !== void 0 && claims.exp * 1e3 < nowMs;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/config/store.ts
|
|
83
|
+
var PROFILE_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
84
|
+
var IS_WIN = process.platform === "win32";
|
|
85
|
+
function configDir() {
|
|
86
|
+
return process.env.BKN_CONFIG_DIR ?? join(homedir(), ".bkn");
|
|
87
|
+
}
|
|
88
|
+
function profileName() {
|
|
89
|
+
const raw = process.env.BKN_PROFILE?.trim();
|
|
90
|
+
if (!raw) return null;
|
|
91
|
+
if (!PROFILE_RE.test(raw)) {
|
|
92
|
+
throw new Error(`BKN_PROFILE='${raw}' is invalid. Use 1-64 chars from [A-Za-z0-9_-].`);
|
|
93
|
+
}
|
|
94
|
+
return raw;
|
|
95
|
+
}
|
|
96
|
+
function statePath() {
|
|
97
|
+
const profile = profileName();
|
|
98
|
+
return profile ? join(configDir(), "profiles", profile, "state.json") : join(configDir(), "state.json");
|
|
99
|
+
}
|
|
100
|
+
function encodeKey(baseUrl) {
|
|
101
|
+
return Buffer.from(baseUrl, "utf8").toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
102
|
+
}
|
|
103
|
+
function platformDir(baseUrl) {
|
|
104
|
+
return join(configDir(), "platforms", encodeKey(baseUrl));
|
|
105
|
+
}
|
|
106
|
+
function userDir(baseUrl, userId) {
|
|
107
|
+
return join(platformDir(baseUrl), "users", userId);
|
|
108
|
+
}
|
|
109
|
+
function userIdFromToken(token) {
|
|
110
|
+
return decodeJwt(token.idToken ?? "")?.sub ?? decodeJwt(token.accessToken)?.sub ?? "default";
|
|
111
|
+
}
|
|
112
|
+
function readState() {
|
|
113
|
+
return readJson(statePath()) ?? {};
|
|
114
|
+
}
|
|
115
|
+
function writeState(state) {
|
|
116
|
+
writeJson(statePath(), state);
|
|
117
|
+
}
|
|
118
|
+
function activePlatform() {
|
|
119
|
+
return readState().currentPlatform;
|
|
120
|
+
}
|
|
121
|
+
function setActivePlatform(baseUrl) {
|
|
122
|
+
writeState({ ...readState(), currentPlatform: baseUrl });
|
|
123
|
+
}
|
|
124
|
+
function activeUserId(baseUrl) {
|
|
125
|
+
return readState().activeUsers?.[baseUrl];
|
|
126
|
+
}
|
|
127
|
+
function setActiveUser(baseUrl, userId) {
|
|
128
|
+
const state = readState();
|
|
129
|
+
writeState({ ...state, activeUsers: { ...state.activeUsers, [baseUrl]: userId } });
|
|
130
|
+
}
|
|
131
|
+
function readToken(baseUrl, userId = activeUserId(baseUrl)) {
|
|
132
|
+
if (!userId) return void 0;
|
|
133
|
+
return readJson(join(userDir(baseUrl, userId), "token.json")) ?? void 0;
|
|
134
|
+
}
|
|
135
|
+
function writeToken(baseUrl, token) {
|
|
136
|
+
const userId = userIdFromToken(token);
|
|
137
|
+
writeJson(join(userDir(baseUrl, userId), "token.json"), token, 384);
|
|
138
|
+
setActiveUser(baseUrl, userId);
|
|
139
|
+
return userId;
|
|
140
|
+
}
|
|
141
|
+
function deleteToken(baseUrl, userId = activeUserId(baseUrl)) {
|
|
142
|
+
if (!userId) return false;
|
|
143
|
+
const path = join(userDir(baseUrl, userId), "token.json");
|
|
144
|
+
if (!existsSync(path)) return false;
|
|
145
|
+
rmSync(path, { force: true });
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
function readPlatformConfig(baseUrl, userId = activeUserId(baseUrl)) {
|
|
149
|
+
if (!userId) return {};
|
|
150
|
+
return readJson(join(userDir(baseUrl, userId), "config.json")) ?? {};
|
|
151
|
+
}
|
|
152
|
+
function writePlatformConfig(baseUrl, config) {
|
|
153
|
+
const userId = activeUserId(baseUrl) ?? "default";
|
|
154
|
+
writeJson(join(userDir(baseUrl, userId), "config.json"), config);
|
|
155
|
+
}
|
|
156
|
+
function listPlatforms() {
|
|
157
|
+
const root = join(configDir(), "platforms");
|
|
158
|
+
if (!existsSync(root)) return [];
|
|
159
|
+
const state = readState();
|
|
160
|
+
const out = [];
|
|
161
|
+
for (const key of readdirSync(root)) {
|
|
162
|
+
const baseUrl = decodeKey(key);
|
|
163
|
+
if (!baseUrl) continue;
|
|
164
|
+
const usersRoot = join(root, key, "users");
|
|
165
|
+
if (!existsSync(usersRoot)) continue;
|
|
166
|
+
const users = [];
|
|
167
|
+
for (const userId of readdirSync(usersRoot)) {
|
|
168
|
+
const token = readJson(join(usersRoot, userId, "token.json"));
|
|
169
|
+
if (token) users.push({ userId, username: token.username, displayName: token.displayName });
|
|
170
|
+
}
|
|
171
|
+
if (users.length > 0) {
|
|
172
|
+
out.push({ baseUrl, users, activeUserId: state.activeUsers?.[baseUrl] });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
function decodeKey(key) {
|
|
178
|
+
try {
|
|
179
|
+
const b64 = key.replace(/-/g, "+").replace(/_/g, "/");
|
|
180
|
+
return Buffer.from(b64, "base64").toString("utf8");
|
|
181
|
+
} catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function readJson(path) {
|
|
186
|
+
if (!existsSync(path)) return void 0;
|
|
187
|
+
try {
|
|
188
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
189
|
+
} catch {
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function writeJson(path, value, mode = 384) {
|
|
194
|
+
const dir = path.slice(0, path.lastIndexOf("/")) || ".";
|
|
195
|
+
mkdirSync(dir, { recursive: true, ...IS_WIN ? {} : { mode: 448 } });
|
|
196
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
197
|
+
`, IS_WIN ? {} : { mode });
|
|
198
|
+
if (!IS_WIN) chmodSync(path, mode);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/config/resolve.ts
|
|
202
|
+
function resolveContext(opts = {}) {
|
|
203
|
+
const baseUrl = opts.baseUrl ?? process.env.BKN_BASE_URL ?? activePlatform();
|
|
204
|
+
if (!baseUrl) {
|
|
205
|
+
throw new InputError(
|
|
206
|
+
"No base URL. Pass --base-url, set BKN_BASE_URL, or run `openbkn auth login`."
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
const normalized = baseUrl.replace(/\/+$/, "");
|
|
210
|
+
const stored = readToken(normalized);
|
|
211
|
+
const token = opts.token ?? process.env.BKN_TOKEN ?? stored?.accessToken;
|
|
212
|
+
if (!token) {
|
|
213
|
+
throw new InputError("No access token. Set BKN_TOKEN or run `openbkn auth login`.");
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
baseUrl: normalized,
|
|
217
|
+
token,
|
|
218
|
+
businessDomain: opts.businessDomain ?? readPlatformConfig(normalized).businessDomain ?? DEFAULT_BUSINESS_DOMAIN,
|
|
219
|
+
insecure: opts.insecure ?? stored?.tlsInsecure ?? false
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// src/api/headers.ts
|
|
224
|
+
function buildHeaders(ctx, extra) {
|
|
225
|
+
return {
|
|
226
|
+
authorization: `Bearer ${ctx.token}`,
|
|
227
|
+
token: ctx.token,
|
|
228
|
+
"x-business-domain": ctx.businessDomain,
|
|
229
|
+
...extra
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// src/api/tls.ts
|
|
234
|
+
function applyTls(ctx) {
|
|
235
|
+
if (ctx.insecure && process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0") {
|
|
236
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/api/http.ts
|
|
241
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
242
|
+
async function request(ctx, path, init = {}) {
|
|
243
|
+
const url = new URL(path.startsWith("http") ? path : `${ctx.baseUrl}${path}`);
|
|
244
|
+
for (const [k, v] of Object.entries(init.query ?? {})) {
|
|
245
|
+
if (v !== void 0) url.searchParams.set(k, String(v));
|
|
246
|
+
}
|
|
247
|
+
applyTls(ctx);
|
|
248
|
+
const hasBody = init.body !== void 0;
|
|
249
|
+
const controller = new AbortController();
|
|
250
|
+
const timer = setTimeout(() => controller.abort(), init.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
251
|
+
try {
|
|
252
|
+
const res = await fetch(url, {
|
|
253
|
+
method: init.method ?? (hasBody ? "POST" : "GET"),
|
|
254
|
+
headers: buildHeaders(ctx, {
|
|
255
|
+
...hasBody ? { "content-type": "application/json" } : {},
|
|
256
|
+
...init.headers
|
|
257
|
+
}),
|
|
258
|
+
body: hasBody ? JSON.stringify(init.body) : void 0,
|
|
259
|
+
signal: controller.signal
|
|
260
|
+
});
|
|
261
|
+
const text = await res.text();
|
|
262
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
263
|
+
return text ? JSON.parse(text) : void 0;
|
|
264
|
+
} finally {
|
|
265
|
+
clearTimeout(timer);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/api/eacp-crypto.ts
|
|
270
|
+
import {
|
|
271
|
+
constants,
|
|
272
|
+
createPrivateKey,
|
|
273
|
+
createPublicKey,
|
|
274
|
+
publicEncrypt
|
|
275
|
+
} from "crypto";
|
|
276
|
+
var EACP_MODIFYPWD_PRIVATE_KEY_PEM = `-----BEGIN RSA PRIVATE KEY-----
|
|
277
|
+
MIICXgIBAAKBgQDB2fhLla9rMx+6LWTXajnK11Kdp520s1Q+TfPfIXI/7G9+L2YC
|
|
278
|
+
4RA3M5rgRi32s5+UFQ/CVqUFqMqVuzaZ4lw/uEdk1qHcP0g6LB3E9wkl2FclFR0M
|
|
279
|
+
+/HrWmxPoON+0y/tFQxxfNgsUodFzbdh0XY1rIVUIbPLvufUBbLKXHDPpwIDAQAB
|
|
280
|
+
AoGBALCM/H6ajXFs1nCR903aCVicUzoS9qckzI0SIhIOPCfMBp8+PAJTSJl9/ohU
|
|
281
|
+
YnhVj/kmVXwBvboxyJAmOcxdRPWL7iTk5nA1oiVXMer3Wby+tRg/ls91xQbJLVv3
|
|
282
|
+
oGSt7q0CXxJpRH2oYkVVlMMlZUwKz3ovHiLKAnhw+jEsdL2BAkEA9hA97yyeA2eq
|
|
283
|
+
f9dMu/ici99R3WJRRtk4NEI4WShtWPyziDg48d3SOzYmhEJjPuOo3g1ze01os70P
|
|
284
|
+
ApE7d0qcyQJBAMmt+FR8h5MwxPQPAzjh/fTuTttvUfBeMiUDrIycK1I/L96lH+fU
|
|
285
|
+
i4Nu+7TPOzExnPeGO5UJbZxrpIEUB7Zs8O8CQQCLzTCTGiNwxc5eMgH77kVrRudp
|
|
286
|
+
Q7nv6ex/7Hu9VDXEUFbkdyULbj9KuvppPJrMmWZROw04qgNp02mayM8jeLXZAkEA
|
|
287
|
+
o+PM/pMn9TPXiWE9xBbaMhUKXgXLd2KEq1GeAbHS/oY8l1hmYhV1vjwNLbSNrH9d
|
|
288
|
+
yEP73TQJL+jFiONHFTbYXwJAU03Xgum5mLIkX/02LpOrz2QCdfX1IMJk2iKi9osV
|
|
289
|
+
KqfbvHsF0+GvFGg18/FXStG9Kr4TjqLsygQJT76/MnMluw==
|
|
290
|
+
-----END RSA PRIVATE KEY-----`;
|
|
291
|
+
var cachedKey;
|
|
292
|
+
function publicKey() {
|
|
293
|
+
if (!cachedKey) cachedKey = createPublicKey(createPrivateKey(EACP_MODIFYPWD_PRIVATE_KEY_PEM));
|
|
294
|
+
return cachedKey;
|
|
295
|
+
}
|
|
296
|
+
function encryptModifyPwd(plain) {
|
|
297
|
+
const buf = publicEncrypt(
|
|
298
|
+
{ key: publicKey(), padding: constants.RSA_PKCS1_PADDING },
|
|
299
|
+
Buffer.from(plain, "utf8")
|
|
300
|
+
);
|
|
301
|
+
return buf.toString("base64");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/api/admin.ts
|
|
305
|
+
var UM = "/api/user-management/v1";
|
|
306
|
+
var AUTHZ = "/api/authorization/v1";
|
|
307
|
+
var ISFWEB = "/isfweb/api/ShareMgnt";
|
|
308
|
+
async function callerUserId(ctx) {
|
|
309
|
+
const sub = decodeJwt(ctx.token)?.sub;
|
|
310
|
+
if (sub) return sub;
|
|
311
|
+
const info = await request(ctx, "/api/eacp/v1/user/get");
|
|
312
|
+
if (info?.userid) return info.userid;
|
|
313
|
+
throw new Error(
|
|
314
|
+
"Cannot resolve the caller user id (token has no JWT `sub` and eacp/v1/user/get returned no `userid`). Re-login."
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
async function shareMgnt(ctx, method, params = []) {
|
|
318
|
+
try {
|
|
319
|
+
return await request(ctx, `${ISFWEB}/${method}`, { method: "POST", body: params });
|
|
320
|
+
} catch (e) {
|
|
321
|
+
if (e instanceof HttpError) {
|
|
322
|
+
try {
|
|
323
|
+
const parsed = JSON.parse(e.body);
|
|
324
|
+
const err = parsed?.error;
|
|
325
|
+
if (err?.errMsg) {
|
|
326
|
+
const m = err.errID !== void 0 ? `${err.errMsg} (errID=${err.errID})` : err.errMsg;
|
|
327
|
+
throw new Error(`ShareMgnt.${method} failed: ${m}`);
|
|
328
|
+
}
|
|
329
|
+
} catch (inner) {
|
|
330
|
+
if (inner instanceof Error && inner.message.startsWith("ShareMgnt.")) throw inner;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
throw e;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
var DEPT_FIELDS = "name,code,remark,manager,enabled,parent_deps,email";
|
|
337
|
+
var USER_FIELDS = "name,account,email,enabled,frozen,parent_deps,roles";
|
|
338
|
+
function listDepartments(ctx, opts = {}) {
|
|
339
|
+
return request(ctx, `${UM}/console/search-departments/${DEPT_FIELDS}`, {
|
|
340
|
+
query: {
|
|
341
|
+
role: opts.role ?? "super_admin",
|
|
342
|
+
offset: opts.offset ?? 0,
|
|
343
|
+
limit: opts.limit ?? 100,
|
|
344
|
+
name: opts.name || void 0
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
async function listAllDepartments(ctx, role = "super_admin", pageSize = 100) {
|
|
349
|
+
const out = [];
|
|
350
|
+
let offset = 0;
|
|
351
|
+
for (; ; ) {
|
|
352
|
+
const data = await listDepartments(ctx, { role, offset, limit: pageSize });
|
|
353
|
+
const entries = data.entries ?? [];
|
|
354
|
+
out.push(...entries);
|
|
355
|
+
if (entries.length < pageSize) break;
|
|
356
|
+
if (data.total_count !== void 0 && out.length >= data.total_count) break;
|
|
357
|
+
offset += pageSize;
|
|
358
|
+
}
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
function listUsers(ctx, opts = {}) {
|
|
362
|
+
return request(ctx, `${UM}/console/search-users/${USER_FIELDS}`, {
|
|
363
|
+
query: {
|
|
364
|
+
role: opts.role ?? "super_admin",
|
|
365
|
+
offset: opts.offset ?? 0,
|
|
366
|
+
limit: opts.limit ?? 100,
|
|
367
|
+
department_id: opts.orgId || void 0,
|
|
368
|
+
name: opts.name || void 0
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
function listRoles(ctx, opts = {}) {
|
|
373
|
+
return request(ctx, `${AUTHZ}/roles`, {
|
|
374
|
+
query: {
|
|
375
|
+
offset: opts.offset ?? 0,
|
|
376
|
+
limit: opts.limit ?? 100,
|
|
377
|
+
keyword: opts.keyword || void 0
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
function getRole(ctx, roleId) {
|
|
382
|
+
return request(ctx, `${AUTHZ}/roles/${encodeURIComponent(roleId)}`);
|
|
383
|
+
}
|
|
384
|
+
function getUser(ctx, userId) {
|
|
385
|
+
return shareMgnt(ctx, "Usrm_GetUserInfo", [userId]);
|
|
386
|
+
}
|
|
387
|
+
async function getUserRoles(ctx, userId) {
|
|
388
|
+
try {
|
|
389
|
+
return await request(ctx, `${AUTHZ}/accessor_roles`, {
|
|
390
|
+
query: { accessor_id: userId, accessor_type: "user" }
|
|
391
|
+
});
|
|
392
|
+
} catch (e) {
|
|
393
|
+
if (!(e instanceof HttpError) || e.status !== 404) throw e;
|
|
394
|
+
const rolesPage = await listRoles(ctx, { offset: 0, limit: 200 });
|
|
395
|
+
const roles = rolesPage.entries ?? [];
|
|
396
|
+
const matched = [];
|
|
397
|
+
await Promise.all(
|
|
398
|
+
roles.map(async (role) => {
|
|
399
|
+
const members = await listRoleMembers(ctx, role.id, { offset: 0, limit: 500 });
|
|
400
|
+
if ((members.entries ?? []).some((m) => m.id === userId && (!m.type || m.type === "user"))) {
|
|
401
|
+
matched.push(role);
|
|
402
|
+
}
|
|
403
|
+
})
|
|
404
|
+
);
|
|
405
|
+
return {
|
|
406
|
+
entries: matched,
|
|
407
|
+
total_count: matched.length,
|
|
408
|
+
route: "fallback:list-roles+role-members"
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
async function getDepartment(ctx, deptId) {
|
|
413
|
+
try {
|
|
414
|
+
return await shareMgnt(ctx, "Usrm_GetOrgDepartmentById", [deptId]);
|
|
415
|
+
} catch (e) {
|
|
416
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
417
|
+
if (!/部门不存在|errID:?\s*20201|errID=?\s*99|NoneType.+subscriptable/i.test(msg)) throw e;
|
|
418
|
+
return shareMgnt(ctx, "Usrm_GetDepartmentById", [deptId]);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
function getDepartmentMembers(ctx, deptId, opts = {}) {
|
|
422
|
+
return request(ctx, `${UM}/department-members/${encodeURIComponent(deptId)}/users`, {
|
|
423
|
+
query: { role: opts.role ?? "super_admin", offset: opts.offset ?? 0, limit: opts.limit ?? 100 }
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
async function createDepartment(ctx, input) {
|
|
427
|
+
const ncTAddDepartParam = {
|
|
428
|
+
parentId: input.parentId ?? "-1",
|
|
429
|
+
departName: input.name,
|
|
430
|
+
managerID: input.managerID ?? null,
|
|
431
|
+
code: input.code ?? "",
|
|
432
|
+
remark: input.remark ?? "",
|
|
433
|
+
status: input.status ?? 1,
|
|
434
|
+
email: input.email ?? "",
|
|
435
|
+
ossId: ""
|
|
436
|
+
};
|
|
437
|
+
const id = await shareMgnt(ctx, "Usrm_AddDepartment", [{ ncTAddDepartParam }]);
|
|
438
|
+
return { id };
|
|
439
|
+
}
|
|
440
|
+
async function updateDepartment(ctx, deptId, input) {
|
|
441
|
+
const p = { departId: deptId };
|
|
442
|
+
if (input.name !== void 0) p.departName = input.name;
|
|
443
|
+
if (input.managerID !== void 0) p.managerID = input.managerID;
|
|
444
|
+
if (input.code !== void 0) p.code = input.code;
|
|
445
|
+
if (input.remark !== void 0) p.remark = input.remark;
|
|
446
|
+
if (input.status !== void 0) p.status = input.status;
|
|
447
|
+
if (input.email !== void 0) p.email = input.email;
|
|
448
|
+
await shareMgnt(ctx, "Usrm_EditDepartment", [{ ncTEditDepartParam: p }]);
|
|
449
|
+
return { id: deptId, updated: true };
|
|
450
|
+
}
|
|
451
|
+
async function deleteDepartment(ctx, deptId) {
|
|
452
|
+
await request(ctx, `${UM}/management/departments/${encodeURIComponent(deptId)}`, {
|
|
453
|
+
method: "DELETE"
|
|
454
|
+
});
|
|
455
|
+
return { id: deptId, deleted: true };
|
|
456
|
+
}
|
|
457
|
+
async function createUser(ctx, input) {
|
|
458
|
+
const ncTUsrmUserInfo = {
|
|
459
|
+
loginName: input.loginName,
|
|
460
|
+
displayName: input.displayName ?? input.loginName,
|
|
461
|
+
code: input.code ?? "",
|
|
462
|
+
position: input.position ?? "",
|
|
463
|
+
managerID: null,
|
|
464
|
+
managerDisplayName: null,
|
|
465
|
+
remark: input.remark ?? "",
|
|
466
|
+
email: input.email ?? "",
|
|
467
|
+
telNumber: input.telNumber ?? "",
|
|
468
|
+
idcardNumber: "",
|
|
469
|
+
departmentIds: input.departmentIds ?? ["-1"],
|
|
470
|
+
priority: input.priority ?? 999,
|
|
471
|
+
csfLevel2: null,
|
|
472
|
+
pwdControl: false,
|
|
473
|
+
expireTime: -1
|
|
474
|
+
};
|
|
475
|
+
if (input.csfLevel !== void 0) ncTUsrmUserInfo.csfLevel = input.csfLevel;
|
|
476
|
+
const id = await shareMgnt(ctx, "Usrm_AddUser", [
|
|
477
|
+
{ ncTUsrmAddUserInfo: { user: { ncTUsrmUserInfo } } },
|
|
478
|
+
await callerUserId(ctx)
|
|
479
|
+
]);
|
|
480
|
+
return { id };
|
|
481
|
+
}
|
|
482
|
+
async function updateUser(ctx, userId, input) {
|
|
483
|
+
const restBody = {};
|
|
484
|
+
if (input.displayName !== void 0) restBody.display_name = input.displayName;
|
|
485
|
+
if (input.code !== void 0) restBody.code = input.code;
|
|
486
|
+
if (input.position !== void 0) restBody.position = input.position;
|
|
487
|
+
if (input.remark !== void 0) restBody.remark = input.remark;
|
|
488
|
+
if (input.email !== void 0) restBody.email = input.email;
|
|
489
|
+
if (input.telNumber !== void 0) restBody.tel_number = input.telNumber;
|
|
490
|
+
if (input.managerID !== void 0) restBody.manager_id = input.managerID;
|
|
491
|
+
if (input.priority !== void 0) restBody.priority = input.priority;
|
|
492
|
+
if (input.csfLevel !== void 0) restBody.csf_level = input.csfLevel;
|
|
493
|
+
try {
|
|
494
|
+
const r = await request(ctx, `${UM}/management/users/${encodeURIComponent(userId)}`, {
|
|
495
|
+
method: "PATCH",
|
|
496
|
+
body: restBody
|
|
497
|
+
});
|
|
498
|
+
return r ?? { id: userId, updated: true, route: "rest" };
|
|
499
|
+
} catch (e) {
|
|
500
|
+
if (!(e instanceof HttpError) || e.status !== 404 && e.status !== 405) throw e;
|
|
501
|
+
const ncTEditUserParam = {
|
|
502
|
+
id: userId,
|
|
503
|
+
displayName: input.displayName ?? "",
|
|
504
|
+
code: input.code ?? "",
|
|
505
|
+
position: input.position ?? "",
|
|
506
|
+
managerID: input.managerID ?? "",
|
|
507
|
+
remark: input.remark ?? "",
|
|
508
|
+
idcardNumber: null,
|
|
509
|
+
priority: input.priority ?? 999,
|
|
510
|
+
csfLevel: input.csfLevel ?? 5,
|
|
511
|
+
csfLevel2: null,
|
|
512
|
+
email: input.email ?? "",
|
|
513
|
+
telNumber: input.telNumber ?? "",
|
|
514
|
+
expireTime: -1
|
|
515
|
+
};
|
|
516
|
+
await shareMgnt(ctx, "Usrm_EditUser", [{ ncTEditUserParam }, await callerUserId(ctx)]);
|
|
517
|
+
return { id: userId, updated: true, route: "shareMgnt" };
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
async function deleteUser(ctx, userId) {
|
|
521
|
+
try {
|
|
522
|
+
await request(ctx, `${UM}/users/${encodeURIComponent(userId)}`, { method: "DELETE" });
|
|
523
|
+
} catch (e) {
|
|
524
|
+
if (!(e instanceof HttpError) || e.status !== 404) throw e;
|
|
525
|
+
await shareMgnt(ctx, "Usrm_DelUser", [userId]);
|
|
526
|
+
}
|
|
527
|
+
return { id: userId, deleted: true };
|
|
528
|
+
}
|
|
529
|
+
async function setUserPassword(ctx, userId, newPassword) {
|
|
530
|
+
await request(ctx, `${UM}/management/users/${encodeURIComponent(userId)}/password`, {
|
|
531
|
+
method: "PUT",
|
|
532
|
+
body: { password: encryptModifyPwd(newPassword) }
|
|
533
|
+
});
|
|
534
|
+
return { id: userId, passwordReset: true };
|
|
535
|
+
}
|
|
536
|
+
var EACP = "/api/eacp/v1";
|
|
537
|
+
function listAuditLogs(ctx, opts = {}) {
|
|
538
|
+
return request(ctx, `${EACP}/auth1/login-log`, {
|
|
539
|
+
method: "POST",
|
|
540
|
+
body: {
|
|
541
|
+
page_num: opts.page ?? 1,
|
|
542
|
+
page_size: opts.size ?? 30,
|
|
543
|
+
user_name: opts.user || void 0,
|
|
544
|
+
start_time: opts.start || void 0,
|
|
545
|
+
end_time: opts.end || void 0
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
function changePassword(ctx, account, oldPassword, newPassword) {
|
|
550
|
+
return request(ctx, `${EACP}/auth1/modifypassword`, {
|
|
551
|
+
method: "POST",
|
|
552
|
+
body: {
|
|
553
|
+
account,
|
|
554
|
+
oldpwd: encryptModifyPwd(oldPassword),
|
|
555
|
+
newpwd: encryptModifyPwd(newPassword)
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
function listRoleMembers(ctx, roleId, opts = {}) {
|
|
560
|
+
return request(ctx, `${AUTHZ}/role-members/${encodeURIComponent(roleId)}`, {
|
|
561
|
+
query: {
|
|
562
|
+
offset: opts.offset ?? 0,
|
|
563
|
+
limit: opts.limit ?? 100,
|
|
564
|
+
keyword: opts.keyword || void 0
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
function modifyRoleMembers(ctx, roleId, method, members) {
|
|
569
|
+
return request(ctx, `${AUTHZ}/role-members/${encodeURIComponent(roleId)}`, {
|
|
570
|
+
method: "POST",
|
|
571
|
+
body: { method, members }
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// src/utils/org-tree.ts
|
|
576
|
+
function buildOrgTree(entries) {
|
|
577
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
578
|
+
for (const e of entries) nodes.set(e.id, { id: e.id, name: e.name ?? e.id, children: [] });
|
|
579
|
+
const roots = [];
|
|
580
|
+
for (const e of entries) {
|
|
581
|
+
const node = nodes.get(e.id);
|
|
582
|
+
if (!node) continue;
|
|
583
|
+
const deps = e.parent_deps;
|
|
584
|
+
const parentId = deps && deps.length > 0 ? deps[deps.length - 1]?.id : void 0;
|
|
585
|
+
const parent = parentId ? nodes.get(parentId) : void 0;
|
|
586
|
+
if (parent) parent.children.push(node);
|
|
587
|
+
else roots.push(node);
|
|
588
|
+
}
|
|
589
|
+
return roots;
|
|
590
|
+
}
|
|
591
|
+
function renderOrgTree(nodes, prefix = "") {
|
|
592
|
+
const lines = [];
|
|
593
|
+
nodes.forEach((node, i) => {
|
|
594
|
+
const last = i === nodes.length - 1;
|
|
595
|
+
lines.push(`${prefix}${last ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "}${node.name} (id: ${node.id})`);
|
|
596
|
+
if (node.children.length) {
|
|
597
|
+
lines.push(renderOrgTree(node.children, `${prefix}${last ? " " : "\u2502 "}`));
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
return lines.join("\n");
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/resources/admin.ts
|
|
604
|
+
function admin(ctx) {
|
|
605
|
+
return {
|
|
606
|
+
orgList: (opts) => listDepartments(ctx, opts),
|
|
607
|
+
orgGet: (deptId) => getDepartment(ctx, deptId),
|
|
608
|
+
orgMembers: (deptId, opts) => getDepartmentMembers(ctx, deptId, opts),
|
|
609
|
+
orgTree: async (role) => buildOrgTree(await listAllDepartments(ctx, role)),
|
|
610
|
+
orgCreate: (input) => createDepartment(ctx, input),
|
|
611
|
+
orgUpdate: (deptId, input) => updateDepartment(ctx, deptId, input),
|
|
612
|
+
orgDelete: (deptId) => deleteDepartment(ctx, deptId),
|
|
613
|
+
userList: (opts) => listUsers(ctx, opts),
|
|
614
|
+
userGet: (userId) => getUser(ctx, userId),
|
|
615
|
+
userRoles: (userId) => getUserRoles(ctx, userId),
|
|
616
|
+
userCreate: (input) => createUser(ctx, input),
|
|
617
|
+
userUpdate: (userId, input) => updateUser(ctx, userId, input),
|
|
618
|
+
userDelete: (userId) => deleteUser(ctx, userId),
|
|
619
|
+
userResetPassword: (userId, newPassword) => setUserPassword(ctx, userId, newPassword),
|
|
620
|
+
roleList: (opts) => listRoles(ctx, opts),
|
|
621
|
+
roleGet: (roleId) => getRole(ctx, roleId),
|
|
622
|
+
roleMembers: (roleId, opts) => listRoleMembers(ctx, roleId, opts),
|
|
623
|
+
addRoleMember: (roleId, id, type = "user") => modifyRoleMembers(ctx, roleId, "POST", [{ id, type }]),
|
|
624
|
+
removeRoleMember: (roleId, id, type = "user") => modifyRoleMembers(ctx, roleId, "DELETE", [{ id, type }]),
|
|
625
|
+
auditList: (opts) => listAuditLogs(ctx, opts)
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/api/agent-chat.ts
|
|
630
|
+
var FACTORY = "/api/agent-factory";
|
|
631
|
+
async function fetchAgentInfo(ctx, agentId, version = "v0") {
|
|
632
|
+
const data = await request(
|
|
633
|
+
ctx,
|
|
634
|
+
`${FACTORY}/v3/agent-market/agent/${encodeURIComponent(agentId)}/version/${encodeURIComponent(version)}`,
|
|
635
|
+
{ query: { is_visit: true } }
|
|
636
|
+
);
|
|
637
|
+
const id = data.id;
|
|
638
|
+
const key = data.key;
|
|
639
|
+
if (typeof id !== "string" || !id || typeof key !== "string" || !key) {
|
|
640
|
+
throw new Error("Agent info response did not include id and key.");
|
|
641
|
+
}
|
|
642
|
+
return { id, key, version: typeof data.version === "string" ? data.version : version };
|
|
643
|
+
}
|
|
644
|
+
function getByPath(obj, path) {
|
|
645
|
+
let cur = obj;
|
|
646
|
+
for (const k of path) {
|
|
647
|
+
if (cur === null || typeof cur !== "object") return void 0;
|
|
648
|
+
cur = cur[k];
|
|
649
|
+
}
|
|
650
|
+
return cur;
|
|
651
|
+
}
|
|
652
|
+
function setByPath(obj, path, value) {
|
|
653
|
+
let cur = obj;
|
|
654
|
+
for (let i = 0; i < path.length - 1; i += 1) {
|
|
655
|
+
const k = path[i];
|
|
656
|
+
if (!(k in cur) || typeof cur[k] !== "object" || cur[k] === null) cur[k] = {};
|
|
657
|
+
cur = cur[k];
|
|
658
|
+
}
|
|
659
|
+
cur[path[path.length - 1]] = value;
|
|
660
|
+
}
|
|
661
|
+
function applyPatch(data, result) {
|
|
662
|
+
const path = data.key;
|
|
663
|
+
if (!path || path.length === 0) return;
|
|
664
|
+
if (data.action === "upsert" && data.content !== void 0) {
|
|
665
|
+
setByPath(result, path, data.content);
|
|
666
|
+
} else if (data.action === "append") {
|
|
667
|
+
const existing = getByPath(result, path);
|
|
668
|
+
const add = typeof data.content === "string" ? data.content : String(data.content ?? "");
|
|
669
|
+
setByPath(result, path, typeof existing === "string" ? existing + add : add);
|
|
670
|
+
} else if (data.action === "remove" && path.length === 1) {
|
|
671
|
+
delete result[path[0]];
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function extractText(result) {
|
|
675
|
+
const content = getByPath(result, ["message", "content"]);
|
|
676
|
+
if (content) {
|
|
677
|
+
if (typeof content.text === "string" && content.text) return content.text;
|
|
678
|
+
const fa = content.final_answer;
|
|
679
|
+
const ans = fa?.answer;
|
|
680
|
+
if (typeof ans?.text === "string" && ans.text) return ans.text;
|
|
681
|
+
if (typeof fa?.text === "string" && fa.text) return fa.text;
|
|
682
|
+
}
|
|
683
|
+
const msg = result.message;
|
|
684
|
+
if (typeof msg?.text === "string" && msg.text) return msg.text;
|
|
685
|
+
return "";
|
|
686
|
+
}
|
|
687
|
+
async function sendChat(ctx, info, query, opts = {}) {
|
|
688
|
+
applyTls(ctx);
|
|
689
|
+
const body = {
|
|
690
|
+
agent_id: info.id,
|
|
691
|
+
agent_key: info.key,
|
|
692
|
+
agent_version: info.version,
|
|
693
|
+
query,
|
|
694
|
+
stream: Boolean(opts.stream)
|
|
695
|
+
};
|
|
696
|
+
if (opts.conversationId) body.conversation_id = opts.conversationId;
|
|
697
|
+
const res = await fetch(`${ctx.baseUrl}${FACTORY}/v1/app/${info.key}/chat/completion`, {
|
|
698
|
+
method: "POST",
|
|
699
|
+
headers: {
|
|
700
|
+
...buildHeaders(ctx),
|
|
701
|
+
"content-type": "application/json",
|
|
702
|
+
accept: opts.stream ? "text/event-stream" : "application/json",
|
|
703
|
+
"x-language": "zh-CN"
|
|
704
|
+
},
|
|
705
|
+
body: JSON.stringify(body)
|
|
706
|
+
});
|
|
707
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, await res.text());
|
|
708
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
709
|
+
if (opts.stream && contentType.includes("text/event-stream")) {
|
|
710
|
+
return consumeStream(res, opts.onDelta);
|
|
711
|
+
}
|
|
712
|
+
const result = JSON.parse(await res.text());
|
|
713
|
+
return { text: extractText(result), conversationId: conversationIdOf(result) };
|
|
714
|
+
}
|
|
715
|
+
function conversationIdOf(result) {
|
|
716
|
+
const id = result.conversation_id;
|
|
717
|
+
return typeof id === "string" ? id : void 0;
|
|
718
|
+
}
|
|
719
|
+
async function consumeStream(res, onDelta) {
|
|
720
|
+
const reader = res.body?.getReader();
|
|
721
|
+
if (!reader) throw new Error("No response body for stream");
|
|
722
|
+
const decoder = new TextDecoder();
|
|
723
|
+
const result = {};
|
|
724
|
+
let buffer = "";
|
|
725
|
+
let lastText = "";
|
|
726
|
+
let conversationId;
|
|
727
|
+
const handle = (line) => {
|
|
728
|
+
if (!line.startsWith("data:")) return;
|
|
729
|
+
const payload = line.slice(5).trim();
|
|
730
|
+
if (payload === "" || payload === "[DONE]") return;
|
|
731
|
+
let data;
|
|
732
|
+
try {
|
|
733
|
+
data = JSON.parse(payload);
|
|
734
|
+
} catch {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
applyPatch(data, result);
|
|
738
|
+
if (data.key?.length === 1 && data.key[0] === "conversation_id") {
|
|
739
|
+
conversationId = typeof data.content === "string" ? data.content : conversationId;
|
|
740
|
+
}
|
|
741
|
+
const text = extractText(result);
|
|
742
|
+
if (text && text !== lastText) {
|
|
743
|
+
if (onDelta) onDelta(text.slice(lastText.length));
|
|
744
|
+
lastText = text;
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
for (; ; ) {
|
|
748
|
+
const { done, value } = await reader.read();
|
|
749
|
+
if (done) break;
|
|
750
|
+
buffer += decoder.decode(value, { stream: true });
|
|
751
|
+
const lines = buffer.split("\n");
|
|
752
|
+
buffer = lines.pop() ?? "";
|
|
753
|
+
for (const ln of lines) handle(ln.replace(/\r$/, ""));
|
|
754
|
+
}
|
|
755
|
+
if (buffer.trim()) handle(buffer.trim());
|
|
756
|
+
return { text: lastText, conversationId: conversationId ?? conversationIdOf(result) };
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// src/api/agents.ts
|
|
760
|
+
var BASE = "/api/agent-factory/v3";
|
|
761
|
+
function listAgents(ctx, opts = {}) {
|
|
762
|
+
return request(ctx, `${BASE}/published/agent`, {
|
|
763
|
+
method: "POST",
|
|
764
|
+
body: {
|
|
765
|
+
offset: opts.offset ?? 0,
|
|
766
|
+
limit: opts.limit ?? 30,
|
|
767
|
+
category_id: opts.categoryId ?? "",
|
|
768
|
+
name: opts.name ?? "",
|
|
769
|
+
custom_space_id: opts.customSpaceId ?? "",
|
|
770
|
+
is_to_square: opts.isToSquare ?? 1
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
function getAgent(ctx, agentId) {
|
|
775
|
+
return request(ctx, `${BASE}/agent/${encodeURIComponent(agentId)}`);
|
|
776
|
+
}
|
|
777
|
+
function getAgentByKey(ctx, key) {
|
|
778
|
+
return request(ctx, `${BASE}/agent/by-key/${encodeURIComponent(key)}`);
|
|
779
|
+
}
|
|
780
|
+
function listPersonalAgents(ctx, opts = {}) {
|
|
781
|
+
return request(ctx, `${BASE}/personal-space/agent-list`, {
|
|
782
|
+
query: { offset: opts.offset ?? 0, limit: opts.limit ?? 30, name: opts.name || void 0 }
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
function listAgentTemplates(ctx, opts = {}) {
|
|
786
|
+
return request(ctx, `${BASE}/published/agent-tpl`, {
|
|
787
|
+
query: { offset: opts.offset ?? 0, limit: opts.limit ?? 30, name: opts.name || void 0 }
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
function getAgentTemplate(ctx, templateId) {
|
|
791
|
+
return request(ctx, `${BASE}/published/agent-tpl/${encodeURIComponent(templateId)}`);
|
|
792
|
+
}
|
|
793
|
+
function listAgentCategories(ctx) {
|
|
794
|
+
return request(ctx, `${BASE}/category`);
|
|
795
|
+
}
|
|
796
|
+
function createAgent(ctx, body) {
|
|
797
|
+
return request(ctx, `${BASE}/agent`, { method: "POST", body });
|
|
798
|
+
}
|
|
799
|
+
function updateAgent(ctx, agentId, body) {
|
|
800
|
+
return request(ctx, `${BASE}/agent/${encodeURIComponent(agentId)}`, { method: "PUT", body });
|
|
801
|
+
}
|
|
802
|
+
function deleteAgent(ctx, agentId) {
|
|
803
|
+
return request(ctx, `${BASE}/agent/${encodeURIComponent(agentId)}`, { method: "DELETE" });
|
|
804
|
+
}
|
|
805
|
+
function publishAgent(ctx, agentId) {
|
|
806
|
+
return request(ctx, `${BASE}/agent/${encodeURIComponent(agentId)}/publish`, { method: "POST" });
|
|
807
|
+
}
|
|
808
|
+
function unpublishAgent(ctx, agentId) {
|
|
809
|
+
return request(ctx, `${BASE}/agent/${encodeURIComponent(agentId)}/unpublish`, { method: "PUT" });
|
|
810
|
+
}
|
|
811
|
+
var APP = "/api/agent-factory/v1/app";
|
|
812
|
+
function listConversations(ctx, agentKey, opts = {}) {
|
|
813
|
+
return request(ctx, `${APP}/${encodeURIComponent(agentKey)}/conversation`, {
|
|
814
|
+
query: { page: opts.page ?? 1, size: opts.size ?? 30 }
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
function listMessages(ctx, agentKey, conversationId) {
|
|
818
|
+
return request(
|
|
819
|
+
ctx,
|
|
820
|
+
`${APP}/${encodeURIComponent(agentKey)}/conversation/${encodeURIComponent(conversationId)}`
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// src/resources/agents.ts
|
|
825
|
+
function skillMembers(agent) {
|
|
826
|
+
const config = agent.config ?? {};
|
|
827
|
+
const skills2 = config.skills ?? {};
|
|
828
|
+
return Array.isArray(skills2.skills) ? skills2.skills : [];
|
|
829
|
+
}
|
|
830
|
+
function setSkillMembers(agent, members) {
|
|
831
|
+
if (!agent.config || typeof agent.config !== "object") agent.config = {};
|
|
832
|
+
const config = agent.config;
|
|
833
|
+
if (!config.skills || typeof config.skills !== "object") config.skills = {};
|
|
834
|
+
config.skills.skills = members;
|
|
835
|
+
}
|
|
836
|
+
function agents(ctx) {
|
|
837
|
+
return {
|
|
838
|
+
list: (opts) => listAgents(ctx, opts),
|
|
839
|
+
get: (agentId) => getAgent(ctx, agentId),
|
|
840
|
+
getByKey: (key) => getAgentByKey(ctx, key),
|
|
841
|
+
personalList: (opts) => listPersonalAgents(ctx, opts),
|
|
842
|
+
templateList: (opts) => listAgentTemplates(ctx, opts),
|
|
843
|
+
templateGet: (templateId) => getAgentTemplate(ctx, templateId),
|
|
844
|
+
categoryList: () => listAgentCategories(ctx),
|
|
845
|
+
create: (body) => createAgent(ctx, body),
|
|
846
|
+
update: (agentId, body) => updateAgent(ctx, agentId, body),
|
|
847
|
+
delete: (agentId) => deleteAgent(ctx, agentId),
|
|
848
|
+
publish: (agentId) => publishAgent(ctx, agentId),
|
|
849
|
+
unpublish: (agentId) => unpublishAgent(ctx, agentId),
|
|
850
|
+
sessions: (agentKey, opts) => listConversations(ctx, agentKey, opts),
|
|
851
|
+
history: (agentKey, conversationId) => listMessages(ctx, agentKey, conversationId),
|
|
852
|
+
/** List skill ids attached to an agent (config.skills.skills). */
|
|
853
|
+
skillList: async (agentId) => {
|
|
854
|
+
const agent = await getAgent(ctx, agentId);
|
|
855
|
+
const arr = skillMembers(agent);
|
|
856
|
+
return arr.map((m) => String(m.skill_id ?? "")).filter(Boolean);
|
|
857
|
+
},
|
|
858
|
+
/** Attach skill(s) to an agent (dedup), then persist. */
|
|
859
|
+
skillAdd: async (agentId, skillIds) => {
|
|
860
|
+
const agent = await getAgent(ctx, agentId);
|
|
861
|
+
const arr = skillMembers(agent);
|
|
862
|
+
const have = new Set(arr.map((m) => String(m.skill_id ?? "")));
|
|
863
|
+
for (const id of skillIds) if (!have.has(id)) arr.push({ skill_id: id });
|
|
864
|
+
setSkillMembers(agent, arr);
|
|
865
|
+
return updateAgent(ctx, agentId, agent);
|
|
866
|
+
},
|
|
867
|
+
/** Detach skill(s) from an agent, then persist. */
|
|
868
|
+
skillRemove: async (agentId, skillIds) => {
|
|
869
|
+
const agent = await getAgent(ctx, agentId);
|
|
870
|
+
const drop = new Set(skillIds);
|
|
871
|
+
const arr = skillMembers(agent).filter(
|
|
872
|
+
(m) => !drop.has(String(m.skill_id ?? ""))
|
|
873
|
+
);
|
|
874
|
+
setSkillMembers(agent, arr);
|
|
875
|
+
return updateAgent(ctx, agentId, agent);
|
|
876
|
+
},
|
|
877
|
+
/** Send a chat turn to an agent (resolves agent id/key/version first). */
|
|
878
|
+
chat: async (agentId, query, opts = {}) => {
|
|
879
|
+
const info = await fetchAgentInfo(ctx, agentId, opts.version ?? "v0");
|
|
880
|
+
return sendChat(ctx, info, query, opts);
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// src/api/context-loader.ts
|
|
886
|
+
var MCP_PATH = "/api/agent-retrieval/v1/mcp";
|
|
887
|
+
var PROTOCOL = "2024-11-05";
|
|
888
|
+
var SESSION_TTL_MS = 3e5;
|
|
889
|
+
var sessions = /* @__PURE__ */ new Map();
|
|
890
|
+
var rpcId = 0;
|
|
891
|
+
function nextId() {
|
|
892
|
+
rpcId += 1;
|
|
893
|
+
return rpcId;
|
|
894
|
+
}
|
|
895
|
+
function mcpUrl(ctx) {
|
|
896
|
+
return `${ctx.baseUrl}${MCP_PATH}`;
|
|
897
|
+
}
|
|
898
|
+
function headers(ctx, knId, sessionId) {
|
|
899
|
+
const h = {
|
|
900
|
+
"content-type": "application/json",
|
|
901
|
+
accept: "application/json, text/event-stream",
|
|
902
|
+
"x-kn-id": knId,
|
|
903
|
+
"mcp-protocol-version": PROTOCOL,
|
|
904
|
+
authorization: `Bearer ${ctx.token}`
|
|
905
|
+
};
|
|
906
|
+
if (sessionId) h["mcp-session-id"] = sessionId;
|
|
907
|
+
return h;
|
|
908
|
+
}
|
|
909
|
+
function parseBody(text) {
|
|
910
|
+
try {
|
|
911
|
+
return JSON.parse(text);
|
|
912
|
+
} catch {
|
|
913
|
+
const data = text.split("\n").filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("");
|
|
914
|
+
if (data) return JSON.parse(data);
|
|
915
|
+
throw new Error(`Context-loader returned invalid JSON: ${text.slice(0, 200)}`);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
async function post(ctx, knId, sessionId, body) {
|
|
919
|
+
applyTls(ctx);
|
|
920
|
+
const res = await fetch(mcpUrl(ctx), {
|
|
921
|
+
method: "POST",
|
|
922
|
+
headers: headers(ctx, knId, sessionId),
|
|
923
|
+
body: JSON.stringify(body)
|
|
924
|
+
});
|
|
925
|
+
const text = await res.text();
|
|
926
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
927
|
+
return { res, text };
|
|
928
|
+
}
|
|
929
|
+
async function ensureSession(ctx, knId) {
|
|
930
|
+
const key = `${mcpUrl(ctx)}:${knId}`;
|
|
931
|
+
const cached = sessions.get(key);
|
|
932
|
+
if (cached && Date.now() - cached.at < SESSION_TTL_MS) return cached.id;
|
|
933
|
+
sessions.delete(key);
|
|
934
|
+
const { res } = await post(ctx, knId, void 0, {
|
|
935
|
+
jsonrpc: "2.0",
|
|
936
|
+
id: 1,
|
|
937
|
+
method: "initialize",
|
|
938
|
+
params: {
|
|
939
|
+
protocolVersion: PROTOCOL,
|
|
940
|
+
capabilities: {},
|
|
941
|
+
clientInfo: { name: "openbkn", version: "0.1.0" }
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
const sessionId = res.headers.get("mcp-session-id") ?? res.headers.get("MCP-Session-Id");
|
|
945
|
+
if (!sessionId) throw new Error("MCP server did not return a session id.");
|
|
946
|
+
await post(ctx, knId, sessionId, { jsonrpc: "2.0", method: "notifications/initialized" });
|
|
947
|
+
sessions.set(key, { id: sessionId, at: Date.now() });
|
|
948
|
+
return sessionId;
|
|
949
|
+
}
|
|
950
|
+
function unwrap(parsed) {
|
|
951
|
+
const rpc = parsed;
|
|
952
|
+
if (rpc.error) throw new Error(`Context-loader error: ${rpc.error.message}`);
|
|
953
|
+
const result = rpc.result;
|
|
954
|
+
if (result === void 0) return parsed;
|
|
955
|
+
const content = result.content;
|
|
956
|
+
if (Array.isArray(content) && content[0] && typeof content[0].text === "string") {
|
|
957
|
+
try {
|
|
958
|
+
return JSON.parse(content[0].text);
|
|
959
|
+
} catch {
|
|
960
|
+
return { raw: content[0].text };
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
return result;
|
|
964
|
+
}
|
|
965
|
+
async function callTool(ctx, knId, name, args) {
|
|
966
|
+
const sessionId = await ensureSession(ctx, knId);
|
|
967
|
+
const { text } = await post(ctx, knId, sessionId, {
|
|
968
|
+
jsonrpc: "2.0",
|
|
969
|
+
method: "tools/call",
|
|
970
|
+
params: { name, arguments: args },
|
|
971
|
+
id: nextId()
|
|
972
|
+
});
|
|
973
|
+
return unwrap(parseBody(text));
|
|
974
|
+
}
|
|
975
|
+
async function callMethod(ctx, knId, method, params = {}) {
|
|
976
|
+
const sessionId = await ensureSession(ctx, knId);
|
|
977
|
+
const { text } = await post(ctx, knId, sessionId, {
|
|
978
|
+
jsonrpc: "2.0",
|
|
979
|
+
method,
|
|
980
|
+
params: Object.keys(params).length > 0 ? params : void 0,
|
|
981
|
+
id: nextId()
|
|
982
|
+
});
|
|
983
|
+
const parsed = parseBody(text);
|
|
984
|
+
if (parsed.error) throw new Error(`Context-loader error: ${parsed.error.message}`);
|
|
985
|
+
return parsed.result;
|
|
986
|
+
}
|
|
987
|
+
function searchSchema(ctx, knId, query, opts = {}) {
|
|
988
|
+
const args = { query, response_format: "json" };
|
|
989
|
+
if (opts.searchScope) args.search_scope = opts.searchScope;
|
|
990
|
+
if (opts.maxConcepts !== void 0) args.max_concepts = opts.maxConcepts;
|
|
991
|
+
return callTool(ctx, knId, "search_schema", args);
|
|
992
|
+
}
|
|
993
|
+
function queryObjectInstance(ctx, knId, args) {
|
|
994
|
+
return callTool(ctx, knId, "query_object_instance", args);
|
|
995
|
+
}
|
|
996
|
+
function findSkills(ctx, knId, objectTypeId, topK) {
|
|
997
|
+
const args = { object_type_id: objectTypeId };
|
|
998
|
+
if (topK !== void 0) args.top_k = topK;
|
|
999
|
+
return callTool(ctx, knId, "find_skills", args);
|
|
1000
|
+
}
|
|
1001
|
+
function listTools(ctx, knId) {
|
|
1002
|
+
return callMethod(ctx, knId, "tools/list");
|
|
1003
|
+
}
|
|
1004
|
+
function queryInstanceSubgraph(ctx, knId, args) {
|
|
1005
|
+
return callTool(ctx, knId, "query_instance_subgraph", args);
|
|
1006
|
+
}
|
|
1007
|
+
function getLogicProperties(ctx, knId, args) {
|
|
1008
|
+
return callTool(ctx, knId, "get_logic_properties_values", args);
|
|
1009
|
+
}
|
|
1010
|
+
function getActionInfo(ctx, knId, args) {
|
|
1011
|
+
return callTool(ctx, knId, "get_action_info", args);
|
|
1012
|
+
}
|
|
1013
|
+
function listResources(ctx, knId) {
|
|
1014
|
+
return callMethod(ctx, knId, "resources/list");
|
|
1015
|
+
}
|
|
1016
|
+
function readResource(ctx, knId, uri) {
|
|
1017
|
+
return callMethod(ctx, knId, "resources/read", { uri });
|
|
1018
|
+
}
|
|
1019
|
+
function listResourceTemplates(ctx, knId) {
|
|
1020
|
+
return callMethod(ctx, knId, "resources/templates/list");
|
|
1021
|
+
}
|
|
1022
|
+
function listPrompts(ctx, knId) {
|
|
1023
|
+
return callMethod(ctx, knId, "prompts/list");
|
|
1024
|
+
}
|
|
1025
|
+
function getPrompt(ctx, knId, name, args = {}) {
|
|
1026
|
+
return callMethod(ctx, knId, "prompts/get", { name, arguments: args });
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
// src/resources/context-loader.ts
|
|
1030
|
+
function context(ctx) {
|
|
1031
|
+
return {
|
|
1032
|
+
searchSchema: (knId, query, opts) => searchSchema(ctx, knId, query, opts),
|
|
1033
|
+
queryObjectInstance: (knId, args) => queryObjectInstance(ctx, knId, args),
|
|
1034
|
+
findSkills: (knId, objectTypeId, topK) => findSkills(ctx, knId, objectTypeId, topK),
|
|
1035
|
+
tools: (knId) => listTools(ctx, knId),
|
|
1036
|
+
toolCall: (knId, name, args) => callTool(ctx, knId, name, args),
|
|
1037
|
+
queryInstanceSubgraph: (knId, args) => queryInstanceSubgraph(ctx, knId, args),
|
|
1038
|
+
logicProperties: (knId, args) => getLogicProperties(ctx, knId, args),
|
|
1039
|
+
actionInfo: (knId, args) => getActionInfo(ctx, knId, args),
|
|
1040
|
+
resources: (knId) => listResources(ctx, knId),
|
|
1041
|
+
resource: (knId, uri) => readResource(ctx, knId, uri),
|
|
1042
|
+
templates: (knId) => listResourceTemplates(ctx, knId),
|
|
1043
|
+
prompts: (knId) => listPrompts(ctx, knId),
|
|
1044
|
+
prompt: (knId, name, args) => getPrompt(ctx, knId, name, args)
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// src/api/dataflow.ts
|
|
1049
|
+
var BASE2 = "/api/automation/v2";
|
|
1050
|
+
var BASE_V1 = "/api/automation/v1";
|
|
1051
|
+
function listDataflows(ctx) {
|
|
1052
|
+
return request(ctx, `${BASE2}/dags`, { query: { type: "data-flow", page: 0, limit: -1 } });
|
|
1053
|
+
}
|
|
1054
|
+
function createDataflow(ctx, body) {
|
|
1055
|
+
return request(ctx, `${BASE_V1}/data-flow/flow`, { method: "POST", body });
|
|
1056
|
+
}
|
|
1057
|
+
function runDataflow(ctx, dagId) {
|
|
1058
|
+
return request(ctx, `${BASE_V1}/run-instance/${encodeURIComponent(dagId)}`, {
|
|
1059
|
+
method: "POST",
|
|
1060
|
+
body: {}
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
function deleteDataflow(ctx, dagId) {
|
|
1064
|
+
return request(ctx, `${BASE_V1}/data-flow/flow/${encodeURIComponent(dagId)}`, {
|
|
1065
|
+
method: "DELETE"
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
async function executeDataflow(ctx, body, opts = {}) {
|
|
1069
|
+
const created = await createDataflow(ctx, body);
|
|
1070
|
+
const dagId = String(created.id ?? "");
|
|
1071
|
+
if (!dagId) throw new Error("createDataflow returned no DAG id");
|
|
1072
|
+
try {
|
|
1073
|
+
await runDataflow(ctx, dagId);
|
|
1074
|
+
const interval = opts.intervalMs ?? 2e3;
|
|
1075
|
+
const deadline = opts.timeoutMs ?? 3e5;
|
|
1076
|
+
let waited = 0;
|
|
1077
|
+
for (; ; ) {
|
|
1078
|
+
const res = await request(ctx, `${BASE_V1}/dag/${encodeURIComponent(dagId)}/results`);
|
|
1079
|
+
const status2 = res.results?.[0]?.status;
|
|
1080
|
+
if (status2 === "success" || status2 === "completed") return { dagId, status: status2 };
|
|
1081
|
+
if (status2 === "failed" || status2 === "error") throw new Error(`Dataflow run ${status2}`);
|
|
1082
|
+
if (waited >= deadline) throw new Error(`Dataflow run timed out after ${deadline}ms`);
|
|
1083
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
1084
|
+
waited += interval;
|
|
1085
|
+
}
|
|
1086
|
+
} finally {
|
|
1087
|
+
await deleteDataflow(ctx, dagId).catch(() => {
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
function listDataflowRuns(ctx, dagId, opts = {}) {
|
|
1092
|
+
return request(ctx, `${BASE2}/dag/${encodeURIComponent(dagId)}/results`, {
|
|
1093
|
+
query: { since: opts.since || void 0 }
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
function runDataflowRemote(ctx, dagId, url, name) {
|
|
1097
|
+
return request(ctx, `${BASE2}/dataflow-doc/trigger/${encodeURIComponent(dagId)}`, {
|
|
1098
|
+
method: "POST",
|
|
1099
|
+
body: { source_from: "remote", url, name }
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
function getDataflowLogs(ctx, dagId, instanceId, opts = {}) {
|
|
1103
|
+
return request(
|
|
1104
|
+
ctx,
|
|
1105
|
+
`${BASE2}/dag/${encodeURIComponent(dagId)}/result/${encodeURIComponent(instanceId)}`,
|
|
1106
|
+
{ query: { page: opts.page ?? 0, limit: opts.limit ?? 30 } }
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// src/api/knowledge-networks.ts
|
|
1111
|
+
var ONTOLOGY_BASE = "/api/ontology-manager/v1/knowledge-networks";
|
|
1112
|
+
var ONTOLOGY_QUERY_BASE = "/api/ontology-query/v1/knowledge-networks";
|
|
1113
|
+
var RETRIEVAL_BASE = "/api/agent-retrieval/v1/kn";
|
|
1114
|
+
function listKnowledgeNetworks(ctx, opts = {}) {
|
|
1115
|
+
return request(ctx, ONTOLOGY_BASE, {
|
|
1116
|
+
query: {
|
|
1117
|
+
offset: opts.offset ?? 0,
|
|
1118
|
+
limit: opts.limit ?? 30,
|
|
1119
|
+
sort: opts.sort ?? "update_time",
|
|
1120
|
+
direction: opts.direction ?? "desc",
|
|
1121
|
+
name_pattern: opts.namePattern || void 0,
|
|
1122
|
+
tag: opts.tag || void 0
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
function getKnowledgeNetwork(ctx, knId, opts = {}) {
|
|
1127
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}`, {
|
|
1128
|
+
query: {
|
|
1129
|
+
mode: opts.exportMode ? "export" : void 0,
|
|
1130
|
+
include_statistics: opts.stats ? "true" : void 0
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
function createKnowledgeNetworkRaw(ctx, body) {
|
|
1135
|
+
return request(ctx, ONTOLOGY_BASE, { method: "POST", body });
|
|
1136
|
+
}
|
|
1137
|
+
function createKnowledgeNetwork(ctx, opts) {
|
|
1138
|
+
return request(ctx, ONTOLOGY_BASE, {
|
|
1139
|
+
method: "POST",
|
|
1140
|
+
body: { name: opts.name, branch: opts.branch ?? "main", base_branch: opts.baseBranch ?? "" }
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
function deleteKnowledgeNetwork(ctx, knId) {
|
|
1144
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}`, { method: "DELETE" });
|
|
1145
|
+
}
|
|
1146
|
+
function updateKnowledgeNetwork(ctx, knId, body) {
|
|
1147
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}`, { method: "PUT", body });
|
|
1148
|
+
}
|
|
1149
|
+
function querySubgraph(ctx, knId, body) {
|
|
1150
|
+
return request(ctx, `${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/subgraph`, {
|
|
1151
|
+
method: "POST",
|
|
1152
|
+
body
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
function listActionLogs(ctx, knId, opts = {}) {
|
|
1156
|
+
return request(ctx, `${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/action-logs`, {
|
|
1157
|
+
query: {
|
|
1158
|
+
action_type_id: opts.actionTypeId || void 0,
|
|
1159
|
+
status: opts.status || void 0,
|
|
1160
|
+
trigger_type: opts.triggerType || void 0,
|
|
1161
|
+
limit: opts.limit ?? 30,
|
|
1162
|
+
need_total: opts.needTotal ? "true" : void 0
|
|
1163
|
+
}
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
function getActionLog(ctx, knId, logId) {
|
|
1167
|
+
return request(
|
|
1168
|
+
ctx,
|
|
1169
|
+
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/action-logs/${encodeURIComponent(logId)}`
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
function cancelActionLog(ctx, knId, logId) {
|
|
1173
|
+
return request(
|
|
1174
|
+
ctx,
|
|
1175
|
+
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/action-logs/${encodeURIComponent(logId)}/cancel`,
|
|
1176
|
+
{ method: "POST" }
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
function queryObjectTypeInstances(ctx, knId, otId, body) {
|
|
1180
|
+
return request(
|
|
1181
|
+
ctx,
|
|
1182
|
+
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/object-types/${encodeURIComponent(otId)}`,
|
|
1183
|
+
{ method: "POST", body }
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
function getObjectTypeProperties(ctx, knId, otId) {
|
|
1187
|
+
return request(
|
|
1188
|
+
ctx,
|
|
1189
|
+
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/object-types/${encodeURIComponent(otId)}/properties`
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
function queryActionType(ctx, knId, atId, body) {
|
|
1193
|
+
return request(
|
|
1194
|
+
ctx,
|
|
1195
|
+
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/action-types/${encodeURIComponent(atId)}/`,
|
|
1196
|
+
{ method: "POST", body }
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
function executeActionType(ctx, knId, atId, body) {
|
|
1200
|
+
return request(
|
|
1201
|
+
ctx,
|
|
1202
|
+
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/action-types/${encodeURIComponent(atId)}/execute`,
|
|
1203
|
+
{ method: "POST", body }
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
function getActionTypeInputs(ctx, knId, atId) {
|
|
1207
|
+
return request(
|
|
1208
|
+
ctx,
|
|
1209
|
+
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/action-types/${encodeURIComponent(atId)}/inputs`
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
function getActionExecution(ctx, knId, executionId) {
|
|
1213
|
+
return request(
|
|
1214
|
+
ctx,
|
|
1215
|
+
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/action-executions/${encodeURIComponent(executionId)}`
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
function queryMetricData(ctx, knId, metricId, body) {
|
|
1219
|
+
return request(
|
|
1220
|
+
ctx,
|
|
1221
|
+
`${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/metrics/${encodeURIComponent(metricId)}/data`,
|
|
1222
|
+
{ method: "POST", body }
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
function dryRunMetric(ctx, knId, body) {
|
|
1226
|
+
return request(ctx, `${ONTOLOGY_QUERY_BASE}/${encodeURIComponent(knId)}/metrics/dry-run`, {
|
|
1227
|
+
method: "POST",
|
|
1228
|
+
body
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
function schemaListQuery(opts) {
|
|
1232
|
+
return { branch: opts.branch ?? "main", limit: String(opts.limit ?? -1) };
|
|
1233
|
+
}
|
|
1234
|
+
function listObjectTypes(ctx, knId, opts = {}) {
|
|
1235
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/object-types`, {
|
|
1236
|
+
query: schemaListQuery(opts)
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
function createObjectTypes(ctx, knId, entries, branch = "main") {
|
|
1240
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/object-types`, {
|
|
1241
|
+
method: "POST",
|
|
1242
|
+
query: { branch },
|
|
1243
|
+
body: { entries }
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
function listRelationTypes(ctx, knId, opts = {}) {
|
|
1247
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/relation-types`, {
|
|
1248
|
+
query: schemaListQuery(opts)
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
function listActionTypes(ctx, knId, opts = {}) {
|
|
1252
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/action-types`, {
|
|
1253
|
+
query: schemaListQuery(opts)
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
function getSchemaItem(ctx, knId, kind, id) {
|
|
1257
|
+
return request(
|
|
1258
|
+
ctx,
|
|
1259
|
+
`${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/${kind}/${encodeURIComponent(id)}`
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
function createSchemaItem(ctx, knId, kind, body) {
|
|
1263
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/${kind}`, {
|
|
1264
|
+
method: "POST",
|
|
1265
|
+
body
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
function updateSchemaItem(ctx, knId, kind, id, body) {
|
|
1269
|
+
return request(
|
|
1270
|
+
ctx,
|
|
1271
|
+
`${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/${kind}/${encodeURIComponent(id)}`,
|
|
1272
|
+
{
|
|
1273
|
+
method: "PUT",
|
|
1274
|
+
body
|
|
1275
|
+
}
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
function deleteSchemaItem(ctx, knId, kind, id) {
|
|
1279
|
+
return request(
|
|
1280
|
+
ctx,
|
|
1281
|
+
`${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/${kind}/${encodeURIComponent(id)}`,
|
|
1282
|
+
{
|
|
1283
|
+
method: "DELETE"
|
|
1284
|
+
}
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
function listMetrics(ctx, knId, opts = {}) {
|
|
1288
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/metrics`, {
|
|
1289
|
+
query: { branch: opts.branch ?? "main", limit: String(opts.limit ?? -1) }
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
function getMetric(ctx, knId, metricId) {
|
|
1293
|
+
return request(
|
|
1294
|
+
ctx,
|
|
1295
|
+
`${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/metrics/${encodeURIComponent(metricId)}`
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
function createMetric(ctx, knId, body) {
|
|
1299
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/metrics`, {
|
|
1300
|
+
method: "POST",
|
|
1301
|
+
body
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
function updateMetric(ctx, knId, metricId, body) {
|
|
1305
|
+
return request(
|
|
1306
|
+
ctx,
|
|
1307
|
+
`${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/metrics/${encodeURIComponent(metricId)}`,
|
|
1308
|
+
{
|
|
1309
|
+
method: "PUT",
|
|
1310
|
+
body
|
|
1311
|
+
}
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
function deleteMetric(ctx, knId, metricId) {
|
|
1315
|
+
return request(
|
|
1316
|
+
ctx,
|
|
1317
|
+
`${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/metrics/${encodeURIComponent(metricId)}`,
|
|
1318
|
+
{
|
|
1319
|
+
method: "DELETE"
|
|
1320
|
+
}
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
function searchMetrics(ctx, knId, body) {
|
|
1324
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/metrics/search`, {
|
|
1325
|
+
method: "POST",
|
|
1326
|
+
body
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
function validateMetric(ctx, knId, body) {
|
|
1330
|
+
return request(ctx, `${ONTOLOGY_BASE}/${encodeURIComponent(knId)}/metrics/validate`, {
|
|
1331
|
+
method: "POST",
|
|
1332
|
+
body
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
function semanticSearch(ctx, knId, query, opts = {}) {
|
|
1336
|
+
return request(ctx, `${RETRIEVAL_BASE}/semantic-search`, {
|
|
1337
|
+
method: "POST",
|
|
1338
|
+
body: {
|
|
1339
|
+
kn_id: knId,
|
|
1340
|
+
query,
|
|
1341
|
+
mode: opts.mode ?? "keyword_vector_retrieval",
|
|
1342
|
+
max_concepts: opts.maxConcepts ?? 10,
|
|
1343
|
+
return_query_understanding: opts.returnQueryUnderstanding ?? false
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// src/api/resources.ts
|
|
1349
|
+
var BASE3 = "/api/vega-backend/v1/resources";
|
|
1350
|
+
function listResources2(ctx, opts = {}) {
|
|
1351
|
+
return request(ctx, BASE3, {
|
|
1352
|
+
query: {
|
|
1353
|
+
catalog_id: opts.datasourceId || void 0,
|
|
1354
|
+
name: opts.name || void 0,
|
|
1355
|
+
category: opts.category || void 0,
|
|
1356
|
+
limit: opts.limit && opts.limit > 0 ? opts.limit : void 0
|
|
1357
|
+
}
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
function getResource(ctx, id) {
|
|
1361
|
+
return request(ctx, `${BASE3}/${encodeURIComponent(id)}`);
|
|
1362
|
+
}
|
|
1363
|
+
function createResourceRaw(ctx, body) {
|
|
1364
|
+
return request(ctx, BASE3, { method: "POST", body });
|
|
1365
|
+
}
|
|
1366
|
+
function createResource(ctx, opts) {
|
|
1367
|
+
const body = {
|
|
1368
|
+
name: opts.name,
|
|
1369
|
+
catalog_id: opts.catalogId,
|
|
1370
|
+
category: "table",
|
|
1371
|
+
source_identifier: opts.sourceIdentifier
|
|
1372
|
+
};
|
|
1373
|
+
if (opts.fields && opts.fields.length > 0) body.schema_definition = opts.fields;
|
|
1374
|
+
return request(ctx, BASE3, { method: "POST", body });
|
|
1375
|
+
}
|
|
1376
|
+
function deleteResource(ctx, id) {
|
|
1377
|
+
return request(ctx, `${BASE3}/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
1378
|
+
}
|
|
1379
|
+
async function findResource(ctx, name, opts = {}) {
|
|
1380
|
+
const result = await listResources2(ctx, { name, datasourceId: opts.datasourceId });
|
|
1381
|
+
const list = Array.isArray(result) ? result : result.entries ?? [];
|
|
1382
|
+
return opts.exact ? list.filter((r) => r.name === name) : list;
|
|
1383
|
+
}
|
|
1384
|
+
function queryResource(ctx, id, opts = {}) {
|
|
1385
|
+
return request(ctx, `${BASE3}/${encodeURIComponent(id)}/data`, {
|
|
1386
|
+
method: "POST",
|
|
1387
|
+
body: {
|
|
1388
|
+
limit: opts.limit ?? 50,
|
|
1389
|
+
offset: opts.offset ?? 0,
|
|
1390
|
+
need_total: opts.needTotal ?? false
|
|
1391
|
+
}
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// src/templates/bkn/document/manifest.json
|
|
1396
|
+
var manifest_default = {
|
|
1397
|
+
name: "document",
|
|
1398
|
+
type: "bkn",
|
|
1399
|
+
description: "\u6587\u6863\u77E5\u8BC6\u7F51\u7EDC",
|
|
1400
|
+
arguments: [
|
|
1401
|
+
{ name: "name", required: true, description: "BKN \u540D\u79F0", type: "string" },
|
|
1402
|
+
{
|
|
1403
|
+
name: "embedding_model_id",
|
|
1404
|
+
required: true,
|
|
1405
|
+
description: "\u5411\u91CF\u5316\u6A21\u578B ID",
|
|
1406
|
+
type: "string"
|
|
1407
|
+
},
|
|
1408
|
+
{
|
|
1409
|
+
name: "content_dataset_id",
|
|
1410
|
+
required: true,
|
|
1411
|
+
description: "\u5185\u5BB9\u6570\u636E\u96C6 ID",
|
|
1412
|
+
type: "string"
|
|
1413
|
+
},
|
|
1414
|
+
{
|
|
1415
|
+
name: "document_dataset_id",
|
|
1416
|
+
required: true,
|
|
1417
|
+
description: "\u6587\u6863\u6570\u636E\u96C6 ID",
|
|
1418
|
+
type: "string"
|
|
1419
|
+
},
|
|
1420
|
+
{
|
|
1421
|
+
name: "element_dataset_id",
|
|
1422
|
+
required: true,
|
|
1423
|
+
description: "\u5143\u7D20\u6570\u636E\u96C6 ID",
|
|
1424
|
+
type: "string"
|
|
1425
|
+
}
|
|
1426
|
+
]
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1429
|
+
// src/templates/bkn/document/template.json
|
|
1430
|
+
var template_default = {
|
|
1431
|
+
branch: "main",
|
|
1432
|
+
business_domain: "bd_public",
|
|
1433
|
+
color: "#0e5fc5",
|
|
1434
|
+
detail: "---\ntype: knowledge_network\nname: {{name}}\ntags: []\nbranch: main\n---\n\n# {{name}}\n\n\n## Network Overview\n\n",
|
|
1435
|
+
icon: "icon-dip-graph",
|
|
1436
|
+
module_type: "knowledge_network",
|
|
1437
|
+
name: "{{name}}",
|
|
1438
|
+
object_types: [
|
|
1439
|
+
{
|
|
1440
|
+
branch: "main",
|
|
1441
|
+
color: "#0e5fc5",
|
|
1442
|
+
comment: "",
|
|
1443
|
+
data_properties: [
|
|
1444
|
+
{
|
|
1445
|
+
comment: "",
|
|
1446
|
+
condition_operations: [
|
|
1447
|
+
"==",
|
|
1448
|
+
"!=",
|
|
1449
|
+
"in",
|
|
1450
|
+
"not_in",
|
|
1451
|
+
">",
|
|
1452
|
+
">=",
|
|
1453
|
+
"<",
|
|
1454
|
+
"<=",
|
|
1455
|
+
"range",
|
|
1456
|
+
"out_range",
|
|
1457
|
+
"regex",
|
|
1458
|
+
"like",
|
|
1459
|
+
"not_like"
|
|
1460
|
+
],
|
|
1461
|
+
display_name: "deduplication_id",
|
|
1462
|
+
index_config: {
|
|
1463
|
+
fulltext_config: {
|
|
1464
|
+
analyzer: "ik_max_word",
|
|
1465
|
+
enabled: true
|
|
1466
|
+
},
|
|
1467
|
+
keyword_config: {
|
|
1468
|
+
enabled: true,
|
|
1469
|
+
ignore_above_len: 1024
|
|
1470
|
+
},
|
|
1471
|
+
vector_config: {
|
|
1472
|
+
enabled: true,
|
|
1473
|
+
model_id: "{{embedding_model_id}}"
|
|
1474
|
+
}
|
|
1475
|
+
},
|
|
1476
|
+
mapped_field: {
|
|
1477
|
+
display_name: "deduplication_id",
|
|
1478
|
+
name: "deduplication_id",
|
|
1479
|
+
type: "string"
|
|
1480
|
+
},
|
|
1481
|
+
name: "deduplication_id",
|
|
1482
|
+
type: "string"
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
comment: "",
|
|
1486
|
+
display_name: "document_id",
|
|
1487
|
+
mapped_field: {
|
|
1488
|
+
display_name: "document_id",
|
|
1489
|
+
name: "document_id",
|
|
1490
|
+
type: "string"
|
|
1491
|
+
},
|
|
1492
|
+
name: "document_id",
|
|
1493
|
+
type: "string"
|
|
1494
|
+
},
|
|
1495
|
+
{
|
|
1496
|
+
comment: "",
|
|
1497
|
+
display_name: "img_path",
|
|
1498
|
+
mapped_field: {
|
|
1499
|
+
display_name: "img_path",
|
|
1500
|
+
name: "img_path",
|
|
1501
|
+
type: "string"
|
|
1502
|
+
},
|
|
1503
|
+
name: "img_path",
|
|
1504
|
+
type: "string"
|
|
1505
|
+
},
|
|
1506
|
+
{
|
|
1507
|
+
comment: "",
|
|
1508
|
+
condition_operations: [
|
|
1509
|
+
"==",
|
|
1510
|
+
"!=",
|
|
1511
|
+
"in",
|
|
1512
|
+
"not_in",
|
|
1513
|
+
">",
|
|
1514
|
+
">=",
|
|
1515
|
+
"<",
|
|
1516
|
+
"<=",
|
|
1517
|
+
"range",
|
|
1518
|
+
"out_range",
|
|
1519
|
+
"regex",
|
|
1520
|
+
"like",
|
|
1521
|
+
"not_like"
|
|
1522
|
+
],
|
|
1523
|
+
display_name: "id",
|
|
1524
|
+
index_config: {
|
|
1525
|
+
fulltext_config: {
|
|
1526
|
+
analyzer: "ik_max_word",
|
|
1527
|
+
enabled: true
|
|
1528
|
+
},
|
|
1529
|
+
keyword_config: {
|
|
1530
|
+
enabled: true,
|
|
1531
|
+
ignore_above_len: 1024
|
|
1532
|
+
},
|
|
1533
|
+
vector_config: {
|
|
1534
|
+
enabled: true,
|
|
1535
|
+
model_id: "{{embedding_model_id}}"
|
|
1536
|
+
}
|
|
1537
|
+
},
|
|
1538
|
+
mapped_field: {
|
|
1539
|
+
display_name: "id",
|
|
1540
|
+
name: "id",
|
|
1541
|
+
type: "string"
|
|
1542
|
+
},
|
|
1543
|
+
name: "id",
|
|
1544
|
+
type: "string"
|
|
1545
|
+
},
|
|
1546
|
+
{
|
|
1547
|
+
comment: "",
|
|
1548
|
+
condition_operations: [
|
|
1549
|
+
"match",
|
|
1550
|
+
"multi_match",
|
|
1551
|
+
"==",
|
|
1552
|
+
"!=",
|
|
1553
|
+
"in",
|
|
1554
|
+
"not_in",
|
|
1555
|
+
">",
|
|
1556
|
+
">=",
|
|
1557
|
+
"<",
|
|
1558
|
+
"<=",
|
|
1559
|
+
"range",
|
|
1560
|
+
"out_range",
|
|
1561
|
+
"regex",
|
|
1562
|
+
"like",
|
|
1563
|
+
"not_like"
|
|
1564
|
+
],
|
|
1565
|
+
display_name: "doc_name",
|
|
1566
|
+
index_config: {
|
|
1567
|
+
fulltext_config: {
|
|
1568
|
+
analyzer: "ik_max_word",
|
|
1569
|
+
enabled: true
|
|
1570
|
+
},
|
|
1571
|
+
keyword_config: {
|
|
1572
|
+
enabled: true,
|
|
1573
|
+
ignore_above_len: 1024
|
|
1574
|
+
},
|
|
1575
|
+
vector_config: {
|
|
1576
|
+
enabled: true,
|
|
1577
|
+
model_id: "{{embedding_model_id}}"
|
|
1578
|
+
}
|
|
1579
|
+
},
|
|
1580
|
+
mapped_field: {
|
|
1581
|
+
display_name: "doc_name",
|
|
1582
|
+
name: "doc_name",
|
|
1583
|
+
type: "text"
|
|
1584
|
+
},
|
|
1585
|
+
name: "doc_name",
|
|
1586
|
+
type: "text"
|
|
1587
|
+
},
|
|
1588
|
+
{
|
|
1589
|
+
comment: "",
|
|
1590
|
+
display_name: "pages",
|
|
1591
|
+
name: "pages",
|
|
1592
|
+
type: "text"
|
|
1593
|
+
},
|
|
1594
|
+
{
|
|
1595
|
+
comment: "",
|
|
1596
|
+
condition_operations: [
|
|
1597
|
+
"match",
|
|
1598
|
+
"multi_match",
|
|
1599
|
+
"==",
|
|
1600
|
+
"!=",
|
|
1601
|
+
"in",
|
|
1602
|
+
"not_in",
|
|
1603
|
+
">",
|
|
1604
|
+
">=",
|
|
1605
|
+
"<",
|
|
1606
|
+
"<=",
|
|
1607
|
+
"range",
|
|
1608
|
+
"out_range",
|
|
1609
|
+
"regex",
|
|
1610
|
+
"like",
|
|
1611
|
+
"not_like"
|
|
1612
|
+
],
|
|
1613
|
+
display_name: "slice_content",
|
|
1614
|
+
index_config: {
|
|
1615
|
+
fulltext_config: {
|
|
1616
|
+
analyzer: "ik_max_word",
|
|
1617
|
+
enabled: true
|
|
1618
|
+
},
|
|
1619
|
+
keyword_config: {
|
|
1620
|
+
enabled: true,
|
|
1621
|
+
ignore_above_len: 1024
|
|
1622
|
+
},
|
|
1623
|
+
vector_config: {
|
|
1624
|
+
enabled: true,
|
|
1625
|
+
model_id: "{{embedding_model_id}}"
|
|
1626
|
+
}
|
|
1627
|
+
},
|
|
1628
|
+
mapped_field: {
|
|
1629
|
+
display_name: "slice_content",
|
|
1630
|
+
name: "slice_content",
|
|
1631
|
+
type: "text"
|
|
1632
|
+
},
|
|
1633
|
+
name: "slice_content",
|
|
1634
|
+
type: "text"
|
|
1635
|
+
},
|
|
1636
|
+
{
|
|
1637
|
+
comment: "",
|
|
1638
|
+
display_name: "segment_id",
|
|
1639
|
+
name: "segment_id",
|
|
1640
|
+
type: "string"
|
|
1641
|
+
},
|
|
1642
|
+
{
|
|
1643
|
+
comment: "",
|
|
1644
|
+
display_name: "updated_at",
|
|
1645
|
+
mapped_field: {
|
|
1646
|
+
display_name: "updated_at",
|
|
1647
|
+
name: "updated_at",
|
|
1648
|
+
type: "datetime"
|
|
1649
|
+
},
|
|
1650
|
+
name: "updated_at",
|
|
1651
|
+
type: "datetime"
|
|
1652
|
+
},
|
|
1653
|
+
{
|
|
1654
|
+
comment: "",
|
|
1655
|
+
display_name: "doc_md5",
|
|
1656
|
+
mapped_field: {
|
|
1657
|
+
display_name: "doc_md5",
|
|
1658
|
+
name: "doc_md5",
|
|
1659
|
+
type: "string"
|
|
1660
|
+
},
|
|
1661
|
+
name: "doc_md5",
|
|
1662
|
+
type: "string"
|
|
1663
|
+
},
|
|
1664
|
+
{
|
|
1665
|
+
comment: "",
|
|
1666
|
+
display_name: "slice_type",
|
|
1667
|
+
mapped_field: {
|
|
1668
|
+
display_name: "slice_type",
|
|
1669
|
+
name: "slice_type",
|
|
1670
|
+
type: "integer"
|
|
1671
|
+
},
|
|
1672
|
+
name: "slice_type",
|
|
1673
|
+
type: "integer"
|
|
1674
|
+
},
|
|
1675
|
+
{
|
|
1676
|
+
comment: "",
|
|
1677
|
+
display_name: "element_ids",
|
|
1678
|
+
name: "element_ids",
|
|
1679
|
+
type: "string"
|
|
1680
|
+
},
|
|
1681
|
+
{
|
|
1682
|
+
comment: "",
|
|
1683
|
+
display_name: "created_at",
|
|
1684
|
+
mapped_field: {
|
|
1685
|
+
display_name: "created_at",
|
|
1686
|
+
name: "created_at",
|
|
1687
|
+
type: "datetime"
|
|
1688
|
+
},
|
|
1689
|
+
name: "created_at",
|
|
1690
|
+
type: "datetime"
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
comment: "",
|
|
1694
|
+
display_name: "element_id",
|
|
1695
|
+
name: "element_id",
|
|
1696
|
+
type: "string"
|
|
1697
|
+
},
|
|
1698
|
+
{
|
|
1699
|
+
comment: "",
|
|
1700
|
+
display_name: "slice_md5",
|
|
1701
|
+
mapped_field: {
|
|
1702
|
+
display_name: "slice_md5",
|
|
1703
|
+
name: "slice_md5",
|
|
1704
|
+
type: "string"
|
|
1705
|
+
},
|
|
1706
|
+
name: "slice_md5",
|
|
1707
|
+
type: "string"
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
comment: "",
|
|
1711
|
+
display_name: "text_vector",
|
|
1712
|
+
mapped_field: {
|
|
1713
|
+
display_name: "text_vector",
|
|
1714
|
+
name: "text_vector",
|
|
1715
|
+
type: "vector"
|
|
1716
|
+
},
|
|
1717
|
+
name: "text_vector",
|
|
1718
|
+
type: "vector"
|
|
1719
|
+
},
|
|
1720
|
+
{
|
|
1721
|
+
comment: "",
|
|
1722
|
+
display_name: "image_vector",
|
|
1723
|
+
mapped_field: {
|
|
1724
|
+
display_name: "image_vector",
|
|
1725
|
+
name: "image_vector",
|
|
1726
|
+
type: "vector"
|
|
1727
|
+
},
|
|
1728
|
+
name: "image_vector",
|
|
1729
|
+
type: "vector"
|
|
1730
|
+
}
|
|
1731
|
+
],
|
|
1732
|
+
data_source: {
|
|
1733
|
+
id: "{{content_dataset_id}}",
|
|
1734
|
+
type: "resource"
|
|
1735
|
+
},
|
|
1736
|
+
display_key: "id",
|
|
1737
|
+
icon: "icon-tianshenpi",
|
|
1738
|
+
id: "d6sco6s2mlgikcusgpo0",
|
|
1739
|
+
incremental_key: "",
|
|
1740
|
+
kn_id: "d6scm5c2mlgikcusgpmg",
|
|
1741
|
+
module_type: "object_type",
|
|
1742
|
+
name: "\u6587\u6863\u7279\u5F81\u5BF9\u8C61",
|
|
1743
|
+
primary_keys: ["id"],
|
|
1744
|
+
tags: []
|
|
1745
|
+
},
|
|
1746
|
+
{
|
|
1747
|
+
branch: "main",
|
|
1748
|
+
color: "#0e5fc5",
|
|
1749
|
+
comment: "",
|
|
1750
|
+
data_properties: [
|
|
1751
|
+
{
|
|
1752
|
+
comment: "",
|
|
1753
|
+
condition_operations: [
|
|
1754
|
+
"match",
|
|
1755
|
+
"multi_match",
|
|
1756
|
+
"==",
|
|
1757
|
+
"!=",
|
|
1758
|
+
"in",
|
|
1759
|
+
"not_in",
|
|
1760
|
+
">",
|
|
1761
|
+
">=",
|
|
1762
|
+
"<",
|
|
1763
|
+
"<=",
|
|
1764
|
+
"range",
|
|
1765
|
+
"out_range",
|
|
1766
|
+
"regex",
|
|
1767
|
+
"like",
|
|
1768
|
+
"not_like"
|
|
1769
|
+
],
|
|
1770
|
+
display_name: "doc_name",
|
|
1771
|
+
index_config: {
|
|
1772
|
+
fulltext_config: {
|
|
1773
|
+
analyzer: "ik_max_word",
|
|
1774
|
+
enabled: true
|
|
1775
|
+
},
|
|
1776
|
+
keyword_config: {
|
|
1777
|
+
enabled: true,
|
|
1778
|
+
ignore_above_len: 1024
|
|
1779
|
+
},
|
|
1780
|
+
vector_config: {
|
|
1781
|
+
enabled: true,
|
|
1782
|
+
model_id: "{{embedding_model_id}}"
|
|
1783
|
+
}
|
|
1784
|
+
},
|
|
1785
|
+
mapped_field: {
|
|
1786
|
+
display_name: "doc_name",
|
|
1787
|
+
name: "doc_name",
|
|
1788
|
+
type: "text"
|
|
1789
|
+
},
|
|
1790
|
+
name: "doc_name",
|
|
1791
|
+
type: "text"
|
|
1792
|
+
},
|
|
1793
|
+
{
|
|
1794
|
+
comment: "",
|
|
1795
|
+
condition_operations: [
|
|
1796
|
+
"==",
|
|
1797
|
+
"!=",
|
|
1798
|
+
"in",
|
|
1799
|
+
"not_in",
|
|
1800
|
+
">",
|
|
1801
|
+
">=",
|
|
1802
|
+
"<",
|
|
1803
|
+
"<=",
|
|
1804
|
+
"range",
|
|
1805
|
+
"out_range",
|
|
1806
|
+
"regex",
|
|
1807
|
+
"like",
|
|
1808
|
+
"not_like"
|
|
1809
|
+
],
|
|
1810
|
+
display_name: "document_id",
|
|
1811
|
+
index_config: {
|
|
1812
|
+
fulltext_config: {
|
|
1813
|
+
analyzer: "",
|
|
1814
|
+
enabled: false
|
|
1815
|
+
},
|
|
1816
|
+
keyword_config: {
|
|
1817
|
+
enabled: true,
|
|
1818
|
+
ignore_above_len: 1024
|
|
1819
|
+
},
|
|
1820
|
+
vector_config: {
|
|
1821
|
+
enabled: false,
|
|
1822
|
+
model_id: ""
|
|
1823
|
+
}
|
|
1824
|
+
},
|
|
1825
|
+
mapped_field: {
|
|
1826
|
+
display_name: "document_id",
|
|
1827
|
+
name: "document_id",
|
|
1828
|
+
type: "string"
|
|
1829
|
+
},
|
|
1830
|
+
name: "document_id",
|
|
1831
|
+
type: "string"
|
|
1832
|
+
},
|
|
1833
|
+
{
|
|
1834
|
+
comment: "",
|
|
1835
|
+
display_name: "rev",
|
|
1836
|
+
mapped_field: {
|
|
1837
|
+
display_name: "rev",
|
|
1838
|
+
name: "rev",
|
|
1839
|
+
type: "text"
|
|
1840
|
+
},
|
|
1841
|
+
name: "rev",
|
|
1842
|
+
type: "text"
|
|
1843
|
+
}
|
|
1844
|
+
],
|
|
1845
|
+
data_source: {
|
|
1846
|
+
id: "{{document_dataset_id}}",
|
|
1847
|
+
type: "resource"
|
|
1848
|
+
},
|
|
1849
|
+
display_key: "doc_name",
|
|
1850
|
+
icon: "icon-tianshenpi",
|
|
1851
|
+
id: "d6scpd42mlgikcusgpp0",
|
|
1852
|
+
incremental_key: "",
|
|
1853
|
+
kn_id: "d6scm5c2mlgikcusgpmg",
|
|
1854
|
+
module_type: "object_type",
|
|
1855
|
+
name: "\u6587\u6863\u5BF9\u8C61",
|
|
1856
|
+
primary_keys: ["document_id"],
|
|
1857
|
+
tags: []
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
branch: "main",
|
|
1861
|
+
color: "#0e5fc5",
|
|
1862
|
+
comment: "",
|
|
1863
|
+
data_properties: [
|
|
1864
|
+
{
|
|
1865
|
+
comment: "",
|
|
1866
|
+
display_name: "reading_order",
|
|
1867
|
+
mapped_field: {
|
|
1868
|
+
display_name: "reading_order",
|
|
1869
|
+
name: "reading_order",
|
|
1870
|
+
type: "integer"
|
|
1871
|
+
},
|
|
1872
|
+
name: "reading_order",
|
|
1873
|
+
type: "integer"
|
|
1874
|
+
},
|
|
1875
|
+
{
|
|
1876
|
+
comment: "",
|
|
1877
|
+
display_name: "bbox_height",
|
|
1878
|
+
name: "bbox_height",
|
|
1879
|
+
type: "integer"
|
|
1880
|
+
},
|
|
1881
|
+
{
|
|
1882
|
+
comment: "",
|
|
1883
|
+
display_name: "bbox_x",
|
|
1884
|
+
name: "bbox_x",
|
|
1885
|
+
type: "integer"
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
comment: "",
|
|
1889
|
+
display_name: "modal_type",
|
|
1890
|
+
mapped_field: {
|
|
1891
|
+
display_name: "modal_type",
|
|
1892
|
+
name: "modal_type",
|
|
1893
|
+
type: "text"
|
|
1894
|
+
},
|
|
1895
|
+
name: "modal_type",
|
|
1896
|
+
type: "text"
|
|
1897
|
+
},
|
|
1898
|
+
{
|
|
1899
|
+
comment: "",
|
|
1900
|
+
display_name: "bbox_y",
|
|
1901
|
+
name: "bbox_y",
|
|
1902
|
+
type: "integer"
|
|
1903
|
+
},
|
|
1904
|
+
{
|
|
1905
|
+
comment: "",
|
|
1906
|
+
display_name: "page_no",
|
|
1907
|
+
name: "page_no",
|
|
1908
|
+
type: "string"
|
|
1909
|
+
},
|
|
1910
|
+
{
|
|
1911
|
+
comment: "",
|
|
1912
|
+
display_name: "metadata_source_format",
|
|
1913
|
+
name: "metadata_source_format",
|
|
1914
|
+
type: "text"
|
|
1915
|
+
},
|
|
1916
|
+
{
|
|
1917
|
+
comment: "",
|
|
1918
|
+
display_name: "structure_html",
|
|
1919
|
+
name: "structure_html",
|
|
1920
|
+
type: "text"
|
|
1921
|
+
},
|
|
1922
|
+
{
|
|
1923
|
+
comment: "",
|
|
1924
|
+
display_name: "lat_lon",
|
|
1925
|
+
mapped_field: {
|
|
1926
|
+
display_name: "lat_lon",
|
|
1927
|
+
name: "lat_lon",
|
|
1928
|
+
type: "point"
|
|
1929
|
+
},
|
|
1930
|
+
name: "lat_lon",
|
|
1931
|
+
type: "point"
|
|
1932
|
+
},
|
|
1933
|
+
{
|
|
1934
|
+
comment: "",
|
|
1935
|
+
display_name: "metadata_deduplication_id",
|
|
1936
|
+
name: "metadata_deduplication_id",
|
|
1937
|
+
type: "string"
|
|
1938
|
+
},
|
|
1939
|
+
{
|
|
1940
|
+
comment: "",
|
|
1941
|
+
display_name: "element_id",
|
|
1942
|
+
mapped_field: {
|
|
1943
|
+
display_name: "element_id",
|
|
1944
|
+
name: "element_id",
|
|
1945
|
+
type: "string"
|
|
1946
|
+
},
|
|
1947
|
+
name: "element_id",
|
|
1948
|
+
type: "string"
|
|
1949
|
+
},
|
|
1950
|
+
{
|
|
1951
|
+
comment: "",
|
|
1952
|
+
display_name: "level",
|
|
1953
|
+
mapped_field: {
|
|
1954
|
+
display_name: "level",
|
|
1955
|
+
name: "level",
|
|
1956
|
+
type: "integer"
|
|
1957
|
+
},
|
|
1958
|
+
name: "level",
|
|
1959
|
+
type: "integer"
|
|
1960
|
+
},
|
|
1961
|
+
{
|
|
1962
|
+
comment: "",
|
|
1963
|
+
display_name: "element_type",
|
|
1964
|
+
mapped_field: {
|
|
1965
|
+
display_name: "element_type",
|
|
1966
|
+
name: "element_type",
|
|
1967
|
+
type: "text"
|
|
1968
|
+
},
|
|
1969
|
+
name: "element_type",
|
|
1970
|
+
type: "text"
|
|
1971
|
+
},
|
|
1972
|
+
{
|
|
1973
|
+
comment: "",
|
|
1974
|
+
display_name: "bbox_width",
|
|
1975
|
+
name: "bbox_width",
|
|
1976
|
+
type: "string"
|
|
1977
|
+
},
|
|
1978
|
+
{
|
|
1979
|
+
comment: "",
|
|
1980
|
+
display_name: "document_id",
|
|
1981
|
+
mapped_field: {
|
|
1982
|
+
display_name: "document_id",
|
|
1983
|
+
name: "document_id",
|
|
1984
|
+
type: "string"
|
|
1985
|
+
},
|
|
1986
|
+
name: "document_id",
|
|
1987
|
+
type: "string"
|
|
1988
|
+
},
|
|
1989
|
+
{
|
|
1990
|
+
comment: "",
|
|
1991
|
+
condition_operations: [
|
|
1992
|
+
"match",
|
|
1993
|
+
"multi_match",
|
|
1994
|
+
"==",
|
|
1995
|
+
"!=",
|
|
1996
|
+
"in",
|
|
1997
|
+
"not_in",
|
|
1998
|
+
">",
|
|
1999
|
+
">=",
|
|
2000
|
+
"<",
|
|
2001
|
+
"<=",
|
|
2002
|
+
"range",
|
|
2003
|
+
"out_range",
|
|
2004
|
+
"regex",
|
|
2005
|
+
"like",
|
|
2006
|
+
"not_like"
|
|
2007
|
+
],
|
|
2008
|
+
display_name: "content",
|
|
2009
|
+
index_config: {
|
|
2010
|
+
fulltext_config: {
|
|
2011
|
+
analyzer: "ik_max_word",
|
|
2012
|
+
enabled: true
|
|
2013
|
+
},
|
|
2014
|
+
keyword_config: {
|
|
2015
|
+
enabled: false,
|
|
2016
|
+
ignore_above_len: 1024
|
|
2017
|
+
},
|
|
2018
|
+
vector_config: {
|
|
2019
|
+
enabled: true,
|
|
2020
|
+
model_id: "{{embedding_model_id}}"
|
|
2021
|
+
}
|
|
2022
|
+
},
|
|
2023
|
+
mapped_field: {
|
|
2024
|
+
display_name: "content",
|
|
2025
|
+
name: "content",
|
|
2026
|
+
type: "text"
|
|
2027
|
+
},
|
|
2028
|
+
name: "content",
|
|
2029
|
+
type: "text"
|
|
2030
|
+
},
|
|
2031
|
+
{
|
|
2032
|
+
comment: "",
|
|
2033
|
+
display_name: "img_path",
|
|
2034
|
+
name: "img_path",
|
|
2035
|
+
type: "string"
|
|
2036
|
+
},
|
|
2037
|
+
{
|
|
2038
|
+
comment: "",
|
|
2039
|
+
display_name: "parent_element_id",
|
|
2040
|
+
mapped_field: {
|
|
2041
|
+
display_name: "parent_element_id",
|
|
2042
|
+
name: "parent_element_id",
|
|
2043
|
+
type: "string"
|
|
2044
|
+
},
|
|
2045
|
+
name: "parent_element_id",
|
|
2046
|
+
type: "string"
|
|
2047
|
+
},
|
|
2048
|
+
{
|
|
2049
|
+
comment: "",
|
|
2050
|
+
display_name: "@timestamp",
|
|
2051
|
+
mapped_field: {
|
|
2052
|
+
display_name: "@timestamp",
|
|
2053
|
+
name: "@timestamp",
|
|
2054
|
+
type: "integer"
|
|
2055
|
+
},
|
|
2056
|
+
name: "timestamp",
|
|
2057
|
+
type: "integer"
|
|
2058
|
+
},
|
|
2059
|
+
{
|
|
2060
|
+
comment: "",
|
|
2061
|
+
display_name: "style",
|
|
2062
|
+
mapped_field: {
|
|
2063
|
+
display_name: "style",
|
|
2064
|
+
name: "style"
|
|
2065
|
+
},
|
|
2066
|
+
name: "style",
|
|
2067
|
+
type: ""
|
|
2068
|
+
}
|
|
2069
|
+
],
|
|
2070
|
+
data_source: {
|
|
2071
|
+
id: "{{element_dataset_id}}",
|
|
2072
|
+
type: "resource"
|
|
2073
|
+
},
|
|
2074
|
+
display_key: "element_id",
|
|
2075
|
+
icon: "icon-tianshenpi",
|
|
2076
|
+
id: "d6scrvk2mlgikcusgpq0",
|
|
2077
|
+
incremental_key: "",
|
|
2078
|
+
kn_id: "d6scm5c2mlgikcusgpmg",
|
|
2079
|
+
module_type: "object_type",
|
|
2080
|
+
name: "\u6587\u6863\u89E3\u6790\u6811\u5BF9\u8C61",
|
|
2081
|
+
primary_keys: ["element_id"],
|
|
2082
|
+
tags: []
|
|
2083
|
+
}
|
|
2084
|
+
],
|
|
2085
|
+
operations: [
|
|
2086
|
+
"task_manage",
|
|
2087
|
+
"import",
|
|
2088
|
+
"export",
|
|
2089
|
+
"create",
|
|
2090
|
+
"delete",
|
|
2091
|
+
"data_query",
|
|
2092
|
+
"view_detail",
|
|
2093
|
+
"modify",
|
|
2094
|
+
"authorize"
|
|
2095
|
+
],
|
|
2096
|
+
relation_types: [
|
|
2097
|
+
{
|
|
2098
|
+
branch: "main",
|
|
2099
|
+
color: "",
|
|
2100
|
+
comment: "",
|
|
2101
|
+
icon: "",
|
|
2102
|
+
id: "d6se2d42mlgikcusgpr0",
|
|
2103
|
+
kn_id: "d6scm5c2mlgikcusgpmg",
|
|
2104
|
+
mapping_rules: [
|
|
2105
|
+
{
|
|
2106
|
+
source_property: {
|
|
2107
|
+
display_name: "document_id",
|
|
2108
|
+
name: "document_id"
|
|
2109
|
+
},
|
|
2110
|
+
target_property: {
|
|
2111
|
+
display_name: "document_id",
|
|
2112
|
+
name: "document_id"
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
],
|
|
2116
|
+
module_type: "relation_type",
|
|
2117
|
+
name: "\u6587\u6863\u5305\u542B\u7279\u5F81",
|
|
2118
|
+
source_object_type: {
|
|
2119
|
+
branch: "",
|
|
2120
|
+
color: "",
|
|
2121
|
+
icon: "",
|
|
2122
|
+
id: "",
|
|
2123
|
+
name: ""
|
|
2124
|
+
},
|
|
2125
|
+
source_object_type_id: "d6scpd42mlgikcusgpp0",
|
|
2126
|
+
tags: [],
|
|
2127
|
+
target_object_type: {
|
|
2128
|
+
branch: "",
|
|
2129
|
+
color: "",
|
|
2130
|
+
icon: "",
|
|
2131
|
+
id: "",
|
|
2132
|
+
name: ""
|
|
2133
|
+
},
|
|
2134
|
+
target_object_type_id: "d6scrvk2mlgikcusgpq0",
|
|
2135
|
+
type: "direct"
|
|
2136
|
+
},
|
|
2137
|
+
{
|
|
2138
|
+
branch: "main",
|
|
2139
|
+
color: "",
|
|
2140
|
+
comment: "",
|
|
2141
|
+
icon: "",
|
|
2142
|
+
id: "d6se2jc2mlgikcusgps0",
|
|
2143
|
+
kn_id: "d6scm5c2mlgikcusgpmg",
|
|
2144
|
+
mapping_rules: [
|
|
2145
|
+
{
|
|
2146
|
+
source_property: {
|
|
2147
|
+
display_name: "document_id",
|
|
2148
|
+
name: "document_id"
|
|
2149
|
+
},
|
|
2150
|
+
target_property: {
|
|
2151
|
+
display_name: "document_id",
|
|
2152
|
+
name: "document_id"
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
],
|
|
2156
|
+
module_type: "relation_type",
|
|
2157
|
+
name: "\u7279\u5F81\u5173\u8054\u5143\u7D20",
|
|
2158
|
+
source_object_type: {
|
|
2159
|
+
branch: "",
|
|
2160
|
+
color: "",
|
|
2161
|
+
icon: "",
|
|
2162
|
+
id: "",
|
|
2163
|
+
name: ""
|
|
2164
|
+
},
|
|
2165
|
+
source_object_type_id: "d6sco6s2mlgikcusgpo0",
|
|
2166
|
+
tags: [],
|
|
2167
|
+
target_object_type: {
|
|
2168
|
+
branch: "",
|
|
2169
|
+
color: "",
|
|
2170
|
+
icon: "",
|
|
2171
|
+
id: "",
|
|
2172
|
+
name: ""
|
|
2173
|
+
},
|
|
2174
|
+
target_object_type_id: "d6scrvk2mlgikcusgpq0",
|
|
2175
|
+
type: "direct"
|
|
2176
|
+
}
|
|
2177
|
+
],
|
|
2178
|
+
tags: [],
|
|
2179
|
+
validate_dependency: false
|
|
2180
|
+
};
|
|
2181
|
+
|
|
2182
|
+
// src/templates/dataflow/unstructured/manifest.json
|
|
2183
|
+
var manifest_default2 = {
|
|
2184
|
+
name: "unstructured",
|
|
2185
|
+
type: "dataflow",
|
|
2186
|
+
description: "\u975E\u7ED3\u6784\u5316\u6587\u6863\u5904\u7406\u6D41\u7A0B",
|
|
2187
|
+
arguments: [
|
|
2188
|
+
{ name: "title", required: true, description: "\u6570\u636E\u6D41\u6807\u9898", type: "string" },
|
|
2189
|
+
{
|
|
2190
|
+
name: "content_dataset_id",
|
|
2191
|
+
required: true,
|
|
2192
|
+
description: "\u5185\u5BB9\u6570\u636E\u96C6 ID",
|
|
2193
|
+
type: "string"
|
|
2194
|
+
},
|
|
2195
|
+
{
|
|
2196
|
+
name: "document_dataset_id",
|
|
2197
|
+
required: true,
|
|
2198
|
+
description: "\u6587\u6863\u6570\u636E\u96C6 ID",
|
|
2199
|
+
type: "string"
|
|
2200
|
+
},
|
|
2201
|
+
{
|
|
2202
|
+
name: "element_dataset_id",
|
|
2203
|
+
required: true,
|
|
2204
|
+
description: "\u5143\u7D20\u6570\u636E\u96C6 ID",
|
|
2205
|
+
type: "string"
|
|
2206
|
+
}
|
|
2207
|
+
]
|
|
2208
|
+
};
|
|
2209
|
+
|
|
2210
|
+
// src/templates/dataflow/unstructured/template.json
|
|
2211
|
+
var template_default2 = {
|
|
2212
|
+
title: "{{title}}",
|
|
2213
|
+
steps: [
|
|
2214
|
+
{
|
|
2215
|
+
id: "0",
|
|
2216
|
+
title: "",
|
|
2217
|
+
operator: "@trigger/dataflow-doc"
|
|
2218
|
+
},
|
|
2219
|
+
{
|
|
2220
|
+
id: "1",
|
|
2221
|
+
title: "",
|
|
2222
|
+
operator: "@content/file_parse",
|
|
2223
|
+
parameters: {
|
|
2224
|
+
docid: "{{__0.id}}",
|
|
2225
|
+
model: "embedding",
|
|
2226
|
+
slice_vector: "slice_vector",
|
|
2227
|
+
source_type: "docid",
|
|
2228
|
+
version: "{{__0.rev}}"
|
|
2229
|
+
}
|
|
2230
|
+
},
|
|
2231
|
+
{
|
|
2232
|
+
id: "1001",
|
|
2233
|
+
title: "\u5199\u5165\u5411\u91CF",
|
|
2234
|
+
operator: "@dataset/write-docs",
|
|
2235
|
+
parameters: {
|
|
2236
|
+
dataset_id: "{{content_dataset_id}}",
|
|
2237
|
+
documents: "{{__1.chunks}}"
|
|
2238
|
+
}
|
|
2239
|
+
},
|
|
2240
|
+
{
|
|
2241
|
+
id: "1002",
|
|
2242
|
+
title: "\u5199\u5165\u5143\u7D20",
|
|
2243
|
+
operator: "@dataset/write-docs",
|
|
2244
|
+
parameters: {
|
|
2245
|
+
dataset_id: "{{element_dataset_id}}",
|
|
2246
|
+
documents: "{{__1.content_list}}"
|
|
2247
|
+
}
|
|
2248
|
+
},
|
|
2249
|
+
{
|
|
2250
|
+
id: "1003",
|
|
2251
|
+
title: "\u5199\u5165\u6587\u4EF6\u5143\u4FE1\u606F",
|
|
2252
|
+
operator: "@dataset/write-docs",
|
|
2253
|
+
parameters: {
|
|
2254
|
+
dataset_id: "{{document_dataset_id}}",
|
|
2255
|
+
documents: [
|
|
2256
|
+
{
|
|
2257
|
+
document_id: "{{__0.id}}",
|
|
2258
|
+
doc_name: "{{__0.name}}"
|
|
2259
|
+
}
|
|
2260
|
+
]
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
],
|
|
2264
|
+
trigger_config: {
|
|
2265
|
+
operator: "@trigger/manual",
|
|
2266
|
+
dataSource: {
|
|
2267
|
+
operator: "",
|
|
2268
|
+
parameters: {
|
|
2269
|
+
accessorid: "00000000-0000-0000-0000-000000000000"
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
};
|
|
2274
|
+
|
|
2275
|
+
// src/templates/dataset/document-content/manifest.json
|
|
2276
|
+
var manifest_default3 = {
|
|
2277
|
+
name: "document-content",
|
|
2278
|
+
type: "dataset",
|
|
2279
|
+
description: "\u6587\u6863\u5207\u7247\u53CA\u5411\u91CF\u6570\u636E\u96C6",
|
|
2280
|
+
arguments: [
|
|
2281
|
+
{ name: "name", required: true, description: "\u6570\u636E\u96C6\u540D\u79F0", type: "string" },
|
|
2282
|
+
{
|
|
2283
|
+
name: "catalog_id",
|
|
2284
|
+
required: false,
|
|
2285
|
+
default: "adp_bkn_catalog",
|
|
2286
|
+
description: "\u6240\u5C5E\u76EE\u5F55ID",
|
|
2287
|
+
type: "string"
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
name: "source_identifier",
|
|
2291
|
+
required: false,
|
|
2292
|
+
default: "",
|
|
2293
|
+
description: "\u6570\u636E\u6E90\u6807\u8BC6\u7B26\uFF0C\u4E3A\u7A7A\u65F6\u81EA\u52A8\u751F\u6210",
|
|
2294
|
+
type: "string"
|
|
2295
|
+
}
|
|
2296
|
+
]
|
|
2297
|
+
};
|
|
2298
|
+
|
|
2299
|
+
// src/templates/dataset/document-content/template.json
|
|
2300
|
+
var template_default3 = {
|
|
2301
|
+
catalog_id: "{{catalog_id}}",
|
|
2302
|
+
name: "{{name}}",
|
|
2303
|
+
category: "dataset",
|
|
2304
|
+
status: "active",
|
|
2305
|
+
description: "\u6587\u6863\u5207\u7247\u53CA\u5411\u91CF\u6570\u636E\u96C6",
|
|
2306
|
+
source_identifier: "{{source_identifier}}",
|
|
2307
|
+
schema_definition: [
|
|
2308
|
+
{ name: "id", type: "keyword" },
|
|
2309
|
+
{ name: "document_id", type: "keyword" },
|
|
2310
|
+
{ name: "slice_md5", type: "keyword" },
|
|
2311
|
+
{ name: "deduplication_id", type: "keyword" },
|
|
2312
|
+
{ name: "segment_id", type: "integer" },
|
|
2313
|
+
{ name: "slice_type", type: "integer" },
|
|
2314
|
+
{
|
|
2315
|
+
name: "slice_content",
|
|
2316
|
+
type: "text",
|
|
2317
|
+
features: [
|
|
2318
|
+
{
|
|
2319
|
+
name: "slice_content_fulltext",
|
|
2320
|
+
feature_type: "fulltext",
|
|
2321
|
+
ref_property: "slice_content",
|
|
2322
|
+
config: { analyzer: "standard" }
|
|
2323
|
+
}
|
|
2324
|
+
]
|
|
2325
|
+
},
|
|
2326
|
+
{
|
|
2327
|
+
name: "text_vector",
|
|
2328
|
+
type: "vector",
|
|
2329
|
+
features: [
|
|
2330
|
+
{
|
|
2331
|
+
name: "text_vector",
|
|
2332
|
+
feature_type: "vector",
|
|
2333
|
+
ref_property: "text_vector",
|
|
2334
|
+
config: {
|
|
2335
|
+
dimension: 768,
|
|
2336
|
+
method: {
|
|
2337
|
+
name: "hnsw",
|
|
2338
|
+
engine: "lucene",
|
|
2339
|
+
parameters: { ef_construction: 256 }
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
]
|
|
2344
|
+
},
|
|
2345
|
+
{ name: "img_path", type: "keyword" },
|
|
2346
|
+
{
|
|
2347
|
+
name: "image_vector",
|
|
2348
|
+
type: "vector",
|
|
2349
|
+
features: [
|
|
2350
|
+
{
|
|
2351
|
+
name: "image_vector",
|
|
2352
|
+
feature_type: "vector",
|
|
2353
|
+
ref_property: "image_vector",
|
|
2354
|
+
config: {
|
|
2355
|
+
dimension: 512,
|
|
2356
|
+
method: {
|
|
2357
|
+
name: "hnsw",
|
|
2358
|
+
engine: "lucene",
|
|
2359
|
+
parameters: { ef_construction: 256 }
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
]
|
|
2364
|
+
},
|
|
2365
|
+
{ name: "created_at", type: "text" },
|
|
2366
|
+
{ name: "updated_at", type: "text" },
|
|
2367
|
+
{ name: "@timestamp", type: "long" }
|
|
2368
|
+
]
|
|
2369
|
+
};
|
|
2370
|
+
|
|
2371
|
+
// src/templates/dataset/document-element/manifest.json
|
|
2372
|
+
var manifest_default4 = {
|
|
2373
|
+
name: "document-element",
|
|
2374
|
+
type: "dataset",
|
|
2375
|
+
description: "\u6587\u6863\u7ED3\u6784\u5316\u5143\u7D20\u6570\u636E\u96C6",
|
|
2376
|
+
arguments: [
|
|
2377
|
+
{ name: "name", required: true, description: "\u6570\u636E\u96C6\u540D\u79F0", type: "string" },
|
|
2378
|
+
{
|
|
2379
|
+
name: "catalog_id",
|
|
2380
|
+
required: false,
|
|
2381
|
+
default: "adp_bkn_catalog",
|
|
2382
|
+
description: "\u6240\u5C5E\u76EE\u5F55ID",
|
|
2383
|
+
type: "string"
|
|
2384
|
+
},
|
|
2385
|
+
{
|
|
2386
|
+
name: "source_identifier",
|
|
2387
|
+
required: false,
|
|
2388
|
+
default: "",
|
|
2389
|
+
description: "\u6570\u636E\u6E90\u6807\u8BC6\u7B26\uFF0C\u4E3A\u7A7A\u65F6\u81EA\u52A8\u751F\u6210",
|
|
2390
|
+
type: "string"
|
|
2391
|
+
}
|
|
2392
|
+
]
|
|
2393
|
+
};
|
|
2394
|
+
|
|
2395
|
+
// src/templates/dataset/document-element/template.json
|
|
2396
|
+
var template_default4 = {
|
|
2397
|
+
catalog_id: "{{catalog_id}}",
|
|
2398
|
+
name: "{{name}}",
|
|
2399
|
+
category: "dataset",
|
|
2400
|
+
status: "active",
|
|
2401
|
+
description: "\u6587\u6863\u7ED3\u6784\u5316\u5143\u7D20\u6570\u636E\u96C6",
|
|
2402
|
+
source_identifier: "{{source_identifier}}",
|
|
2403
|
+
schema_definition: [
|
|
2404
|
+
{ name: "id", type: "keyword" },
|
|
2405
|
+
{ name: "element_id", type: "keyword" },
|
|
2406
|
+
{ name: "document_id", type: "keyword" },
|
|
2407
|
+
{ name: "element_type", type: "keyword" },
|
|
2408
|
+
{ name: "parent_id", type: "keyword" },
|
|
2409
|
+
{ name: "level", type: "integer" },
|
|
2410
|
+
{
|
|
2411
|
+
name: "content",
|
|
2412
|
+
type: "text",
|
|
2413
|
+
features: [
|
|
2414
|
+
{
|
|
2415
|
+
name: "content_fulltext",
|
|
2416
|
+
feature_type: "fulltext",
|
|
2417
|
+
ref_property: "content",
|
|
2418
|
+
config: { analyzer: "standard" }
|
|
2419
|
+
}
|
|
2420
|
+
]
|
|
2421
|
+
},
|
|
2422
|
+
{ name: "metadata", type: "object" },
|
|
2423
|
+
{ name: "@timestamp", type: "long" }
|
|
2424
|
+
]
|
|
2425
|
+
};
|
|
2426
|
+
|
|
2427
|
+
// src/templates/dataset/document/manifest.json
|
|
2428
|
+
var manifest_default5 = {
|
|
2429
|
+
name: "document",
|
|
2430
|
+
type: "dataset",
|
|
2431
|
+
description: "\u6587\u6863\u5143\u4FE1\u606F\u6570\u636E\u96C6",
|
|
2432
|
+
arguments: [
|
|
2433
|
+
{ name: "name", required: true, description: "\u6570\u636E\u96C6\u540D\u79F0", type: "string" },
|
|
2434
|
+
{
|
|
2435
|
+
name: "catalog_id",
|
|
2436
|
+
required: false,
|
|
2437
|
+
default: "adp_bkn_catalog",
|
|
2438
|
+
description: "\u6240\u5C5E\u76EE\u5F55ID",
|
|
2439
|
+
type: "string"
|
|
2440
|
+
},
|
|
2441
|
+
{
|
|
2442
|
+
name: "source_identifier",
|
|
2443
|
+
required: false,
|
|
2444
|
+
default: "",
|
|
2445
|
+
description: "\u6570\u636E\u6E90\u6807\u8BC6\u7B26\uFF0C\u4E3A\u7A7A\u65F6\u81EA\u52A8\u751F\u6210",
|
|
2446
|
+
type: "string"
|
|
2447
|
+
}
|
|
2448
|
+
]
|
|
2449
|
+
};
|
|
2450
|
+
|
|
2451
|
+
// src/templates/dataset/document/template.json
|
|
2452
|
+
var template_default5 = {
|
|
2453
|
+
catalog_id: "{{catalog_id}}",
|
|
2454
|
+
name: "{{name}}",
|
|
2455
|
+
category: "dataset",
|
|
2456
|
+
status: "active",
|
|
2457
|
+
description: "\u6587\u6863\u5143\u4FE1\u606F\u6570\u636E\u96C6",
|
|
2458
|
+
source_identifier: "{{source_identifier}}",
|
|
2459
|
+
schema_definition: [
|
|
2460
|
+
{ name: "id", type: "keyword" },
|
|
2461
|
+
{ name: "document_id", type: "keyword" },
|
|
2462
|
+
{
|
|
2463
|
+
name: "doc_name",
|
|
2464
|
+
type: "text",
|
|
2465
|
+
features: [
|
|
2466
|
+
{ name: "doc_name_keyword", feature_type: "keyword", ref_property: "doc_name" },
|
|
2467
|
+
{
|
|
2468
|
+
name: "doc_name_fulltext",
|
|
2469
|
+
feature_type: "fulltext",
|
|
2470
|
+
ref_property: "doc_name",
|
|
2471
|
+
config: { analyzer: "standard" }
|
|
2472
|
+
}
|
|
2473
|
+
]
|
|
2474
|
+
},
|
|
2475
|
+
{ name: "doc_md5", type: "keyword" },
|
|
2476
|
+
{ name: "pages", type: "integer" },
|
|
2477
|
+
{ name: "file_type", type: "keyword" },
|
|
2478
|
+
{ name: "creator_id", type: "keyword" },
|
|
2479
|
+
{ name: "created_at", type: "text" },
|
|
2480
|
+
{ name: "updated_at", type: "text" },
|
|
2481
|
+
{ name: "@timestamp", type: "long" }
|
|
2482
|
+
]
|
|
2483
|
+
};
|
|
2484
|
+
|
|
2485
|
+
// src/utils/dataflow-templates.ts
|
|
2486
|
+
var REGISTRY = {
|
|
2487
|
+
dataset: {
|
|
2488
|
+
document: {
|
|
2489
|
+
manifest: manifest_default5,
|
|
2490
|
+
template: template_default5
|
|
2491
|
+
},
|
|
2492
|
+
"document-content": {
|
|
2493
|
+
manifest: manifest_default3,
|
|
2494
|
+
template: template_default3
|
|
2495
|
+
},
|
|
2496
|
+
"document-element": {
|
|
2497
|
+
manifest: manifest_default4,
|
|
2498
|
+
template: template_default4
|
|
2499
|
+
}
|
|
2500
|
+
},
|
|
2501
|
+
bkn: {
|
|
2502
|
+
document: {
|
|
2503
|
+
manifest: manifest_default,
|
|
2504
|
+
template: template_default
|
|
2505
|
+
}
|
|
2506
|
+
},
|
|
2507
|
+
dataflow: {
|
|
2508
|
+
unstructured: {
|
|
2509
|
+
manifest: manifest_default2,
|
|
2510
|
+
template: template_default2
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
};
|
|
2514
|
+
function loadTemplate(type, name) {
|
|
2515
|
+
return REGISTRY[type]?.[name] ?? null;
|
|
2516
|
+
}
|
|
2517
|
+
function listTemplates() {
|
|
2518
|
+
const out = [];
|
|
2519
|
+
for (const type of Object.keys(REGISTRY)) {
|
|
2520
|
+
for (const [name, t] of Object.entries(REGISTRY[type])) {
|
|
2521
|
+
out.push({
|
|
2522
|
+
type,
|
|
2523
|
+
name,
|
|
2524
|
+
description: t.manifest.description,
|
|
2525
|
+
arguments: t.manifest.arguments
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
return out;
|
|
2530
|
+
}
|
|
2531
|
+
function generateSourceIdentifier(prefix) {
|
|
2532
|
+
return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 12)}`;
|
|
2533
|
+
}
|
|
2534
|
+
function interpolate(s, values) {
|
|
2535
|
+
return s.replace(
|
|
2536
|
+
/\{\{(\w+)\}\}/g,
|
|
2537
|
+
(_m, k) => values[k] !== void 0 ? String(values[k]) : `{{${k}}}`
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
function deepReplace(obj, values) {
|
|
2541
|
+
if (typeof obj === "string") return interpolate(obj, values);
|
|
2542
|
+
if (Array.isArray(obj)) return obj.map((x) => deepReplace(x, values));
|
|
2543
|
+
if (obj && typeof obj === "object") {
|
|
2544
|
+
const out = {};
|
|
2545
|
+
for (const [k, v] of Object.entries(obj))
|
|
2546
|
+
out[k] = deepReplace(v, values);
|
|
2547
|
+
return out;
|
|
2548
|
+
}
|
|
2549
|
+
return obj;
|
|
2550
|
+
}
|
|
2551
|
+
function renderTemplate(t, args) {
|
|
2552
|
+
const merged = {};
|
|
2553
|
+
const missing = [];
|
|
2554
|
+
for (const arg of t.manifest.arguments) {
|
|
2555
|
+
if (args[arg.name] !== void 0) merged[arg.name] = args[arg.name];
|
|
2556
|
+
else if (arg.default !== void 0) merged[arg.name] = arg.default;
|
|
2557
|
+
else if (arg.required) missing.push(arg.name);
|
|
2558
|
+
}
|
|
2559
|
+
if (missing.length > 0) throw new Error(`Missing required argument(s): ${missing.join(", ")}`);
|
|
2560
|
+
return deepReplace(t.template, merged);
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
// src/resources/dataflows.ts
|
|
2564
|
+
function instantiate(type, name, args) {
|
|
2565
|
+
const t = loadTemplate(type, name);
|
|
2566
|
+
if (!t) throw new Error(`Template not found: ${type}/${name}`);
|
|
2567
|
+
const merged = { ...args };
|
|
2568
|
+
if (type === "dataset" && !merged.source_identifier) {
|
|
2569
|
+
merged.source_identifier = generateSourceIdentifier(`dataflow_${t.manifest.name}`);
|
|
2570
|
+
}
|
|
2571
|
+
return renderTemplate(t, merged);
|
|
2572
|
+
}
|
|
2573
|
+
function dataflows(ctx) {
|
|
2574
|
+
return {
|
|
2575
|
+
list: () => listDataflows(ctx),
|
|
2576
|
+
runs: (dagId, opts) => listDataflowRuns(ctx, dagId, opts),
|
|
2577
|
+
logs: (dagId, instanceId, opts) => getDataflowLogs(ctx, dagId, instanceId, opts),
|
|
2578
|
+
run: (dagId, url, name) => runDataflowRemote(ctx, dagId, url, name),
|
|
2579
|
+
create: (body) => createDataflow(ctx, body),
|
|
2580
|
+
templates: () => listTemplates(),
|
|
2581
|
+
/** Instantiate a dataset template → create a vega resource. */
|
|
2582
|
+
createDataset: (template, args) => createResourceRaw(ctx, instantiate("dataset", template, args)),
|
|
2583
|
+
/** Instantiate a bkn template → create a knowledge network. */
|
|
2584
|
+
createBkn: (template, args) => createKnowledgeNetworkRaw(ctx, instantiate("bkn", template, args))
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
// src/resources/knowledge-networks.ts
|
|
2589
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
2590
|
+
import { resolve as resolve3 } from "path";
|
|
2591
|
+
|
|
2592
|
+
// src/api/bkn-backend.ts
|
|
2593
|
+
var BASE4 = "/api/bkn-backend/v1/knowledge-networks";
|
|
2594
|
+
var BKNS = "/api/bkn-backend/v1/bkns";
|
|
2595
|
+
function knPath(knId, path) {
|
|
2596
|
+
return `${BASE4}/${encodeURIComponent(knId)}/${path}`;
|
|
2597
|
+
}
|
|
2598
|
+
async function uploadBkn(ctx, tarBuffer, opts = {}) {
|
|
2599
|
+
applyTls(ctx);
|
|
2600
|
+
const url = new URL(`${ctx.baseUrl}${BKNS}`);
|
|
2601
|
+
url.searchParams.set("branch", opts.branch ?? "main");
|
|
2602
|
+
const form = new FormData();
|
|
2603
|
+
form.append(
|
|
2604
|
+
"file",
|
|
2605
|
+
new Blob([new Uint8Array(tarBuffer)], { type: "application/octet-stream" }),
|
|
2606
|
+
"bkn.tar"
|
|
2607
|
+
);
|
|
2608
|
+
const res = await fetch(url, { method: "POST", headers: buildHeaders(ctx), body: form });
|
|
2609
|
+
const text = await res.text();
|
|
2610
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
2611
|
+
return text ? JSON.parse(text) : void 0;
|
|
2612
|
+
}
|
|
2613
|
+
async function downloadBkn(ctx, knId, opts = {}) {
|
|
2614
|
+
applyTls(ctx);
|
|
2615
|
+
const url = new URL(`${ctx.baseUrl}${BKNS}/${encodeURIComponent(knId)}`);
|
|
2616
|
+
url.searchParams.set("branch", opts.branch ?? "main");
|
|
2617
|
+
const res = await fetch(url, { method: "GET", headers: buildHeaders(ctx) });
|
|
2618
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, await res.text());
|
|
2619
|
+
return Buffer.from(await res.arrayBuffer());
|
|
2620
|
+
}
|
|
2621
|
+
function listBknResources(ctx) {
|
|
2622
|
+
return request(ctx, "/api/bkn-backend/v1/resources");
|
|
2623
|
+
}
|
|
2624
|
+
function relationTypePaths(ctx, knId, body) {
|
|
2625
|
+
return request(ctx, knPath(knId, "relation-type-paths"), { method: "POST", body });
|
|
2626
|
+
}
|
|
2627
|
+
function listConceptGroups(ctx, knId) {
|
|
2628
|
+
return request(ctx, knPath(knId, "concept-groups"));
|
|
2629
|
+
}
|
|
2630
|
+
function getConceptGroup(ctx, knId, cgId) {
|
|
2631
|
+
return request(ctx, knPath(knId, `concept-groups/${encodeURIComponent(cgId)}`));
|
|
2632
|
+
}
|
|
2633
|
+
function createConceptGroup(ctx, knId, body) {
|
|
2634
|
+
return request(ctx, knPath(knId, "concept-groups"), { method: "POST", body });
|
|
2635
|
+
}
|
|
2636
|
+
function updateConceptGroup(ctx, knId, cgId, body) {
|
|
2637
|
+
return request(ctx, knPath(knId, `concept-groups/${encodeURIComponent(cgId)}`), {
|
|
2638
|
+
method: "PUT",
|
|
2639
|
+
body
|
|
2640
|
+
});
|
|
2641
|
+
}
|
|
2642
|
+
function deleteConceptGroup(ctx, knId, cgId) {
|
|
2643
|
+
return request(ctx, knPath(knId, `concept-groups/${encodeURIComponent(cgId)}`), {
|
|
2644
|
+
method: "DELETE"
|
|
2645
|
+
});
|
|
2646
|
+
}
|
|
2647
|
+
function addConceptGroupMembers(ctx, knId, cgId, body) {
|
|
2648
|
+
return request(ctx, knPath(knId, `concept-groups/${encodeURIComponent(cgId)}/object-types`), {
|
|
2649
|
+
method: "POST",
|
|
2650
|
+
body
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
function removeConceptGroupMembers(ctx, knId, cgId, otIds) {
|
|
2654
|
+
return request(
|
|
2655
|
+
ctx,
|
|
2656
|
+
knPath(knId, `concept-groups/${encodeURIComponent(cgId)}/object-types/${otIds}`),
|
|
2657
|
+
{
|
|
2658
|
+
method: "DELETE"
|
|
2659
|
+
}
|
|
2660
|
+
);
|
|
2661
|
+
}
|
|
2662
|
+
function listActionSchedules(ctx, knId) {
|
|
2663
|
+
return request(ctx, knPath(knId, "action-schedules"));
|
|
2664
|
+
}
|
|
2665
|
+
function getActionSchedule(ctx, knId, scheduleId) {
|
|
2666
|
+
return request(ctx, knPath(knId, `action-schedules/${encodeURIComponent(scheduleId)}`));
|
|
2667
|
+
}
|
|
2668
|
+
function createActionSchedule(ctx, knId, body) {
|
|
2669
|
+
return request(ctx, knPath(knId, "action-schedules"), { method: "POST", body });
|
|
2670
|
+
}
|
|
2671
|
+
function updateActionSchedule(ctx, knId, scheduleId, body) {
|
|
2672
|
+
return request(ctx, knPath(knId, `action-schedules/${encodeURIComponent(scheduleId)}`), {
|
|
2673
|
+
method: "PUT",
|
|
2674
|
+
body
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2677
|
+
function setActionScheduleStatus(ctx, knId, scheduleId, body) {
|
|
2678
|
+
return request(ctx, knPath(knId, `action-schedules/${encodeURIComponent(scheduleId)}/status`), {
|
|
2679
|
+
method: "PUT",
|
|
2680
|
+
body
|
|
2681
|
+
});
|
|
2682
|
+
}
|
|
2683
|
+
function deleteActionSchedules(ctx, knId, ids) {
|
|
2684
|
+
return request(ctx, knPath(knId, `action-schedules/${ids}`), { method: "DELETE" });
|
|
2685
|
+
}
|
|
2686
|
+
function listJobs(ctx, knId) {
|
|
2687
|
+
return request(ctx, knPath(knId, "jobs"));
|
|
2688
|
+
}
|
|
2689
|
+
function getJob(ctx, knId, jobId) {
|
|
2690
|
+
return request(ctx, knPath(knId, `jobs/${encodeURIComponent(jobId)}`));
|
|
2691
|
+
}
|
|
2692
|
+
function getJobTasks(ctx, knId, jobId) {
|
|
2693
|
+
return request(ctx, knPath(knId, `jobs/${encodeURIComponent(jobId)}/tasks`));
|
|
2694
|
+
}
|
|
2695
|
+
function deleteJobs(ctx, knId, ids) {
|
|
2696
|
+
return request(ctx, knPath(knId, `jobs/${ids}`), { method: "DELETE" });
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
// src/utils/tar.ts
|
|
2700
|
+
import { spawnSync } from "child_process";
|
|
2701
|
+
import { resolve } from "path";
|
|
2702
|
+
var TAR_MISSING = "tar executable not found. On Windows, ensure tar.exe is in PATH (ships with Windows 10 1803+) or install GNU tar via Git for Windows / scoop.";
|
|
2703
|
+
function packDirectoryToTar(dirPath) {
|
|
2704
|
+
const abs = resolve(dirPath);
|
|
2705
|
+
const result = spawnSync("tar", ["cf", "-", "-C", abs, "."], {
|
|
2706
|
+
maxBuffer: 1024 * 1024 * 512,
|
|
2707
|
+
env: { ...process.env, COPYFILE_DISABLE: "1" }
|
|
2708
|
+
});
|
|
2709
|
+
if (result.error && result.error.code === "ENOENT") {
|
|
2710
|
+
throw new Error(TAR_MISSING);
|
|
2711
|
+
}
|
|
2712
|
+
if (result.status !== 0) {
|
|
2713
|
+
throw new Error(`tar pack failed: ${result.stderr?.toString() ?? result.status}`);
|
|
2714
|
+
}
|
|
2715
|
+
return result.stdout;
|
|
2716
|
+
}
|
|
2717
|
+
function extractTarToDirectory(tarBuffer, dirPath) {
|
|
2718
|
+
const abs = resolve(dirPath);
|
|
2719
|
+
const result = spawnSync("tar", ["xf", "-", "-C", abs], {
|
|
2720
|
+
input: tarBuffer,
|
|
2721
|
+
maxBuffer: 1024 * 1024 * 512
|
|
2722
|
+
});
|
|
2723
|
+
if (result.error && result.error.code === "ENOENT") {
|
|
2724
|
+
throw new Error(TAR_MISSING);
|
|
2725
|
+
}
|
|
2726
|
+
if (result.status !== 0) {
|
|
2727
|
+
throw new Error(`tar extract failed: ${result.stderr?.toString() ?? result.status}`);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
// src/api/vega.ts
|
|
2732
|
+
import { z } from "zod";
|
|
2733
|
+
var VEGA_BASE = "/api/vega-backend/v1";
|
|
2734
|
+
var BuildMode = z.enum(["batch", "streaming"]);
|
|
2735
|
+
var CreateBuildTaskRequest = z.object({
|
|
2736
|
+
resource_id: z.string().min(1),
|
|
2737
|
+
mode: BuildMode,
|
|
2738
|
+
embedding_fields: z.array(z.string()).optional(),
|
|
2739
|
+
build_key_fields: z.array(z.string()).optional(),
|
|
2740
|
+
embedding_model: z.string().optional(),
|
|
2741
|
+
model_dimensions: z.number().int().positive().optional()
|
|
2742
|
+
});
|
|
2743
|
+
var BuildTask = z.object({
|
|
2744
|
+
id: z.string(),
|
|
2745
|
+
resource_id: z.string(),
|
|
2746
|
+
mode: BuildMode,
|
|
2747
|
+
state: z.string().optional(),
|
|
2748
|
+
synced_count: z.number().optional(),
|
|
2749
|
+
vectorized_count: z.number().optional()
|
|
2750
|
+
});
|
|
2751
|
+
async function createBuildTask(ctx, req) {
|
|
2752
|
+
const body = CreateBuildTaskRequest.parse(req);
|
|
2753
|
+
const res = await request(ctx, `${VEGA_BASE}/build-tasks`, { method: "POST", body });
|
|
2754
|
+
return BuildTask.parse(res);
|
|
2755
|
+
}
|
|
2756
|
+
async function getBuildTask(ctx, taskId) {
|
|
2757
|
+
const res = await request(ctx, `${VEGA_BASE}/build-tasks/${encodeURIComponent(taskId)}`);
|
|
2758
|
+
return BuildTask.parse(res);
|
|
2759
|
+
}
|
|
2760
|
+
async function listCatalogs(ctx, opts = {}) {
|
|
2761
|
+
return request(ctx, `${VEGA_BASE}/catalogs`, {
|
|
2762
|
+
query: { limit: opts.limit, offset: opts.offset }
|
|
2763
|
+
});
|
|
2764
|
+
}
|
|
2765
|
+
function getCatalog(ctx, id) {
|
|
2766
|
+
return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}`);
|
|
2767
|
+
}
|
|
2768
|
+
function discoverCatalog(ctx, id, wait = true) {
|
|
2769
|
+
return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/discover`, {
|
|
2770
|
+
method: "POST",
|
|
2771
|
+
query: { wait },
|
|
2772
|
+
timeoutMs: 12e4
|
|
2773
|
+
});
|
|
2774
|
+
}
|
|
2775
|
+
function listCatalogResources(ctx, id, category) {
|
|
2776
|
+
return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/resources`, {
|
|
2777
|
+
query: { category: category || void 0 }
|
|
2778
|
+
});
|
|
2779
|
+
}
|
|
2780
|
+
function catalogHealthStatus(ctx, ids) {
|
|
2781
|
+
return request(
|
|
2782
|
+
ctx,
|
|
2783
|
+
`${VEGA_BASE}/catalogs/${ids.map(encodeURIComponent).join(",")}/health-status`
|
|
2784
|
+
);
|
|
2785
|
+
}
|
|
2786
|
+
function listConnectorTypes(ctx) {
|
|
2787
|
+
return request(ctx, `${VEGA_BASE}/connector-types`, { query: { sort: "name", order: "asc" } });
|
|
2788
|
+
}
|
|
2789
|
+
function getConnectorType(ctx, type) {
|
|
2790
|
+
return request(ctx, `${VEGA_BASE}/connector-types/${encodeURIComponent(type)}`);
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
// src/utils/csv-import.ts
|
|
2794
|
+
import { statSync } from "fs";
|
|
2795
|
+
import { glob, readFile } from "fs/promises";
|
|
2796
|
+
import { basename, resolve as resolve2 } from "path";
|
|
2797
|
+
import { parse } from "csv-parse/sync";
|
|
2798
|
+
async function parseCsvFile(filePath) {
|
|
2799
|
+
let content = await readFile(filePath, "utf8");
|
|
2800
|
+
if (content.charCodeAt(0) === 65279) content = content.slice(1);
|
|
2801
|
+
const records = parse(content, {
|
|
2802
|
+
columns: true,
|
|
2803
|
+
skip_empty_lines: true,
|
|
2804
|
+
trim: true,
|
|
2805
|
+
relax_column_count: false
|
|
2806
|
+
});
|
|
2807
|
+
if (records.length === 0) {
|
|
2808
|
+
const headerRows = parse(content, {
|
|
2809
|
+
columns: false,
|
|
2810
|
+
skip_empty_lines: false,
|
|
2811
|
+
trim: true,
|
|
2812
|
+
to: 1
|
|
2813
|
+
});
|
|
2814
|
+
return { headers: headerRows[0] ?? [], rows: [] };
|
|
2815
|
+
}
|
|
2816
|
+
const headers2 = Object.keys(records[0]);
|
|
2817
|
+
const rows = records.map((rec) => {
|
|
2818
|
+
const row = {};
|
|
2819
|
+
for (const k of headers2) row[k] = rec[k] === "" ? null : rec[k] ?? null;
|
|
2820
|
+
return row;
|
|
2821
|
+
});
|
|
2822
|
+
return { headers: headers2, rows };
|
|
2823
|
+
}
|
|
2824
|
+
function buildTableName(filePath, prefix = "") {
|
|
2825
|
+
const stem = basename(filePath).replace(/\.csv$/i, "");
|
|
2826
|
+
return `${prefix}${stem}`.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^(\d)/, "_$1");
|
|
2827
|
+
}
|
|
2828
|
+
function splitBatches(rows, batchSize) {
|
|
2829
|
+
const out = [];
|
|
2830
|
+
for (let i = 0; i < rows.length; i += batchSize) out.push(rows.slice(i, i + batchSize));
|
|
2831
|
+
return out;
|
|
2832
|
+
}
|
|
2833
|
+
function buildFieldMappings(headers2) {
|
|
2834
|
+
return headers2.map((name) => ({ source: { name }, target: { name, data_type: "VARCHAR(512)" } }));
|
|
2835
|
+
}
|
|
2836
|
+
function buildImportDag(opts) {
|
|
2837
|
+
return {
|
|
2838
|
+
title: `import-csv-${opts.tableName}`,
|
|
2839
|
+
description: `CSV import into table ${opts.tableName}`,
|
|
2840
|
+
trigger_config: { operator: "@internal/trigger/manual" },
|
|
2841
|
+
steps: [
|
|
2842
|
+
{ id: "step-trigger", title: "Trigger", operator: "@trigger/manual", parameters: {} },
|
|
2843
|
+
{
|
|
2844
|
+
id: "step-write",
|
|
2845
|
+
title: "Write to Database",
|
|
2846
|
+
operator: "@internal/database/write",
|
|
2847
|
+
parameters: {
|
|
2848
|
+
datasource_type: opts.datasourceType,
|
|
2849
|
+
datasource_id: opts.catalogId,
|
|
2850
|
+
table_name: opts.tableName,
|
|
2851
|
+
table_exist: opts.tableExist,
|
|
2852
|
+
operate_type: "append",
|
|
2853
|
+
data: opts.data,
|
|
2854
|
+
sync_model_fields: opts.fieldMappings
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
]
|
|
2858
|
+
};
|
|
2859
|
+
}
|
|
2860
|
+
async function resolveFiles(pattern) {
|
|
2861
|
+
const out = [];
|
|
2862
|
+
for (const part of pattern.split(",").map((p) => p.trim()).filter(Boolean)) {
|
|
2863
|
+
if (part.includes("*") || part.includes("?")) {
|
|
2864
|
+
for await (const entry of glob(part)) {
|
|
2865
|
+
const p = String(entry);
|
|
2866
|
+
if (/\.csv$/i.test(p)) out.push(resolve2(p));
|
|
2867
|
+
}
|
|
2868
|
+
} else {
|
|
2869
|
+
statSync(resolve2(part));
|
|
2870
|
+
out.push(resolve2(part));
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
if (out.length === 0) throw new Error(`No CSV files matched: ${pattern}`);
|
|
2874
|
+
return out;
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
// src/utils/pk-detection.ts
|
|
2878
|
+
var PK_NAME_HINTS = ["id", "_id", "pk"];
|
|
2879
|
+
var DISPLAY_HINTS = ["name", "title", "label", "display_name", "description"];
|
|
2880
|
+
function detectPrimaryKey(table, rows) {
|
|
2881
|
+
if (!rows || rows.length === 0) return { pk: null, candidates: [], sampleSize: 0 };
|
|
2882
|
+
const candidates = table.columns.map((col) => ({ name: col.name, cardinality: new Set(rows.map((r) => r[col.name])).size })).sort((a, b) => b.cardinality - a.cardinality);
|
|
2883
|
+
const full = candidates.filter((c) => c.cardinality === rows.length);
|
|
2884
|
+
if (full.length === 0) return { pk: null, candidates, sampleSize: rows.length };
|
|
2885
|
+
const named = full.find((c) => {
|
|
2886
|
+
const lower = c.name.toLowerCase();
|
|
2887
|
+
return PK_NAME_HINTS.some((h) => lower === h || lower.endsWith(`_${h}`));
|
|
2888
|
+
});
|
|
2889
|
+
return { pk: named?.name ?? full[0].name, candidates, sampleSize: rows.length };
|
|
2890
|
+
}
|
|
2891
|
+
function collectSchemaPks(table) {
|
|
2892
|
+
const colNames = new Set(table.columns.map((c) => c.name));
|
|
2893
|
+
if (Array.isArray(table.primaryKeys) && table.primaryKeys.length > 0) {
|
|
2894
|
+
return table.primaryKeys.filter((n) => colNames.has(n));
|
|
2895
|
+
}
|
|
2896
|
+
return table.columns.filter((c) => c.isPrimaryKey === true).map((c) => c.name);
|
|
2897
|
+
}
|
|
2898
|
+
function resolvePrimaryKey(table, sampleRows2, override) {
|
|
2899
|
+
if (override) return { pk: override, source: "override" };
|
|
2900
|
+
const schemaPks = collectSchemaPks(table);
|
|
2901
|
+
if (schemaPks.length === 1) return { pk: schemaPks[0], source: "schema" };
|
|
2902
|
+
if (schemaPks.length > 1) return { pk: null, source: "ambiguous", ambiguous: schemaPks };
|
|
2903
|
+
const sample = detectPrimaryKey(table, sampleRows2);
|
|
2904
|
+
return {
|
|
2905
|
+
pk: sample.pk,
|
|
2906
|
+
source: "sample",
|
|
2907
|
+
candidates: sample.candidates,
|
|
2908
|
+
sampleSize: sample.sampleSize
|
|
2909
|
+
};
|
|
2910
|
+
}
|
|
2911
|
+
function detectDisplayKey(table, primaryKey) {
|
|
2912
|
+
for (const col of table.columns) {
|
|
2913
|
+
if (DISPLAY_HINTS.some((h) => col.name.toLowerCase().includes(h))) return col.name;
|
|
2914
|
+
}
|
|
2915
|
+
return primaryKey;
|
|
2916
|
+
}
|
|
2917
|
+
function parsePkMap(input) {
|
|
2918
|
+
const out = {};
|
|
2919
|
+
for (const pair of input.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
2920
|
+
const idx = pair.indexOf(":");
|
|
2921
|
+
if (idx <= 0 || idx >= pair.length - 1) {
|
|
2922
|
+
throw new Error(`Invalid --pk-map entry '${pair}'. Expected '<table>:<field>[,...]'`);
|
|
2923
|
+
}
|
|
2924
|
+
out[pair.slice(0, idx).trim()] = pair.slice(idx + 1).trim();
|
|
2925
|
+
}
|
|
2926
|
+
return out;
|
|
2927
|
+
}
|
|
2928
|
+
function formatPkDetectionError(tableName, result) {
|
|
2929
|
+
const lines = [`Cannot auto-detect a primary key for table '${tableName}'.`];
|
|
2930
|
+
if (result.sampleSize === 0) {
|
|
2931
|
+
lines.push(" No sample data available \u2014 pass --pk-map <table>:<column>.");
|
|
2932
|
+
} else {
|
|
2933
|
+
lines.push(` No column is unique across the ${result.sampleSize}-row sample.`);
|
|
2934
|
+
lines.push(" Top candidates by cardinality:");
|
|
2935
|
+
for (const c of result.candidates.slice(0, 5)) {
|
|
2936
|
+
lines.push(` ${c.name} ${c.cardinality} unique`);
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
lines.push("", ` Specify explicitly: --pk-map ${tableName}:<column>`);
|
|
2940
|
+
return lines.join("\n");
|
|
2941
|
+
}
|
|
2942
|
+
|
|
2943
|
+
// src/resources/bkn-create.ts
|
|
2944
|
+
function columnIsPk(col) {
|
|
2945
|
+
if (col.is_primary_key === true) return true;
|
|
2946
|
+
return typeof col.column_key === "string" && col.column_key.toUpperCase() === "PRI";
|
|
2947
|
+
}
|
|
2948
|
+
function toTableInfo(detail) {
|
|
2949
|
+
const raw = detail.source_metadata?.columns ?? [];
|
|
2950
|
+
const tablePks = Array.isArray(detail.primary_keys) ? detail.primary_keys.filter((x) => typeof x === "string") : [];
|
|
2951
|
+
const columns = raw.map((c) => {
|
|
2952
|
+
const name = String(c.name ?? c.field_name ?? "");
|
|
2953
|
+
const flagged = columnIsPk(c) || tablePks.includes(name);
|
|
2954
|
+
return {
|
|
2955
|
+
name,
|
|
2956
|
+
type: String(c.type ?? c.field_type ?? "varchar"),
|
|
2957
|
+
...flagged ? { isPrimaryKey: true } : {}
|
|
2958
|
+
};
|
|
2959
|
+
});
|
|
2960
|
+
const pks = tablePks.length > 0 ? tablePks : columns.filter((c) => c.isPrimaryKey).map((c) => c.name);
|
|
2961
|
+
return {
|
|
2962
|
+
name: String(detail.name ?? ""),
|
|
2963
|
+
columns,
|
|
2964
|
+
...pks.length > 0 ? { primaryKeys: pks } : {}
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
function asArray(v) {
|
|
2968
|
+
if (Array.isArray(v)) return v;
|
|
2969
|
+
if (v && typeof v === "object") {
|
|
2970
|
+
const o = v;
|
|
2971
|
+
if (Array.isArray(o.entries)) return o.entries;
|
|
2972
|
+
if (Array.isArray(o.data)) return o.data;
|
|
2973
|
+
}
|
|
2974
|
+
return [];
|
|
2975
|
+
}
|
|
2976
|
+
async function sampleRows(ctx, resourceId) {
|
|
2977
|
+
try {
|
|
2978
|
+
const res = await queryResource(ctx, resourceId, { limit: 100 });
|
|
2979
|
+
return asArray(res);
|
|
2980
|
+
} catch {
|
|
2981
|
+
return [];
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
async function createFromCatalog(ctx, opts) {
|
|
2985
|
+
const log = opts.onProgress ?? (() => {
|
|
2986
|
+
});
|
|
2987
|
+
const pkMap = opts.pkMap ?? {};
|
|
2988
|
+
const listTables = () => listResources2(ctx, { datasourceId: opts.catalogId, category: "table" }).then(asArray);
|
|
2989
|
+
let summaries = await listTables();
|
|
2990
|
+
if (summaries.length === 0) {
|
|
2991
|
+
log("No tables found; scanning catalog metadata...");
|
|
2992
|
+
await discoverCatalog(ctx, opts.catalogId, true);
|
|
2993
|
+
summaries = await listTables();
|
|
2994
|
+
}
|
|
2995
|
+
if (summaries.length === 0) throw new Error("No tables available in catalog after scan.");
|
|
2996
|
+
const details = await Promise.all(
|
|
2997
|
+
summaries.map(async (s) => {
|
|
2998
|
+
const id = s.id;
|
|
2999
|
+
const detail = id ? await getResource(ctx, id) : s;
|
|
3000
|
+
return toTableInfo(detail);
|
|
3001
|
+
})
|
|
3002
|
+
);
|
|
3003
|
+
const targets = opts.tables && opts.tables.length > 0 ? details.filter((t) => opts.tables?.includes(t.name)) : details;
|
|
3004
|
+
if (targets.length === 0) throw new Error("No matching tables to build from.");
|
|
3005
|
+
for (const name of Object.keys(pkMap)) {
|
|
3006
|
+
if (!targets.some((t) => t.name === name)) {
|
|
3007
|
+
throw new Error(`--pk-map references unknown table '${name}'.`);
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
const tablePk = {};
|
|
3011
|
+
for (const t of targets) {
|
|
3012
|
+
const override = pkMap[t.name];
|
|
3013
|
+
if (override && !t.columns.some((c) => c.name === override)) {
|
|
3014
|
+
throw new Error(
|
|
3015
|
+
`--pk-map '${override}' for table '${t.name}' is not a column. Columns: ${t.columns.map((c) => c.name).join(", ")}`
|
|
3016
|
+
);
|
|
3017
|
+
}
|
|
3018
|
+
let res = resolvePrimaryKey(t, opts.sampleRows?.[t.name], override);
|
|
3019
|
+
if (res.pk === null && res.source === "sample") {
|
|
3020
|
+
const summary = summaries.find((s) => s.name === t.name);
|
|
3021
|
+
const rows = summary?.id ? await sampleRows(ctx, summary.id) : [];
|
|
3022
|
+
res = resolvePrimaryKey(t, rows, override);
|
|
3023
|
+
}
|
|
3024
|
+
if (res.source === "ambiguous") {
|
|
3025
|
+
throw new Error(
|
|
3026
|
+
`Table '${t.name}' has a composite primary key (${(res.ambiguous ?? []).join(", ")}). BKN object types take one key \u2014 pick with --pk-map ${t.name}:<column>.`
|
|
3027
|
+
);
|
|
3028
|
+
}
|
|
3029
|
+
if (!res.pk) {
|
|
3030
|
+
throw new Error(
|
|
3031
|
+
formatPkDetectionError(t.name, {
|
|
3032
|
+
candidates: res.candidates ?? [],
|
|
3033
|
+
sampleSize: res.sampleSize ?? 0
|
|
3034
|
+
})
|
|
3035
|
+
);
|
|
3036
|
+
}
|
|
3037
|
+
tablePk[t.name] = res.pk;
|
|
3038
|
+
}
|
|
3039
|
+
log(`Creating resources for ${targets.length} table(s)...`);
|
|
3040
|
+
const viewMap = {};
|
|
3041
|
+
for (const t of targets) {
|
|
3042
|
+
const found = asArray(
|
|
3043
|
+
await findResource(ctx, t.name, { datasourceId: opts.catalogId, exact: true })
|
|
3044
|
+
);
|
|
3045
|
+
const existingId = found[0]?.id;
|
|
3046
|
+
if (existingId) {
|
|
3047
|
+
viewMap[t.name] = existingId;
|
|
3048
|
+
} else {
|
|
3049
|
+
const created = await createResource(ctx, {
|
|
3050
|
+
name: t.name,
|
|
3051
|
+
catalogId: opts.catalogId,
|
|
3052
|
+
sourceIdentifier: t.name,
|
|
3053
|
+
fields: t.columns.map((c) => ({ name: c.name, type: c.type }))
|
|
3054
|
+
});
|
|
3055
|
+
viewMap[t.name] = String(created.id ?? "");
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
const knCreated = await createKnowledgeNetwork(ctx, { name: opts.name });
|
|
3059
|
+
const knItem = Array.isArray(knCreated) ? knCreated[0] : knCreated;
|
|
3060
|
+
const knId = String(knItem?.id ?? "");
|
|
3061
|
+
log(`Knowledge network created: ${knId}`);
|
|
3062
|
+
let rollbackKn = knId;
|
|
3063
|
+
try {
|
|
3064
|
+
const entries = targets.map((t) => {
|
|
3065
|
+
const pk = tablePk[t.name];
|
|
3066
|
+
return {
|
|
3067
|
+
branch: "main",
|
|
3068
|
+
name: t.name,
|
|
3069
|
+
data_source: { type: "resource", id: viewMap[t.name] },
|
|
3070
|
+
primary_keys: [pk],
|
|
3071
|
+
display_key: detectDisplayKey(t, pk),
|
|
3072
|
+
data_properties: t.columns.map((c) => ({
|
|
3073
|
+
name: c.name,
|
|
3074
|
+
display_name: c.name,
|
|
3075
|
+
type: "string",
|
|
3076
|
+
mapped_field: { name: c.name, type: c.type || "varchar" }
|
|
3077
|
+
}))
|
|
3078
|
+
};
|
|
3079
|
+
});
|
|
3080
|
+
log(`Creating ${entries.length} object type(s)...`);
|
|
3081
|
+
await createObjectTypes(ctx, knId, entries);
|
|
3082
|
+
const builds = [];
|
|
3083
|
+
if (opts.build) {
|
|
3084
|
+
log("Submitting build tasks...");
|
|
3085
|
+
for (const t of targets) {
|
|
3086
|
+
const task = await createBuildTask(ctx, {
|
|
3087
|
+
resource_id: viewMap[t.name],
|
|
3088
|
+
mode: "batch",
|
|
3089
|
+
build_key_fields: [tablePk[t.name]]
|
|
3090
|
+
});
|
|
3091
|
+
builds.push({ table: t.name, taskId: String(task.id ?? "") });
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
rollbackKn = void 0;
|
|
3095
|
+
return {
|
|
3096
|
+
kn_id: knId,
|
|
3097
|
+
kn_name: opts.name,
|
|
3098
|
+
object_types: targets.map((t) => ({ name: t.name, pk: tablePk[t.name] })),
|
|
3099
|
+
build_tasks: builds
|
|
3100
|
+
};
|
|
3101
|
+
} finally {
|
|
3102
|
+
if (rollbackKn !== void 0) {
|
|
3103
|
+
if (opts.noRollback) {
|
|
3104
|
+
log(`Leaving partial KN ${rollbackKn} in place (--no-rollback).`);
|
|
3105
|
+
} else {
|
|
3106
|
+
log(`Rolling back KN ${rollbackKn}...`);
|
|
3107
|
+
try {
|
|
3108
|
+
await deleteKnowledgeNetwork(ctx, rollbackKn);
|
|
3109
|
+
} catch {
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
async function importCsvToCatalog(ctx, opts) {
|
|
3116
|
+
const log = opts.onProgress ?? (() => {
|
|
3117
|
+
});
|
|
3118
|
+
const batchSize = opts.batchSize ?? 500;
|
|
3119
|
+
const paths = await resolveFiles(opts.files);
|
|
3120
|
+
const catalog = await getCatalog(ctx, opts.catalogId);
|
|
3121
|
+
const datasourceType = catalog.connector_type ?? catalog.type ?? "";
|
|
3122
|
+
const tables = [];
|
|
3123
|
+
const failed = [];
|
|
3124
|
+
const sampleRows2 = {};
|
|
3125
|
+
for (const path of paths) {
|
|
3126
|
+
const tableName = buildTableName(path, opts.tablePrefix ?? "");
|
|
3127
|
+
const { headers: headers2, rows } = await parseCsvFile(path);
|
|
3128
|
+
if (headers2.length === 0 || rows.length === 0) {
|
|
3129
|
+
log(`Skipping ${tableName} (no headers/rows).`);
|
|
3130
|
+
failed.push(tableName);
|
|
3131
|
+
continue;
|
|
3132
|
+
}
|
|
3133
|
+
const fieldMappings = buildFieldMappings(headers2);
|
|
3134
|
+
const batches = splitBatches(rows, batchSize);
|
|
3135
|
+
try {
|
|
3136
|
+
for (let i = 0; i < batches.length; i += 1) {
|
|
3137
|
+
log(`[${tableName}] batch ${i + 1}/${batches.length} (${batches[i]?.length} rows)...`);
|
|
3138
|
+
await executeDataflow(
|
|
3139
|
+
ctx,
|
|
3140
|
+
buildImportDag({
|
|
3141
|
+
catalogId: opts.catalogId,
|
|
3142
|
+
datasourceType,
|
|
3143
|
+
tableName,
|
|
3144
|
+
tableExist: i > 0,
|
|
3145
|
+
data: batches[i],
|
|
3146
|
+
fieldMappings
|
|
3147
|
+
})
|
|
3148
|
+
);
|
|
3149
|
+
}
|
|
3150
|
+
tables.push(tableName);
|
|
3151
|
+
sampleRows2[tableName] = rows.slice(0, 100);
|
|
3152
|
+
} catch (e) {
|
|
3153
|
+
log(`[${tableName}] import failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
3154
|
+
failed.push(tableName);
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
await discoverCatalog(ctx, opts.catalogId, true).catch(() => {
|
|
3158
|
+
});
|
|
3159
|
+
return { tables, failed, sampleRows: sampleRows2 };
|
|
3160
|
+
}
|
|
3161
|
+
async function createFromCsv(ctx, opts) {
|
|
3162
|
+
const log = opts.onProgress ?? (() => {
|
|
3163
|
+
});
|
|
3164
|
+
log("Phase 1: importing CSVs...");
|
|
3165
|
+
const imported = await importCsvToCatalog(ctx, {
|
|
3166
|
+
catalogId: opts.catalogId,
|
|
3167
|
+
files: opts.files,
|
|
3168
|
+
tablePrefix: opts.tablePrefix,
|
|
3169
|
+
batchSize: opts.batchSize,
|
|
3170
|
+
onProgress: log
|
|
3171
|
+
});
|
|
3172
|
+
if (imported.tables.length === 0) {
|
|
3173
|
+
throw new Error(`No tables imported (failed: ${imported.failed.join(", ") || "none"}).`);
|
|
3174
|
+
}
|
|
3175
|
+
log(`Phase 2: building KN from ${imported.tables.length} table(s)...`);
|
|
3176
|
+
const result = await createFromCatalog(ctx, {
|
|
3177
|
+
catalogId: opts.catalogId,
|
|
3178
|
+
name: opts.name,
|
|
3179
|
+
tables: opts.tables && opts.tables.length > 0 ? opts.tables : imported.tables,
|
|
3180
|
+
pkMap: opts.pkMap,
|
|
3181
|
+
build: opts.build,
|
|
3182
|
+
noRollback: opts.noRollback,
|
|
3183
|
+
sampleRows: imported.sampleRows,
|
|
3184
|
+
onProgress: log
|
|
3185
|
+
});
|
|
3186
|
+
return {
|
|
3187
|
+
imported_tables: imported.tables,
|
|
3188
|
+
failed_imports: imported.failed,
|
|
3189
|
+
...result
|
|
3190
|
+
};
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
// src/resources/knowledge-networks.ts
|
|
3194
|
+
function kn(ctx) {
|
|
3195
|
+
return {
|
|
3196
|
+
list: (opts) => listKnowledgeNetworks(ctx, opts),
|
|
3197
|
+
get: (knId, opts) => getKnowledgeNetwork(ctx, knId, opts),
|
|
3198
|
+
search: (knId, query, opts) => semanticSearch(ctx, knId, query, opts),
|
|
3199
|
+
create: (opts) => createKnowledgeNetwork(ctx, opts),
|
|
3200
|
+
update: (knId, body) => updateKnowledgeNetwork(ctx, knId, body),
|
|
3201
|
+
delete: (knId) => deleteKnowledgeNetwork(ctx, knId),
|
|
3202
|
+
subgraph: (knId, body) => querySubgraph(ctx, knId, body),
|
|
3203
|
+
actionLogs: (knId, opts) => listActionLogs(ctx, knId, opts),
|
|
3204
|
+
actionLog: (knId, logId) => getActionLog(ctx, knId, logId),
|
|
3205
|
+
cancelActionLog: (knId, logId) => cancelActionLog(ctx, knId, logId),
|
|
3206
|
+
actionExecution: (knId, executionId) => getActionExecution(ctx, knId, executionId),
|
|
3207
|
+
metricQuery: (knId, metricId, body) => queryMetricData(ctx, knId, metricId, body),
|
|
3208
|
+
metricDryRun: (knId, body) => dryRunMetric(ctx, knId, body),
|
|
3209
|
+
metricList: (knId, opts) => listMetrics(ctx, knId, opts),
|
|
3210
|
+
metricGet: (knId, metricId) => getMetric(ctx, knId, metricId),
|
|
3211
|
+
metricCreate: (knId, body) => createMetric(ctx, knId, body),
|
|
3212
|
+
metricUpdate: (knId, metricId, body) => updateMetric(ctx, knId, metricId, body),
|
|
3213
|
+
metricDelete: (knId, metricId) => deleteMetric(ctx, knId, metricId),
|
|
3214
|
+
metricSearch: (knId, body) => searchMetrics(ctx, knId, body),
|
|
3215
|
+
metricValidate: (knId, body) => validateMetric(ctx, knId, body),
|
|
3216
|
+
objectTypes: (knId, opts) => listObjectTypes(ctx, knId, opts),
|
|
3217
|
+
objectTypeQuery: (knId, otId, body) => queryObjectTypeInstances(ctx, knId, otId, body),
|
|
3218
|
+
objectTypeProperties: (knId, otId) => getObjectTypeProperties(ctx, knId, otId),
|
|
3219
|
+
objectTypeGet: (knId, id) => getSchemaItem(ctx, knId, "object-types", id),
|
|
3220
|
+
objectTypeCreate: (knId, body) => createSchemaItem(ctx, knId, "object-types", body),
|
|
3221
|
+
objectTypeUpdate: (knId, id, body) => updateSchemaItem(ctx, knId, "object-types", id, body),
|
|
3222
|
+
objectTypeDelete: (knId, id) => deleteSchemaItem(ctx, knId, "object-types", id),
|
|
3223
|
+
relationTypes: (knId, opts) => listRelationTypes(ctx, knId, opts),
|
|
3224
|
+
relationTypeGet: (knId, id) => getSchemaItem(ctx, knId, "relation-types", id),
|
|
3225
|
+
relationTypeCreate: (knId, body) => createSchemaItem(ctx, knId, "relation-types", body),
|
|
3226
|
+
relationTypeUpdate: (knId, id, body) => updateSchemaItem(ctx, knId, "relation-types", id, body),
|
|
3227
|
+
relationTypeDelete: (knId, id) => deleteSchemaItem(ctx, knId, "relation-types", id),
|
|
3228
|
+
actionTypes: (knId, opts) => listActionTypes(ctx, knId, opts),
|
|
3229
|
+
actionTypeQuery: (knId, atId, body) => queryActionType(ctx, knId, atId, body),
|
|
3230
|
+
actionTypeExecute: (knId, atId, body) => executeActionType(ctx, knId, atId, body),
|
|
3231
|
+
actionTypeInputs: (knId, atId) => getActionTypeInputs(ctx, knId, atId),
|
|
3232
|
+
actionTypeGet: (knId, id) => getSchemaItem(ctx, knId, "action-types", id),
|
|
3233
|
+
conceptGroups: (knId) => listConceptGroups(ctx, knId),
|
|
3234
|
+
conceptGroup: (knId, cgId) => getConceptGroup(ctx, knId, cgId),
|
|
3235
|
+
conceptGroupCreate: (knId, body) => createConceptGroup(ctx, knId, body),
|
|
3236
|
+
conceptGroupUpdate: (knId, cgId, body) => updateConceptGroup(ctx, knId, cgId, body),
|
|
3237
|
+
conceptGroupDelete: (knId, cgId) => deleteConceptGroup(ctx, knId, cgId),
|
|
3238
|
+
conceptGroupAddMembers: (knId, cgId, body) => addConceptGroupMembers(ctx, knId, cgId, body),
|
|
3239
|
+
conceptGroupRemoveMembers: (knId, cgId, otIds) => removeConceptGroupMembers(ctx, knId, cgId, otIds),
|
|
3240
|
+
actionSchedules: (knId) => listActionSchedules(ctx, knId),
|
|
3241
|
+
actionSchedule: (knId, scheduleId) => getActionSchedule(ctx, knId, scheduleId),
|
|
3242
|
+
actionScheduleCreate: (knId, body) => createActionSchedule(ctx, knId, body),
|
|
3243
|
+
actionScheduleUpdate: (knId, scheduleId, body) => updateActionSchedule(ctx, knId, scheduleId, body),
|
|
3244
|
+
actionScheduleSetStatus: (knId, scheduleId, body) => setActionScheduleStatus(ctx, knId, scheduleId, body),
|
|
3245
|
+
actionScheduleDelete: (knId, ids) => deleteActionSchedules(ctx, knId, ids),
|
|
3246
|
+
jobs: (knId) => listJobs(ctx, knId),
|
|
3247
|
+
job: (knId, jobId) => getJob(ctx, knId, jobId),
|
|
3248
|
+
jobTasks: (knId, jobId) => getJobTasks(ctx, knId, jobId),
|
|
3249
|
+
jobDelete: (knId, ids) => deleteJobs(ctx, knId, ids),
|
|
3250
|
+
relationTypePaths: (knId, body) => relationTypePaths(ctx, knId, body),
|
|
3251
|
+
bknResources: () => listBknResources(ctx),
|
|
3252
|
+
createFromCatalog: (opts) => createFromCatalog(ctx, opts),
|
|
3253
|
+
createFromCsv: (opts) => createFromCsv(ctx, opts),
|
|
3254
|
+
/** Pack a local BKN directory and upload it as a knowledge network. */
|
|
3255
|
+
push: (dir, opts) => uploadBkn(ctx, packDirectoryToTar(dir), opts),
|
|
3256
|
+
/** Download a knowledge network and extract it into a local directory. */
|
|
3257
|
+
pull: async (knId, dir, opts) => {
|
|
3258
|
+
const tar = await downloadBkn(ctx, knId, opts);
|
|
3259
|
+
mkdirSync2(resolve3(dir), { recursive: true });
|
|
3260
|
+
extractTarToDirectory(tar, dir);
|
|
3261
|
+
return { knId, dir: resolve3(dir), bytes: tar.length };
|
|
3262
|
+
}
|
|
3263
|
+
};
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
// src/api/models.ts
|
|
3267
|
+
var MANAGER = "/api/mf-model-manager/v1";
|
|
3268
|
+
var API = "/api/mf-model-api/v1";
|
|
3269
|
+
function listQuery(opts) {
|
|
3270
|
+
return {
|
|
3271
|
+
page: String(opts.page ?? 1),
|
|
3272
|
+
size: String(opts.limit ?? 30),
|
|
3273
|
+
name: opts.name ?? "",
|
|
3274
|
+
model_type: opts.modelType ?? ""
|
|
3275
|
+
};
|
|
3276
|
+
}
|
|
3277
|
+
function listLlmModels(ctx, opts = {}) {
|
|
3278
|
+
return request(ctx, `${MANAGER}/llm/list`, { query: listQuery(opts) });
|
|
3279
|
+
}
|
|
3280
|
+
function getLlmModel(ctx, modelId) {
|
|
3281
|
+
return request(ctx, `${MANAGER}/llm/get`, { query: { model_id: modelId } });
|
|
3282
|
+
}
|
|
3283
|
+
function listSmallModels(ctx, opts = {}) {
|
|
3284
|
+
return request(ctx, `${MANAGER}/small-model/list`, { query: listQuery(opts) });
|
|
3285
|
+
}
|
|
3286
|
+
function getSmallModel(ctx, modelId) {
|
|
3287
|
+
return request(ctx, `${MANAGER}/small-model/get`, { query: { model_id: modelId } });
|
|
3288
|
+
}
|
|
3289
|
+
function chatCompletions(ctx, model, messages) {
|
|
3290
|
+
return request(ctx, `${API}/chat/completions`, {
|
|
3291
|
+
method: "POST",
|
|
3292
|
+
body: { model, messages, stream: false }
|
|
3293
|
+
});
|
|
3294
|
+
}
|
|
3295
|
+
function deltaContent(chunk) {
|
|
3296
|
+
const choices = chunk.choices;
|
|
3297
|
+
const c = choices?.[0]?.delta?.content;
|
|
3298
|
+
return typeof c === "string" ? c : "";
|
|
3299
|
+
}
|
|
3300
|
+
async function chatCompletionsStream(ctx, model, messages, onDelta) {
|
|
3301
|
+
applyTls(ctx);
|
|
3302
|
+
const res = await fetch(`${ctx.baseUrl}${API}/chat/completions`, {
|
|
3303
|
+
method: "POST",
|
|
3304
|
+
headers: {
|
|
3305
|
+
...buildHeaders(ctx),
|
|
3306
|
+
"content-type": "application/json",
|
|
3307
|
+
accept: "text/event-stream"
|
|
3308
|
+
},
|
|
3309
|
+
body: JSON.stringify({ model, messages, stream: true })
|
|
3310
|
+
});
|
|
3311
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, await res.text());
|
|
3312
|
+
const reader = res.body?.getReader();
|
|
3313
|
+
if (!reader) throw new Error("No response body for stream");
|
|
3314
|
+
const decoder = new TextDecoder();
|
|
3315
|
+
let buffer = "";
|
|
3316
|
+
let out = "";
|
|
3317
|
+
const handle = (line) => {
|
|
3318
|
+
const trimmed = line.trimEnd();
|
|
3319
|
+
if (!trimmed.startsWith("data:")) return;
|
|
3320
|
+
const payload = trimmed.slice(5).trim();
|
|
3321
|
+
if (payload === "[DONE]" || payload === "") return;
|
|
3322
|
+
try {
|
|
3323
|
+
const text = deltaContent(JSON.parse(payload));
|
|
3324
|
+
if (text) {
|
|
3325
|
+
out += text;
|
|
3326
|
+
onDelta(text);
|
|
3327
|
+
}
|
|
3328
|
+
} catch {
|
|
3329
|
+
}
|
|
3330
|
+
};
|
|
3331
|
+
while (true) {
|
|
3332
|
+
const { done, value } = await reader.read();
|
|
3333
|
+
if (done) break;
|
|
3334
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3335
|
+
const lines = buffer.split("\n");
|
|
3336
|
+
buffer = lines.pop() ?? "";
|
|
3337
|
+
for (const ln of lines) handle(ln);
|
|
3338
|
+
}
|
|
3339
|
+
if (buffer.trim()) handle(buffer);
|
|
3340
|
+
return out;
|
|
3341
|
+
}
|
|
3342
|
+
function embeddings(ctx, model, input) {
|
|
3343
|
+
return request(ctx, `${API}/small-model/embeddings`, { method: "POST", body: { model, input } });
|
|
3344
|
+
}
|
|
3345
|
+
function addModel(ctx, kind, body) {
|
|
3346
|
+
return request(ctx, `${MANAGER}/${kind}/add`, { method: "POST", body });
|
|
3347
|
+
}
|
|
3348
|
+
function editModel(ctx, kind, body) {
|
|
3349
|
+
return request(ctx, `${MANAGER}/${kind}/edit`, { method: "POST", body });
|
|
3350
|
+
}
|
|
3351
|
+
function deleteModels(ctx, kind, modelIds) {
|
|
3352
|
+
return request(ctx, `${MANAGER}/${kind}/delete`, {
|
|
3353
|
+
method: "POST",
|
|
3354
|
+
body: { model_ids: modelIds }
|
|
3355
|
+
});
|
|
3356
|
+
}
|
|
3357
|
+
function testModel(ctx, kind, body) {
|
|
3358
|
+
return request(ctx, `${MANAGER}/${kind}/test`, { method: "POST", body });
|
|
3359
|
+
}
|
|
3360
|
+
function rerank(ctx, model, query, documents) {
|
|
3361
|
+
return request(ctx, `${API}/small-model/reranker`, {
|
|
3362
|
+
method: "POST",
|
|
3363
|
+
body: { model, query, documents }
|
|
3364
|
+
});
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
// src/resources/models.ts
|
|
3368
|
+
function models(ctx) {
|
|
3369
|
+
return {
|
|
3370
|
+
llm: {
|
|
3371
|
+
list: (opts) => listLlmModels(ctx, opts),
|
|
3372
|
+
get: (modelId) => getLlmModel(ctx, modelId),
|
|
3373
|
+
chat: (modelId, messages) => chatCompletions(ctx, modelId, messages),
|
|
3374
|
+
chatStream: (modelId, messages, onDelta) => chatCompletionsStream(ctx, modelId, messages, onDelta),
|
|
3375
|
+
add: (body) => addModel(ctx, "llm", body),
|
|
3376
|
+
edit: (body) => editModel(ctx, "llm", body),
|
|
3377
|
+
delete: (modelIds) => deleteModels(ctx, "llm", modelIds),
|
|
3378
|
+
test: (body) => testModel(ctx, "llm", body)
|
|
3379
|
+
},
|
|
3380
|
+
small: {
|
|
3381
|
+
list: (opts) => listSmallModels(ctx, opts),
|
|
3382
|
+
get: (modelId) => getSmallModel(ctx, modelId),
|
|
3383
|
+
embeddings: (modelId, input) => embeddings(ctx, modelId, input),
|
|
3384
|
+
rerank: (modelId, query, documents) => rerank(ctx, modelId, query, documents),
|
|
3385
|
+
add: (body) => addModel(ctx, "small-model", body),
|
|
3386
|
+
edit: (body) => editModel(ctx, "small-model", body),
|
|
3387
|
+
delete: (modelIds) => deleteModels(ctx, "small-model", modelIds),
|
|
3388
|
+
test: (body) => testModel(ctx, "small-model", body)
|
|
3389
|
+
}
|
|
3390
|
+
};
|
|
3391
|
+
}
|
|
3392
|
+
|
|
3393
|
+
// src/resources/resources.ts
|
|
3394
|
+
function resources(ctx) {
|
|
3395
|
+
return {
|
|
3396
|
+
list: (opts) => listResources2(ctx, opts),
|
|
3397
|
+
get: (id) => getResource(ctx, id),
|
|
3398
|
+
delete: (id) => deleteResource(ctx, id),
|
|
3399
|
+
find: (name, opts) => findResource(ctx, name, opts),
|
|
3400
|
+
query: (id, opts) => queryResource(ctx, id, opts)
|
|
3401
|
+
};
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
// src/resources/skills.ts
|
|
3405
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
3406
|
+
import { basename as basename2, dirname as dirname2, resolve as resolve5 } from "path";
|
|
3407
|
+
|
|
3408
|
+
// src/api/skills.ts
|
|
3409
|
+
var BASE5 = "/api/agent-operator-integration/v1";
|
|
3410
|
+
async function registerSkillZip(ctx, bytes, opts = {}) {
|
|
3411
|
+
applyTls(ctx);
|
|
3412
|
+
const form = new FormData();
|
|
3413
|
+
form.set("file_type", "zip");
|
|
3414
|
+
form.set("file", new Blob([bytes]), opts.filename ?? "skill.zip");
|
|
3415
|
+
if (opts.source) form.set("source", opts.source);
|
|
3416
|
+
if (opts.extendInfo) form.set("extend_info", JSON.stringify(opts.extendInfo));
|
|
3417
|
+
const res = await fetch(`${ctx.baseUrl}${BASE5}/skills`, {
|
|
3418
|
+
method: "POST",
|
|
3419
|
+
headers: buildHeaders(ctx),
|
|
3420
|
+
body: form
|
|
3421
|
+
});
|
|
3422
|
+
const text = await res.text();
|
|
3423
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
3424
|
+
return text ? JSON.parse(text) : void 0;
|
|
3425
|
+
}
|
|
3426
|
+
async function updateSkillPackageZip(ctx, skillId, bytes, filename = "skill.zip") {
|
|
3427
|
+
applyTls(ctx);
|
|
3428
|
+
const form = new FormData();
|
|
3429
|
+
form.set("file_type", "zip");
|
|
3430
|
+
form.set("file", new Blob([bytes]), filename);
|
|
3431
|
+
const res = await fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/package`, {
|
|
3432
|
+
method: "PUT",
|
|
3433
|
+
headers: buildHeaders(ctx),
|
|
3434
|
+
body: form
|
|
3435
|
+
});
|
|
3436
|
+
const text = await res.text();
|
|
3437
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
3438
|
+
return text ? JSON.parse(text) : void 0;
|
|
3439
|
+
}
|
|
3440
|
+
async function downloadSkill(ctx, skillId) {
|
|
3441
|
+
applyTls(ctx);
|
|
3442
|
+
const res = await fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/download`, {
|
|
3443
|
+
headers: buildHeaders(ctx)
|
|
3444
|
+
});
|
|
3445
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, await res.text());
|
|
3446
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
3447
|
+
}
|
|
3448
|
+
function updateSkillMetadata(ctx, skillId, body) {
|
|
3449
|
+
return request(ctx, `${BASE5}/skills/${encodeURIComponent(skillId)}`, { method: "PUT", body });
|
|
3450
|
+
}
|
|
3451
|
+
function republishSkillVersion(ctx, skillId, version) {
|
|
3452
|
+
return request(ctx, `${BASE5}/skills/${encodeURIComponent(skillId)}/history/republish`, {
|
|
3453
|
+
method: "POST",
|
|
3454
|
+
body: { version }
|
|
3455
|
+
});
|
|
3456
|
+
}
|
|
3457
|
+
function publishSkillVersion(ctx, skillId, version) {
|
|
3458
|
+
return request(ctx, `${BASE5}/skills/${encodeURIComponent(skillId)}/history/publish`, {
|
|
3459
|
+
method: "POST",
|
|
3460
|
+
body: { version }
|
|
3461
|
+
});
|
|
3462
|
+
}
|
|
3463
|
+
function listQuery2(opts) {
|
|
3464
|
+
return {
|
|
3465
|
+
page: opts.page ?? 1,
|
|
3466
|
+
page_size: opts.pageSize ?? 30,
|
|
3467
|
+
name: opts.name || void 0,
|
|
3468
|
+
source: opts.source || void 0,
|
|
3469
|
+
status: opts.status || void 0,
|
|
3470
|
+
create_user: opts.createUser || void 0
|
|
3471
|
+
};
|
|
3472
|
+
}
|
|
3473
|
+
function listSkills(ctx, opts = {}) {
|
|
3474
|
+
return request(ctx, `${BASE5}/skills`, { query: listQuery2(opts) });
|
|
3475
|
+
}
|
|
3476
|
+
function listSkillMarket(ctx, opts = {}) {
|
|
3477
|
+
return request(ctx, `${BASE5}/skills/market`, { query: listQuery2(opts) });
|
|
3478
|
+
}
|
|
3479
|
+
function getSkill(ctx, skillId) {
|
|
3480
|
+
return request(ctx, `${BASE5}/skills/${encodeURIComponent(skillId)}`);
|
|
3481
|
+
}
|
|
3482
|
+
function getSkillMarket(ctx, skillId) {
|
|
3483
|
+
return request(ctx, `${BASE5}/skills/market/${encodeURIComponent(skillId)}`);
|
|
3484
|
+
}
|
|
3485
|
+
function deleteSkill(ctx, skillId) {
|
|
3486
|
+
return request(ctx, `${BASE5}/skills/${encodeURIComponent(skillId)}`, { method: "DELETE" });
|
|
3487
|
+
}
|
|
3488
|
+
function getSkillContent(ctx, skillId) {
|
|
3489
|
+
return request(ctx, `${BASE5}/skills/${encodeURIComponent(skillId)}/content`);
|
|
3490
|
+
}
|
|
3491
|
+
function readSkillFile(ctx, skillId, relPath) {
|
|
3492
|
+
return request(ctx, `${BASE5}/skills/${encodeURIComponent(skillId)}/files/read`, {
|
|
3493
|
+
method: "POST",
|
|
3494
|
+
body: { rel_path: relPath }
|
|
3495
|
+
});
|
|
3496
|
+
}
|
|
3497
|
+
function getSkillHistory(ctx, skillId) {
|
|
3498
|
+
return request(ctx, `${BASE5}/skills/${encodeURIComponent(skillId)}/history`);
|
|
3499
|
+
}
|
|
3500
|
+
function setSkillStatus(ctx, skillId, status2) {
|
|
3501
|
+
return request(ctx, `${BASE5}/skills/${encodeURIComponent(skillId)}/status`, {
|
|
3502
|
+
method: "PUT",
|
|
3503
|
+
body: { status: status2 }
|
|
3504
|
+
});
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
// src/utils/skill-archive.ts
|
|
3508
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
3509
|
+
import { dirname, join as join2, relative, resolve as resolve4, sep } from "path";
|
|
3510
|
+
import JSZip from "jszip";
|
|
3511
|
+
function walk(dir, base, files) {
|
|
3512
|
+
for (const entry of readdirSync2(dir)) {
|
|
3513
|
+
const full = join2(dir, entry);
|
|
3514
|
+
const st = statSync2(full);
|
|
3515
|
+
if (st.isDirectory()) walk(full, base, files);
|
|
3516
|
+
else files.push(full);
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
async function zipDirectory(dir) {
|
|
3520
|
+
const abs = resolve4(dir);
|
|
3521
|
+
if (!existsSync2(abs)) throw new Error(`directory not found: ${abs}`);
|
|
3522
|
+
const zip = new JSZip();
|
|
3523
|
+
const files = [];
|
|
3524
|
+
walk(abs, abs, files);
|
|
3525
|
+
for (const file of files) {
|
|
3526
|
+
const rel = relative(abs, file).split(sep).join("/");
|
|
3527
|
+
zip.file(rel, readFileSync2(file));
|
|
3528
|
+
}
|
|
3529
|
+
return new Uint8Array(await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" }));
|
|
3530
|
+
}
|
|
3531
|
+
async function unzipToDirectory(bytes, dir) {
|
|
3532
|
+
const abs = resolve4(dir);
|
|
3533
|
+
mkdirSync3(abs, { recursive: true });
|
|
3534
|
+
const zip = await JSZip.loadAsync(bytes);
|
|
3535
|
+
const written = [];
|
|
3536
|
+
const entries = Object.values(zip.files);
|
|
3537
|
+
for (const entry of entries) {
|
|
3538
|
+
if (entry.dir) continue;
|
|
3539
|
+
const out = join2(abs, entry.name);
|
|
3540
|
+
mkdirSync3(dirname(out), { recursive: true });
|
|
3541
|
+
writeFileSync2(out, await entry.async("nodebuffer"));
|
|
3542
|
+
written.push(out);
|
|
3543
|
+
}
|
|
3544
|
+
return written;
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
// src/resources/skills.ts
|
|
3548
|
+
function skills(ctx) {
|
|
3549
|
+
return {
|
|
3550
|
+
list: (opts) => listSkills(ctx, opts),
|
|
3551
|
+
get: (skillId) => getSkill(ctx, skillId),
|
|
3552
|
+
market: (opts) => listSkillMarket(ctx, opts),
|
|
3553
|
+
marketGet: (skillId) => getSkillMarket(ctx, skillId),
|
|
3554
|
+
delete: (skillId) => deleteSkill(ctx, skillId),
|
|
3555
|
+
content: (skillId) => getSkillContent(ctx, skillId),
|
|
3556
|
+
readFile: (skillId, relPath) => readSkillFile(ctx, skillId, relPath),
|
|
3557
|
+
history: (skillId) => getSkillHistory(ctx, skillId),
|
|
3558
|
+
setStatus: (skillId, status2) => setSkillStatus(ctx, skillId, status2),
|
|
3559
|
+
updateMetadata: (skillId, body) => updateSkillMetadata(ctx, skillId, body),
|
|
3560
|
+
republish: (skillId, version) => republishSkillVersion(ctx, skillId, version),
|
|
3561
|
+
publishHistory: (skillId, version) => publishSkillVersion(ctx, skillId, version),
|
|
3562
|
+
/** Zip a local skill directory and register it. */
|
|
3563
|
+
register: async (dir, opts) => registerSkillZip(ctx, await zipDirectory(dir), {
|
|
3564
|
+
filename: `${basename2(resolve5(dir))}.zip`,
|
|
3565
|
+
...opts
|
|
3566
|
+
}),
|
|
3567
|
+
/** Replace a skill's package from a local directory. */
|
|
3568
|
+
updatePackage: async (skillId, dir) => updateSkillPackageZip(ctx, skillId, await zipDirectory(dir), `${basename2(resolve5(dir))}.zip`),
|
|
3569
|
+
/** Download a skill archive to a local .zip file. */
|
|
3570
|
+
download: async (skillId, outPath) => {
|
|
3571
|
+
const bytes = await downloadSkill(ctx, skillId);
|
|
3572
|
+
const dest = resolve5(outPath ?? `${skillId}.zip`);
|
|
3573
|
+
mkdirSync4(dirname2(dest), { recursive: true });
|
|
3574
|
+
writeFileSync3(dest, bytes);
|
|
3575
|
+
return { skillId, path: dest, bytes: bytes.length };
|
|
3576
|
+
},
|
|
3577
|
+
/** Download a skill archive and extract it into a directory. */
|
|
3578
|
+
install: async (skillId, dir) => {
|
|
3579
|
+
const bytes = await downloadSkill(ctx, skillId);
|
|
3580
|
+
const target = resolve5(dir ?? skillId);
|
|
3581
|
+
const files = await unzipToDirectory(bytes, target);
|
|
3582
|
+
return { skillId, dir: target, files: files.length };
|
|
3583
|
+
}
|
|
3584
|
+
};
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3587
|
+
// src/resources/toolboxes.ts
|
|
3588
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
3589
|
+
import { dirname as dirname3, resolve as resolve6 } from "path";
|
|
3590
|
+
|
|
3591
|
+
// src/api/toolboxes.ts
|
|
3592
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
3593
|
+
import { basename as basename3 } from "path";
|
|
3594
|
+
var PATH = "/api/agent-operator-integration/v1/tool-box";
|
|
3595
|
+
var IMPEX = "/api/agent-operator-integration/v1/impex";
|
|
3596
|
+
async function exportConfig(ctx, id, type = "toolbox") {
|
|
3597
|
+
applyTls(ctx);
|
|
3598
|
+
const res = await fetch(
|
|
3599
|
+
`${ctx.baseUrl}${IMPEX}/export/${encodeURIComponent(type)}/${encodeURIComponent(id)}`,
|
|
3600
|
+
{ headers: buildHeaders(ctx) }
|
|
3601
|
+
);
|
|
3602
|
+
const buf = new Uint8Array(await res.arrayBuffer());
|
|
3603
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, new TextDecoder().decode(buf));
|
|
3604
|
+
return buf;
|
|
3605
|
+
}
|
|
3606
|
+
async function importConfig(ctx, filePath, type = "toolbox") {
|
|
3607
|
+
applyTls(ctx);
|
|
3608
|
+
const buf = await readFile2(filePath);
|
|
3609
|
+
const form = new FormData();
|
|
3610
|
+
form.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
|
|
3611
|
+
const res = await fetch(`${ctx.baseUrl}${IMPEX}/import/${encodeURIComponent(type)}`, {
|
|
3612
|
+
method: "POST",
|
|
3613
|
+
headers: buildHeaders(ctx),
|
|
3614
|
+
body: form
|
|
3615
|
+
});
|
|
3616
|
+
const text = await res.text();
|
|
3617
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
3618
|
+
return text ? JSON.parse(text) : text;
|
|
3619
|
+
}
|
|
3620
|
+
async function uploadTool(ctx, boxId, filePath, metadataType = "openapi") {
|
|
3621
|
+
applyTls(ctx);
|
|
3622
|
+
const buf = await readFile2(filePath);
|
|
3623
|
+
const form = new FormData();
|
|
3624
|
+
form.append("metadata_type", metadataType);
|
|
3625
|
+
form.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
|
|
3626
|
+
const res = await fetch(`${ctx.baseUrl}${PATH}/${encodeURIComponent(boxId)}/tool`, {
|
|
3627
|
+
method: "POST",
|
|
3628
|
+
headers: buildHeaders(ctx),
|
|
3629
|
+
body: form
|
|
3630
|
+
});
|
|
3631
|
+
const text = await res.text();
|
|
3632
|
+
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
3633
|
+
return text ? JSON.parse(text) : text;
|
|
3634
|
+
}
|
|
3635
|
+
function listToolboxes(ctx, opts = {}) {
|
|
3636
|
+
return request(ctx, `${PATH}/list`, {
|
|
3637
|
+
query: { keyword: opts.keyword || void 0, limit: opts.limit, offset: opts.offset ?? 0 }
|
|
3638
|
+
});
|
|
3639
|
+
}
|
|
3640
|
+
function listTools2(ctx, boxId) {
|
|
3641
|
+
return request(ctx, `${PATH}/${encodeURIComponent(boxId)}/tools/list`);
|
|
3642
|
+
}
|
|
3643
|
+
function createToolbox(ctx, opts) {
|
|
3644
|
+
return request(ctx, PATH, {
|
|
3645
|
+
method: "POST",
|
|
3646
|
+
body: {
|
|
3647
|
+
metadata_type: "openapi",
|
|
3648
|
+
box_name: opts.name,
|
|
3649
|
+
box_desc: opts.description ?? "",
|
|
3650
|
+
box_svc_url: opts.serviceUrl,
|
|
3651
|
+
source: opts.source ?? "custom"
|
|
3652
|
+
}
|
|
3653
|
+
});
|
|
3654
|
+
}
|
|
3655
|
+
function deleteToolbox(ctx, boxId) {
|
|
3656
|
+
return request(ctx, `${PATH}/${encodeURIComponent(boxId)}`, { method: "DELETE" });
|
|
3657
|
+
}
|
|
3658
|
+
function setToolboxStatus(ctx, boxId, status2) {
|
|
3659
|
+
return request(ctx, `${PATH}/${encodeURIComponent(boxId)}/status`, {
|
|
3660
|
+
method: "POST",
|
|
3661
|
+
body: { status: status2 }
|
|
3662
|
+
});
|
|
3663
|
+
}
|
|
3664
|
+
function envelope(e) {
|
|
3665
|
+
return {
|
|
3666
|
+
...e.timeout !== void 0 ? { timeout: e.timeout } : {},
|
|
3667
|
+
header: e.header ?? {},
|
|
3668
|
+
query: e.query ?? {},
|
|
3669
|
+
path: e.path ?? {},
|
|
3670
|
+
body: e.body ?? {}
|
|
3671
|
+
};
|
|
3672
|
+
}
|
|
3673
|
+
function executeTool(ctx, boxId, toolId, e = {}) {
|
|
3674
|
+
return request(ctx, `${PATH}/${encodeURIComponent(boxId)}/proxy/${encodeURIComponent(toolId)}`, {
|
|
3675
|
+
method: "POST",
|
|
3676
|
+
body: envelope(e)
|
|
3677
|
+
});
|
|
3678
|
+
}
|
|
3679
|
+
function debugTool(ctx, boxId, toolId, e = {}) {
|
|
3680
|
+
return request(
|
|
3681
|
+
ctx,
|
|
3682
|
+
`${PATH}/${encodeURIComponent(boxId)}/tool/${encodeURIComponent(toolId)}/debug`,
|
|
3683
|
+
{
|
|
3684
|
+
method: "POST",
|
|
3685
|
+
body: envelope(e)
|
|
3686
|
+
}
|
|
3687
|
+
);
|
|
3688
|
+
}
|
|
3689
|
+
function setToolStatuses(ctx, boxId, updates) {
|
|
3690
|
+
return request(ctx, `${PATH}/${encodeURIComponent(boxId)}/tools/status`, {
|
|
3691
|
+
method: "POST",
|
|
3692
|
+
body: updates.map((u) => ({ tool_id: u.toolId, status: u.status }))
|
|
3693
|
+
});
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
// src/resources/toolboxes.ts
|
|
3697
|
+
function toolboxes(ctx) {
|
|
3698
|
+
return {
|
|
3699
|
+
list: (opts) => listToolboxes(ctx, opts),
|
|
3700
|
+
tools: (boxId) => listTools2(ctx, boxId),
|
|
3701
|
+
create: (opts) => createToolbox(ctx, opts),
|
|
3702
|
+
delete: (boxId) => deleteToolbox(ctx, boxId),
|
|
3703
|
+
publish: (boxId) => setToolboxStatus(ctx, boxId, "published"),
|
|
3704
|
+
unpublish: (boxId) => setToolboxStatus(ctx, boxId, "draft"),
|
|
3705
|
+
setToolStatus: (boxId, toolIds, status2) => setToolStatuses(
|
|
3706
|
+
ctx,
|
|
3707
|
+
boxId,
|
|
3708
|
+
toolIds.map((toolId) => ({ toolId, status: status2 }))
|
|
3709
|
+
),
|
|
3710
|
+
upload: (boxId, filePath, metadataType) => uploadTool(ctx, boxId, filePath, metadataType),
|
|
3711
|
+
/** Export a toolbox config to a local `.adp` file. */
|
|
3712
|
+
export: async (id, outPath, type) => {
|
|
3713
|
+
const bytes = await exportConfig(ctx, id, type);
|
|
3714
|
+
const dest = resolve6(outPath);
|
|
3715
|
+
mkdirSync5(dirname3(dest), { recursive: true });
|
|
3716
|
+
writeFileSync4(dest, bytes);
|
|
3717
|
+
return { id, path: dest, bytes: bytes.length };
|
|
3718
|
+
},
|
|
3719
|
+
/** Import a toolbox config from a local `.adp` file. */
|
|
3720
|
+
import: (filePath, type) => importConfig(ctx, filePath, type),
|
|
3721
|
+
execute: (boxId, toolId, e) => executeTool(ctx, boxId, toolId, e),
|
|
3722
|
+
debug: (boxId, toolId, e) => debugTool(ctx, boxId, toolId, e)
|
|
3723
|
+
};
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
// src/api/trace.ts
|
|
3727
|
+
var SEARCH = "/api/agent-observability/v1/traces/_search";
|
|
3728
|
+
function isoToNanos(iso) {
|
|
3729
|
+
const ms = Date.parse(iso);
|
|
3730
|
+
if (Number.isNaN(ms)) return void 0;
|
|
3731
|
+
return (BigInt(ms) * 1000000n).toString();
|
|
3732
|
+
}
|
|
3733
|
+
function normalizeRawSpan(src) {
|
|
3734
|
+
const spanIdRaw = src.spanId ?? src.span_id;
|
|
3735
|
+
const spanId = typeof spanIdRaw === "string" ? spanIdRaw : "";
|
|
3736
|
+
if (!spanId) return null;
|
|
3737
|
+
const parentRaw = src.parentSpanId ?? src.parent_span_id;
|
|
3738
|
+
const parentSpanId = typeof parentRaw === "string" && parentRaw !== "" && parentRaw !== "0" ? parentRaw : null;
|
|
3739
|
+
const start = typeof src.startTimeUnixNano === "string" ? src.startTimeUnixNano : typeof src.startTime === "string" ? isoToNanos(src.startTime) : void 0;
|
|
3740
|
+
const end = typeof src.endTimeUnixNano === "string" ? src.endTimeUnixNano : typeof src.endTime === "string" ? isoToNanos(src.endTime) : void 0;
|
|
3741
|
+
const traceIdRaw = src.traceId ?? src.trace_id;
|
|
3742
|
+
return {
|
|
3743
|
+
spanId,
|
|
3744
|
+
parentSpanId,
|
|
3745
|
+
name: typeof src.name === "string" ? src.name : void 0,
|
|
3746
|
+
traceId: typeof traceIdRaw === "string" ? traceIdRaw : void 0,
|
|
3747
|
+
startTimeUnixNano: start,
|
|
3748
|
+
endTimeUnixNano: end,
|
|
3749
|
+
status: src.status,
|
|
3750
|
+
attributes: src.attributes,
|
|
3751
|
+
events: Array.isArray(src.events) ? src.events : void 0
|
|
3752
|
+
};
|
|
3753
|
+
}
|
|
3754
|
+
async function getRawSpansByConversation(ctx, conversationId, opts = {}) {
|
|
3755
|
+
const sources = await getSpansByConversation(ctx, conversationId, opts);
|
|
3756
|
+
const spans = [];
|
|
3757
|
+
const traceIds = /* @__PURE__ */ new Set();
|
|
3758
|
+
for (const src of sources) {
|
|
3759
|
+
const span = normalizeRawSpan(src);
|
|
3760
|
+
if (!span) continue;
|
|
3761
|
+
spans.push(span);
|
|
3762
|
+
if (span.traceId) traceIds.add(span.traceId);
|
|
3763
|
+
}
|
|
3764
|
+
return { spans, traceIds: [...traceIds] };
|
|
3765
|
+
}
|
|
3766
|
+
function traceSearch(ctx, body) {
|
|
3767
|
+
return request(ctx, SEARCH, { method: "POST", body });
|
|
3768
|
+
}
|
|
3769
|
+
async function getSpansByConversation(ctx, conversationId, opts = {}) {
|
|
3770
|
+
const agg = await request(ctx, SEARCH, {
|
|
3771
|
+
method: "POST",
|
|
3772
|
+
body: {
|
|
3773
|
+
size: 0,
|
|
3774
|
+
query: { term: { "attributes.gen_ai.conversation.id.keyword": conversationId } },
|
|
3775
|
+
aggs: { tids: { terms: { field: "traceId.keyword", size: opts.maxTraceIds ?? 100 } } }
|
|
3776
|
+
}
|
|
3777
|
+
}) ?? {};
|
|
3778
|
+
const direct = agg.hits?.hits;
|
|
3779
|
+
if (!agg.aggregations && Array.isArray(direct)) {
|
|
3780
|
+
return direct.map((h) => h._source ?? {});
|
|
3781
|
+
}
|
|
3782
|
+
const traceIds = (agg.aggregations?.tids?.buckets ?? []).map((b) => b.key).filter((k) => typeof k === "string" && k.length > 0);
|
|
3783
|
+
if (traceIds.length === 0) return [];
|
|
3784
|
+
const spans = await request(ctx, SEARCH, {
|
|
3785
|
+
method: "POST",
|
|
3786
|
+
body: {
|
|
3787
|
+
size: opts.maxSpans ?? 2e3,
|
|
3788
|
+
query: { terms: { "traceId.keyword": traceIds } }
|
|
3789
|
+
}
|
|
3790
|
+
}) ?? {};
|
|
3791
|
+
return (spans.hits?.hits ?? []).map((h) => h._source ?? {});
|
|
3792
|
+
}
|
|
3793
|
+
|
|
3794
|
+
// src/trace-ai/claude-judge.ts
|
|
3795
|
+
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
3796
|
+
var ClaudeJudgeError = class extends Error {
|
|
3797
|
+
constructor(message, reason) {
|
|
3798
|
+
super(message);
|
|
3799
|
+
this.reason = reason;
|
|
3800
|
+
this.name = "ClaudeJudgeError";
|
|
3801
|
+
}
|
|
3802
|
+
reason;
|
|
3803
|
+
};
|
|
3804
|
+
function claudeAvailable(binary = "claude") {
|
|
3805
|
+
try {
|
|
3806
|
+
const r = spawnSync2(binary, ["--version"], {
|
|
3807
|
+
timeout: 5e3,
|
|
3808
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
3809
|
+
});
|
|
3810
|
+
return r.status === 0;
|
|
3811
|
+
} catch {
|
|
3812
|
+
return false;
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
function extractEnvelope(stdout) {
|
|
3816
|
+
const trimmed = stdout.trim();
|
|
3817
|
+
if (!trimmed) throw new ClaudeJudgeError("claude returned empty stdout", "bad_output");
|
|
3818
|
+
const env = JSON.parse(trimmed);
|
|
3819
|
+
const text = typeof env.result === "string" ? env.result : env.text;
|
|
3820
|
+
if (typeof text !== "string")
|
|
3821
|
+
throw new ClaudeJudgeError("claude envelope had no result text", "bad_output");
|
|
3822
|
+
return text;
|
|
3823
|
+
}
|
|
3824
|
+
function extractJsonObject(text) {
|
|
3825
|
+
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
3826
|
+
const body = fenced?.[1] ?? text;
|
|
3827
|
+
const start = body.indexOf("{");
|
|
3828
|
+
if (start === -1) throw new ClaudeJudgeError("no JSON object in judge output", "bad_output");
|
|
3829
|
+
let depth = 0;
|
|
3830
|
+
for (let i = start; i < body.length; i++) {
|
|
3831
|
+
if (body[i] === "{") depth++;
|
|
3832
|
+
else if (body[i] === "}" && --depth === 0) return body.slice(start, i + 1);
|
|
3833
|
+
}
|
|
3834
|
+
throw new ClaudeJudgeError("unbalanced JSON object in judge output", "bad_output");
|
|
3835
|
+
}
|
|
3836
|
+
async function judgeJson(prompt, opts = {}) {
|
|
3837
|
+
const binary = opts.binary ?? "claude";
|
|
3838
|
+
const timeoutMs = opts.timeoutMs ?? 12e4;
|
|
3839
|
+
const args = ["-p", "--output-format=json", "--dangerously-skip-permissions"];
|
|
3840
|
+
const stdout = await new Promise((resolve7, reject) => {
|
|
3841
|
+
const child = spawn(binary, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
3842
|
+
let out = "";
|
|
3843
|
+
let timedOut = false;
|
|
3844
|
+
const killer = setTimeout(() => {
|
|
3845
|
+
timedOut = true;
|
|
3846
|
+
child.kill("SIGTERM");
|
|
3847
|
+
setTimeout(() => child.kill("SIGKILL"), 2e3).unref();
|
|
3848
|
+
}, timeoutMs);
|
|
3849
|
+
killer.unref();
|
|
3850
|
+
child.stdout.setEncoding("utf8");
|
|
3851
|
+
child.stdout.on("data", (d) => {
|
|
3852
|
+
out += d;
|
|
3853
|
+
});
|
|
3854
|
+
child.on("error", (err) => {
|
|
3855
|
+
clearTimeout(killer);
|
|
3856
|
+
reject(
|
|
3857
|
+
err.code === "ENOENT" ? new ClaudeJudgeError("`claude` not found on PATH", "not_available") : err
|
|
3858
|
+
);
|
|
3859
|
+
});
|
|
3860
|
+
child.on("close", (code) => {
|
|
3861
|
+
clearTimeout(killer);
|
|
3862
|
+
if (timedOut)
|
|
3863
|
+
return reject(new ClaudeJudgeError(`claude timed out after ${timeoutMs}ms`, "timeout"));
|
|
3864
|
+
if (code !== 0) return reject(new ClaudeJudgeError(`claude exited ${code}`, "exit"));
|
|
3865
|
+
resolve7(out);
|
|
3866
|
+
});
|
|
3867
|
+
child.stdin.on("error", (err) => {
|
|
3868
|
+
if (err.code !== "EPIPE") reject(err);
|
|
3869
|
+
});
|
|
3870
|
+
child.stdin.end(prompt);
|
|
3871
|
+
});
|
|
3872
|
+
const text = extractEnvelope(stdout);
|
|
3873
|
+
return JSON.parse(extractJsonObject(text));
|
|
3874
|
+
}
|
|
3875
|
+
|
|
3876
|
+
// src/trace-ai/diagnose.ts
|
|
3877
|
+
var KIND_MAP = {
|
|
3878
|
+
chat: "llm",
|
|
3879
|
+
text_completion: "llm",
|
|
3880
|
+
embeddings: "retrieval",
|
|
3881
|
+
execute_tool: "tool",
|
|
3882
|
+
model: "llm",
|
|
3883
|
+
llm: "llm",
|
|
3884
|
+
tool: "tool",
|
|
3885
|
+
retrieval: "retrieval",
|
|
3886
|
+
reasoning: "reasoning"
|
|
3887
|
+
};
|
|
3888
|
+
function deriveKind(attrs) {
|
|
3889
|
+
const op = attrs["gen_ai.operation.name"];
|
|
3890
|
+
if (typeof op === "string" && op in KIND_MAP) return KIND_MAP[op];
|
|
3891
|
+
const t = attrs["agent.trace.type"];
|
|
3892
|
+
if (typeof t === "string" && t in KIND_MAP) return KIND_MAP[t];
|
|
3893
|
+
return "unknown";
|
|
3894
|
+
}
|
|
3895
|
+
function durationMs(start, end) {
|
|
3896
|
+
if (!start || !end) return 0;
|
|
3897
|
+
try {
|
|
3898
|
+
return Number((BigInt(end) - BigInt(start)) / 1000000n);
|
|
3899
|
+
} catch {
|
|
3900
|
+
return 0;
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
function assembleTraceTree(traceId, raw) {
|
|
3904
|
+
const spans = raw.map((r) => {
|
|
3905
|
+
const attrs = r.attributes ?? {};
|
|
3906
|
+
const code = r.status?.code?.toUpperCase();
|
|
3907
|
+
return {
|
|
3908
|
+
spanId: r.spanId,
|
|
3909
|
+
parentSpanId: r.parentSpanId ?? null,
|
|
3910
|
+
name: r.name ?? "",
|
|
3911
|
+
kind: deriveKind(attrs),
|
|
3912
|
+
startTimeUnixNano: r.startTimeUnixNano ?? "0",
|
|
3913
|
+
endTimeUnixNano: r.endTimeUnixNano ?? "0",
|
|
3914
|
+
durationMs: durationMs(r.startTimeUnixNano, r.endTimeUnixNano),
|
|
3915
|
+
status: code === "OK" ? "ok" : code === "ERROR" ? "error" : "unset",
|
|
3916
|
+
attributes: attrs
|
|
3917
|
+
};
|
|
3918
|
+
});
|
|
3919
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
3920
|
+
for (const s of spans) {
|
|
3921
|
+
const arr = byKind.get(s.kind) ?? [];
|
|
3922
|
+
arr.push(s);
|
|
3923
|
+
byKind.set(s.kind, arr);
|
|
3924
|
+
}
|
|
3925
|
+
return { traceId, spans, byKind };
|
|
3926
|
+
}
|
|
3927
|
+
function byStart(spans) {
|
|
3928
|
+
return spans.slice().sort((a, b) => Number(BigInt(a.startTimeUnixNano) - BigInt(b.startTimeUnixNano)));
|
|
3929
|
+
}
|
|
3930
|
+
function strAttr(s, key) {
|
|
3931
|
+
const v = s.attributes[key];
|
|
3932
|
+
return typeof v === "string" ? v : "";
|
|
3933
|
+
}
|
|
3934
|
+
function toolName(s) {
|
|
3935
|
+
return strAttr(s, "gen_ai.tool.name") || s.name;
|
|
3936
|
+
}
|
|
3937
|
+
var toolLoopNoStateChange = (trace2, params) => {
|
|
3938
|
+
const STATE = "gen_ai.conversation.state";
|
|
3939
|
+
const min = params.min_consecutive ?? 3;
|
|
3940
|
+
const tools = byStart(trace2.byKind.get("tool") ?? []);
|
|
3941
|
+
const hits = [];
|
|
3942
|
+
let i = 0;
|
|
3943
|
+
while (i < tools.length) {
|
|
3944
|
+
const start = tools[i];
|
|
3945
|
+
const name = toolName(start);
|
|
3946
|
+
const args = start.attributes["gen_ai.tool.args"];
|
|
3947
|
+
const state = start.attributes[STATE];
|
|
3948
|
+
let j = i + 1;
|
|
3949
|
+
while (j < tools.length && toolName(tools[j]) === name && JSON.stringify(tools[j].attributes["gen_ai.tool.args"]) === JSON.stringify(args) && tools[j].attributes[STATE] === state)
|
|
3950
|
+
j++;
|
|
3951
|
+
const runLen = j - i;
|
|
3952
|
+
if (runLen >= min) {
|
|
3953
|
+
hits.push({
|
|
3954
|
+
evidenceSpans: tools.slice(i, j).map((s) => s.spanId),
|
|
3955
|
+
excerpt: `tool '${name}' called ${runLen} times consecutively with identical args; conversation state unchanged`,
|
|
3956
|
+
bindings: { tool_name: name, loop_count: runLen, max_count: min - 1 }
|
|
3957
|
+
});
|
|
3958
|
+
}
|
|
3959
|
+
i = j;
|
|
3960
|
+
}
|
|
3961
|
+
return hits;
|
|
3962
|
+
};
|
|
3963
|
+
var toolErrorSwallowed = (trace2) => {
|
|
3964
|
+
const all = byStart(trace2.spans);
|
|
3965
|
+
const hits = [];
|
|
3966
|
+
for (let i = 0; i < all.length; i++) {
|
|
3967
|
+
const s = all[i];
|
|
3968
|
+
if (s.kind !== "tool" || s.status !== "error") continue;
|
|
3969
|
+
const errMsg = strAttr(s, "error.message");
|
|
3970
|
+
let next;
|
|
3971
|
+
for (let j = i + 1; j < all.length; j++) {
|
|
3972
|
+
if (all[j].kind === "llm") {
|
|
3973
|
+
next = all[j];
|
|
3974
|
+
break;
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
if (!next) continue;
|
|
3978
|
+
const prompt = (strAttr(next, "gen_ai.prompt") || strAttr(next, "llm.prompt")).toLowerCase();
|
|
3979
|
+
if (!(errMsg.length > 0 && prompt.includes(errMsg.toLowerCase()))) {
|
|
3980
|
+
hits.push({
|
|
3981
|
+
evidenceSpans: [s.spanId, next.spanId],
|
|
3982
|
+
excerpt: `tool '${toolName(s)}' errored ('${errMsg}') but the next LLM prompt did not propagate the error`,
|
|
3983
|
+
bindings: { tool_name: toolName(s), error_message: errMsg }
|
|
3984
|
+
});
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
return hits;
|
|
3988
|
+
};
|
|
3989
|
+
var retrievalEmptyNoFallback = (trace2) => {
|
|
3990
|
+
const ordered = byStart(trace2.spans);
|
|
3991
|
+
const hits = [];
|
|
3992
|
+
for (let i = 0; i < ordered.length; i++) {
|
|
3993
|
+
const s = ordered[i];
|
|
3994
|
+
if (s.kind !== "retrieval") continue;
|
|
3995
|
+
if (s.attributes["gen_ai.retrieval.result_count"] !== 0) continue;
|
|
3996
|
+
const next = ordered[i + 1];
|
|
3997
|
+
if (next?.kind === "llm") {
|
|
3998
|
+
hits.push({
|
|
3999
|
+
evidenceSpans: [s.spanId, next.spanId],
|
|
4000
|
+
excerpt: "retrieval returned 0 results; next step was LLM generation with no fallback path",
|
|
4001
|
+
bindings: {}
|
|
4002
|
+
});
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
return hits;
|
|
4006
|
+
};
|
|
4007
|
+
var llmResponseTruncatedNoContinue = (trace2) => {
|
|
4008
|
+
const finishReason = (s) => {
|
|
4009
|
+
const arr = s.attributes["gen_ai.response.finish_reasons"];
|
|
4010
|
+
if (Array.isArray(arr)) {
|
|
4011
|
+
for (const r of arr) if (typeof r === "string" && r) return r;
|
|
4012
|
+
}
|
|
4013
|
+
return strAttr(s, "gen_ai.response.finish_reason") || strAttr(s, "llm.finish_reason");
|
|
4014
|
+
};
|
|
4015
|
+
const convId = (s) => strAttr(s, "gen_ai.conversation.id");
|
|
4016
|
+
const llms = byStart(trace2.byKind.get("llm") ?? []);
|
|
4017
|
+
const hits = [];
|
|
4018
|
+
for (let i = 0; i < llms.length; i++) {
|
|
4019
|
+
const s = llms[i];
|
|
4020
|
+
if (finishReason(s) !== "length") continue;
|
|
4021
|
+
const id = convId(s);
|
|
4022
|
+
let cont = false;
|
|
4023
|
+
for (let j = i + 1; j < llms.length; j++) if (convId(llms[j]) === id) cont = true;
|
|
4024
|
+
if (!cont) {
|
|
4025
|
+
hits.push({
|
|
4026
|
+
evidenceSpans: [s.spanId],
|
|
4027
|
+
excerpt: `LLM response truncated (finish_reason=length) with no continuation span in conversation '${id}'`,
|
|
4028
|
+
bindings: { conversation_id: id }
|
|
4029
|
+
});
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
return hits;
|
|
4033
|
+
};
|
|
4034
|
+
var excessiveToolCallsPerTurn = (trace2, params) => {
|
|
4035
|
+
const max = params.max_tool_calls_per_turn ?? 10;
|
|
4036
|
+
const tools = trace2.byKind.get("tool") ?? [];
|
|
4037
|
+
if (tools.length <= max) return [];
|
|
4038
|
+
return [
|
|
4039
|
+
{
|
|
4040
|
+
evidenceSpans: tools.map((t) => t.spanId),
|
|
4041
|
+
excerpt: `tool calls per turn exceeded threshold: ${tools.length} > ${max}`,
|
|
4042
|
+
bindings: { count: tools.length, max_calls: max }
|
|
4043
|
+
}
|
|
4044
|
+
];
|
|
4045
|
+
};
|
|
4046
|
+
var BUILTIN_RULES = [
|
|
4047
|
+
{
|
|
4048
|
+
id: "tool_loop_no_state_change",
|
|
4049
|
+
severity: "high",
|
|
4050
|
+
symptom: "repeated_tool_call_without_state_change",
|
|
4051
|
+
suggestedFix: {
|
|
4052
|
+
target: "decision_agent.prompt",
|
|
4053
|
+
changeTemplate: "add a stop condition after {{loop_count}} equivalent retries of '{{tool_name}}'"
|
|
4054
|
+
},
|
|
4055
|
+
predicate: toolLoopNoStateChange,
|
|
4056
|
+
params: { min_consecutive: 3 }
|
|
4057
|
+
},
|
|
4058
|
+
{
|
|
4059
|
+
id: "tool_error_swallowed",
|
|
4060
|
+
severity: "high",
|
|
4061
|
+
symptom: "tool_error_not_propagated_to_llm",
|
|
4062
|
+
suggestedFix: {
|
|
4063
|
+
target: "agent.tool_result_handling",
|
|
4064
|
+
changeTemplate: "feed tool errors back into the next LLM prompt"
|
|
4065
|
+
},
|
|
4066
|
+
predicate: toolErrorSwallowed,
|
|
4067
|
+
params: {}
|
|
4068
|
+
},
|
|
4069
|
+
{
|
|
4070
|
+
id: "retrieval_empty_no_fallback",
|
|
4071
|
+
severity: "medium",
|
|
4072
|
+
symptom: "empty_retrieval_no_fallback",
|
|
4073
|
+
suggestedFix: {
|
|
4074
|
+
target: "retrieval.fallback",
|
|
4075
|
+
changeTemplate: "add a rewrite/alternate-source fallback when retrieval returns 0 results"
|
|
4076
|
+
},
|
|
4077
|
+
predicate: retrievalEmptyNoFallback,
|
|
4078
|
+
params: {}
|
|
4079
|
+
},
|
|
4080
|
+
{
|
|
4081
|
+
id: "llm_response_truncated_no_continue",
|
|
4082
|
+
severity: "medium",
|
|
4083
|
+
symptom: "truncated_response_no_continuation",
|
|
4084
|
+
suggestedFix: {
|
|
4085
|
+
target: "llm.max_tokens",
|
|
4086
|
+
changeTemplate: "raise max_tokens or add a continuation turn when finish_reason=length"
|
|
4087
|
+
},
|
|
4088
|
+
predicate: llmResponseTruncatedNoContinue,
|
|
4089
|
+
params: {}
|
|
4090
|
+
},
|
|
4091
|
+
{
|
|
4092
|
+
id: "excessive_tool_calls_per_turn",
|
|
4093
|
+
severity: "low",
|
|
4094
|
+
symptom: "excessive_tool_calls_per_turn",
|
|
4095
|
+
suggestedFix: {
|
|
4096
|
+
target: "decision_agent.prompt",
|
|
4097
|
+
changeTemplate: "cap tool calls per turn at {{max_calls}}"
|
|
4098
|
+
},
|
|
4099
|
+
predicate: excessiveToolCallsPerTurn,
|
|
4100
|
+
params: { max_tool_calls_per_turn: 10 }
|
|
4101
|
+
}
|
|
4102
|
+
];
|
|
4103
|
+
function applyTemplate(tpl, bindings) {
|
|
4104
|
+
return tpl.replace(/\{\{(\w+)\}\}/g, (_m, k) => String(bindings[k] ?? `{{${k}}}`));
|
|
4105
|
+
}
|
|
4106
|
+
function runRules(tree, rules = BUILTIN_RULES) {
|
|
4107
|
+
const findings = [];
|
|
4108
|
+
for (const rule of rules) {
|
|
4109
|
+
for (const hit of rule.predicate(tree, rule.params)) {
|
|
4110
|
+
findings.push({
|
|
4111
|
+
ruleId: rule.id,
|
|
4112
|
+
judgmentKind: "symbolic",
|
|
4113
|
+
severity: rule.severity,
|
|
4114
|
+
symptom: rule.symptom,
|
|
4115
|
+
evidence: { spans: hit.evidenceSpans, excerpt: hit.excerpt },
|
|
4116
|
+
suggestedFix: {
|
|
4117
|
+
target: rule.suggestedFix.target,
|
|
4118
|
+
change: applyTemplate(rule.suggestedFix.changeTemplate, hit.bindings)
|
|
4119
|
+
}
|
|
4120
|
+
});
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
return findings;
|
|
4124
|
+
}
|
|
4125
|
+
var BUILTIN_RUBRIC_RULES = [
|
|
4126
|
+
{
|
|
4127
|
+
id: "tool_retry_intent_mismatch",
|
|
4128
|
+
severity: "high",
|
|
4129
|
+
symptom: "repeated_tool_call_without_state_change",
|
|
4130
|
+
gatesOn: "tool_loop_no_state_change",
|
|
4131
|
+
judgeQuestion: "Given the user's intent and the tool retry pattern in this trace, classify why the agent kept calling the same tool: a legitimate retry (expecting changed state), a stale-results handling failure (results were identical and the agent didn't notice), prompt confusion (misinterpreted its own instructions), or other.",
|
|
4132
|
+
suggestedFix: {
|
|
4133
|
+
target: "decision_agent.prompt",
|
|
4134
|
+
changeTemplate: "agent retried because of '{{category}}'; address that intent (staleness detection, broaden query, or escalate)"
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
];
|
|
4138
|
+
function userIntent(tree) {
|
|
4139
|
+
for (const s of tree.spans) {
|
|
4140
|
+
const v = s.attributes["gen_ai.user.message"];
|
|
4141
|
+
if (typeof v === "string" && v) return v;
|
|
4142
|
+
}
|
|
4143
|
+
const firstLlm = byStart(tree.byKind.get("llm") ?? [])[0];
|
|
4144
|
+
return firstLlm ? strAttr(firstLlm, "gen_ai.prompt") || strAttr(firstLlm, "llm.prompt") : "";
|
|
4145
|
+
}
|
|
4146
|
+
function spanSequence(tree) {
|
|
4147
|
+
return byStart(tree.spans).filter((s) => s.kind === "tool" || s.kind === "llm").map((s) => ({
|
|
4148
|
+
span_id: s.spanId,
|
|
4149
|
+
kind: s.kind,
|
|
4150
|
+
name: s.name,
|
|
4151
|
+
status: s.status,
|
|
4152
|
+
tool: strAttr(s, "gen_ai.tool.name") || void 0,
|
|
4153
|
+
args: s.attributes["gen_ai.tool.args"]
|
|
4154
|
+
}));
|
|
4155
|
+
}
|
|
4156
|
+
function buildRubricPrompt(rule, tree) {
|
|
4157
|
+
return [
|
|
4158
|
+
"You are a trace-diagnosis judge. Answer the question about the agent trace below.",
|
|
4159
|
+
"",
|
|
4160
|
+
`QUESTION: ${rule.judgeQuestion}`,
|
|
4161
|
+
"",
|
|
4162
|
+
`USER INTENT: ${userIntent(tree) || "(unknown)"}`,
|
|
4163
|
+
"",
|
|
4164
|
+
"SPAN SEQUENCE (tool + llm steps, chronological):",
|
|
4165
|
+
JSON.stringify(spanSequence(tree), null, 2),
|
|
4166
|
+
"",
|
|
4167
|
+
"Respond with ONLY a JSON object (no prose, no code fence) of exactly this shape:",
|
|
4168
|
+
'{"category": "legitimate_retry|stale_results|prompt_confusion|other", "reasoning": "<one or two sentences>", "severity": "low|medium|high", "confidence": "low|medium|high", "first_violating_step_id": "<span_id>", "evidence_span_ids": ["<span_id>", ...]}'
|
|
4169
|
+
].join("\n");
|
|
4170
|
+
}
|
|
4171
|
+
async function runRubric(tree, symbolicFindings, judge, rules = BUILTIN_RUBRIC_RULES) {
|
|
4172
|
+
const fired = new Set(symbolicFindings.map((f) => f.ruleId));
|
|
4173
|
+
const out = [];
|
|
4174
|
+
for (const rule of rules) {
|
|
4175
|
+
if (!fired.has(rule.gatesOn)) continue;
|
|
4176
|
+
let judged;
|
|
4177
|
+
try {
|
|
4178
|
+
judged = await judge(buildRubricPrompt(rule, tree));
|
|
4179
|
+
} catch {
|
|
4180
|
+
continue;
|
|
4181
|
+
}
|
|
4182
|
+
const category = typeof judged.category === "string" ? judged.category : "other";
|
|
4183
|
+
const sev = judged.severity;
|
|
4184
|
+
const conf = judged.confidence;
|
|
4185
|
+
const spans = Array.isArray(judged.evidence_span_ids) ? judged.evidence_span_ids.filter((s) => typeof s === "string") : [];
|
|
4186
|
+
out.push({
|
|
4187
|
+
ruleId: rule.id,
|
|
4188
|
+
judgmentKind: "rubric",
|
|
4189
|
+
severity: sev === "low" || sev === "medium" || sev === "high" ? sev : rule.severity,
|
|
4190
|
+
symptom: rule.symptom,
|
|
4191
|
+
evidence: { spans, excerpt: typeof judged.reasoning === "string" ? judged.reasoning : "" },
|
|
4192
|
+
suggestedFix: {
|
|
4193
|
+
target: rule.suggestedFix.target,
|
|
4194
|
+
change: applyTemplate(rule.suggestedFix.changeTemplate, { category })
|
|
4195
|
+
},
|
|
4196
|
+
...conf === "low" || conf === "medium" || conf === "high" ? { confidence: conf } : {}
|
|
4197
|
+
});
|
|
4198
|
+
}
|
|
4199
|
+
return out;
|
|
4200
|
+
}
|
|
4201
|
+
async function synthesizeFindings(findings, judge) {
|
|
4202
|
+
if (findings.length === 0) {
|
|
4203
|
+
return {
|
|
4204
|
+
headline: "No issues found by the diagnosis rules.",
|
|
4205
|
+
primaryRootCause: null,
|
|
4206
|
+
targetForFix: null
|
|
4207
|
+
};
|
|
4208
|
+
}
|
|
4209
|
+
const prompt = [
|
|
4210
|
+
"You are a trace-diagnosis synthesizer. Given these findings, write a concise summary.",
|
|
4211
|
+
"FINDINGS:",
|
|
4212
|
+
JSON.stringify(
|
|
4213
|
+
findings.map((f) => ({
|
|
4214
|
+
rule: f.ruleId,
|
|
4215
|
+
severity: f.severity,
|
|
4216
|
+
symptom: f.symptom,
|
|
4217
|
+
evidence: f.evidence.excerpt,
|
|
4218
|
+
fix: f.suggestedFix
|
|
4219
|
+
})),
|
|
4220
|
+
null,
|
|
4221
|
+
2
|
|
4222
|
+
),
|
|
4223
|
+
"",
|
|
4224
|
+
'Respond with ONLY JSON: {"headline":"<one sentence>","primary_root_cause":"<one sentence or null>","target_for_fix":"<component or null>"}'
|
|
4225
|
+
].join("\n");
|
|
4226
|
+
try {
|
|
4227
|
+
const out = await judge(prompt);
|
|
4228
|
+
return {
|
|
4229
|
+
headline: typeof out.headline === "string" ? out.headline : fallbackHeadline(findings),
|
|
4230
|
+
primaryRootCause: typeof out.primary_root_cause === "string" ? out.primary_root_cause : null,
|
|
4231
|
+
targetForFix: typeof out.target_for_fix === "string" ? out.target_for_fix : null
|
|
4232
|
+
};
|
|
4233
|
+
} catch {
|
|
4234
|
+
return { headline: fallbackHeadline(findings), primaryRootCause: null, targetForFix: null };
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
function fallbackHeadline(findings) {
|
|
4238
|
+
const high = findings.filter((f) => f.severity === "high").length;
|
|
4239
|
+
return `${findings.length} finding(s)${high > 0 ? `, ${high} high-severity` : ""}.`;
|
|
4240
|
+
}
|
|
4241
|
+
function renderReportMarkdown(r) {
|
|
4242
|
+
const lines = [`# Trace Diagnose \u2014 \`${r.traceId}\``, ""];
|
|
4243
|
+
lines.push(
|
|
4244
|
+
`> conversation \`${r.conversationId}\` \xB7 mode ${r.mode} \xB7 ${r.findingCount} finding(s)`,
|
|
4245
|
+
""
|
|
4246
|
+
);
|
|
4247
|
+
if (r.summary) {
|
|
4248
|
+
lines.push(`**${r.summary.headline}**`, "");
|
|
4249
|
+
if (r.summary.primaryRootCause) {
|
|
4250
|
+
const tgt = r.summary.targetForFix ? ` (fix target: \`${r.summary.targetForFix}\`)` : "";
|
|
4251
|
+
lines.push(`Primary root cause: ${r.summary.primaryRootCause}${tgt}`, "");
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
if (r.findings.length === 0) {
|
|
4255
|
+
if (!r.summary) lines.push("No issues found by the symbolic rules.");
|
|
4256
|
+
return lines.join("\n");
|
|
4257
|
+
}
|
|
4258
|
+
const order = { high: 0, medium: 1, low: 2 };
|
|
4259
|
+
for (const f of r.findings.slice().sort((a, b) => order[a.severity] - order[b.severity])) {
|
|
4260
|
+
const conf = f.confidence ? `, confidence ${f.confidence}` : "";
|
|
4261
|
+
lines.push(`## [${f.severity.toUpperCase()}] ${f.ruleId} (${f.judgmentKind}${conf})`, "");
|
|
4262
|
+
lines.push(`- **symptom**: ${f.symptom}`);
|
|
4263
|
+
lines.push(`- **evidence**: ${f.evidence.excerpt}`);
|
|
4264
|
+
lines.push(`- **spans**: ${f.evidence.spans.map((s) => `\`${s}\``).join(", ") || "\u2014"}`);
|
|
4265
|
+
lines.push(`- **suggested fix** (\`${f.suggestedFix.target}\`): ${f.suggestedFix.change}`, "");
|
|
4266
|
+
}
|
|
4267
|
+
return lines.join("\n");
|
|
4268
|
+
}
|
|
4269
|
+
|
|
4270
|
+
// src/trace-ai/eval-set.ts
|
|
4271
|
+
function hashId(s) {
|
|
4272
|
+
let h = 5381;
|
|
4273
|
+
for (let i = 0; i < s.length; i++) h = h * 33 ^ s.charCodeAt(i);
|
|
4274
|
+
return `q_${(h >>> 0).toString(36)}`;
|
|
4275
|
+
}
|
|
4276
|
+
function buildCasesFromQueries(raw) {
|
|
4277
|
+
const arr = Array.isArray(raw) ? raw : Array.isArray(raw?.cases) ? raw.cases : Array.isArray(raw?.queries) ? raw.queries : [];
|
|
4278
|
+
return arr.map((entry) => {
|
|
4279
|
+
const e = entry;
|
|
4280
|
+
const userMessage = typeof e.query === "string" ? e.query : typeof e.input?.user_message === "string" ? e.input.user_message : "";
|
|
4281
|
+
const refAnswer = e.reference?.answer;
|
|
4282
|
+
return {
|
|
4283
|
+
query_id: typeof e.query_id === "string" && e.query_id ? e.query_id : hashId(userMessage),
|
|
4284
|
+
input: { user_message: userMessage },
|
|
4285
|
+
...typeof refAnswer === "string" ? { reference: { answer: refAnswer } } : {},
|
|
4286
|
+
...Array.isArray(e.assertions) ? { assertions: e.assertions } : {},
|
|
4287
|
+
...Array.isArray(e.tags) ? { tags: e.tags.map(String) } : {}
|
|
4288
|
+
};
|
|
4289
|
+
});
|
|
4290
|
+
}
|
|
4291
|
+
function applyOp(actual, op, expected) {
|
|
4292
|
+
return op === "eq" ? actual === expected : op === "lt" ? actual < expected : op === "lte" ? actual <= expected : op === "gt" ? actual > expected : actual >= expected;
|
|
4293
|
+
}
|
|
4294
|
+
function toolName2(s) {
|
|
4295
|
+
const v = s.attributes["gen_ai.tool.name"];
|
|
4296
|
+
return typeof v === "string" ? v : s.name;
|
|
4297
|
+
}
|
|
4298
|
+
function orderedToolNames(spans) {
|
|
4299
|
+
return spans.filter((s) => s.kind === "tool").slice().sort((a, b) => Number(BigInt(a.startTimeUnixNano) - BigInt(b.startTimeUnixNano))).map(toolName2);
|
|
4300
|
+
}
|
|
4301
|
+
function isSubsequence(seq, actual) {
|
|
4302
|
+
let i = 0;
|
|
4303
|
+
for (const n of actual) {
|
|
4304
|
+
if (n === seq[i]) i++;
|
|
4305
|
+
if (i === seq.length) return true;
|
|
4306
|
+
}
|
|
4307
|
+
return seq.length === 0;
|
|
4308
|
+
}
|
|
4309
|
+
async function evaluateAssertion(a, ctx) {
|
|
4310
|
+
const r = a;
|
|
4311
|
+
switch (a.type) {
|
|
4312
|
+
case "contains": {
|
|
4313
|
+
const v = String(r.value ?? "");
|
|
4314
|
+
return ctx.answer.includes(v) ? { type: a.type, verdict: "pass" } : { type: a.type, verdict: "fail", actual: ctx.answer };
|
|
4315
|
+
}
|
|
4316
|
+
case "not_contains": {
|
|
4317
|
+
const v = String(r.value ?? "");
|
|
4318
|
+
return ctx.answer.includes(v) ? { type: a.type, verdict: "fail", actual: ctx.answer } : { type: a.type, verdict: "pass" };
|
|
4319
|
+
}
|
|
4320
|
+
case "regex": {
|
|
4321
|
+
let re;
|
|
4322
|
+
try {
|
|
4323
|
+
re = new RegExp(String(r.pattern ?? ""));
|
|
4324
|
+
} catch {
|
|
4325
|
+
return { type: a.type, verdict: "skip", reason: `invalid regex: ${r.pattern}` };
|
|
4326
|
+
}
|
|
4327
|
+
return re.test(ctx.answer) ? { type: a.type, verdict: "pass" } : { type: a.type, verdict: "fail", actual: ctx.answer };
|
|
4328
|
+
}
|
|
4329
|
+
case "tool_call_count": {
|
|
4330
|
+
const count = ctx.spans.filter(
|
|
4331
|
+
(s) => s.kind === "tool" && toolName2(s) === String(r.tool ?? "")
|
|
4332
|
+
).length;
|
|
4333
|
+
return applyOp(count, r.op ?? "eq", Number(r.value ?? 0)) ? { type: a.type, verdict: "pass", actual: count } : { type: a.type, verdict: "fail", actual: count };
|
|
4334
|
+
}
|
|
4335
|
+
case "tool_call_order": {
|
|
4336
|
+
const seq = Array.isArray(r.sequence) ? r.sequence.map(String) : [];
|
|
4337
|
+
const actual = orderedToolNames(ctx.spans);
|
|
4338
|
+
return isSubsequence(seq, actual) ? { type: a.type, verdict: "pass", actual } : { type: a.type, verdict: "fail", actual };
|
|
4339
|
+
}
|
|
4340
|
+
case "latency_ms": {
|
|
4341
|
+
if (ctx.durationMs == null)
|
|
4342
|
+
return { type: a.type, verdict: "skip", reason: "durationMs unavailable" };
|
|
4343
|
+
return applyOp(ctx.durationMs, r.op ?? "lte", Number(r.value ?? 0)) ? { type: a.type, verdict: "pass", actual: ctx.durationMs } : { type: a.type, verdict: "fail", actual: ctx.durationMs };
|
|
4344
|
+
}
|
|
4345
|
+
case "semantic_match": {
|
|
4346
|
+
if (!ctx.judgeSemanticMatch)
|
|
4347
|
+
return { type: a.type, verdict: "skip", reason: "no judge (pass --llm)" };
|
|
4348
|
+
if (!ctx.reference?.answer)
|
|
4349
|
+
return { type: a.type, verdict: "skip", reason: "no reference.answer on case" };
|
|
4350
|
+
const smv = await ctx.judgeSemanticMatch(
|
|
4351
|
+
String(r.question ?? ""),
|
|
4352
|
+
ctx.answer,
|
|
4353
|
+
ctx.reference.answer
|
|
4354
|
+
);
|
|
4355
|
+
return { type: a.type, verdict: smv.verdict, actual: smv.reasoning };
|
|
4356
|
+
}
|
|
4357
|
+
default:
|
|
4358
|
+
return { type: a.type, verdict: "skip", reason: `unknown assertion type: ${a.type}` };
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
async function runEvalSet(agentId, cases, deps) {
|
|
4362
|
+
const results = [];
|
|
4363
|
+
for (const c of cases) {
|
|
4364
|
+
const query = c.input.user_message;
|
|
4365
|
+
let answer = "";
|
|
4366
|
+
let conversationId = null;
|
|
4367
|
+
let spans = [];
|
|
4368
|
+
let errorCode;
|
|
4369
|
+
let chatFailed = false;
|
|
4370
|
+
try {
|
|
4371
|
+
const run = await deps.runQuery(query);
|
|
4372
|
+
answer = run.answer;
|
|
4373
|
+
conversationId = run.conversationId;
|
|
4374
|
+
if (conversationId) {
|
|
4375
|
+
try {
|
|
4376
|
+
spans = await deps.fetchSpans(conversationId);
|
|
4377
|
+
} catch {
|
|
4378
|
+
errorCode = "trace-fetch-failed";
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4381
|
+
} catch {
|
|
4382
|
+
chatFailed = true;
|
|
4383
|
+
errorCode = "chat-failed";
|
|
4384
|
+
}
|
|
4385
|
+
const assertionResults = [];
|
|
4386
|
+
if (!chatFailed) {
|
|
4387
|
+
for (const a of c.assertions ?? []) {
|
|
4388
|
+
assertionResults.push(
|
|
4389
|
+
await evaluateAssertion(a, {
|
|
4390
|
+
answer,
|
|
4391
|
+
spans,
|
|
4392
|
+
reference: c.reference,
|
|
4393
|
+
judgeSemanticMatch: deps.judgeSemanticMatch
|
|
4394
|
+
})
|
|
4395
|
+
);
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
results.push({
|
|
4399
|
+
queryId: c.query_id,
|
|
4400
|
+
query,
|
|
4401
|
+
answer,
|
|
4402
|
+
conversationId,
|
|
4403
|
+
assertions: assertionResults,
|
|
4404
|
+
...errorCode ? { errorCode } : {}
|
|
4405
|
+
});
|
|
4406
|
+
}
|
|
4407
|
+
const flat = results.flatMap((r) => r.assertions);
|
|
4408
|
+
return {
|
|
4409
|
+
agentId,
|
|
4410
|
+
total: cases.length,
|
|
4411
|
+
passed: flat.filter((a) => a.verdict === "pass").length,
|
|
4412
|
+
failed: flat.filter((a) => a.verdict === "fail").length,
|
|
4413
|
+
skipped: flat.filter((a) => a.verdict === "skip").length,
|
|
4414
|
+
cases: results
|
|
4415
|
+
};
|
|
4416
|
+
}
|
|
4417
|
+
|
|
4418
|
+
// src/resources/trace.ts
|
|
4419
|
+
async function semanticJudge(question, answer, reference) {
|
|
4420
|
+
const prompt = [
|
|
4421
|
+
"You judge whether an agent ANSWER semantically matches a REFERENCE answer.",
|
|
4422
|
+
question ? `QUESTION/CRITERION: ${question}` : "",
|
|
4423
|
+
`ANSWER: ${answer}`,
|
|
4424
|
+
`REFERENCE: ${reference}`,
|
|
4425
|
+
'Respond with ONLY JSON: {"verdict":"pass|fail","reasoning":"<one sentence>"}'
|
|
4426
|
+
].filter(Boolean).join("\n");
|
|
4427
|
+
const out = await judgeJson(prompt, { timeoutMs: 12e4 });
|
|
4428
|
+
return {
|
|
4429
|
+
verdict: out.verdict === "pass" ? "pass" : "fail",
|
|
4430
|
+
reasoning: typeof out.reasoning === "string" ? out.reasoning : ""
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
function trace(ctx) {
|
|
4434
|
+
const diagnoseOne = async (conversationId, opts = {}) => {
|
|
4435
|
+
const { spans, traceIds } = await getRawSpansByConversation(ctx, conversationId);
|
|
4436
|
+
if (spans.length === 0) throw new Error(`No spans found for conversation: ${conversationId}`);
|
|
4437
|
+
const primaryTraceId = traceIds[0] ?? conversationId;
|
|
4438
|
+
const spansForPrimary = traceIds.length > 0 ? spans.filter((s) => !s.traceId || s.traceId === primaryTraceId) : spans;
|
|
4439
|
+
const tree = assembleTraceTree(primaryTraceId, spansForPrimary);
|
|
4440
|
+
const findings = runRules(tree);
|
|
4441
|
+
let mode = "symbolic-only";
|
|
4442
|
+
let summary;
|
|
4443
|
+
if (opts.llm && claudeAvailable()) {
|
|
4444
|
+
const judge = (prompt) => judgeJson(prompt, { timeoutMs: opts.judgeTimeoutMs });
|
|
4445
|
+
const rubric = await runRubric(tree, findings, judge);
|
|
4446
|
+
findings.push(...rubric);
|
|
4447
|
+
summary = await synthesizeFindings(findings, judge);
|
|
4448
|
+
mode = "hybrid";
|
|
4449
|
+
}
|
|
4450
|
+
return {
|
|
4451
|
+
traceId: primaryTraceId,
|
|
4452
|
+
conversationId,
|
|
4453
|
+
diagnosedAt: null,
|
|
4454
|
+
mode,
|
|
4455
|
+
rulesApplied: BUILTIN_RULES.map((r) => r.id),
|
|
4456
|
+
findingCount: findings.length,
|
|
4457
|
+
...summary ? { summary } : {},
|
|
4458
|
+
findings
|
|
4459
|
+
};
|
|
4460
|
+
};
|
|
4461
|
+
return {
|
|
4462
|
+
/** Raw trace search (OpenSearch-style body). */
|
|
4463
|
+
search: (body) => traceSearch(ctx, body),
|
|
4464
|
+
/** All span source docs for a conversation. */
|
|
4465
|
+
spans: (conversationId, opts) => getSpansByConversation(ctx, conversationId, opts),
|
|
4466
|
+
diagnose: diagnoseOne,
|
|
4467
|
+
/**
|
|
4468
|
+
* Scan (batch-diagnose) several conversations and aggregate the findings:
|
|
4469
|
+
* per-trace reports + a recurring-rule tally across the batch.
|
|
4470
|
+
*/
|
|
4471
|
+
scan: async (conversationIds, opts = {}) => {
|
|
4472
|
+
const reports = [];
|
|
4473
|
+
for (const id of conversationIds) {
|
|
4474
|
+
try {
|
|
4475
|
+
reports.push(await diagnoseOne(id, opts));
|
|
4476
|
+
} catch (e) {
|
|
4477
|
+
reports.push({
|
|
4478
|
+
conversationId: id,
|
|
4479
|
+
error: e instanceof Error ? e.message : String(e)
|
|
4480
|
+
});
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
const byRule = {};
|
|
4484
|
+
let totalFindings = 0;
|
|
4485
|
+
for (const r of reports) {
|
|
4486
|
+
if (!("findings" in r)) continue;
|
|
4487
|
+
for (const f of r.findings) {
|
|
4488
|
+
byRule[f.ruleId] = (byRule[f.ruleId] ?? 0) + 1;
|
|
4489
|
+
totalFindings += 1;
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
return {
|
|
4493
|
+
scanned: conversationIds.length,
|
|
4494
|
+
totalFindings,
|
|
4495
|
+
recurringRules: Object.entries(byRule).sort((a, b) => b[1] - a[1]).map(([ruleId, count]) => ({ ruleId, count })),
|
|
4496
|
+
reports
|
|
4497
|
+
};
|
|
4498
|
+
},
|
|
4499
|
+
/** Build eval cases from a loosely-shaped queries object/array. */
|
|
4500
|
+
evalSetBuild: (raw) => buildCasesFromQueries(raw),
|
|
4501
|
+
/**
|
|
4502
|
+
* Run an eval set against an agent: each case's query is sent to the agent,
|
|
4503
|
+
* the resulting trace is fetched, and assertions are checked. `llm` enables
|
|
4504
|
+
* `semantic_match` assertions via the local claude judge.
|
|
4505
|
+
*/
|
|
4506
|
+
evalSetTest: async (agentId, cases, opts = {}) => {
|
|
4507
|
+
const info = await fetchAgentInfo(ctx, agentId, opts.version ?? "v0");
|
|
4508
|
+
return runEvalSet(agentId, cases, {
|
|
4509
|
+
runQuery: async (query) => {
|
|
4510
|
+
const res = await sendChat(ctx, info, query, {});
|
|
4511
|
+
return { answer: res.text, conversationId: res.conversationId ?? null };
|
|
4512
|
+
},
|
|
4513
|
+
fetchSpans: async (conversationId) => {
|
|
4514
|
+
const { spans, traceIds } = await getRawSpansByConversation(ctx, conversationId);
|
|
4515
|
+
const primary = traceIds[0] ?? conversationId;
|
|
4516
|
+
return assembleTraceTree(
|
|
4517
|
+
primary,
|
|
4518
|
+
traceIds.length > 0 ? spans.filter((s) => !s.traceId || s.traceId === primary) : spans
|
|
4519
|
+
).spans;
|
|
4520
|
+
},
|
|
4521
|
+
judgeSemanticMatch: opts.llm && claudeAvailable() ? semanticJudge : void 0
|
|
4522
|
+
});
|
|
4523
|
+
}
|
|
4524
|
+
};
|
|
4525
|
+
}
|
|
4526
|
+
|
|
4527
|
+
// src/resources/vega.ts
|
|
4528
|
+
var TERMINAL_STATES = /* @__PURE__ */ new Set(["completed", "success", "failed"]);
|
|
4529
|
+
function vega(ctx) {
|
|
4530
|
+
return {
|
|
4531
|
+
catalogs: (opts) => listCatalogs(ctx, opts),
|
|
4532
|
+
getCatalog: (id) => getCatalog(ctx, id),
|
|
4533
|
+
catalogResources: (id, category) => listCatalogResources(ctx, id, category),
|
|
4534
|
+
catalogHealth: (ids) => catalogHealthStatus(ctx, ids),
|
|
4535
|
+
connectorTypes: () => listConnectorTypes(ctx),
|
|
4536
|
+
connectorType: (type) => getConnectorType(ctx, type),
|
|
4537
|
+
/** Build a resource's index. With `wait`, polls until terminal. */
|
|
4538
|
+
build: async (req, opts = {}) => {
|
|
4539
|
+
const task = await createBuildTask(ctx, req);
|
|
4540
|
+
if (!opts.wait) return task;
|
|
4541
|
+
return pollBuildTask(ctx, task.id, opts.timeoutMs ?? 3e5, opts.intervalMs ?? 2e3);
|
|
4542
|
+
},
|
|
4543
|
+
buildStatus: (taskId) => getBuildTask(ctx, taskId)
|
|
4544
|
+
};
|
|
4545
|
+
}
|
|
4546
|
+
async function pollBuildTask(ctx, taskId, timeoutMs, intervalMs) {
|
|
4547
|
+
const deadline = Date.now() + timeoutMs;
|
|
4548
|
+
let last = await getBuildTask(ctx, taskId);
|
|
4549
|
+
while (!TERMINAL_STATES.has((last.state ?? "").toLowerCase()) && Date.now() < deadline) {
|
|
4550
|
+
await sleep(intervalMs);
|
|
4551
|
+
last = await getBuildTask(ctx, taskId);
|
|
4552
|
+
}
|
|
4553
|
+
return last;
|
|
4554
|
+
}
|
|
4555
|
+
function sleep(ms) {
|
|
4556
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
4557
|
+
}
|
|
4558
|
+
|
|
4559
|
+
// src/api/call.ts
|
|
4560
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
4561
|
+
function parseHeader(raw) {
|
|
4562
|
+
const idx = raw.indexOf(":");
|
|
4563
|
+
if (idx <= 0) return null;
|
|
4564
|
+
return [raw.slice(0, idx).trim(), raw.slice(idx + 1).trim()];
|
|
4565
|
+
}
|
|
4566
|
+
function parseFormField(raw) {
|
|
4567
|
+
const idx = raw.indexOf("=");
|
|
4568
|
+
if (idx < 0) throw new Error(`Invalid form field (expected key=value): ${raw}`);
|
|
4569
|
+
const key = raw.slice(0, idx);
|
|
4570
|
+
const value = raw.slice(idx + 1);
|
|
4571
|
+
if (value.startsWith("@")) return [key, value.slice(1), true];
|
|
4572
|
+
return [key, value, false];
|
|
4573
|
+
}
|
|
4574
|
+
function resolveUrl(ctx, path) {
|
|
4575
|
+
return path.startsWith("http") ? path : `${ctx.baseUrl}${path.startsWith("/") ? "" : "/"}${path}`;
|
|
4576
|
+
}
|
|
4577
|
+
async function rawCall(ctx, path, opts = {}) {
|
|
4578
|
+
applyTls(ctx);
|
|
4579
|
+
const url = resolveUrl(ctx, path);
|
|
4580
|
+
const extra = {};
|
|
4581
|
+
for (const h of opts.header ?? []) {
|
|
4582
|
+
const parsed = parseHeader(h);
|
|
4583
|
+
if (parsed) extra[parsed[0].toLowerCase()] = parsed[1];
|
|
4584
|
+
}
|
|
4585
|
+
let body;
|
|
4586
|
+
if (opts.form && opts.form.length > 0) {
|
|
4587
|
+
const fd = new FormData();
|
|
4588
|
+
for (const field of opts.form) {
|
|
4589
|
+
const [key, value, isFile] = parseFormField(field);
|
|
4590
|
+
if (isFile) fd.append(key, new Blob([readFileSync3(value)]), value.split("/").pop());
|
|
4591
|
+
else fd.append(key, value);
|
|
4592
|
+
}
|
|
4593
|
+
body = fd;
|
|
4594
|
+
} else if (opts.data !== void 0) {
|
|
4595
|
+
body = opts.data;
|
|
4596
|
+
if (!extra["content-type"]) extra["content-type"] = "application/json";
|
|
4597
|
+
}
|
|
4598
|
+
const method = opts.method ?? (body !== void 0 ? "POST" : "GET");
|
|
4599
|
+
const headers2 = buildHeaders(
|
|
4600
|
+
{ ...ctx, businessDomain: opts.businessDomain ?? ctx.businessDomain },
|
|
4601
|
+
extra
|
|
4602
|
+
);
|
|
4603
|
+
if (opts.verbose) process.stderr.write(`> ${method} ${url}
|
|
4604
|
+
`);
|
|
4605
|
+
const controller = new AbortController();
|
|
4606
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? 3e4);
|
|
4607
|
+
try {
|
|
4608
|
+
const res = await fetch(url, { method, headers: headers2, body, signal: controller.signal });
|
|
4609
|
+
return { status: res.status, statusText: res.statusText, body: await res.text() };
|
|
4610
|
+
} finally {
|
|
4611
|
+
clearTimeout(timer);
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
|
|
4615
|
+
// src/client.ts
|
|
4616
|
+
function createClient(opts = {}) {
|
|
4617
|
+
const ctx = resolveContext(opts);
|
|
4618
|
+
return {
|
|
4619
|
+
ctx,
|
|
4620
|
+
kn: kn(ctx),
|
|
4621
|
+
resource: resources(ctx),
|
|
4622
|
+
dataflows: dataflows(ctx),
|
|
4623
|
+
agents: agents(ctx),
|
|
4624
|
+
context: context(ctx),
|
|
4625
|
+
models: models(ctx),
|
|
4626
|
+
skills: skills(ctx),
|
|
4627
|
+
toolboxes: toolboxes(ctx),
|
|
4628
|
+
trace: trace(ctx),
|
|
4629
|
+
admin: admin(ctx),
|
|
4630
|
+
vega: vega(ctx),
|
|
4631
|
+
call: (path, callOpts) => rawCall(ctx, path, callOpts)
|
|
4632
|
+
};
|
|
4633
|
+
}
|
|
4634
|
+
|
|
4635
|
+
// src/resources/auth.ts
|
|
4636
|
+
var auth_exports = {};
|
|
4637
|
+
__export(auth_exports, {
|
|
4638
|
+
attachToken: () => attachToken,
|
|
4639
|
+
currentToken: () => currentToken,
|
|
4640
|
+
deletePlatform: () => deletePlatform,
|
|
4641
|
+
exportCreds: () => exportCreds,
|
|
4642
|
+
hostOf: () => hostOf,
|
|
4643
|
+
listPlatforms: () => listPlatforms2,
|
|
4644
|
+
logout: () => logout,
|
|
4645
|
+
status: () => status,
|
|
4646
|
+
switchUser: () => switchUser,
|
|
4647
|
+
use: () => use,
|
|
4648
|
+
userIdFromToken: () => userIdFromToken,
|
|
4649
|
+
usersOf: () => usersOf,
|
|
4650
|
+
whoami: () => whoami
|
|
4651
|
+
});
|
|
4652
|
+
function hostOf(baseUrl) {
|
|
4653
|
+
try {
|
|
4654
|
+
return new URL(baseUrl).host;
|
|
4655
|
+
} catch {
|
|
4656
|
+
return baseUrl;
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
function normalize(baseUrl) {
|
|
4660
|
+
return baseUrl.replace(/\/+$/, "");
|
|
4661
|
+
}
|
|
4662
|
+
function usernameOf(token) {
|
|
4663
|
+
if (!token) return void 0;
|
|
4664
|
+
const claims = decodeJwt(token.accessToken);
|
|
4665
|
+
return token.username ?? token.displayName ?? claims?.preferred_username ?? claims?.name ?? claims?.sub;
|
|
4666
|
+
}
|
|
4667
|
+
function attachToken(baseUrl, accessToken, opts = {}) {
|
|
4668
|
+
const url = normalize(baseUrl);
|
|
4669
|
+
const token = {
|
|
4670
|
+
baseUrl: url,
|
|
4671
|
+
accessToken,
|
|
4672
|
+
refreshToken: opts.refreshToken,
|
|
4673
|
+
idToken: opts.idToken,
|
|
4674
|
+
tlsInsecure: opts.insecure,
|
|
4675
|
+
username: decodeJwt(opts.idToken ?? accessToken)?.preferred_username
|
|
4676
|
+
};
|
|
4677
|
+
const userId = writeToken(url, token);
|
|
4678
|
+
setActivePlatform(url);
|
|
4679
|
+
return { baseUrl: url, userId, username: usernameOf(token) };
|
|
4680
|
+
}
|
|
4681
|
+
function status() {
|
|
4682
|
+
const baseUrl = activePlatform();
|
|
4683
|
+
if (!baseUrl) return { hasToken: false };
|
|
4684
|
+
const token = readToken(baseUrl);
|
|
4685
|
+
return {
|
|
4686
|
+
baseUrl,
|
|
4687
|
+
userId: activeUserId(baseUrl),
|
|
4688
|
+
hasToken: token !== void 0,
|
|
4689
|
+
username: usernameOf(token),
|
|
4690
|
+
expired: token ? isExpired(decodeJwt(token.accessToken)) : void 0
|
|
4691
|
+
};
|
|
4692
|
+
}
|
|
4693
|
+
function currentToken() {
|
|
4694
|
+
const baseUrl = activePlatform();
|
|
4695
|
+
const token = baseUrl ? readToken(baseUrl) : void 0;
|
|
4696
|
+
if (!token) throw new InputError("Not logged in. Run `openbkn auth login <url> --token <t>`.");
|
|
4697
|
+
return token.accessToken;
|
|
4698
|
+
}
|
|
4699
|
+
function whoami() {
|
|
4700
|
+
const baseUrl = activePlatform();
|
|
4701
|
+
const token = baseUrl ? readToken(baseUrl) : void 0;
|
|
4702
|
+
const claims = decodeJwt(token?.idToken ?? "") ?? decodeJwt(token?.accessToken ?? "");
|
|
4703
|
+
if (!claims) {
|
|
4704
|
+
throw new InputError(
|
|
4705
|
+
"No decodable identity (token is opaque and no id_token saved). Use `auth status`."
|
|
4706
|
+
);
|
|
4707
|
+
}
|
|
4708
|
+
return claims;
|
|
4709
|
+
}
|
|
4710
|
+
function listPlatforms2() {
|
|
4711
|
+
const current = activePlatform();
|
|
4712
|
+
return listPlatforms().flatMap(
|
|
4713
|
+
(p) => p.users.map((u) => ({
|
|
4714
|
+
baseUrl: p.baseUrl,
|
|
4715
|
+
userId: u.userId,
|
|
4716
|
+
username: u.username ?? u.displayName,
|
|
4717
|
+
active: p.baseUrl === current && u.userId === p.activeUserId
|
|
4718
|
+
}))
|
|
4719
|
+
);
|
|
4720
|
+
}
|
|
4721
|
+
function use(baseUrl) {
|
|
4722
|
+
const url = normalize(baseUrl);
|
|
4723
|
+
if (!readToken(url)) {
|
|
4724
|
+
throw new InputError(`No saved credentials for ${url}. Run \`openbkn auth login\` first.`);
|
|
4725
|
+
}
|
|
4726
|
+
setActivePlatform(url);
|
|
4727
|
+
}
|
|
4728
|
+
function logout() {
|
|
4729
|
+
const baseUrl = activePlatform();
|
|
4730
|
+
return baseUrl ? deleteToken(baseUrl) : false;
|
|
4731
|
+
}
|
|
4732
|
+
function deletePlatform(baseUrl, userId) {
|
|
4733
|
+
return deleteToken(normalize(baseUrl), userId);
|
|
4734
|
+
}
|
|
4735
|
+
function switchUser(baseUrl, userId) {
|
|
4736
|
+
const url = normalize(baseUrl);
|
|
4737
|
+
if (!readToken(url, userId)) {
|
|
4738
|
+
throw new InputError(`No saved token for user '${userId}' on ${url}.`);
|
|
4739
|
+
}
|
|
4740
|
+
setActiveUser(url, userId);
|
|
4741
|
+
setActivePlatform(url);
|
|
4742
|
+
return { baseUrl: url, userId };
|
|
4743
|
+
}
|
|
4744
|
+
function usersOf(baseUrl) {
|
|
4745
|
+
const url = normalize(baseUrl);
|
|
4746
|
+
return listPlatforms().find((p) => p.baseUrl === url)?.users ?? [];
|
|
4747
|
+
}
|
|
4748
|
+
function exportCreds() {
|
|
4749
|
+
const baseUrl = activePlatform();
|
|
4750
|
+
if (!baseUrl) throw new InputError("No active platform. Run `openbkn auth login` first.");
|
|
4751
|
+
const token = readToken(baseUrl);
|
|
4752
|
+
if (!token) throw new InputError(`No saved token for ${baseUrl}.`);
|
|
4753
|
+
return {
|
|
4754
|
+
baseUrl,
|
|
4755
|
+
accessToken: token.accessToken,
|
|
4756
|
+
refreshToken: token.refreshToken,
|
|
4757
|
+
idToken: token.idToken
|
|
4758
|
+
};
|
|
4759
|
+
}
|
|
4760
|
+
|
|
4761
|
+
export {
|
|
4762
|
+
rawCall,
|
|
4763
|
+
DEFAULT_BUSINESS_DOMAIN,
|
|
4764
|
+
DEFAULT_LIST_LIMIT,
|
|
4765
|
+
DEFAULT_QUERY_LIMIT,
|
|
4766
|
+
HttpError,
|
|
4767
|
+
InputError,
|
|
4768
|
+
toExitCode,
|
|
4769
|
+
formatError,
|
|
4770
|
+
activePlatform,
|
|
4771
|
+
setActivePlatform,
|
|
4772
|
+
readPlatformConfig,
|
|
4773
|
+
writePlatformConfig,
|
|
4774
|
+
resolveContext,
|
|
4775
|
+
request,
|
|
4776
|
+
changePassword,
|
|
4777
|
+
renderOrgTree,
|
|
4778
|
+
admin,
|
|
4779
|
+
agents,
|
|
4780
|
+
context,
|
|
4781
|
+
dataflows,
|
|
4782
|
+
parsePkMap,
|
|
4783
|
+
kn,
|
|
4784
|
+
models,
|
|
4785
|
+
resources,
|
|
4786
|
+
skills,
|
|
4787
|
+
toolboxes,
|
|
4788
|
+
renderReportMarkdown,
|
|
4789
|
+
trace,
|
|
4790
|
+
vega,
|
|
4791
|
+
createClient,
|
|
4792
|
+
attachToken,
|
|
4793
|
+
status,
|
|
4794
|
+
currentToken,
|
|
4795
|
+
whoami,
|
|
4796
|
+
listPlatforms2 as listPlatforms,
|
|
4797
|
+
use,
|
|
4798
|
+
logout,
|
|
4799
|
+
deletePlatform,
|
|
4800
|
+
switchUser,
|
|
4801
|
+
usersOf,
|
|
4802
|
+
exportCreds,
|
|
4803
|
+
auth_exports
|
|
4804
|
+
};
|
|
4805
|
+
//# sourceMappingURL=chunk-4NXAIG4G.js.map
|