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

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.
@@ -4,11 +4,6 @@ var __export = (target, all) => {
4
4
  __defProp(target, name, { get: all[name], enumerable: true });
5
5
  };
6
6
 
7
- // src/types.ts
8
- var DEFAULT_BUSINESS_DOMAIN = "bd_public";
9
- var DEFAULT_LIST_LIMIT = 30;
10
- var DEFAULT_QUERY_LIMIT = 50;
11
-
12
7
  // src/utils/errors.ts
13
8
  var HttpError = class extends Error {
14
9
  status;
@@ -38,8 +33,12 @@ function toExitCode(err) {
38
33
  }
39
34
  function formatError(err) {
40
35
  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.`;
36
+ const serverMsg = serverError(err.body);
37
+ if (err.status === 401) {
38
+ return `Not authorized (HTTP 401)${serverMsg ? `: ${serverMsg}` : ""}. Run \`openbkn auth login\` and retry.`;
39
+ }
40
+ if (err.status === 403) {
41
+ return `Forbidden (HTTP 403)${serverMsg ? `: ${serverMsg}` : " \u2014 admin privileges required"}.`;
43
42
  }
44
43
  const detail = err.body ? `: ${truncate(err.body, 500)}` : "";
45
44
  return `Request failed (HTTP ${err.status} ${err.statusText})${detail}`;
@@ -56,6 +55,16 @@ function formatError(err) {
56
55
  }
57
56
  return String(err);
58
57
  }
58
+ function serverError(body) {
59
+ if (!body) return "";
60
+ try {
61
+ const j = JSON.parse(body);
62
+ const m = j.error ?? j.detail ?? j.description ?? j.message;
63
+ return typeof m === "string" ? m : "";
64
+ } catch {
65
+ return "";
66
+ }
67
+ }
59
68
  function isTlsCertError(code) {
60
69
  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
70
  }
@@ -63,6 +72,271 @@ function truncate(s, n) {
63
72
  return s.length > n ? `${s.slice(0, n)}\u2026` : s;
64
73
  }
65
74
 
