@openbkn/bkn-sdk 0.1.1-alpha.0 → 0.1.1-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,8 +38,12 @@ function toExitCode(err) {
38
38
  }
39
39
  function formatError(err) {
40
40
  if (err instanceof HttpError) {
41
- if (err.status === 401 || err.status === 403) {
42
- return `Not authorized (HTTP ${err.status}). Run \`openbkn auth login\` and retry.`;
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 token = opts.token ?? process.env.BKN_TOKEN ?? stored?.accessToken;
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: opts.insecure ?? stored?.tlsInsecure ?? false
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
- const res = await fetch(url, {
265
- method: init.method ?? (hasBody ? "POST" : "GET"),
266
- headers: buildHeaders(ctx, {
267
- ...hasBody ? { "content-type": "application/json" } : {},
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
- // src/api/eacp-crypto.ts
282
- import {
283
- constants,
284
- createPrivateKey,
285
- createPublicKey,
286
- publicEncrypt
287
- } from "crypto";
288
- var EACP_MODIFYPWD_PRIVATE_KEY_PEM = `-----BEGIN RSA PRIVATE KEY-----
289
- MIICXgIBAAKBgQDB2fhLla9rMx+6LWTXajnK11Kdp520s1Q+TfPfIXI/7G9+L2YC
290
- 4RA3M5rgRi32s5+UFQ/CVqUFqMqVuzaZ4lw/uEdk1qHcP0g6LB3E9wkl2FclFR0M
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/admin.ts
317
- var UM = "/api/user-management/v1";
318
- var AUTHZ = "/api/authorization/v1";
319
- var ISFWEB = "/isfweb/api/ShareMgnt";
320
- async function callerUserId(ctx) {
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
- async function shareMgnt(ctx, method, params = []) {
330
- try {
331
- return await request(ctx, `${ISFWEB}/${method}`, { method: "POST", body: params });
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
- async function listAllDepartments(ctx, role = "super_admin", pageSize = 100) {
361
- const out = [];
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 listUsers(ctx, opts = {}) {
374
- return request(ctx, `${UM}/console/search-users/${USER_FIELDS}`, {
375
- query: {
376
- role: opts.role ?? "super_admin",
377
- offset: opts.offset ?? 0,
378
- limit: opts.limit ?? 100,
379
- department_id: opts.orgId || void 0,
380
- name: opts.name || void 0
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 listRoles(ctx, opts = {}) {
385
- return request(ctx, `${AUTHZ}/roles`, {
386
- query: {
387
- offset: opts.offset ?? 0,
388
- limit: opts.limit ?? 100,
389
- keyword: opts.keyword || void 0
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 getRole(ctx, roleId) {
394
- return request(ctx, `${AUTHZ}/roles/${encodeURIComponent(roleId)}`);
543
+ async function deleteUserSafe(ctx, userId) {
544
+ await request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`, { method: "DELETE" });
545
+ return { ok: true };
395
546
  }
396
- function getUser(ctx, userId) {
397
- return shareMgnt(ctx, "Usrm_GetUserInfo", [userId]);
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
- async function getUserRoles(ctx, userId) {
400
- try {
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 getDepartment(ctx, deptId) {
425
- try {
426
- return await shareMgnt(ctx, "Usrm_GetOrgDepartmentById", [deptId]);
427
- } catch (e) {
428
- const msg = e instanceof Error ? e.message : String(e);
429
- if (!/部门不存在|errID:?\s*20201|errID=?\s*99|NoneType.+subscriptable/i.test(msg)) throw e;
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 getDepartmentMembers(ctx, deptId, opts = {}) {
434
- return request(ctx, `${UM}/department-members/${encodeURIComponent(deptId)}/users`, {
435
- query: { role: opts.role ?? "super_admin", offset: opts.offset ?? 0, limit: opts.limit ?? 100 }
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
- async function createDepartment(ctx, input) {
439
- const ncTAddDepartParam = {
440
- parentId: input.parentId ?? "-1",
441
- departName: input.name,
442
- managerID: input.managerID ?? null,
443
- code: input.code ?? "",
444
- remark: input.remark ?? "",
445
- status: input.status ?? 1,
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
- async function deleteUser(ctx, userId) {
533
- try {
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
- async function setUserPassword(ctx, userId, newPassword) {
542
- await request(ctx, `${UM}/management/users/${encodeURIComponent(userId)}/password`, {
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
- var EACP = "/api/eacp/v1";
549
- function listAuditLogs(ctx, opts = {}) {
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
- page_num: opts.page ?? 1,
554
- page_size: opts.size ?? 30,
555
- user_name: opts.user || void 0,
556
- start_time: opts.start || void 0,
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 changePassword(ctx, account, oldPassword, newPassword) {
562
- return request(ctx, `${EACP}/auth1/modifypassword`, {
563
- method: "POST",
598
+ function updateDepartmentSafe(ctx, deptId, input) {
599
+ return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`, {
600
+ method: "PUT",
564
601
  body: {
565
- account,
566
- oldpwd: encryptModifyPwd(oldPassword),
567
- newpwd: encryptModifyPwd(newPassword)
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 listRoleMembers(ctx, roleId, opts = {}) {
572
- return request(ctx, `${AUTHZ}/role-members/${encodeURIComponent(roleId)}`, {
573
- query: {
574
- offset: opts.offset ?? 0,
575
- limit: opts.limit ?? 100,
576
- keyword: opts.keyword || void 0
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 modifyRoleMembers(ctx, roleId, method, members) {
581
- return request(ctx, `${AUTHZ}/role-members/${encodeURIComponent(roleId)}`, {
582
- method: "POST",
583
- body: { method, members }
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
- // src/utils/org-tree.ts
588
- function buildOrgTree(entries) {
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 renderOrgTree(nodes, prefix = "") {
604
- const lines = [];
605
- nodes.forEach((node, i) => {
606
- const last = i === nodes.length - 1;
607
- lines.push(`${prefix}${last ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "}${node.name} (id: ${node.id})`);
608
- if (node.children.length) {
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 lines.join("\n");
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
- orgList: (opts) => listDepartments(ctx, opts),
619
- orgGet: (deptId) => getDepartment(ctx, deptId),
620
- orgMembers: (deptId, opts) => getDepartmentMembers(ctx, deptId, opts),
621
- orgTree: async (role) => buildOrgTree(await listAllDepartments(ctx, role)),
622
- orgCreate: (input) => createDepartment(ctx, input),
623
- orgUpdate: (deptId, input) => updateDepartment(ctx, deptId, input),
624
- orgDelete: (deptId) => deleteDepartment(ctx, deptId),
625
- userList: (opts) => listUsers(ctx, opts),
626
- userGet: (userId) => getUser(ctx, userId),
627
- userRoles: (userId) => getUserRoles(ctx, userId),
628
- userCreate: (input) => createUser(ctx, input),
629
- userUpdate: (userId, input) => updateUser(ctx, userId, input),
630
- userDelete: (userId) => deleteUser(ctx, userId),
631
- userResetPassword: (userId, newPassword) => setUserPassword(ctx, userId, newPassword),
632
- roleList: (opts) => listRoles(ctx, opts),
633
- roleGet: (roleId) => getRole(ctx, roleId),
634
- roleMembers: (roleId, opts) => listRoleMembers(ctx, roleId, opts),
635
- addRoleMember: (roleId, id, type = "user") => modifyRoleMembers(ctx, roleId, "POST", [{ id, type }]),
636
- removeRoleMember: (roleId, id, type = "user") => modifyRoleMembers(ctx, roleId, "DELETE", [{ id, type }]),
637
- auditList: (opts) => listAuditLogs(ctx, opts)
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 form = new FormData();
2615
- form.append(
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: form });
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;
@@ -2783,8 +2873,8 @@ function discoverCatalog(ctx, id, wait = true) {
2783
2873
  });
2784
2874
  }
2785
2875
  function listCatalogResources(ctx, id, category) {
2786
- return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/resources`, {
2787
- query: { category: category || void 0 }
2876
+ return request(ctx, `${VEGA_BASE}/resources`, {
2877
+ query: { catalog_id: id, category: category || void 0 }
2788
2878
  });
2789
2879
  }
2790
2880
  function catalogHealthStatus(ctx, ids) {
@@ -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 { glob, readFile } from "fs/promises";
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 await (const entry of glob(part)) {
3047
- const p = String(entry);
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 dirname2, resolve as resolve5 } from "path";
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 form = new FormData();
3638
- form.set("file_type", "zip");
3639
- form.set("file", new Blob([bytes]), opts.filename ?? "skill.zip");
3640
- if (opts.source) form.set("source", opts.source);
3641
- if (opts.extendInfo) form.set("extend_info", JSON.stringify(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: form
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 form = new FormData();
3654
- form.set("file_type", "zip");
3655
- form.set("file", new Blob([bytes]), filename);
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: form
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 readdirSync3, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
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 readdirSync3(dir)) {
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(dirname(out), { recursive: true });
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(dirname2(dest), { recursive: true });
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 dirname3, resolve as resolve6 } from "path";
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 form = new FormData();
3835
- form.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
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: form
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 form = new FormData();
3849
- form.append("metadata_type", metadataType);
3850
- form.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
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: form
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(dirname3(dest), { recursive: true });
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 = spawn(binary, args, { stdio: ["pipe", "pipe", "pipe"] });
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
- username: decodeJwt(opts.idToken ?? accessToken)?.preferred_username
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, userId) {
5067
+ function switchUser(baseUrl, userOrName) {
4965
5068
  const url = normalize(baseUrl);
4966
- if (!readToken(url, userId)) {
4967
- throw new InputError(`No saved token for user '${userId}' on ${url}.`);
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
- changePassword,
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-DXA44XWY.js.map
5142
+ //# sourceMappingURL=chunk-5MOIXIMJ.js.map