@openbkn/bkn-sdk 0.1.1-alpha.1 → 0.1.1-alpha.3
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-APJNRHLS.js} +504 -375
- package/dist/chunk-APJNRHLS.js.map +1 -0
- package/dist/cli.js +284 -344
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +81 -35
- 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,23 +234,228 @@ function resolveContext(opts = {}) {
|
|
|
220
234
|
}
|
|
221
235
|
const normalized = baseUrl.replace(/\/+$/, "");
|
|
222
236
|
const stored = readToken(normalized);
|
|
223
|
-
const
|
|
224
|
-
|
|
237
|
+
const explicit = opts.token ?? process.env.BKN_TOKEN;
|
|
238
|
+
const token = explicit ?? stored?.accessToken ?? "";
|
|
239
|
+
if (!token && !stored?.noAuth) {
|
|
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 } : {}
|
|
232
260
|
};
|
|
233
261
|
}
|
|
234
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 fetchAuthStatus(baseUrl) {
|
|
299
|
+
try {
|
|
300
|
+
const res = await fetch(`${normalizeBaseUrl(baseUrl)}/install-status.json`, {
|
|
301
|
+
headers: { Accept: "application/json" }
|
|
302
|
+
});
|
|
303
|
+
if (!res.ok) return null;
|
|
304
|
+
const j = await res.json();
|
|
305
|
+
if (!j.auth) return null;
|
|
306
|
+
return { enabled: Boolean(j.auth.enabled), stack: j.auth.stack };
|
|
307
|
+
} catch {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
async function refreshAccessToken(baseUrl, refreshToken, clientId = "openbkn-sdk") {
|
|
312
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
313
|
+
const res = await fetch(`${base}/oauth2/token`, {
|
|
314
|
+
method: "POST",
|
|
315
|
+
headers: {
|
|
316
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
317
|
+
Accept: "application/json"
|
|
318
|
+
},
|
|
319
|
+
body: new URLSearchParams({
|
|
320
|
+
grant_type: "refresh_token",
|
|
321
|
+
refresh_token: refreshToken,
|
|
322
|
+
client_id: clientId
|
|
323
|
+
}).toString()
|
|
324
|
+
});
|
|
325
|
+
if (!res.ok) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
`Token refresh failed (${res.status}): ${await res.text() || res.statusText}`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
return mapToken(await res.json());
|
|
331
|
+
}
|
|
332
|
+
var DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
333
|
+
var DEFAULT_DEVICE_CLIENT_ID = "openbkn-sdk";
|
|
334
|
+
var DEVICE_SCOPE = "openid offline";
|
|
335
|
+
var form = (body) => ({
|
|
336
|
+
method: "POST",
|
|
337
|
+
headers: {
|
|
338
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
339
|
+
Accept: "application/json"
|
|
340
|
+
},
|
|
341
|
+
body: new URLSearchParams(body).toString()
|
|
342
|
+
});
|
|
343
|
+
async function requestDeviceCode(base, clientId, scope, audience) {
|
|
344
|
+
const params = { client_id: clientId, scope };
|
|
345
|
+
if (audience) params.audience = audience;
|
|
346
|
+
const res = await fetch(`${base}/oauth2/device/auth`, form(params));
|
|
347
|
+
if (!res.ok) {
|
|
348
|
+
throw new Error(`Device auth failed (${res.status}): ${await res.text() || res.statusText}`);
|
|
349
|
+
}
|
|
350
|
+
const da = await res.json();
|
|
351
|
+
if (!da.verification_uri) {
|
|
352
|
+
throw new Error(
|
|
353
|
+
"Device auth returned no verification_uri \u2014 is Hydra's device flow configured?"
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
return da;
|
|
357
|
+
}
|
|
358
|
+
async function pollDeviceToken(base, deviceCode, clientId, intervalMs, windowMs) {
|
|
359
|
+
let interval = intervalMs;
|
|
360
|
+
const deadline = Date.now() + windowMs;
|
|
361
|
+
while (Date.now() < deadline) {
|
|
362
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
363
|
+
const tokRes = await fetch(
|
|
364
|
+
`${base}/oauth2/token`,
|
|
365
|
+
form({ grant_type: DEVICE_GRANT, device_code: deviceCode, client_id: clientId })
|
|
366
|
+
);
|
|
367
|
+
const data = await tokRes.json().catch(() => ({}));
|
|
368
|
+
if (tokRes.ok) return mapToken(data);
|
|
369
|
+
switch (data.error) {
|
|
370
|
+
case "authorization_pending":
|
|
371
|
+
break;
|
|
372
|
+
// keep polling
|
|
373
|
+
case "slow_down":
|
|
374
|
+
interval += 5e3;
|
|
375
|
+
break;
|
|
376
|
+
case "access_denied":
|
|
377
|
+
throw new InputError("Device authorization denied.");
|
|
378
|
+
case "expired_token":
|
|
379
|
+
throw new InputError("Device code expired \u2014 run login again.");
|
|
380
|
+
default:
|
|
381
|
+
throw new Error(`Device token poll failed: ${String(data.error ?? tokRes.status)}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
throw new InputError("Device login timed out.");
|
|
385
|
+
}
|
|
386
|
+
async function deviceLogin(baseUrl, opts = {}) {
|
|
387
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
388
|
+
const clientId = opts.clientId ?? DEFAULT_DEVICE_CLIENT_ID;
|
|
389
|
+
const da = await requestDeviceCode(base, clientId, opts.scope ?? DEVICE_SCOPE, opts.audience);
|
|
390
|
+
opts.onPrompt?.({
|
|
391
|
+
userCode: da.user_code,
|
|
392
|
+
verificationUri: onBaseHost(base, da.verification_uri),
|
|
393
|
+
verificationUriComplete: da.verification_uri_complete ? onBaseHost(base, da.verification_uri_complete) : void 0
|
|
394
|
+
});
|
|
395
|
+
const windowMs = Math.min(opts.timeoutMs ?? Number.POSITIVE_INFINITY, da.expires_in * 1e3);
|
|
396
|
+
return pollDeviceToken(base, da.device_code, clientId, (da.interval ?? 5) * 1e3, windowMs);
|
|
397
|
+
}
|
|
398
|
+
function onBaseHost(base, uri) {
|
|
399
|
+
try {
|
|
400
|
+
const u = new URL(uri);
|
|
401
|
+
const b = new URL(base);
|
|
402
|
+
return `${b.origin}${u.pathname}${u.search}`;
|
|
403
|
+
} catch {
|
|
404
|
+
return uri;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async function credentialDeviceLogin(baseUrl, username, password, opts = {}) {
|
|
408
|
+
const base = normalizeBaseUrl(baseUrl);
|
|
409
|
+
const clientId = opts.clientId ?? DEFAULT_DEVICE_CLIENT_ID;
|
|
410
|
+
const da = await requestDeviceCode(base, clientId, opts.scope ?? DEVICE_SCOPE, opts.audience);
|
|
411
|
+
let jar = "";
|
|
412
|
+
const hop = async (url, init) => {
|
|
413
|
+
const r = await fetch(url, {
|
|
414
|
+
method: init?.method ?? "GET",
|
|
415
|
+
headers: { Cookie: jar, Accept: "text/html,*/*;q=0.8", ...init?.headers ?? {} },
|
|
416
|
+
body: init?.body,
|
|
417
|
+
redirect: "manual"
|
|
418
|
+
});
|
|
419
|
+
jar = mergeCookies(jar, r);
|
|
420
|
+
return r;
|
|
421
|
+
};
|
|
422
|
+
const postForm = (path, body) => hop(`${base}${path}`, {
|
|
423
|
+
method: "POST",
|
|
424
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
425
|
+
body: new URLSearchParams(body).toString()
|
|
426
|
+
});
|
|
427
|
+
const v = await hop(`${base}/oauth2/device/verify?user_code=${encodeURIComponent(da.user_code)}`);
|
|
428
|
+
let loc = v.headers.get("location");
|
|
429
|
+
for (let i = 0; i < 25 && loc; i++) {
|
|
430
|
+
const u = new URL(loc, base);
|
|
431
|
+
let r;
|
|
432
|
+
if (u.pathname.endsWith("/device")) {
|
|
433
|
+
const dc = u.searchParams.get("device_challenge");
|
|
434
|
+
if (!dc) throw new Error(`/device without device_challenge: ${loc}`);
|
|
435
|
+
r = await postForm("/device", { device_challenge: dc, user_code: da.user_code });
|
|
436
|
+
} else if (u.pathname.endsWith("/login")) {
|
|
437
|
+
const lc = u.searchParams.get("login_challenge");
|
|
438
|
+
if (!lc) throw new Error(`/login without login_challenge: ${loc}`);
|
|
439
|
+
r = await postForm("/login", { login_challenge: lc, account: username, password });
|
|
440
|
+
if (r.status === 401) throw new InputError("Sign-in failed: wrong account or password");
|
|
441
|
+
} else if (u.pathname.endsWith("/consent")) {
|
|
442
|
+
const cc = u.searchParams.get("consent_challenge");
|
|
443
|
+
if (!cc) throw new Error(`/consent without consent_challenge: ${loc}`);
|
|
444
|
+
r = await postForm("/consent", { consent_challenge: cc, decision: "allow" });
|
|
445
|
+
} else {
|
|
446
|
+
r = await hop(u.href);
|
|
447
|
+
}
|
|
448
|
+
loc = r.headers.get("location");
|
|
449
|
+
}
|
|
450
|
+
const windowMs = Math.min(opts.timeoutMs ?? Number.POSITIVE_INFINITY, da.expires_in * 1e3);
|
|
451
|
+
return pollDeviceToken(base, da.device_code, clientId, (da.interval ?? 5) * 1e3, windowMs);
|
|
452
|
+
}
|
|
453
|
+
|
|
235
454
|
// src/api/headers.ts
|
|
236
455
|
function buildHeaders(ctx, extra) {
|
|
237
456
|
return {
|
|
238
|
-
|
|
239
|
-
token: ctx.token,
|
|
457
|
+
// No token = a no-auth platform (no bkn-safe); send no Authorization.
|
|
458
|
+
...ctx.token ? { authorization: `Bearer ${ctx.token}`, token: ctx.token } : {},
|
|
240
459
|
"x-business-domain": ctx.businessDomain,
|
|
241
460
|
...extra
|
|
242
461
|
};
|
|
@@ -260,16 +479,20 @@ async function request(ctx, path, init = {}) {
|
|
|
260
479
|
const hasBody = init.body !== void 0;
|
|
261
480
|
const controller = new AbortController();
|
|
262
481
|
const timer = setTimeout(() => controller.abort(), init.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
482
|
+
const send = () => fetch(url, {
|
|
483
|
+
method: init.method ?? (hasBody ? "POST" : "GET"),
|
|
484
|
+
headers: buildHeaders(ctx, {
|
|
485
|
+
...hasBody ? { "content-type": "application/json" } : {},
|
|
486
|
+
...init.headers
|
|
487
|
+
}),
|
|
488
|
+
body: hasBody ? JSON.stringify(init.body) : void 0,
|
|
489
|
+
signal: controller.signal
|
|
490
|
+
});
|
|
263
491
|
try {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
...init.headers
|
|
269
|
-
}),
|
|
270
|
-
body: hasBody ? JSON.stringify(init.body) : void 0,
|
|
271
|
-
signal: controller.signal
|
|
272
|
-
});
|
|
492
|
+
let res = await send();
|
|
493
|
+
if (res.status === 401 && ctx.refresh && await tryRefresh(ctx)) {
|
|
494
|
+
res = await send();
|
|
495
|
+
}
|
|
273
496
|
const text = await res.text();
|
|
274
497
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
275
498
|
return text ? JSON.parse(text) : void 0;
|
|
@@ -277,364 +500,244 @@ async function request(ctx, path, init = {}) {
|
|
|
277
500
|
clearTimeout(timer);
|
|
278
501
|
}
|
|
279
502
|
}
|
|
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");
|
|
503
|
+
async function tryRefresh(ctx) {
|
|
504
|
+
if (!ctx.refresh) return false;
|
|
505
|
+
try {
|
|
506
|
+
const t = await refreshAccessToken(ctx.baseUrl, ctx.refresh.refreshToken, ctx.refresh.clientId);
|
|
507
|
+
ctx.token = t.accessToken;
|
|
508
|
+
if (t.refreshToken) ctx.refresh.refreshToken = t.refreshToken;
|
|
509
|
+
ctx.refresh.persist(t);
|
|
510
|
+
return true;
|
|
511
|
+
} catch {
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
314
514
|
}
|
|
315
515
|
|
|
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."
|
|
516
|
+
// src/api/safe.ts
|
|
517
|
+
var ADMIN = "/api/safe/v1/admin";
|
|
518
|
+
function notOnSafe(operation) {
|
|
519
|
+
throw new InputError(
|
|
520
|
+
`'${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
521
|
);
|
|
328
522
|
}
|
|
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
|
-
}
|
|
523
|
+
function listUsersSafe(ctx, opts = {}) {
|
|
524
|
+
return request(ctx, `${ADMIN}/users`, {
|
|
525
|
+
query: { search: opts.search || void 0, offset: opts.offset, limit: opts.limit }
|
|
358
526
|
});
|
|
359
527
|
}
|
|
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;
|
|
528
|
+
function getUserSafe(ctx, userId) {
|
|
529
|
+
return request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`);
|
|
372
530
|
}
|
|
373
|
-
function
|
|
374
|
-
return request(ctx, `${
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
531
|
+
function createUserSafe(ctx, input) {
|
|
532
|
+
return request(ctx, `${ADMIN}/users`, {
|
|
533
|
+
method: "POST",
|
|
534
|
+
body: {
|
|
535
|
+
account: input.account,
|
|
536
|
+
password: input.password,
|
|
537
|
+
...input.name ? { name: input.name } : {},
|
|
538
|
+
...input.email ? { email: input.email } : {},
|
|
539
|
+
...input.accountType ? { account_type: input.accountType } : {},
|
|
540
|
+
...input.id ? { id: input.id } : {}
|
|
381
541
|
}
|
|
382
542
|
});
|
|
383
543
|
}
|
|
384
|
-
function
|
|
385
|
-
return request(ctx, `${
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
544
|
+
function updateUserSafe(ctx, userId, input) {
|
|
545
|
+
return request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`, {
|
|
546
|
+
method: "PUT",
|
|
547
|
+
body: {
|
|
548
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
549
|
+
...input.email !== void 0 ? { email: input.email } : {},
|
|
550
|
+
...input.telephone !== void 0 ? { telephone: input.telephone } : {},
|
|
551
|
+
...input.enabled !== void 0 ? { enabled: input.enabled } : {},
|
|
552
|
+
...input.accountType !== void 0 ? { account_type: input.accountType } : {}
|
|
390
553
|
}
|
|
391
554
|
});
|
|
392
555
|
}
|
|
393
|
-
function
|
|
394
|
-
|
|
556
|
+
async function deleteUserSafe(ctx, userId) {
|
|
557
|
+
await request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`, { method: "DELETE" });
|
|
558
|
+
return { ok: true };
|
|
395
559
|
}
|
|
396
|
-
function
|
|
397
|
-
|
|
560
|
+
async function setUserPasswordSafe(ctx, userId, password) {
|
|
561
|
+
await request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}/password`, {
|
|
562
|
+
method: "PUT",
|
|
563
|
+
body: { password }
|
|
564
|
+
});
|
|
565
|
+
return { ok: true };
|
|
398
566
|
}
|
|
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
|
-
}
|
|
567
|
+
function getUserRolesSafe(ctx, userId) {
|
|
568
|
+
return request(ctx, `${ADMIN}/role-bindings`, { query: { accessor_id: userId } });
|
|
423
569
|
}
|
|
424
|
-
async function
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
return shareMgnt(ctx, "Usrm_GetDepartmentById", [deptId]);
|
|
431
|
-
}
|
|
570
|
+
async function assignRoleSafe(ctx, accessorId, roleId) {
|
|
571
|
+
await request(ctx, `${ADMIN}/role-bindings`, {
|
|
572
|
+
method: "POST",
|
|
573
|
+
body: { accessor_id: accessorId, role_id: roleId }
|
|
574
|
+
});
|
|
575
|
+
return { ok: true };
|
|
432
576
|
}
|
|
433
|
-
function
|
|
434
|
-
|
|
435
|
-
|
|
577
|
+
async function removeRoleSafe(ctx, accessorId, roleId) {
|
|
578
|
+
await request(ctx, `${ADMIN}/role-bindings`, {
|
|
579
|
+
method: "DELETE",
|
|
580
|
+
body: { accessor_id: accessorId, role_id: roleId }
|
|
436
581
|
});
|
|
582
|
+
return { ok: true };
|
|
437
583
|
}
|
|
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"
|
|
584
|
+
function listDepartmentsSafe(ctx, opts = {}) {
|
|
585
|
+
return request(ctx, `${ADMIN}/departments`, {
|
|
586
|
+
query: {
|
|
587
|
+
search: opts.search || void 0,
|
|
588
|
+
parent_id: opts.parentId,
|
|
589
|
+
offset: opts.offset,
|
|
590
|
+
limit: opts.limit
|
|
591
|
+
}
|
|
466
592
|
});
|
|
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
593
|
}
|
|
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 };
|
|
594
|
+
function getDepartmentSafe(ctx, deptId) {
|
|
595
|
+
return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`);
|
|
540
596
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
method: "PUT",
|
|
544
|
-
body: { password: encryptModifyPwd(newPassword) }
|
|
545
|
-
});
|
|
546
|
-
return { id: userId, passwordReset: true };
|
|
597
|
+
function getDepartmentMembersSafe(ctx, deptId) {
|
|
598
|
+
return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}/members`);
|
|
547
599
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
return request(ctx, `${EACP}/auth1/login-log`, {
|
|
600
|
+
function createDepartmentSafe(ctx, input) {
|
|
601
|
+
return request(ctx, `${ADMIN}/departments`, {
|
|
551
602
|
method: "POST",
|
|
552
603
|
body: {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
end_time: opts.end || void 0
|
|
604
|
+
name: input.name,
|
|
605
|
+
...input.parentId ? { parent_id: input.parentId } : {},
|
|
606
|
+
...input.type ? { type: input.type } : {},
|
|
607
|
+
...input.id ? { id: input.id } : {}
|
|
558
608
|
}
|
|
559
609
|
});
|
|
560
610
|
}
|
|
561
|
-
function
|
|
562
|
-
return request(ctx, `${
|
|
563
|
-
method: "
|
|
611
|
+
function updateDepartmentSafe(ctx, deptId, input) {
|
|
612
|
+
return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`, {
|
|
613
|
+
method: "PUT",
|
|
564
614
|
body: {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
615
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
616
|
+
...input.parentId !== void 0 ? { parent_id: input.parentId } : {},
|
|
617
|
+
...input.type !== void 0 ? { type: input.type } : {}
|
|
568
618
|
}
|
|
569
619
|
});
|
|
570
620
|
}
|
|
571
|
-
function
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
621
|
+
async function deleteDepartmentSafe(ctx, deptId) {
|
|
622
|
+
await request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`, { method: "DELETE" });
|
|
623
|
+
return { ok: true };
|
|
624
|
+
}
|
|
625
|
+
async function buildDepartmentTree(ctx) {
|
|
626
|
+
const res = await listDepartmentsSafe(ctx, { limit: 1e3 });
|
|
627
|
+
const flat = Array.isArray(res) ? res : res.departments ?? [];
|
|
628
|
+
const byId = /* @__PURE__ */ new Map();
|
|
629
|
+
for (const d of flat) if (d.id) byId.set(d.id, { ...d, children: [] });
|
|
630
|
+
const roots = [];
|
|
631
|
+
for (const d of byId.values()) {
|
|
632
|
+
const parent = d.parent_id ? byId.get(d.parent_id) : void 0;
|
|
633
|
+
if (parent) parent.children?.push(d);
|
|
634
|
+
else roots.push(d);
|
|
635
|
+
}
|
|
636
|
+
return roots;
|
|
637
|
+
}
|
|
638
|
+
function listRolesSafe(ctx, source) {
|
|
639
|
+
return request(ctx, `${ADMIN}/roles`, { query: { source: source || void 0 } });
|
|
640
|
+
}
|
|
641
|
+
function getRoleSafe(ctx, roleId) {
|
|
642
|
+
return request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}`);
|
|
643
|
+
}
|
|
644
|
+
function roleMembersSafe(ctx, roleId) {
|
|
645
|
+
return request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}/members`);
|
|
646
|
+
}
|
|
647
|
+
function createRoleSafe(ctx, input) {
|
|
648
|
+
return request(ctx, `${ADMIN}/roles`, {
|
|
649
|
+
method: "POST",
|
|
650
|
+
body: {
|
|
651
|
+
name: input.name,
|
|
652
|
+
...input.description ? { description: input.description } : {},
|
|
653
|
+
...input.id ? { id: input.id } : {}
|
|
577
654
|
}
|
|
578
655
|
});
|
|
579
656
|
}
|
|
580
|
-
function
|
|
581
|
-
return request(ctx, `${
|
|
582
|
-
method: "
|
|
583
|
-
body: {
|
|
657
|
+
function updateRoleSafe(ctx, roleId, input) {
|
|
658
|
+
return request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}`, {
|
|
659
|
+
method: "PUT",
|
|
660
|
+
body: {
|
|
661
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
662
|
+
...input.description !== void 0 ? { description: input.description } : {}
|
|
663
|
+
}
|
|
584
664
|
});
|
|
585
665
|
}
|
|
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;
|
|
666
|
+
async function deleteRoleSafe(ctx, roleId) {
|
|
667
|
+
await request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}`, { method: "DELETE" });
|
|
668
|
+
return { ok: true };
|
|
602
669
|
}
|
|
603
|
-
function
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
lines.push(renderOrgTree(node.children, `${prefix}${last ? " " : "\u2502 "}`));
|
|
670
|
+
async function setRolePermissionSafe(ctx, roleId, grant, perm) {
|
|
671
|
+
await request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}/permissions`, {
|
|
672
|
+
method: grant ? "POST" : "DELETE",
|
|
673
|
+
body: {
|
|
674
|
+
resource: { type: perm.resourceType, id: perm.resourceId },
|
|
675
|
+
operations: perm.operations
|
|
610
676
|
}
|
|
611
677
|
});
|
|
612
|
-
return
|
|
678
|
+
return { ok: true };
|
|
613
679
|
}
|
|
614
680
|
|
|
615
681
|
// src/resources/admin.ts
|
|
682
|
+
var DEFAULT_NEW_USER_PASSWORD = "openbkn";
|
|
616
683
|
function admin(ctx) {
|
|
617
684
|
return {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
orgTree:
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
685
|
+
// ── departments ──
|
|
686
|
+
orgList: (opts) => listDepartmentsSafe(ctx, { search: opts?.name, offset: opts?.offset, limit: opts?.limit }),
|
|
687
|
+
orgGet: (deptId) => getDepartmentSafe(ctx, deptId),
|
|
688
|
+
orgTree: (_role) => buildDepartmentTree(ctx),
|
|
689
|
+
orgMembers: (deptId, _opts) => getDepartmentMembersSafe(ctx, deptId),
|
|
690
|
+
orgCreate: (input) => createDepartmentSafe(ctx, { name: input.name, parentId: input.parentId }),
|
|
691
|
+
orgUpdate: (deptId, input) => updateDepartmentSafe(ctx, deptId, { name: input.name }),
|
|
692
|
+
orgDelete: (deptId) => deleteDepartmentSafe(ctx, deptId),
|
|
693
|
+
// ── users ──
|
|
694
|
+
userList: (opts) => listUsersSafe(ctx, { search: opts?.name, offset: opts?.offset, limit: opts?.limit }),
|
|
695
|
+
userGet: (userId) => getUserSafe(ctx, userId),
|
|
696
|
+
userRoles: async (userId) => {
|
|
697
|
+
const [bound, all] = await Promise.all([getUserRolesSafe(ctx, userId), listRolesSafe(ctx)]);
|
|
698
|
+
const ids = bound.role_ids ?? [];
|
|
699
|
+
const nameById = new Map(
|
|
700
|
+
(all.roles ?? []).map((r) => [
|
|
701
|
+
r.id,
|
|
702
|
+
r.name
|
|
703
|
+
])
|
|
704
|
+
);
|
|
705
|
+
return { roles: ids.map((id) => ({ name: nameById.get(id) ?? id, id })) };
|
|
706
|
+
},
|
|
707
|
+
userCreate: (input) => createUserSafe(ctx, {
|
|
708
|
+
account: input.loginName,
|
|
709
|
+
password: DEFAULT_NEW_USER_PASSWORD,
|
|
710
|
+
name: input.displayName,
|
|
711
|
+
email: input.email
|
|
712
|
+
}),
|
|
713
|
+
userUpdate: (userId, input) => updateUserSafe(ctx, userId, {
|
|
714
|
+
name: input.displayName,
|
|
715
|
+
email: input.email,
|
|
716
|
+
telephone: input.telNumber
|
|
717
|
+
}),
|
|
718
|
+
userDelete: (userId) => deleteUserSafe(ctx, userId),
|
|
719
|
+
userResetPassword: (userId, newPassword) => setUserPasswordSafe(ctx, userId, newPassword),
|
|
720
|
+
// ── roles ──
|
|
721
|
+
roleList: (_opts) => listRolesSafe(ctx),
|
|
722
|
+
roleGet: (roleId) => getRoleSafe(ctx, roleId),
|
|
723
|
+
roleMembers: async (roleId, _opts) => {
|
|
724
|
+
const [mem, users] = await Promise.all([
|
|
725
|
+
roleMembersSafe(ctx, roleId),
|
|
726
|
+
listUsersSafe(ctx, { limit: 500 })
|
|
727
|
+
]);
|
|
728
|
+
const ids = mem.accessor_ids ?? [];
|
|
729
|
+
const nameById = new Map(
|
|
730
|
+
(users.users ?? []).map((u) => [u.id, u.account ?? u.name ?? u.id])
|
|
731
|
+
);
|
|
732
|
+
return { members: ids.map((id) => ({ account: nameById.get(id) ?? id, id })) };
|
|
733
|
+
},
|
|
734
|
+
addRoleMember: (roleId, id, _type = "user") => assignRoleSafe(ctx, id, roleId),
|
|
735
|
+
removeRoleMember: (roleId, id, _type = "user") => removeRoleSafe(ctx, id, roleId),
|
|
736
|
+
roleCreate: (name, description) => createRoleSafe(ctx, { name, description }),
|
|
737
|
+
roleUpdate: (roleId, input) => updateRoleSafe(ctx, roleId, input),
|
|
738
|
+
roleDelete: (roleId) => deleteRoleSafe(ctx, roleId),
|
|
739
|
+
rolePermission: (roleId, grant, resourceType, resourceId, operations) => setRolePermissionSafe(ctx, roleId, grant, { resourceType, resourceId, operations }),
|
|
740
|
+
auditList: (_opts) => notOnSafe("audit list")
|
|
638
741
|
};
|
|
639
742
|
}
|
|
640
743
|
|
|
@@ -2611,13 +2714,13 @@ async function uploadBkn(ctx, tarBuffer, opts = {}) {
|
|
|
2611
2714
|
applyTls(ctx);
|
|
2612
2715
|
const url = new URL(`${ctx.baseUrl}${BKNS}`);
|
|
2613
2716
|
url.searchParams.set("branch", opts.branch ?? "main");
|
|
2614
|
-
const
|
|
2615
|
-
|
|
2717
|
+
const form2 = new FormData();
|
|
2718
|
+
form2.append(
|
|
2616
2719
|
"file",
|
|
2617
2720
|
new Blob([new Uint8Array(tarBuffer)], { type: "application/octet-stream" }),
|
|
2618
2721
|
"bkn.tar"
|
|
2619
2722
|
);
|
|
2620
|
-
const res = await fetch(url, { method: "POST", headers: buildHeaders(ctx), body:
|
|
2723
|
+
const res = await fetch(url, { method: "POST", headers: buildHeaders(ctx), body: form2 });
|
|
2621
2724
|
const text = await res.text();
|
|
2622
2725
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
2623
2726
|
return text ? JSON.parse(text) : void 0;
|
|
@@ -2973,9 +3076,9 @@ function extractTarToDirectory(tarBuffer, dirPath) {
|
|
|
2973
3076
|
}
|
|
2974
3077
|
|
|
2975
3078
|
// src/utils/csv-import.ts
|
|
2976
|
-
import { statSync as statSync2 } from "fs";
|
|
2977
|
-
import {
|
|
2978
|
-
import { basename, resolve as resolve2 } from "path";
|
|
3079
|
+
import { readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
3080
|
+
import { readFile } from "fs/promises";
|
|
3081
|
+
import { basename, dirname, resolve as resolve2 } from "path";
|
|
2979
3082
|
import { parse } from "csv-parse/sync";
|
|
2980
3083
|
async function parseCsvFile(filePath) {
|
|
2981
3084
|
let content = await readFile(filePath, "utf8");
|
|
@@ -3039,13 +3142,25 @@ function buildImportDag(opts) {
|
|
|
3039
3142
|
]
|
|
3040
3143
|
};
|
|
3041
3144
|
}
|
|
3145
|
+
function globOne(pattern) {
|
|
3146
|
+
const dir = dirname(pattern);
|
|
3147
|
+
const re = new RegExp(
|
|
3148
|
+
`^${basename(pattern).replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`
|
|
3149
|
+
);
|
|
3150
|
+
let entries;
|
|
3151
|
+
try {
|
|
3152
|
+
entries = readdirSync3(resolve2(dir));
|
|
3153
|
+
} catch {
|
|
3154
|
+
return [];
|
|
3155
|
+
}
|
|
3156
|
+
return entries.filter((f) => re.test(f)).map((f) => resolve2(dir, f));
|
|
3157
|
+
}
|
|
3042
3158
|
async function resolveFiles(pattern) {
|
|
3043
3159
|
const out = [];
|
|
3044
3160
|
for (const part of pattern.split(",").map((p) => p.trim()).filter(Boolean)) {
|
|
3045
3161
|
if (part.includes("*") || part.includes("?")) {
|
|
3046
|
-
for
|
|
3047
|
-
|
|
3048
|
-
if (/\.csv$/i.test(p)) out.push(resolve2(p));
|
|
3162
|
+
for (const p of globOne(part)) {
|
|
3163
|
+
if (/\.csv$/i.test(p)) out.push(p);
|
|
3049
3164
|
}
|
|
3050
3165
|
} else {
|
|
3051
3166
|
statSync2(resolve2(part));
|
|
@@ -3628,21 +3743,21 @@ function resources(ctx) {
|
|
|
3628
3743
|
|
|
3629
3744
|
// src/resources/skills.ts
|
|
3630
3745
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
3631
|
-
import { basename as basename2, dirname as
|
|
3746
|
+
import { basename as basename2, dirname as dirname3, resolve as resolve5 } from "path";
|
|
3632
3747
|
|
|
3633
3748
|
// src/api/skills.ts
|
|
3634
3749
|
var BASE5 = "/api/agent-operator-integration/v1";
|
|
3635
3750
|
async function registerSkillZip(ctx, bytes, opts = {}) {
|
|
3636
3751
|
applyTls(ctx);
|
|
3637
|
-
const
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
if (opts.source)
|
|
3641
|
-
if (opts.extendInfo)
|
|
3752
|
+
const form2 = new FormData();
|
|
3753
|
+
form2.set("file_type", "zip");
|
|
3754
|
+
form2.set("file", new Blob([bytes]), opts.filename ?? "skill.zip");
|
|
3755
|
+
if (opts.source) form2.set("source", opts.source);
|
|
3756
|
+
if (opts.extendInfo) form2.set("extend_info", JSON.stringify(opts.extendInfo));
|
|
3642
3757
|
const res = await fetch(`${ctx.baseUrl}${BASE5}/skills`, {
|
|
3643
3758
|
method: "POST",
|
|
3644
3759
|
headers: buildHeaders(ctx),
|
|
3645
|
-
body:
|
|
3760
|
+
body: form2
|
|
3646
3761
|
});
|
|
3647
3762
|
const text = await res.text();
|
|
3648
3763
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
@@ -3650,13 +3765,13 @@ async function registerSkillZip(ctx, bytes, opts = {}) {
|
|
|
3650
3765
|
}
|
|
3651
3766
|
async function updateSkillPackageZip(ctx, skillId, bytes, filename = "skill.zip") {
|
|
3652
3767
|
applyTls(ctx);
|
|
3653
|
-
const
|
|
3654
|
-
|
|
3655
|
-
|
|
3768
|
+
const form2 = new FormData();
|
|
3769
|
+
form2.set("file_type", "zip");
|
|
3770
|
+
form2.set("file", new Blob([bytes]), filename);
|
|
3656
3771
|
const res = await fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/package`, {
|
|
3657
3772
|
method: "PUT",
|
|
3658
3773
|
headers: buildHeaders(ctx),
|
|
3659
|
-
body:
|
|
3774
|
+
body: form2
|
|
3660
3775
|
});
|
|
3661
3776
|
const text = await res.text();
|
|
3662
3777
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
@@ -3730,11 +3845,11 @@ function setSkillStatus(ctx, skillId, status2) {
|
|
|
3730
3845
|
}
|
|
3731
3846
|
|
|
3732
3847
|
// 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";
|
|
3848
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as readdirSync4, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
3849
|
+
import { dirname as dirname2, join as join3, relative, resolve as resolve4, sep } from "path";
|
|
3735
3850
|
import JSZip from "jszip";
|
|
3736
3851
|
function walk(dir, base, files) {
|
|
3737
|
-
for (const entry of
|
|
3852
|
+
for (const entry of readdirSync4(dir)) {
|
|
3738
3853
|
const full = join3(dir, entry);
|
|
3739
3854
|
const st = statSync3(full);
|
|
3740
3855
|
if (st.isDirectory()) walk(full, base, files);
|
|
@@ -3762,7 +3877,7 @@ async function unzipToDirectory(bytes, dir) {
|
|
|
3762
3877
|
for (const entry of entries) {
|
|
3763
3878
|
if (entry.dir) continue;
|
|
3764
3879
|
const out = join3(abs, entry.name);
|
|
3765
|
-
mkdirSync3(
|
|
3880
|
+
mkdirSync3(dirname2(out), { recursive: true });
|
|
3766
3881
|
writeFileSync2(out, await entry.async("nodebuffer"));
|
|
3767
3882
|
written.push(out);
|
|
3768
3883
|
}
|
|
@@ -3795,7 +3910,7 @@ function skills(ctx) {
|
|
|
3795
3910
|
download: async (skillId, outPath) => {
|
|
3796
3911
|
const bytes = await downloadSkill(ctx, skillId);
|
|
3797
3912
|
const dest = resolve5(outPath ?? `${skillId}.zip`);
|
|
3798
|
-
mkdirSync4(
|
|
3913
|
+
mkdirSync4(dirname3(dest), { recursive: true });
|
|
3799
3914
|
writeFileSync3(dest, bytes);
|
|
3800
3915
|
return { skillId, path: dest, bytes: bytes.length };
|
|
3801
3916
|
},
|
|
@@ -3811,7 +3926,7 @@ function skills(ctx) {
|
|
|
3811
3926
|
|
|
3812
3927
|
// src/resources/toolboxes.ts
|
|
3813
3928
|
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
3814
|
-
import { dirname as
|
|
3929
|
+
import { dirname as dirname4, resolve as resolve6 } from "path";
|
|
3815
3930
|
|
|
3816
3931
|
// src/api/toolboxes.ts
|
|
3817
3932
|
import { readFile as readFile2 } from "fs/promises";
|
|
@@ -3831,12 +3946,12 @@ async function exportConfig(ctx, id, type = "toolbox") {
|
|
|
3831
3946
|
async function importConfig(ctx, filePath, type = "toolbox") {
|
|
3832
3947
|
applyTls(ctx);
|
|
3833
3948
|
const buf = await readFile2(filePath);
|
|
3834
|
-
const
|
|
3835
|
-
|
|
3949
|
+
const form2 = new FormData();
|
|
3950
|
+
form2.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
|
|
3836
3951
|
const res = await fetch(`${ctx.baseUrl}${IMPEX}/import/${encodeURIComponent(type)}`, {
|
|
3837
3952
|
method: "POST",
|
|
3838
3953
|
headers: buildHeaders(ctx),
|
|
3839
|
-
body:
|
|
3954
|
+
body: form2
|
|
3840
3955
|
});
|
|
3841
3956
|
const text = await res.text();
|
|
3842
3957
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
@@ -3845,13 +3960,13 @@ async function importConfig(ctx, filePath, type = "toolbox") {
|
|
|
3845
3960
|
async function uploadTool(ctx, boxId, filePath, metadataType = "openapi") {
|
|
3846
3961
|
applyTls(ctx);
|
|
3847
3962
|
const buf = await readFile2(filePath);
|
|
3848
|
-
const
|
|
3849
|
-
|
|
3850
|
-
|
|
3963
|
+
const form2 = new FormData();
|
|
3964
|
+
form2.append("metadata_type", metadataType);
|
|
3965
|
+
form2.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
|
|
3851
3966
|
const res = await fetch(`${ctx.baseUrl}${PATH}/${encodeURIComponent(boxId)}/tool`, {
|
|
3852
3967
|
method: "POST",
|
|
3853
3968
|
headers: buildHeaders(ctx),
|
|
3854
|
-
body:
|
|
3969
|
+
body: form2
|
|
3855
3970
|
});
|
|
3856
3971
|
const text = await res.text();
|
|
3857
3972
|
if (!res.ok) throw new HttpError(res.status, res.statusText, text);
|
|
@@ -3937,7 +4052,7 @@ function toolboxes(ctx) {
|
|
|
3937
4052
|
export: async (id, outPath, type) => {
|
|
3938
4053
|
const bytes = await exportConfig(ctx, id, type);
|
|
3939
4054
|
const dest = resolve6(outPath);
|
|
3940
|
-
mkdirSync5(
|
|
4055
|
+
mkdirSync5(dirname4(dest), { recursive: true });
|
|
3941
4056
|
writeFileSync4(dest, bytes);
|
|
3942
4057
|
return { id, path: dest, bytes: bytes.length };
|
|
3943
4058
|
},
|
|
@@ -4017,7 +4132,7 @@ async function getSpansByConversation(ctx, conversationId, opts = {}) {
|
|
|
4017
4132
|
}
|
|
4018
4133
|
|
|
4019
4134
|
// src/trace-ai/claude-judge.ts
|
|
4020
|
-
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
4135
|
+
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
4021
4136
|
var ClaudeJudgeError = class extends Error {
|
|
4022
4137
|
constructor(message, reason) {
|
|
4023
4138
|
super(message);
|
|
@@ -4063,7 +4178,7 @@ async function judgeJson(prompt, opts = {}) {
|
|
|
4063
4178
|
const timeoutMs = opts.timeoutMs ?? 12e4;
|
|
4064
4179
|
const args = ["-p", "--output-format=json", "--dangerously-skip-permissions"];
|
|
4065
4180
|
const stdout = await new Promise((resolve7, reject) => {
|
|
4066
|
-
const child =
|
|
4181
|
+
const child = spawn2(binary, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
4067
4182
|
let out = "";
|
|
4068
4183
|
let timedOut = false;
|
|
4069
4184
|
const killer = setTimeout(() => {
|
|
@@ -4864,6 +4979,7 @@ function createClient(opts = {}) {
|
|
|
4864
4979
|
// src/resources/auth.ts
|
|
4865
4980
|
var auth_exports = {};
|
|
4866
4981
|
__export(auth_exports, {
|
|
4982
|
+
attachNoAuth: () => attachNoAuth,
|
|
4867
4983
|
attachToken: () => attachToken,
|
|
4868
4984
|
currentToken: () => currentToken,
|
|
4869
4985
|
deletePlatform: () => deletePlatform,
|
|
@@ -4901,12 +5017,19 @@ function attachToken(baseUrl, accessToken, opts = {}) {
|
|
|
4901
5017
|
refreshToken: opts.refreshToken,
|
|
4902
5018
|
idToken: opts.idToken,
|
|
4903
5019
|
tlsInsecure: opts.insecure,
|
|
4904
|
-
|
|
5020
|
+
// Prefer the account the user typed (-u); device tokens carry no username.
|
|
5021
|
+
username: opts.username ?? decodeJwt(opts.idToken ?? accessToken)?.preferred_username
|
|
4905
5022
|
};
|
|
4906
5023
|
const userId = writeToken(url, token);
|
|
4907
5024
|
setActivePlatform(url);
|
|
4908
5025
|
return { baseUrl: url, userId, username: usernameOf(token) };
|
|
4909
5026
|
}
|
|
5027
|
+
function attachNoAuth(baseUrl, opts = {}) {
|
|
5028
|
+
const url = normalize(baseUrl);
|
|
5029
|
+
writeToken(url, { baseUrl: url, accessToken: "", noAuth: true, tlsInsecure: opts.insecure });
|
|
5030
|
+
setActivePlatform(url);
|
|
5031
|
+
return { baseUrl: url, noAuth: true };
|
|
5032
|
+
}
|
|
4910
5033
|
function status() {
|
|
4911
5034
|
const baseUrl = activePlatform();
|
|
4912
5035
|
if (!baseUrl) return { hasToken: false };
|
|
@@ -4961,14 +5084,16 @@ function logout() {
|
|
|
4961
5084
|
function deletePlatform(baseUrl, userId) {
|
|
4962
5085
|
return deleteToken(normalize(baseUrl), userId);
|
|
4963
5086
|
}
|
|
4964
|
-
function switchUser(baseUrl,
|
|
5087
|
+
function switchUser(baseUrl, userOrName) {
|
|
4965
5088
|
const url = normalize(baseUrl);
|
|
4966
|
-
|
|
4967
|
-
|
|
5089
|
+
const users = usersOf(url);
|
|
5090
|
+
const match = users.find((u) => u.userId === userOrName) ?? users.find((u) => (u.username ?? u.displayName) === userOrName);
|
|
5091
|
+
if (!match) {
|
|
5092
|
+
throw new InputError(`No saved user '${userOrName}' on ${url}. Try \`auth users ${url}\`.`);
|
|
4968
5093
|
}
|
|
4969
|
-
setActiveUser(url, userId);
|
|
5094
|
+
setActiveUser(url, match.userId);
|
|
4970
5095
|
setActivePlatform(url);
|
|
4971
|
-
return { baseUrl: url, userId };
|
|
5096
|
+
return { baseUrl: url, userId: match.userId, username: match.username ?? match.displayName };
|
|
4972
5097
|
}
|
|
4973
5098
|
function usersOf(baseUrl) {
|
|
4974
5099
|
const url = normalize(baseUrl);
|
|
@@ -4996,14 +5121,18 @@ export {
|
|
|
4996
5121
|
InputError,
|
|
4997
5122
|
toExitCode,
|
|
4998
5123
|
formatError,
|
|
5124
|
+
decodeJwt,
|
|
4999
5125
|
activePlatform,
|
|
5000
5126
|
setActivePlatform,
|
|
5001
5127
|
readPlatformConfig,
|
|
5002
5128
|
writePlatformConfig,
|
|
5003
5129
|
resolveContext,
|
|
5130
|
+
openBrowser,
|
|
5131
|
+
fetchAuthStatus,
|
|
5132
|
+
deviceLogin,
|
|
5133
|
+
credentialDeviceLogin,
|
|
5004
5134
|
request,
|
|
5005
|
-
|
|
5006
|
-
renderOrgTree,
|
|
5135
|
+
getUserSafe,
|
|
5007
5136
|
admin,
|
|
5008
5137
|
agents,
|
|
5009
5138
|
context,
|
|
@@ -5020,6 +5149,7 @@ export {
|
|
|
5020
5149
|
vega,
|
|
5021
5150
|
createClient,
|
|
5022
5151
|
attachToken,
|
|
5152
|
+
attachNoAuth,
|
|
5023
5153
|
status,
|
|
5024
5154
|
currentToken,
|
|
5025
5155
|
whoami,
|
|
@@ -5028,8 +5158,7 @@ export {
|
|
|
5028
5158
|
logout,
|
|
5029
5159
|
deletePlatform,
|
|
5030
5160
|
switchUser,
|
|
5031
|
-
usersOf,
|
|
5032
5161
|
exportCreds,
|
|
5033
5162
|
auth_exports
|
|
5034
5163
|
};
|
|
5035
|
-
//# sourceMappingURL=chunk-
|
|
5164
|
+
//# sourceMappingURL=chunk-APJNRHLS.js.map
|