alink-cli 0.11.4 → 0.11.6

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
@@ -22,6 +22,6 @@ alink-cli
22
22
 
23
23
  按提示登录,然后在手机或浏览器打开 [AgentLink 控制台](https://link.harmopath.com/login),即可开始任务或继续已有工作。
24
24
 
25
- `alink-cli login` 会打开浏览器,支持使用 GitHub 或已有 AgentLink 账号登录并添加当前电脑;同一账号重新链接时会复用本机已有机器身份,不会重复添加。本机身份文件已经丢失时,用 `alink-cli relink` 选择账号下已有的离线机器。
25
+ `alink-cli login` 会打开浏览器,支持使用 GitHub 或已有 AgentLink 账号登录并添加当前电脑。
26
26
 
27
27
  [GitHub](https://github.com/baichen99/agentlink) · [提交问题](https://github.com/baichen99/agentlink/issues)
@@ -35,7 +35,7 @@ function normalizeMachineRow(row) {
35
35
  };
36
36
  }
37
37
 
38
- export function machineIdFromToken(authToken) {
38
+ function machineIdFromToken(authToken) {
39
39
  try {
40
40
  const payload = JSON.parse(
41
41
  Buffer.from(authToken.split(".")[1] || "", "base64url").toString("utf8"),
@@ -60,7 +60,6 @@ export async function registerAgentLinkAccountMachine({
60
60
  identifier,
61
61
  password,
62
62
  machineName,
63
- preferredMachineId,
64
63
  fetchFn = fetch,
65
64
  cloudbaseSdk,
66
65
  randomBytesFn = randomBytes,
@@ -90,7 +89,7 @@ export async function registerAgentLinkAccountMachine({
90
89
  "Content-Type": "application/json",
91
90
  "X-Requested-With": "agentlink",
92
91
  },
93
- body: JSON.stringify({ name: machineName, ...(preferredMachineId ? { machineId: preferredMachineId } : {}) }),
92
+ body: JSON.stringify({ name: machineName }),
94
93
  });
95
94
  const minted = await json(mintResponse);
96
95
  const authToken = typeof minted.authToken === "string" ? minted.authToken : "";
@@ -145,7 +144,6 @@ export async function registerAgentLinkSessionMachine({
145
144
  hub,
146
145
  jwt,
147
146
  machineName,
148
- preferredMachineId,
149
147
  fetchFn = fetch,
150
148
  randomBytesFn = randomBytes,
151
149
  }) {
@@ -158,20 +156,10 @@ export async function registerAgentLinkSessionMachine({
158
156
  "Content-Type": "application/json",
159
157
  "X-Requested-With": "agentlink",
160
158
  },
161
- body: JSON.stringify({
162
- name: machineName,
163
- enckey,
164
- ...(preferredMachineId ? { machineId: preferredMachineId } : {}),
165
- }),
159
+ body: JSON.stringify({ name: machineName, enckey }),
166
160
  });
167
161
  const minted = await json(response);
168
- if (
169
- !response.ok ||
170
- typeof minted.authToken !== "string" ||
171
- !minted.authToken.startsWith("al1.") ||
172
- typeof minted.machineId !== "string" ||
173
- !minted.machineId
174
- ) {
162
+ if (!response.ok || typeof minted.authToken !== "string" || !minted.authToken.startsWith("al1.")) {
175
163
  throw new Error(minted.error || "机器注册失败。");
176
164
  }
177
165
  return { credential: `${minted.authToken}.${enckey}`, machineId: minted.machineId, jwt };
@@ -58,30 +58,3 @@ export async function accountCredentials() {
58
58
  if (!identifier || !password) throw new Error("账号和密码不能为空。");
59
59
  return { identifier, password };
60
60
  }
61
-
62
- export async function chooseExistingMachine(machines, questionFn) {
63
- const available = machines.filter((machine) => !machine.online);
64
- if (available.length === 0) return undefined;
65
-
66
- process.stdout.write("\n发现账号下已有的离线机器:\n");
67
- for (const [index, machine] of available.entries()) {
68
- const label = machine.name || machine.hostname || machine.machineId;
69
- process.stdout.write(` ${index + 1}. ${label} (${machine.machineId})\n`);
70
- }
71
- process.stdout.write(" 0. 这是新机器\n");
72
-
73
- let readline;
74
- const ask = questionFn ?? (async (label) => {
75
- readline = createInterface({ input: process.stdin, output: process.stdout });
76
- return readline.question(label);
77
- });
78
- try {
79
- const answer = (await ask("选择要重新链接的机器 [0]:")).trim();
80
- if (!answer || answer === "0") return undefined;
81
- const selected = available[Number(answer) - 1];
82
- if (!selected || !/^\d+$/.test(answer)) throw new Error("机器选择无效,请重新登录。");
83
- return selected.machineId;
84
- } finally {
85
- readline?.close();
86
- }
87
- }
package/bin/agentlink.js CHANGED
@@ -14,11 +14,10 @@ import {
14
14
  listAgentLinkAccountMachines,
15
15
  listAgentLinkAccountMachinesFromHub,
16
16
  loadAgentLinkAccountConfig,
17
- machineIdFromToken,
18
17
  registerAgentLinkAccountMachine,
19
18
  registerAgentLinkSessionMachine,
20
19
  } from "./agentlink-account.js";
21
- import { accountCredentials, chooseExistingMachine } from "./agentlink-prompts.js";
20
+ import { accountCredentials } from "./agentlink-prompts.js";
22
21
 
23
22
  function savedSessionJwt(hub = OFFICIAL_HUB) {
24
23
  try {
@@ -61,7 +60,6 @@ function clearSessionJwt(hub = OFFICIAL_HUB) {
61
60
  const OFFICIAL_HUB = process.env.AGENTLINK_DEFAULT_HUB || "wss://link.harmopath.com";
62
61
  const STATE_DIR = process.env.AGENTLINK_STATE_DIR || join(homedir(), ".agentlink");
63
62
  const CREDENTIAL_FILE = join(STATE_DIR, "credential");
64
- const MACHINE_ID_FILE = join(STATE_DIR, "machine-id");
65
63
  const SESSION_FILE = join(STATE_DIR, "session");
66
64
  const args = process.argv.slice(2);
67
65
  const legacyCredentialCommands = process.env.AGENTLINK_ENABLE_LEGACY_CREDENTIALS === "1";
@@ -120,26 +118,6 @@ function saveCredential(credential, hub = OFFICIAL_HUB) {
120
118
  chmodSync(CREDENTIAL_FILE, 0o600);
121
119
  }
122
120
 
123
- function savedMachineId(hub = OFFICIAL_HUB) {
124
- try {
125
- const saved = JSON.parse(readFileSync(MACHINE_ID_FILE, "utf8"));
126
- return saved.hub === normalizeHub(hub) && typeof saved.machineId === "string"
127
- ? saved.machineId
128
- : undefined;
129
- } catch {
130
- return undefined;
131
- }
132
- }
133
-
134
- function saveMachineId(machineId, hub = OFFICIAL_HUB) {
135
- if (!machineId) return;
136
- mkdirSync(STATE_DIR, { recursive: true });
137
- writeFileSync(MACHINE_ID_FILE, JSON.stringify({ hub: normalizeHub(hub), machineId }), {
138
- mode: 0o600,
139
- });
140
- chmodSync(MACHINE_ID_FILE, 0o600);
141
- }
142
-
143
121
  function parseCredential(credential) {
144
122
  const parts = credential.split(".");
145
123
  if (parts.length === 3 && parts[0] === "als1" && parts.slice(1).every(Boolean)) {
@@ -171,31 +149,17 @@ async function createSingleCredential(hub) {
171
149
 
172
150
  async function loginAndCreateCredential(hub, config) {
173
151
  const envLogin = process.env.AGENTLINK_ACCOUNT && process.env.AGENTLINK_PASSWORD;
174
- let preferredMachineId = savedMachineId(hub);
175
- let result;
176
- if (envLogin) {
177
- result = await registerAgentLinkAccountMachine({
152
+ const result = envLogin
153
+ ? await registerAgentLinkAccountMachine({
178
154
  hub,
179
155
  config,
180
156
  ...(await accountCredentials()),
181
157
  machineName: hostname(),
182
- preferredMachineId,
183
- });
184
- } else {
185
- const jwt = await browserLogin(hub);
186
- if (!preferredMachineId && process.stdin.isTTY) {
187
- const machines = await listAgentLinkAccountMachinesFromHub({ hub, jwt });
188
- preferredMachineId = await chooseExistingMachine(machines);
189
- }
190
- result = await registerAgentLinkSessionMachine({
191
- hub,
192
- jwt,
193
- machineName: hostname(),
194
- preferredMachineId,
195
- });
196
- }
158
+ })
159
+ : await browserLogin(hub).then((jwt) =>
160
+ registerAgentLinkSessionMachine({ hub, jwt, machineName: hostname() }),
161
+ );
197
162
  saveCredential(result.credential, hub);
198
- saveMachineId(result.machineId, hub);
199
163
  if (result.jwt) saveSessionJwt(result.jwt, hub);
200
164
  console.log("[agentlink] 登录成功,这台电脑已加入账号。");
201
165
  return result.credential;
@@ -243,7 +207,7 @@ async function browserLogin(hub) {
243
207
  const url = httpUrl(hub, "/login");
244
208
  url.searchParams.set("cli_callback", `http://127.0.0.1:${address.port}/callback`);
245
209
  url.searchParams.set("cli_state", state);
246
- console.log(`\n请在浏览器完成 AgentLink 账号登录:\n${url}\n`);
210
+ console.log(`\n请在浏览器完成 GitHub 登录:\n${url}\n`);
247
211
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
248
212
  const commandArgs = process.platform === "win32" ? ["/c", "start", "", url.toString()] : [url.toString()];
249
213
  const opener = spawn(command, commandArgs, { detached: true, stdio: "ignore" });
@@ -309,7 +273,7 @@ async function pair(hub) {
309
273
 
310
274
  async function showHelp() {
311
275
  console.log(
312
- `用法:\n alink-cli 进入交互式 TUI(默认)\n alink-cli --no-tui 跳过 TUI,按旧行为启动 daemon\n alink-cli login 登录账号\n alink-cli relink 选择账号下已有的离线机器身份\n alink-cli status 查看登录状态\n alink-cli machines 查看账号下的所有机器\n alink-cli logout 退出账号\n alink-cli --legacy 临时运行旧 daemon\n alink-cli help 查看帮助\n\n选项:\n --hub <wss://…> Hub 地址\n --dir <目录> 默认工作目录\n --machine <machineId> 指定要重新链接的已有机器\n --no-tui 禁用交互式 TUI`,
276
+ `用法:\n alink-cli 进入交互式 TUI(默认)\n alink-cli --no-tui 跳过 TUI,按旧行为启动 daemon\n alink-cli login 登录账号\n alink-cli status 查看登录状态\n alink-cli machines 查看账号下的所有机器\n alink-cli logout 退出账号\n alink-cli --legacy 临时运行旧 daemon\n alink-cli help 查看帮助\n\n选项:\n --hub <wss://…> Hub 地址\n --dir <目录> 默认工作目录\n --no-tui 禁用交互式 TUI`,
313
277
  );
314
278
  }
315
279
 
@@ -332,11 +296,7 @@ async function showStatus() {
332
296
  async function doLogout() {
333
297
  const hub = value("hub") || OFFICIAL_HUB;
334
298
  const loggedIn = savedCredential(hub);
335
- if (loggedIn) {
336
- const { machineToken } = parseCredential(loggedIn);
337
- saveMachineId(machineIdFromToken(machineToken), hub);
338
- rmSync(CREDENTIAL_FILE, { force: true });
339
- }
299
+ if (loggedIn) rmSync(CREDENTIAL_FILE, { force: true });
340
300
  clearSessionJwt(hub);
341
301
  console.log(
342
302
  loggedIn ? "[agentlink] 已退出账号;下次启动时需要重新登录。" : "[agentlink] 当前未登录。",
@@ -401,49 +361,6 @@ async function doMachines() {
401
361
  }
402
362
  }
403
363
 
404
- async function doRelink() {
405
- const hub = value("hub") || OFFICIAL_HUB;
406
- const config = await loadAgentLinkAccountConfig(hub);
407
- if (config?.mode !== "multi") throw new Error("当前 Hub 不使用账号机器目录。");
408
- const credential = savedCredential(hub);
409
- if (!credential) throw new Error("请先运行 alink-cli login 登录账号。");
410
- const jwt = savedSessionJwt(hub) || await exchangeCredentialForSessionJwt({ hub, credential });
411
- saveSessionJwt(jwt, hub);
412
- const currentMachineId = machineIdFromToken(parseCredential(credential).machineToken);
413
- const machines = await listAgentLinkAccountMachinesFromHub({ hub, jwt });
414
- const candidates = machines.filter((machine) => machine.machineId !== currentMachineId);
415
- const requested = value("machine");
416
- let target;
417
- if (requested) {
418
- const matches = candidates.filter((machine) =>
419
- machine.machineId === requested || machine.name === requested || machine.hostname === requested
420
- );
421
- if (matches.length !== 1) throw new Error("未找到唯一匹配的已有机器,请运行 alink-cli machines 查看 machineId。");
422
- [target] = matches;
423
- } else {
424
- const selected = await chooseExistingMachine(candidates);
425
- target = candidates.find((machine) => machine.machineId === selected);
426
- }
427
- if (!target) {
428
- console.log("[agentlink] 未更改机器身份。");
429
- return;
430
- }
431
- if (target.online) throw new Error("目标机器当前在线;为避免替换另一台 daemon,本次未重新链接。");
432
-
433
- const result = await registerAgentLinkSessionMachine({
434
- hub,
435
- jwt,
436
- machineName: hostname(),
437
- preferredMachineId: target.machineId,
438
- });
439
- if (result.machineId !== target.machineId) throw new Error("Hub 未复用所选机器身份,本次未保存凭证。");
440
- saveCredential(result.credential, hub);
441
- saveMachineId(result.machineId, hub);
442
- console.log(
443
- `[agentlink] 已选择已有机器 ${target.name || target.hostname || target.machineId}。重新启动 daemon 后生效;旧对话仍保存在本机。`,
444
- );
445
- }
446
-
447
364
  async function doLegacy() {
448
365
  process.argv = process.argv.filter((arg) => arg !== "--legacy");
449
366
  await import(new URL("../dist/daemon.js", import.meta.url));
@@ -527,11 +444,6 @@ async function main() {
527
444
  return;
528
445
  }
529
446
 
530
- if (publicArgs[0] === "relink") {
531
- await doRelink();
532
- return;
533
- }
534
-
535
447
  if (publicArgs[0] === "machines") {
536
448
  await doMachines();
537
449
  return;
package/dist/bin.mjs CHANGED
@@ -84882,6 +84882,20 @@ function isProtocolError(value) {
84882
84882
  }
84883
84883
  //#endregion
84884
84884
  //#region ../effect-acp/src/rpc.ts
84885
+ const NonCanonicalPromptStopReasonResponse = Struct({
84886
+ _meta: optionalKey(NullOr(Record(String$1, Unknown))),
84887
+ stopReason: Unknown,
84888
+ usage: optionalKey(NullOr(Usage)),
84889
+ userMessageId: optionalKey(NullOr(String$1))
84890
+ });
84891
+ const isCanonicalPromptResponse = is(PromptResponse);
84892
+ const PromptResponseCompat = Union([PromptResponse, NonCanonicalPromptStopReasonResponse]).pipe(decodeTo(toType(PromptResponse), transform$1({
84893
+ decode: (response) => isCanonicalPromptResponse(response) ? response : {
84894
+ ...response,
84895
+ stopReason: "end_turn"
84896
+ },
84897
+ encode: (response) => response
84898
+ })));
84885
84899
  const InitializeRpc = make$52(AGENT_METHODS.initialize, {
84886
84900
  payload: InitializeRequest,
84887
84901
  success: InitializeResponse,
@@ -84929,7 +84943,7 @@ const CloseSessionRpc = make$52(AGENT_METHODS.session_close, {
84929
84943
  });
84930
84944
  const PromptRpc = make$52(AGENT_METHODS.session_prompt, {
84931
84945
  payload: PromptRequest,
84932
- success: PromptResponse,
84946
+ success: PromptResponseCompat,
84933
84947
  error: Error$1
84934
84948
  });
84935
84949
  const SetSessionModelRpc = make$52(AGENT_METHODS.session_set_model, {