@bigbrain-work/mcp-connect 1.2.2 → 1.3.0
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/LICENSE +21 -0
- package/README.md +33 -59
- package/bin/mcp-connect.js +4 -4
- package/bin/shiliu.js +8 -0
- package/package.json +9 -5
- package/src/arguments.js +75 -19
- package/src/authorization.js +49 -0
- package/src/cli.js +424 -73
- package/src/configurators.js +118 -137
- package/src/constants.js +12 -6
- package/src/credentials.js +166 -148
- package/src/detection.js +16 -21
- package/src/device-auth-client.js +130 -0
- package/src/login-flow.js +39 -0
- package/src/proxy.js +59 -58
- package/src/remote-client.js +27 -10
- package/src/security.js +63 -0
- package/src/status.js +117 -58
- package/src/token-store.js +116 -0
- package/src/updater.js +59 -0
package/src/cli.js
CHANGED
|
@@ -1,129 +1,480 @@
|
|
|
1
|
-
import os from
|
|
2
|
-
import path from
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { resolveAuthorization } from "./authorization.js";
|
|
5
|
+
import { parseCliArguments } from "./arguments.js";
|
|
6
|
+
import { configureAgent, stdioServerDefinition } from "./configurators.js";
|
|
7
7
|
import {
|
|
8
8
|
API_KEY_ENV,
|
|
9
|
+
AUTH_URL,
|
|
9
10
|
MCP_URL,
|
|
10
11
|
PACKAGE_NAME,
|
|
11
12
|
PACKAGE_VERSION,
|
|
12
13
|
SERVER_NAME,
|
|
13
14
|
SUPPORTED_AGENTS,
|
|
14
|
-
} from
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
} from "./constants.js";
|
|
16
|
+
import {
|
|
17
|
+
clearPersistedApiKey,
|
|
18
|
+
persistApiKey,
|
|
19
|
+
resolveApiKey,
|
|
20
|
+
} from "./credentials.js";
|
|
21
|
+
import { detectInstalledAgents } from "./detection.js";
|
|
22
|
+
import {
|
|
23
|
+
DeviceAuthClient,
|
|
24
|
+
DeviceAuthError,
|
|
25
|
+
renderQrCode,
|
|
26
|
+
waitForDeviceAuthorization,
|
|
27
|
+
} from "./device-auth-client.js";
|
|
28
|
+
import { verifyAndPersistLogin } from "./login-flow.js";
|
|
29
|
+
import { runProxy } from "./proxy.js";
|
|
30
|
+
import {
|
|
31
|
+
normalizeDeviceUserCode,
|
|
32
|
+
validateAuthUrl,
|
|
33
|
+
validateMcpUrl,
|
|
34
|
+
} from "./security.js";
|
|
35
|
+
import { printStatus, printTools, probeMcp } from "./status.js";
|
|
36
|
+
import { PendingLoginStore, TokenStore } from "./token-store.js";
|
|
37
|
+
import { printUpdateStatus } from "./updater.js";
|
|
18
38
|
|
|
19
39
|
function printHelp() {
|
|
20
40
|
console.log(`${PACKAGE_NAME} ${PACKAGE_VERSION}
|
|
21
41
|
|
|
22
42
|
用法:
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
shiliu login
|
|
44
|
+
shiliu login --no-wait --json
|
|
45
|
+
shiliu login poll --session <id> [--wait] [--json]
|
|
46
|
+
shiliu install [--agent <name>]
|
|
47
|
+
shiliu status [--json]
|
|
48
|
+
shiliu tools [--json]
|
|
49
|
+
shiliu update
|
|
50
|
+
shiliu logout
|
|
51
|
+
shiliu mcp
|
|
52
|
+
|
|
53
|
+
兼容命令:
|
|
54
|
+
mcp-connect ...
|
|
25
55
|
mcp-connect proxy
|
|
26
|
-
mcp-connect status
|
|
27
56
|
|
|
28
57
|
选项:
|
|
29
58
|
-a, --agent <name> 配置指定客户端
|
|
30
59
|
--dry-run 只显示将执行的操作
|
|
31
60
|
--home <path> 指定用户目录(主要用于测试)
|
|
32
|
-
--url <url>
|
|
61
|
+
--url <url> 指定石榴 AI MCP 地址(诊断或本机测试)
|
|
62
|
+
--auth-url <url> 指定石榴 AI 授权地址(主要用于本机测试)
|
|
63
|
+
--allow-localhost 显式允许连接本机回环测试服务
|
|
64
|
+
--legacy-api-key 使用旧 API Key 兼容登录
|
|
65
|
+
--no-wait 创建登录会话后立即返回
|
|
66
|
+
--wait 等待指定登录会话完成
|
|
67
|
+
--session <id> 指定待继续的登录会话
|
|
68
|
+
--json 以 JSON 输出 status/tools
|
|
33
69
|
-h, --help 显示帮助
|
|
34
70
|
-v, --version 显示版本
|
|
35
71
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
72
|
+
安全:
|
|
73
|
+
login 默认显示微信二维码,换取短期访问令牌和可轮换刷新令牌。
|
|
74
|
+
令牌保存在系统凭据库,不会写入 Agent 配置,也不会由 status/tools 输出。
|
|
75
|
+
兼容期可使用 --legacy-api-key;不提供会进入 shell 历史的密钥参数。
|
|
39
76
|
|
|
40
77
|
智能配置:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
stdio 代理会从本机安全存储读取 API Key,再转发到远程 HTTP MCP。`)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function stdioProxyDefinition(platform = process.platform) {
|
|
47
|
-
if (platform === 'win32') {
|
|
48
|
-
return {
|
|
49
|
-
command: 'cmd',
|
|
50
|
-
args: ['/d', '/s', '/c', 'npx', '-y', PACKAGE_NAME, 'proxy'],
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
command: 'npx',
|
|
55
|
-
args: ['-y', PACKAGE_NAME, 'proxy'],
|
|
56
|
-
}
|
|
78
|
+
未指定 --agent 时自动检测 ${SUPPORTED_AGENTS.join("、")}。
|
|
79
|
+
任意安全的 Agent 名称均可传入;未内置适配器时输出标准 stdio MCP 配置。`);
|
|
57
80
|
}
|
|
58
81
|
|
|
59
82
|
function printGenericInstructions(agent, { dryRun = false } = {}) {
|
|
60
|
-
const proxy = stdioProxyDefinition()
|
|
61
83
|
const config = {
|
|
62
84
|
mcpServers: {
|
|
63
|
-
[SERVER_NAME]:
|
|
85
|
+
[SERVER_NAME]: stdioServerDefinition(),
|
|
64
86
|
},
|
|
65
|
-
}
|
|
66
|
-
console.log(`暂未内置 ${agent} 的自动配置适配器。请在该 Agent
|
|
87
|
+
};
|
|
88
|
+
console.log(`暂未内置 ${agent} 的自动配置适配器。请在该 Agent 中添加下面的标准 stdio MCP 配置:
|
|
67
89
|
|
|
68
90
|
${JSON.stringify(config, null, 2)}
|
|
69
91
|
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
桥接器连接 ${MCP_URL},并从本机凭据存储读取授权信息。
|
|
93
|
+
${dryRun ? "演练模式没有保存凭据。" : "Agent 配置中不包含真实凭据。"} 请勿将凭据写入项目代码或提交到 Git。`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function legacyLogin({ home, dryRun, url }) {
|
|
97
|
+
const apiKey = await resolveApiKey();
|
|
98
|
+
if (!dryRun) {
|
|
99
|
+
const remote = await probeMcp(apiKey, { url });
|
|
100
|
+
if (!remote.ok) throw new Error(`凭据验证失败:${remote.detail}`);
|
|
101
|
+
}
|
|
102
|
+
await persistApiKey(apiKey, { dryRun, home });
|
|
103
|
+
console.log(
|
|
104
|
+
dryRun
|
|
105
|
+
? "登录演练通过:凭据格式有效,未连接服务、未写入本机。"
|
|
106
|
+
: "登录成功:凭据已验证并保存到本机;未写入 Agent 配置或项目文件。",
|
|
107
|
+
);
|
|
108
|
+
}
|
|
72
109
|
|
|
73
|
-
|
|
110
|
+
function buildPendingLogin(start, authUrl, allowLocalhost) {
|
|
111
|
+
return {
|
|
112
|
+
sessionId: normalizeDeviceUserCode(start.user_code),
|
|
113
|
+
deviceCode: start.device_code,
|
|
114
|
+
expiresAt: Date.now() + Number(start.expires_in) * 1000,
|
|
115
|
+
interval: Number(start.interval) || 5,
|
|
116
|
+
authUrl,
|
|
117
|
+
allowLocalhost,
|
|
118
|
+
};
|
|
74
119
|
}
|
|
75
120
|
|
|
76
|
-
export
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
121
|
+
export function buildLoginInstructions(start, { allowLocalhost = false } = {}) {
|
|
122
|
+
const sessionId = normalizeDeviceUserCode(start.user_code);
|
|
123
|
+
const localhostOption = allowLocalhost ? " --allow-localhost" : "";
|
|
124
|
+
return {
|
|
125
|
+
status: "authorization_pending",
|
|
126
|
+
login_session_id: sessionId,
|
|
127
|
+
user_code: start.user_code,
|
|
128
|
+
verification_uri:
|
|
129
|
+
start.verification_uri_complete ||
|
|
130
|
+
start.qr_code_uri ||
|
|
131
|
+
start.verification_uri,
|
|
132
|
+
expires_in: Number(start.expires_in),
|
|
133
|
+
poll_command: `shiliu login poll --session ${sessionId} --wait --json${localhostOption}`,
|
|
134
|
+
next_action_hint:
|
|
135
|
+
"请让用户打开 verification_uri 完成微信授权;用户确认后运行 poll_command。",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function printJson(value) {
|
|
140
|
+
console.log(JSON.stringify(value, null, 2));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function startPendingDeviceLogin({
|
|
144
|
+
authUrl,
|
|
145
|
+
allowLocalhost,
|
|
146
|
+
json,
|
|
147
|
+
pendingLoginStore,
|
|
148
|
+
}) {
|
|
149
|
+
const client = new DeviceAuthClient({ baseUrl: authUrl });
|
|
150
|
+
const start = await client.start();
|
|
151
|
+
await pendingLoginStore.save(
|
|
152
|
+
buildPendingLogin(start, authUrl, allowLocalhost),
|
|
153
|
+
);
|
|
154
|
+
const instructions = buildLoginInstructions(start, { allowLocalhost });
|
|
155
|
+
if (json) {
|
|
156
|
+
printJson(instructions);
|
|
157
|
+
return;
|
|
81
158
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
159
|
+
console.log(`请使用微信扫描二维码完成登录(验证码 ${start.user_code}):`);
|
|
160
|
+
console.log(await renderQrCode(start.qr_code_uri));
|
|
161
|
+
console.log(`也可以打开:${instructions.verification_uri}`);
|
|
162
|
+
console.log(
|
|
163
|
+
`完成后运行:${instructions.poll_command.replace(" --json", "")}`,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function pollPendingDeviceLogin({
|
|
168
|
+
session,
|
|
169
|
+
wait,
|
|
170
|
+
json,
|
|
171
|
+
url,
|
|
172
|
+
tokenStore,
|
|
173
|
+
pendingLoginStore,
|
|
174
|
+
}) {
|
|
175
|
+
if (!session) throw new Error("login poll 需要 --session <id>");
|
|
176
|
+
const pending = await pendingLoginStore.load();
|
|
177
|
+
if (!pending || pending.sessionId !== session) {
|
|
178
|
+
throw new Error("未找到对应的待处理登录会话,请重新运行 shiliu login");
|
|
85
179
|
}
|
|
180
|
+
if (pending.expiresAt <= Date.now()) {
|
|
181
|
+
await pendingLoginStore.clear();
|
|
182
|
+
throw new Error("登录会话已过期,请重新运行 shiliu login");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const pendingAuthUrl = validateAuthUrl(pending.authUrl, {
|
|
186
|
+
allowLocalhost: pending.allowLocalhost === true,
|
|
187
|
+
});
|
|
188
|
+
const client = new DeviceAuthClient({ baseUrl: pendingAuthUrl });
|
|
189
|
+
let response;
|
|
190
|
+
try {
|
|
191
|
+
if (wait) {
|
|
192
|
+
response = await waitForDeviceAuthorization(
|
|
193
|
+
{
|
|
194
|
+
device_code: pending.deviceCode,
|
|
195
|
+
expires_in: Math.max(
|
|
196
|
+
1,
|
|
197
|
+
Math.ceil((pending.expiresAt - Date.now()) / 1000),
|
|
198
|
+
),
|
|
199
|
+
interval: pending.interval,
|
|
200
|
+
},
|
|
201
|
+
client,
|
|
202
|
+
{ onPending: json ? undefined : () => process.stdout.write(".") },
|
|
203
|
+
);
|
|
204
|
+
if (!json) process.stdout.write("\n");
|
|
205
|
+
} else {
|
|
206
|
+
response = await client.exchange(pending.deviceCode);
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (
|
|
210
|
+
error instanceof DeviceAuthError &&
|
|
211
|
+
["authorization_pending", "slow_down"].includes(error.code)
|
|
212
|
+
) {
|
|
213
|
+
const result = {
|
|
214
|
+
status: "authorization_pending",
|
|
215
|
+
login_session_id: pending.sessionId,
|
|
216
|
+
poll_command: `shiliu login poll --session ${pending.sessionId} --wait --json`,
|
|
217
|
+
};
|
|
218
|
+
if (json) printJson(result);
|
|
219
|
+
else console.log("登录尚未完成,请授权后重新运行并加上 --wait。");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (error instanceof DeviceAuthError && error.code === "expired_token") {
|
|
223
|
+
await pendingLoginStore.clear();
|
|
224
|
+
}
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const remote = await verifyAndPersistLogin({
|
|
229
|
+
response,
|
|
230
|
+
client,
|
|
231
|
+
tokenStore,
|
|
232
|
+
url,
|
|
233
|
+
});
|
|
234
|
+
await pendingLoginStore.clear();
|
|
235
|
+
const result = {
|
|
236
|
+
status: "success",
|
|
237
|
+
message: "登录成功",
|
|
238
|
+
tool_count: remote.toolCount,
|
|
239
|
+
};
|
|
240
|
+
if (json) printJson(result);
|
|
241
|
+
else console.log(`登录成功:${remote.detail};令牌已保存到系统凭据库。`);
|
|
242
|
+
}
|
|
86
243
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
244
|
+
async function deviceLogin({
|
|
245
|
+
dryRun,
|
|
246
|
+
authUrl,
|
|
247
|
+
url,
|
|
248
|
+
tokenStore,
|
|
249
|
+
pendingLoginStore,
|
|
250
|
+
noWait,
|
|
251
|
+
json,
|
|
252
|
+
allowLocalhost,
|
|
253
|
+
}) {
|
|
254
|
+
if (dryRun) {
|
|
255
|
+
console.log(
|
|
256
|
+
`登录演练:将从 ${authUrl} 获取设备码、显示微信二维码,并把令牌保存到系统凭据库;未发起网络请求。`,
|
|
257
|
+
);
|
|
258
|
+
return;
|
|
91
259
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
260
|
+
|
|
261
|
+
if (noWait) {
|
|
262
|
+
return startPendingDeviceLogin({
|
|
263
|
+
authUrl,
|
|
264
|
+
allowLocalhost,
|
|
265
|
+
json,
|
|
266
|
+
pendingLoginStore,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const client = new DeviceAuthClient({ baseUrl: authUrl });
|
|
271
|
+
const start = await client.start();
|
|
272
|
+
console.log(`请使用微信扫描二维码完成登录(验证码 ${start.user_code}):`);
|
|
273
|
+
console.log(await renderQrCode(start.qr_code_uri));
|
|
274
|
+
console.log(
|
|
275
|
+
`若终端二维码无法识别,请打开:${start.verification_uri_complete}`,
|
|
276
|
+
);
|
|
277
|
+
const response = await waitForDeviceAuthorization(start, client, {
|
|
278
|
+
onPending: () => process.stdout.write("."),
|
|
279
|
+
});
|
|
280
|
+
process.stdout.write("\n");
|
|
281
|
+
const remote = await verifyAndPersistLogin({
|
|
282
|
+
response,
|
|
283
|
+
client,
|
|
284
|
+
tokenStore,
|
|
285
|
+
url,
|
|
286
|
+
});
|
|
287
|
+
console.log(`登录成功:${remote.detail};短期访问令牌已保存到系统凭据库。`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function login({
|
|
291
|
+
home,
|
|
292
|
+
dryRun,
|
|
293
|
+
url,
|
|
294
|
+
authUrl,
|
|
295
|
+
legacyApiKey,
|
|
296
|
+
tokenStore,
|
|
297
|
+
pendingLoginStore,
|
|
298
|
+
noWait = false,
|
|
299
|
+
json = false,
|
|
300
|
+
allowLocalhost = false,
|
|
301
|
+
}) {
|
|
302
|
+
if (legacyApiKey) return legacyLogin({ home, dryRun, url });
|
|
303
|
+
return deviceLogin({
|
|
304
|
+
dryRun,
|
|
305
|
+
authUrl,
|
|
306
|
+
url,
|
|
307
|
+
tokenStore,
|
|
308
|
+
pendingLoginStore,
|
|
309
|
+
noWait,
|
|
310
|
+
json,
|
|
311
|
+
allowLocalhost,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export async function logout({
|
|
316
|
+
home,
|
|
317
|
+
dryRun,
|
|
318
|
+
authUrl,
|
|
319
|
+
tokenStore,
|
|
320
|
+
pendingLoginStore,
|
|
321
|
+
clearLegacyApiKey = clearPersistedApiKey,
|
|
322
|
+
clientFactory = (baseUrl) => new DeviceAuthClient({ baseUrl }),
|
|
323
|
+
}) {
|
|
324
|
+
if (dryRun) {
|
|
325
|
+
console.log("演练模式:未清除本机凭据。");
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const current = await tokenStore.load();
|
|
330
|
+
if (current?.refreshToken) {
|
|
331
|
+
try {
|
|
332
|
+
await clientFactory(authUrl).revoke(current.refreshToken);
|
|
333
|
+
} catch (error) {
|
|
334
|
+
throw new Error(
|
|
335
|
+
`远端令牌撤销失败,本机凭据已保留,请稍后重试退出:${error.message}`,
|
|
336
|
+
);
|
|
96
337
|
}
|
|
97
|
-
return
|
|
98
338
|
}
|
|
99
339
|
|
|
100
|
-
|
|
101
|
-
await
|
|
102
|
-
|
|
340
|
+
await tokenStore.clear();
|
|
341
|
+
await pendingLoginStore.clear();
|
|
342
|
+
await clearLegacyApiKey({ home, dryRun: false });
|
|
343
|
+
console.log("已退出登录,并从系统凭据库清除令牌和旧版兼容凭据。");
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async function ensureLogin(options) {
|
|
347
|
+
const authorization = await resolveAuthorization({
|
|
348
|
+
home: options.home,
|
|
349
|
+
authUrl: options.authUrl,
|
|
350
|
+
tokenStore: options.tokenStore,
|
|
351
|
+
});
|
|
352
|
+
if (authorization.token) return;
|
|
353
|
+
await login(options);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function install({
|
|
357
|
+
agent,
|
|
358
|
+
dryRun,
|
|
359
|
+
home,
|
|
360
|
+
url,
|
|
361
|
+
authUrl,
|
|
362
|
+
legacyApiKey,
|
|
363
|
+
tokenStore,
|
|
364
|
+
}) {
|
|
365
|
+
await ensureLogin({
|
|
103
366
|
home,
|
|
104
|
-
|
|
105
|
-
|
|
367
|
+
dryRun,
|
|
368
|
+
url,
|
|
369
|
+
authUrl,
|
|
370
|
+
legacyApiKey,
|
|
371
|
+
tokenStore,
|
|
372
|
+
});
|
|
373
|
+
const requestedAgent = agent === "auto" ? undefined : agent;
|
|
106
374
|
if (requestedAgent && !SUPPORTED_AGENTS.includes(requestedAgent)) {
|
|
107
|
-
printGenericInstructions(requestedAgent, { dryRun
|
|
108
|
-
return
|
|
375
|
+
printGenericInstructions(requestedAgent, { dryRun });
|
|
376
|
+
return;
|
|
109
377
|
}
|
|
110
378
|
|
|
111
379
|
const agents = requestedAgent
|
|
112
380
|
? [requestedAgent]
|
|
113
|
-
: await detectInstalledAgents({ home })
|
|
381
|
+
: await detectInstalledAgents({ home });
|
|
114
382
|
if (agents.length === 0) {
|
|
115
|
-
printGenericInstructions(
|
|
116
|
-
return
|
|
383
|
+
printGenericInstructions("当前客户端", { dryRun });
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
for (const detectedAgent of agents) {
|
|
387
|
+
await configureAgent(detectedAgent, { dryRun, home });
|
|
388
|
+
}
|
|
389
|
+
console.log(
|
|
390
|
+
dryRun
|
|
391
|
+
? `检查完成:将为 ${agents.join("、")} 配置 ${MCP_URL},未写入文件。`
|
|
392
|
+
: `配置完成:${agents.join("、")} 已接入石榴 AI MCP。请重启客户端后运行 shiliu status。`,
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export async function runCli(argv = process.argv.slice(2)) {
|
|
397
|
+
const options = parseCliArguments(argv);
|
|
398
|
+
if (options.help) return printHelp();
|
|
399
|
+
if (options.version) {
|
|
400
|
+
console.log(PACKAGE_VERSION);
|
|
401
|
+
return;
|
|
117
402
|
}
|
|
118
403
|
|
|
119
|
-
|
|
120
|
-
|
|
404
|
+
const home = path.resolve(options.home || os.homedir());
|
|
405
|
+
const url = validateMcpUrl(options.url || MCP_URL, {
|
|
406
|
+
allowLocalhost: options.allowLocalhost,
|
|
407
|
+
});
|
|
408
|
+
const authUrl = validateAuthUrl(options.authUrl || AUTH_URL, {
|
|
409
|
+
allowLocalhost: options.allowLocalhost,
|
|
410
|
+
});
|
|
411
|
+
const tokenStore = new TokenStore();
|
|
412
|
+
const pendingLoginStore = new PendingLoginStore();
|
|
413
|
+
if (options.command === "mcp" || options.command === "proxy") {
|
|
414
|
+
await runProxy({ home, url, authUrl });
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (options.command === "login") {
|
|
418
|
+
if (options.subcommand === "poll") {
|
|
419
|
+
await pollPendingDeviceLogin({
|
|
420
|
+
session: options.session,
|
|
421
|
+
wait: options.wait,
|
|
422
|
+
json: options.json,
|
|
423
|
+
url,
|
|
424
|
+
tokenStore,
|
|
425
|
+
pendingLoginStore,
|
|
426
|
+
});
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
await login({
|
|
430
|
+
home,
|
|
431
|
+
dryRun: options.dryRun,
|
|
432
|
+
url,
|
|
433
|
+
authUrl,
|
|
434
|
+
legacyApiKey: options.legacyApiKey,
|
|
435
|
+
allowLocalhost: options.allowLocalhost,
|
|
436
|
+
tokenStore,
|
|
437
|
+
pendingLoginStore,
|
|
438
|
+
noWait: options.noWait,
|
|
439
|
+
json: options.json,
|
|
440
|
+
});
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (options.command === "logout") {
|
|
444
|
+
return logout({
|
|
445
|
+
home,
|
|
121
446
|
dryRun: options.dryRun,
|
|
447
|
+
authUrl,
|
|
448
|
+
tokenStore,
|
|
449
|
+
pendingLoginStore,
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
if (options.command === "status") {
|
|
453
|
+
const result = await printStatus({
|
|
122
454
|
home,
|
|
123
|
-
|
|
455
|
+
url,
|
|
456
|
+
authUrl,
|
|
457
|
+
json: options.json,
|
|
458
|
+
});
|
|
459
|
+
if (!result.remote.ok) process.exitCode = 1;
|
|
460
|
+
return;
|
|
124
461
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
462
|
+
if (options.command === "tools") {
|
|
463
|
+
await printTools({ home, url, authUrl, json: options.json });
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
if (options.command === "update") {
|
|
467
|
+
await printUpdateStatus();
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
await install({
|
|
471
|
+
agent: options.agent,
|
|
472
|
+
dryRun: options.dryRun,
|
|
473
|
+
home,
|
|
474
|
+
url,
|
|
475
|
+
authUrl,
|
|
476
|
+
legacyApiKey: options.legacyApiKey,
|
|
477
|
+
allowLocalhost: options.allowLocalhost,
|
|
478
|
+
tokenStore,
|
|
479
|
+
});
|
|
129
480
|
}
|