lark-agent-bridge 0.1.39
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/LICENSE +21 -0
- package/README.md +265 -0
- package/README.zh.md +265 -0
- package/bin/lark-agent-bridge.mjs +2 -0
- package/dist/agent/cursor/cursor-sdk-worker.js +702 -0
- package/dist/cli.js +7588 -0
- package/dist/index.d.ts +113 -0
- package/dist/index.js +721 -0
- package/package.json +72 -0
- package/vendor/cursor-sdk/LICENSE.md +3 -0
- package/vendor/cursor-sdk/README.md +23 -0
- package/vendor/cursor-sdk/dist/cjs/642.index.js +1 -0
- package/vendor/cursor-sdk/dist/cjs/agent.d.ts +170 -0
- package/vendor/cursor-sdk/dist/cjs/agent.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/analytics.d.ts +98 -0
- package/vendor/cursor-sdk/dist/cjs/analytics.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/artifacts.d.ts +6 -0
- package/vendor/cursor-sdk/dist/cjs/artifacts.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/cloud-agent.d.ts +47 -0
- package/vendor/cursor-sdk/dist/cjs/cloud-agent.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/cloud-api-client.d.ts +197 -0
- package/vendor/cursor-sdk/dist/cjs/cloud-api-client.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/cloud-executor.d.ts +14 -0
- package/vendor/cursor-sdk/dist/cjs/cloud-executor.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/cloud-mcp-utils.d.ts +4 -0
- package/vendor/cursor-sdk/dist/cjs/cloud-mcp-utils.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/core-adapter.d.ts +2 -0
- package/vendor/cursor-sdk/dist/cjs/core-adapter.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/errors.d.ts +135 -0
- package/vendor/cursor-sdk/dist/cjs/errors.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/executor-common.d.ts +16 -0
- package/vendor/cursor-sdk/dist/cjs/executor-common.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/executor-types.d.ts +65 -0
- package/vendor/cursor-sdk/dist/cjs/executor-types.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/index.d.ts +18 -0
- package/vendor/cursor-sdk/dist/cjs/index.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/index.js +8 -0
- package/vendor/cursor-sdk/dist/cjs/index.js.LICENSE.txt +285 -0
- package/vendor/cursor-sdk/dist/cjs/local-executor.d.ts +18 -0
- package/vendor/cursor-sdk/dist/cjs/local-executor.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/local-model-validation.d.ts +6 -0
- package/vendor/cursor-sdk/dist/cjs/local-model-validation.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/messages.d.ts +110 -0
- package/vendor/cursor-sdk/dist/cjs/messages.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/options.d.ts +145 -0
- package/vendor/cursor-sdk/dist/cjs/options.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/package.json +3 -0
- package/vendor/cursor-sdk/dist/cjs/platform.d.ts +71 -0
- package/vendor/cursor-sdk/dist/cjs/platform.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/prefetched-blobs.d.ts +13 -0
- package/vendor/cursor-sdk/dist/cjs/prefetched-blobs.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/public-api.d.ts +12 -0
- package/vendor/cursor-sdk/dist/cjs/public-api.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/run-event-tailer.d.ts +33 -0
- package/vendor/cursor-sdk/dist/cjs/run-event-tailer.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/run-interaction-accumulator.d.ts +33 -0
- package/vendor/cursor-sdk/dist/cjs/run-interaction-accumulator.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/run.d.ts +44 -0
- package/vendor/cursor-sdk/dist/cjs/run.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/sdk-statsig.d.ts +10 -0
- package/vendor/cursor-sdk/dist/cjs/sdk-statsig.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/stubs.d.ts +98 -0
- package/vendor/cursor-sdk/dist/cjs/stubs.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/subagent-conversion.d.ts +22 -0
- package/vendor/cursor-sdk/dist/cjs/subagent-conversion.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/tool-call-utils.d.ts +7 -0
- package/vendor/cursor-sdk/dist/cjs/tool-call-utils.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/types/conversation-types.d.ts +12062 -0
- package/vendor/cursor-sdk/dist/cjs/types/conversation-types.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/types/delta-types.d.ts +2 -0
- package/vendor/cursor-sdk/dist/cjs/types/delta-types.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/types/tool-call-types.d.ts +2 -0
- package/vendor/cursor-sdk/dist/cjs/types/tool-call-types.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/utils/conversation-utils.d.ts +3 -0
- package/vendor/cursor-sdk/dist/cjs/utils/conversation-utils.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/utils/logger.d.ts +12 -0
- package/vendor/cursor-sdk/dist/cjs/utils/logger.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/cjs/utils/message-schemas.d.ts +2 -0
- package/vendor/cursor-sdk/dist/cjs/utils/message-schemas.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/642.index.js +1 -0
- package/vendor/cursor-sdk/dist/esm/agent.d.ts +170 -0
- package/vendor/cursor-sdk/dist/esm/agent.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/analytics.d.ts +98 -0
- package/vendor/cursor-sdk/dist/esm/analytics.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/artifacts.d.ts +6 -0
- package/vendor/cursor-sdk/dist/esm/artifacts.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/cloud-agent.d.ts +47 -0
- package/vendor/cursor-sdk/dist/esm/cloud-agent.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/cloud-api-client.d.ts +197 -0
- package/vendor/cursor-sdk/dist/esm/cloud-api-client.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/cloud-executor.d.ts +14 -0
- package/vendor/cursor-sdk/dist/esm/cloud-executor.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/cloud-mcp-utils.d.ts +4 -0
- package/vendor/cursor-sdk/dist/esm/cloud-mcp-utils.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/core-adapter.d.ts +2 -0
- package/vendor/cursor-sdk/dist/esm/core-adapter.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/errors.d.ts +135 -0
- package/vendor/cursor-sdk/dist/esm/errors.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/executor-common.d.ts +16 -0
- package/vendor/cursor-sdk/dist/esm/executor-common.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/executor-types.d.ts +65 -0
- package/vendor/cursor-sdk/dist/esm/executor-types.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/index.d.ts +18 -0
- package/vendor/cursor-sdk/dist/esm/index.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/index.js +8 -0
- package/vendor/cursor-sdk/dist/esm/index.js.LICENSE.txt +285 -0
- package/vendor/cursor-sdk/dist/esm/local-executor.d.ts +18 -0
- package/vendor/cursor-sdk/dist/esm/local-executor.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/local-model-validation.d.ts +6 -0
- package/vendor/cursor-sdk/dist/esm/local-model-validation.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/messages.d.ts +110 -0
- package/vendor/cursor-sdk/dist/esm/messages.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/options.d.ts +145 -0
- package/vendor/cursor-sdk/dist/esm/options.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/platform.d.ts +71 -0
- package/vendor/cursor-sdk/dist/esm/platform.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/prefetched-blobs.d.ts +13 -0
- package/vendor/cursor-sdk/dist/esm/prefetched-blobs.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/public-api.d.ts +12 -0
- package/vendor/cursor-sdk/dist/esm/public-api.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/run-event-tailer.d.ts +33 -0
- package/vendor/cursor-sdk/dist/esm/run-event-tailer.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/run-interaction-accumulator.d.ts +33 -0
- package/vendor/cursor-sdk/dist/esm/run-interaction-accumulator.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/run.d.ts +44 -0
- package/vendor/cursor-sdk/dist/esm/run.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/sdk-statsig.d.ts +10 -0
- package/vendor/cursor-sdk/dist/esm/sdk-statsig.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/stubs.d.ts +98 -0
- package/vendor/cursor-sdk/dist/esm/stubs.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/subagent-conversion.d.ts +22 -0
- package/vendor/cursor-sdk/dist/esm/subagent-conversion.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/tool-call-utils.d.ts +7 -0
- package/vendor/cursor-sdk/dist/esm/tool-call-utils.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/types/conversation-types.d.ts +12062 -0
- package/vendor/cursor-sdk/dist/esm/types/conversation-types.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/types/delta-types.d.ts +2 -0
- package/vendor/cursor-sdk/dist/esm/types/delta-types.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/types/tool-call-types.d.ts +2 -0
- package/vendor/cursor-sdk/dist/esm/types/tool-call-types.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/utils/conversation-utils.d.ts +3 -0
- package/vendor/cursor-sdk/dist/esm/utils/conversation-utils.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/utils/logger.d.ts +12 -0
- package/vendor/cursor-sdk/dist/esm/utils/logger.d.ts.map +1 -0
- package/vendor/cursor-sdk/dist/esm/utils/message-schemas.d.ts +2 -0
- package/vendor/cursor-sdk/dist/esm/utils/message-schemas.d.ts.map +1 -0
- package/vendor/cursor-sdk/package.json +77 -0
- package/vendor/cursor-sdk-linux-x64/README.md +5 -0
- package/vendor/cursor-sdk-linux-x64/bin/cursorsandbox +0 -0
- package/vendor/cursor-sdk-linux-x64/bin/rg +0 -0
- package/vendor/cursor-sdk-linux-x64/package.json +8 -0
- package/vendor/package/README.md +5 -0
- package/vendor/package/bin/cursorsandbox +0 -0
- package/vendor/package/bin/rg +0 -0
- package/vendor/package/package.json +25 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
// src/agent/cursor/sdk-worker.ts
|
|
2
|
+
import { Agent } from "@cursor/sdk";
|
|
3
|
+
|
|
4
|
+
// src/agent/cursor/sdk-error.ts
|
|
5
|
+
import { CursorAgentError } from "@cursor/sdk";
|
|
6
|
+
var AUTH_HINT = 'Cursor \u9700\u8981 API Key\uFF1A\u5728 config.json \u914D\u7F6E preferences.agentCursorApiKey\uFF08\u63A8\u8350\u52A0\u5BC6\uFF1Alark-agent-bridge secrets set --id cursor-api-key\uFF09\uFF0C\u6216 export CURSOR_API_KEY\uFF1B\u4E5F\u53EF\u5C06 agentCursorRuntime \u8BBE\u4E3A "cli" \u4EE5\u4F7F\u7528 `agent login` \u4F1A\u8BDD\u3002';
|
|
7
|
+
function getCause(err) {
|
|
8
|
+
if (!err || typeof err !== "object") return void 0;
|
|
9
|
+
if ("cause" in err && err.cause !== err) {
|
|
10
|
+
return err.cause;
|
|
11
|
+
}
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
function errorName(err) {
|
|
15
|
+
if (!err || typeof err !== "object") return "Error";
|
|
16
|
+
if ("name" in err && typeof err.name === "string") {
|
|
17
|
+
return err.name;
|
|
18
|
+
}
|
|
19
|
+
return "Error";
|
|
20
|
+
}
|
|
21
|
+
function errorMessage(err) {
|
|
22
|
+
if (err instanceof Error) return err.message || err.name;
|
|
23
|
+
if (typeof err === "string") return err;
|
|
24
|
+
return String(err);
|
|
25
|
+
}
|
|
26
|
+
function errorCode(err) {
|
|
27
|
+
if (!err || typeof err !== "object") return void 0;
|
|
28
|
+
if ("code" in err) {
|
|
29
|
+
const code = err.code;
|
|
30
|
+
if (typeof code === "string" || typeof code === "number") return code;
|
|
31
|
+
}
|
|
32
|
+
return void 0;
|
|
33
|
+
}
|
|
34
|
+
function connectRawMessage(err) {
|
|
35
|
+
if (!err || typeof err !== "object" || !("rawMessage" in err)) return void 0;
|
|
36
|
+
const raw = err.rawMessage;
|
|
37
|
+
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
38
|
+
}
|
|
39
|
+
function sdkExtras(err) {
|
|
40
|
+
if (!(err instanceof CursorAgentError)) return [];
|
|
41
|
+
const parts = [];
|
|
42
|
+
if (err.code) parts.push(`code=${err.code}`);
|
|
43
|
+
if (err.status !== void 0) parts.push(`status=${err.status}`);
|
|
44
|
+
if (err.operation) parts.push(`operation=${err.operation}`);
|
|
45
|
+
if (err.endpoint) parts.push(`endpoint=${err.endpoint}`);
|
|
46
|
+
if (err.requestId) parts.push(`requestId=${err.requestId}`);
|
|
47
|
+
parts.push(`retryable=${err.isRetryable}`);
|
|
48
|
+
return parts;
|
|
49
|
+
}
|
|
50
|
+
function classifyKind(err) {
|
|
51
|
+
if (err instanceof CursorAgentError) {
|
|
52
|
+
const name = errorName(err);
|
|
53
|
+
if (name === "AuthenticationError" || err.code === "unauthenticated") return "auth";
|
|
54
|
+
if (name === "RateLimitError") return "rate_limit";
|
|
55
|
+
if (name === "NetworkError") return "network";
|
|
56
|
+
if (name === "ConfigurationError") return "config";
|
|
57
|
+
}
|
|
58
|
+
if (errorName(err) === "ConfigurationError") return "config";
|
|
59
|
+
if (errorMessage(err).includes("Cannot use this model")) return "config";
|
|
60
|
+
const code = errorCode(err);
|
|
61
|
+
if (code === "unauthenticated" || code === 16) return "auth";
|
|
62
|
+
if (typeof code === "number" && code === 8) return "rate_limit";
|
|
63
|
+
const msg = errorMessage(err).toLowerCase();
|
|
64
|
+
if (msg.includes("unauthenticated") || msg.includes("authentication") || msg.includes("api key")) {
|
|
65
|
+
return "auth";
|
|
66
|
+
}
|
|
67
|
+
if (msg.includes("cancelled") || msg.includes("canceled")) return "cancelled";
|
|
68
|
+
if (msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("socket hang up") || msg.includes("network") || msg.includes("timeout")) {
|
|
69
|
+
return "network";
|
|
70
|
+
}
|
|
71
|
+
return "unknown";
|
|
72
|
+
}
|
|
73
|
+
function formatChainLine(err, depth) {
|
|
74
|
+
const name = errorName(err);
|
|
75
|
+
const msg = errorMessage(err);
|
|
76
|
+
const raw = connectRawMessage(err);
|
|
77
|
+
const code = errorCode(err);
|
|
78
|
+
const extras = sdkExtras(err);
|
|
79
|
+
const bits = [`${" ".repeat(depth)}${name}: ${msg}`];
|
|
80
|
+
if (raw && raw !== msg) bits.push(`raw=${raw}`);
|
|
81
|
+
if (code !== void 0) bits.push(`code=${code}`);
|
|
82
|
+
if (extras.length > 0) bits.push(extras.join(", "));
|
|
83
|
+
return bits.join(" | ");
|
|
84
|
+
}
|
|
85
|
+
function headlineForKind(kind, root) {
|
|
86
|
+
const msg = errorMessage(root);
|
|
87
|
+
switch (kind) {
|
|
88
|
+
case "auth":
|
|
89
|
+
return msg && msg !== "Error" ? `Cursor API \u9274\u6743\u5931\u8D25: ${msg}` : "Cursor API \u9274\u6743\u5931\u8D25 (unauthenticated)";
|
|
90
|
+
case "rate_limit":
|
|
91
|
+
return `Cursor API \u9650\u6D41: ${msg}`;
|
|
92
|
+
case "network":
|
|
93
|
+
return `Cursor API \u7F51\u7EDC\u9519\u8BEF: ${msg}`;
|
|
94
|
+
case "config":
|
|
95
|
+
return `Cursor SDK \u914D\u7F6E\u9519\u8BEF: ${msg}`;
|
|
96
|
+
case "cancelled":
|
|
97
|
+
return "Cursor agent run cancelled";
|
|
98
|
+
default:
|
|
99
|
+
return msg || "Cursor SDK error";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function describeSdkError(err) {
|
|
103
|
+
const lines = [];
|
|
104
|
+
let current = err;
|
|
105
|
+
let depth = 0;
|
|
106
|
+
let kind = "unknown";
|
|
107
|
+
while (current && depth < 8) {
|
|
108
|
+
lines.push(formatChainLine(current, depth));
|
|
109
|
+
const k = classifyKind(current);
|
|
110
|
+
if (k !== "unknown") kind = k;
|
|
111
|
+
current = getCause(current);
|
|
112
|
+
depth += 1;
|
|
113
|
+
}
|
|
114
|
+
const headline = headlineForKind(kind, err);
|
|
115
|
+
const hint = kind === "auth" ? AUTH_HINT : kind === "rate_limit" ? "\u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u964D\u4F4E\u5E76\u53D1\uFF08maxConcurrentRuns / agentSessionPoolSize\uFF09\u3002" : void 0;
|
|
116
|
+
const detail = [headline, ...lines, hint].filter(Boolean).join("\n");
|
|
117
|
+
return { kind, headline, detail, hint };
|
|
118
|
+
}
|
|
119
|
+
function isCursorAgentNotFoundError(err, agentId) {
|
|
120
|
+
const msg = errorMessage(err);
|
|
121
|
+
if (!msg.includes(`Agent ${agentId} not found`)) return false;
|
|
122
|
+
if (errorName(err) !== "ConfigurationError") return false;
|
|
123
|
+
if (err instanceof CursorAgentError && err.operation !== "Agent.resume") return false;
|
|
124
|
+
if (err && typeof err === "object" && "operation" in err && err.operation !== "Agent.resume") {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
function isCursorAgentActiveRunError(err, agentId) {
|
|
130
|
+
const msg = errorMessage(err);
|
|
131
|
+
if (!msg.includes(`Agent ${agentId} already has active run`)) return false;
|
|
132
|
+
if (err instanceof CursorAgentError && err.operation !== "agent.send") return false;
|
|
133
|
+
if (err && typeof err === "object" && "operation" in err && err.operation !== "agent.send") {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
function isCursorRateLimitError(err) {
|
|
139
|
+
return describeSdkError(err).kind === "rate_limit";
|
|
140
|
+
}
|
|
141
|
+
function isCursorNetworkError(err) {
|
|
142
|
+
return describeSdkError(err).kind === "network";
|
|
143
|
+
}
|
|
144
|
+
function formatSdkErrorForIpc(phase, err) {
|
|
145
|
+
const d = describeSdkError(err);
|
|
146
|
+
const parts = [`${phase}: ${d.headline}`];
|
|
147
|
+
const rest = d.detail.split("\n").filter((line) => line !== d.headline && line !== d.hint);
|
|
148
|
+
if (rest.length > 0) parts.push(...rest);
|
|
149
|
+
if (d.hint) parts.push(d.hint);
|
|
150
|
+
return parts.join("\n");
|
|
151
|
+
}
|
|
152
|
+
function formatSdkErrorForStderr(phase, err) {
|
|
153
|
+
const d = describeSdkError(err);
|
|
154
|
+
return [`[sdk-worker] ${phase}`, d.detail, d.hint].filter(Boolean).join("\n");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/agent/cursor/spawn-run.ts
|
|
158
|
+
import { spawn as spawn2 } from "child_process";
|
|
159
|
+
import { statSync } from "fs";
|
|
160
|
+
import { createInterface as createInterface2 } from "readline";
|
|
161
|
+
|
|
162
|
+
// src/core/logger.ts
|
|
163
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
164
|
+
import { createWriteStream, mkdirSync } from "fs";
|
|
165
|
+
import { open, readdir, rm, stat } from "fs/promises";
|
|
166
|
+
import { join as join2 } from "path";
|
|
167
|
+
|
|
168
|
+
// src/config/paths.ts
|
|
169
|
+
import { homedir } from "os";
|
|
170
|
+
import { join } from "path";
|
|
171
|
+
var appDir = join(homedir(), ".lark-channel");
|
|
172
|
+
var paths = {
|
|
173
|
+
appDir,
|
|
174
|
+
cacheDir: appDir,
|
|
175
|
+
configFile: join(appDir, "config.json"),
|
|
176
|
+
sessionsFile: join(appDir, "sessions.json"),
|
|
177
|
+
workspacesFile: join(appDir, "workspaces.json"),
|
|
178
|
+
processesFile: join(appDir, "processes.json"),
|
|
179
|
+
secretsFile: join(appDir, "secrets.enc"),
|
|
180
|
+
keystoreSaltFile: join(appDir, ".keystore.salt"),
|
|
181
|
+
/**
|
|
182
|
+
* Thin shell wrapper that lark-cli (and other openclaw-exec-protocol
|
|
183
|
+
* consumers) invoke to resolve secrets from the bridge's encrypted store.
|
|
184
|
+
* Written user-owned and non-symlinked so it passes lark-cli's
|
|
185
|
+
* AssertSecurePath audit on machines where `node` is a Homebrew/Volta
|
|
186
|
+
* symlink or root-owned (`/usr/bin/node`). Wrapper internals do the
|
|
187
|
+
* `node ... secrets get` invocation; lark-cli only audits the wrapper.
|
|
188
|
+
*/
|
|
189
|
+
secretsGetterScript: join(appDir, "secrets-getter"),
|
|
190
|
+
mediaDir: join(appDir, "media")
|
|
191
|
+
};
|
|
192
|
+
var legacyPaths = {
|
|
193
|
+
appDir: join(
|
|
194
|
+
process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"),
|
|
195
|
+
"lark-channel-bridge"
|
|
196
|
+
),
|
|
197
|
+
cacheDir: join(
|
|
198
|
+
process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache"),
|
|
199
|
+
"lark-channel-bridge"
|
|
200
|
+
)
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// src/core/logger.ts
|
|
204
|
+
var LOG_RETENTION_DAYS = Math.max(
|
|
205
|
+
1,
|
|
206
|
+
Number(process.env.LARK_CHANNEL_LOG_DAYS ?? 7) || 7
|
|
207
|
+
);
|
|
208
|
+
var als = new AsyncLocalStorage();
|
|
209
|
+
|
|
210
|
+
// src/agent/claude/adapter.ts
|
|
211
|
+
import { spawn } from "child_process";
|
|
212
|
+
import { createInterface } from "readline";
|
|
213
|
+
var BRIDGE_SYSTEM_PROMPT = `# lark-agent-bridge \u8FD0\u884C\u7EA6\u5B9A
|
|
214
|
+
|
|
215
|
+
\u4F60\u6B63\u5728 lark-agent-bridge \u91CC\u8DD1\uFF1A\u628A\u98DE\u4E66/Lark \u7528\u6237\u6D88\u606F\u6865\u5230\u672C\u5730\u547D\u4EE4\u884C coding agent\u3002
|
|
216
|
+
|
|
217
|
+
## bridge_context
|
|
218
|
+
|
|
219
|
+
\u6BCF\u6761 user message \u9876\u90E8\u4F1A\u5E26\u4E00\u4E2A \`<bridge_context>\` \u5757\uFF1A
|
|
220
|
+
|
|
221
|
+
\`\`\`
|
|
222
|
+
<bridge_context>
|
|
223
|
+
chat_id: oc_xxx
|
|
224
|
+
chat_type: p2p
|
|
225
|
+
sender_id: ou_xxx
|
|
226
|
+
sender_name: ...
|
|
227
|
+
</bridge_context>
|
|
228
|
+
\`\`\`
|
|
229
|
+
|
|
230
|
+
\u91CC\u9762\u662F\u5F53\u524D\u5BF9\u8BDD\u7684 chat_id\u3001chat \u7C7B\u578B\uFF08p2p / group\uFF09\u3001\u53D1\u9001\u8005\u3002\u8FD9\u4E9B\u662F bridge \u6CE8\u5165\u7684\u5143\u6570\u636E\uFF0C**\u4E0D\u8981\u7167\u6284\u3001\u4E0D\u8981\u5728\u4F60\u7684\u56DE\u590D\u91CC\u6E32\u67D3**\u2014\u2014\u5B83\u5BF9\u7528\u6237\u4E0D\u53EF\u89C1\u3002
|
|
231
|
+
|
|
232
|
+
## quoted_message
|
|
233
|
+
|
|
234
|
+
\u5982\u679C\u7528\u6237\u7528"\u5F15\u7528\u56DE\u590D"\u6307\u5411\u67D0\u6761\u6D88\u606F\uFF0Cbridge \u4F1A\u5728 \`<bridge_context>\` \u540E\u6CE8\u5165\u4E00\u4E2A \`<quoted_message>\` \u5757\uFF1A
|
|
235
|
+
|
|
236
|
+
\`\`\`
|
|
237
|
+
<quoted_message id="om_xxx" sender_id="ou_xxx" sender_name="..." created_at="..." type="text|merge_forward|...">
|
|
238
|
+
\uFF08\u88AB\u5F15\u7528\u6D88\u606F\u7684\u5185\u5BB9\uFF1Bmerge_forward \u7C7B\u578B\u4F1A\u5C55\u5F00\u6210 <forwarded_messages>...</forwarded_messages>\uFF09
|
|
239
|
+
</quoted_message>
|
|
240
|
+
\`\`\`
|
|
241
|
+
|
|
242
|
+
\u8FD9\u662F\u7528\u6237**\u6307\u5411\u7684\u5BF9\u8C61**\u2014\u2014\u7528\u6237\u7684\u5B9E\u9645\u95EE\u9898\u5728\u5B83\u4E4B\u540E\u3002\u56DE\u7B54\u65F6\u56F4\u7ED5\u8FD9\u6BB5\u5185\u5BB9\u5C55\u5F00\uFF1B\u5B83\u4E5F\u662F bridge \u6CE8\u5165\u7684\u5143\u6570\u636E\uFF0C**\u4E0D\u8981\u7167\u6284 XML \u6807\u7B7E**\u5230\u56DE\u590D\u91CC\u3002
|
|
243
|
+
|
|
244
|
+
## \u53D1\u4EA4\u4E92\u5361\u7247\uFF08\u6309\u94AE\u3001\u8868\u5355\uFF09\u7684\u56DE\u8C03\u7EA6\u5B9A
|
|
245
|
+
|
|
246
|
+
\u4F60\u60F3\u53D1\u4E00\u5F20\u53EF\u4EA4\u4E92\u7684\u5361\u7247\u8BA9\u7528\u6237\u70B9\u9009\u65F6\uFF1A
|
|
247
|
+
|
|
248
|
+
1. \u7528 \`lark-cli\` \u628A\u5361\u53D1\u5230 \`bridge_context.chat_id\`\uFF1A
|
|
249
|
+
\`lark-cli im send-card --chat-id <chat_id> --card '<json>'\`
|
|
250
|
+
2. \u5361\u7247\u7528 CardKit 2.0 schema\uFF08\`schema: "2.0"\`\uFF09\u3002
|
|
251
|
+
3. **\u5982\u679C\u4F60\u5E0C\u671B\u7528\u6237\u70B9\u6309\u94AE\u540E\u56DE\u8C03\u5230\u4F60\uFF08\u8BA9\u4F60\u5728\u540C\u4E00\u4F1A\u8BDD\u91CC\u7EE7\u7EED\u5904\u7406\uFF09**\uFF1A
|
|
252
|
+
- \u6309\u94AE\u7684 \`value\` \u5BF9\u8C61**\u5FC5\u987B**\u5305\u542B \`__claude_cb: true\`
|
|
253
|
+
- \u540C\u65F6\u53EF\u4EE5\u585E\u4EFB\u610F\u5176\u5B83\u5B57\u6BB5\uFF0C\u4F5C\u4E3A\u4F60\u9700\u8981\u5728\u56DE\u8C03\u65F6\u8BB0\u4F4F\u7684\u72B6\u6001\uFF08\u6BD4\u5982 \`{"__claude_cb": true, "choice": "a", "ticket_id": "T-123"}\`\uFF09
|
|
254
|
+
4. \u7528\u6237\u70B9\u51FB\u540E\uFF0Cbridge \u4F1A\u628A payload\uFF08\u53BB\u6389 \`__claude_cb\` marker\uFF09\u4F5C\u4E3A \`[card-click] {...}\` \u6D88\u606F\u53D1\u56DE\u7ED9\u4F60\uFF1B\u4F60\u7684 session \u81EA\u52A8\u7EED\u4E0A\uFF0C\u80FD\u770B\u5230\u81EA\u5DF1\u4E0A\u8F6E\u53D1\u4E86\u4EC0\u4E48\u5361\u3002
|
|
255
|
+
5. **\u5982\u679C\u53EA\u662F\u5C55\u793A\u5361\uFF08\u4E0D\u9700\u8981\u56DE\u8C03\uFF09**\uFF0C\u4E0D\u8981\u52A0 \`__claude_cb\`\uFF0C\u5426\u5219\u70B9\u51FB\u5C31\u4F1A\u89E6\u53D1\u989D\u5916\u7684\u4F1A\u8BDD\u8F6E\u6B21\u3002
|
|
256
|
+
|
|
257
|
+
\u793A\u4F8B button\uFF1A
|
|
258
|
+
\`\`\`json
|
|
259
|
+
{
|
|
260
|
+
"tag": "button",
|
|
261
|
+
"text": { "tag": "plain_text", "content": "\u65B9\u6848 A" },
|
|
262
|
+
"behaviors": [{
|
|
263
|
+
"type": "callback",
|
|
264
|
+
"value": { "__claude_cb": true, "choice": "a" }
|
|
265
|
+
}]
|
|
266
|
+
}
|
|
267
|
+
\`\`\`
|
|
268
|
+
|
|
269
|
+
## \u98DE\u4E66 OAuth \u6388\u6743\uFF08\`lark-cli auth login\`\uFF09
|
|
270
|
+
|
|
271
|
+
\u6388\u6743\u6D41\u7A0B\u8981\u8BA9 \`lark-cli\` \u8FDB\u7A0B\u4E00\u76F4\u6D3B\u5230\u7528\u6237\u5728\u6D4F\u89C8\u5668\u91CC\u70B9\u5B8C\u4E3A\u6B62\u3002bridge \u5728\u4F60\u7684 run \u7ED3\u675F\u4E4B\u540E\u4F1A\u56DE\u6536 claude\uFF0C**\u4F60 spawn \u7684\u4EFB\u4F55\u540E\u53F0 bash \u4E5F\u4F1A\u8DDF\u7740\u6B7B**\u2014\u2014\u6240\u4EE5\u6388\u6743\u5FC5\u987B\u7528"\u524D\u53F0\u963B\u585E"\u7684\u65B9\u5F0F\u8DD1\uFF1A
|
|
272
|
+
|
|
273
|
+
1. **\u4EC5\u5728 p2p \u91CC\u53D1\u8D77\u6388\u6743**\u3002\u4ECE \`bridge_context.chat_type\` \u770B\uFF1A
|
|
274
|
+
- \`chat_type: p2p\` \u2014\u2014 \u6B63\u5E38\u6309\u4E0B\u9762\u6D41\u7A0B\u8D70\u3002
|
|
275
|
+
- \`chat_type: group\`\uFF08\u542B topic \u7FA4\uFF09\u2014\u2014 **\u4E0D\u8981**\u8C03 \`lark-cli auth login\`\u3002device flow \u628A \`verification_url\` \u53D1\u5230\u7FA4\u91CC\uFF0C\u8C01\u5148\u70B9\u8C01\u62FF\u8D70 token\u2014\u2014\u4F1A\u7ED1\u5B9A\u5230\u9519\u7684\u8EAB\u4EFD\u3002\u6B63\u786E\u505A\u6CD5\u662F\u56DE\u590D\u7528\u6237\uFF1A"\u6388\u6743\u8981\u5728\u79C1\u804A\u91CC\u505A\uFF0C\u8BF7\u5355\u72EC\u79C1\u4FE1\u6211\u3002"
|
|
276
|
+
2. **\u7981\u6B62** \u7528 \`run_in_background: true\` \u8C03 \`lark-cli auth login\`\u2014\u2014\u5B83\u4F1A\u88AB\u4F60 exit \u65F6\u4E00\u8D77\u5E26\u8D70\uFF0C\u7528\u6237\u8FD8\u6CA1\u70B9\u5B8C\u5C31\u4E22\u4E86\u3002
|
|
277
|
+
3. **\u63A8\u8350\u4E24\u9636\u6BB5\u6D41**\uFF08lark-cli \u5728 \`--no-wait\` \u7684\u8F93\u51FA\u91CC\u4E5F\u4F1A\u544A\u8BC9\u4F60\u8FD9\u5957\uFF09\uFF1A
|
|
278
|
+
- \u5148\u8DD1 \`lark-cli auth login --no-wait --json [--recommend | --domain ... | --scope ...]\`\uFF0C**\u8FD9\u4E00\u6B65\u79D2\u8FD4\u56DE**\uFF0Cstdout \u91CC\u6709 \`verification_url\` \u548C \`device_code\`\u3002
|
|
279
|
+
- \u628A \`verification_url\` **\u539F\u6837**\u7528\u4EE3\u7801\u5757\u53D1\u7ED9\u7528\u6237\uFF08\u4E0D\u8981 Markdown \u94FE\u63A5\u5316\u3001\u4E0D\u8981 URL \u7F16\u7801\uFF09\u3002
|
|
280
|
+
- \u7D27\u63A5\u7740\u540C\u4E00\u8F6E\u91CC\u8DD1 \`lark-cli auth login --device-code <code>\`\uFF0C**\u8FD9\u4E00\u6B65\u524D\u53F0\u963B\u585E**\u76F4\u5230\u7528\u6237\u70B9\u5B8C\u6216 10 \u5206\u949F\u8D85\u65F6\u2014\u2014\u8FD9\u662F\u4F60\u5E94\u8BE5\u7B49\u7684\u5730\u65B9\uFF0C\u4E0D\u8981\u4E22\u5230\u540E\u53F0\u3002
|
|
281
|
+
4. \u4F60\u524D\u53F0\u963B\u585E\u671F\u95F4\uFF0C\u7528\u6237\u53D1\u7684\u65B0\u6D88\u606F bridge \u4F1A\u81EA\u52A8\u6392\u961F\uFF0C**\u4E0D\u4F1A\u6253\u65AD\u4F60**\uFF1B\u7B49\u4F60 tool_result \u4E00\u56DE\u6765\uFF0C\u4E0B\u4E00\u6279\u6D88\u606F\u518D\u8FDB\u6765\u3002\u6240\u4EE5\u653E\u5FC3\u963B\u585E\u3002
|
|
282
|
+
5. \u5982\u679C\u7528\u6237\u4E2D\u9014\u60F3\u53D6\u6D88\uFF0C\u4ED6\u4EEC\u4F1A\u53D1 \`/stop\`\u2014\u2014\u90A3\u65F6\u88AB kill \u662F\u9884\u671F\u884C\u4E3A\uFF0C\u4E0D\u7528\u515C\u5E95\u3002
|
|
283
|
+
`;
|
|
284
|
+
|
|
285
|
+
// src/agent/cursor/spawn-run.ts
|
|
286
|
+
function buildCursorPrompt(prompt) {
|
|
287
|
+
return `<bridge_system_prompt>
|
|
288
|
+
${BRIDGE_SYSTEM_PROMPT}
|
|
289
|
+
</bridge_system_prompt>
|
|
290
|
+
|
|
291
|
+
<user_prompt>
|
|
292
|
+
${prompt}
|
|
293
|
+
</user_prompt>`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/agent/cursor/sdk-translate.ts
|
|
297
|
+
function* translateSdkMessage(msg) {
|
|
298
|
+
if (msg.type === "system" && msg.subtype === "init") {
|
|
299
|
+
yield {
|
|
300
|
+
type: "system",
|
|
301
|
+
sessionId: msg.agent_id,
|
|
302
|
+
model: typeof msg.model?.id === "string" ? msg.model.id : void 0
|
|
303
|
+
};
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (msg.type === "thinking" && typeof msg.text === "string" && msg.text) {
|
|
307
|
+
yield { type: "thinking", delta: msg.text };
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (msg.type === "status" && typeof msg.status === "string") {
|
|
311
|
+
yield {
|
|
312
|
+
type: "progress",
|
|
313
|
+
phase: phaseForSdkStatus(msg.status),
|
|
314
|
+
label: labelForSdkStatus(msg.status, msg.text)
|
|
315
|
+
};
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (msg.type === "task") {
|
|
319
|
+
const label = typeof msg.text === "string" && msg.text.trim() ? msg.text : msg.status;
|
|
320
|
+
if (typeof label === "string" && label.trim()) {
|
|
321
|
+
yield { type: "progress", phase: "thinking", label };
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (msg.type === "assistant" && msg.message?.content) {
|
|
326
|
+
for (const block of msg.message.content) {
|
|
327
|
+
if (block.type === "text" && typeof block.text === "string" && block.text) {
|
|
328
|
+
yield { type: "text", delta: block.text };
|
|
329
|
+
}
|
|
330
|
+
if (block.type === "tool_use" && block.id && block.name) {
|
|
331
|
+
yield { type: "tool_use", id: block.id, name: block.name, input: block.input ?? {} };
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
if (msg.type === "tool_call" && msg.call_id && msg.name) {
|
|
337
|
+
if (msg.status === "running") {
|
|
338
|
+
yield { type: "tool_use", id: msg.call_id, name: msg.name, input: msg.args ?? {} };
|
|
339
|
+
} else if (msg.status === "completed" || msg.status === "error") {
|
|
340
|
+
yield {
|
|
341
|
+
type: "tool_result",
|
|
342
|
+
id: msg.call_id,
|
|
343
|
+
output: stringifyResult(msg.result),
|
|
344
|
+
isError: msg.status === "error" || isErrorResult(msg.result)
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function phaseForSdkStatus(status) {
|
|
350
|
+
switch (status) {
|
|
351
|
+
case "CREATING":
|
|
352
|
+
return "starting";
|
|
353
|
+
case "FINISHED":
|
|
354
|
+
return "streaming";
|
|
355
|
+
case "RUNNING":
|
|
356
|
+
case "ERROR":
|
|
357
|
+
case "CANCELLED":
|
|
358
|
+
case "EXPIRED":
|
|
359
|
+
default:
|
|
360
|
+
return "thinking";
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function labelForSdkStatus(status, message) {
|
|
364
|
+
if (typeof message === "string" && message.trim()) return message;
|
|
365
|
+
switch (status) {
|
|
366
|
+
case "CREATING":
|
|
367
|
+
return "\u6B63\u5728\u521B\u5EFA Agent";
|
|
368
|
+
case "RUNNING":
|
|
369
|
+
return "Agent \u6B63\u5728\u8FD0\u884C";
|
|
370
|
+
case "FINISHED":
|
|
371
|
+
return "Agent \u6B63\u5728\u6536\u5C3E";
|
|
372
|
+
case "ERROR":
|
|
373
|
+
return "Agent \u72B6\u6001\u5F02\u5E38";
|
|
374
|
+
case "CANCELLED":
|
|
375
|
+
return "Agent \u5DF2\u53D6\u6D88";
|
|
376
|
+
case "EXPIRED":
|
|
377
|
+
return "Agent \u5DF2\u8FC7\u671F";
|
|
378
|
+
default:
|
|
379
|
+
return `Agent \u72B6\u6001: ${status}`;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function stringifyResult(result) {
|
|
383
|
+
if (typeof result === "string") return result;
|
|
384
|
+
if (result === void 0) return "";
|
|
385
|
+
try {
|
|
386
|
+
return JSON.stringify(result);
|
|
387
|
+
} catch {
|
|
388
|
+
return String(result);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function isErrorResult(result) {
|
|
392
|
+
if (!result || typeof result !== "object") return false;
|
|
393
|
+
const record = result;
|
|
394
|
+
return record.error === true || record.is_error === true || typeof record.error === "string";
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// src/agent/cursor/sdk-worker.ts
|
|
398
|
+
function readConfig() {
|
|
399
|
+
const raw = process.env.LARK_CURSOR_SDK_CONFIG;
|
|
400
|
+
if (!raw) return void 0;
|
|
401
|
+
try {
|
|
402
|
+
return JSON.parse(raw);
|
|
403
|
+
} catch {
|
|
404
|
+
return void 0;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
var config = readConfig();
|
|
408
|
+
var agent;
|
|
409
|
+
var agentCwd;
|
|
410
|
+
var activeRunId;
|
|
411
|
+
var activeAbort;
|
|
412
|
+
var ensureQueue = Promise.resolve();
|
|
413
|
+
var runQueue = Promise.resolve();
|
|
414
|
+
var pendingRunRecoveryNotes = [];
|
|
415
|
+
var RATE_LIMIT_RETRY_DELAYS_MS = [1e3, 2500, 5e3];
|
|
416
|
+
var RECOVERY_NOTE_MAX = 6;
|
|
417
|
+
function send(msg) {
|
|
418
|
+
if (typeof process.send === "function") process.send(msg);
|
|
419
|
+
}
|
|
420
|
+
function reportWorkerError(phase, err, runId, recoveryNotes = [], fatal = false) {
|
|
421
|
+
process.stderr.write(`${formatSdkErrorForStderr(phase, err)}
|
|
422
|
+
`);
|
|
423
|
+
send({
|
|
424
|
+
type: "error",
|
|
425
|
+
id: runId,
|
|
426
|
+
message: withWorkerRecoveryHint(withRecoverySummary(formatSdkErrorForIpc(phase, err), recoveryNotes), fatal),
|
|
427
|
+
fatal
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
async function withRecoverableRetry(label, task, recoveryNotes) {
|
|
431
|
+
for (let attempt = 0; ; attempt++) {
|
|
432
|
+
try {
|
|
433
|
+
return await task();
|
|
434
|
+
} catch (err) {
|
|
435
|
+
const delayMs = RATE_LIMIT_RETRY_DELAYS_MS[attempt];
|
|
436
|
+
const kind = isCursorRateLimitError(err) ? "\u9650\u6D41" : isCursorNetworkError(err) ? "\u7F51\u7EDC\u9519\u8BEF" : void 0;
|
|
437
|
+
if (delayMs === void 0 || !kind) throw err;
|
|
438
|
+
addRecoveryNote(
|
|
439
|
+
recoveryNotes,
|
|
440
|
+
`${label} \u9047\u5230${kind}\uFF0C\u5DF2\u81EA\u52A8\u91CD\u8BD5 ${attempt + 1}/${RATE_LIMIT_RETRY_DELAYS_MS.length} \u6B21`
|
|
441
|
+
);
|
|
442
|
+
process.stderr.write(
|
|
443
|
+
`[sdk-worker] ${label} recoverable ${kind}; retrying in ${delayMs}ms (attempt ${attempt + 2})
|
|
444
|
+
`
|
|
445
|
+
);
|
|
446
|
+
await sleep(delayMs);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function agentOptions(cwd) {
|
|
451
|
+
return {
|
|
452
|
+
...config?.apiKey ? { apiKey: config.apiKey } : {},
|
|
453
|
+
model: config.model,
|
|
454
|
+
local: { cwd }
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function queueEnsure(task) {
|
|
458
|
+
ensureQueue = ensureQueue.then(task).catch((err) => {
|
|
459
|
+
reportWorkerError("sdk ensure failed", err);
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
function queueRun(id, task) {
|
|
463
|
+
runQueue = runQueue.then(task).catch((err) => {
|
|
464
|
+
reportWorkerError("sdk run queue failed", err, id);
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
async function ensureAgent(id, cwd, agentId, allowReplacement = true) {
|
|
468
|
+
if (!config) {
|
|
469
|
+
send({ type: "error", id, message: "sdk worker config missing" });
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (agent && agent.agentId === agentId) {
|
|
473
|
+
send({ type: "agent", id, agentId: agent.agentId });
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (agent) {
|
|
477
|
+
try {
|
|
478
|
+
agent.close();
|
|
479
|
+
} catch {
|
|
480
|
+
}
|
|
481
|
+
agent = void 0;
|
|
482
|
+
}
|
|
483
|
+
const opts = agentOptions(cwd);
|
|
484
|
+
const recoveryNotes = [];
|
|
485
|
+
try {
|
|
486
|
+
if (agentId) {
|
|
487
|
+
try {
|
|
488
|
+
agent = await withRecoverableRetry("Agent.resume", () => Agent.resume(agentId, opts), recoveryNotes);
|
|
489
|
+
} catch (err) {
|
|
490
|
+
if (!isCursorAgentNotFoundError(err, agentId)) throw err;
|
|
491
|
+
if (!allowReplacement) {
|
|
492
|
+
addRecoveryNote(
|
|
493
|
+
recoveryNotes,
|
|
494
|
+
`\u539F SDK session \u4E0D\u53EF\u6062\u590D\uFF0C\u672A\u81EA\u52A8\u521B\u5EFA replacement session`
|
|
495
|
+
);
|
|
496
|
+
throw err;
|
|
497
|
+
}
|
|
498
|
+
process.stderr.write(
|
|
499
|
+
`[sdk-worker] stale SDK agent ${agentId}; creating a replacement session
|
|
500
|
+
`
|
|
501
|
+
);
|
|
502
|
+
addRecoveryNote(
|
|
503
|
+
recoveryNotes,
|
|
504
|
+
`\u65E7 SDK session \u4E0D\u53EF\u6062\u590D\uFF0C\u5DF2\u81EA\u52A8\u521B\u5EFA replacement session`
|
|
505
|
+
);
|
|
506
|
+
agent = await withRecoverableRetry("Agent.create", () => Agent.create(opts), recoveryNotes);
|
|
507
|
+
}
|
|
508
|
+
} else {
|
|
509
|
+
agent = await withRecoverableRetry("Agent.create", () => Agent.create(opts), recoveryNotes);
|
|
510
|
+
}
|
|
511
|
+
agentCwd = cwd;
|
|
512
|
+
if (id.startsWith("ensure-")) stashRunRecoveryNotes(recoveryNotes);
|
|
513
|
+
send({ type: "agent", id, agentId: agent.agentId });
|
|
514
|
+
} catch (err) {
|
|
515
|
+
reportWorkerError("sdk agent init failed", err, id);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
async function sendPromptWithStaleSessionRecovery(id, prompt, recoveryNotes, allowReplacement = true) {
|
|
519
|
+
if (!agent) throw new Error("sdk worker agent not initialized");
|
|
520
|
+
try {
|
|
521
|
+
return await withRecoverableRetry("agent.send", () => agent.send(buildCursorPrompt(prompt)), recoveryNotes);
|
|
522
|
+
} catch (err) {
|
|
523
|
+
const staleAgentId = agent.agentId;
|
|
524
|
+
if (!agentCwd || !isCursorAgentActiveRunError(err, staleAgentId)) throw err;
|
|
525
|
+
if (!allowReplacement) {
|
|
526
|
+
addRecoveryNote(
|
|
527
|
+
recoveryNotes,
|
|
528
|
+
`\u539F SDK session \u4ECD\u6709 active run\uFF0C\u672A\u81EA\u52A8\u521B\u5EFA replacement session`
|
|
529
|
+
);
|
|
530
|
+
throw err;
|
|
531
|
+
}
|
|
532
|
+
const replacementCwd = agentCwd;
|
|
533
|
+
process.stderr.write(
|
|
534
|
+
`[sdk-worker] SDK agent ${staleAgentId} still has an active run; creating a replacement session
|
|
535
|
+
`
|
|
536
|
+
);
|
|
537
|
+
addRecoveryNote(
|
|
538
|
+
recoveryNotes,
|
|
539
|
+
`\u68C0\u6D4B\u5230\u65E7 SDK session \u4ECD\u6709 active run\uFF0C\u5DF2\u81EA\u52A8\u521B\u5EFA replacement session`
|
|
540
|
+
);
|
|
541
|
+
try {
|
|
542
|
+
agent.close();
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
545
|
+
agent = await withRecoverableRetry("Agent.create", () => Agent.create(agentOptions(replacementCwd)), recoveryNotes);
|
|
546
|
+
send({ type: "event", id, event: { type: "system", sessionId: agent.agentId, cwd: replacementCwd } });
|
|
547
|
+
return await withRecoverableRetry("agent.send", () => agent.send(buildCursorPrompt(prompt)), recoveryNotes);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function sleep(ms) {
|
|
551
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
552
|
+
}
|
|
553
|
+
async function handleRun(id, prompt, allowReplacement = true) {
|
|
554
|
+
if (!agent) {
|
|
555
|
+
send({ type: "error", id, message: "sdk worker agent not initialized" });
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (activeRunId) {
|
|
559
|
+
send({ type: "error", id, message: "run already active in sdk worker" });
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
activeRunId = id;
|
|
563
|
+
const recoveryNotes = consumeRunRecoveryNotes();
|
|
564
|
+
let cancelled = false;
|
|
565
|
+
activeAbort = () => {
|
|
566
|
+
cancelled = true;
|
|
567
|
+
};
|
|
568
|
+
try {
|
|
569
|
+
const run = await sendPromptWithStaleSessionRecovery(id, prompt, recoveryNotes, allowReplacement);
|
|
570
|
+
activeAbort = () => {
|
|
571
|
+
cancelled = true;
|
|
572
|
+
void run.cancel().catch(() => {
|
|
573
|
+
});
|
|
574
|
+
};
|
|
575
|
+
try {
|
|
576
|
+
for await (const msg of run.stream()) {
|
|
577
|
+
if (cancelled) break;
|
|
578
|
+
for (const event of translateSdkMessage(msg)) {
|
|
579
|
+
send({ type: "event", id, event });
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
} catch (streamErr) {
|
|
583
|
+
reportWorkerError("sdk run stream failed", streamErr, id, recoveryNotes, true);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
if (cancelled) {
|
|
587
|
+
send({ type: "error", id, message: "run cancelled" });
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
const result = await run.wait();
|
|
591
|
+
if (result.status === "error") {
|
|
592
|
+
const message = withWorkerRecoveryHint(withRecoverySummary(formatRunResultError(result), recoveryNotes), true);
|
|
593
|
+
process.stderr.write(`[sdk-worker] ${message}
|
|
594
|
+
`);
|
|
595
|
+
send({
|
|
596
|
+
type: "error",
|
|
597
|
+
id,
|
|
598
|
+
message,
|
|
599
|
+
fatal: true
|
|
600
|
+
});
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
send({ type: "done", id, agentId: agent.agentId });
|
|
604
|
+
} catch (err) {
|
|
605
|
+
reportWorkerError("sdk run failed", err, id, recoveryNotes, true);
|
|
606
|
+
} finally {
|
|
607
|
+
activeRunId = void 0;
|
|
608
|
+
activeAbort = void 0;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
function formatRunResultError(result) {
|
|
612
|
+
const headline = `sdk run failed (runId=${result.id}, status=${result.status})`;
|
|
613
|
+
if (typeof result.result === "string" && result.result.trim()) {
|
|
614
|
+
return `${headline}: ${result.result.trim()}`;
|
|
615
|
+
}
|
|
616
|
+
const diagnostic = safeJson({
|
|
617
|
+
id: result.id,
|
|
618
|
+
status: result.status,
|
|
619
|
+
result: result.result
|
|
620
|
+
});
|
|
621
|
+
return `${headline}; Cursor returned no error detail${diagnostic ? ` | result=${diagnostic}` : ""}`;
|
|
622
|
+
}
|
|
623
|
+
function safeJson(value) {
|
|
624
|
+
try {
|
|
625
|
+
return JSON.stringify(value).slice(0, 1200);
|
|
626
|
+
} catch {
|
|
627
|
+
return "";
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
function addRecoveryNote(notes, note) {
|
|
631
|
+
if (!notes || notes.includes(note)) return;
|
|
632
|
+
notes.push(note);
|
|
633
|
+
if (notes.length > RECOVERY_NOTE_MAX) notes.splice(0, notes.length - RECOVERY_NOTE_MAX);
|
|
634
|
+
}
|
|
635
|
+
function stashRunRecoveryNotes(notes) {
|
|
636
|
+
for (const note of notes) addRecoveryNote(pendingRunRecoveryNotes, note);
|
|
637
|
+
}
|
|
638
|
+
function consumeRunRecoveryNotes() {
|
|
639
|
+
const notes = pendingRunRecoveryNotes;
|
|
640
|
+
pendingRunRecoveryNotes = [];
|
|
641
|
+
return notes;
|
|
642
|
+
}
|
|
643
|
+
function withRecoverySummary(message, recoveryNotes) {
|
|
644
|
+
if (recoveryNotes.length === 0) return message;
|
|
645
|
+
return [
|
|
646
|
+
`\u5DF2\u81EA\u52A8\u6062\u590D/\u91CD\u8BD5 ${recoveryNotes.length} \u6B65\uFF1A`,
|
|
647
|
+
...recoveryNotes.map((note) => `- ${note}`),
|
|
648
|
+
"",
|
|
649
|
+
`\u6700\u7EC8\u5931\u8D25\u539F\u56E0\uFF1A${message}`
|
|
650
|
+
].join("\n");
|
|
651
|
+
}
|
|
652
|
+
function withWorkerRecoveryHint(message, fatal) {
|
|
653
|
+
if (!fatal) return message;
|
|
654
|
+
return `${message}
|
|
655
|
+
|
|
656
|
+
\u5DF2\u81EA\u52A8\u4E22\u5F03\u5F53\u524D SDK worker\u3002bridge \u4F1A\u4F18\u5148\u7528\u539F session \u521B\u5EFA\u65B0 worker \u5E76\u7EE7\u7EED\u4E00\u6B21\uFF1B\u82E5\u539F session \u4E0D\u53EF\u6062\u590D\u6216\u4ECD\u6709 active run\uFF0C\u5C06\u505C\u6B62\u81EA\u52A8\u7EE7\u7EED\uFF0C\u4FDD\u7559\u4E00\u952E\u91CD\u8BD5/\u65B0 session \u5165\u53E3\u3002`;
|
|
657
|
+
}
|
|
658
|
+
process.on("uncaughtException", (err) => {
|
|
659
|
+
reportWorkerError("uncaughtException", err, activeRunId, [], true);
|
|
660
|
+
activeRunId = void 0;
|
|
661
|
+
activeAbort = void 0;
|
|
662
|
+
});
|
|
663
|
+
process.on("unhandledRejection", (reason) => {
|
|
664
|
+
reportWorkerError("unhandledRejection", reason, activeRunId, [], true);
|
|
665
|
+
activeRunId = void 0;
|
|
666
|
+
activeAbort = void 0;
|
|
667
|
+
});
|
|
668
|
+
process.on("message", (msg) => {
|
|
669
|
+
if (!msg || typeof msg !== "object") return;
|
|
670
|
+
switch (msg.type) {
|
|
671
|
+
case "ensure":
|
|
672
|
+
if (agent && msg.agentId !== void 0 && agent.agentId === msg.agentId) {
|
|
673
|
+
send({ type: "agent", id: msg.id, agentId: agent.agentId });
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
queueEnsure(() => ensureAgent(msg.id, msg.cwd, msg.agentId, msg.allowReplacement));
|
|
677
|
+
return;
|
|
678
|
+
case "run":
|
|
679
|
+
queueRun(msg.id, () => handleRun(msg.id, msg.prompt, msg.allowReplacement));
|
|
680
|
+
return;
|
|
681
|
+
case "stop":
|
|
682
|
+
if (activeRunId === msg.id) activeAbort?.();
|
|
683
|
+
return;
|
|
684
|
+
case "shutdown":
|
|
685
|
+
if (activeRunId) activeAbort?.();
|
|
686
|
+
if (agent) {
|
|
687
|
+
try {
|
|
688
|
+
agent.close();
|
|
689
|
+
} catch {
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
process.exit(0);
|
|
693
|
+
return;
|
|
694
|
+
default:
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
if (config) {
|
|
699
|
+
send({ type: "ready" });
|
|
700
|
+
} else {
|
|
701
|
+
send({ type: "error", message: "sdk worker config missing" });
|
|
702
|
+
}
|