alink-cli 0.7.3 → 0.7.5

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
@@ -9,14 +9,24 @@ npx alink-cli
9
9
  首次运行输入 AgentLink 账号和密码,这台电脑会自动加入账号并上线。之后直接重复 `npx alink-cli` 即可上线;密码不会保存到本机。
10
10
 
11
11
  ```bash
12
+ npm install -g alink-cli
13
+ alink-cli help
14
+ alink-cli login
15
+ alink-cli status
16
+ alink-cli machines
17
+ alink-cli logout
18
+
12
19
  npx alink-cli --hub wss://your-hub.example.com
13
20
  npx alink-cli --dir /path/to/project
14
- npx alink-cli logout
15
21
  ```
16
22
 
23
+ - 全局安装后可直接使用 `alink-cli`(兼容旧命令 `agentlink`)。
24
+ - `login`:登录账号并注册这台电脑,但不启动服务。
25
+ - `status`:查看当前登录状态和 Hub。
26
+ - `machines`:验证账号密码后查看账号下的所有机器,不展示内部凭证。
27
+ - `logout`:退出当前账号并清除本机登录记录,下次运行重新输入账号密码。
17
28
  - `--hub`:Hub 地址,默认 `wss://link.harmopath.com`。
18
29
  - `--dir`:默认工作目录,缺省为当前目录。
19
- - `logout`:退出当前账号并清除本机登录记录,下次运行重新输入账号密码。
20
30
  - `--legacy`:临时运行旧 daemon;默认始终是 daemon-t3。
21
31
 
22
32
  Node.js 22.16+。连接为出站 WebSocket,无需端口转发;Hub 只转发 E2EE 密文。
@@ -1,4 +1,9 @@
1
1
  import { randomBytes } from "node:crypto";
2
+ import { createRequire } from "node:module";
3
+
4
+ // CloudBase's Node adapter resolves optional dependencies through a runtime
5
+ // `require`; expose this package's resolver so its bundled `ws` dependency is found.
6
+ globalThis.require ??= createRequire(import.meta.url);
2
7
 
3
8
  const MACHINE_BUCKET = "agentlink-machines";
4
9
  const REQUEST_TIMEOUT_MS = 10_000;
@@ -131,3 +136,21 @@ export async function registerAgentLinkAccountMachine({
131
136
  await auth.signOut().catch(() => undefined);
132
137
  }
133
138
  }
139
+
140
+ export async function listAgentLinkAccountMachines({ config, identifier, password, cloudbaseSdk }) {
141
+ if (!config?.cloudbaseEnvId) throw new Error("当前 Hub 尚未配置账号机器目录。");
142
+ const sdk = cloudbaseSdk ?? (await import("@cloudbase/js-sdk")).default;
143
+ const app = sdk.init({ env: config.cloudbaseEnvId, region: "ap-shanghai" });
144
+ const auth = app.auth();
145
+ const signedIn = await auth.signInWithPassword(
146
+ identifier.includes("@") ? { email: identifier, password } : { username: identifier, password },
147
+ );
148
+ if (signedIn?.error) throw new Error(signedIn.error.message || "账号或密码错误。");
149
+ try {
150
+ const user = await auth.getCurrentUser();
151
+ if (!user?.uid) throw new Error("CloudBase 账号登录状态无效。");
152
+ return readMachines(app.storage.from(MACHINE_BUCKET), `${user.uid}/machines.json`);
153
+ } finally {
154
+ await auth.signOut().catch(() => undefined);
155
+ }
156
+ }
package/bin/agentlink.js CHANGED
@@ -9,6 +9,7 @@ import { fileURLToPath } from "node:url";
9
9
  import WebSocket from "ws";
10
10
 
11
11
  import {
12
+ listAgentLinkAccountMachines,
12
13
  loadAgentLinkAccountConfig,
13
14
  registerAgentLinkAccountMachine,
14
15
  } from "./agentlink-account.js";
@@ -17,6 +18,7 @@ const OFFICIAL_HUB = process.env.AGENTLINK_DEFAULT_HUB || "wss://link.harmopath.
17
18
  const STATE_DIR = process.env.AGENTLINK_STATE_DIR || join(homedir(), ".agentlink");
18
19
  const CREDENTIAL_FILE = join(STATE_DIR, "credential");
19
20
  const args = process.argv.slice(2);
21
+ const legacyCredentialCommands = process.env.AGENTLINK_ENABLE_LEGACY_CREDENTIALS === "1";
20
22
 
21
23
  function value(name) {
22
24
  const index = args.findIndex((arg) => arg === `--${name}` || arg.startsWith(`--${name}=`));
@@ -206,20 +208,61 @@ async function pair(hub) {
206
208
  });
207
209
  }
208
210
 
