@bigbrain-work/mcp-connect 1.2.2 → 1.3.1

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.
@@ -0,0 +1,116 @@
1
+ import {
2
+ KEYRING_ACCOUNT,
3
+ KEYRING_SERVICE,
4
+ PENDING_KEYRING_ACCOUNT,
5
+ } from "./constants.js";
6
+
7
+ function validateTokenSet(value) {
8
+ if (
9
+ !value ||
10
+ typeof value !== "object" ||
11
+ typeof value.accessToken !== "string" ||
12
+ typeof value.refreshToken !== "string" ||
13
+ typeof value.expiresAt !== "number"
14
+ ) {
15
+ throw new Error("本机凭据格式无效,请重新运行 shiliu login");
16
+ }
17
+ return value;
18
+ }
19
+
20
+ async function createSystemEntry(account) {
21
+ let module;
22
+ try {
23
+ module = await import("@napi-rs/keyring");
24
+ } catch (error) {
25
+ throw new Error(`无法加载系统凭据库:${error.message}`);
26
+ }
27
+ return new module.Entry(KEYRING_SERVICE, account);
28
+ }
29
+
30
+ async function defaultEntryFactory() {
31
+ return createSystemEntry(KEYRING_ACCOUNT);
32
+ }
33
+
34
+ async function defaultPendingEntryFactory() {
35
+ return createSystemEntry(PENDING_KEYRING_ACCOUNT);
36
+ }
37
+
38
+ export class TokenStore {
39
+ constructor({ entryFactory = defaultEntryFactory } = {}) {
40
+ this.entryFactory = entryFactory;
41
+ }
42
+
43
+ async save(tokenSet) {
44
+ const entry = await this.entryFactory();
45
+ await entry.setPassword(JSON.stringify(validateTokenSet(tokenSet)));
46
+ }
47
+
48
+ async load() {
49
+ const entry = await this.entryFactory();
50
+ try {
51
+ const serialized = await entry.getPassword();
52
+ if (!serialized) return null;
53
+ return validateTokenSet(JSON.parse(serialized));
54
+ } catch (error) {
55
+ if (this.isMissingEntry(error)) return null;
56
+ throw new Error(`无法读取系统凭据库:${error.message}`);
57
+ }
58
+ }
59
+
60
+ async clear() {
61
+ const entry = await this.entryFactory();
62
+ try {
63
+ await entry.deletePassword();
64
+ } catch (error) {
65
+ if (!this.isMissingEntry(error)) {
66
+ throw new Error(`无法清除系统凭据库:${error.message}`);
67
+ }
68
+ }
69
+ }
70
+
71
+ isMissingEntry(error) {
72
+ return /not found|no entry|no matching|credential.*missing/iu.test(
73
+ error?.message || "",
74
+ );
75
+ }
76
+ }
77
+
78
+ function validatePendingLogin(value) {
79
+ if (
80
+ !value ||
81
+ typeof value !== "object" ||
82
+ typeof value.sessionId !== "string" ||
83
+ typeof value.deviceCode !== "string" ||
84
+ typeof value.expiresAt !== "number" ||
85
+ typeof value.interval !== "number" ||
86
+ typeof value.authUrl !== "string" ||
87
+ (value.allowLocalhost !== undefined &&
88
+ typeof value.allowLocalhost !== "boolean")
89
+ ) {
90
+ throw new Error("待处理登录会话格式无效,请重新运行 shiliu login");
91
+ }
92
+ return value;
93
+ }
94
+
95
+ export class PendingLoginStore extends TokenStore {
96
+ constructor({ entryFactory = defaultPendingEntryFactory } = {}) {
97
+ super({ entryFactory });
98
+ }
99
+
100
+ async save(value) {
101
+ const entry = await this.entryFactory();
102
+ await entry.setPassword(JSON.stringify(validatePendingLogin(value)));
103
+ }
104
+
105
+ async load() {
106
+ const entry = await this.entryFactory();
107
+ try {
108
+ const serialized = await entry.getPassword();
109
+ if (!serialized) return null;
110
+ return validatePendingLogin(JSON.parse(serialized));
111
+ } catch (error) {
112
+ if (this.isMissingEntry(error)) return null;
113
+ throw new Error(`无法读取待处理登录会话:${error.message}`);
114
+ }
115
+ }
116
+ }
package/src/updater.js ADDED
@@ -0,0 +1,59 @@
1
+ import {
2
+ PACKAGE_NAME,
3
+ PACKAGE_VERSION,
4
+ REGISTRY_LATEST_URL,
5
+ } from "./constants.js";
6
+
7
+ function parseVersion(value) {
8
+ const match = String(value).match(
9
+ /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/u,
10
+ );
11
+ if (!match) return null;
12
+ return {
13
+ numbers: match.slice(1, 4).map(Number),
14
+ prerelease: match[4] || "",
15
+ };
16
+ }
17
+
18
+ export function compareVersions(left, right) {
19
+ const a = parseVersion(left);
20
+ const b = parseVersion(right);
21
+ if (!a || !b) return 0;
22
+ for (let index = 0; index < 3; index += 1) {
23
+ if (a.numbers[index] !== b.numbers[index]) {
24
+ return a.numbers[index] > b.numbers[index] ? 1 : -1;
25
+ }
26
+ }
27
+ if (a.prerelease === b.prerelease) return 0;
28
+ if (!a.prerelease) return 1;
29
+ if (!b.prerelease) return -1;
30
+ return a.prerelease.localeCompare(b.prerelease);
31
+ }
32
+
33
+ export async function checkForUpdates({ fetchImpl = fetch } = {}) {
34
+ const response = await fetchImpl(REGISTRY_LATEST_URL, {
35
+ headers: { Accept: "application/json" },
36
+ signal: AbortSignal.timeout(10000),
37
+ });
38
+ if (!response.ok)
39
+ throw new Error(`npm registry 返回 HTTP ${response.status}`);
40
+ const metadata = await response.json();
41
+ const latest = metadata.version;
42
+ if (!latest) throw new Error("npm registry 未返回版本号");
43
+ return {
44
+ current: PACKAGE_VERSION,
45
+ latest,
46
+ updateAvailable: compareVersions(latest, PACKAGE_VERSION) > 0,
47
+ };
48
+ }
49
+
50
+ export async function printUpdateStatus(options = {}) {
51
+ const result = await checkForUpdates(options);
52
+ if (result.updateAvailable) {
53
+ console.log(`发现新版本:${result.current} → ${result.latest}`);
54
+ console.log(`更新命令:npm install -g ${PACKAGE_NAME}@latest`);
55
+ } else {
56
+ console.log(`当前版本:${result.current};npm latest:${result.latest}`);
57
+ }
58
+ return result;
59
+ }