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 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)
@@ -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 网页扫码,或打开下面的链接:\n");
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.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" }).end("登录成功,可以关闭此页面。");
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 = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
213
- const commandArgs = process.platform === "win32" ? ["/c", "start", "", url.toString()] : [url.toString()];
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 查看连接状态\n alink-cli machines 查看账号下的机器\n alink-cli logout 退出账号\n alink-cli help 查看帮助`,
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 = !running || !statusMatchesHub
310
- ? "未连接"
311
- : status.hubConnected
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" ? "账号或密码错误。" : login.error || "账号登录失败。",
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
- console.log(` ${onlineMarker} ${label} (${machine.machineId})`);
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 || savedCredential(hub) || args.includes("--pair")
397
- ? null
398
- : await loadAgentLinkAccountConfig(hub);
427
+ explicit || stored || args.includes("--pair") ? null : await loadAgentLinkAccountConfig(hub);
399
428
  const credential =
400
429
  explicit ||
401
- (!args.includes("--pair") && savedCredential(hub)) ||
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])) saveCredential(publicArgs[2], hub);
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") || process.env.AGENTLINK_TOKEN || savedCredential(value("hub") || OFFICIAL_HUB);
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;