bfi-cli 0.1.110 → 0.1.111
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/_chunks/package-plugin.mjs +1 -1
- package/dist/cli.mjs +99 -3
- package/package.json +1 -1
|
@@ -7,8 +7,8 @@ import { randomUUID } from "node:crypto";
|
|
|
7
7
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
8
8
|
import { lstat, mkdir, open, readFile, readdir, realpath, rename, writeFile } from "node:fs/promises";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
|
-
import { z } from "zod";
|
|
11
10
|
import { execFile } from "node:child_process";
|
|
11
|
+
import { z } from "zod";
|
|
12
12
|
const nativeManifestFormats = {
|
|
13
13
|
"claude-code": ["CLAUDE"],
|
|
14
14
|
codex: [
|
package/dist/cli.mjs
CHANGED
|
@@ -30,6 +30,7 @@ import { promisify } from "util";
|
|
|
30
30
|
import { execFile, spawn, spawnSync } from "child_process";
|
|
31
31
|
import { access as access$1, chmod, cp as cp$1, lstat as lstat$1, mkdir as mkdir$1, mkdtemp, readFile as readFile$1, readdir as readdir$1, readlink, realpath as realpath$1, rename as rename$1, rm as rm$1, stat, symlink, writeFile as writeFile$1 } from "fs/promises";
|
|
32
32
|
import { determineAgent } from "@vercel/detect-agent";
|
|
33
|
+
import { spawn as spawn$1 } from "node:child_process";
|
|
33
34
|
import { applyEdits, getNodeValue, modify, parseTree } from "jsonc-parser";
|
|
34
35
|
import { parse as parse$1, stringify } from "smol-toml";
|
|
35
36
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
@@ -5899,9 +5900,68 @@ function fail(message) {
|
|
|
5899
5900
|
process.exit(1);
|
|
5900
5901
|
}
|
|
5901
5902
|
var UseCommandError = class extends Error {};
|
|
5903
|
+
function openBrowser(url) {
|
|
5904
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
5905
|
+
const args = process.platform === "win32" ? [
|
|
5906
|
+
"/c",
|
|
5907
|
+
"start",
|
|
5908
|
+
"",
|
|
5909
|
+
url
|
|
5910
|
+
] : [url];
|
|
5911
|
+
try {
|
|
5912
|
+
const child = spawn$1(cmd, args, {
|
|
5913
|
+
detached: true,
|
|
5914
|
+
stdio: "ignore"
|
|
5915
|
+
});
|
|
5916
|
+
child.on("error", () => {});
|
|
5917
|
+
child.unref();
|
|
5918
|
+
} catch {}
|
|
5919
|
+
}
|
|
5920
|
+
var DeviceFlowUnavailableError = class extends Error {};
|
|
5921
|
+
var DeviceFlowDeniedError = class extends Error {};
|
|
5922
|
+
function dataOf(body) {
|
|
5923
|
+
if (body && typeof body === "object") {
|
|
5924
|
+
const data = body.data;
|
|
5925
|
+
if (data && typeof data === "object") return data;
|
|
5926
|
+
}
|
|
5927
|
+
return {};
|
|
5928
|
+
}
|
|
5929
|
+
async function requestDeviceCode(client) {
|
|
5930
|
+
const resp = await client.request("POST", "/auth/device/code");
|
|
5931
|
+
const data = dataOf(resp.body);
|
|
5932
|
+
const deviceCode = String(data.deviceCode || "");
|
|
5933
|
+
const userCode = String(data.userCode || "");
|
|
5934
|
+
const verificationUri = String(data.verificationUri || "");
|
|
5935
|
+
if (resp.status !== 200 || !deviceCode || !userCode || !verificationUri) throw new DeviceFlowUnavailableError(`设备授权接口不可用(HTTP ${resp.status})`);
|
|
5936
|
+
return {
|
|
5937
|
+
deviceCode,
|
|
5938
|
+
userCode,
|
|
5939
|
+
verificationUri,
|
|
5940
|
+
expiresIn: Number(data.expiresIn) > 0 ? Number(data.expiresIn) : 900,
|
|
5941
|
+
interval: Number(data.interval) > 0 ? Number(data.interval) : 5
|
|
5942
|
+
};
|
|
5943
|
+
}
|
|
5944
|
+
async function pollDeviceToken(client, deviceCode, opts) {
|
|
5945
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
5946
|
+
const now = opts.now ?? (() => Date.now());
|
|
5947
|
+
const deadline = now() + opts.expiresInSec * 1e3;
|
|
5948
|
+
while (now() < deadline) {
|
|
5949
|
+
await sleep(opts.intervalSec * 1e3);
|
|
5950
|
+
const resp = await client.request("POST", "/auth/device/token", { body: { deviceCode } });
|
|
5951
|
+
const data = dataOf(resp.body);
|
|
5952
|
+
if (resp.status === 200 && data.accessToken) return String(data.accessToken);
|
|
5953
|
+
if (resp.status >= 500) throw new DeviceFlowUnavailableError(`设备授权轮询失败(HTTP ${resp.status})`);
|
|
5954
|
+
const error = String(data.error || "");
|
|
5955
|
+
if (error === "authorization_pending") continue;
|
|
5956
|
+
if (error) throw new DeviceFlowDeniedError(`设备授权失败:${error}`);
|
|
5957
|
+
}
|
|
5958
|
+
throw new DeviceFlowDeniedError("设备授权超时,请重试");
|
|
5959
|
+
}
|
|
5902
5960
|
async function runLogin(args, globalOpts) {
|
|
5903
|
-
const token = getOptionValue$3(args, "--token") || await promptForToken();
|
|
5904
5961
|
const { registry } = resolveConfig(globalOpts);
|
|
5962
|
+
const explicitToken = getOptionValue$3(args, "--token");
|
|
5963
|
+
const noDevice = args.includes("--no-device");
|
|
5964
|
+
const token = explicitToken || (noDevice ? await promptForToken() : await loginViaDevice(registry));
|
|
5905
5965
|
const api = new SkillHubAPI(registry, token);
|
|
5906
5966
|
try {
|
|
5907
5967
|
const response = await api.whoami();
|
|
@@ -5922,6 +5982,42 @@ async function runLogin(args, globalOpts) {
|
|
|
5922
5982
|
process.exit(1);
|
|
5923
5983
|
}
|
|
5924
5984
|
}
|
|
5985
|
+
async function loginViaDevice(registry) {
|
|
5986
|
+
const tokenPage = `${registry.replace(/\/$/, "")}/dashboard/tokens`;
|
|
5987
|
+
const client = new SkillHubAPI(registry, null);
|
|
5988
|
+
let deviceCode;
|
|
5989
|
+
try {
|
|
5990
|
+
deviceCode = await requestDeviceCode(client);
|
|
5991
|
+
} catch (error) {
|
|
5992
|
+
return fallbackToTokenInput(error, tokenPage);
|
|
5993
|
+
}
|
|
5994
|
+
console.error("\n请在浏览器打开以下地址完成授权:");
|
|
5995
|
+
console.error(` ${deviceCode.verificationUri}`);
|
|
5996
|
+
console.error(`并输入设备代码:${deviceCode.userCode}(${Math.ceil(deviceCode.expiresIn / 60)} 分钟内有效)`);
|
|
5997
|
+
openBrowser(deviceCode.verificationUri);
|
|
5998
|
+
console.error("等待授权中…(Ctrl+C 取消)");
|
|
5999
|
+
try {
|
|
6000
|
+
const token = await pollDeviceToken(client, deviceCode.deviceCode, {
|
|
6001
|
+
intervalSec: deviceCode.interval,
|
|
6002
|
+
expiresInSec: deviceCode.expiresIn
|
|
6003
|
+
});
|
|
6004
|
+
console.error("✓ 设备授权成功");
|
|
6005
|
+
return token;
|
|
6006
|
+
} catch (error) {
|
|
6007
|
+
if (error instanceof DeviceFlowUnavailableError) return fallbackToTokenInput(error, tokenPage);
|
|
6008
|
+
if (error instanceof DeviceFlowDeniedError) {
|
|
6009
|
+
console.error(`登录失败:${error.message}`);
|
|
6010
|
+
process.exit(1);
|
|
6011
|
+
}
|
|
6012
|
+
throw error;
|
|
6013
|
+
}
|
|
6014
|
+
}
|
|
6015
|
+
async function fallbackToTokenInput(error, tokenPage) {
|
|
6016
|
+
console.error(`⚠️ ${error?.message || "设备授权不可用"},改用网页 Token。`);
|
|
6017
|
+
console.error(`Token 页面:${tokenPage}`);
|
|
6018
|
+
openBrowser(tokenPage);
|
|
6019
|
+
return promptForToken();
|
|
6020
|
+
}
|
|
5925
6021
|
async function runLogout() {
|
|
5926
6022
|
const config = loadGlobalConfig();
|
|
5927
6023
|
if (config?.token) {
|
|
@@ -5934,7 +6030,7 @@ async function runLogout() {
|
|
|
5934
6030
|
async function runWhoami(globalOpts) {
|
|
5935
6031
|
const { registry, token } = resolveConfig(globalOpts);
|
|
5936
6032
|
if (!token) {
|
|
5937
|
-
console.error("未认证。运行:bfi auth login
|
|
6033
|
+
console.error("未认证。运行:bfi auth login(浏览器设备授权;或 --token 直接指定)");
|
|
5938
6034
|
process.exit(1);
|
|
5939
6035
|
}
|
|
5940
6036
|
const response = await new SkillHubAPI(registry, token).whoami();
|
|
@@ -10710,7 +10806,7 @@ ${BOLD}bfi — 用友大业财BFI统一命令行工具${RESET}
|
|
|
10710
10806
|
${BOLD}Usage:${RESET} bfi <command> [options]
|
|
10711
10807
|
|
|
10712
10808
|
${BOLD}━━━ 认证 ━━━${RESET}
|
|
10713
|
-
auth login [--token <token>] 登录 SkillHub
|
|
10809
|
+
auth login [--token <token>] 登录 SkillHub(默认设备授权:浏览器打开并输入代码;--no-device 走隐藏输入)
|
|
10714
10810
|
auth logout 登出
|
|
10715
10811
|
auth whoami 查看当前用户
|
|
10716
10812
|
|