alink-cli 0.11.4 → 0.11.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 +1 -1
- package/bin/agentlink-account.js +4 -16
- package/bin/agentlink-prompts.js +0 -27
- package/bin/agentlink.js +10 -98
- package/dist/bin.mjs +13 -3
- package/dist/bin.mjs.map +1 -1
- package/dist/ui.js +6 -19
- package/package.json +1 -1
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
|
|
25
|
+
`alink-cli login` 会打开浏览器,支持使用 GitHub 或已有 AgentLink 账号登录并添加当前电脑。
|
|
26
26
|
|
|
27
27
|
[GitHub](https://github.com/baichen99/agentlink) · [提交问题](https://github.com/baichen99/agentlink/issues)
|
package/bin/agentlink-account.js
CHANGED
|
@@ -35,7 +35,7 @@ function normalizeMachineRow(row) {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
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
|
|
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 };
|
package/bin/agentlink-prompts.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
175
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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请在浏览器完成
|
|
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
|
|
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
|
@@ -84880,8 +84880,18 @@ const makeAcpPatchedProtocol = fn("makeAcpPatchedProtocol")(function* (options)
|
|
|
84880
84880
|
function isProtocolError(value) {
|
|
84881
84881
|
return typeof value === "object" && value !== null && "code" in value && typeof value.code === "number" && "message" in value && typeof value.message === "string";
|
|
84882
84882
|
}
|
|
84883
|
-
|
|
84884
|
-
|
|
84883
|
+
const PromptResponseCompat = Union([PromptResponse, Struct({
|
|
84884
|
+
_meta: optionalKey(NullOr(Record(String$1, Unknown))),
|
|
84885
|
+
stopReason: Literal(""),
|
|
84886
|
+
usage: optionalKey(NullOr(Usage)),
|
|
84887
|
+
userMessageId: optionalKey(NullOr(String$1))
|
|
84888
|
+
})]).pipe(decodeTo(toType(PromptResponse), transform$1({
|
|
84889
|
+
decode: (response) => response.stopReason === "" ? {
|
|
84890
|
+
...response,
|
|
84891
|
+
stopReason: "end_turn"
|
|
84892
|
+
} : response,
|
|
84893
|
+
encode: (response) => response
|
|
84894
|
+
})));
|
|
84885
84895
|
const InitializeRpc = make$52(AGENT_METHODS.initialize, {
|
|
84886
84896
|
payload: InitializeRequest,
|
|
84887
84897
|
success: InitializeResponse,
|
|
@@ -84929,7 +84939,7 @@ const CloseSessionRpc = make$52(AGENT_METHODS.session_close, {
|
|
|
84929
84939
|
});
|
|
84930
84940
|
const PromptRpc = make$52(AGENT_METHODS.session_prompt, {
|
|
84931
84941
|
payload: PromptRequest,
|
|
84932
|
-
success:
|
|
84942
|
+
success: PromptResponseCompat,
|
|
84933
84943
|
error: Error$1
|
|
84934
84944
|
});
|
|
84935
84945
|
const SetSessionModelRpc = make$52(AGENT_METHODS.session_set_model, {
|