alink-cli 0.11.12 → 0.11.14

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
@@ -1,5 +1,7 @@
1
1
  # AgentLink
2
2
 
3
+ **中文(默认)** · [English](#english)
4
+
3
5
  AgentLink 让你随时从手机或浏览器继续使用运行在自己电脑上的 Claude Code、Codex、Gemini、Qwen、Kimi 等编码 agent。
4
6
 
5
7
  **官网:[https://link.harmopath.com/marketing](https://link.harmopath.com/marketing)**
@@ -8,7 +10,7 @@ AgentLink 让你随时从手机或浏览器继续使用运行在自己电脑上
8
10
 
9
11
  - **Local-first:** agent、项目文件和任务记录都保存在你的电脑上。
10
12
  - **安全连接:** 设备之间端到端加密,Hub 不保存你的对话。
11
- - **跨设备继续:** 离开电脑后,仍可查看进度、回复消息和处理授权。
13
+ - **跨设备继续:** 离开电脑后,仍可查看进度、回复消息、处理授权、操作终端、访问本机服务和加密分享文件。
12
14
  - **一个账号:** 登录后自动发现自己的电脑,无需手动配置连接。
13
15
 
14
16
  ## 开始使用
@@ -20,10 +22,46 @@ npm install -g alink-cli
20
22
  alink
21
23
  ```
22
24
 
25
+ `alink` 是推荐的短命令别名;`agentlink` 和 `alink-cli` 也都支持。
26
+
23
27
  按提示登录,然后在手机或浏览器打开 [AgentLink 控制台](https://link.harmopath.com/login),即可开始任务或继续已有工作。
24
28
 
25
- `alink login` 会打开浏览器,支持使用 GitHub 或已有 AgentLink 账号登录并添加当前电脑。原命令 `alink-cli` 继续可用。
29
+ `alink login` 会打开浏览器,支持使用 GitHub 或已有 AgentLink 账号登录并添加当前电脑。脚本或排障时可用 `alink status --json` 和 `alink machines --json` 获取结构化状态。
30
+
31
+ ## 联系
32
+
33
+ 问题、建议或 TestFlight 申请:[imbistone@gmail.com](mailto:imbistone@gmail.com)
34
+
35
+ ---
36
+
37
+ ## English
38
+
39
+ AgentLink lets you continue using Claude Code, Codex, Gemini, Qwen, Kimi, and other coding agents running on your own computer from any phone or browser.
40
+
41
+ ### Runs locally, ready remotely
42
+
43
+ - **Local-first:** Your agents, project files, and task history stay on your computer.
44
+ - **Secure connection:** Devices use end-to-end encryption, and the Hub does not store your conversations.
45
+ - **Continue anywhere:** Follow progress, reply, approve actions, operate terminals, open local services, and share encrypted files while away from your computer.
46
+ - **One account:** Sign in to find your computers automatically, without manually configuring connections.
47
+
48
+ ### Get started
49
+
50
+ Node.js 22.16 or later is required.
51
+
52
+ ```bash
53
+ npm install -g alink-cli
54
+ alink
55
+ ```
56
+
57
+ `alink` is the recommended short alias. `agentlink` and `alink-cli` are also supported.
58
+
59
+ Follow the sign-in prompt, then open the [AgentLink console](https://link.harmopath.com/login) on your phone or browser to start a task or continue existing work.
60
+
61
+ Use `alink status --json` and `alink machines --json` for structured local connection and machine status in scripts or troubleshooting.
62
+
63
+ ### Contact
26
64
 
27
- 脚本或排障时可用 `alink status --json` `alink machines --json` 获取结构化的本机连接状态与账号机器数据。daemon 在网络中断、电脑唤醒或 Hub 更新后会自动退避重连,无需手动重启。
65
+ Questions, feedback, or TestFlight access: [imbistone@gmail.com](mailto:imbistone@gmail.com)
28
66
 
29
- [GitHub](https://github.com/baichen99/agentlink) · [提交问题](https://github.com/baichen99/agentlink/issues)
67
+ [Website](https://link.harmopath.com/marketing) · [GitHub](https://github.com/baichen99/agentlink) · [Issues](https://github.com/baichen99/agentlink/issues) · MIT
package/bin/agentlink.js CHANGED
@@ -62,6 +62,7 @@ const STATE_DIR = process.env.AGENTLINK_STATE_DIR || join(homedir(), ".agentlink
62
62
  const CREDENTIAL_FILE = join(STATE_DIR, "credential");
63
63
  const SESSION_FILE = join(STATE_DIR, "session");
64
64
  const STATUS_FILE = join(STATE_DIR, "status.json");
65
+ const CLI_VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
65
66
  const args = process.argv.slice(2);
66
67
  const legacyCredentialCommands = process.env.AGENTLINK_ENABLE_LEGACY_CREDENTIALS === "1";
67
68
  const forceTui = process.env.AGENTLINK_TUI === "1";
@@ -446,6 +447,7 @@ async function doNoTuiStart() {
446
447
  T3CODE_HUB_URL: hub,
447
448
  T3CODE_HUB_MACHINE_TOKEN: machineToken,
448
449
  T3CODE_HUB_ENCKEY: enckey,
450
+ AGENTLINK_CLI_VERSION: CLI_VERSION,
449
451
  },
450
452
  });
451
453
  for (const signal of ["SIGINT", "SIGTERM"]) process.on(signal, () => child.kill(signal));
package/dist/bin.mjs CHANGED
@@ -52047,6 +52047,9 @@ var PtyAdapter = class extends Service$2()("t3/terminal/PtyAdapter") {};
52047
52047
  //#region src/terminal/Manager.ts
52048
52048
  const DEFAULT_COLS = 120;
52049
52049
  const DEFAULT_ROWS = 30;
52050
+ function terminalLocale(environment, platform) {
52051
+ return environment.LC_ALL ?? environment.LC_CTYPE ?? environment.LANG ?? (platform === "darwin" ? "en_US.UTF-8" : "C.UTF-8");
52052
+ }
52050
52053
  var TerminalManager = class extends Service$2()("agentlink/terminal/TerminalManager") {};
52051
52054
  function sessionKey(threadId, terminalId) {
52052
52055
  return `${threadId}\0${terminalId}`;
@@ -52155,7 +52158,11 @@ const layer$7 = effect(TerminalManager, gen(function* () {
52155
52158
  env: {
52156
52159
  ...process.env,
52157
52160
  ...session.env,
52158
- TERM: "xterm-256color"
52161
+ TERM: "xterm-256color",
52162
+ LANG: terminalLocale({
52163
+ ...process.env,
52164
+ ...session.env
52165
+ }, process.platform)
52159
52166
  }
52160
52167
  }));
52161
52168
  if (result$1._tag === "Success") {
@@ -53292,6 +53299,12 @@ function hubHttpUrl(hub) {
53292
53299
  url.hash = "";
53293
53300
  return url.toString().replace(/\/$/, "");
53294
53301
  }
53302
+ function hubRegistration(cliVersion) {
53303
+ return cliVersion ? {
53304
+ type: "register",
53305
+ daemonVersion: cliVersion
53306
+ } : null;
53307
+ }
53295
53308
  function writeHubConnectionStatus(config, connected, stateDir = process.env.AGENTLINK_STATE_DIR || join(homedir(), ".agentlink")) {
53296
53309
  try {
53297
53310
  mkdirSync(stateDir, { recursive: true });
@@ -53306,6 +53319,7 @@ function writeHubConnectionStatus(config, connected, stateDir = process.env.AGEN
53306
53319
  state: connected ? "connected" : "reconnecting",
53307
53320
  hub: config.hubUrl,
53308
53321
  hubConnected: connected,
53322
+ ...process.env.AGENTLINK_CLI_VERSION ? { daemonVersion: process.env.AGENTLINK_CLI_VERSION } : {},
53309
53323
  machineId: machineIdFromToken(config.machineToken) ?? void 0,
53310
53324
  startedAt: typeof current.startedAt === "number" ? current.startedAt : Date.now(),
53311
53325
  ts: Date.now()
@@ -53714,7 +53728,10 @@ const runHubTunnel = (config) => gen(function* () {
53714
53728
  else dropTunnel(framed.tunnelId);
53715
53729
  return;
53716
53730
  }
53717
- }, { onOpen: logInfo("hub tunnel: connected to hub").pipe(tap(() => sync(() => writeHubConnectionStatus(config, true))), tap(() => sync(() => void runFork(upload)))) }).pipe(ensuring(sync(() => writeHubConnectionStatus(config, false))), tapDefect((cause) => logWarning$1("hub tunnel: hub socket defect", { cause: String(cause) })));
53731
+ }, { onOpen: logInfo("hub tunnel: connected to hub").pipe(tap(() => sync(() => {
53732
+ const registration = hubRegistration(process.env.AGENTLINK_CLI_VERSION);
53733
+ if (registration) writeHub(JSON.stringify(registration));
53734
+ })), tap(() => sync(() => writeHubConnectionStatus(config, true))), tap(() => sync(() => void runFork(upload)))) }).pipe(ensuring(sync(() => writeHubConnectionStatus(config, false))), tapDefect((cause) => logWarning$1("hub tunnel: hub socket defect", { cause: String(cause) })));
53718
53735
  yield* logWarning$1("hub tunnel: hub connection closed");
53719
53736
  return yield* fail(/* @__PURE__ */ new Error("hub tunnel closed"));
53720
53737
  }).pipe(scoped, tapError((error) => logWarning$1("hub tunnel: connection error", { error }))), {
@@ -88871,6 +88888,9 @@ effect(RuntimeReceiptBus, makeRuntimeReceiptBusTest);
88871
88888
  //#region src/orchestration/Layers/ProviderRuntimeIngestion.ts
88872
88889
  const providerTurnKey = (threadId, turnId) => `${threadId}:${turnId}`;
88873
88890
  const providerTaskKey = (threadId, taskId) => `${threadId}:${taskId}`;
88891
+ function providerReconciliationFailureDetail(cause) {
88892
+ return cause instanceof Error && cause.message.toLowerCase().includes("active writer") ? "对话正在另一端运行,请等待另一端结束后再继续。" : cause instanceof Error ? cause.message : "Provider snapshot sync failed.";
88893
+ }
88874
88894
  function findTaskTitleInActivities(activities, taskId) {
88875
88895
  if (!activities) return;
88876
88896
  for (let index = activities.length - 1; index >= 0; index -= 1) {
@@ -89880,7 +89900,9 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
89880
89900
  });
89881
89901
  const reconcileThreadUnlocked = fn("reconcileThreadUnlocked")(function* (threadId, options) {
89882
89902
  const thread = yield* resolveThreadShell(threadId);
89883
- if (!thread || thread.session?.providerName !== ProviderDriverKind.make("codex")) return;
89903
+ if (!thread) return;
89904
+ const session = thread.session;
89905
+ if (session?.providerName !== ProviderDriverKind.make("codex")) return;
89884
89906
  const snapshot = yield* providerService.readThread(threadId);
89885
89907
  for (const turn of snapshot.turns) {
89886
89908
  const existing = getOrUndefined(yield* projectionTurnRepository.getByTurnId({
@@ -89892,7 +89914,7 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
89892
89914
  if (isTerminal) {
89893
89915
  const pendingTurnStart = yield* projectionTurnRepository.getPendingTurnStartByThreadId({ threadId });
89894
89916
  if (!(existing?.pendingMessageId !== null && existing?.pendingMessageId !== void 0 ? true : isSome(pendingTurnStart))) {
89895
- const createdAt = turn.startedAt ?? turn.runtimeEvents?.[0]?.createdAt ?? thread.session.updatedAt;
89917
+ const createdAt = turn.startedAt ?? turn.runtimeEvents?.[0]?.createdAt ?? session.updatedAt;
89896
89918
  const providerThreadId = snapshot.providerThreadId ?? String(threadId);
89897
89919
  for (const userMessage of turn.userMessages ?? []) yield* orchestrationEngine.dispatch({
89898
89920
  type: "thread.message.user.import",
@@ -89912,11 +89934,27 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
89912
89934
  });
89913
89935
  }
89914
89936
  if (options?.requireWritable !== true) return;
89915
- yield* providerService.ensureSession(threadId);
89937
+ yield* providerService.ensureSession(threadId).pipe(tapError((cause) => gen(function* () {
89938
+ const updatedAt = formatIso(yield* now$1);
89939
+ const detail = providerReconciliationFailureDetail(cause);
89940
+ yield* orchestrationEngine.dispatch({
89941
+ type: "thread.session.set",
89942
+ commandId: CommandId.make(`provider-writer-acquisition:${threadId}:${yield* crypto.randomUUIDv4}`),
89943
+ threadId,
89944
+ session: {
89945
+ ...session,
89946
+ status: "error",
89947
+ activeTurnId: null,
89948
+ lastError: detail,
89949
+ updatedAt
89950
+ },
89951
+ createdAt: updatedAt
89952
+ });
89953
+ })));
89916
89954
  });
89917
89955
  const reconcileThread = (threadId, options) => withThreadLock(threadId, reconcileThreadUnlocked(threadId, options)).pipe(mapError((cause) => cause instanceof ProviderRuntimeReconciliationError ? cause : new ProviderRuntimeReconciliationError({
89918
89956
  threadId,
89919
- detail: cause instanceof Error && cause.message.toLowerCase().includes("active writer") ? "对话正在另一端运行,请等待另一端结束后再继续。" : cause instanceof Error ? cause.message : "Provider snapshot sync failed.",
89957
+ detail: providerReconciliationFailureDetail(cause),
89920
89958
  cause
89921
89959
  })));
89922
89960
  const processDomainEvent = (_event) => void_$1;