@yachiyo-5i/xlyra-agent 1.0.2 → 1.1.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/dist/{chunk-QH6SEOO6.js → chunk-3DOSX63Z.js} +545 -119
- package/dist/chunk-3DOSX63Z.js.map +1 -0
- package/dist/cli.cjs +560 -127
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +18 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +555 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +229 -53
- package/dist/index.d.ts +229 -53
- package/dist/index.js +13 -1
- package/package.json +3 -2
- package/dist/chunk-QH6SEOO6.js.map +0 -1
|
@@ -58,6 +58,176 @@ var LlmError = class extends Error {
|
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
// src/llm/credential.ts
|
|
62
|
+
var StaticCredentialProvider = class {
|
|
63
|
+
constructor(credential) {
|
|
64
|
+
this.credential = credential;
|
|
65
|
+
}
|
|
66
|
+
credential;
|
|
67
|
+
getCredential() {
|
|
68
|
+
return Promise.resolve(this.credential);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var XlyraCallbackCredentialProvider = class {
|
|
72
|
+
constructor(url, opts) {
|
|
73
|
+
this.url = url;
|
|
74
|
+
this.fetchImpl = opts?.fetchImpl ?? fetch;
|
|
75
|
+
}
|
|
76
|
+
url;
|
|
77
|
+
context = null;
|
|
78
|
+
/** runId → 当前有效 token */
|
|
79
|
+
tokensByRun = /* @__PURE__ */ new Map();
|
|
80
|
+
/** runId → 进行中的 callback(并发去重) */
|
|
81
|
+
inflight = /* @__PURE__ */ new Map();
|
|
82
|
+
fetchImpl;
|
|
83
|
+
/** 更新当前 run 上下文。token 按 runId 隔离,切换 run 不影响其他 run 的 token */
|
|
84
|
+
setRunContext(context) {
|
|
85
|
+
this.context = context;
|
|
86
|
+
}
|
|
87
|
+
async getCredential() {
|
|
88
|
+
const context = this.requireContext();
|
|
89
|
+
const cached = this.tokensByRun.get(context.runId);
|
|
90
|
+
if (cached) return cached;
|
|
91
|
+
const refreshed = await this.refreshCredential("initial");
|
|
92
|
+
if (!refreshed) {
|
|
93
|
+
throw new LlmError("xLyra LLM \u8C03\u7528\u51ED\u8BC1\u4E0D\u53EF\u7528\uFF1Arun \u5DF2\u7ED3\u675F\u6216 callback \u672A\u7B7E\u53D1 token");
|
|
94
|
+
}
|
|
95
|
+
return refreshed;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 按 run 取凭证(并发安全):chatStream 在请求开始时调用一次并全程使用
|
|
99
|
+
* 返回值——并发 run 各自的 token 互不干扰(setRunContext 只影响下一次
|
|
100
|
+
* 请求开始时的解析)。
|
|
101
|
+
*/
|
|
102
|
+
async getCredentialFor(runId) {
|
|
103
|
+
const cached = this.tokensByRun.get(runId);
|
|
104
|
+
if (cached) return cached;
|
|
105
|
+
const refreshed = await this.refreshCredentialFor(runId, "initial");
|
|
106
|
+
if (!refreshed) {
|
|
107
|
+
throw new LlmError("xLyra LLM \u8C03\u7528\u51ED\u8BC1\u4E0D\u53EF\u7528\uFF1Arun \u5DF2\u7ED3\u675F\u6216 callback \u672A\u7B7E\u53D1 token");
|
|
108
|
+
}
|
|
109
|
+
return refreshed;
|
|
110
|
+
}
|
|
111
|
+
/** 按 run 刷新(401 重试用):与 getCredentialFor 相同的并发去重 */
|
|
112
|
+
refreshCredentialFor(runId, reason) {
|
|
113
|
+
const context = this.requireContext();
|
|
114
|
+
if (context.runId !== runId) return Promise.resolve(null);
|
|
115
|
+
return this.refreshCredential(reason);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 调用 xLyra credential callback 取新 token(并发去重:同一 run 的并发
|
|
119
|
+
* 调用合并为一次 in-flight 请求)。
|
|
120
|
+
* 返回 null:run inactive(active: false)或 callback 失败——调用方不得重试。
|
|
121
|
+
* token 不进入任何日志/错误信息。
|
|
122
|
+
*/
|
|
123
|
+
refreshCredential(reason) {
|
|
124
|
+
const context = this.requireContext();
|
|
125
|
+
const pending = this.inflight.get(context.runId);
|
|
126
|
+
if (pending) return pending;
|
|
127
|
+
const task = this.doRefresh(context, reason).finally(() => {
|
|
128
|
+
this.inflight.delete(context.runId);
|
|
129
|
+
});
|
|
130
|
+
this.inflight.set(context.runId, task);
|
|
131
|
+
return task;
|
|
132
|
+
}
|
|
133
|
+
requireContext() {
|
|
134
|
+
if (!this.context) {
|
|
135
|
+
throw new LlmError("xLyra LLM \u51ED\u8BC1\u7F3A\u5C11 run \u4E0A\u4E0B\u6587\uFF08agent_instance_id/session_id/run_id\uFF09");
|
|
136
|
+
}
|
|
137
|
+
return this.context;
|
|
138
|
+
}
|
|
139
|
+
async doRefresh(context, reason) {
|
|
140
|
+
let res;
|
|
141
|
+
try {
|
|
142
|
+
res = await this.fetchImpl(this.url, {
|
|
143
|
+
method: "POST",
|
|
144
|
+
headers: { "content-type": "application/json" },
|
|
145
|
+
body: JSON.stringify({
|
|
146
|
+
agent_instance_id: context.agentInstanceId,
|
|
147
|
+
session_id: context.sessionId,
|
|
148
|
+
run_id: context.runId,
|
|
149
|
+
model: context.model,
|
|
150
|
+
reason
|
|
151
|
+
})
|
|
152
|
+
});
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
if (!res.ok) return null;
|
|
157
|
+
let payload;
|
|
158
|
+
try {
|
|
159
|
+
payload = await res.json();
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
if (payload.active !== true || typeof payload.token !== "string" || !payload.token) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
const credential = {
|
|
167
|
+
kind: "temporary-bearer",
|
|
168
|
+
headerName: "authorization",
|
|
169
|
+
headerValue: `Bearer ${payload.token}`
|
|
170
|
+
};
|
|
171
|
+
this.tokensByRun.set(context.runId, credential);
|
|
172
|
+
if (this.tokensByRun.size > 64) {
|
|
173
|
+
const oldest = this.tokensByRun.keys().next().value;
|
|
174
|
+
if (oldest !== void 0) this.tokensByRun.delete(oldest);
|
|
175
|
+
}
|
|
176
|
+
return credential;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
function resolveCredentialProvider(endpoint) {
|
|
180
|
+
if (endpoint.mode === "remote-xlyra") {
|
|
181
|
+
if (endpoint.credential?.type === "xlyra-callback") {
|
|
182
|
+
return new XlyraCallbackCredentialProvider(endpoint.credential.url);
|
|
183
|
+
}
|
|
184
|
+
const token = endpoint.credential?.token;
|
|
185
|
+
if (!token) throw new LlmError(`\u7AEF\u70B9 ${endpoint.name} \u672A\u914D\u7F6E\u4E34\u65F6 LLM \u51ED\u8BC1`);
|
|
186
|
+
return new StaticCredentialProvider({
|
|
187
|
+
kind: "temporary-bearer",
|
|
188
|
+
headerName: "authorization",
|
|
189
|
+
headerValue: `Bearer ${token}`
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (!endpoint.api_key) {
|
|
193
|
+
throw new LlmError(`\u7AEF\u70B9 ${endpoint.name} \u672A\u914D\u7F6E api_key`);
|
|
194
|
+
}
|
|
195
|
+
return new StaticCredentialProvider(
|
|
196
|
+
endpoint.protocol === "anthropic-messages" ? { kind: "api-key", headerName: "x-api-key", headerValue: endpoint.api_key } : {
|
|
197
|
+
kind: "api-key",
|
|
198
|
+
headerName: "authorization",
|
|
199
|
+
headerValue: `Bearer ${endpoint.api_key}`
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
function isRefreshable(provider) {
|
|
204
|
+
return provider instanceof XlyraCallbackCredentialProvider;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/llm/http-error.ts
|
|
208
|
+
async function httpErrorSummary(res, secrets = []) {
|
|
209
|
+
const raw = await res.text().catch(() => "");
|
|
210
|
+
let msg = "";
|
|
211
|
+
try {
|
|
212
|
+
const parsed = JSON.parse(raw);
|
|
213
|
+
if (parsed && typeof parsed === "object") {
|
|
214
|
+
const j = parsed;
|
|
215
|
+
const candidate = j.error?.message ?? j.message;
|
|
216
|
+
if (typeof candidate === "string") msg = candidate;
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
msg = msg.slice(0, 200);
|
|
221
|
+
for (const secret of secrets) {
|
|
222
|
+
if (secret) msg = msg.split(secret).join("***");
|
|
223
|
+
}
|
|
224
|
+
return `\u8BF7\u6C42\u5931\u8D25\uFF08HTTP ${res.status}\uFF09\uFF1A${msg || res.statusText}`;
|
|
225
|
+
}
|
|
226
|
+
function credentialSecrets(cred) {
|
|
227
|
+
const bare = cred.headerValue.startsWith("Bearer ") ? cred.headerValue.slice("Bearer ".length) : cred.headerValue;
|
|
228
|
+
return bare === cred.headerValue ? [cred.headerValue] : [cred.headerValue, bare];
|
|
229
|
+
}
|
|
230
|
+
|
|
61
231
|
// src/llm/sse.ts
|
|
62
232
|
async function* parseSse(body) {
|
|
63
233
|
const reader = body.getReader();
|
|
@@ -200,17 +370,50 @@ function toAnthropicTools(tools) {
|
|
|
200
370
|
var AnthropicMessagesProtocol = class {
|
|
201
371
|
name = "anthropic-messages";
|
|
202
372
|
baseUrl;
|
|
203
|
-
|
|
373
|
+
credential;
|
|
204
374
|
provider;
|
|
205
375
|
apiVersion;
|
|
206
376
|
fetchImpl;
|
|
377
|
+
runContext = null;
|
|
207
378
|
constructor(opts) {
|
|
208
379
|
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
209
|
-
|
|
380
|
+
if (opts.credential) {
|
|
381
|
+
this.credential = opts.credential;
|
|
382
|
+
} else if (opts.apiKey) {
|
|
383
|
+
this.credential = new StaticCredentialProvider({
|
|
384
|
+
kind: "api-key",
|
|
385
|
+
headerName: "x-api-key",
|
|
386
|
+
headerValue: opts.apiKey
|
|
387
|
+
});
|
|
388
|
+
} else {
|
|
389
|
+
throw new LlmError("AnthropicMessagesProtocol \u7F3A\u5C11\u51ED\u8BC1\uFF08credential \u6216 apiKey\uFF09");
|
|
390
|
+
}
|
|
210
391
|
this.provider = opts.providerName ?? "anthropic";
|
|
211
392
|
this.apiVersion = opts.apiVersion ?? "2023-06-01";
|
|
212
393
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
213
394
|
}
|
|
395
|
+
/** 更新 run 上下文:仅 xlyra-callback 凭证消费;静态凭证为 no-op */
|
|
396
|
+
setRunContext(context) {
|
|
397
|
+
this.runContext = context;
|
|
398
|
+
if (isRefreshable(this.credential)) this.credential.setRunContext(context);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* 请求开始时解析本次 run 的凭证:xlyra-callback 按 runId 定向取
|
|
402
|
+
* (并发 run 各自独立),静态凭证直接返回。
|
|
403
|
+
*/
|
|
404
|
+
resolveRequestCredential() {
|
|
405
|
+
if (isRefreshable(this.credential) && this.runContext) {
|
|
406
|
+
return this.credential.getCredentialFor(this.runContext.runId);
|
|
407
|
+
}
|
|
408
|
+
return this.credential.getCredential();
|
|
409
|
+
}
|
|
410
|
+
/** 401 后按本次 run 定向刷新(run 上下文缺失时无法刷新) */
|
|
411
|
+
refreshRequestCredential() {
|
|
412
|
+
if (isRefreshable(this.credential) && this.runContext) {
|
|
413
|
+
return this.credential.refreshCredentialFor(this.runContext.runId, "unauthorized");
|
|
414
|
+
}
|
|
415
|
+
return Promise.resolve(null);
|
|
416
|
+
}
|
|
214
417
|
async *chatStream(req) {
|
|
215
418
|
const { system, messages } = toAnthropicMessages(req.messages);
|
|
216
419
|
const maxTokens = req.settings.max_tokens ?? DEFAULT_MAX_TOKENS;
|
|
@@ -222,29 +425,59 @@ var AnthropicMessagesProtocol = class {
|
|
|
222
425
|
const budget = Math.min(THINKING_BUDGET[req.settings.reasoning_effort], maxTokens - 1);
|
|
223
426
|
if (budget >= 1024) body.thinking = { type: "enabled", budget_tokens: budget };
|
|
224
427
|
}
|
|
428
|
+
let cred;
|
|
429
|
+
try {
|
|
430
|
+
cred = await this.resolveRequestCredential();
|
|
431
|
+
} catch (err) {
|
|
432
|
+
yield { type: "error", error: errMsg(err) };
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
const doFetch = (c) => this.fetchImpl(`${this.baseUrl}/v1/messages`, {
|
|
436
|
+
method: "POST",
|
|
437
|
+
headers: {
|
|
438
|
+
"content-type": "application/json",
|
|
439
|
+
[c.headerName]: c.headerValue,
|
|
440
|
+
"anthropic-version": this.apiVersion
|
|
441
|
+
},
|
|
442
|
+
body: JSON.stringify(body),
|
|
443
|
+
signal: req.signal ?? null
|
|
444
|
+
});
|
|
225
445
|
let res;
|
|
226
446
|
try {
|
|
227
|
-
res = await
|
|
228
|
-
method: "POST",
|
|
229
|
-
headers: {
|
|
230
|
-
"content-type": "application/json",
|
|
231
|
-
"x-api-key": this.apiKey,
|
|
232
|
-
"anthropic-version": this.apiVersion
|
|
233
|
-
},
|
|
234
|
-
body: JSON.stringify(body),
|
|
235
|
-
signal: req.signal ?? null
|
|
236
|
-
});
|
|
447
|
+
res = await doFetch(cred);
|
|
237
448
|
} catch (err) {
|
|
238
449
|
if (req.signal?.aborted) return;
|
|
239
450
|
yield { type: "error", error: `\u8BF7\u6C42\u5931\u8D25\uFF08\u7F51\u7EDC\u9519\u8BEF\uFF09\uFF1A${errMsg(err)}` };
|
|
240
451
|
return;
|
|
241
452
|
}
|
|
453
|
+
if (res.status === 401 && isRefreshable(this.credential)) {
|
|
454
|
+
await res.body?.cancel().catch(() => {
|
|
455
|
+
});
|
|
456
|
+
let refreshed = null;
|
|
457
|
+
try {
|
|
458
|
+
refreshed = await this.refreshRequestCredential();
|
|
459
|
+
} catch (err) {
|
|
460
|
+
yield { type: "error", error: errMsg(err) };
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (!refreshed) {
|
|
464
|
+
yield {
|
|
465
|
+
type: "error",
|
|
466
|
+
error: "xLyra LLM \u8C03\u7528\u51ED\u8BC1\u5DF2\u8FC7\u671F\uFF0C\u4E14 xLyra \u672A\u7B7E\u53D1\u65B0 token\uFF08run \u5DF2\u7ED3\u675F\u6216\u4E0D\u53EF\u7528\uFF09\uFF08HTTP 401\uFF09"
|
|
467
|
+
};
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
cred = refreshed;
|
|
471
|
+
try {
|
|
472
|
+
res = await doFetch(cred);
|
|
473
|
+
} catch (err) {
|
|
474
|
+
if (req.signal?.aborted) return;
|
|
475
|
+
yield { type: "error", error: `\u8BF7\u6C42\u5931\u8D25\uFF08\u7F51\u7EDC\u9519\u8BEF\uFF09\uFF1A${errMsg(err)}` };
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
242
479
|
if (!res.ok || !res.body) {
|
|
243
|
-
|
|
244
|
-
yield {
|
|
245
|
-
type: "error",
|
|
246
|
-
error: `\u8BF7\u6C42\u5931\u8D25\uFF08HTTP ${res.status}\uFF09\uFF1A${detail.slice(0, 500) || res.statusText}`
|
|
247
|
-
};
|
|
480
|
+
yield { type: "error", error: await httpErrorSummary(res, credentialSecrets(cred)) };
|
|
248
481
|
return;
|
|
249
482
|
}
|
|
250
483
|
const blockKinds = /* @__PURE__ */ new Map();
|
|
@@ -438,15 +671,48 @@ function responseFromCompleted(response, provider) {
|
|
|
438
671
|
var OpenAIResponsesProtocol = class {
|
|
439
672
|
name = "openai-responses";
|
|
440
673
|
baseUrl;
|
|
441
|
-
|
|
674
|
+
credential;
|
|
442
675
|
provider;
|
|
443
676
|
fetchImpl;
|
|
677
|
+
runContext = null;
|
|
444
678
|
constructor(opts) {
|
|
445
679
|
this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
|
|
446
|
-
|
|
680
|
+
if (opts.credential) {
|
|
681
|
+
this.credential = opts.credential;
|
|
682
|
+
} else if (opts.apiKey) {
|
|
683
|
+
this.credential = new StaticCredentialProvider({
|
|
684
|
+
kind: "api-key",
|
|
685
|
+
headerName: "authorization",
|
|
686
|
+
headerValue: `Bearer ${opts.apiKey}`
|
|
687
|
+
});
|
|
688
|
+
} else {
|
|
689
|
+
throw new LlmError("OpenAIResponsesProtocol \u7F3A\u5C11\u51ED\u8BC1\uFF08credential \u6216 apiKey\uFF09");
|
|
690
|
+
}
|
|
447
691
|
this.provider = opts.providerName ?? "openai";
|
|
448
692
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
449
693
|
}
|
|
694
|
+
/** 更新 run 上下文:仅 xlyra-callback 凭证消费;静态凭证为 no-op */
|
|
695
|
+
setRunContext(context) {
|
|
696
|
+
this.runContext = context;
|
|
697
|
+
if (isRefreshable(this.credential)) this.credential.setRunContext(context);
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* 请求开始时解析本次 run 的凭证:xlyra-callback 按 runId 定向取
|
|
701
|
+
* (并发 run 各自独立),静态凭证直接返回。
|
|
702
|
+
*/
|
|
703
|
+
resolveRequestCredential() {
|
|
704
|
+
if (isRefreshable(this.credential) && this.runContext) {
|
|
705
|
+
return this.credential.getCredentialFor(this.runContext.runId);
|
|
706
|
+
}
|
|
707
|
+
return this.credential.getCredential();
|
|
708
|
+
}
|
|
709
|
+
/** 401 后按本次 run 定向刷新(run 上下文缺失时无法刷新) */
|
|
710
|
+
refreshRequestCredential() {
|
|
711
|
+
if (isRefreshable(this.credential) && this.runContext) {
|
|
712
|
+
return this.credential.refreshCredentialFor(this.runContext.runId, "unauthorized");
|
|
713
|
+
}
|
|
714
|
+
return Promise.resolve(null);
|
|
715
|
+
}
|
|
450
716
|
async *chatStream(req) {
|
|
451
717
|
const { instructions, input } = toResponsesInput(req.messages);
|
|
452
718
|
const body = { model: req.model, input, stream: true };
|
|
@@ -458,28 +724,58 @@ var OpenAIResponsesProtocol = class {
|
|
|
458
724
|
body.reasoning = { effort: req.settings.reasoning_effort, summary: "auto" };
|
|
459
725
|
body.include = ["reasoning.encrypted_content"];
|
|
460
726
|
}
|
|
727
|
+
let cred;
|
|
728
|
+
try {
|
|
729
|
+
cred = await this.resolveRequestCredential();
|
|
730
|
+
} catch (err) {
|
|
731
|
+
yield { type: "error", error: errMsg2(err) };
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
const doFetch = (c) => this.fetchImpl(`${this.baseUrl}/responses`, {
|
|
735
|
+
method: "POST",
|
|
736
|
+
headers: {
|
|
737
|
+
"content-type": "application/json",
|
|
738
|
+
[c.headerName]: c.headerValue
|
|
739
|
+
},
|
|
740
|
+
body: JSON.stringify(body),
|
|
741
|
+
signal: req.signal ?? null
|
|
742
|
+
});
|
|
461
743
|
let res;
|
|
462
744
|
try {
|
|
463
|
-
res = await
|
|
464
|
-
method: "POST",
|
|
465
|
-
headers: {
|
|
466
|
-
"content-type": "application/json",
|
|
467
|
-
authorization: `Bearer ${this.apiKey}`
|
|
468
|
-
},
|
|
469
|
-
body: JSON.stringify(body),
|
|
470
|
-
signal: req.signal ?? null
|
|
471
|
-
});
|
|
745
|
+
res = await doFetch(cred);
|
|
472
746
|
} catch (err) {
|
|
473
747
|
if (req.signal?.aborted) return;
|
|
474
748
|
yield { type: "error", error: `\u8BF7\u6C42\u5931\u8D25\uFF08\u7F51\u7EDC\u9519\u8BEF\uFF09\uFF1A${errMsg2(err)}` };
|
|
475
749
|
return;
|
|
476
750
|
}
|
|
751
|
+
if (res.status === 401 && isRefreshable(this.credential)) {
|
|
752
|
+
await res.body?.cancel().catch(() => {
|
|
753
|
+
});
|
|
754
|
+
let refreshed = null;
|
|
755
|
+
try {
|
|
756
|
+
refreshed = await this.refreshRequestCredential();
|
|
757
|
+
} catch (err) {
|
|
758
|
+
yield { type: "error", error: errMsg2(err) };
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (!refreshed) {
|
|
762
|
+
yield {
|
|
763
|
+
type: "error",
|
|
764
|
+
error: "xLyra LLM \u8C03\u7528\u51ED\u8BC1\u5DF2\u8FC7\u671F\uFF0C\u4E14 xLyra \u672A\u7B7E\u53D1\u65B0 token\uFF08run \u5DF2\u7ED3\u675F\u6216\u4E0D\u53EF\u7528\uFF09\uFF08HTTP 401\uFF09"
|
|
765
|
+
};
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
cred = refreshed;
|
|
769
|
+
try {
|
|
770
|
+
res = await doFetch(cred);
|
|
771
|
+
} catch (err) {
|
|
772
|
+
if (req.signal?.aborted) return;
|
|
773
|
+
yield { type: "error", error: `\u8BF7\u6C42\u5931\u8D25\uFF08\u7F51\u7EDC\u9519\u8BEF\uFF09\uFF1A${errMsg2(err)}` };
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
477
777
|
if (!res.ok || !res.body) {
|
|
478
|
-
|
|
479
|
-
yield {
|
|
480
|
-
type: "error",
|
|
481
|
-
error: `\u8BF7\u6C42\u5931\u8D25\uFF08HTTP ${res.status}\uFF09\uFF1A${detail.slice(0, 500) || res.statusText}`
|
|
482
|
-
};
|
|
778
|
+
yield { type: "error", error: await httpErrorSummary(res, credentialSecrets(cred)) };
|
|
483
779
|
return;
|
|
484
780
|
}
|
|
485
781
|
const calls = new ToolCallBuffer();
|
|
@@ -635,7 +931,7 @@ var EndpointResolver = class {
|
|
|
635
931
|
if (!protocol) {
|
|
636
932
|
const opts = {
|
|
637
933
|
baseUrl: endpoint.base_url,
|
|
638
|
-
|
|
934
|
+
credential: resolveCredentialProvider(endpoint),
|
|
639
935
|
providerName: endpoint.name
|
|
640
936
|
};
|
|
641
937
|
protocol = endpoint.protocol === "anthropic-messages" ? new AnthropicMessagesProtocol(opts) : new OpenAIResponsesProtocol(opts);
|
|
@@ -1325,6 +1621,7 @@ ${tail}`;
|
|
|
1325
1621
|
}
|
|
1326
1622
|
|
|
1327
1623
|
// src/agent/escalation.ts
|
|
1624
|
+
import path2 from "path";
|
|
1328
1625
|
var COMMAND_EXECUTION_GRANT = "capability://exec_command";
|
|
1329
1626
|
var EscalationRequiredError = class extends Error {
|
|
1330
1627
|
constructor(requestedPath, resolvedPath, message, resourceType = "path", requestedCommand) {
|
|
@@ -1358,7 +1655,8 @@ var EscalationGrants = class {
|
|
|
1358
1655
|
const set = this.grants.get(sessionId);
|
|
1359
1656
|
if (!set) return false;
|
|
1360
1657
|
for (const prefix of set) {
|
|
1361
|
-
|
|
1658
|
+
const relative = path2.relative(prefix, resolvedPath);
|
|
1659
|
+
if (relative === "" || !relative.startsWith("..") && !path2.isAbsolute(relative)) return true;
|
|
1362
1660
|
}
|
|
1363
1661
|
return false;
|
|
1364
1662
|
}
|
|
@@ -1430,6 +1728,14 @@ var AgentRunner = class {
|
|
|
1430
1728
|
if (!contextWindow) {
|
|
1431
1729
|
this.logger.info(`\u6A21\u578B\u672A\u58F0\u660E\u4E0A\u4E0B\u6587\u7A97\u53E3\uFF0C\u81EA\u52A8\u538B\u7F29\u505C\u7528 model=${modelId}`);
|
|
1432
1730
|
}
|
|
1731
|
+
if (runOpts.sessionId) {
|
|
1732
|
+
protocol.setRunContext?.({
|
|
1733
|
+
agentInstanceId: runOpts.agentInstanceId ?? "default",
|
|
1734
|
+
sessionId: runOpts.sessionId,
|
|
1735
|
+
runId,
|
|
1736
|
+
model: modelId
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1433
1739
|
const history = (params.history ?? []).map((m) => chatMessageSchema.parse(m));
|
|
1434
1740
|
const settings = modelSettingsSchema.parse(params.settings ?? {});
|
|
1435
1741
|
const definitions = this.tools.map((t) => t.definition);
|
|
@@ -1686,22 +1992,22 @@ ${availableToolsPrompt}` : buildSystemPrompt({ availableTools: definitions.map((
|
|
|
1686
1992
|
|
|
1687
1993
|
// src/tools/workdir.ts
|
|
1688
1994
|
import fs2 from "fs";
|
|
1689
|
-
import
|
|
1995
|
+
import path3 from "path";
|
|
1690
1996
|
function resolveSandboxed(workdir, raw, opts = {}) {
|
|
1691
1997
|
const baseReal = fs2.realpathSync(workdir);
|
|
1692
|
-
const candidate =
|
|
1998
|
+
const candidate = path3.resolve(path3.isAbsolute(raw) ? raw : path3.join(baseReal, raw));
|
|
1693
1999
|
if (opts.allowOutsideWorkdir) return candidate;
|
|
1694
2000
|
let existing = candidate;
|
|
1695
2001
|
const missing = [];
|
|
1696
2002
|
while (!fs2.existsSync(existing)) {
|
|
1697
|
-
missing.unshift(
|
|
1698
|
-
const parent =
|
|
2003
|
+
missing.unshift(path3.basename(existing));
|
|
2004
|
+
const parent = path3.dirname(existing);
|
|
1699
2005
|
if (parent === existing) break;
|
|
1700
2006
|
existing = parent;
|
|
1701
2007
|
}
|
|
1702
2008
|
const existingReal = fs2.realpathSync(existing);
|
|
1703
|
-
const realTarget =
|
|
1704
|
-
if (realTarget !== baseReal && !realTarget.startsWith(baseReal +
|
|
2009
|
+
const realTarget = path3.join(existingReal, ...missing);
|
|
2010
|
+
if (realTarget !== baseReal && !realTarget.startsWith(baseReal + path3.sep)) {
|
|
1705
2011
|
if (opts.grants && opts.sessionId && opts.grants.isGranted(opts.sessionId, realTarget)) {
|
|
1706
2012
|
return realTarget;
|
|
1707
2013
|
}
|
|
@@ -1713,7 +2019,7 @@ function resolveSandboxed(workdir, raw, opts = {}) {
|
|
|
1713
2019
|
// src/tools/apply-patch.ts
|
|
1714
2020
|
import crypto3 from "crypto";
|
|
1715
2021
|
import fs3 from "fs";
|
|
1716
|
-
import
|
|
2022
|
+
import path4 from "path";
|
|
1717
2023
|
import { z as z4 } from "zod";
|
|
1718
2024
|
var argsSchema = z4.object({ patch: z4.string().min(1) });
|
|
1719
2025
|
function makeApplyPatchTool(workdir, sandbox = {}) {
|
|
@@ -1761,7 +2067,7 @@ function makeApplyPatchTool(workdir, sandbox = {}) {
|
|
|
1761
2067
|
} else {
|
|
1762
2068
|
installedAdds.push(item.file);
|
|
1763
2069
|
}
|
|
1764
|
-
fs3.mkdirSync(
|
|
2070
|
+
fs3.mkdirSync(path4.dirname(item.file), { recursive: true });
|
|
1765
2071
|
fs3.renameSync(item.tmp, item.file);
|
|
1766
2072
|
}
|
|
1767
2073
|
for (const deletion of deletes) {
|
|
@@ -1872,18 +2178,18 @@ function assertRegularFile(rawPath, file) {
|
|
|
1872
2178
|
if (!fs3.statSync(file).isFile()) throw new Error(`${rawPath} \u4E0D\u662F\u666E\u901A\u6587\u4EF6`);
|
|
1873
2179
|
}
|
|
1874
2180
|
function stageWrite(write) {
|
|
1875
|
-
fs3.mkdirSync(
|
|
1876
|
-
const tmp =
|
|
2181
|
+
fs3.mkdirSync(path4.dirname(write.file), { recursive: true });
|
|
2182
|
+
const tmp = path4.join(path4.dirname(write.file), `.xlyra-tmp-${crypto3.randomUUID()}${path4.extname(write.file)}`);
|
|
1877
2183
|
fs3.writeFileSync(tmp, write.content);
|
|
1878
2184
|
return { ...write, tmp };
|
|
1879
2185
|
}
|
|
1880
2186
|
function backupPath(file) {
|
|
1881
|
-
return
|
|
2187
|
+
return path4.join(path4.dirname(file), `.xlyra-backup-${crypto3.randomUUID()}${path4.extname(file)}`);
|
|
1882
2188
|
}
|
|
1883
2189
|
|
|
1884
2190
|
// src/tools/create.ts
|
|
1885
2191
|
import fs4 from "fs";
|
|
1886
|
-
import
|
|
2192
|
+
import path5 from "path";
|
|
1887
2193
|
import { z as z5 } from "zod";
|
|
1888
2194
|
var argsSchema2 = z5.object({
|
|
1889
2195
|
path: z5.string(),
|
|
@@ -1900,7 +2206,7 @@ function makeCreateTool(workdir, sandbox = {}) {
|
|
|
1900
2206
|
handler: async (args) => {
|
|
1901
2207
|
const { path: rawPath, content } = argsSchema2.parse(args);
|
|
1902
2208
|
const file = resolveSandboxed(workdir, rawPath, sandbox);
|
|
1903
|
-
fs4.mkdirSync(
|
|
2209
|
+
fs4.mkdirSync(path5.dirname(file), { recursive: true });
|
|
1904
2210
|
try {
|
|
1905
2211
|
fs4.writeFileSync(file, content, { flag: "wx" });
|
|
1906
2212
|
} catch (err) {
|
|
@@ -1917,7 +2223,7 @@ function makeCreateTool(workdir, sandbox = {}) {
|
|
|
1917
2223
|
// src/tools/edit.ts
|
|
1918
2224
|
import crypto4 from "crypto";
|
|
1919
2225
|
import fs5 from "fs";
|
|
1920
|
-
import
|
|
2226
|
+
import path6 from "path";
|
|
1921
2227
|
import { z as z6 } from "zod";
|
|
1922
2228
|
var argsSchema3 = z6.object({
|
|
1923
2229
|
path: z6.string(),
|
|
@@ -1964,7 +2270,7 @@ function countMatches(text, needle) {
|
|
|
1964
2270
|
}
|
|
1965
2271
|
}
|
|
1966
2272
|
function atomicReplace(file, content) {
|
|
1967
|
-
const tmp =
|
|
2273
|
+
const tmp = path6.join(path6.dirname(file), `.xlyra-tmp-${crypto4.randomUUID()}${path6.extname(file)}`);
|
|
1968
2274
|
try {
|
|
1969
2275
|
fs5.writeFileSync(tmp, content);
|
|
1970
2276
|
fs5.renameSync(tmp, file);
|
|
@@ -2170,7 +2476,7 @@ ${result.output}`);
|
|
|
2170
2476
|
|
|
2171
2477
|
// src/tools/list.ts
|
|
2172
2478
|
import fs7 from "fs";
|
|
2173
|
-
import
|
|
2479
|
+
import path7 from "path";
|
|
2174
2480
|
import { z as z8 } from "zod";
|
|
2175
2481
|
var MAX_ENTRIES = 500;
|
|
2176
2482
|
var IGNORE_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -2224,8 +2530,8 @@ function makeListTool(workdir, sandbox = {}) {
|
|
|
2224
2530
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
2225
2531
|
for (const entry of entries) {
|
|
2226
2532
|
if (IGNORE_NAMES.has(entry.name)) continue;
|
|
2227
|
-
const full =
|
|
2228
|
-
const rel =
|
|
2533
|
+
const full = path7.join(current, entry.name);
|
|
2534
|
+
const rel = path7.relative(dir, full);
|
|
2229
2535
|
const display = entry.isDirectory() ? `${rel}/` : rel;
|
|
2230
2536
|
if (!matcher || matcher.test(display)) {
|
|
2231
2537
|
if (results.length < MAX_ENTRIES) results.push(display);
|
|
@@ -2257,14 +2563,14 @@ function countRemaining(dir, recursive) {
|
|
|
2257
2563
|
for (const entry of entries) {
|
|
2258
2564
|
if (IGNORE_NAMES.has(entry.name)) continue;
|
|
2259
2565
|
count += 1;
|
|
2260
|
-
if (recursive && entry.isDirectory()) count += countRemaining(
|
|
2566
|
+
if (recursive && entry.isDirectory()) count += countRemaining(path7.join(dir, entry.name), true);
|
|
2261
2567
|
}
|
|
2262
2568
|
return count;
|
|
2263
2569
|
}
|
|
2264
2570
|
|
|
2265
2571
|
// src/tools/read.ts
|
|
2266
2572
|
import fs8 from "fs";
|
|
2267
|
-
import
|
|
2573
|
+
import path8 from "path";
|
|
2268
2574
|
import { z as z9 } from "zod";
|
|
2269
2575
|
var MAX_LINES = 2e3;
|
|
2270
2576
|
var MAX_BYTES = 5e4;
|
|
@@ -2289,7 +2595,7 @@ function makeReadTool(workdir, sandbox = {}) {
|
|
|
2289
2595
|
if (fs8.statSync(file).isDirectory()) {
|
|
2290
2596
|
throw new Error(`${rawPath} \u662F\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6\uFF1B\u67E5\u770B\u76EE\u5F55\u5185\u5BB9\u8BF7\u7528 list \u5DE5\u5177`);
|
|
2291
2597
|
}
|
|
2292
|
-
if (IMAGE_SUFFIXES.has(
|
|
2598
|
+
if (IMAGE_SUFFIXES.has(path8.extname(file).toLowerCase())) {
|
|
2293
2599
|
throw new Error("\u6682\u4E0D\u652F\u6301\u8BFB\u53D6\u56FE\u7247\u6587\u4EF6\uFF0C\u53EA\u652F\u6301\u6587\u672C\u6587\u4EF6");
|
|
2294
2600
|
}
|
|
2295
2601
|
const lines = fs8.readFileSync(file, "utf-8").split("\n");
|
|
@@ -2319,7 +2625,7 @@ function makeReadTool(workdir, sandbox = {}) {
|
|
|
2319
2625
|
|
|
2320
2626
|
// src/tools/search.ts
|
|
2321
2627
|
import fs9 from "fs";
|
|
2322
|
-
import
|
|
2628
|
+
import path9 from "path";
|
|
2323
2629
|
import { z as z10 } from "zod";
|
|
2324
2630
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
2325
2631
|
var HARD_MAX_RESULTS = 500;
|
|
@@ -2369,13 +2675,13 @@ function makeSearchTool(workdir, sandbox = {}) {
|
|
|
2369
2675
|
if (stat.isDirectory()) {
|
|
2370
2676
|
for (const entry of fs9.readdirSync(entryPath, { withFileTypes: true })) {
|
|
2371
2677
|
if (IGNORE_NAMES2.has(entry.name)) continue;
|
|
2372
|
-
visit(
|
|
2678
|
+
visit(path9.join(entryPath, entry.name));
|
|
2373
2679
|
if (results.length >= parsed.max_results || context?.signal?.aborted) break;
|
|
2374
2680
|
}
|
|
2375
2681
|
return;
|
|
2376
2682
|
}
|
|
2377
|
-
const relative =
|
|
2378
|
-
const portable = relative.split(
|
|
2683
|
+
const relative = path9.relative(workdir, entryPath) || path9.basename(entryPath);
|
|
2684
|
+
const portable = relative.split(path9.sep).join("/");
|
|
2379
2685
|
if (matcher && !matcher.test(portable)) return;
|
|
2380
2686
|
if (stat.size > MAX_FILE_BYTES || isBinary(entryPath)) return;
|
|
2381
2687
|
let text;
|
|
@@ -2439,7 +2745,7 @@ function globToRegExp2(glob) {
|
|
|
2439
2745
|
|
|
2440
2746
|
// src/tools/write.ts
|
|
2441
2747
|
import fs10 from "fs";
|
|
2442
|
-
import
|
|
2748
|
+
import path10 from "path";
|
|
2443
2749
|
import crypto6 from "crypto";
|
|
2444
2750
|
import { z as z11 } from "zod";
|
|
2445
2751
|
var argsSchema8 = z11.object({
|
|
@@ -2463,9 +2769,9 @@ function makeWriteTool(workdir, sandbox = {}) {
|
|
|
2463
2769
|
if (fs10.statSync(file).isDirectory()) {
|
|
2464
2770
|
throw new Error(`${rawPath} \u662F\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6`);
|
|
2465
2771
|
}
|
|
2466
|
-
const tmp =
|
|
2467
|
-
|
|
2468
|
-
`.xlyra-tmp-${crypto6.randomUUID()}${
|
|
2772
|
+
const tmp = path10.join(
|
|
2773
|
+
path10.dirname(file),
|
|
2774
|
+
`.xlyra-tmp-${crypto6.randomUUID()}${path10.extname(file)}`
|
|
2469
2775
|
);
|
|
2470
2776
|
try {
|
|
2471
2777
|
fs10.writeFileSync(tmp, content);
|
|
@@ -2515,9 +2821,9 @@ function makeWriteStdinTool(manager, sandbox = {}) {
|
|
|
2515
2821
|
|
|
2516
2822
|
// src/tools/index.ts
|
|
2517
2823
|
import fs11 from "fs";
|
|
2518
|
-
import
|
|
2824
|
+
import path11 from "path";
|
|
2519
2825
|
function builtinTools(opts) {
|
|
2520
|
-
const workdir =
|
|
2826
|
+
const workdir = path11.resolve(opts.workdir);
|
|
2521
2827
|
fs11.mkdirSync(workdir, { recursive: true });
|
|
2522
2828
|
const sandbox = {
|
|
2523
2829
|
allowOutsideWorkdir: opts.allowOutsideWorkdir ?? false,
|
|
@@ -2544,15 +2850,59 @@ function builtinTools(opts) {
|
|
|
2544
2850
|
|
|
2545
2851
|
// src/server/config.ts
|
|
2546
2852
|
import fs12 from "fs";
|
|
2547
|
-
import
|
|
2853
|
+
import path12 from "path";
|
|
2548
2854
|
import { z as z13 } from "zod";
|
|
2549
2855
|
var endpointSchema = z13.object({
|
|
2550
2856
|
name: z13.string().min(1, "\u7AEF\u70B9\u540D\u4E0D\u80FD\u4E3A\u7A7A"),
|
|
2551
2857
|
protocol: z13.enum(["openai-responses", "anthropic-messages"]),
|
|
2552
|
-
|
|
2553
|
-
|
|
2858
|
+
/** 省略时按 direct 处理(兼容旧配置) */
|
|
2859
|
+
mode: z13.enum(["direct", "remote-xlyra"]).default("direct"),
|
|
2860
|
+
base_url: z13.url("base_url \u5FC5\u987B\u662F\u5408\u6CD5 URL"),
|
|
2861
|
+
api_key: z13.string().min(1, "api_key \u4E0D\u80FD\u4E3A\u7A7A\uFF08\u53EF\u7528 ${ENV_VAR} \u5F15\u7528\u73AF\u5883\u53D8\u91CF\uFF09").optional(),
|
|
2862
|
+
credential: z13.discriminatedUnion("type", [
|
|
2863
|
+
z13.object({
|
|
2864
|
+
type: z13.literal("temporary-bearer"),
|
|
2865
|
+
token: z13.string().min(1, "credential.token \u4E0D\u80FD\u4E3A\u7A7A\uFF08\u53EF\u7528 ${ENV_VAR} \u5F15\u7528\u73AF\u5883\u53D8\u91CF\uFF09")
|
|
2866
|
+
}),
|
|
2867
|
+
z13.object({
|
|
2868
|
+
type: z13.literal("xlyra-callback"),
|
|
2869
|
+
url: z13.url("credential.url \u5FC5\u987B\u662F\u5408\u6CD5 URL")
|
|
2870
|
+
})
|
|
2871
|
+
]).optional(),
|
|
2554
2872
|
models: z13.record(z13.string(), z13.object({ context_window: z13.number().positive().optional() })).optional(),
|
|
2555
2873
|
default_model: z13.string().optional()
|
|
2874
|
+
}).superRefine((endpoint, ctx) => {
|
|
2875
|
+
if (endpoint.mode === "remote-xlyra") {
|
|
2876
|
+
if (!endpoint.credential) {
|
|
2877
|
+
ctx.addIssue({
|
|
2878
|
+
code: "custom",
|
|
2879
|
+
path: ["credential"],
|
|
2880
|
+
message: "remote-xlyra \u6A21\u5F0F\u5FC5\u987B\u914D\u7F6E\u4E34\u65F6\u51ED\u8BC1\uFF08temporary-bearer \u6216 xlyra-callback\uFF09"
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
if (endpoint.api_key !== void 0) {
|
|
2884
|
+
ctx.addIssue({
|
|
2885
|
+
code: "custom",
|
|
2886
|
+
path: ["api_key"],
|
|
2887
|
+
message: "remote-xlyra \u6A21\u5F0F\u4E0D\u5141\u8BB8\u914D\u7F6E api_key"
|
|
2888
|
+
});
|
|
2889
|
+
}
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2892
|
+
if (!endpoint.api_key) {
|
|
2893
|
+
ctx.addIssue({
|
|
2894
|
+
code: "custom",
|
|
2895
|
+
path: ["api_key"],
|
|
2896
|
+
message: "direct \u6A21\u5F0F\u5FC5\u987B\u914D\u7F6E api_key"
|
|
2897
|
+
});
|
|
2898
|
+
}
|
|
2899
|
+
if (endpoint.credential !== void 0) {
|
|
2900
|
+
ctx.addIssue({
|
|
2901
|
+
code: "custom",
|
|
2902
|
+
path: ["credential"],
|
|
2903
|
+
message: "direct \u6A21\u5F0F\u4E0D\u5141\u8BB8\u914D\u7F6E credential"
|
|
2904
|
+
});
|
|
2905
|
+
}
|
|
2556
2906
|
});
|
|
2557
2907
|
var appConfigSchema = z13.object({
|
|
2558
2908
|
endpoints: z13.array(endpointSchema).min(1, "endpoints \u4E3A\u7A7A\uFF1A\u81F3\u5C11\u914D\u7F6E\u4E00\u4E2A\u6A21\u578B\u7AEF\u70B9"),
|
|
@@ -2564,12 +2914,14 @@ var appConfigSchema = z13.object({
|
|
|
2564
2914
|
workdir: z13.string().optional(),
|
|
2565
2915
|
agent_name: z13.string().optional(),
|
|
2566
2916
|
persona: z13.string().optional(),
|
|
2917
|
+
/** 部署方注入的 agent 实例标识(xlyra-callback 凭证用),run 启动参数可覆盖 */
|
|
2918
|
+
agent_instance_id: z13.string().optional(),
|
|
2567
2919
|
/** 本机命令可访问当前用户权限范围内的系统资源,必须显式开启。 */
|
|
2568
2920
|
enable_command_execution: z13.boolean().optional()
|
|
2569
2921
|
}).optional()
|
|
2570
2922
|
});
|
|
2571
2923
|
function defaultConfigPath() {
|
|
2572
|
-
return
|
|
2924
|
+
return path12.join(defaultDataDir(), "config.json");
|
|
2573
2925
|
}
|
|
2574
2926
|
function loadConfig(configPath) {
|
|
2575
2927
|
const file = configPath ?? defaultConfigPath();
|
|
@@ -2592,16 +2944,23 @@ function loadConfig(configPath) {
|
|
|
2592
2944
|
${issues}`);
|
|
2593
2945
|
}
|
|
2594
2946
|
for (const endpoint of parsed.data.endpoints) {
|
|
2595
|
-
|
|
2947
|
+
if (endpoint.api_key !== void 0) {
|
|
2948
|
+
endpoint.api_key = interpolateEnv(endpoint.api_key, file);
|
|
2949
|
+
}
|
|
2950
|
+
if (endpoint.credential?.type === "temporary-bearer") {
|
|
2951
|
+
endpoint.credential.token = interpolateEnv(endpoint.credential.token, file);
|
|
2952
|
+
}
|
|
2596
2953
|
}
|
|
2597
2954
|
return parsed.data;
|
|
2598
2955
|
}
|
|
2599
2956
|
function saveConfig(config, configPath) {
|
|
2600
2957
|
const file = configPath ?? defaultConfigPath();
|
|
2601
|
-
|
|
2958
|
+
const validated = appConfigSchema.parse(config);
|
|
2959
|
+
fs12.mkdirSync(path12.dirname(file), { recursive: true, mode: 448 });
|
|
2602
2960
|
const tmp = `${file}.tmp`;
|
|
2603
|
-
fs12.writeFileSync(tmp, JSON.stringify(
|
|
2961
|
+
fs12.writeFileSync(tmp, JSON.stringify(validated, null, 2) + "\n", { mode: 384 });
|
|
2604
2962
|
fs12.renameSync(tmp, file);
|
|
2963
|
+
fs12.chmodSync(file, 384);
|
|
2605
2964
|
}
|
|
2606
2965
|
function configExists(configPath) {
|
|
2607
2966
|
return fs12.existsSync(configPath ?? defaultConfigPath());
|
|
@@ -2616,10 +2975,11 @@ function upsertEndpoint(endpoint, configPath) {
|
|
|
2616
2975
|
config = { endpoints: [] };
|
|
2617
2976
|
}
|
|
2618
2977
|
const idx = config.endpoints.findIndex((e) => e.name === endpoint.name);
|
|
2978
|
+
const parsedEndpoint = endpointSchema.parse(endpoint);
|
|
2619
2979
|
if (idx >= 0) {
|
|
2620
|
-
config.endpoints[idx] =
|
|
2980
|
+
config.endpoints[idx] = parsedEndpoint;
|
|
2621
2981
|
} else {
|
|
2622
|
-
config.endpoints.push(
|
|
2982
|
+
config.endpoints.push(parsedEndpoint);
|
|
2623
2983
|
}
|
|
2624
2984
|
const validated = appConfigSchema.parse(config);
|
|
2625
2985
|
saveConfig(validated, file);
|
|
@@ -2675,7 +3035,7 @@ var RunRegistry = class {
|
|
|
2675
3035
|
};
|
|
2676
3036
|
this.runs.set(runId, run);
|
|
2677
3037
|
this.latestBySession.set(opts.sessionId, runId);
|
|
2678
|
-
void this.execute(run, runner, params);
|
|
3038
|
+
void this.execute(run, runner, params, opts.agentInstanceId);
|
|
2679
3039
|
return runId;
|
|
2680
3040
|
}
|
|
2681
3041
|
/** 按公开会话编号读取当前(或最近一轮)事件 */
|
|
@@ -2733,11 +3093,13 @@ var RunRegistry = class {
|
|
|
2733
3093
|
this.latestBySession.clear();
|
|
2734
3094
|
}
|
|
2735
3095
|
/** 消费 runner 事件并写入日志,兜住所有退出路径补齐终态 */
|
|
2736
|
-
async execute(run, runner, params) {
|
|
3096
|
+
async execute(run, runner, params, agentInstanceId) {
|
|
2737
3097
|
try {
|
|
2738
3098
|
for await (const event of runner.start(params, {
|
|
2739
3099
|
runId: run.runId,
|
|
2740
|
-
signal: run.controller.signal
|
|
3100
|
+
signal: run.controller.signal,
|
|
3101
|
+
sessionId: run.sessionId,
|
|
3102
|
+
...agentInstanceId ? { agentInstanceId } : {}
|
|
2741
3103
|
})) {
|
|
2742
3104
|
await this.publish(run, event);
|
|
2743
3105
|
}
|
|
@@ -2938,12 +3300,15 @@ function fail(status, message) {
|
|
|
2938
3300
|
var startPayloadSchema = z14.object({
|
|
2939
3301
|
content: z14.string().min(1, "\u6D88\u606F\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A"),
|
|
2940
3302
|
session_id: z14.string().optional(),
|
|
2941
|
-
model: z14.string().default("")
|
|
3303
|
+
model: z14.string().default(""),
|
|
3304
|
+
/** 部署方注入的 agent 实例标识(xlyra-callback 凭证用),缺省用服务级配置 */
|
|
3305
|
+
agent_instance_id: z14.string().optional()
|
|
2942
3306
|
});
|
|
2943
3307
|
var retryPayloadSchema = z14.object({
|
|
2944
3308
|
message_id: z14.string().min(1),
|
|
2945
3309
|
content: z14.string().optional(),
|
|
2946
|
-
model: z14.string().default("")
|
|
3310
|
+
model: z14.string().default(""),
|
|
3311
|
+
agent_instance_id: z14.string().optional()
|
|
2947
3312
|
});
|
|
2948
3313
|
function createAgentRoutes(ctx) {
|
|
2949
3314
|
const app = new Hono();
|
|
@@ -2984,10 +3349,14 @@ function createAgentRoutes(ctx) {
|
|
|
2984
3349
|
model: args.model,
|
|
2985
3350
|
system_prompt: args.systemPrompt ?? buildSystem()
|
|
2986
3351
|
},
|
|
2987
|
-
{
|
|
3352
|
+
{
|
|
3353
|
+
sessionId: args.sessionId,
|
|
3354
|
+
onTerminal: recorder.onTerminal,
|
|
3355
|
+
...args.agentInstanceId ?? ctx.agentInstanceId ? { agentInstanceId: args.agentInstanceId ?? ctx.agentInstanceId } : {}
|
|
3356
|
+
}
|
|
2988
3357
|
);
|
|
2989
3358
|
await recorder.begin(runId);
|
|
2990
|
-
return { sessionId: args.sessionId, messageId };
|
|
3359
|
+
return { sessionId: args.sessionId, messageId, runId };
|
|
2991
3360
|
}
|
|
2992
3361
|
app.post("/sessions", async (c) => {
|
|
2993
3362
|
const payload = startPayloadSchema.parse(await c.req.json());
|
|
@@ -3010,14 +3379,15 @@ function createAgentRoutes(ctx) {
|
|
|
3010
3379
|
history = [];
|
|
3011
3380
|
entryCount = 0;
|
|
3012
3381
|
}
|
|
3013
|
-
const { messageId } = await launchUserMessage({
|
|
3382
|
+
const { messageId, runId } = await launchUserMessage({
|
|
3014
3383
|
sessionId,
|
|
3015
3384
|
content: payload.content,
|
|
3016
3385
|
model: payload.model,
|
|
3017
3386
|
history,
|
|
3018
|
-
entryCount
|
|
3387
|
+
entryCount,
|
|
3388
|
+
...payload.agent_instance_id ? { agentInstanceId: payload.agent_instance_id } : {}
|
|
3019
3389
|
});
|
|
3020
|
-
return c.json(ok({ session_id: sessionId, message_id: messageId }, "\u7528\u6237\u6D88\u606F\u5DF2\u63D0\u4EA4"), 202);
|
|
3390
|
+
return c.json(ok({ session_id: sessionId, message_id: messageId, run_id: runId }, "\u7528\u6237\u6D88\u606F\u5DF2\u63D0\u4EA4"), 202);
|
|
3021
3391
|
});
|
|
3022
3392
|
app.get("/sessions", (c) => {
|
|
3023
3393
|
const limit = Math.min(Number(c.req.query("limit") ?? 50), 200);
|
|
@@ -3128,14 +3498,15 @@ function createAgentRoutes(ctx) {
|
|
|
3128
3498
|
ctx.store.discardFromUserMessage(sessionId, payload.message_id);
|
|
3129
3499
|
const history = ctx.store.buildHistory(sessionId);
|
|
3130
3500
|
const remaining = ctx.store.read(sessionId).entries.length;
|
|
3131
|
-
const { messageId } = await launchUserMessage({
|
|
3501
|
+
const { messageId, runId } = await launchUserMessage({
|
|
3132
3502
|
sessionId,
|
|
3133
3503
|
content,
|
|
3134
3504
|
model: payload.model,
|
|
3135
3505
|
history,
|
|
3136
|
-
entryCount: remaining
|
|
3506
|
+
entryCount: remaining,
|
|
3507
|
+
...payload.agent_instance_id ? { agentInstanceId: payload.agent_instance_id } : {}
|
|
3137
3508
|
});
|
|
3138
|
-
return c.json(ok({ session_id: sessionId, message_id: messageId }, "\u5DF2\u91CD\u65B0\u63D0\u4EA4"), 202);
|
|
3509
|
+
return c.json(ok({ session_id: sessionId, message_id: messageId, run_id: runId }, "\u5DF2\u91CD\u65B0\u63D0\u4EA4"), 202);
|
|
3139
3510
|
});
|
|
3140
3511
|
app.post("/sessions/:id/grant-access", async (c) => {
|
|
3141
3512
|
const sessionId = c.req.param("id");
|
|
@@ -3168,7 +3539,7 @@ function createAgentRoutes(ctx) {
|
|
|
3168
3539
|
if (!lastUser || !isMessageEntry(lastUser)) fail(400, "\u4F1A\u8BDD\u4E2D\u6CA1\u6709\u53EF\u7EED\u8DD1\u7684\u7528\u6237\u6D88\u606F");
|
|
3169
3540
|
ctx.resolver.resolve("");
|
|
3170
3541
|
const history = ctx.store.buildHistoryBeforeEscalation(sessionId, escalation_id);
|
|
3171
|
-
const { messageId } = await launchUserMessage({
|
|
3542
|
+
const { messageId, runId } = await launchUserMessage({
|
|
3172
3543
|
sessionId,
|
|
3173
3544
|
content: "",
|
|
3174
3545
|
model: "",
|
|
@@ -3177,7 +3548,7 @@ function createAgentRoutes(ctx) {
|
|
|
3177
3548
|
recordUser: false
|
|
3178
3549
|
});
|
|
3179
3550
|
return c.json(
|
|
3180
|
-
ok({ session_id: sessionId, message_id: messageId, granted_path: resolved_path }, "\u5DF2\u6388\u6743\u5E76\u7EE7\u7EED\u6267\u884C"),
|
|
3551
|
+
ok({ session_id: sessionId, message_id: messageId, run_id: runId, granted_path: resolved_path }, "\u5DF2\u6388\u6743\u5E76\u7EE7\u7EED\u6267\u884C"),
|
|
3181
3552
|
202
|
|
3182
3553
|
);
|
|
3183
3554
|
});
|
|
@@ -3244,7 +3615,7 @@ data: ${JSON.stringify(payload)}
|
|
|
3244
3615
|
|
|
3245
3616
|
// src/server/session-index.ts
|
|
3246
3617
|
import fs13 from "fs";
|
|
3247
|
-
import
|
|
3618
|
+
import path13 from "path";
|
|
3248
3619
|
var HEARTBEAT_TIMEOUT_MS = 3e4;
|
|
3249
3620
|
var SessionIndex = class {
|
|
3250
3621
|
file;
|
|
@@ -3353,7 +3724,7 @@ var SessionIndex = class {
|
|
|
3353
3724
|
this.persist();
|
|
3354
3725
|
}
|
|
3355
3726
|
persist() {
|
|
3356
|
-
const dir =
|
|
3727
|
+
const dir = path13.dirname(this.file);
|
|
3357
3728
|
fs13.mkdirSync(dir, { recursive: true });
|
|
3358
3729
|
const tmp = `${this.file}.tmp`;
|
|
3359
3730
|
fs13.writeFileSync(tmp, JSON.stringify({ sessions: [...this.metas.values()] }, null, 2));
|
|
@@ -3362,42 +3733,71 @@ var SessionIndex = class {
|
|
|
3362
3733
|
};
|
|
3363
3734
|
|
|
3364
3735
|
// src/server/index.ts
|
|
3365
|
-
import
|
|
3736
|
+
import path14 from "path";
|
|
3366
3737
|
import { serve } from "@hono/node-server";
|
|
3367
3738
|
|
|
3368
3739
|
// src/server/config-routes.ts
|
|
3369
3740
|
import { Hono as Hono2 } from "hono";
|
|
3741
|
+
import fs14 from "fs";
|
|
3370
3742
|
import { z as z15 } from "zod";
|
|
3371
3743
|
function maskApiKey(key) {
|
|
3372
3744
|
if (key.includes("${")) return key;
|
|
3373
3745
|
if (key.length <= 8) return "****";
|
|
3374
3746
|
return `${key.slice(0, 4)}\u2026${key.slice(-4)}`;
|
|
3375
3747
|
}
|
|
3748
|
+
function maskCredential(credential) {
|
|
3749
|
+
if (credential.type === "temporary-bearer") {
|
|
3750
|
+
return { type: credential.type, token: maskApiKey(credential.token) };
|
|
3751
|
+
}
|
|
3752
|
+
return { type: credential.type, url: credential.url, configured: true };
|
|
3753
|
+
}
|
|
3376
3754
|
function maskedConfig(config) {
|
|
3377
3755
|
return {
|
|
3378
3756
|
...config,
|
|
3379
|
-
endpoints: config.endpoints.map((e) => ({
|
|
3757
|
+
endpoints: config.endpoints.map((e) => ({
|
|
3758
|
+
...e,
|
|
3759
|
+
...e.api_key !== void 0 ? { api_key: maskApiKey(e.api_key) } : {},
|
|
3760
|
+
...e.credential ? { credential: maskCredential(e.credential) } : {}
|
|
3761
|
+
}))
|
|
3380
3762
|
};
|
|
3381
3763
|
}
|
|
3764
|
+
function retainSecret(incoming, existingRaw, existingResolved) {
|
|
3765
|
+
if (incoming === void 0 || existingRaw === void 0) return incoming;
|
|
3766
|
+
if (incoming === existingRaw) return existingRaw;
|
|
3767
|
+
if (existingResolved !== void 0 && incoming === maskApiKey(existingResolved)) return existingRaw;
|
|
3768
|
+
return incoming;
|
|
3769
|
+
}
|
|
3770
|
+
function retainCredential(incoming, existingRaw, existingResolved) {
|
|
3771
|
+
const prevRaw = existingRaw?.credential;
|
|
3772
|
+
const prevResolved = existingResolved?.credential;
|
|
3773
|
+
if (!incoming || !prevRaw) return incoming;
|
|
3774
|
+
if (prevRaw.type === "temporary-bearer" && incoming.type === "temporary-bearer") {
|
|
3775
|
+
const resolvedToken = prevResolved?.type === "temporary-bearer" ? prevResolved.token : void 0;
|
|
3776
|
+
const token = retainSecret(incoming.token, prevRaw.token, resolvedToken);
|
|
3777
|
+
return token === void 0 ? incoming : { type: "temporary-bearer", token };
|
|
3778
|
+
}
|
|
3779
|
+
if (prevRaw.type === "xlyra-callback" && incoming.type === "xlyra-callback" && incoming.url === prevRaw.url) {
|
|
3780
|
+
return prevRaw;
|
|
3781
|
+
}
|
|
3782
|
+
return incoming;
|
|
3783
|
+
}
|
|
3382
3784
|
var putPayloadSchema = z15.object({
|
|
3383
|
-
endpoints: z15.array(
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
protocol: z15.enum(["openai-responses", "anthropic-messages"]),
|
|
3387
|
-
base_url: z15.string().min(1),
|
|
3388
|
-
api_key: z15.string().min(1),
|
|
3389
|
-
models: z15.record(z15.string(), z15.object({ context_window: z15.number().positive().optional() })).optional(),
|
|
3390
|
-
default_model: z15.string().optional()
|
|
3391
|
-
})
|
|
3392
|
-
).min(1, "endpoints \u4E3A\u7A7A\uFF1A\u81F3\u5C11\u914D\u7F6E\u4E00\u4E2A\u6A21\u578B\u7AEF\u70B9"),
|
|
3785
|
+
endpoints: z15.array(endpointSchema).min(1, "endpoints \u4E3A\u7A7A\uFF1A\u81F3\u5C11\u914D\u7F6E\u4E00\u4E2A\u6A21\u578B\u7AEF\u70B9"),
|
|
3786
|
+
/** 显式确认修改 remote-xlyra 端点的 base_url/callback 地址(SSRF 防护闸) */
|
|
3787
|
+
allow_remote_endpoint_override: z15.boolean().optional(),
|
|
3393
3788
|
server: z15.object({ port: z15.number().int().positive().optional(), token: z15.string().optional() }).optional(),
|
|
3394
3789
|
agent: z15.object({
|
|
3395
3790
|
workdir: z15.string().optional(),
|
|
3396
3791
|
agent_name: z15.string().optional(),
|
|
3397
3792
|
persona: z15.string().optional(),
|
|
3793
|
+
agent_instance_id: z15.string().optional(),
|
|
3398
3794
|
enable_command_execution: z15.boolean().optional()
|
|
3399
3795
|
}).optional()
|
|
3400
3796
|
});
|
|
3797
|
+
function loadRawConfig(configPath) {
|
|
3798
|
+
const raw = JSON.parse(fs14.readFileSync(configPath, "utf-8"));
|
|
3799
|
+
return appConfigSchema.parse(raw);
|
|
3800
|
+
}
|
|
3401
3801
|
function createConfigRoutes(ctx) {
|
|
3402
3802
|
const app = new Hono2();
|
|
3403
3803
|
app.onError((err, c) => {
|
|
@@ -3413,19 +3813,34 @@ function createConfigRoutes(ctx) {
|
|
|
3413
3813
|
});
|
|
3414
3814
|
app.put("/config", async (c) => {
|
|
3415
3815
|
const payload = putPayloadSchema.parse(await c.req.json());
|
|
3416
|
-
const current =
|
|
3816
|
+
const current = loadRawConfig(ctx.configPath);
|
|
3817
|
+
const resolved = loadConfig(ctx.configPath);
|
|
3417
3818
|
const endpoints = payload.endpoints.map((incoming) => {
|
|
3418
|
-
const
|
|
3419
|
-
|
|
3420
|
-
|
|
3819
|
+
const existingRaw = current.endpoints.find((e) => e.name === incoming.name);
|
|
3820
|
+
const existingResolved = resolved.endpoints.find((e) => e.name === incoming.name);
|
|
3821
|
+
const apiKey = retainSecret(incoming.api_key, existingRaw?.api_key, existingResolved?.api_key);
|
|
3822
|
+
const credential = retainCredential(incoming.credential, existingRaw, existingResolved);
|
|
3823
|
+
const merged = { ...incoming, credential };
|
|
3824
|
+
if (apiKey !== void 0) merged.api_key = apiKey;
|
|
3825
|
+
else delete merged.api_key;
|
|
3826
|
+
if (incoming.mode === "remote-xlyra" && existingRaw?.mode === "remote-xlyra") {
|
|
3827
|
+
const callbackUrlChanged = incoming.credential?.type === "xlyra-callback" && existingRaw.credential?.type === "xlyra-callback" && incoming.credential.url !== existingRaw.credential.url;
|
|
3828
|
+
if (incoming.base_url !== existingRaw.base_url || callbackUrlChanged) {
|
|
3829
|
+
if (payload.allow_remote_endpoint_override !== true) {
|
|
3830
|
+
throw new HttpError(
|
|
3831
|
+
400,
|
|
3832
|
+
`\u7AEF\u70B9 ${incoming.name} \u662F remote-xlyra \u6A21\u5F0F\uFF0Cbase_url/callback \u5730\u5740\u53EA\u80FD\u7531 runner \u6216\u53D7\u4FE1\u4EFB\u7BA1\u7406\u63A5\u53E3\u4FEE\u6539\uFF08PUT \u9700\u5E26 allow_remote_endpoint_override: true \u663E\u5F0F\u786E\u8BA4\uFF09`
|
|
3833
|
+
);
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3421
3836
|
}
|
|
3422
|
-
return
|
|
3837
|
+
return merged;
|
|
3423
3838
|
});
|
|
3424
|
-
const next = {
|
|
3839
|
+
const next = appConfigSchema.parse({
|
|
3425
3840
|
endpoints,
|
|
3426
3841
|
...payload.server ? { server: payload.server } : {},
|
|
3427
3842
|
...payload.agent ? { agent: payload.agent } : {}
|
|
3428
|
-
};
|
|
3843
|
+
});
|
|
3429
3844
|
saveConfig(next, ctx.configPath);
|
|
3430
3845
|
return c.json({ success: true, code: 0, message: "\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF08\u90E8\u5206\u542F\u52A8\u671F\u88C5\u914D\u9879\u9700\u91CD\u542F\u670D\u52A1\u540E\u751F\u6548\uFF09", data: maskedConfig(next) });
|
|
3431
3846
|
});
|
|
@@ -3434,9 +3849,13 @@ function createConfigRoutes(ctx) {
|
|
|
3434
3849
|
const config = loadConfig(ctx.configPath);
|
|
3435
3850
|
const endpoint = config.endpoints.find((e) => e.name === name);
|
|
3436
3851
|
if (!endpoint) throw new HttpError(404, `\u7AEF\u70B9\u4E0D\u5B58\u5728\uFF1A${name}`);
|
|
3852
|
+
if (endpoint.mode === "remote-xlyra" && endpoint.credential?.type === "xlyra-callback") {
|
|
3853
|
+
throw new HttpError(400, `\u7AEF\u70B9 ${name} \u4F7F\u7528 xlyra-callback \u51ED\u8BC1\uFF0C\u9700\u5728 run \u4E0A\u4E0B\u6587\u4E2D\u6D4B\u8BD5\uFF08\u6B64\u5904\u65E0\u6D3B\u52A8 run\uFF09`);
|
|
3854
|
+
}
|
|
3437
3855
|
const model = endpoint.default_model ?? Object.keys(endpoint.models ?? {})[0];
|
|
3438
3856
|
if (!model) throw new HttpError(400, `\u7AEF\u70B9 ${name} \u672A\u914D\u7F6E default_model \u6216 models\uFF0C\u65E0\u6CD5\u6D4B\u8BD5`);
|
|
3439
|
-
const
|
|
3857
|
+
const credential = resolveCredentialProvider(endpoint);
|
|
3858
|
+
const protocol = endpoint.protocol === "anthropic-messages" ? new AnthropicMessagesProtocol({ baseUrl: endpoint.base_url, credential, providerName: endpoint.name }) : new OpenAIResponsesProtocol({ baseUrl: endpoint.base_url, credential, providerName: endpoint.name });
|
|
3440
3859
|
const started = Date.now();
|
|
3441
3860
|
for await (const event of protocol.chatStream({
|
|
3442
3861
|
model,
|
|
@@ -3462,14 +3881,14 @@ function createConfigRoutes(ctx) {
|
|
|
3462
3881
|
|
|
3463
3882
|
// src/server/index.ts
|
|
3464
3883
|
function createAgentServer(config, opts = {}) {
|
|
3465
|
-
const dataDir =
|
|
3466
|
-
const configPath = opts.configPath ??
|
|
3884
|
+
const dataDir = path14.resolve(opts.dataDir ?? defaultDataDir());
|
|
3885
|
+
const configPath = opts.configPath ?? path14.join(dataDir, "config.json");
|
|
3467
3886
|
const resolver = new EndpointResolver(config.endpoints);
|
|
3468
|
-
const store = new AgentSessionStore(
|
|
3469
|
-
const index = new SessionIndex(
|
|
3887
|
+
const store = new AgentSessionStore(path14.join(dataDir, "sessions"));
|
|
3888
|
+
const index = new SessionIndex(path14.join(dataDir, "index.json"));
|
|
3470
3889
|
index.rebuild(store.scanAll());
|
|
3471
3890
|
const registry = new RunRegistry();
|
|
3472
|
-
const workdir =
|
|
3891
|
+
const workdir = path14.resolve(config.agent?.workdir ?? path14.join(dataDir, "workspace"));
|
|
3473
3892
|
const grants = new EscalationGrants();
|
|
3474
3893
|
const app = createAgentRoutes({
|
|
3475
3894
|
resolver,
|
|
@@ -3486,6 +3905,7 @@ function createAgentServer(config, opts = {}) {
|
|
|
3486
3905
|
workdir,
|
|
3487
3906
|
...config.agent?.agent_name ? { agentName: config.agent.agent_name } : {},
|
|
3488
3907
|
...config.agent?.persona ? { persona: config.agent.persona } : {},
|
|
3908
|
+
...config.agent?.agent_instance_id ? { agentInstanceId: config.agent.agent_instance_id } : {},
|
|
3489
3909
|
...config.server?.token ? { token: config.server.token } : {}
|
|
3490
3910
|
});
|
|
3491
3911
|
const configRoutes = createConfigRoutes({ configPath });
|
|
@@ -3502,7 +3922,7 @@ function createAgentServer(config, opts = {}) {
|
|
|
3502
3922
|
return { app, registry, store, index };
|
|
3503
3923
|
}
|
|
3504
3924
|
function serveFromConfig(opts) {
|
|
3505
|
-
const configPath = opts.configPath ??
|
|
3925
|
+
const configPath = opts.configPath ?? path14.join(defaultDataDir(), "config.json");
|
|
3506
3926
|
const config = loadConfig(configPath);
|
|
3507
3927
|
const { app } = createAgentServer(config, { configPath });
|
|
3508
3928
|
const port = opts.port ?? config.server?.port ?? 3210;
|
|
@@ -3525,6 +3945,12 @@ export {
|
|
|
3525
3945
|
modelSettingsSchema,
|
|
3526
3946
|
responseToMessage,
|
|
3527
3947
|
LlmError,
|
|
3948
|
+
StaticCredentialProvider,
|
|
3949
|
+
XlyraCallbackCredentialProvider,
|
|
3950
|
+
resolveCredentialProvider,
|
|
3951
|
+
isRefreshable,
|
|
3952
|
+
httpErrorSummary,
|
|
3953
|
+
credentialSecrets,
|
|
3528
3954
|
parseSse,
|
|
3529
3955
|
ToolCallBuffer,
|
|
3530
3956
|
AnthropicMessagesProtocol,
|
|
@@ -3587,4 +4013,4 @@ export {
|
|
|
3587
4013
|
createAgentServer,
|
|
3588
4014
|
serveFromConfig
|
|
3589
4015
|
};
|
|
3590
|
-
//# sourceMappingURL=chunk-
|
|
4016
|
+
//# sourceMappingURL=chunk-3DOSX63Z.js.map
|