75
+ // src/auth/oauth.ts
76
+ import { spawn } from "child_process";
77
+ function normalizeBaseUrl(value) {
78
+ return value.replace(/\/+$/, "");
79
+ }
80
+ function mapToken(data) {
81
+ return {
82
+ accessToken: data.access_token,
83
+ refreshToken: data.refresh_token,
84
+ idToken: data.id_token
85
+ };
86
+ }
87
+ function isHeadless() {
88
+ if (process.platform !== "linux") return false;
89
+ return !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY;
90
+ }
91
+ function openBrowser(url) {
92
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
93
+ try {
94
+ const child = spawn(cmd, [url], {
95
+ stdio: "ignore",
96
+ detached: true,
97
+ shell: process.platform === "win32"
98
+ });
99
+ child.on("error", () => {
100
+ });
101
+ child.unref();
102
+ } catch {
103
+ }
104
+ }
105
+ function mergeCookies(existing, res) {
106
+ const setCookies = typeof res.headers.getSetCookie === "function" ? res.headers.getSetCookie() : res.headers.get("set-cookie") ? [res.headers.get("set-cookie")] : [];
107
+ const map = /* @__PURE__ */ new Map();
108
+ const add = (pair) => {
109
+ const eq = pair.indexOf("=");
110
+ if (eq > 0) map.set(pair.slice(0, eq), pair.slice(eq + 1));
111
+ };
112
+ for (const p of existing.split(";").map((s) => s.trim()).filter(Boolean))
113
+ add(p);
114
+ for (const sc of setCookies) add(sc.split(";")[0]?.trim() ?? "");
115
+ return [...map.entries()].map(([k, v]) => `${k}=${v}`).join("; ");
116
+ }
117
+ async function fetchAuthStatus(baseUrl) {
118
+ try {
119
+ const res = await fetch(`${normalizeBaseUrl(baseUrl)}/install-status.json`, {
120
+ headers: { Accept: "application/json" }
121
+ });
122
+ if (!res.ok) return null;
123
+ const j = await res.json();
124
+ if (!j.auth) return null;
125
+ return { enabled: Boolean(j.auth.enabled), stack: j.auth.stack };
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+ async function refreshAccessToken(baseUrl, refreshToken, clientId = "openbkn-sdk") {
131
+ const base = normalizeBaseUrl(baseUrl);
132
+ const res = await fetch(`${base}/oauth2/token`, {
133
+ method: "POST",
134
+ headers: {
135
+ "Content-Type": "application/x-www-form-urlencoded",
136
+ Accept: "application/json"
137
+ },
138
+ body: new URLSearchParams({
139
+ grant_type: "refresh_token",
140
+ refresh_token: refreshToken,
141
+ client_id: clientId
142
+ }).toString()
143
+ });
144
+ if (!res.ok) {
145
+ throw new Error(
146
+ `Token refresh failed (${res.status}): ${await res.text() || res.statusText}`
147
+ );
148
+ }
149
+ return mapToken(await res.json());
150
+ }
151
+ var DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
152
+ var DEFAULT_DEVICE_CLIENT_ID = "openbkn-sdk";
153
+ var DEVICE_SCOPE = "openid offline";
154
+ var form = (body) => ({
155
+ method: "POST",
156
+ headers: {
157
+ "Content-Type": "application/x-www-form-urlencoded",
158
+ Accept: "application/json"
159
+ },
160
+ body: new URLSearchParams(body).toString()
161
+ });
162
+ async function requestDeviceCode(base, clientId, scope, audience) {
163
+ const params = { client_id: clientId, scope };
164
+ if (audience) params.audience = audience;
165
+ const res = await fetch(`${base}/oauth2/device/auth`, form(params));
166
+ if (!res.ok) {
167
+ throw new Error(`Device auth failed (${res.status}): ${await res.text() || res.statusText}`);
168
+ }
169
+ const da = await res.json();
170
+ if (!da.verification_uri) {
171
+ throw new Error(
172
+ "Device auth returned no verification_uri \u2014 is Hydra's device flow configured?"
173
+ );
174
+ }
175
+ return da;
176
+ }
177
+ async function pollDeviceToken(base, deviceCode, clientId, intervalMs, windowMs) {
178
+ let interval = intervalMs;
179
+ const deadline = Date.now() + windowMs;
180
+ while (Date.now() < deadline) {
181
+ await new Promise((r) => setTimeout(r, interval));
182
+ const tokRes = await fetch(
183
+ `${base}/oauth2/token`,
184
+ form({ grant_type: DEVICE_GRANT, device_code: deviceCode, client_id: clientId })
185
+ );
186
+ const data = await tokRes.json().catch(() => ({}));
187
+ if (tokRes.ok) return mapToken(data);
188
+ switch (data.error) {
189
+ case "authorization_pending":
190
+ break;
191
+ // keep polling
192
+ case "slow_down":
193
+ interval += 5e3;
194
+ break;
195
+ case "access_denied":
196
+ throw new InputError("Device authorization denied.");
197
+ case "expired_token":
198
+ throw new InputError("Device code expired \u2014 run login again.");
199
+ default:
200
+ throw new Error(`Device token poll failed: ${String(data.error ?? tokRes.status)}`);
201
+ }
202
+ }
203
+ throw new InputError("Device login timed out.");
204
+ }
205
+ async function deviceLogin(baseUrl, opts = {}) {
206
+ const base = normalizeBaseUrl(baseUrl);
207
+ const clientId = opts.clientId ?? DEFAULT_DEVICE_CLIENT_ID;
208
+ const da = await requestDeviceCode(base, clientId, opts.scope ?? DEVICE_SCOPE, opts.audience);
209
+ opts.onPrompt?.({
210
+ userCode: da.user_code,
211
+ verificationUri: onBaseHost(base, da.verification_uri),
212
+ verificationUriComplete: da.verification_uri_complete ? onBaseHost(base, da.verification_uri_complete) : void 0
213
+ });
214
+ const windowMs = Math.min(opts.timeoutMs ?? Number.POSITIVE_INFINITY, da.expires_in * 1e3);
215
+ return pollDeviceToken(base, da.device_code, clientId, (da.interval ?? 5) * 1e3, windowMs);
216
+ }
217
+ function onBaseHost(base, uri) {
218
+ try {
219
+ const u = new URL(uri);
220
+ const b = new URL(base);
221
+ return `${b.origin}${u.pathname}${u.search}`;
222
+ } catch {
223
+ return uri;
224
+ }
225
+ }
226
+ async function credentialDeviceLogin(baseUrl, username, password, opts = {}) {
227
+ const base = normalizeBaseUrl(baseUrl);
228
+ const clientId = opts.clientId ?? DEFAULT_DEVICE_CLIENT_ID;
229
+ const da = await requestDeviceCode(base, clientId, opts.scope ?? DEVICE_SCOPE, opts.audience);
230
+ let jar = "";
231
+ const hop = async (url, init) => {
232
+ const r = await fetch(url, {
233
+ method: init?.method ?? "GET",
234
+ headers: { Cookie: jar, Accept: "text/html,*/*;q=0.8", ...init?.headers ?? {} },
235
+ body: init?.body,
236
+ redirect: "manual"
237
+ });
238
+ jar = mergeCookies(jar, r);
239
+ return r;
240
+ };
241
+ const postForm = (path, body) => hop(`${base}${path}`, {
242
+ method: "POST",
243
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
244
+ body: new URLSearchParams(body).toString()
245
+ });
246
+ const v = await hop(`${base}/oauth2/device/verify?user_code=${encodeURIComponent(da.user_code)}`);
247
+ let loc = v.headers.get("location");
248
+ for (let i = 0; i < 25 && loc; i++) {
249
+ const u = new URL(loc, base);
250
+ let r;
251
+ if (u.pathname.endsWith("/device")) {
252
+ const dc = u.searchParams.get("device_challenge");
253
+ if (!dc) throw new Error(`/device without device_challenge: ${loc}`);
254
+ r = await postForm("/device", { device_challenge: dc, user_code: da.user_code });
255
+ } else if (u.pathname.endsWith("/login")) {
256
+ const lc = u.searchParams.get("login_challenge");
257
+ if (!lc) throw new Error(`/login without login_challenge: ${loc}`);
258
+ r = await postForm("/login", { login_challenge: lc, account: username, password });
259
+ if (r.status === 401) throw new InputError("Sign-in failed: wrong account or password");
260
+ } else if (u.pathname.endsWith("/consent")) {
261
+ const cc = u.searchParams.get("consent_challenge");
262
+ if (!cc) throw new Error(`/consent without consent_challenge: ${loc}`);
263
+ r = await postForm("/consent", { consent_challenge: cc, decision: "allow" });
264
+ } else {
265
+ r = await hop(u.href);
266
+ }
267
+ loc = r.headers.get("location");
268
+ }
269
+ const windowMs = Math.min(opts.timeoutMs ?? Number.POSITIVE_INFINITY, da.expires_in * 1e3);
270
+ return pollDeviceToken(base, da.device_code, clientId, (da.interval ?? 5) * 1e3, windowMs);
271
+ }
272
+
273
+ // src/api/headers.ts
274
+ function buildHeaders(ctx, extra) {
275
+ return {
276
+ // No token = a no-auth platform (no bkn-safe); send no Authorization.
277
+ ...ctx.token ? { authorization: `Bearer ${ctx.token}`, token: ctx.token } : {},
278
+ "x-business-domain": ctx.businessDomain,
279
+ ...extra
280
+ };
281
+ }
282
+
283
+ // src/api/tls.ts
284
+ function applyTls(ctx) {
285
+ if (ctx.insecure && process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0") {
286
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
287
+ }
288
+ }
289
+
290
+ // src/api/http.ts
291
+ var DEFAULT_TIMEOUT_MS = 3e4;
292
+ async function request(ctx, path, init = {}) {
293
+ const url = new URL(path.startsWith("http") ? path : `${ctx.baseUrl}${path}`);
294
+ for (const [k, v] of Object.entries(init.query ?? {})) {
295
+ if (v !== void 0) url.searchParams.set(k, String(v));
296
+ }
297
+ applyTls(ctx);
298
+ const hasBody = init.body !== void 0;
299
+ const controller = new AbortController();
300
+ const timer = setTimeout(() => controller.abort(), init.timeoutMs ?? DEFAULT_TIMEOUT_MS);
301
+ const send = () => fetch(url, {
302
+ method: init.method ?? (hasBody ? "POST" : "GET"),
303
+ headers: buildHeaders(ctx, {
304
+ ...hasBody ? { "content-type": "application/json" } : {},
305
+ ...init.headers
306
+ }),
307
+ body: hasBody ? JSON.stringify(init.body) : void 0,
308
+ signal: controller.signal
309
+ });
310
+ try {
311
+ let res = await send();
312
+ if (res.status === 401 && ctx.refresh && await tryRefresh(ctx)) {
313
+ res = await send();
314
+ }
315
+ const text = await res.text();
316
+ if (!res.ok) throw new HttpError(res.status, res.statusText, text);
317
+ return text ? JSON.parse(text) : void 0;
318
+ } finally {
319
+ clearTimeout(timer);
320
+ }
321
+ }
322
+ async function tryRefresh(ctx) {
323
+ if (!ctx.refresh) return false;
324
+ try {
325
+ const t = await refreshAccessToken(ctx.baseUrl, ctx.refresh.refreshToken, ctx.refresh.clientId);
326
+ ctx.token = t.accessToken;
327
+ if (t.refreshToken) ctx.refresh.refreshToken = t.refreshToken;
328
+ ctx.refresh.persist(t);
329
+ return true;
330
+ } catch {
331
+ return false;
332
+ }
333
+ }
334
+
335
+ // src/types.ts
336
+ var DEFAULT_BUSINESS_DOMAIN = "bd_public";
337
+ var DEFAULT_LIST_LIMIT = 30;
338
+ var DEFAULT_QUERY_LIMIT = 50;
339
+
66
340
  // src/config/store.ts
67
341
  import {
68
342
  chmodSync,
@@ -220,424 +494,269 @@ function resolveContext(opts = {}) {
220
494
  }
221
495
  const normalized = baseUrl.replace(/\/+$/, "");
222
496
  const stored = readToken(normalized);
223
- const token = opts.token ?? process.env.BKN_TOKEN ?? stored?.accessToken;
224
- if (!token) {
497
+ const explicit = opts.token ?? process.env.BKN_TOKEN;
498
+ const token = explicit ?? stored?.accessToken ?? "";
499
+ if (!token && !stored?.noAuth) {
225
500
  throw new InputError("No access token. Set BKN_TOKEN or run `openbkn auth login`.");
226
501
  }
502
+ const insecure = opts.insecure ?? stored?.tlsInsecure ?? false;
503
+ const refresh = !explicit && stored?.refreshToken ? {
504
+ refreshToken: stored.refreshToken,
505
+ persist: (t) => {
506
+ writeToken(normalized, {
507
+ ...stored,
508
+ accessToken: t.accessToken,
509
+ refreshToken: t.refreshToken ?? stored.refreshToken,
510
+ idToken: t.idToken ?? stored.idToken
511
+ });
512
+ }
513
+ } : void 0;
227
514
  return {
228
515
  baseUrl: normalized,
229
516
  token,
230
517
  businessDomain: opts.businessDomain ?? readPlatformConfig(normalized).businessDomain ?? DEFAULT_BUSINESS_DOMAIN,
231
- insecure: opts.insecure ?? stored?.tlsInsecure ?? false
232
- };
233
- }
234
-
235
- // src/api/headers.ts
236
- function buildHeaders(ctx, extra) {
237
- return {
238
- authorization: `Bearer ${ctx.token}`,
239
- token: ctx.token,
240
- "x-business-domain": ctx.businessDomain,
241
- ...extra
518
+ insecure,
519
+ ...refresh ? { refresh } : {}
242
520
  };
243
521
  }
244
522
 
245
- // src/api/tls.ts
246
- function applyTls(ctx) {
247
- if (ctx.insecure && process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0") {
248
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
249
- }
250
- }
251
-
252
- // src/api/http.ts
253
- var DEFAULT_TIMEOUT_MS = 3e4;
254
- async function request(ctx, path, init = {}) {
255
- const url = new URL(path.startsWith("http") ? path : `${ctx.baseUrl}${path}`);
256
- for (const [k, v] of Object.entries(init.query ?? {})) {
257
- if (v !== void 0) url.searchParams.set(k, String(v));
258
- }
259
- applyTls(ctx);
260
- const hasBody = init.body !== void 0;
261
- const controller = new AbortController();
262
- const timer = setTimeout(() => controller.abort(), init.timeoutMs ?? DEFAULT_TIMEOUT_MS);
263
- 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
- });
273
- const text = await res.text();
274
- if (!res.ok) throw new HttpError(res.status, res.statusText, text);
275
- return text ? JSON.parse(text) : void 0;
276
- } finally {
277
- clearTimeout(timer);
278
- }
279
- }
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")
523
+ // src/api/safe.ts
524
+ var ADMIN = "/api/safe/v1/admin";
525
+ function notOnSafe(operation) {
526
+ throw new InputError(
527
+ `'${operation}' is not available on bkn-safe \u2014 its admin API has no such endpoint. See docs/exec-plans/admin-bkn-safe-migration.md.`
312
528
  );
313
- return buf.toString("base64");
314
- }
315
-
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."
327
- );
328
- }
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
529
  }
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
- }
530
+ function listUsersSafe(ctx, opts = {}) {
531
+ return request(ctx, `${ADMIN}/users`, {
532
+ query: { search: opts.search || void 0, offset: opts.offset, limit: opts.limit }
358
533
  });
359
534
  }
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;
535
+ function getUserSafe(ctx, userId) {
536
+ return request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`);
372
537
  }
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
538
+ function createUserSafe(ctx, input) {
539
+ return request(ctx, `${ADMIN}/users`, {
540
+ method: "POST",
541
+ body: {
542
+ account: input.account,
543
+ password: input.password,
544
+ ...input.name ? { name: input.name } : {},
545
+ ...input.email ? { email: input.email } : {},
546
+ ...input.accountType ? { account_type: input.accountType } : {},
547
+ ...input.id ? { id: input.id } : {}
381
548
  }
382
549
  });
383
550
  }
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
551
+ function updateUserSafe(ctx, userId, input) {
552
+ return request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`, {
553
+ method: "PUT",
554
+ body: {
555
+ ...input.name !== void 0 ? { name: input.name } : {},
556
+ ...input.email !== void 0 ? { email: input.email } : {},
557
+ ...input.telephone !== void 0 ? { telephone: input.telephone } : {},
558
+ ...input.enabled !== void 0 ? { enabled: input.enabled } : {},
559
+ ...input.accountType !== void 0 ? { account_type: input.accountType } : {}
390
560
  }
391
561
  });
