@openbkn/bkn-sdk 0.1.1-alpha.1 → 0.1.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ADZ23DPF.js → chunk-5MOIXIMJ.js} +479 -372
- package/dist/chunk-5MOIXIMJ.js.map +1 -0
- package/dist/cli.js +257 -344
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +70 -34
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-ADZ23DPF.js.map +0 -1
|
@@ -38,8 +38,12 @@ function toExitCode(err) {
|
|
|
38
38
|
}
|
|
39
39
|
function formatError(err) {
|
|
40
40
|
if (err instanceof HttpError) {
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const serverMsg = serverError(err.body);
|
|
42
|
+
if (err.status === 401) {
|
|
43
|
+
return `Not authorized (HTTP 401)${serverMsg ? `: ${serverMsg}` : ""}. Run \`openbkn auth login\` and retry.`;
|
|
44
|
+
}
|
|
45
|
+
if (err.status === 403) {
|
|
46
|
+
return `Forbidden (HTTP 403)${serverMsg ? `: ${serverMsg}` : " \u2014 admin privileges required"}.`;
|
|
43
47
|
}
|
|
44
48
|
const detail = err.body ? `: ${truncate(err.body, 500)}` : "";
|
|
45
49
|
return `Request failed (HTTP ${err.status} ${err.statusText})${detail}`;
|
|
@@ -56,6 +60,16 @@ function formatError(err) {
|
|
|
56
60
|
}
|
|
57
61
|
return String(err);
|
|
58
62
|
}
|
|
63
|
+
function serverError(body) {
|
|
64
|
+
if (!body) return "";
|
|
65
|
+
try {
|
|
66
|
+
const j = JSON.parse(body);
|
|
67
|
+
const m = j.error ?? j.detail ?? j.description ?? j.message;
|
|
68
|
+
return typeof m === "string" ? m : "";
|
|
69
|
+
} catch {
|
|
70
|
+
return "";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
59
73
|
function isTlsCertError(code) {
|
|
60
74
|
return code === "DEPTH_ZERO_SELF_SIGNED_CERT" || code === "SELF_SIGNED_CERT_IN_CHAIN" || code === "UNABLE_TO_VERIFY_LEAF_SIGNATURE" || code === "CERT_HAS_EXPIRED" || code.includes("CERT");
|
|
61
75
|
}
|
|
@@ -220,16 +234,208 @@ function resolveContext(opts = {}) {
|
|
|
220
234
|
}
|
|
221
235
|
const normalized = baseUrl.replace(/\/+$/, "");
|
|
222
236
|
const stored = readToken(normalized);
|
|
223
|
-
const
|
|
237
|
+
const explicit = opts.token ?? process.env.BKN_TOKEN;
|
|
238
|
+
const token = explicit ?? stored?.accessToken;
|
|
224
239
|
if (!token) {
|
|
225
240
|
throw new InputError("No access token. Set BKN_TOKEN or run `openbkn auth login`.");
|
|
226
241
|
}
|
|
242
|
+
const insecure = opts.insecure ?? stored?.tlsInsecure ?? false;
|
|
243
|
+
const refresh = !explicit && stored?.refreshToken ? {
|
|
244
|
+
refreshToken: stored.refreshToken,
|
|
245
|
+
persist: (t) => {
|
|
246
|
+
writeToken(normalized, {
|
|
247
|
+
...stored,
|
|
248
|
+
accessToken: t.accessToken,
|
|
249
|
+
refreshToken: t.refreshToken ?? stored.refreshToken,
|
|
250
|
+
idToken: t.idToken ?? stored.idToken
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
} : void 0;
|
|
227
254
|
return {
|
|
228
255
|
baseUrl: normalized,
|
|
229
256
|
token,
|
|
230
257
|
businessDomain: opts.businessDomain ?? readPlatformConfig(normalized).businessDomain ?? DEFAULT_BUSINESS_DOMAIN,
|
|
231
|
-
insecure
|
|
258
|
+
insecure,
|
|
259
|
+
...refresh ? { refresh } : {}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// src/auth/oauth.ts
|
|
264
|
+
import { spawn } from "child_process";
|
|
265
|
+
function normalizeBaseUrl(value) {
|
|
266
|
+
return value.replace(/\/+$/, "");
|
|
267
|
+
}
|
|
268
|
+
function mapToken(data) {
|
|
269
|
+
return {
|
|
270
|
+
accessToken: data.access_token,
|
|
271
|
+
refreshToken: data.refresh_token,
|
|
272
|
+
idToken: data.id_token
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function openBrowser(url) {
|
|
276
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
277
|
+
try {
|
|
278
|
+
spawn(cmd, [url], {
|
|
279
|
+
stdio: "ignore",
|
|
280
|
+
detached: true,
|
|
281
|
+
shell: process.platform === "win32"
|
|
282
|
+
}).unref();
|
|
283
|
+
} catch {
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function mergeCookies(existing, res) {
|
|
287
|
+
const setCookies = typeof res.headers.getSetCookie === "function" ? res.headers.getSetCookie() : res.headers.get("set-cookie") ? [res.headers.get("set-cookie")] : [];
|
|
288
|
+
const map = /* @__PURE__ */ new Map();
|
|
289
|
+
const add = (pair) => {
|
|
290
|
+
const eq = pair.indexOf("=");
|
|
291
|
+
if (eq > 0) map.set(pair.slice(0, eq), pair.slice(eq + 1));
|
|
292
|
+
};
|
|
293
|
+
for (const p of existing.split(";").map((s) => s.trim()).filter(Boolean))
|
|
294
|
+
add(p);
|
|
295
|
+
for (const sc of setCookies) add(sc.split(";")[0]?.trim() ?? "");
|
|
296
|
+
return [...map.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
|
|
297
|
+
}
|
|
298
|
+
async function refreshAccessToken(baseUrl, refreshToken, clientId = "openbkn-sdk") {
|
|
299
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
300
|
+
const res = await fetch(`${base}/oauth2/token`, {
|
|
301
|
+
method: "POST",
|
|
302
|
+
headers: {
|
|
303
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
304
|
+
Accept: "application/json"
|
|
305
|
+
},
|
|
306
|
+
body: new URLSearchParams({
|
|
307
|
+
grant_type: "refresh_token",
|
|
308
|
+
refresh_token: refreshToken,
|
|
309
|
+
client_id: clientId
|
|
310
|
+
}).toString()
|
|
311
|
+
});
|
|
312
|
+
if (!res.ok) {
|
|
313
|
+
throw new Error(
|
|
314
|
+
`Token refresh failed (${res.status}): ${await res.text() || res.statusText}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
return mapToken(await res.json());
|
|
318
|
+
}
|
|
319
|
+
var DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
320
|
+
var DEFAULT_DEVICE_CLIENT_ID = "openbkn-sdk";
|
|
321
|
+
var DEVICE_SCOPE = "openid offline";
|
|
322
|
+
var form = (body) => ({
|
|
323
|
+
method: "POST",
|
|
324
|
+
headers: {
|
|
325
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
326
|
+
Accept: "application/json"
|
|
327
|
+
},
|
|
328
|
+
body: new URLSearchParams(body).toString()
|
|
329
|
+
});
|
|
330
|
+
async function requestDeviceCode(base, clientId, scope, audience) {
|
|
331
|
+
const params = { client_id: clientId, scope };
|
|
332
|
+
if (audience) params.audience = audience;
|
|
333
|
+
const res = await fetch(`${base}/oauth2/device/auth`, form(params));
|
|
334
|
+
if (!res.ok) {
|
|
335
|
+
throw new Error(`Device auth failed (${res.status}): ${await res.text() || res.statusText}`);
|
|
336
|
+
}
|
|
337
|
+
const da = await res.json();
|
|
338
|
+
if (!da.verification_uri) {
|
|
339
|
+
throw new Error(
|
|
340
|
+
"Device auth returned no verification_uri \u2014 is Hydra's device flow configured?"
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
return da;
|
|
344
|
+
}
|
|
345
|
+
async function pollDeviceToken(base, deviceCode, clientId, intervalMs, windowMs) {
|
|
346
|
+
let interval = intervalMs;
|
|
347
|
+
const deadline = Date.now() + windowMs;
|
|
348
|
+
while (Date.now() < deadline) {
|
|
349
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
350
|
+
const tokRes = await fetch(
|
|
351
|
+
`${base}/oauth2/token`,
|
|
352
|
+
form({ grant_type: DEVICE_GRANT, device_code: deviceCode, client_id: clientId })
|
|
353
|
+
);
|
|
354
|
+
const data = await tokRes.json().catch(() => ({}));
|
|
355
|
+
if (tokRes.ok) return mapToken(data);
|
|
356
|
+
switch (data.error) {
|
|
357
|
+
case "authorization_pending":
|
|
358
|
+
break;
|
|
359
|
+
// keep polling
|
|
360
|
+
case "slow_down":
|
|
361
|
+
interval += 5e3;
|
|
362
|
+
break;
|
|
363
|
+
case "access_denied":
|
|
364
|
+
throw new InputError("Device authorization denied.");
|
|
365
|
+
case "expired_token":
|
|
366
|
+
throw new InputError("Device code expired \u2014 run login again.");
|
|
367
|
+
default:
|
|
368
|
+
throw new Error(`Device token poll failed: ${String(data.error ?? tokRes.status)}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
throw new InputError("Device login timed out.");
|
|
372
|
+
}
|
|
373
|
+
async function deviceLogin(baseUrl, opts = {}) {
|
|
374
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
375
|
+
const clientId = opts.clientId ?? DEFAULT_DEVICE_CLIENT_ID;
|
|
376
|
+
const da = await requestDeviceCode(base, clientId, opts.scope ?? DEVICE_SCOPE, opts.audience);
|
|
377
|
+
opts.onPrompt?.({
|
|
378
|
+
userCode: da.user_code,
|
|
379
|
+
verificationUri: onBaseHost(base, da.verification_uri),
|
|
380
|
+
verificationUriComplete: da.verification_uri_complete ? onBaseHost(base, da.verification_uri_complete) : void 0
|
|
381
|
+
});
|
|
382
|
+
const windowMs = Math.min(opts.timeoutMs ?? Number.POSITIVE_INFINITY, da.expires_in * 1e3);
|
|
383
|
+
return pollDeviceToken(base, da.device_code, clientId, (da.interval ?? 5) * 1e3, windowMs);
|
|
384
|
+
}
|
|
385
|
+
function onBaseHost(base, uri) {
|
|
386
|
+
try {
|
|
387
|
+
const u = new URL(uri);
|
|
388
|
+
const b = new URL(base);
|
|
389
|
+
return `${b.origin}${u.pathname}${u.search}`;
|
|
390
|
+
} catch {
|
|
391
|
+
return uri;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
async function credentialDeviceLogin(baseUrl, username, password, opts = {}) {
|
|
395
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
396
|
+
const clientId = opts.clientId ?? DEFAULT_DEVICE_CLIENT_ID;
|
|
397
|
+
const da = await requestDeviceCode(base, clientId, opts.scope ?? DEVICE_SCOPE, opts.audience);
|
|
398
|
+
let jar = "";
|
|
399
|
+
const hop = async (url, init) => {
|
|
400
|
+
const r = await fetch(url, {
|
|
401
|
+
method: init?.method ?? "GET",
|
|
402
|
+
headers: { Cookie: jar, Accept: "text/html,*/*;q=0.8", ...init?.headers ?? {} },
|
|
403
|
+
body: init?.body,
|
|
404
|
+
redirect: "manual"
|
|
405
|
+
});
|
|
406
|
+
jar = mergeCookies(jar, r);
|
|
407
|
+
return r;
|
|
232
408
|
};
|
|
409
|
+
const postForm = (path, body) => hop(`${base}${path}`, {
|
|
410
|
+
method: "POST",
|
|
411
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
412
|
+
body: new URLSearchParams(body).toString()
|
|
413
|
+
});
|
|
414
|
+
const v = await hop(`${base}/oauth2/device/verify?user_code=${encodeURIComponent(da.user_code)}`);
|
|
415
|
+
let loc = v.headers.get("location");
|
|
416
|
+
for (let i = 0; i < 25 && loc; i++) {
|
|
417
|
+
const u = new URL(loc, base);
|
|
418
|
+
let r;
|
|
419
|
+
if (u.pathname.endsWith("/device")) {
|
|
420
|
+
const dc = u.searchParams.get("device_challenge");
|
|
421
|
+
if (!dc) throw new Error(`/device without device_challenge: ${loc}`);
|
|
422
|
+
r = await postForm("/device", { device_challenge: dc, user_code: da.user_code });
|
|
423
|
+
} else if (u.pathname.endsWith("/login")) {
|
|
424
|
+
const lc = u.searchParams.get("login_challenge");
|
|
425
|
+
if (!lc) throw new Error(`/login without login_challenge: ${loc}`);
|
|
426
|
+
r = await postForm("/login", { login_challenge: lc, account: username, password });
|
|
427
|
+
if (r.status === 401) throw new InputError("Sign-in failed: wrong account or password");
|
|
428
|
+
} else if (u.pathname.endsWith("/consent")) {
|
|
429
|
+
const cc = u.searchParams.get("consent_challenge");
|
|
430
|
+
if (!cc) throw new Error(`/consent without consent_challenge: ${loc}`);
|
|
431
|
+
r = await postForm("/consent", { consent_challenge: cc, decision: "allow" });
|
|
432
|
+
} else {
|
|
433
|
+
r = await hop(u.href);
|
|
434
|
+
}
|
|
435
|
+
loc = r.headers.get("location");
|
|
436
|
+
}
|
|
437
|
+
const windowMs = Math.min(opts.timeoutMs ?? Number.POSITIVE_INFINITY, da.expires_in * 1e3);
|
|
438
|
+
return pollDeviceToken(base, da.device_code, clientId, (da.interval ?? 5) * 1e3, windowMs);
|
|
233
439
|
}
|
|
234
440
|
|
|
235
441
|
// src/api/headers.ts
|
|
@@ -260,16 +466,20 @@ async function request(ctx, path, init = {}) {
|
|
|
260
466
|
const hasBody = init.body !== void 0;
|
|
261
467
|
const controller = new AbortController();
|
|
262
468
|
const timer = setTimeout(() => controller.abort(), init.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
469
|
+
const send = () => fetch(url, {
|
|
470
|
+
method: init.method ?? (hasBody ? "POST" : "GET"),
|
|
471
|
+
headers: buildHeaders(ctx, {
|
|
472
|
+
...hasBody ? { "content-type": "application/json" } : {},
|
|
473
|
+
...init.headers
|
|
474
|
+
}),
|
|
475
|
+
body: hasBody ? JSON.stringify(init.body) : void 0,
|
|
476
|
+
signal: controller.signal
|
|
477
|
+
});
|
|
263
478
|
try {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
...init.headers
|
|
269
|
-
}),
|
|
270
|
-
body: hasBody ? JSON.stringify(init.body) : void 0,
|
|
271
|
-
signal: controller.signal
|
|
272
|
-
});
|
|
479
|
+
let res = await send();
|
|
480
|
+
if (res.status === 401 && ctx.refresh && await tryRefresh(ctx)) {
|
|
481
|
+
res = await send();
|
|
482
|
+
}
|
|
273
483
|
const text = await res.text();
|
|
274
484
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
275
485
|
return text ? JSON.parse(text) : void 0;
|
|
@@ -277,364 +487,244 @@ async function request(ctx, path, init = {}) {
|
|
|
277
487
|
clearTimeout(timer);
|
|
278
488
|
}
|
|
279
489
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
+/HrWmxPoON+0y/tFQxxfNgsUodFzbdh0XY1rIVUIbPLvufUBbLKXHDPpwIDAQAB
|
|
292
|
-
AoGBALCM/H6ajXFs1nCR903aCVicUzoS9qckzI0SIhIOPCfMBp8+PAJTSJl9/ohU
|
|
293
|
-
YnhVj/kmVXwBvboxyJAmOcxdRPWL7iTk5nA1oiVXMer3Wby+tRg/ls91xQbJLVv3
|
|
294
|
-
oGSt7q0CXxJpRH2oYkVVlMMlZUwKz3ovHiLKAnhw+jEsdL2BAkEA9hA97yyeA2eq
|
|
295
|
-
f9dMu/ici99R3WJRRtk4NEI4WShtWPyziDg48d3SOzYmhEJjPuOo3g1ze01os70P
|
|
296
|
-
ApE7d0qcyQJBAMmt+FR8h5MwxPQPAzjh/fTuTttvUfBeMiUDrIycK1I/L96lH+fU
|
|
297
|
-
i4Nu+7TPOzExnPeGO5UJbZxrpIEUB7Zs8O8CQQCLzTCTGiNwxc5eMgH77kVrRudp
|
|
298
|
-
Q7nv6ex/7Hu9VDXEUFbkdyULbj9KuvppPJrMmWZROw04qgNp02mayM8jeLXZAkEA
|
|
299
|
-
o+PM/pMn9TPXiWE9xBbaMhUKXgXLd2KEq1GeAbHS/oY8l1hmYhV1vjwNLbSNrH9d
|
|
300
|
-
yEP73TQJL+jFiONHFTbYXwJAU03Xgum5mLIkX/02LpOrz2QCdfX1IMJk2iKi9osV
|
|
301
|
-
KqfbvHsF0+GvFGg18/FXStG9Kr4TjqLsygQJT76/MnMluw==
|
|
302
|
-
-----END RSA PRIVATE KEY-----`;
|
|
303
|
-
var cachedKey;
|
|
304
|
-
function publicKey() {
|
|
305
|
-
if (!cachedKey) cachedKey = createPublicKey(createPrivateKey(EACP_MODIFYPWD_PRIVATE_KEY_PEM));
|
|
306
|
-
return cachedKey;
|
|
307
|
-
}
|
|
308
|
-
function encryptModifyPwd(plain) {
|
|
309
|
-
const buf = publicEncrypt(
|
|
310
|
-
{ key: publicKey(), padding: constants.RSA_PKCS1_PADDING },
|
|
311
|
-
Buffer.from(plain, "utf8")
|
|
312
|
-
);
|
|
313
|
-
return buf.toString("base64");
|
|
490
|
+
async function tryRefresh(ctx) {
|
|
491
|
+
if (!ctx.refresh) return false;
|
|
492
|
+
try {
|
|
493
|
+
const t = await refreshAccessToken(ctx.baseUrl, ctx.refresh.refreshToken, ctx.refresh.clientId);
|
|
494
|
+
ctx.token = t.accessToken;
|
|
495
|
+
if (t.refreshToken) ctx.refresh.refreshToken = t.refreshToken;
|
|
496
|
+
ctx.refresh.persist(t);
|
|
497
|
+
return true;
|
|
498
|
+
} catch {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
314
501
|
}
|
|
315
502
|
|
|
316
|
-
// src/api/
|
|
317
|
-
var
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const sub = decodeJwt(ctx.token)?.sub;
|
|
322
|
-
if (sub) return sub;
|
|
323
|
-
const info = await request(ctx, "/api/eacp/v1/user/get");
|
|
324
|
-
if (info?.userid) return info.userid;
|
|
325
|
-
throw new Error(
|
|
326
|
-
"Cannot resolve the caller user id (token has no JWT `sub` and eacp/v1/user/get returned no `userid`). Re-login."
|
|
503
|
+
// src/api/safe.ts
|
|
504
|
+
var ADMIN = "/api/safe/v1/admin";
|
|
505
|
+
function notOnSafe(operation) {
|
|
506
|
+
throw new InputError(
|
|
507
|
+
`'${operation}' is not available on bkn-safe \u2014 its admin API has no such endpoint. See docs/exec-plans/admin-bkn-safe-migration.md.`
|
|
327
508
|
);
|
|
328
509
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
} catch (e) {
|
|
333
|
-
if (e instanceof HttpError) {
|
|
334
|
-
try {
|
|
335
|
-
const parsed = JSON.parse(e.body);
|
|
336
|
-
const err = parsed?.error;
|
|
337
|
-
if (err?.errMsg) {
|
|
338
|
-
const m = err.errID !== void 0 ? `${err.errMsg} (errID=${err.errID})` : err.errMsg;
|
|
339
|
-
throw new Error(`ShareMgnt.${method} failed: ${m}`);
|
|
340
|
-
}
|
|
341
|
-
} catch (inner) {
|
|
342
|
-
if (inner instanceof Error && inner.message.startsWith("ShareMgnt.")) throw inner;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
throw e;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
var DEPT_FIELDS = "name,code,remark,manager,enabled,parent_deps,email";
|
|
349
|
-
var USER_FIELDS = "name,account,email,enabled,frozen,parent_deps,roles";
|
|
350
|
-
function listDepartments(ctx, opts = {}) {
|
|
351
|
-
return request(ctx, `${UM}/console/search-departments/${DEPT_FIELDS}`, {
|
|
352
|
-
query: {
|
|
353
|
-
role: opts.role ?? "super_admin",
|
|
354
|
-
offset: opts.offset ?? 0,
|
|
355
|
-
limit: opts.limit ?? 100,
|
|
356
|
-
name: opts.name || void 0
|
|
357
|
-
}
|
|
510
|
+
function listUsersSafe(ctx, opts = {}) {
|
|
511
|
+
return request(ctx, `${ADMIN}/users`, {
|
|
512
|
+
query: { search: opts.search || void 0, offset: opts.offset, limit: opts.limit }
|
|
358
513
|
});
|
|
359
514
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
let offset = 0;
|
|
363
|
-
for (; ; ) {
|
|
364
|
-
const data = await listDepartments(ctx, { role, offset, limit: pageSize });
|
|
365
|
-
const entries = data.entries ?? [];
|
|
366
|
-
out.push(...entries);
|
|
367
|
-
if (entries.length < pageSize) break;
|
|
368
|
-
if (data.total_count !== void 0 && out.length >= data.total_count) break;
|
|
369
|
-
offset += pageSize;
|
|
370
|
-
}
|
|
371
|
-
return out;
|
|
515
|
+
function getUserSafe(ctx, userId) {
|
|
516
|
+
return request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`);
|
|
372
517
|
}
|
|
373
|
-
function
|
|
374
|
-
return request(ctx, `${
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
518
|
+
function createUserSafe(ctx, input) {
|
|
519
|
+
return request(ctx, `${ADMIN}/users`, {
|
|
520
|
+
method: "POST",
|
|
521
|
+
body: {
|
|
522
|
+
account: input.account,
|
|
523
|
+
password: input.password,
|
|
524
|
+
...input.name ? { name: input.name } : {},
|
|
525
|
+
...input.email ? { email: input.email } : {},
|
|
526
|
+
...input.accountType ? { account_type: input.accountType } : {},
|
|
527
|
+
...input.id ? { id: input.id } : {}
|
|
381
528
|
}
|
|
382
529
|
});
|
|
383
530
|
}
|
|
384
|
-
function
|
|
385
|
-
return request(ctx, `${
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
531
|
+
function updateUserSafe(ctx, userId, input) {
|
|
532
|
+
return request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`, {
|
|
533
|
+
method: "PUT",
|
|
534
|
+
body: {
|
|
535
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
536
|
+
...input.email !== void 0 ? { email: input.email } : {},
|
|
537
|
+
...input.telephone !== void 0 ? { telephone: input.telephone } : {},
|
|
538
|
+
...input.enabled !== void 0 ? { enabled: input.enabled } : {},
|
|
539
|
+
...input.accountType !== void 0 ? { account_type: input.accountType } : {}
|
|
390
540
|
}
|
|
391
541
|
});
|
|
392
542
|
}
|
|
393
|
-
function
|
|
394
|
-
|
|
543
|
+
async function deleteUserSafe(ctx, userId) {
|
|
544
|
+
await request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`, { method: "DELETE" });
|
|
545
|
+
return { ok: true };
|
|
395
546
|
}
|
|
396
|
-
function
|
|
397
|
-
|
|
547
|
+
async function setUserPasswordSafe(ctx, userId, password) {
|
|
548
|
+
await request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}/password`, {
|
|
549
|
+
method: "PUT",
|
|
550
|
+
body: { password }
|
|
551
|
+
});
|
|
552
|
+
return { ok: true };
|
|
398
553
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
return await request(ctx, `${AUTHZ}/accessor_roles`, {
|
|
402
|
-
query: { accessor_id: userId, accessor_type: "user" }
|
|
403
|
-
});
|
|
404
|
-
} catch (e) {
|
|
405
|
-
if (!(e instanceof HttpError) || e.status !== 404) throw e;
|
|
406
|
-
const rolesPage = await listRoles(ctx, { offset: 0, limit: 200 });
|
|
407
|
-
const roles = rolesPage.entries ?? [];
|
|
408
|
-
const matched = [];
|
|
409
|
-
await Promise.all(
|
|
410
|
-
roles.map(async (role) => {
|
|
411
|
-
const members = await listRoleMembers(ctx, role.id, { offset: 0, limit: 500 });
|
|
412
|
-
if ((members.entries ?? []).some((m) => m.id === userId && (!m.type || m.type === "user"))) {
|
|
413
|
-
matched.push(role);
|
|
414
|
-
}
|
|
415
|
-
})
|
|
416
|
-
);
|
|
417
|
-
return {
|
|
418
|
-
entries: matched,
|
|
419
|
-
total_count: matched.length,
|
|
420
|
-
route: "fallback:list-roles+role-members"
|
|
421
|
-
};
|
|
422
|
-
}
|
|
554
|
+
function getUserRolesSafe(ctx, userId) {
|
|
555
|
+
return request(ctx, `${ADMIN}/role-bindings`, { query: { accessor_id: userId } });
|
|
423
556
|
}
|
|
424
|
-
async function
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
return shareMgnt(ctx, "Usrm_GetDepartmentById", [deptId]);
|
|
431
|
-
}
|
|
557
|
+
async function assignRoleSafe(ctx, accessorId, roleId) {
|
|
558
|
+
await request(ctx, `${ADMIN}/role-bindings`, {
|
|
559
|
+
method: "POST",
|
|
560
|
+
body: { accessor_id: accessorId, role_id: roleId }
|
|
561
|
+
});
|
|
562
|
+
return { ok: true };
|
|
432
563
|
}
|
|
433
|
-
function
|
|
434
|
-
|
|
435
|
-
|
|
564
|
+
async function removeRoleSafe(ctx, accessorId, roleId) {
|
|
565
|
+
await request(ctx, `${ADMIN}/role-bindings`, {
|
|
566
|
+
method: "DELETE",
|
|
567
|
+
body: { accessor_id: accessorId, role_id: roleId }
|
|
436
568
|
});
|
|
569
|
+
return { ok: true };
|
|
437
570
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
email: input.email ?? "",
|
|
447
|
-
ossId: ""
|
|
448
|
-
};
|
|
449
|
-
const id = await shareMgnt(ctx, "Usrm_AddDepartment", [{ ncTAddDepartParam }]);
|
|
450
|
-
return { id };
|
|
451
|
-
}
|
|
452
|
-
async function updateDepartment(ctx, deptId, input) {
|
|
453
|
-
const p = { departId: deptId };
|
|
454
|
-
if (input.name !== void 0) p.departName = input.name;
|
|
455
|
-
if (input.managerID !== void 0) p.managerID = input.managerID;
|
|
456
|
-
if (input.code !== void 0) p.code = input.code;
|
|
457
|
-
if (input.remark !== void 0) p.remark = input.remark;
|
|
458
|
-
if (input.status !== void 0) p.status = input.status;
|
|
459
|
-
if (input.email !== void 0) p.email = input.email;
|
|
460
|
-
await shareMgnt(ctx, "Usrm_EditDepartment", [{ ncTEditDepartParam: p }]);
|
|
461
|
-
return { id: deptId, updated: true };
|
|
462
|
-
}
|
|
463
|
-
async function deleteDepartment(ctx, deptId) {
|
|
464
|
-
await request(ctx, `${UM}/management/departments/${encodeURIComponent(deptId)}`, {
|
|
465
|
-
method: "DELETE"
|
|
571
|
+
function listDepartmentsSafe(ctx, opts = {}) {
|
|
572
|
+
return request(ctx, `${ADMIN}/departments`, {
|
|
573
|
+
query: {
|
|
574
|
+
search: opts.search || void 0,
|
|
575
|
+
parent_id: opts.parentId,
|
|
576
|
+
offset: opts.offset,
|
|
577
|
+
limit: opts.limit
|
|
578
|
+
}
|
|
466
579
|
});
|
|
467
|
-
return { id: deptId, deleted: true };
|
|
468
|
-
}
|
|
469
|
-
async function createUser(ctx, input) {
|
|
470
|
-
const ncTUsrmUserInfo = {
|
|
471
|
-
loginName: input.loginName,
|
|
472
|
-
displayName: input.displayName ?? input.loginName,
|
|
473
|
-
code: input.code ?? "",
|
|
474
|
-
position: input.position ?? "",
|
|
475
|
-
managerID: null,
|
|
476
|
-
managerDisplayName: null,
|
|
477
|
-
remark: input.remark ?? "",
|
|
478
|
-
email: input.email ?? "",
|
|
479
|
-
telNumber: input.telNumber ?? "",
|
|
480
|
-
idcardNumber: "",
|
|
481
|
-
departmentIds: input.departmentIds ?? ["-1"],
|
|
482
|
-
priority: input.priority ?? 999,
|
|
483
|
-
csfLevel2: null,
|
|
484
|
-
pwdControl: false,
|
|
485
|
-
expireTime: -1
|
|
486
|
-
};
|
|
487
|
-
if (input.csfLevel !== void 0) ncTUsrmUserInfo.csfLevel = input.csfLevel;
|
|
488
|
-
const id = await shareMgnt(ctx, "Usrm_AddUser", [
|
|
489
|
-
{ ncTUsrmAddUserInfo: { user: { ncTUsrmUserInfo } } },
|
|
490
|
-
await callerUserId(ctx)
|
|
491
|
-
]);
|
|
492
|
-
return { id };
|
|
493
|
-
}
|
|
494
|
-
async function updateUser(ctx, userId, input) {
|
|
495
|
-
const restBody = {};
|
|
496
|
-
if (input.displayName !== void 0) restBody.display_name = input.displayName;
|
|
497
|
-
if (input.code !== void 0) restBody.code = input.code;
|
|
498
|
-
if (input.position !== void 0) restBody.position = input.position;
|
|
499
|
-
if (input.remark !== void 0) restBody.remark = input.remark;
|
|
500
|
-
if (input.email !== void 0) restBody.email = input.email;
|
|
501
|
-
if (input.telNumber !== void 0) restBody.tel_number = input.telNumber;
|
|
502
|
-
if (input.managerID !== void 0) restBody.manager_id = input.managerID;
|
|
503
|
-
if (input.priority !== void 0) restBody.priority = input.priority;
|
|
504
|
-
if (input.csfLevel !== void 0) restBody.csf_level = input.csfLevel;
|
|
505
|
-
try {
|
|
506
|
-
const r = await request(ctx, `${UM}/management/users/${encodeURIComponent(userId)}`, {
|
|
507
|
-
method: "PATCH",
|
|
508
|
-
body: restBody
|
|
509
|
-
});
|
|
510
|
-
return r ?? { id: userId, updated: true, route: "rest" };
|
|
511
|
-
} catch (e) {
|
|
512
|
-
if (!(e instanceof HttpError) || e.status !== 404 && e.status !== 405) throw e;
|
|
513
|
-
const ncTEditUserParam = {
|
|
514
|
-
id: userId,
|
|
515
|
-
displayName: input.displayName ?? "",
|
|
516
|
-
code: input.code ?? "",
|
|
517
|
-
position: input.position ?? "",
|
|
518
|
-
managerID: input.managerID ?? "",
|
|
519
|
-
remark: input.remark ?? "",
|
|
520
|
-
idcardNumber: null,
|
|
521
|
-
priority: input.priority ?? 999,
|
|
522
|
-
csfLevel: input.csfLevel ?? 5,
|
|
523
|
-
csfLevel2: null,
|
|
524
|
-
email: input.email ?? "",
|
|
525
|
-
telNumber: input.telNumber ?? "",
|
|
526
|
-
expireTime: -1
|
|
527
|
-
};
|
|
528
|
-
await shareMgnt(ctx, "Usrm_EditUser", [{ ncTEditUserParam }, await callerUserId(ctx)]);
|
|
529
|
-
return { id: userId, updated: true, route: "shareMgnt" };
|
|
530
|
-
}
|
|
531
580
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
await request(ctx, `${UM}/users/${encodeURIComponent(userId)}`, { method: "DELETE" });
|
|
535
|
-
} catch (e) {
|
|
536
|
-
if (!(e instanceof HttpError) || e.status !== 404) throw e;
|
|
537
|
-
await shareMgnt(ctx, "Usrm_DelUser", [userId]);
|
|
538
|
-
}
|
|
539
|
-
return { id: userId, deleted: true };
|
|
581
|
+
function getDepartmentSafe(ctx, deptId) {
|
|
582
|
+
return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`);
|
|
540
583
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
method: "PUT",
|
|
544
|
-
body: { password: encryptModifyPwd(newPassword) }
|
|
545
|
-
});
|
|
546
|
-
return { id: userId, passwordReset: true };
|
|
584
|
+
function getDepartmentMembersSafe(ctx, deptId) {
|
|
585
|
+
return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}/members`);
|
|
547
586
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
return request(ctx, `${EACP}/auth1/login-log`, {
|
|
587
|
+
function createDepartmentSafe(ctx, input) {
|
|
588
|
+
return request(ctx, `${ADMIN}/departments`, {
|
|
551
589
|
method: "POST",
|
|
552
590
|
body: {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
end_time: opts.end || void 0
|
|
591
|
+
name: input.name,
|
|
592
|
+
...input.parentId ? { parent_id: input.parentId } : {},
|
|
593
|
+
...input.type ? { type: input.type } : {},
|
|
594
|
+
...input.id ? { id: input.id } : {}
|
|
558
595
|
}
|
|
559
596
|
});
|
|
560
597
|
}
|
|
561
|
-
function
|
|
562
|
-
return request(ctx, `${
|
|
563
|
-
method: "
|
|
598
|
+
function updateDepartmentSafe(ctx, deptId, input) {
|
|
599
|
+
return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`, {
|
|
600
|
+
method: "PUT",
|
|
564
601
|
body: {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
602
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
603
|
+
...input.parentId !== void 0 ? { parent_id: input.parentId } : {},
|
|
604
|
+
...input.type !== void 0 ? { type: input.type } : {}
|
|
568
605
|
}
|
|
569
606
|
});
|
|
570
607
|
}
|
|
571
|
-
function
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
608
|
+
async function deleteDepartmentSafe(ctx, deptId) {
|
|
609
|
+
await request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`, { method: "DELETE" });
|
|
610
|
+
return { ok: true };
|
|
611
|
+
}
|
|
612
|
+
async function buildDepartmentTree(ctx) {
|
|
613
|
+
const res = await listDepartmentsSafe(ctx, { limit: 1e3 });
|
|
614
|
+
const flat = Array.isArray(res) ? res : res.departments ?? [];
|
|
615
|
+
const byId = /* @__PURE__ */ new Map();
|
|
616
|
+
for (const d of flat) if (d.id) byId.set(d.id, { ...d, children: [] });
|
|
617
|
+
const roots = [];
|
|
618
|
+
for (const d of byId.values()) {
|
|
619
|
+
const parent = d.parent_id ? byId.get(d.parent_id) : void 0;
|
|
620
|
+
if (parent) parent.children?.push(d);
|
|
621
|
+
else roots.push(d);
|
|
622
|
+
}
|
|
623
|
+
return roots;
|
|
624
|
+
}
|
|
625
|
+
function listRolesSafe(ctx, source) {
|
|
626
|
+
return request(ctx, `${ADMIN}/roles`, { query: { source: source || void 0 } });
|
|
627
|
+
}
|
|
628
|
+
function getRoleSafe(ctx, roleId) {
|
|
629
|
+
return request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}`);
|
|
630
|
+
}
|
|
631
|
+
function roleMembersSafe(ctx, roleId) {
|
|
632
|
+
return request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}/members`);
|
|
633
|
+
}
|
|
634
|
+
function createRoleSafe(ctx, input) {
|
|
635
|
+
return request(ctx, `${ADMIN}/roles`, {
|
|
636
|
+
method: "POST",
|
|
637
|
+
body: {
|
|
638
|
+
name: input.name,
|
|
639
|
+
...input.description ? { description: input.description } : {},
|
|
640
|
+
...input.id ? { id: input.id } : {}
|
|
577
641
|
}
|
|
578
642
|
});
|
|
579
643
|
}
|
|
580
|
-
function
|
|
581
|
-
return request(ctx, `${
|
|
582
|
-
method: "
|
|
583
|
-
body: {
|
|
644
|
+
function updateRoleSafe(ctx, roleId, input) {
|
|
645
|
+
return request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}`, {
|
|
646
|
+
method: "PUT",
|
|
647
|
+
body: {
|
|
648
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
649
|
+
...input.description !== void 0 ? { description: input.description } : {}
|
|
650
|
+
}
|
|
584
651
|
});
|
|
585
652
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
590
|
-
for (const e of entries) nodes.set(e.id, { id: e.id, name: e.name ?? e.id, children: [] });
|
|
591
|
-
const roots = [];
|
|
592
|
-
for (const e of entries) {
|
|
593
|
-
const node = nodes.get(e.id);
|
|
594
|
-
if (!node) continue;
|
|
595
|
-
const deps = e.parent_deps;
|
|
596
|
-
const parentId = deps && deps.length > 0 ? deps[deps.length - 1]?.id : void 0;
|
|
597
|
-
const parent = parentId ? nodes.get(parentId) : void 0;
|
|
598
|
-
if (parent) parent.children.push(node);
|
|
599
|
-
else roots.push(node);
|
|
600
|
-
}
|
|
601
|
-
return roots;
|
|
653
|
+
async function deleteRoleSafe(ctx, roleId) {
|
|
654
|
+
await request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}`, { method: "DELETE" });
|
|
655
|
+
return { ok: true };
|
|
602
656
|
}
|
|
603
|
-
function
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
lines.push(renderOrgTree(node.children, `${prefix}${last ? " " : "\u2502 "}`));
|
|
657
|
+
async function setRolePermissionSafe(ctx, roleId, grant, perm) {
|
|
658
|
+
await request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}/permissions`, {
|
|
659
|
+
method: grant ? "POST" : "DELETE",
|
|
660
|
+
body: {
|
|
661
|
+
resource: { type: perm.resourceType, id: perm.resourceId },
|
|
662
|
+
operations: perm.operations
|
|
610
663
|
}
|
|
611
664
|
});
|
|
612
|
-
return
|
|
665
|
+
return { ok: true };
|
|
613
666
|
}
|
|
614
667
|
|
|
615
668
|
// src/resources/admin.ts
|
|
669
|
+
var DEFAULT_NEW_USER_PASSWORD = "openbkn";
|
|
616
670
|
function admin(ctx) {
|
|
617
671
|
return {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
orgTree:
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
672
|
+
// ── departments ──
|
|
673
|
+
orgList: (opts) => listDepartmentsSafe(ctx, { search: opts?.name, offset: opts?.offset, limit: opts?.limit }),
|
|
674
|
+
orgGet: (deptId) => getDepartmentSafe(ctx, deptId),
|
|
675
|
+
orgTree: (_role) => buildDepartmentTree(ctx),
|
|
676
|
+
orgMembers: (deptId, _opts) => getDepartmentMembersSafe(ctx, deptId),
|
|
677
|
+
orgCreate: (input) => createDepartmentSafe(ctx, { name: input.name, parentId: input.parentId }),
|
|
678
|
+
orgUpdate: (deptId, input) => updateDepartmentSafe(ctx, deptId, { name: input.name }),
|
|
679
|
+
orgDelete: (deptId) => deleteDepartmentSafe(ctx, deptId),
|
|
680
|
+
// ── users ──
|
|
681
|
+
userList: (opts) => listUsersSafe(ctx, { search: opts?.name, offset: opts?.offset, limit: opts?.limit }),
|
|
682
|
+
userGet: (userId) => getUserSafe(ctx, userId),
|
|
683
|
+
userRoles: async (userId) => {
|
|
684
|
+
const [bound, all] = await Promise.all([getUserRolesSafe(ctx, userId), listRolesSafe(ctx)]);
|
|
685
|
+
const ids = bound.role_ids ?? [];
|
|
686
|
+
const nameById = new Map(
|
|
687
|
+
(all.roles ?? []).map((r) => [
|
|
688
|
+
r.id,
|
|
689
|
+
r.name
|
|
690
|
+
])
|
|
691
|
+
);
|
|
692
|
+
return { roles: ids.map((id) => ({ name: nameById.get(id) ?? id, id })) };
|
|
693
|
+
},
|
|
694
|
+
userCreate: (input) => createUserSafe(ctx, {
|
|
695
|
+
account: input.loginName,
|
|
696
|
+
password: DEFAULT_NEW_USER_PASSWORD,
|
|
697
|
+
name: input.displayName,
|
|
698
|
+
email: input.email
|
|
699
|
+
}),
|
|
700
|
+
userUpdate: (userId, input) => updateUserSafe(ctx, userId, {
|
|
701
|
+
name: input.displayName,
|
|
702
|
+
email: input.email,
|
|
703
|
+
telephone: input.telNumber
|
|
704
|
+
}),
|
|
705
|
+
userDelete: (userId) => deleteUserSafe(ctx, userId),
|
|
706
|
+
userResetPassword: (userId, newPassword) => setUserPasswordSafe(ctx, userId, newPassword),
|
|
707
|
+
// ── roles ──
|
|
708
|
+
roleList: (_opts) => listRolesSafe(ctx),
|
|
709
|
+
roleGet: (roleId) => getRoleSafe(ctx, roleId),
|
|
710
|
+
roleMembers: async (roleId, _opts) => {
|
|
711
|
+
const [mem, users] = await Promise.all([
|
|
712
|
+
roleMembersSafe(ctx, roleId),
|
|
713
|
+
listUsersSafe(ctx, { limit: 500 })
|
|
714
|
+
]);
|
|
715
|
+
const ids = mem.accessor_ids ?? [];
|
|
716
|
+
const nameById = new Map(
|
|
717
|
+
(users.users ?? []).map((u) => [u.id, u.account ?? u.name ?? u.id])
|
|
718
|
+
);
|
|
719
|
+
return { members: ids.map((id) => ({ account: nameById.get(id) ?? id, id })) };
|
|
720
|
+
},
|
|
721
|
+
addRoleMember: (roleId, id, _type = "user") => assignRoleSafe(ctx, id, roleId),
|
|
722
|
+
removeRoleMember: (roleId, id, _type = "user") => removeRoleSafe(ctx, id, roleId),
|
|
723
|
+
roleCreate: (name, description) => createRoleSafe(ctx, { name, description }),
|
|
724
|
+
roleUpdate: (roleId, input) => updateRoleSafe(ctx, roleId, input),
|
|
725
|
+
roleDelete: (roleId) => deleteRoleSafe(ctx, roleId),
|
|
726
|
+
rolePermission: (roleId, grant, resourceType, resourceId, operations) => setRolePermissionSafe(ctx, roleId, grant, { resourceType, resourceId, operations }),
|
|
727
|
+
auditList: (_opts) => notOnSafe("audit list")
|
|
638
728
|
};
|
|
639
729
|
}
|
|
640
730
|
|
|
@@ -2611,13 +2701,13 @@ async function uploadBkn(ctx, tarBuffer, opts = {}) {
|
|
|
2611
2701
|
applyTls(ctx);
|
|
2612
2702
|
const url = new URL(`${ctx.baseUrl}${BKNS}`);
|
|
2613
2703
|
url.searchParams.set("branch", opts.branch ?? "main");
|
|
2614
|
-
const
|
|
2615
|
-
|
|
2704
|
+
const form2 = new FormData();
|
|
2705
|
+
form2.append(
|
|
2616
2706
|
"file",
|
|
2617
2707
|
new Blob([new Uint8Array(tarBuffer)], { type: "application/octet-stream" }),
|
|
2618
2708
|
"bkn.tar"
|
|
2619
2709
|
);
|
|
2620
|
-
const res = await fetch(url, { method: "POST", headers: buildHeaders(ctx), body:
|
|
2710
|
+
const res = await fetch(url, { method: "POST", headers: buildHeaders(ctx), body: form2 });
|
|
2621
2711
|
const text = await res.text();
|
|
2622
2712
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
2623
2713
|
return text ? JSON.parse(text) : void 0;
|
|
@@ -2973,9 +3063,9 @@ function extractTarToDirectory(tarBuffer, dirPath) {
|
|
|
2973
3063
|
}
|
|
2974
3064
|
|
|
2975
3065
|
// src/utils/csv-import.ts
|
|
2976
|
-
import { statSync as statSync2 } from "fs";
|
|
2977
|
-
import {
|
|
2978
|
-
import { basename, resolve as resolve2 } from "path";
|
|
3066
|
+
import { readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
3067
|
+
import { readFile } from "fs/promises";
|
|
3068
|
+
import { basename, dirname, resolve as resolve2 } from "path";
|
|
2979
3069
|
import { parse } from "csv-parse/sync";
|
|
2980
3070
|
async function parseCsvFile(filePath) {
|
|
2981
3071
|
let content = await readFile(filePath, "utf8");
|
|
@@ -3039,13 +3129,25 @@ function buildImportDag(opts) {
|
|
|
3039
3129
|
]
|
|
3040
3130
|
};
|
|
3041
3131
|
}
|
|
3132
|
+
function globOne(pattern) {
|
|
3133
|
+
const dir = dirname(pattern);
|
|
3134
|
+
const re = new RegExp(
|
|
3135
|
+
`^${basename(pattern).replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`
|
|
3136
|
+
);
|
|
3137
|
+
let entries;
|
|
3138
|
+
try {
|
|
3139
|
+
entries = readdirSync3(resolve2(dir));
|
|
3140
|
+
} catch {
|
|
3141
|
+
return [];
|
|
3142
|
+
}
|
|
3143
|
+
return entries.filter((f) => re.test(f)).map((f) => resolve2(dir, f));
|
|
3144
|
+
}
|
|
3042
3145
|
async function resolveFiles(pattern) {
|
|
3043
3146
|
const out = [];
|
|
3044
3147
|
for (const part of pattern.split(",").map((p) => p.trim()).filter(Boolean)) {
|
|
3045
3148
|
if (part.includes("*") || part.includes("?")) {
|
|
3046
|
-
for
|
|
3047
|
-
|
|
3048
|
-
if (/\.csv$/i.test(p)) out.push(resolve2(p));
|
|
3149
|
+
for (const p of globOne(part)) {
|
|
3150
|
+
if (/\.csv$/i.test(p)) out.push(p);
|
|
3049
3151
|
}
|
|
3050
3152
|
} else {
|
|
3051
3153
|
statSync2(resolve2(part));
|
|
@@ -3628,21 +3730,21 @@ function resources(ctx) {
|
|
|
3628
3730
|
|
|
3629
3731
|
// src/resources/skills.ts
|
|
3630
3732
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
3631
|
-
import { basename as basename2, dirname as
|
|
3733
|
+
import { basename as basename2, dirname as dirname3, resolve as resolve5 } from "path";
|
|
3632
3734
|
|
|
3633
3735
|
// src/api/skills.ts
|
|
3634
3736
|
var BASE5 = "/api/agent-operator-integration/v1";
|
|
3635
3737
|
async function registerSkillZip(ctx, bytes, opts = {}) {
|
|
3636
3738
|
applyTls(ctx);
|
|
3637
|
-
const
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
if (opts.source)
|
|
3641
|
-
if (opts.extendInfo)
|
|
3739
|
+
const form2 = new FormData();
|
|
3740
|
+
form2.set("file_type", "zip");
|
|
3741
|
+
form2.set("file", new Blob([bytes]), opts.filename ?? "skill.zip");
|
|
3742
|
+
if (opts.source) form2.set("source", opts.source);
|
|
3743
|
+
if (opts.extendInfo) form2.set("extend_info", JSON.stringify(opts.extendInfo));
|
|
3642
3744
|
const res = await fetch(`${ctx.baseUrl}${BASE5}/skills`, {
|
|
3643
3745
|
method: "POST",
|
|
3644
3746
|
headers: buildHeaders(ctx),
|
|
3645
|
-
body:
|
|
3747
|
+
body: form2
|
|
3646
3748
|
});
|
|
3647
3749
|
const text = await res.text();
|
|
3648
3750
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
@@ -3650,13 +3752,13 @@ async function registerSkillZip(ctx, bytes, opts = {}) {
|
|
|
3650
3752
|
}
|
|
3651
3753
|
async function updateSkillPackageZip(ctx, skillId, bytes, filename = "skill.zip") {
|
|
3652
3754
|
applyTls(ctx);
|
|
3653
|
-
const
|
|
3654
|
-
|
|
3655
|
-
|
|
3755
|
+
const form2 = new FormData();
|
|
3756
|
+
form2.set("file_type", "zip");
|
|
3757
|
+
form2.set("file", new Blob([bytes]), filename);
|
|
3656
3758
|
const res = await fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/package`, {
|
|
3657
3759
|
method: "PUT",
|
|
3658
3760
|
headers: buildHeaders(ctx),
|
|
3659
|
-
body:
|
|
3761
|
+
body: form2
|
|
3660
3762
|
});
|
|
3661
3763
|
const text = await res.text();
|
|
3662
3764
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
@@ -3730,11 +3832,11 @@ function setSkillStatus(ctx, skillId, status2) {
|
|
|
3730
3832
|
}
|
|
3731
3833
|
|
|
3732
3834
|
// src/utils/skill-archive.ts
|
|
3733
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as
|
|
3734
|
-
import { dirname, join as join3, relative, resolve as resolve4, sep } from "path";
|
|
3835
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as readdirSync4, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
3836
|
+
import { dirname as dirname2, join as join3, relative, resolve as resolve4, sep } from "path";
|
|
3735
3837
|
import JSZip from "jszip";
|
|
3736
3838
|
function walk(dir, base, files) {
|
|
3737
|
-
for (const entry of
|
|
3839
|
+
for (const entry of readdirSync4(dir)) {
|
|
3738
3840
|
const full = join3(dir, entry);
|
|
3739
3841
|
const st = statSync3(full);
|
|
3740
3842
|
if (st.isDirectory()) walk(full, base, files);
|
|
@@ -3762,7 +3864,7 @@ async function unzipToDirectory(bytes, dir) {
|
|
|
3762
3864
|
for (const entry of entries) {
|
|
3763
3865
|
if (entry.dir) continue;
|
|
3764
3866
|
const out = join3(abs, entry.name);
|
|
3765
|
-
mkdirSync3(
|
|
3867
|
+
mkdirSync3(dirname2(out), { recursive: true });
|
|
3766
3868
|
writeFileSync2(out, await entry.async("nodebuffer"));
|
|
3767
3869
|
written.push(out);
|
|
3768
3870
|
}
|
|
@@ -3795,7 +3897,7 @@ function skills(ctx) {
|
|
|
3795
3897
|
download: async (skillId, outPath) => {
|
|
3796
3898
|
const bytes = await downloadSkill(ctx, skillId);
|
|
3797
3899
|
const dest = resolve5(outPath ?? `${skillId}.zip`);
|
|
3798
|
-
mkdirSync4(
|
|
3900
|
+
mkdirSync4(dirname3(dest), { recursive: true });
|
|
3799
3901
|
writeFileSync3(dest, bytes);
|
|
3800
3902
|
return { skillId, path: dest, bytes: bytes.length };
|
|
3801
3903
|
},
|
|
@@ -3811,7 +3913,7 @@ function skills(ctx) {
|
|
|
3811
3913
|
|
|
3812
3914
|
// src/resources/toolboxes.ts
|
|
3813
3915
|
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
3814
|
-
import { dirname as
|
|
3916
|
+
import { dirname as dirname4, resolve as resolve6 } from "path";
|
|
3815
3917
|
|
|
3816
3918
|
// src/api/toolboxes.ts
|
|
3817
3919
|
import { readFile as readFile2 } from "fs/promises";
|
|
@@ -3831,12 +3933,12 @@ async function exportConfig(ctx, id, type = "toolbox") {
|
|
|
3831
3933
|
async function importConfig(ctx, filePath, type = "toolbox") {
|
|
3832
3934
|
applyTls(ctx);
|
|
3833
3935
|
const buf = await readFile2(filePath);
|
|
3834
|
-
const
|
|
3835
|
-
|
|
3936
|
+
const form2 = new FormData();
|
|
3937
|
+
form2.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
|
|
3836
3938
|
const res = await fetch(`${ctx.baseUrl}${IMPEX}/import/${encodeURIComponent(type)}`, {
|
|
3837
3939
|
method: "POST",
|
|
3838
3940
|
headers: buildHeaders(ctx),
|
|
3839
|
-
body:
|
|
3941
|
+
body: form2
|
|
3840
3942
|
});
|
|
3841
3943
|
const text = await res.text();
|
|
3842
3944
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
@@ -3845,13 +3947,13 @@ async function importConfig(ctx, filePath, type = "toolbox") {
|
|
|
3845
3947
|
async function uploadTool(ctx, boxId, filePath, metadataType = "openapi") {
|
|
3846
3948
|
applyTls(ctx);
|
|
3847
3949
|
const buf = await readFile2(filePath);
|
|
3848
|
-
const
|
|
3849
|
-
|
|
3850
|
-
|
|
3950
|
+
const form2 = new FormData();
|
|
3951
|
+
form2.append("metadata_type", metadataType);
|
|
3952
|
+
form2.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
|
|
3851
3953
|
const res = await fetch(`${ctx.baseUrl}${PATH}/${encodeURIComponent(boxId)}/tool`, {
|
|
3852
3954
|
method: "POST",
|
|
3853
3955
|
headers: buildHeaders(ctx),
|
|
3854
|
-
body:
|
|
3956
|
+
body: form2
|
|
3855
3957
|
});
|
|
3856
3958
|
const text = await res.text();
|
|
3857
3959
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
@@ -3937,7 +4039,7 @@ function toolboxes(ctx) {
|
|
|
3937
4039
|
export: async (id, outPath, type) => {
|
|
3938
4040
|
const bytes = await exportConfig(ctx, id, type);
|
|
3939
4041
|
const dest = resolve6(outPath);
|
|
3940
|
-
mkdirSync5(
|
|
4042
|
+
mkdirSync5(dirname4(dest), { recursive: true });
|
|
3941
4043
|
writeFileSync4(dest, bytes);
|
|
3942
4044
|
return { id, path: dest, bytes: bytes.length };
|
|
3943
4045
|
},
|
|
@@ -4017,7 +4119,7 @@ async function getSpansByConversation(ctx, conversationId, opts = {}) {
|
|
|
4017
4119
|
}
|
|
4018
4120
|
|
|
4019
4121
|
// src/trace-ai/claude-judge.ts
|
|
4020
|
-
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
4122
|
+
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
4021
4123
|
var ClaudeJudgeError = class extends Error {
|
|
4022
4124
|
constructor(message, reason) {
|
|
4023
4125
|
super(message);
|
|
@@ -4063,7 +4165,7 @@ async function judgeJson(prompt, opts = {}) {
|
|
|
4063
4165
|
const timeoutMs = opts.timeoutMs ?? 12e4;
|
|
4064
4166
|
const args = ["-p", "--output-format=json", "--dangerously-skip-permissions"];
|
|
4065
4167
|
const stdout = await new Promise((resolve7, reject) => {
|
|
4066
|
-
const child =
|
|
4168
|
+
const child = spawn2(binary, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
4067
4169
|
let out = "";
|
|
4068
4170
|
let timedOut = false;
|
|
4069
4171
|
const killer = setTimeout(() => {
|
|
@@ -4901,7 +5003,8 @@ function attachToken(baseUrl, accessToken, opts = {}) {
|
|
|
4901
5003
|
refreshToken: opts.refreshToken,
|
|
4902
5004
|
idToken: opts.idToken,
|
|
4903
5005
|
tlsInsecure: opts.insecure,
|
|
4904
|
-
|
|
5006
|
+
// Prefer the account the user typed (-u); device tokens carry no username.
|
|
5007
|
+
username: opts.username ?? decodeJwt(opts.idToken ?? accessToken)?.preferred_username
|
|
4905
5008
|
};
|
|
4906
5009
|
const userId = writeToken(url, token);
|
|
4907
5010
|
setActivePlatform(url);
|
|
@@ -4961,14 +5064,16 @@ function logout() {
|
|
|
4961
5064
|
function deletePlatform(baseUrl, userId) {
|
|
4962
5065
|
return deleteToken(normalize(baseUrl), userId);
|
|
4963
5066
|
}
|
|
4964
|
-
function switchUser(baseUrl,
|
|
5067
|
+
function switchUser(baseUrl, userOrName) {
|
|
4965
5068
|
const url = normalize(baseUrl);
|
|
4966
|
-
|
|
4967
|
-
|
|
5069
|
+
const users = usersOf(url);
|
|
5070
|
+
const match = users.find((u) => u.userId === userOrName) ?? users.find((u) => (u.username ?? u.displayName) === userOrName);
|
|
5071
|
+
if (!match) {
|
|
5072
|
+
throw new InputError(`No saved user '${userOrName}' on ${url}. Try \`auth users ${url}\`.`);
|
|
4968
5073
|
}
|
|
4969
|
-
setActiveUser(url, userId);
|
|
5074
|
+
setActiveUser(url, match.userId);
|
|
4970
5075
|
setActivePlatform(url);
|
|
4971
|
-
return { baseUrl: url, userId };
|
|
5076
|
+
return { baseUrl: url, userId: match.userId, username: match.username ?? match.displayName };
|
|
4972
5077
|
}
|
|
4973
5078
|
function usersOf(baseUrl) {
|
|
4974
5079
|
const url = normalize(baseUrl);
|
|
@@ -4996,14 +5101,17 @@ export {
|
|
|
4996
5101
|
InputError,
|
|
4997
5102
|
toExitCode,
|
|
4998
5103
|
formatError,
|
|
5104
|
+
decodeJwt,
|
|
4999
5105
|
activePlatform,
|
|
5000
5106
|
setActivePlatform,
|
|
5001
5107
|
readPlatformConfig,
|
|
5002
5108
|
writePlatformConfig,
|
|
5003
5109
|
resolveContext,
|
|
5110
|
+
openBrowser,
|
|
5111
|
+
deviceLogin,
|
|
5112
|
+
credentialDeviceLogin,
|
|
5004
5113
|
request,
|
|
5005
|
-
|
|
5006
|
-
renderOrgTree,
|
|
5114
|
+
getUserSafe,
|
|
5007
5115
|
admin,
|
|
5008
5116
|
agents,
|
|
5009
5117
|
context,
|
|
@@ -5028,8 +5136,7 @@ export {
|
|
|
5028
5136
|
logout,
|
|
5029
5137
|
deletePlatform,
|
|
5030
5138
|
switchUser,
|
|
5031
|
-
usersOf,
|
|
5032
5139
|
exportCreds,
|
|
5033
5140
|
auth_exports
|
|
5034
5141
|
};
|
|
5035
|
-
//# sourceMappingURL=chunk-
|
|
5142
|
+
//# sourceMappingURL=chunk-5MOIXIMJ.js.map
|