@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.
- package/dist/{chunk-DXA44XWY.js → chunk-GNL6Z5VF.js} +689 -476
- package/dist/chunk-GNL6Z5VF.js.map +1 -0
- package/dist/cli.js +495 -382
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +101 -36
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-DXA44XWY.js.map +0 -1
|
@@ -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
|
-
|
|
42
|
-
|
|
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
|
|
224
|
-
|
|
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
|
|
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/
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
361
|
-
|
|
362
|
-
let offset = 0;
|
|
363
|
-
for (; ; ) {
|
|
364
|
-
const data = await listDepartments(ctx, { role, offset, limit: pageSize });
|
|
365
|
-
const entries = data.entries ?? [];
|
|
366
|
-
out.push(...entries);
|
|
367
|
-
if (entries.length < pageSize) break;
|
|
368
|
-
if (data.total_count !== void 0 && out.length >= data.total_count) break;
|
|
369
|
-
offset += pageSize;
|
|
370
|
-
}
|
|
371
|
-
return out;
|
|
535
|
+
function getUserSafe(ctx, userId) {
|
|
536
|
+
return request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`);
|
|
372
537
|
}
|
|
373
|
-
function
|
|
374
|
-
return request(ctx, `${
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
|
385
|
-
return request(ctx, `${
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
|
394
|
-
|
|
563
|
+
async function deleteUserSafe(ctx, userId) {
|
|
564
|
+
await request(ctx, `${ADMIN}/users/${encodeURIComponent(userId)}`, { method: "DELETE" });
|
|
565
|
+
return { ok: true };
|
|
395
566
|
}
|
|
396
|
-
function
|
|
397
|
-
|
|
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
|
-
|
|
400
|
-
|
|
401
|
-
return await request(ctx, `${AUTHZ}/accessor_roles`, {
|
|
402
|
-
query: { accessor_id: userId, accessor_type: "user" }
|
|
403
|
-
});
|
|
404
|
-
} catch (e) {
|
|
405
|
-
if (!(e instanceof HttpError) || e.status !== 404) throw e;
|
|
406
|
-
const rolesPage = await listRoles(ctx, { offset: 0, limit: 200 });
|
|
407
|
-
const roles = rolesPage.entries ?? [];
|
|
408
|
-
const matched = [];
|
|
409
|
-
await Promise.all(
|
|
410
|
-
roles.map(async (role) => {
|
|
411
|
-
const members = await listRoleMembers(ctx, role.id, { offset: 0, limit: 500 });
|
|
412
|
-
if ((members.entries ?? []).some((m) => m.id === userId && (!m.type || m.type === "user"))) {
|
|
413
|
-
matched.push(role);
|
|
414
|
-
}
|
|
415
|
-
})
|
|
416
|
-
);
|
|
417
|
-
return {
|
|
418
|
-
entries: matched,
|
|
419
|
-
total_count: matched.length,
|
|
420
|
-
route: "fallback:list-roles+role-members"
|
|
421
|
-
};
|
|
422
|
-
}
|
|
574
|
+
function getUserRolesSafe(ctx, userId) {
|
|
575
|
+
return request(ctx, `${ADMIN}/role-bindings`, { query: { accessor_id: userId } });
|
|
423
576
|
}
|
|
424
|
-
async function
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
|
434
|
-
|
|
435
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
email: input.email ?? "",
|
|
447
|
-
ossId: ""
|
|
448
|
-
};
|
|
449
|
-
const id = await shareMgnt(ctx, "Usrm_AddDepartment", [{ ncTAddDepartParam }]);
|
|
450
|
-
return { id };
|
|
451
|
-
}
|
|
452
|
-
async function updateDepartment(ctx, deptId, input) {
|
|
453
|
-
const p = { departId: deptId };
|
|
454
|
-
if (input.name !== void 0) p.departName = input.name;
|
|
455
|
-
if (input.managerID !== void 0) p.managerID = input.managerID;
|
|
456
|
-
if (input.code !== void 0) p.code = input.code;
|
|
457
|
-
if (input.remark !== void 0) p.remark = input.remark;
|
|
458
|
-
if (input.status !== void 0) p.status = input.status;
|
|
459
|
-
if (input.email !== void 0) p.email = input.email;
|
|
460
|
-
await shareMgnt(ctx, "Usrm_EditDepartment", [{ ncTEditDepartParam: p }]);
|
|
461
|
-
return { id: deptId, updated: true };
|
|
462
|
-
}
|
|
463
|
-
async function deleteDepartment(ctx, deptId) {
|
|
464
|
-
await request(ctx, `${UM}/management/departments/${encodeURIComponent(deptId)}`, {
|
|
465
|
-
method: "DELETE"
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
await request(ctx, `${UM}/users/${encodeURIComponent(userId)}`, { method: "DELETE" });
|
|
535
|
-
} catch (e) {
|
|
536
|
-
if (!(e instanceof HttpError) || e.status !== 404) throw e;
|
|
537
|
-
await shareMgnt(ctx, "Usrm_DelUser", [userId]);
|
|
538
|
-
}
|
|
539
|
-
return { id: userId, deleted: true };
|
|
601
|
+
function getDepartmentSafe(ctx, deptId) {
|
|
602
|
+
return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`);
|
|
540
603
|
}
|
|
541
|
-
|
|
542
|
-
|
|
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
|
-
|
|
549
|
-
|
|
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
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
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
|
|
562
|
-
return request(ctx, `${
|
|
563
|
-
method: "
|
|
618
|
+
function updateDepartmentSafe(ctx, deptId, input) {
|
|
619
|
+
return request(ctx, `${ADMIN}/departments/${encodeURIComponent(deptId)}`, {
|
|
620
|
+
method: "PUT",
|
|
564
621
|
body: {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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
|
|
581
|
-
return request(ctx, `${
|
|
582
|
-
method: "
|
|
583
|
-
body: {
|
|
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
|
-
|
|
588
|
-
|
|
589
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
590
|
-
for (const e of entries) nodes.set(e.id, { id: e.id, name: e.name ?? e.id, children: [] });
|
|
591
|
-
const roots = [];
|
|
592
|
-
for (const e of entries) {
|
|
593
|
-
const node = nodes.get(e.id);
|
|
594
|
-
if (!node) continue;
|
|
595
|
-
const deps = e.parent_deps;
|
|
596
|
-
const parentId = deps && deps.length > 0 ? deps[deps.length - 1]?.id : void 0;
|
|
597
|
-
const parent = parentId ? nodes.get(parentId) : void 0;
|
|
598
|
-
if (parent) parent.children.push(node);
|
|
599
|
-
else roots.push(node);
|
|
600
|
-
}
|
|
601
|
-
return roots;
|
|
673
|
+
async function deleteRoleSafe(ctx, roleId) {
|
|
674
|
+
await request(ctx, `${ADMIN}/roles/${encodeURIComponent(roleId)}`, { method: "DELETE" });
|
|
675
|
+
return { ok: true };
|
|
602
676
|
}
|
|
603
|
-
function
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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
|
|
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
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
orgTree:
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
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
|
|
2615
|
-
|
|
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
|
|
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}/
|
|
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 {
|
|
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
|
|
3047
|
-
|
|
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
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
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
|
|
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
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
if (opts.source)
|
|
3641
|
-
if (opts.extendInfo)
|
|
3642
|
-
const res = await
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
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
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
const res = await
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
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
|
|
3668
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
3824
|
-
|
|
3825
|
-
|
|
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
|
|
3835
|
-
|
|
3836
|
-
const res = await
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
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
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
const res = await
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|
|
5169
|
+
function switchUser(baseUrl, userOrName) {
|
|
4965
5170
|
const url = normalize(baseUrl);
|
|
4966
|
-
|
|
4967
|
-
|
|
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
|
-
|
|
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-
|
|
5248
|
+
//# sourceMappingURL=chunk-GNL6Z5VF.js.map
|