392
562
  }
393
- function getRole(ctx, roleId) {
394
- return request(ctx, `${AUTHZ}/roles/${encodeURIComponent(roleId)}`);
563
+ async function deleteUserSafe(ctx, userId) {
564
+ await request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`, { method: "DELETE" });
565
+ return { ok: true };
395
566
  }
396
- function getUser(ctx, userId) {
397
- return shareMgnt(ctx, "Usrm_GetUserInfo", [userId]);
567
+ async function setUserPasswordSafe(ctx, userId, password) {
568
+ await request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}/password`, {
569
+ method: "PUT",
570
+ body: { password }
571
+ });
572
+ return { ok: true };
398
573
  }
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
- }
574
+ function getUserRolesSafe(ctx, userId) {
575
+ return request(ctx, `${ADMIN}/role-bindings`, { query: { accessor_id: userId } });
423
576
  }
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
- }
577
+ async function assignRoleSafe(ctx, accessorId, roleId) {
578
+ await request(ctx, `${ADMIN}/role-bindings`, {
579
+ method: "POST",
580
+ body: { accessor_id: accessorId, role_id: roleId }
581
+ });
582
+ return { ok: true };
432
583
  }
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 }
584
+ async function removeRoleSafe(ctx, accessorId, roleId) {
585
+ await request(ctx, `${ADMIN}/role-bindings`, {
586
+ method: "DELETE",
587
+ body: { accessor_id: accessorId, role_id: roleId }
436
588
  });
589
+ return { ok: true };
437
590
  }
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"
591
+ function listDepartmentsSafe(ctx, opts = {}) {
592
+ return request(ctx, `${ADMIN}/departments`, {
593
+ query: {
594
+ search: opts.search || void 0,
595
+ parent_id: opts.parentId,
596
+ offset: opts.offset,
597
+ limit: opts.limit
598
+ }
466
599
  });
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
600
  }
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 };
601
+ function getDepartmentSafe(ctx, deptId) {
602
+ return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`);
540
603
  }
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 };
604
+ function getDepartmentMembersSafe(ctx, deptId) {
605
+ return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}/members`);
547
606
  }
