alink-cli 0.10.2 → 0.11.1
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 +16 -44
- package/bin/agentlink-account.js +25 -0
- package/bin/agentlink.js +65 -9
- package/dist/bin.mjs +82 -15
- package/dist/bin.mjs.map +1 -1
- package/dist/daemon.js +11 -17
- package/dist/ui.js +49 -82
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,55 +1,27 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AgentLink
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AgentLink 让你随时从手机或浏览器继续使用运行在自己电脑上的 Claude Code、Codex、Gemini、Qwen、Kimi 等编码 agent。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**官网:[https://link.harmopath.com/marketing](https://link.harmopath.com/marketing)**
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## 本地运行,随时继续
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm install -g alink-cli
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
2. 启动 AgentLink,并按提示登录
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
alink-cli
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
3. 在手机或浏览器打开 [AgentLink 控制台](https://link.harmopath.com/login)
|
|
24
|
-
|
|
25
|
-
登录同一个账号,你的电脑会自动出现。选择 agent 和工作目录,就可以开始任务或继续已有工作。
|
|
9
|
+
- **Local-first:** agent、项目文件和任务记录都保存在你的电脑上。
|
|
10
|
+
- **安全连接:** 设备之间端到端加密,Hub 不保存你的对话。
|
|
11
|
+
- **跨设备继续:** 离开电脑后,仍可查看进度、回复消息和处理授权。
|
|
12
|
+
- **一个账号:** 登录后自动发现自己的电脑,无需手动配置连接。
|
|
26
13
|
|
|
27
|
-
|
|
14
|
+
## 开始使用
|
|
28
15
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
对于已经由 AgentLink 绑定的 Codex 原生会话,AgentLink 会先只读同步 Codex 中的终态消息。Codex 释放写入权后,从 AgentLink 发送的新消息会恢复同一个原生 thread,不会另建分叉会话。
|
|
32
|
-
|
|
33
|
-
当前不会扫描或自动导入只在 Codex App 中新建、尚未绑定到 AgentLink 的会话;这类会话请先从 AgentLink 新建或绑定后再继续。
|
|
34
|
-
|
|
35
|
-
## 为什么是 local-first
|
|
36
|
-
|
|
37
|
-
- **本地运行:** agent 和命令都在你的电脑上执行。
|
|
38
|
-
- **本地保存:** AgentLink 不会把你的项目复制到云端,任务记录保存在本机。
|
|
39
|
-
- **安全连接:** 设备之间端到端加密;Hub 只转发连接,不保存对话内容。
|
|
40
|
-
- **账号隔离:** 只有登录你账号的设备才能看到你的电脑。
|
|
41
|
-
|
|
42
|
-
## 常用命令
|
|
16
|
+
需要 Node.js 22.16 或更高版本。
|
|
43
17
|
|
|
44
18
|
```bash
|
|
45
|
-
alink-cli
|
|
46
|
-
alink-cli
|
|
47
|
-
alink-cli status # 查看连接状态
|
|
48
|
-
alink-cli machines # 查看账号下的电脑
|
|
49
|
-
alink-cli logout # 退出登录
|
|
19
|
+
npm install -g alink-cli
|
|
20
|
+
alink-cli
|
|
50
21
|
```
|
|
51
22
|
|
|
52
|
-
|
|
23
|
+
按提示登录,然后在手机或浏览器打开 [AgentLink 控制台](https://link.harmopath.com/login),即可开始任务或继续已有工作。
|
|
24
|
+
|
|
25
|
+
`alink-cli login` 会打开浏览器,支持使用 GitHub 或已有 AgentLink 账号登录并添加当前电脑。
|
|
53
26
|
|
|
54
|
-
|
|
55
|
-
- [提交问题](https://github.com/baichen99/agentlink/issues)
|
|
27
|
+
[GitHub](https://github.com/baichen99/agentlink) · [提交问题](https://github.com/baichen99/agentlink/issues)
|
package/bin/agentlink-account.js
CHANGED
|
@@ -140,6 +140,31 @@ export async function registerAgentLinkAccountMachine({
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
export async function registerAgentLinkSessionMachine({
|
|
144
|
+
hub,
|
|
145
|
+
jwt,
|
|
146
|
+
machineName,
|
|
147
|
+
fetchFn = fetch,
|
|
148
|
+
randomBytesFn = randomBytes,
|
|
149
|
+
}) {
|
|
150
|
+
const enckey = randomBytesFn(32).toString("base64url");
|
|
151
|
+
const response = await fetchFn(httpUrl(hub, "/api/machines/mint"), {
|
|
152
|
+
method: "POST",
|
|
153
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
154
|
+
headers: {
|
|
155
|
+
Authorization: `Bearer ${jwt}`,
|
|
156
|
+
"Content-Type": "application/json",
|
|
157
|
+
"X-Requested-With": "agentlink",
|
|
158
|
+
},
|
|
159
|
+
body: JSON.stringify({ name: machineName, enckey }),
|
|
160
|
+
});
|
|
161
|
+
const minted = await json(response);
|
|
162
|
+
if (!response.ok || typeof minted.authToken !== "string" || !minted.authToken.startsWith("al1.")) {
|
|
163
|
+
throw new Error(minted.error || "机器注册失败。");
|
|
164
|
+
}
|
|
165
|
+
return { credential: `${minted.authToken}.${enckey}`, machineId: minted.machineId, jwt };
|
|
166
|
+
}
|
|
167
|
+
|
|
143
168
|
export async function listAgentLinkAccountMachinesFromHub({ hub, jwt, fetchFn = fetch }) {
|
|
144
169
|
if (!jwt) throw new Error("没有可用的登录会话,请先运行 alink-cli login。");
|
|
145
170
|
const response = await fetchFn(httpUrl(hub, "/api/machines"), {
|
package/bin/agentlink.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { createServer } from "node:http";
|
|
4
5
|
import { existsSync } from "node:fs";
|
|
5
6
|
import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
7
|
import { hostname, homedir } from "node:os";
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
listAgentLinkAccountMachinesFromHub,
|
|
15
16
|
loadAgentLinkAccountConfig,
|
|
16
17
|
registerAgentLinkAccountMachine,
|
|
18
|
+
registerAgentLinkSessionMachine,
|
|
17
19
|
} from "./agentlink-account.js";
|
|
18
20
|
import { accountCredentials } from "./agentlink-prompts.js";
|
|
19
21
|
|
|
@@ -146,21 +148,75 @@ async function createSingleCredential(hub) {
|
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
async function loginAndCreateCredential(hub, config) {
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
const envLogin = process.env.AGENTLINK_ACCOUNT && process.env.AGENTLINK_PASSWORD;
|
|
152
|
+
const result = envLogin
|
|
153
|
+
? await registerAgentLinkAccountMachine({
|
|
154
|
+
hub,
|
|
155
|
+
config,
|
|
156
|
+
...(await accountCredentials()),
|
|
157
|
+
machineName: hostname(),
|
|
158
|
+
})
|
|
159
|
+
: await browserLogin(hub).then((jwt) =>
|
|
160
|
+
registerAgentLinkSessionMachine({ hub, jwt, machineName: hostname() }),
|
|
161
|
+
);
|
|
158
162
|
saveCredential(result.credential, hub);
|
|
159
163
|
if (result.jwt) saveSessionJwt(result.jwt, hub);
|
|
160
164
|
console.log("[agentlink] 登录成功,这台电脑已加入账号。");
|
|
161
165
|
return result.credential;
|
|
162
166
|
}
|
|
163
167
|
|
|
168
|
+
async function browserLogin(hub) {
|
|
169
|
+
const state = randomBytes(24).toString("base64url");
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
const server = createServer((req, res) => {
|
|
172
|
+
if (req.method !== "POST") {
|
|
173
|
+
res.writeHead(405).end();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
let body = "";
|
|
177
|
+
req.setEncoding("utf8");
|
|
178
|
+
req.on("data", (chunk) => {
|
|
179
|
+
body += chunk;
|
|
180
|
+
if (body.length > 16_384) req.destroy();
|
|
181
|
+
});
|
|
182
|
+
req.on("end", () => {
|
|
183
|
+
const value = new URLSearchParams(body);
|
|
184
|
+
const jwt = value.get("jwt");
|
|
185
|
+
if (value.get("state") !== state || !jwt) {
|
|
186
|
+
res.writeHead(400, { "Content-Type": "text/plain; charset=utf-8" }).end("登录请求无效。");
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" }).end("登录成功,可以关闭此页面。");
|
|
190
|
+
clearTimeout(timer);
|
|
191
|
+
server.close();
|
|
192
|
+
resolve(jwt);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
const timer = setTimeout(() => {
|
|
196
|
+
server.close();
|
|
197
|
+
reject(new Error("浏览器登录超时,请重试。"));
|
|
198
|
+
}, 10 * 60_000);
|
|
199
|
+
server.listen(0, "127.0.0.1", () => {
|
|
200
|
+
const address = server.address();
|
|
201
|
+
if (!address || typeof address === "string") {
|
|
202
|
+
clearTimeout(timer);
|
|
203
|
+
server.close();
|
|
204
|
+
reject(new Error("无法启动本地登录回调。"));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const url = httpUrl(hub, "/login");
|
|
208
|
+
url.searchParams.set("cli_callback", `http://127.0.0.1:${address.port}/callback`);
|
|
209
|
+
url.searchParams.set("cli_state", state);
|
|
210
|
+
console.log(`\n请在浏览器完成 GitHub 登录:\n${url}\n`);
|
|
211
|
+
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
212
|
+
const commandArgs = process.platform === "win32" ? ["/c", "start", "", url.toString()] : [url.toString()];
|
|
213
|
+
const opener = spawn(command, commandArgs, { detached: true, stdio: "ignore" });
|
|
214
|
+
opener.on("error", () => undefined);
|
|
215
|
+
opener.unref();
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
164
220
|
async function printQr(text) {
|
|
165
221
|
const { renderQr } = await import(new URL("../dist/qr.js", import.meta.url));
|
|
166
222
|
renderQr(text);
|
package/dist/bin.mjs
CHANGED
|
@@ -13702,6 +13702,7 @@ const CLAUDE_DRIVER_KIND = ProviderDriverKind.make("claudeAgent");
|
|
|
13702
13702
|
const GEMINI_DRIVER_KIND = ProviderDriverKind.make("gemini");
|
|
13703
13703
|
const QWEN_DRIVER_KIND = ProviderDriverKind.make("qwen");
|
|
13704
13704
|
const KIMI_DRIVER_KIND = ProviderDriverKind.make("kimi");
|
|
13705
|
+
const JOYCODE_DRIVER_KIND = ProviderDriverKind.make("joycode");
|
|
13705
13706
|
const DEFAULT_MODEL = "gpt-5.6-sol";
|
|
13706
13707
|
/**
|
|
13707
13708
|
* Codex default-model preference, most preferred first. The provider snapshot
|
|
@@ -13715,7 +13716,8 @@ const DEFAULT_MODEL_BY_PROVIDER = {
|
|
|
13715
13716
|
[CLAUDE_DRIVER_KIND]: "claude-sonnet-5",
|
|
13716
13717
|
[GEMINI_DRIVER_KIND]: "gemini-2.5-pro",
|
|
13717
13718
|
[QWEN_DRIVER_KIND]: "qwen3-coder-plus",
|
|
13718
|
-
[KIMI_DRIVER_KIND]: "kimi-k2"
|
|
13719
|
+
[KIMI_DRIVER_KIND]: "kimi-k2",
|
|
13720
|
+
[JOYCODE_DRIVER_KIND]: "default"
|
|
13719
13721
|
};
|
|
13720
13722
|
/** Per-provider text generation model defaults. */
|
|
13721
13723
|
const DEFAULT_TEXT_GENERATION_MODEL_BY_PROVIDER = {
|
|
@@ -13723,7 +13725,8 @@ const DEFAULT_TEXT_GENERATION_MODEL_BY_PROVIDER = {
|
|
|
13723
13725
|
[CLAUDE_DRIVER_KIND]: "claude-haiku-4-5",
|
|
13724
13726
|
[GEMINI_DRIVER_KIND]: "gemini-2.5-flash",
|
|
13725
13727
|
[QWEN_DRIVER_KIND]: "qwen3-coder-flash",
|
|
13726
|
-
[KIMI_DRIVER_KIND]: "kimi-k2"
|
|
13728
|
+
[KIMI_DRIVER_KIND]: "kimi-k2",
|
|
13729
|
+
[JOYCODE_DRIVER_KIND]: "default"
|
|
13727
13730
|
};
|
|
13728
13731
|
const MODEL_SLUG_ALIASES_BY_PROVIDER = {
|
|
13729
13732
|
[CODEX_DRIVER_KIND]: {
|
|
@@ -13760,7 +13763,8 @@ const MODEL_SLUG_ALIASES_BY_PROVIDER = {
|
|
|
13760
13763
|
},
|
|
13761
13764
|
[GEMINI_DRIVER_KIND]: {},
|
|
13762
13765
|
[QWEN_DRIVER_KIND]: {},
|
|
13763
|
-
[KIMI_DRIVER_KIND]: {}
|
|
13766
|
+
[KIMI_DRIVER_KIND]: {},
|
|
13767
|
+
[JOYCODE_DRIVER_KIND]: {}
|
|
13764
13768
|
};
|
|
13765
13769
|
//#endregion
|
|
13766
13770
|
//#region ../t3-contracts/src/orchestration.ts
|
|
@@ -20758,7 +20762,7 @@ const ClaudeSettings = makeProviderSettingsSchema({
|
|
|
20758
20762
|
] });
|
|
20759
20763
|
/**
|
|
20760
20764
|
* Shared shape for ACP-based agents (agentlink fork additions): Gemini CLI,
|
|
20761
|
-
* Qwen Code, Kimi CLI. Each only needs an enable switch, a binary path, and
|
|
20765
|
+
* Qwen Code, Kimi CLI, JoyCode. Each only needs an enable switch, a binary path, and
|
|
20762
20766
|
* a custom-model list — everything else is negotiated over ACP at runtime.
|
|
20763
20767
|
*/
|
|
20764
20768
|
const makeAcpAgentSettingsSchema = (input) => makeProviderSettingsSchema({
|
|
@@ -20788,6 +20792,11 @@ const KimiSettings = makeAcpAgentSettingsSchema({
|
|
|
20788
20792
|
binaryTitle: "Binary path",
|
|
20789
20793
|
binaryDescription: "Path to the Kimi CLI binary (started with `kimi acp`)."
|
|
20790
20794
|
});
|
|
20795
|
+
const JoyCodeSettings = makeAcpAgentSettingsSchema({
|
|
20796
|
+
binaryFallback: "joycode",
|
|
20797
|
+
binaryTitle: "Binary path",
|
|
20798
|
+
binaryDescription: "Path to the JoyCode CLI binary (started with `joycode app-server`)."
|
|
20799
|
+
});
|
|
20791
20800
|
const ObservabilitySettings = Struct({
|
|
20792
20801
|
otlpTracesUrl: TrimmedString.pipe(withDecodingDefault(succeed$1(""))),
|
|
20793
20802
|
otlpMetricsUrl: TrimmedString.pipe(withDecodingDefault(succeed$1("")))
|
|
@@ -20858,7 +20867,8 @@ const ServerSettings = Struct({
|
|
|
20858
20867
|
claudeAgent: ClaudeSettings.pipe(withDecodingDefault(succeed$1({}))),
|
|
20859
20868
|
gemini: GeminiSettings.pipe(withDecodingDefault(succeed$1({}))),
|
|
20860
20869
|
qwen: QwenSettings.pipe(withDecodingDefault(succeed$1({}))),
|
|
20861
|
-
kimi: KimiSettings.pipe(withDecodingDefault(succeed$1({})))
|
|
20870
|
+
kimi: KimiSettings.pipe(withDecodingDefault(succeed$1({}))),
|
|
20871
|
+
joycode: JoyCodeSettings.pipe(withDecodingDefault(succeed$1({})))
|
|
20862
20872
|
}).pipe(withDecodingDefault(succeed$1({}))),
|
|
20863
20873
|
providerInstances: Record(ProviderInstanceId, ProviderInstanceConfig).pipe(withDecodingDefault(succeed$1({}))),
|
|
20864
20874
|
observability: ObservabilitySettings.pipe(withDecodingDefault(succeed$1({})))
|
|
@@ -20948,7 +20958,8 @@ const ServerSettingsPatch = Struct({
|
|
|
20948
20958
|
claudeAgent: optionalKey(ClaudeSettingsPatch),
|
|
20949
20959
|
gemini: optionalKey(AcpAgentSettingsPatch),
|
|
20950
20960
|
qwen: optionalKey(AcpAgentSettingsPatch),
|
|
20951
|
-
kimi: optionalKey(AcpAgentSettingsPatch)
|
|
20961
|
+
kimi: optionalKey(AcpAgentSettingsPatch),
|
|
20962
|
+
joycode: optionalKey(AcpAgentSettingsPatch)
|
|
20952
20963
|
})),
|
|
20953
20964
|
providerInstances: optionalKey(Record(ProviderInstanceId, ProviderInstanceConfig))
|
|
20954
20965
|
});
|
|
@@ -53573,8 +53584,9 @@ const runHubTunnel = (config) => gen(function* () {
|
|
|
53573
53584
|
}
|
|
53574
53585
|
}, { onOpen: logInfo("hub tunnel: connected to hub").pipe(tap(() => sync(() => void runFork(upload)))) }).pipe(tapDefect((cause) => logWarning$1("hub tunnel: hub socket defect", { cause: String(cause) })));
|
|
53575
53586
|
yield* logWarning$1("hub tunnel: hub connection closed");
|
|
53587
|
+
return yield* fail(/* @__PURE__ */ new Error("hub tunnel closed"));
|
|
53576
53588
|
}).pipe(scoped, tapError((error) => logWarning$1("hub tunnel: connection error", { error }))), {
|
|
53577
|
-
schedule: spaced("
|
|
53589
|
+
schedule: spaced("500 millis"),
|
|
53578
53590
|
while: (error) => !(SocketError.is(error) && error.reason._tag === "SocketCloseError" && (error.reason.code === 4401 || error.reason.code === 4403))
|
|
53579
53591
|
});
|
|
53580
53592
|
}).pipe(tapDefect((cause) => logError("hub tunnel: worker defect", { cause: String(cause) })), catchCause((cause) => logError("hub tunnel: worker failed", { cause: String(cause) })));
|
|
@@ -86686,7 +86698,7 @@ const makeAcpNativeLoggerFactory = fn("makeAcpNativeLoggerFactory")(function* ()
|
|
|
86686
86698
|
//#region src/provider/Layers/AcpAgentAdapter.ts
|
|
86687
86699
|
/**
|
|
86688
86700
|
* AcpAgentAdapter — generic `ProviderAdapter` over `AcpSessionRuntime` for
|
|
86689
|
-
* the fork's ACP CLIs (Gemini / Qwen / Kimi).
|
|
86701
|
+
* the fork's ACP CLIs (Gemini / Qwen / Kimi / JoyCode).
|
|
86690
86702
|
*
|
|
86691
86703
|
* Ported from the upstream `GrokAdapter`, minus the Grok-specific pieces the
|
|
86692
86704
|
* fork doesn't need: the MCP provider-session bridge (the `mcp/` subsystem
|
|
@@ -86697,7 +86709,7 @@ const makeAcpNativeLoggerFactory = fn("makeAcpNativeLoggerFactory")(function* ()
|
|
|
86697
86709
|
* cursors.
|
|
86698
86710
|
*
|
|
86699
86711
|
* One instance of this adapter is created per provider instance by the
|
|
86700
|
-
* driver (`Drivers/<X>Driver.ts`); behavior differences between the
|
|
86712
|
+
* driver (`Drivers/<X>Driver.ts`); behavior differences between the
|
|
86701
86713
|
* CLIs live entirely in their `AcpAgentDescriptor`.
|
|
86702
86714
|
*
|
|
86703
86715
|
* @module provider/Layers/AcpAgentAdapter
|
|
@@ -87543,8 +87555,27 @@ function buildDiscoveredModelsFromSessionModelState(descriptor, modelState) {
|
|
|
87543
87555
|
};
|
|
87544
87556
|
}).filter((model) => model !== void 0);
|
|
87545
87557
|
}
|
|
87558
|
+
function buildDiscoveredAcpAgentModels(descriptor, sessionSetupResult) {
|
|
87559
|
+
const models = buildDiscoveredModelsFromSessionModelState(descriptor, sessionSetupResult.models);
|
|
87560
|
+
if (models.length > 0) return models;
|
|
87561
|
+
const modelConfig = sessionSetupResult.configOptions?.find((option) => option.category === "model" && option.type === "select");
|
|
87562
|
+
if (!modelConfig || modelConfig.type !== "select") return [];
|
|
87563
|
+
const options = modelConfig.options.flatMap((entry) => "value" in entry ? [entry] : entry.options);
|
|
87564
|
+
const seen = /* @__PURE__ */ new Set();
|
|
87565
|
+
return options.map((option) => {
|
|
87566
|
+
const slug = resolveAcpAgentModelId(descriptor, option.value);
|
|
87567
|
+
if (!slug || seen.has(slug)) return;
|
|
87568
|
+
seen.add(slug);
|
|
87569
|
+
return {
|
|
87570
|
+
slug,
|
|
87571
|
+
name: option.name.trim() || slug,
|
|
87572
|
+
isCustom: false,
|
|
87573
|
+
capabilities: EMPTY_CAPABILITIES
|
|
87574
|
+
};
|
|
87575
|
+
}).filter((model) => model !== void 0);
|
|
87576
|
+
}
|
|
87546
87577
|
const discoverModelsViaAcp = (descriptor, settings, environment = process.env) => gen(function* () {
|
|
87547
|
-
return
|
|
87578
|
+
return buildDiscoveredAcpAgentModels(descriptor, (yield* (yield* makeAcpAgentRuntime(descriptor, {
|
|
87548
87579
|
settings,
|
|
87549
87580
|
environment,
|
|
87550
87581
|
childProcessSpawner: yield* ChildProcessSpawner,
|
|
@@ -87553,7 +87584,7 @@ const discoverModelsViaAcp = (descriptor, settings, environment = process.env) =
|
|
|
87553
87584
|
name: "agentlink-provider-probe",
|
|
87554
87585
|
version: "0.0.0"
|
|
87555
87586
|
}
|
|
87556
|
-
})).start()).sessionSetupResult
|
|
87587
|
+
})).start()).sessionSetupResult);
|
|
87557
87588
|
}).pipe(scoped);
|
|
87558
87589
|
const runVersionCommand = (descriptor, settings, environment = process.env) => gen(function* () {
|
|
87559
87590
|
const command = settings.binaryPath.trim() || descriptor.defaultBinary;
|
|
@@ -87594,7 +87625,7 @@ const checkAcpAgentProviderStatus = (descriptor, settings, environment = process
|
|
|
87594
87625
|
version: null,
|
|
87595
87626
|
status: "error",
|
|
87596
87627
|
auth: { status: "unknown" },
|
|
87597
|
-
message: isCommandMissingCause(error) ? `${descriptor.displayName} (\`${descriptor.defaultBinary}\`) is not installed or not on PATH.` : `Failed to execute ${descriptor.displayName} health check.`
|
|
87628
|
+
message: isCommandMissingCause(error) ? descriptor.missingBinaryMessage ?? `${descriptor.displayName} (\`${descriptor.defaultBinary}\`) is not installed or not on PATH.` : `Failed to execute ${descriptor.displayName} health check.`
|
|
87598
87629
|
}
|
|
87599
87630
|
});
|
|
87600
87631
|
}
|
|
@@ -87796,6 +87827,41 @@ const GeminiDriver = makeAcpAgentDriver({
|
|
|
87796
87827
|
defaultConfig: () => decodeGeminiSettings({})
|
|
87797
87828
|
});
|
|
87798
87829
|
//#endregion
|
|
87830
|
+
//#region src/provider/acp/JoyCodeAcpAgent.ts
|
|
87831
|
+
/**
|
|
87832
|
+
* JoyCodeAcpAgent — descriptor for JoyCode CLI. `joycode app-server` starts
|
|
87833
|
+
* its ACP server over stdio.
|
|
87834
|
+
*
|
|
87835
|
+
* JoyCode 1.0.1 advertises both `loadSession` and `session/resume`, but its
|
|
87836
|
+
* `session/load` response is currently JSON-RPC `null`. daemon-t3 decodes
|
|
87837
|
+
* that response as an ACP LoadSessionResponse object, so the probe driver
|
|
87838
|
+
* deliberately starts fresh until that compatibility boundary is resolved.
|
|
87839
|
+
*
|
|
87840
|
+
* @module provider/acp/JoyCodeAcpAgent
|
|
87841
|
+
*/
|
|
87842
|
+
const JoyCodeAcpAgent = {
|
|
87843
|
+
provider: ProviderDriverKind.make("joycode"),
|
|
87844
|
+
displayName: "JoyCode",
|
|
87845
|
+
defaultBinary: "joycode",
|
|
87846
|
+
acpArgs: ["app-server"],
|
|
87847
|
+
defaultModel: "default",
|
|
87848
|
+
builtInModels: ["default"],
|
|
87849
|
+
missingBinaryMessage: "请安装 joycode-cli,参考内部文档。",
|
|
87850
|
+
supportsSessionLoad: false
|
|
87851
|
+
};
|
|
87852
|
+
//#endregion
|
|
87853
|
+
//#region src/provider/Drivers/JoyCodeDriver.ts
|
|
87854
|
+
/** JoyCodeDriver — JoyCode CLI (`joycode app-server`) via generic ACP. */
|
|
87855
|
+
const decodeJoyCodeSettings = decodeSync(JoyCodeSettings);
|
|
87856
|
+
const JoyCodeDriver = makeAcpAgentDriver({
|
|
87857
|
+
driver: {
|
|
87858
|
+
descriptor: JoyCodeAcpAgent,
|
|
87859
|
+
npmPackageName: "@joycode/joycode-cli"
|
|
87860
|
+
},
|
|
87861
|
+
configSchema: JoyCodeSettings,
|
|
87862
|
+
defaultConfig: () => decodeJoyCodeSettings({})
|
|
87863
|
+
});
|
|
87864
|
+
//#endregion
|
|
87799
87865
|
//#region src/provider/acp/KimiAcpAgent.ts
|
|
87800
87866
|
/**
|
|
87801
87867
|
* KimiAcpAgent — descriptor for the Kimi CLI (Kimi Code CLI, Moonshot),
|
|
@@ -87885,7 +87951,7 @@ const decodeQwenSettings = decodeSync(QwenSettings);
|
|
|
87885
87951
|
* satisfy it.
|
|
87886
87952
|
*
|
|
87887
87953
|
* agentlink fork: Cursor/Grok/OpenCode drivers were removed; ACP-based
|
|
87888
|
-
* drivers (Gemini/Qwen/Kimi) land here on top of `acp/AcpSessionRuntime.ts`.
|
|
87954
|
+
* drivers (Gemini/Qwen/Kimi/JoyCode) land here on top of `acp/AcpSessionRuntime.ts`.
|
|
87889
87955
|
*
|
|
87890
87956
|
* @module provider/builtInDrivers
|
|
87891
87957
|
*/
|
|
@@ -87906,7 +87972,8 @@ const BUILT_IN_DRIVERS = [
|
|
|
87906
87972
|
configSchema: QwenSettings,
|
|
87907
87973
|
defaultConfig: () => decodeQwenSettings({})
|
|
87908
87974
|
}),
|
|
87909
|
-
KimiDriver
|
|
87975
|
+
KimiDriver,
|
|
87976
|
+
JoyCodeDriver
|
|
87910
87977
|
];
|
|
87911
87978
|
//#endregion
|
|
87912
87979
|
//#region src/provider/Services/ProviderInstanceRegistryMutator.ts
|
|
@@ -89460,7 +89527,7 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
|
|
|
89460
89527
|
});
|
|
89461
89528
|
const reconcileThread = (threadId, options) => withThreadLock(threadId, reconcileThreadUnlocked(threadId, options)).pipe(mapError((cause) => cause instanceof ProviderRuntimeReconciliationError ? cause : new ProviderRuntimeReconciliationError({
|
|
89462
89529
|
threadId,
|
|
89463
|
-
detail: cause instanceof Error ? cause.message : "Provider snapshot sync failed.",
|
|
89530
|
+
detail: cause instanceof Error && cause.message.toLowerCase().includes("active writer") ? "对话正在另一端运行,请等待另一端结束后再继续。" : cause instanceof Error ? cause.message : "Provider snapshot sync failed.",
|
|
89464
89531
|
cause
|
|
89465
89532
|
})));
|
|
89466
89533
|
const processDomainEvent = (_event) => void_$1;
|