209
- if (args.includes("--help") || args.includes("-h")) {
211
+ if (args[0] === "help" || args.includes("--help") || args.includes("-h")) {
210
212
  console.log(
211
- `用法: npx alink-cli [--hub <wss://…>] [--dir <目录>]\n npx alink-cli logout\n\n首次运行输入 AgentLink 账号密码,这台电脑会自动加入账号;以后直接运行即可上线。\nlogout 退出当前账号;--legacy 临时运行旧 daemon。`,
213
+ `用法:\n alink-cli 登录(首次)并让这台电脑上线\n alink-cli login 登录账号,不启动服务\n alink-cli status 查看登录状态\n alink-cli machines 查看账号下的所有机器\n alink-cli logout 退出账号\n alink-cli help 查看帮助\n\n选项:\n --hub <wss://…> Hub 地址\n --dir <目录> 默认工作目录\n --legacy 临时运行旧 daemon`,
212
214
  );
213
215
  process.exit(0);
214
216
  }
215
217
 
218
+ if (args[0] === "status") {
219
+ const hub = value("hub") || OFFICIAL_HUB;
220
+ console.log(`[agentlink] 登录状态:${savedCredential(hub) ? "已登录" : "未登录"}`);
221
+ console.log(`[agentlink] Hub:${normalizeHub(hub)}`);
222
+ process.exit(0);
223
+ }
224
+
216
225
  if (args[0] === "logout") {
217
- rmSync(CREDENTIAL_FILE, { force: true });
218
- console.log("[agentlink] 已退出账号;下次启动时需要重新登录。");
226
+ const loggedIn = savedCredential(value("hub") || OFFICIAL_HUB);
227
+ if (loggedIn) rmSync(CREDENTIAL_FILE, { force: true });
228
+ console.log(
229
+ loggedIn ? "[agentlink] 已退出账号;下次启动时需要重新登录。" : "[agentlink] 当前未登录。",
230
+ );
231
+ process.exit(0);
232
+ }
233
+
234
+ if (args[0] === "login") {
235
+ const hub = value("hub") || OFFICIAL_HUB;
236
+ if (savedCredential(hub)) {
237
+ console.log("[agentlink] 已登录。需要切换或修复机器时,请先运行 alink-cli logout。");
238
+ process.exit(0);
239
+ }
240
+ const config = await loadAgentLinkAccountConfig(hub);
241
+ if (config?.mode !== "multi") throw new Error("当前 Hub 不使用账号登录。");
242
+ await loginAndCreateCredential(hub, config);
219
243
  process.exit(0);
220
244
  }
221
245
 
222
- if (args[0] === "credential") {
246
+ if (args[0] === "machines") {
247
+ const hub = value("hub") || OFFICIAL_HUB;
248
+ const config = await loadAgentLinkAccountConfig(hub);
249
+ if (config?.mode !== "multi") throw new Error("当前 Hub 不使用账号机器目录。");
250
+ console.log("[agentlink] 查看账号机器,请验证 AgentLink 账号。");
251
+ const { identifier, password } = await accountCredentials();
252
+ const machines = await listAgentLinkAccountMachines({ config, identifier, password });
253
+ if (machines.length === 0) console.log("[agentlink] 账号下还没有机器。");
254
+ else {
255
+ console.log(`[agentlink] 账号下共 ${machines.length} 台机器:`);
256
+ for (const machine of machines) {
257
+ console.log(
258
+ `- ${machine.name || machine.hostname || machine.machineId} (${machine.machineId})`,
259
+ );
260
+ }
261
+ }
262
+ process.exit(0);
263
+ }
264
+
265
+ if (args[0] === "credential" && legacyCredentialCommands) {
223
266
  const hub = value("hub") || OFFICIAL_HUB;
224
267
  const command = args[1] || "status";
225
268
  if (command === "path") console.log(CREDENTIAL_FILE);
@@ -231,7 +274,7 @@ if (args[0] === "credential") {
231
274
  process.exit(0);
232
275
  }
233
276
 
234
- if (args[0] === "qr") {
277
+ if (args[0] === "qr" && legacyCredentialCommands) {
235
278
  const credential =
236
279
  value("token") || process.env.AGENTLINK_TOKEN || savedCredential(value("hub") || OFFICIAL_HUB);
237
280
  if (!credential) throw new Error("这台电脑还没有凭证,请先运行 npx alink-cli 完成配对。");
@@ -239,6 +282,11 @@ if (args[0] === "qr") {
239
282
  process.exit(0);
240
283
  }
241
284
 
285
+ if (args[0] && !args[0].startsWith("-")) {
286
+ console.error(`[agentlink] 未知命令:${args[0]}。运行 alink-cli help 查看帮助。`);
287
+ process.exit(1);
288
+ }
289
+
242
290
  if (args.includes("--legacy")) {
243
291
  process.argv = process.argv.filter((arg) => arg !== "--legacy");
244
292
  await import(new URL("../dist/daemon.js", import.meta.url));
@@ -252,13 +300,13 @@ if (args.includes("--legacy")) {
252
300
  const credential =
253
301
  explicit ||
254
302
  (!args.includes("--pair") && savedCredential(hub)) ||
255
- (config?.mode === "multi"
256
- ? await loginAndCreateCredential(hub, config)
257
- : await createSingleCredential(hub));
303
+ (args.includes("--pair")
304
+ ? await pair(hub)
305
+ : config?.mode === "multi"
306
+ ? await loginAndCreateCredential(hub, config)
307
+ : await createSingleCredential(hub));
258
308
  if (explicit && explicit !== savedCredential(hub)) saveCredential(explicit, hub);
259
- if (!explicit && savedCredential(hub) && !args.includes("--pair")) {
260
- console.log("[agentlink] 已登录,正在让这台电脑上线。");
261
- }
309
+ if (!explicit && !args.includes("--pair")) console.log("[agentlink] 正在让这台电脑上线。");
262
310
  const { machineToken, enckey } = parseCredential(credential);
263
311
  const daemon = fileURLToPath(new URL("../dist/bin.mjs", import.meta.url));
264
312
  const cwd = value("dir") || process.cwd();