548
- var EACP = "/api/eacp/v1";
549
- function listAuditLogs(ctx, opts = {}) {
550
- return request(ctx, `${EACP}/auth1/login-log`, {
607
+ function createDepartmentSafe(ctx, input) {
608
+ return request(ctx, `${ADMIN}/departments`, {
551
609
  method: "POST",
552
610
  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
611
+ name: input.name,
612
+ ...input.parentId ? { parent_id: input.parentId } : {},
613
+ ...input.type ? { type: input.type } : {},
614
+ ...input.id ? { id: input.id } : {}
558
615
  }
559
616
  });
560
617
  }
561
- function changePassword(ctx, account, oldPassword, newPassword) {
562
- return request(ctx, `${EACP}/auth1/modifypassword`, {
563
- method: "POST",
618
+ function updateDepartmentSafe(ctx, deptId, input) {
619
+ return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`, {
620
+ method: "PUT",
564
621
  body: {
565
- account,
566
- oldpwd: encryptModifyPwd(oldPassword),
567
- newpwd: encryptModifyPwd(newPassword)
622
+ ...input.name !== void 0 ? { name: input.name } : {},
623
+ ...input.parentId !== void 0 ? { parent_id: input.parentId } : {},
624
+ ...input.type !== void 0 ? { type: input.type } : {}
568
625
  }
569
626
  });
570
627
  }
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
628
+ async function deleteDepartmentSafe(ctx, deptId) {
629
+ await request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`, { method: "DELETE" });
630
+ return { ok: true };
631
+ }
632
+ async function buildDepartmentTree(ctx) {
633
+ const res = await listDepartmentsSafe(ctx, { limit: 1e3 });
634
+ const flat = Array.isArray(res) ? res : res.departments ?? [];
635
+ const byId = /* @__PURE__ */ new Map();
636
+ for (const d of flat) if (d.id) byId.set(d.id, { ...d, children: [] });
637
+ const roots = [];
638
+ for (const d of byId.values()) {
639
+ const parent = d.parent_id ? byId.get(d.parent_id) : void 0;
640
+ if (parent) parent.children?.push(d);
641
+ else roots.push(d);
642
+ }
643
+ return roots;
644
+ }
645
+ function listRolesSafe(ctx, source) {
646
+ return request(ctx, `${ADMIN}/roles`, { query: { source: source || void 0 } });
647
+ }
648
+ function getRoleSafe(ctx, roleId) {
649
+ return request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}`);
650
+ }
651
+ function roleMembersSafe(ctx, roleId) {
652
+ return request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}/members`);
653
+ }
654
+ function createRoleSafe(ctx, input) {
655
+ return request(ctx, `${ADMIN}/roles`, {
656
+ method: "POST",
657
+ body: {
658
+ name: input.name,
659
+ ...input.description ? { description: input.description } : {},
660
+ ...input.id ? { id: input.id } : {}
577
661
  }
578
662
  });
579
663
  }
580
- function modifyRoleMembers(ctx, roleId, method, members) {
581
- return request(ctx, `${AUTHZ}/role-members/${encodeURIComponent(roleId)}`, {
582
- method: "POST",
583
- body: { method, members }
664
+ function updateRoleSafe(ctx, roleId, input) {
665
+ return request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}`, {
666
+ method: "PUT",
667
+ body: {
668
+ ...input.name !== void 0 ? { name: input.name } : {},
669
+ ...input.description !== void 0 ? { description: input.description } : {}
670
+ }
584
671
  });
585
672
  }
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;
673
+ async function deleteRoleSafe(ctx, roleId) {
674
+ await request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}`, { method: "DELETE" });
675
+ return { ok: true };
602
676
  }
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 "}`));
677
+ async function setRolePermissionSafe(ctx, roleId, grant, perm) {
678
+ await request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}/permissions`, {
679
+ method: grant ? "POST" : "DELETE",
680
+ body: {
681
+ resource: { type: perm.resourceType, id: perm.resourceId },
682
+ operations: perm.operations
610
683
  }
611
684
  });
612
- return lines.join("\n");
685
+ return { ok: true };
613
686
  }
614
687
 
615
688
  // src/resources/admin.ts
689
+ var DEFAULT_NEW_USER_PASSWORD = "openbkn";
616
690
  function admin(ctx) {
617
691
  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)
692
+ // ── departments ──
693
+ orgList: (opts) => listDepartmentsSafe(ctx, { search: opts?.name, offset: opts?.offset, limit: opts?.limit }),
694
+ orgGet: (deptId) => getDepartmentSafe(ctx, deptId),
695
+ orgTree: (_role) => buildDepartmentTree(ctx),
696
+ orgMembers: (deptId, _opts) => getDepartmentMembersSafe(ctx, deptId),
697
+ orgCreate: (input) => createDepartmentSafe(ctx, { name: input.name, parentId: input.parentId }),
698
+ orgUpdate: (deptId, input) => updateDepartmentSafe(ctx, deptId, { name: input.name }),
699
+ orgDelete: (deptId) => deleteDepartmentSafe(ctx, deptId),
700
+ // ── users ──
701
+ userList: (opts) => listUsersSafe(ctx, { search: opts?.name, offset: opts?.offset, limit: opts?.limit }),
702
+ userGet: (userId) => getUserSafe(ctx, userId),
703
+ userRoles: async (userId) => {
704
+ const [bound, all] = await Promise.all([getUserRolesSafe(ctx, userId), listRolesSafe(ctx)]);
705
+ const ids = bound.role_ids ?? [];
706
+ const nameById = new Map(
707
+ (all.roles ?? []).map((r) => [
708
+ r.id,
709
+ r.name
710
+ ])
711
+ );
712
+ return { roles: ids.map((id) => ({ name: nameById.get(id) ?? id, id })) };
713
+ },
714
+ userCreate: (input) => createUserSafe(ctx, {
715
+ account: input.loginName,
716
+ password: DEFAULT_NEW_USER_PASSWORD,
717
+ name: input.displayName,
718
+ email: input.email
719
+ }),
720
+ userUpdate: (userId, input) => updateUserSafe(ctx, userId, {
721
+ name: input.displayName,
722
+ email: input.email,
723
+ telephone: input.telNumber
724
+ }),
725
+ userDelete: (userId) => deleteUserSafe(ctx, userId),
726
+ userResetPassword: (userId, newPassword) => setUserPasswordSafe(ctx, userId, newPassword),
727
+ // ── roles ──
728
+ roleList: (_opts) => listRolesSafe(ctx),
729
+ roleGet: (roleId) => getRoleSafe(ctx, roleId),
730
+ roleMembers: async (roleId, _opts) => {
731
+ const [mem, users] = await Promise.all([
732
+ roleMembersSafe(ctx, roleId),
733
+ listUsersSafe(ctx, { limit: 500 })
734
+ ]);
735
+ const ids = mem.accessor_ids ?? [];
736
+ const nameById = new Map(
737
+ (users.users ?? []).map((u) => [u.id, u.account ?? u.name ?? u.id])
738
+ );
739
+ return { members: ids.map((id) => ({ account: nameById.get(id) ?? id, id })) };
740
+ },
741
+ addRoleMember: (roleId, id, _type = "user") => assignRoleSafe(ctx, id, roleId),
742
+ removeRoleMember: (roleId, id, _type = "user") => removeRoleSafe(ctx, id, roleId),
743
+ roleCreate: (name, description) => createRoleSafe(ctx, { name, description }),
744
+ roleUpdate: (roleId, input) => updateRoleSafe(ctx, roleId, input),
745
+ roleDelete: (roleId) => deleteRoleSafe(ctx, roleId),
746
+ rolePermission: (roleId, grant, resourceType, resourceId, operations) => setRolePermissionSafe(ctx, roleId, grant, { resourceType, resourceId, operations }),
747
+ auditList: (_opts) => notOnSafe("audit list")
638
748
  };
639
749
  }
640
750
 
751
+ // src/api/auth-fetch.ts
752
+ async function authFetch(ctx, send) {
753
+ let res = await send();
754
+ if (res.status === 401 && ctx.refresh && await tryRefresh(ctx)) {
755
+ res = await send();
756
+ }
757
+ return res;
758
+ }
759
+
641
760
  // src/api/agent-chat.ts
642
761
  var FACTORY = "/api/agent-factory";
643
762
  async function fetchAgentInfo(ctx, agentId, version = "v0") {
@@ -706,16 +825,19 @@ async function sendChat(ctx, info, query, opts = {}) {
706
825
  stream: Boolean(opts.stream)
707
826
  };
708
827
  if (opts.conversationId) body.conversation_id = opts.conversationId;
709
- const res = await fetch(`${ctx.baseUrl}${FACTORY}/v1/app/${info.key}/chat/completion`, {
710
- method: "POST",
711
- headers: {
712
- ...buildHeaders(ctx),
713
- "content-type": "application/json",
714
- accept: opts.stream ? "text/event-stream" : "application/json",
715
- "x-language": "zh-CN"
716
- },
717
- body: JSON.stringify(body)
718
- });
828
+ const res = await authFetch(
829
+ ctx,
830
+ () => fetch(`${ctx.baseUrl}${FACTORY}/v1/app/${info.key}/chat/completion`, {
831
+ method: "POST",
832
+ headers: {
833
+ ...buildHeaders(ctx),
834
+ "content-type": "application/json",
835
+ accept: opts.stream ? "text/event-stream" : "application/json",
836
+ "x-language": "zh-CN"
837
+ },
838
+ body: JSON.stringify(body)
839
+ })
840
+ );
719
841
  if (!res.ok) throw new HttpError(res.status, res.statusText, await res.text());
720
842
  const contentType = res.headers.get("content-type") ?? "";
721
843
  if (opts.stream && contentType.includes("text/event-stream")) {
@@ -907,6 +1029,9 @@ function nextId() {
907
1029
  function mcpUrl(ctx) {
908
1030
  return `${ctx.baseUrl}${MCP_PATH}`;
909
1031
  }
1032
+ function mcpInfo(ctx) {
1033
+ return request(ctx, `${MCP_PATH}/info`);
1034
+ }
910
1035
  function headers(ctx, knId, sessionId) {
911
1036
  const h = {
912
1037
  "content-type": "application/json",
@@ -929,11 +1054,14 @@ function parseBody(text) {
929
1054
  }
930
1055
  async function post(ctx, knId, sessionId, body) {
931
1056
  applyTls(ctx);
932
- const res = await fetch(mcpUrl(ctx), {
933
- method: "POST",
934
- headers: headers(ctx, knId, sessionId),
935
- body: JSON.stringify(body)
936
- });
1057
+ const res = await authFetch(
1058
+ ctx,
1059
+ () => fetch(mcpUrl(ctx), {
1060
+ method: "POST",
1061
+ headers: headers(ctx, knId, sessionId),
1062
+ body: JSON.stringify(body)
1063
+ })
1064
+ );
937
1065
  const text = await res.text();
938
1066
  if (!res.ok) throw new HttpError(res.status, res.statusText, text);
939
1067
  return { res, text };
@@ -1044,8 +1172,12 @@ function context(ctx) {
1044
1172
  searchSchema: (knId, query, opts) => searchSchema(ctx, knId, query, opts),
1045
1173
  queryObjectInstance: (knId, args) => queryObjectInstance(ctx, knId, args),
1046
1174
  findSkills: (knId, objectTypeId, topK) => findSkills(ctx, knId, objectTypeId, topK),
1175
+ info: () => mcpInfo(ctx),
1047
1176
  tools: (knId) => listTools(ctx, knId),
1048
1177
  toolCall: (knId, name, args) => callTool(ctx, knId, name, args),
1178
+ // Generic MCP method passthrough — covers methods not yet wrapped, so the
1179
+ // surface doesn't have to grow every time the server adds one.
1180
+ callMethod: (knId, method, params) => callMethod(ctx, knId, method, params),
1049
1181
  queryInstanceSubgraph: (knId, args) => queryInstanceSubgraph(ctx, knId, args),
1050
1182
  logicProperties: (knId, args) => getLogicProperties(ctx, knId, args),
1051
1183
  actionInfo: (knId, args) => getActionInfo(ctx, knId, args),
@@ -2611,13 +2743,16 @@ async function uploadBkn(ctx, tarBuffer, opts = {}) {
2611
2743
  applyTls(ctx);
2612
2744
  const url = new URL(`${ctx.baseUrl}${BKNS}`);
2613
2745
  url.searchParams.set("branch", opts.branch ?? "main");
2614
- const form = new FormData();
2615
- form.append(
2746
+ const form2 = new FormData();
2747
+ form2.append(
2616
2748
  "file",
2617
2749
  new Blob([new Uint8Array(tarBuffer)], { type: "application/octet-stream" }),
2618
2750
  "bkn.tar"
2619
2751
  );
2620
- const res = await fetch(url, { method: "POST", headers: buildHeaders(ctx), body: form });
2752
+ const res = await authFetch(
2753
+ ctx,
2754
+ () => fetch(url, { method: "POST", headers: buildHeaders(ctx), body: form2 })
2755
+ );
2621
2756
  const text = await res.text();
2622
2757
  if (!res.ok) throw new HttpError(res.status, res.statusText, text);
2623
2758
  return text ? JSON.parse(text) : void 0;
@@ -2626,7 +2761,7 @@ async function downloadBkn(ctx, knId, opts = {}) {
2626
2761
  applyTls(ctx);
2627
2762
  const url = new URL(`${ctx.baseUrl}${BKNS}/${encodeURIComponent(knId)}`);
2628
2763
  url.searchParams.set("branch", opts.branch ?? "main");
2629
- const res = await fetch(url, { method: "GET", headers: buildHeaders(ctx) });
2764
+ const res = await authFetch(ctx, () => fetch(url, { method: "GET", headers: buildHeaders(ctx) }));
2630
2765
  if (!res.ok) throw new HttpError(res.status, res.statusText, await res.text());
2631
2766
  return Buffer.from(await res.arrayBuffer());
2632
2767
  }
@@ -2783,8 +2918,8 @@ function discoverCatalog(ctx, id, wait = true) {
2783
2918
  });
2784
2919
  }
2785
2920
  function listCatalogResources(ctx, id, category) {
2786
- return request(ctx, `${VEGA_BASE}/catalogs/${encodeURIComponent(id)}/resources`, {
2787
- query: { category: category || void 0 }
2921
+ return request(ctx, `${VEGA_BASE}/resources`, {
2922
+ query: { catalog_id: id, category: category || void 0 }
2788
2923
  });
2789
2924
  }
2790
2925
  function catalogHealthStatus(ctx, ids) {
@@ -2973,9 +3108,9 @@ function extractTarToDirectory(tarBuffer, dirPath) {
2973
3108
  }
2974
3109
 
2975
3110
  // 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";
3111
+ import { readdirSync as readdirSync3, statSync as statSync2 } from "fs";
3112
+ import { readFile } from "fs/promises";
3113
+ import { basename, dirname, resolve as resolve2 } from "path";
2979
3114
  import { parse } from "csv-parse/sync";
2980
3115
  async function parseCsvFile(filePath) {
2981
3116
  let content = await readFile(filePath, "utf8");
@@ -3039,13 +3174,25 @@ function buildImportDag(opts) {
3039
3174
  ]
3040
3175
  };
3041
3176
  }
3177
+ function globOne(pattern) {
3178
+ const dir = dirname(pattern);
3179
+ const re = new RegExp(
3180
+ `^${basename(pattern).replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`
3181
+ );
3182
+ let entries;
3183
+ try {
3184
+ entries = readdirSync3(resolve2(dir));
3185
+ } catch {
3186
+ return [];
3187
+ }
3188
+ return entries.filter((f) => re.test(f)).map((f) => resolve2(dir, f));
3189
+ }
3042
3190
  async function resolveFiles(pattern) {
3043
3191
  const out = [];
3044
3192
  for (const part of pattern.split(",").map((p) => p.trim()).filter(Boolean)) {
3045
3193
  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));
3194
+ for (const p of globOne(part)) {
3195
+ if (/\.csv$/i.test(p)) out.push(p);
3049
3196
  }
3050
3197
  } else {
3051
3198
  statSync2(resolve2(part));
@@ -3524,15 +3671,18 @@ function deltaContent(chunk) {
3524
3671
  }
3525
3672
  async function chatCompletionsStream(ctx, model, messages, onDelta) {
3526
3673
  applyTls(ctx);
3527
- const res = await fetch(`${ctx.baseUrl}${API}/chat/completions`, {
3528
- method: "POST",
3529
- headers: {
3530
- ...buildHeaders(ctx),
3531
- "content-type": "application/json",
3532
- accept: "text/event-stream"
3533
- },
3534
- body: JSON.stringify({ model, messages, stream: true })
3535
- });
3674
+ const res = await authFetch(
3675
+ ctx,
3676
+ () => fetch(`${ctx.baseUrl}${API}/chat/completions`, {
3677
+ method: "POST",
3678
+ headers: {
3679
+ ...buildHeaders(ctx),
3680
+ "content-type": "application/json",
3681
+ accept: "text/event-stream"
3682
+ },
3683
+ body: JSON.stringify({ model, messages, stream: true })
3684
+ })
3685
+ );
3536
3686
  if (!res.ok) throw new HttpError(res.status, res.statusText, await res.text());
3537
3687
  const reader = res.body?.getReader();
3538
3688
  if (!reader) throw new Error("No response body for stream");
@@ -3628,45 +3778,54 @@ function resources(ctx) {
3628
3778
 
3629
3779
  // src/resources/skills.ts
3630
3780
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
3631
- import { basename as basename2, dirname as dirname2, resolve as resolve5 } from "path";
3781
+ import { basename as basename2, dirname as dirname3, resolve as resolve5 } from "path";
3632
3782
 
3633
3783
  // src/api/skills.ts
3634
3784
  var BASE5 = "/api/agent-operator-integration/v1";
3635
3785
  async function registerSkillZip(ctx, bytes, opts = {}) {
3636
3786
  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));
3642
- const res = await fetch(`${ctx.baseUrl}${BASE5}/skills`, {
3643
- method: "POST",
3644
- headers: buildHeaders(ctx),
3645
- body: form
3646
- });
3787
+ const form2 = new FormData();
3788
+ form2.set("file_type", "zip");
3789
+ form2.set("file", new Blob([bytes]), opts.filename ?? "skill.zip");
3790
+ if (opts.source) form2.set("source", opts.source);
3791
+ if (opts.extendInfo) form2.set("extend_info", JSON.stringify(opts.extendInfo));
3792
+ const res = await authFetch(
3793
+ ctx,
3794
+ () => fetch(`${ctx.baseUrl}${BASE5}/skills`, {
3795
+ method: "POST",
3796
+ headers: buildHeaders(ctx),
3797
+ body: form2
3798
+ })
3799
+ );
3647
3800
  const text = await res.text();
3648
3801
  if (!res.ok) throw new HttpError(res.status, res.statusText, text);
3649
3802
  return text ? JSON.parse(text) : void 0;
3650
3803
  }
3651
3804
  async function updateSkillPackageZip(ctx, skillId, bytes, filename = "skill.zip") {
3652
3805
  applyTls(ctx);
3653
- const form = new FormData();
3654
- form.set("file_type", "zip");
3655
- form.set("file", new Blob([bytes]), filename);
3656
- const res = await fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/package`, {
3657
- method: "PUT",
3658
- headers: buildHeaders(ctx),
3659
- body: form
3660
- });
3806
+ const form2 = new FormData();
3807
+ form2.set("file_type", "zip");
3808
+ form2.set("file", new Blob([bytes]), filename);
3809
+ const res = await authFetch(
3810
+ ctx,
3811
+ () => fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/package`, {
3812
+ method: "PUT",
3813
+ headers: buildHeaders(ctx),
3814
+ body: form2
3815
+ })
3816
+ );
3661
3817
  const text = await res.text();
3662
3818
  if (!res.ok) throw new HttpError(res.status, res.statusText, text);
3663
3819
  return text ? JSON.parse(text) : void 0;
3664
3820
  }
3665
3821
  async function downloadSkill(ctx, skillId) {
3666
3822
  applyTls(ctx);
3667
- const res = await fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/download`, {
3668
- headers: buildHeaders(ctx)
3669
- });
3823
+ const res = await authFetch(
3824
+ ctx,
3825
+ () => fetch(`${ctx.baseUrl}${BASE5}/skills/${encodeURIComponent(skillId)}/download`, {
3826
+ headers: buildHeaders(ctx)
3827
+ })
3828
+ );
3670
3829
  if (!res.ok) throw new HttpError(res.status, res.statusText, await res.text());
3671
3830
  return new Uint8Array(await res.arrayBuffer());
3672
3831
  }
@@ -3730,11 +3889,11 @@ function setSkillStatus(ctx, skillId, status2) {
3730
3889
  }
3731
3890
 
3732
3891
  // 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";
3892
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync as readdirSync4, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
3893
+ import { dirname as dirname2, join as join3, relative, resolve as resolve4, sep } from "path";
3735
3894
  import JSZip from "jszip";
3736
3895
  function walk(dir, base, files) {
3737
- for (const entry of readdirSync3(dir)) {
3896
+ for (const entry of readdirSync4(dir)) {
3738
3897
  const full = join3(dir, entry);
3739
3898
  const st = statSync3(full);
3740
3899
  if (st.isDirectory()) walk(full, base, files);
@@ -3762,7 +3921,7 @@ async function unzipToDirectory(bytes, dir) {
3762
3921
  for (const entry of entries) {
3763
3922
  if (entry.dir) continue;
3764
3923
  const out = join3(abs, entry.name);
3765
- mkdirSync3(dirname(out), { recursive: true });
3924
+ mkdirSync3(dirname2(out), { recursive: true });
3766
3925
  writeFileSync2(out, await entry.async("nodebuffer"));
3767
3926
  written.push(out);
3768
3927
  }
@@ -3795,7 +3954,7 @@ function skills(ctx) {
3795
3954
  download: async (skillId, outPath) => {
3796
3955
  const bytes = await downloadSkill(ctx, skillId);
3797
3956
  const dest = resolve5(outPath ?? `${skillId}.zip`);
3798
- mkdirSync4(dirname2(dest), { recursive: true });
3957
+ mkdirSync4(dirname3(dest), { recursive: true });
3799
3958
  writeFileSync3(dest, bytes);
3800
3959
  return { skillId, path: dest, bytes: bytes.length };
3801
3960
  },
@@ -3811,7 +3970,7 @@ function skills(ctx) {
3811
3970
 
3812
3971
  // src/resources/toolboxes.ts
3813
3972
  import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
3814
- import { dirname as dirname3, resolve as resolve6 } from "path";
3973
+ import { dirname as dirname4, resolve as resolve6 } from "path";
3815
3974
 
3816
3975
  // src/api/toolboxes.ts
3817
3976
  import { readFile as readFile2 } from "fs/promises";
@@ -3820,9 +3979,11 @@ var PATH = "/api/agent-operator-integration/v1/tool-box";
3820
3979
  var IMPEX = "/api/agent-operator-integration/v1/impex";
3821
3980
  async function exportConfig(ctx, id, type = "toolbox") {
3822
3981
  applyTls(ctx);
3823
- const res = await fetch(
3824
- `${ctx.baseUrl}${IMPEX}/export/${encodeURIComponent(type)}/${encodeURIComponent(id)}`,
3825
- { headers: buildHeaders(ctx) }
3982
+ const res = await authFetch(
3983
+ ctx,
3984
+ () => fetch(`${ctx.baseUrl}${IMPEX}/export/${encodeURIComponent(type)}/${encodeURIComponent(id)}`, {
3985
+ headers: buildHeaders(ctx)
3986
+ })
3826
3987
  );
3827
3988
  const buf = new Uint8Array(await res.arrayBuffer());
3828
3989
  if (!res.ok) throw new HttpError(res.status, res.statusText, new TextDecoder().decode(buf));
@@ -3831,13 +3992,16 @@ async function exportConfig(ctx, id, type = "toolbox") {
3831
3992
  async function importConfig(ctx, filePath, type = "toolbox") {
3832
3993
  applyTls(ctx);
3833
3994
  const buf = await readFile2(filePath);
3834
- const form = new FormData();
3835
- form.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
3836
- const res = await fetch(`${ctx.baseUrl}${IMPEX}/import/${encodeURIComponent(type)}`, {
3837
- method: "POST",
3838
- headers: buildHeaders(ctx),
3839
- body: form
3840
- });
3995
+ const form2 = new FormData();
3996
+ form2.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
3997
+ const res = await authFetch(
3998
+ ctx,
3999
+ () => fetch(`${ctx.baseUrl}${IMPEX}/import/${encodeURIComponent(type)}`, {
4000
+ method: "POST",
4001
+ headers: buildHeaders(ctx),
4002
+ body: form2
4003
+ })
4004
+ );
3841
4005
  const text = await res.text();
3842
4006
  if (!res.ok) throw new HttpError(res.status, res.statusText, text);
3843
4007
  return text ? JSON.parse(text) : text;
@@ -3845,14 +4009,17 @@ async function importConfig(ctx, filePath, type = "toolbox") {
3845
4009
  async function uploadTool(ctx, boxId, filePath, metadataType = "openapi") {
3846
4010
  applyTls(ctx);
3847
4011
  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));
3851
- const res = await fetch(`${ctx.baseUrl}${PATH}/${encodeURIComponent(boxId)}/tool`, {
3852
- method: "POST",
3853
- headers: buildHeaders(ctx),
3854
- body: form
3855
- });
4012
+ const form2 = new FormData();
4013
+ form2.append("metadata_type", metadataType);
4014
+ form2.append("data", new Blob([new Uint8Array(buf)]), basename3(filePath));
4015
+ const res = await authFetch(
4016
+ ctx,
4017
+ () => fetch(`${ctx.baseUrl}${PATH}/${encodeURIComponent(boxId)}/tool`, {
4018
+ method: "POST",
4019
+ headers: buildHeaders(ctx),
4020
+ body: form2
4021
+ })
4022
+ );
3856
4023
  const text = await res.text();
3857
4024
  if (!res.ok) throw new HttpError(res.status, res.statusText, text);
3858
4025
  return text ? JSON.parse(text) : text;
@@ -3937,7 +4104,7 @@ function toolboxes(ctx) {
3937
4104
  export: async (id, outPath, type) => {
3938
4105
  const bytes = await exportConfig(ctx, id, type);
3939
4106
  const dest = resolve6(outPath);
3940
- mkdirSync5(dirname3(dest), { recursive: true });
4107
+ mkdirSync5(dirname4(dest), { recursive: true });
3941
4108
  writeFileSync4(dest, bytes);
3942
4109
  return { id, path: dest, bytes: bytes.length };
3943
4110
  },
@@ -4017,7 +4184,7 @@ async function getSpansByConversation(ctx, conversationId, opts = {}) {
4017
4184
  }
4018
4185
 
4019
4186
  // src/trace-ai/claude-judge.ts
4020
- import { spawn, spawnSync as spawnSync2 } from "child_process";
4187
+ import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
4021
4188
  var ClaudeJudgeError = class extends Error {
4022
4189
  constructor(message, reason) {
4023
4190
  super(message);
@@ -4063,7 +4230,7 @@ async function judgeJson(prompt, opts = {}) {
4063
4230
  const timeoutMs = opts.timeoutMs ?? 12e4;
4064
4231
  const args = ["-p", "--output-format=json", "--dangerously-skip-permissions"];
4065
4232
  const stdout = await new Promise((resolve7, reject) => {
4066
- const child = spawn(binary, args, { stdio: ["pipe", "pipe", "pipe"] });
4233
+ const child = spawn2(binary, args, { stdio: ["pipe", "pipe", "pipe"] });
4067
4234
  let out = "";
4068
4235
  let timedOut = false;
4069
4236
  const killer = setTimeout(() => {
@@ -4825,16 +4992,16 @@ async function rawCall(ctx, path, opts = {}) {
4825
4992
  if (!extra["content-type"]) extra["content-type"] = "application/json";
4826
4993
  }
4827
4994
  const method = opts.method ?? (body !== void 0 ? "POST" : "GET");
4828
- const headers2 = buildHeaders(
4829
- { ...ctx, businessDomain: opts.businessDomain ?? ctx.businessDomain },
4830
- extra
4831
- );
4995
+ const headersFor = () => buildHeaders({ ...ctx, businessDomain: opts.businessDomain ?? ctx.businessDomain }, extra);
4832
4996
  if (opts.verbose) process.stderr.write(`> ${method} ${url}
4833
4997
  `);
4834
4998
  const controller = new AbortController();
4835
4999
  const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? 3e4);
4836
5000
  try {
4837
- const res = await fetch(url, { method, headers: headers2, body, signal: controller.signal });
5001
+ const res = await authFetch(
5002
+ ctx,
5003
+ () => fetch(url, { method, headers: headersFor(), body, signal: controller.signal })
5004
+ );
4838
5005
  return { status: res.status, statusText: res.statusText, body: await res.text() };
4839
5006
  } finally {
4840
5007
  clearTimeout(timer);
@@ -4864,8 +5031,10 @@ function createClient(opts = {}) {
4864
5031
  // src/resources/auth.ts
4865
5032
  var auth_exports = {};
4866
5033
  __export(auth_exports, {
5034
+ attachNoAuth: () => attachNoAuth,
4867
5035
  attachToken: () => attachToken,
4868
5036
  currentToken: () => currentToken,
5037
+ currentTokenFresh: () => currentTokenFresh,
4869
5038
  deletePlatform: () => deletePlatform,
4870
5039
  exportCreds: () => exportCreds,
4871
5040
  hostOf: () => hostOf,
@@ -4901,12 +5070,19 @@ function attachToken(baseUrl, accessToken, opts = {}) {
4901
5070
  refreshToken: opts.refreshToken,
4902
5071
  idToken: opts.idToken,
4903
5072
  tlsInsecure: opts.insecure,
4904
- username: decodeJwt(opts.idToken ?? accessToken)?.preferred_username
5073
+ // Prefer the account the user typed (-u); device tokens carry no username.
5074
+ username: opts.username ?? decodeJwt(opts.idToken ?? accessToken)?.preferred_username
4905
5075
  };
4906
5076
  const userId = writeToken(url, token);
4907
5077
  setActivePlatform(url);
4908
5078
  return { baseUrl: url, userId, username: usernameOf(token) };
4909
5079
  }
5080
+ function attachNoAuth(baseUrl, opts = {}) {
5081
+ const url = normalize(baseUrl);
5082
+ writeToken(url, { baseUrl: url, accessToken: "", noAuth: true, tlsInsecure: opts.insecure });
5083
+ setActivePlatform(url);
5084
+ return { baseUrl: url, noAuth: true };
5085
+ }
4910
5086
  function status() {
4911
5087
  const baseUrl = activePlatform();
4912
5088
  if (!baseUrl) return { hasToken: false };
@@ -4925,6 +5101,30 @@ function currentToken() {
4925
5101
  if (!token) throw new InputError("Not logged in. Run `openbkn auth login <url> --token <t>`.");
4926
5102
  return token.accessToken;
4927
5103
  }
5104
+ async function currentTokenFresh() {
5105
+ const baseUrl = activePlatform();
5106
+ const token = baseUrl ? readToken(baseUrl) : void 0;
5107
+ if (!baseUrl || !token) {
5108
+ throw new InputError("Not logged in. Run `openbkn auth login <url> --token <t>`.");
5109
+ }
5110
+ const claims = decodeJwt(token.accessToken);
5111
+ const decodable = claims?.exp !== void 0;
5112
+ const needsRefresh = decodable ? isExpired(claims) : true;
5113
+ if (token.refreshToken && needsRefresh) {
5114
+ try {
5115
+ const t = await refreshAccessToken(baseUrl, token.refreshToken);
5116
+ writeToken(baseUrl, {
5117
+ ...token,
5118
+ accessToken: t.accessToken,
5119
+ refreshToken: t.refreshToken ?? token.refreshToken,
5120
+ idToken: t.idToken ?? token.idToken
5121
+ });
5122
+ return t.accessToken;
5123
+ } catch {
5124
+ }
5125
+ }
5126
+ return token.accessToken;
5127
+ }
4928
5128
  function whoami() {
4929
5129
  const baseUrl = activePlatform();
4930
5130
  const token = baseUrl ? readToken(baseUrl) : void 0;
@@ -4934,7 +5134,12 @@ function whoami() {
4934
5134
  "No decodable identity (token is opaque and no id_token saved). Use `auth status`."
4935
5135
  );
4936
5136
  }
4937
- return claims;
5137
+ return {
5138
+ ...claims,
5139
+ baseUrl: baseUrl ?? void 0,
5140
+ userId: baseUrl ? activeUserId(baseUrl) : void 0,
5141
+ username: usernameOf(token)
5142
+ };
4938
5143
  }
4939
5144
  function listPlatforms2() {
4940
5145
  const current = activePlatform();
@@ -4961,14 +5166,16 @@ function logout() {
4961
5166
  function deletePlatform(baseUrl, userId) {
4962
5167
  return deleteToken(normalize(baseUrl), userId);
4963
5168
  }
4964
- function switchUser(baseUrl, userId) {
5169
+ function switchUser(baseUrl, userOrName) {
4965
5170
  const url = normalize(baseUrl);
4966
- if (!readToken(url, userId)) {
4967
- throw new InputError(`No saved token for user '${userId}' on ${url}.`);
5171
+ const users = usersOf(url);
5172
+ const match = users.find((u) => u.userId === userOrName) ?? users.find((u) => (u.username ?? u.displayName) === userOrName);
5173
+ if (!match) {
5174
+ throw new InputError(`No saved user '${userOrName}' on ${url}. Try \`auth users ${url}\`.`);
4968
5175
  }
4969
- setActiveUser(url, userId);
5176
+ setActiveUser(url, match.userId);
4970
5177
  setActivePlatform(url);
4971
- return { baseUrl: url, userId };
5178
+ return { baseUrl: url, userId: match.userId, username: match.username ?? match.displayName };
4972
5179
  }
4973
5180
  function usersOf(baseUrl) {
4974
5181
  const url = normalize(baseUrl);
@@ -4988,22 +5195,27 @@ function exportCreds() {
4988
5195
  }
4989
5196
 
4990
5197
  export {
4991
- rawCall,
4992
- DEFAULT_BUSINESS_DOMAIN,
4993
- DEFAULT_LIST_LIMIT,
4994
- DEFAULT_QUERY_LIMIT,
4995
5198
  HttpError,
4996
5199
  InputError,
4997
5200
  toExitCode,
4998
5201
  formatError,
5202
+ isHeadless,
5203
+ openBrowser,
5204
+ fetchAuthStatus,
5205
+ deviceLogin,
5206
+ credentialDeviceLogin,
5207
+ request,
5208
+ rawCall,
5209
+ DEFAULT_BUSINESS_DOMAIN,
5210
+ DEFAULT_LIST_LIMIT,
5211
+ DEFAULT_QUERY_LIMIT,
5212
+ decodeJwt,
4999
5213
  activePlatform,
5000
5214
  setActivePlatform,
5001
5215
  readPlatformConfig,
5002
5216
  writePlatformConfig,
5003
5217
  resolveContext,
5004
- request,
5005
- changePassword,
5006
- renderOrgTree,
5218
+ getUserSafe,
5007
5219
  admin,
5008
5220
  agents,
5009
5221
  context,
@@ -5020,16 +5232,17 @@ export {
5020
5232
  vega,
5021
5233
  createClient,
5022
5234
  attachToken,
5235
+ attachNoAuth,
5023
5236
  status,
5024
5237
  currentToken,
5238
+ currentTokenFresh,
5025
5239
  whoami,
5026
5240
  listPlatforms2 as listPlatforms,
5027
5241
  use,
5028
5242
  logout,
5029
5243
  deletePlatform,
5030
5244
  switchUser,
5031
- usersOf,
5032
5245
  exportCreds,
5033
5246
  auth_exports
5034
5247
  };
5035
- //# sourceMappingURL=chunk-DXA44XWY.js.map
5248
+ //# sourceMappingURL=chunk-GNL6Z5VF.js.map