alink-cli 0.11.8 → 0.11.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/README.md +2 -0
- package/bin/agentlink-account.js +4 -0
- package/bin/agentlink.js +53 -20
- package/dist/bin.mjs +393 -40
- package/dist/bin.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,4 +24,6 @@ alink
|
|
|
24
24
|
|
|
25
25
|
`alink login` 会打开浏览器,支持使用 GitHub 或已有 AgentLink 账号登录并添加当前电脑。原命令 `alink-cli` 继续可用。
|
|
26
26
|
|
|
27
|
+
脚本或排障时可用 `alink status --json` 和 `alink machines --json` 获取结构化的本机连接状态与账号机器数据。daemon 在网络中断、电脑唤醒或 Hub 更新后会自动退避重连,无需手动重启。
|
|
28
|
+
|
|
27
29
|
[GitHub](https://github.com/baichen99/agentlink) · [提交问题](https://github.com/baichen99/agentlink/issues)
|
package/bin/agentlink-account.js
CHANGED
|
@@ -185,9 +185,13 @@ export async function listAgentLinkAccountMachinesFromHub({ hub, jwt, fetchFn =
|
|
|
185
185
|
online: row.online === true,
|
|
186
186
|
name: typeof row.name === "string" ? row.name : undefined,
|
|
187
187
|
hostname: typeof row.hostname === "string" ? row.hostname : undefined,
|
|
188
|
+
daemonVersion: typeof row.daemonVersion === "string" ? row.daemonVersion : undefined,
|
|
188
189
|
connectedAt: timestamp(row.connectedAt),
|
|
189
190
|
createdAt: timestamp(row.createdAt),
|
|
190
191
|
updatedAt: timestamp(row.updatedAt),
|
|
192
|
+
dirs: Array.isArray(row.dirs) ? row.dirs.filter((dir) => typeof dir === "string") : undefined,
|
|
193
|
+
agents: Array.isArray(row.agents) ? row.agents.filter((agent) => typeof agent === "string") : undefined,
|
|
194
|
+
e2e: typeof row.e2e === "boolean" ? row.e2e : undefined,
|
|
191
195
|
})).filter((m) => m.machineId);
|
|
192
196
|
}
|
|
193
197
|
|
package/bin/agentlink.js
CHANGED
|
@@ -137,15 +137,20 @@ async function createSingleCredential(hub) {
|
|
|
137
137
|
const enckey = randomBytes(32).toString("base64url");
|
|
138
138
|
const credential = `als1.${token}.${enckey}`;
|
|
139
139
|
saveCredential(credential, hub);
|
|
140
|
+
await printSingleCredential(credential, hub, true);
|
|
141
|
+
return credential;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function printSingleCredential(credential, hub, qr = false) {
|
|
145
|
+
const { machineToken: token, enckey } = parseCredential(credential);
|
|
140
146
|
const url = new URL("/pair", hub);
|
|
141
147
|
if (url.protocol === "ws:") url.protocol = "http:";
|
|
142
148
|
if (url.protocol === "wss:") url.protocol = "https:";
|
|
143
149
|
url.searchParams.set("token", token);
|
|
144
150
|
url.hash = new URLSearchParams({ enckey }).toString();
|
|
145
|
-
console.log("\n在 AgentLink
|
|
146
|
-
await printQr(credential);
|
|
151
|
+
console.log("\n在 AgentLink 网页打开下面的本机配对链接:\n");
|
|
152
|
+
if (qr) await printQr(credential);
|
|
147
153
|
console.log(`\n${url}\n`);
|
|
148
|
-
return credential;
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
async function loginAndCreateCredential(hub, config) {
|
|
@@ -187,7 +192,9 @@ async function browserLogin(hub) {
|
|
|
187
192
|
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" }).end("登录请求无效。");
|
|
188
193
|
return;
|
|
189
194
|
}
|
|
190
|
-
res
|
|
195
|
+
res
|
|
196
|
+
.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" })
|
|
197
|
+
.end("登录成功,可以关闭此页面。");
|
|
191
198
|
clearTimeout(timer);
|
|
192
199
|
server.close();
|
|
193
200
|
resolve(jwt);
|
|
@@ -209,8 +216,10 @@ async function browserLogin(hub) {
|
|
|
209
216
|
url.searchParams.set("cli_callback", `http://127.0.0.1:${address.port}/callback`);
|
|
210
217
|
url.searchParams.set("cli_state", state);
|
|
211
218
|
console.log(`\n请在浏览器完成 AgentLink 账号验证:\n${url}\n`);
|
|
212
|
-
const command =
|
|
213
|
-
|
|
219
|
+
const command =
|
|
220
|
+
process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
221
|
+
const commandArgs =
|
|
222
|
+
process.platform === "win32" ? ["/c", "start", "", url.toString()] : [url.toString()];
|
|
214
223
|
const opener = spawn(command, commandArgs, { detached: true, stdio: "ignore" });
|
|
215
224
|
opener.on("error", () => undefined);
|
|
216
225
|
opener.unref();
|
|
@@ -274,7 +283,7 @@ async function pair(hub) {
|
|
|
274
283
|
|
|
275
284
|
async function showHelp() {
|
|
276
285
|
console.log(
|
|
277
|
-
`用法:\n alink-cli 打开 AgentLink\n alink-cli login 登录账号\n alink-cli status
|
|
286
|
+
`用法:\n alink-cli 打开 AgentLink\n alink-cli login 登录账号\n alink-cli status [--json] 查看连接状态\n alink-cli machines [--json] 查看账号下的机器\n alink-cli logout 退出账号\n alink-cli help 查看帮助`,
|
|
278
287
|
);
|
|
279
288
|
}
|
|
280
289
|
|
|
@@ -306,11 +315,22 @@ async function showStatus() {
|
|
|
306
315
|
}
|
|
307
316
|
const statusMatchesHub =
|
|
308
317
|
typeof status.hub !== "string" || normalizeHub(status.hub) === normalizeHub(hub);
|
|
309
|
-
const hubStatus =
|
|
310
|
-
? "未连接"
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
:
|
|
318
|
+
const hubStatus =
|
|
319
|
+
!running || !statusMatchesHub ? "未连接" : status.hubConnected ? "已连接" : "连接中";
|
|
320
|
+
if (args.includes("--json")) {
|
|
321
|
+
console.log(JSON.stringify({
|
|
322
|
+
loggedIn: Boolean(savedCredential(hub)),
|
|
323
|
+
running,
|
|
324
|
+
hubConnected: hubStatus === "已连接",
|
|
325
|
+
hubStatus,
|
|
326
|
+
hub: normalizeHub(hub),
|
|
327
|
+
...(typeof status.machineId === "string" ? { machineId: status.machineId } : {}),
|
|
328
|
+
...(typeof status.daemonVersion === "string" ? { daemonVersion: status.daemonVersion } : {}),
|
|
329
|
+
...(typeof status.startedAt === "number" ? { startedAt: status.startedAt } : {}),
|
|
330
|
+
...(typeof status.ts === "number" ? { updatedAt: status.ts } : {}),
|
|
331
|
+
}, null, 2));
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
314
334
|
console.log(`[agentlink] 登录状态:${savedCredential(hub) ? "已登录" : "未登录"}`);
|
|
315
335
|
console.log(`[agentlink] 本机服务:${running ? "运行中" : "已停止"}`);
|
|
316
336
|
console.log(`[agentlink] Hub 连接:${hubStatus}`);
|
|
@@ -351,7 +371,9 @@ async function ensureSessionJwt(hub, config) {
|
|
|
351
371
|
const login = await loginResponse.json().catch(() => ({}));
|
|
352
372
|
if (!loginResponse.ok || typeof login.jwt !== "string" || !login.jwt) {
|
|
353
373
|
throw new Error(
|
|
354
|
-
login.error === "invalid_account_credentials"
|
|
374
|
+
login.error === "invalid_account_credentials"
|
|
375
|
+
? "账号或密码错误。"
|
|
376
|
+
: login.error || "账号登录失败。",
|
|
355
377
|
);
|
|
356
378
|
}
|
|
357
379
|
saveSessionJwt(login.jwt, hub);
|
|
@@ -373,13 +395,21 @@ async function doMachines() {
|
|
|
373
395
|
saveSessionJwt(jwt, hub);
|
|
374
396
|
}
|
|
375
397
|
const machines = await listAgentLinkAccountMachinesFromHub({ hub, jwt });
|
|
398
|
+
if (args.includes("--json")) {
|
|
399
|
+
console.log(JSON.stringify(machines, null, 2));
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
376
402
|
if (machines.length === 0) console.log("[agentlink] 账号下还没有机器。");
|
|
377
403
|
else {
|
|
378
404
|
console.log(`[agentlink] 账号下共 ${machines.length} 台机器:`);
|
|
379
405
|
for (const machine of machines) {
|
|
380
406
|
const onlineMarker = machine.online ? "●" : "○";
|
|
381
407
|
const label = machine.name || machine.hostname || machine.machineId;
|
|
382
|
-
|
|
408
|
+
const details = [
|
|
409
|
+
machine.daemonVersion ? `v${machine.daemonVersion}` : undefined,
|
|
410
|
+
machine.connectedAt ? `连接于 ${new Date(machine.connectedAt).toLocaleString()}` : undefined,
|
|
411
|
+
].filter(Boolean).join(" · ");
|
|
412
|
+
console.log(` ${onlineMarker} ${label} (${machine.machineId})${details ? ` · ${details}` : ""}`);
|
|
383
413
|
}
|
|
384
414
|
}
|
|
385
415
|
}
|
|
@@ -392,18 +422,18 @@ async function doLegacy() {
|
|
|
392
422
|
async function doNoTuiStart() {
|
|
393
423
|
const hub = value("hub") || OFFICIAL_HUB;
|
|
394
424
|
const explicit = value("token") || process.env.AGENTLINK_TOKEN;
|
|
425
|
+
const stored = !args.includes("--pair") ? savedCredential(hub) : undefined;
|
|
395
426
|
const config =
|
|
396
|
-
explicit ||
|
|
397
|
-
? null
|
|
398
|
-
: await loadAgentLinkAccountConfig(hub);
|
|
427
|
+
explicit || stored || args.includes("--pair") ? null : await loadAgentLinkAccountConfig(hub);
|
|
399
428
|
const credential =
|
|
400
429
|
explicit ||
|
|
401
|
-
|
|
430
|
+
stored ||
|
|
402
431
|
(args.includes("--pair")
|
|
403
432
|
? await pair(hub)
|
|
404
433
|
: config?.mode === "multi"
|
|
405
434
|
? await loginAndCreateCredential(hub, config)
|
|
406
435
|
: await createSingleCredential(hub));
|
|
436
|
+
if (!explicit && stored?.startsWith("als1.")) await printSingleCredential(stored, hub);
|
|
407
437
|
if (explicit && explicit !== savedCredential(hub)) saveCredential(explicit, hub);
|
|
408
438
|
if (!explicit && !args.includes("--pair")) console.log("[agentlink] 正在让这台电脑上线。");
|
|
409
439
|
const { machineToken, enckey } = parseCredential(credential);
|
|
@@ -478,7 +508,8 @@ async function main() {
|
|
|
478
508
|
if (command === "path") console.log(CREDENTIAL_FILE);
|
|
479
509
|
else if (command === "status") console.log(savedCredential(hub) ? "已保存凭证" : "未保存凭证");
|
|
480
510
|
else if (command === "show") console.log(savedCredential(hub) || "");
|
|
481
|
-
else if (command === "set" && publicArgs[2] && !/\s/.test(publicArgs[2]))
|
|
511
|
+
else if (command === "set" && publicArgs[2] && !/\s/.test(publicArgs[2]))
|
|
512
|
+
saveCredential(publicArgs[2], hub);
|
|
482
513
|
else if (command === "clear") rmSync(CREDENTIAL_FILE, { force: true });
|
|
483
514
|
else throw new Error("用法: alink-cli credential status|path|show|set <credential>|clear");
|
|
484
515
|
return;
|
|
@@ -486,7 +517,9 @@ async function main() {
|
|
|
486
517
|
|
|
487
518
|
if (publicArgs[0] === "qr" && legacyCredentialCommands) {
|
|
488
519
|
const credential =
|
|
489
|
-
value("token") ||
|
|
520
|
+
value("token") ||
|
|
521
|
+
process.env.AGENTLINK_TOKEN ||
|
|
522
|
+
savedCredential(value("hub") || OFFICIAL_HUB);
|
|
490
523
|
if (!credential) throw new Error("这台电脑还没有凭证,请先运行 npx alink-cli 完成配对。");
|
|
491
524
|
await printQr(credential);
|
|
492
525
|
return;
|