agents-bridge 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/agents/model-auto-binding.d.ts.map +1 -1
- package/dist/agents/model-auto-binding.js +15 -23
- package/dist/agents/model-auto-binding.js.map +1 -1
- package/dist/agents/model-control-files.d.ts +5 -0
- package/dist/agents/model-control-files.d.ts.map +1 -0
- package/dist/agents/model-control-files.js +101 -0
- package/dist/agents/model-control-files.js.map +1 -0
- package/dist/agents/model-controller.d.ts +39 -2
- package/dist/agents/model-controller.d.ts.map +1 -1
- package/dist/agents/model-controller.js +211 -66
- package/dist/agents/model-controller.js.map +1 -1
- package/dist/cli/cmd-start.d.ts.map +1 -1
- package/dist/cli/cmd-start.js +5 -2
- package/dist/cli/cmd-start.js.map +1 -1
- package/dist/config/zod-schema.d.ts +166 -54
- package/dist/config/zod-schema.d.ts.map +1 -1
- package/dist/config/zod-schema.js +12 -1
- package/dist/config/zod-schema.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -171,6 +171,8 @@ botlink 配置读取时兼容旧的顶层 `gateway_url` / `bot_token` 写法,
|
|
|
171
171
|
- `channels.botlink.accounts.<accountId>.bot_token`:bot token
|
|
172
172
|
- `agents.defaults.model.primary`:主模型引用,例如 `claude-cli/opus-4.6`、`codex-cli/gpt-5.4`、`qwen-cli/qwen3-coder-plus`、`wukong-cli/wukong-coder` 或 `cursor-cli/auto`
|
|
173
173
|
- `agents.defaults.model.fallbacks`:备用模型链
|
|
174
|
+
- `agents.defaults.modelControl`:模型控制策略;设为 `false` 时全局禁用,且不创建状态文件、不轮询服务,也不为 CLI turn 设置模型门禁
|
|
175
|
+
- `agents.list[].modelControl`:Agent 级覆盖;`false` 禁用,对象显式启用,未配置时兼容既有自动绑定。显式 endpoint 必须使用受信 HTTPS
|
|
174
176
|
- `agents.defaults.cliBackends`:CLI backend 启动参数与运行模式
|
|
175
177
|
- `agents.list`:命名 agent 列表
|
|
176
178
|
- `bindings`:消息路由规则,可按 `channel`、`accountId`、`peer.id` 等条件匹配
|
|
@@ -178,6 +180,20 @@ botlink 配置读取时兼容旧的顶层 `gateway_url` / `bot_token` 写法,
|
|
|
178
180
|
- `logging.level`:日志级别
|
|
179
181
|
- `logging.retentionDays`:按天日志保留时长,默认 `14` 天;更早的 `agent-bridge-YYYY-MM-DD.log` 自动清理
|
|
180
182
|
|
|
183
|
+
FCH runtime 应显式配置:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"agents": {
|
|
188
|
+
"defaults": {
|
|
189
|
+
"modelControl": false
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Agent 级配置优先于全局配置。模型控制服务暂时不可用时,Bridge 使用最后一次已持久化确认的模型(无状态时使用初始模型)继续 CLI turn,不等待远端恢复;失败轮询采用有上限的指数退避和 jitter。
|
|
196
|
+
|
|
181
197
|
### 多 agent 配置
|
|
182
198
|
|
|
183
199
|
多 agent 模式通过 `agents.list` 定义运行实例,通过 `bindings` 决定消息路由到哪个 agent。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-auto-binding.d.ts","sourceRoot":"","sources":["../../src/agents/model-auto-binding.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model-auto-binding.d.ts","sourceRoot":"","sources":["../../src/agents/model-auto-binding.ts"],"names":[],"mappings":"AACA,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxF,OAAO,KAAK,EAAE,MAAM,EAAmB,MAAM,mBAAmB,CAAA;AAIhE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAG/D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAE9E;AAED,2FAA2F;AAC3F,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAsC1J"}
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
|
-
import { mkdir, open, readFile } from 'node:fs/promises';
|
|
3
|
-
import { dirname } from 'node:path';
|
|
4
2
|
import { normalizeBotlinkChannelConfigs } from '../config/zod-schema.js';
|
|
5
3
|
import { resolveAgentConfigs } from './agent-resolver.js';
|
|
6
4
|
import { resolveAgent } from './agent-router.js';
|
|
7
5
|
import { resolveOpenApiBaseUrl } from '../cli/botlink-openapi.js';
|
|
6
|
+
import { createSecureModelControlFile, readSecureModelControlFile } from './model-control-files.js';
|
|
8
7
|
export function modelSwitchAllowed(config, agentId) {
|
|
9
8
|
return config.agents.list?.find(a => a.id === agentId)?.allowModelSwitch ?? config.agents.defaults.allowModelSwitch ?? true;
|
|
10
9
|
}
|
|
11
10
|
/** Reuses legacy channel routing and credentials; never rewrites Agent IDs or sessions. */
|
|
12
11
|
export async function automaticModelControl(config, agentId, statePath, logger) {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const agentSetting = config.agents.list?.find(a => a.id === agentId)?.modelControl;
|
|
13
|
+
const configured = agentSetting !== undefined ? agentSetting : config.agents.defaults.modelControl;
|
|
14
|
+
if (configured === false) {
|
|
15
|
+
logger.info({ agentId, event: 'MODEL_CONTROL_DISABLED' }, 'Model control disabled by configuration');
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
if (configured)
|
|
19
|
+
return { ...configured, allowModelSwitch: modelSwitchAllowed(config, agentId) };
|
|
16
20
|
const resolved = resolveAgentConfigs(config, logger);
|
|
17
21
|
const ref = resolved.agents.get(agentId).model.primary;
|
|
18
22
|
const slash = ref.indexOf('/'), provider = ref.slice(0, slash), initial = ref.slice(slash + 1);
|
|
@@ -31,37 +35,25 @@ export async function automaticModelControl(config, agentId, statePath, logger)
|
|
|
31
35
|
}
|
|
32
36
|
const account = accounts[0].config;
|
|
33
37
|
const endpoint = `${resolveOpenApiBaseUrl(account)}/openapi/agent/model-control/poll`;
|
|
38
|
+
const endpointUrl = new URL(endpoint);
|
|
39
|
+
if (endpointUrl.protocol !== 'https:' || endpointUrl.username || endpointUrl.password || endpointUrl.hash)
|
|
40
|
+
throw new Error('MODEL_CONTROL_HTTPS_REQUIRED');
|
|
34
41
|
const path = statePath + '.device';
|
|
35
|
-
await mkdir(dirname(path), { recursive: true });
|
|
36
42
|
let secret;
|
|
37
43
|
try {
|
|
38
|
-
secret = (await
|
|
44
|
+
secret = (await readSecureModelControlFile(path)).trim();
|
|
39
45
|
}
|
|
40
46
|
catch (e) {
|
|
41
47
|
if (e.code !== 'ENOENT')
|
|
42
48
|
throw e;
|
|
43
49
|
secret = randomBytes(32).toString('hex');
|
|
44
50
|
try {
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
await f.writeFile(secret);
|
|
48
|
-
await f.sync();
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
await f.close();
|
|
52
|
-
}
|
|
51
|
+
await createSecureModelControlFile(path, secret);
|
|
53
52
|
}
|
|
54
53
|
catch (e) {
|
|
55
54
|
if (e.code !== 'EEXIST')
|
|
56
55
|
throw e;
|
|
57
|
-
secret = (await
|
|
58
|
-
}
|
|
59
|
-
const dir = await open(dirname(path), 'r');
|
|
60
|
-
try {
|
|
61
|
-
await dir.sync();
|
|
62
|
-
}
|
|
63
|
-
finally {
|
|
64
|
-
await dir.close();
|
|
56
|
+
secret = (await readSecureModelControlFile(path)).trim();
|
|
65
57
|
}
|
|
66
58
|
}
|
|
67
59
|
if (!/^[a-f0-9]{64}$/.test(secret))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-auto-binding.js","sourceRoot":"","sources":["../../src/agents/model-auto-binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"model-auto-binding.js","sourceRoot":"","sources":["../../src/agents/model-auto-binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,8BAA8B,EAAkB,MAAM,yBAAyB,CAAA;AAExF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AAEjE,OAAO,EAAE,4BAA4B,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAEnG,MAAM,UAAU,kBAAkB,CAAC,MAAiB,EAAE,OAAe;IACnE,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,gBAAgB,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,IAAI,IAAI,CAAA;AAC7H,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAAiB,EAAE,OAAe,EAAE,SAAiB,EAAE,MAAc;IAC/G,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,YAAY,CAAA;IAClF,MAAM,UAAU,GAAG,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAA;IAClG,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,wBAAwB,EAAE,EAAE,yCAAyC,CAAC,CAAA;QACpG,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,UAAU;QAAE,OAAO,EAAE,GAAG,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IAC/F,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpD,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAE,CAAC,KAAK,CAAC,OAAO,CAAA;IACvD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;IAC9F,MAAM,OAAO,GAA6B,EAAE,YAAY,EAAE,CAAC,QAAQ,EAAC,MAAM,EAAC,OAAO,EAAC,YAAY,EAAC,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC,SAAS,EAAC,aAAa,EAAC,cAAc,CAAC,EAAE,CAAA;IACpK,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAA;IACpF,MAAM,QAAQ,GAAG,8BAA8B,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,EAAC,SAAS,EAAC,EAAE,EAAE;QAC9F,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,GAAG,EAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAA;QAChL,MAAM,MAAM,GAAG,YAAY,CAAC,EAAC,MAAM,EAAC,EAAC,OAAO,EAAC,SAAS,EAAC,SAAS,EAAC,MAAM,EAAC,EAAE,EAAC,QAAQ,EAAC,SAAS,EAAC,MAAM,EAAC,EAAE,EAAC,EAAoB,EAAE,MAAM,CAAC,QAAQ,IAAI,EAAE,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,EAAE,CAAA;QACjM,4EAA4E;QAC5E,OAAO,MAAM,KAAK,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAA;IAC5G,CAAC,CAAC,CAAA;IACF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,EAAC,OAAO,EAAC,YAAY,EAAC,QAAQ,CAAC,MAAM,EAAC,EAAE,wFAAwF,CAAC,CAAA;QAC7I,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAClC,MAAM,QAAQ,GAAG,GAAG,qBAAqB,CAAC,OAAO,CAAC,mCAAmC,CAAA;IACrF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAA;IACrC,IAAI,WAAW,CAAC,QAAQ,KAAK,QAAQ,IAAI,WAAW,CAAC,QAAQ,IAAI,WAAW,CAAC,QAAQ,IAAI,WAAW,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;IAC1J,MAAM,IAAI,GAAG,SAAS,GAAG,SAAS,CAAA;IAClC,IAAI,MAAc,CAAA;IAClB,IAAI,CAAC;QAAC,MAAM,GAAG,CAAC,MAAM,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAAC,CAAC;IAChE,OAAO,CAAK,EAAE,CAAC;QACb,IAAG,CAAC,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,CAAC,CAAA;QAC/B,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QACxC,IAAI,CAAC;YAAC,MAAM,4BAA4B,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAAC,CAAC;QACxD,OAAM,CAAK,EAAE,CAAC;YAAC,IAAG,CAAC,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,CAAC,CAAC;YAAC,MAAM,GAAG,CAAC,MAAM,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAAC,CAAC;IAC5G,CAAC;IACD,IAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC;QAAC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;IAChF,OAAO,EAAC,QAAQ,EAAC,QAAQ,EAAC,OAAO,CAAC,SAAS,EAAC,YAAY,EAAC,MAAM,EAAC,MAAM,EAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAC,OAAO,CAAC,CAAC,CAAC,EAAC,gBAAgB,EAAC,kBAAkB,CAAC,MAAM,EAAC,OAAO,CAAC,EAAC,CAAA;AAC1K,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function ensureSecureModelControlDirectory(path: string): Promise<void>;
|
|
2
|
+
export declare function readSecureModelControlFile(path: string): Promise<string>;
|
|
3
|
+
export declare function createSecureModelControlFile(path: string, contents: string): Promise<void>;
|
|
4
|
+
export declare function writeSecureModelControlFile(path: string, contents: string): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=model-control-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-control-files.d.ts","sourceRoot":"","sources":["../../src/agents/model-control-files.ts"],"names":[],"mappings":"AAWA,wBAAsB,iCAAiC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASnF;AAWD,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS9E;AAOD,wBAAsB,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYhG;AAED,wBAAsB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB/F"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { constants } from 'node:fs';
|
|
2
|
+
import { lstat, mkdir, open, realpath, rename, unlink } from 'node:fs/promises';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
const MAX_MODEL_CONTROL_FILE_BYTES = 64 * 1024;
|
|
6
|
+
function expectedUid() {
|
|
7
|
+
return typeof process.geteuid === 'function' ? process.geteuid() : typeof process.getuid === 'function' ? process.getuid() : undefined;
|
|
8
|
+
}
|
|
9
|
+
export async function ensureSecureModelControlDirectory(path) {
|
|
10
|
+
const directory = resolve(path);
|
|
11
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
12
|
+
const info = await lstat(directory);
|
|
13
|
+
if (!info.isDirectory() || info.isSymbolicLink())
|
|
14
|
+
throw new Error('INSECURE_MODEL_CONTROL_DIRECTORY');
|
|
15
|
+
const uid = expectedUid();
|
|
16
|
+
if (uid !== undefined && info.uid !== uid)
|
|
17
|
+
throw new Error('INSECURE_MODEL_CONTROL_DIRECTORY_OWNER');
|
|
18
|
+
if ((info.mode & 0o022) !== 0)
|
|
19
|
+
throw new Error('INSECURE_MODEL_CONTROL_DIRECTORY_MODE');
|
|
20
|
+
if (await realpath(directory) !== directory)
|
|
21
|
+
throw new Error('INSECURE_MODEL_CONTROL_DIRECTORY_PATH');
|
|
22
|
+
}
|
|
23
|
+
async function validateOpenFile(file) {
|
|
24
|
+
const info = await file.stat();
|
|
25
|
+
if (!info.isFile() || info.isSymbolicLink())
|
|
26
|
+
throw new Error('INSECURE_MODEL_CONTROL_FILE_TYPE');
|
|
27
|
+
const uid = expectedUid();
|
|
28
|
+
if (uid !== undefined && info.uid !== uid)
|
|
29
|
+
throw new Error('INSECURE_MODEL_CONTROL_FILE_OWNER');
|
|
30
|
+
if ((info.mode & 0o777) !== 0o600)
|
|
31
|
+
throw new Error('INSECURE_MODEL_CONTROL_FILE_MODE');
|
|
32
|
+
if (info.size > MAX_MODEL_CONTROL_FILE_BYTES)
|
|
33
|
+
throw new Error('MODEL_CONTROL_FILE_TOO_LARGE');
|
|
34
|
+
}
|
|
35
|
+
export async function readSecureModelControlFile(path) {
|
|
36
|
+
await ensureSecureModelControlDirectory(dirname(path));
|
|
37
|
+
const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
38
|
+
try {
|
|
39
|
+
await validateOpenFile(file);
|
|
40
|
+
return await file.readFile('utf8');
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
await file.close();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function syncDirectory(path) {
|
|
47
|
+
const directory = await open(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
|
|
48
|
+
try {
|
|
49
|
+
await directory.sync();
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
await directory.close();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export async function createSecureModelControlFile(path, contents) {
|
|
56
|
+
await ensureSecureModelControlDirectory(dirname(path));
|
|
57
|
+
const file = await open(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
58
|
+
try {
|
|
59
|
+
await validateOpenFile(file);
|
|
60
|
+
await file.writeFile(contents);
|
|
61
|
+
await validateOpenFile(file);
|
|
62
|
+
await file.sync();
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
await file.close();
|
|
66
|
+
}
|
|
67
|
+
await syncDirectory(dirname(path));
|
|
68
|
+
}
|
|
69
|
+
export async function writeSecureModelControlFile(path, contents) {
|
|
70
|
+
await ensureSecureModelControlDirectory(dirname(path));
|
|
71
|
+
try {
|
|
72
|
+
const existing = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
73
|
+
try {
|
|
74
|
+
await validateOpenFile(existing);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
await existing.close();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error?.code !== 'ENOENT')
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
const temporaryPath = `${path}.${randomUUID()}.tmp`;
|
|
85
|
+
const file = await open(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
86
|
+
try {
|
|
87
|
+
await validateOpenFile(file);
|
|
88
|
+
await file.writeFile(contents);
|
|
89
|
+
await validateOpenFile(file);
|
|
90
|
+
await file.sync();
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
await file.close().catch(() => undefined);
|
|
94
|
+
await unlink(temporaryPath).catch(() => undefined);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
await file.close();
|
|
98
|
+
await rename(temporaryPath, path);
|
|
99
|
+
await syncDirectory(dirname(path));
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=model-control-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-control-files.js","sourceRoot":"","sources":["../../src/agents/model-control-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Logger } from '../types/index.js';
|
|
1
2
|
import type { CliSessionStore } from './cli-session-store.js';
|
|
2
3
|
export interface ModelControlConfig {
|
|
3
4
|
allowModelSwitch?: boolean;
|
|
@@ -6,6 +7,18 @@ export interface ModelControlConfig {
|
|
|
6
7
|
deviceSecret: string;
|
|
7
8
|
models: string[];
|
|
8
9
|
}
|
|
10
|
+
interface ModelControllerDependencies {
|
|
11
|
+
fetch?: typeof fetch;
|
|
12
|
+
now?: () => number;
|
|
13
|
+
random?: () => number;
|
|
14
|
+
setTimeout?: typeof setTimeout;
|
|
15
|
+
clearTimeout?: typeof clearTimeout;
|
|
16
|
+
pollIntervalMs?: number;
|
|
17
|
+
baseBackoffMs?: number;
|
|
18
|
+
maxBackoffMs?: number;
|
|
19
|
+
requestTimeoutMs?: number;
|
|
20
|
+
}
|
|
21
|
+
export declare const MODEL_CONTROL_MAX_RESPONSE_BYTES: number;
|
|
9
22
|
/** Agent-scoped durable overlay. No context or session identifiers are rewritten. */
|
|
10
23
|
export declare class ModelController {
|
|
11
24
|
private config;
|
|
@@ -13,20 +26,44 @@ export declare class ModelController {
|
|
|
13
26
|
private initialModel;
|
|
14
27
|
private sessions;
|
|
15
28
|
private provider;
|
|
29
|
+
private logger?;
|
|
16
30
|
private connectionId;
|
|
17
31
|
private timer?;
|
|
18
|
-
private
|
|
32
|
+
private inFlight?;
|
|
19
33
|
private ready;
|
|
20
34
|
private lastSeen;
|
|
21
35
|
private stopped;
|
|
36
|
+
private started;
|
|
37
|
+
private failures;
|
|
38
|
+
private availability;
|
|
22
39
|
private state?;
|
|
23
40
|
private loaded;
|
|
24
|
-
|
|
41
|
+
private loadPromise?;
|
|
42
|
+
private readonly fetchFn;
|
|
43
|
+
private readonly now;
|
|
44
|
+
private readonly random;
|
|
45
|
+
private readonly setTimer;
|
|
46
|
+
private readonly clearTimer;
|
|
47
|
+
private readonly pollIntervalMs;
|
|
48
|
+
private readonly baseBackoffMs;
|
|
49
|
+
private readonly maxBackoffMs;
|
|
50
|
+
private readonly requestTimeoutMs;
|
|
51
|
+
constructor(config: ModelControlConfig, path: string, initialModel: string, sessions: CliSessionStore, provider?: 'claude-cli' | 'codex-cli', logger?: Pick<Logger, "info" | "warn"> | undefined, dependencies?: ModelControllerDependencies);
|
|
25
52
|
setAllowModelSwitch(allowed: boolean): void;
|
|
26
53
|
start(): void;
|
|
27
54
|
stop(): void;
|
|
28
55
|
modelAtStart(): Promise<string>;
|
|
56
|
+
private markUnavailable;
|
|
57
|
+
private markAvailable;
|
|
29
58
|
private save;
|
|
59
|
+
private ensureLoaded;
|
|
60
|
+
private schedule;
|
|
61
|
+
private retryDelay;
|
|
62
|
+
private runScheduled;
|
|
30
63
|
tick(): Promise<void>;
|
|
64
|
+
private runSingleFlight;
|
|
65
|
+
private parseResponse;
|
|
66
|
+
private performTick;
|
|
31
67
|
}
|
|
68
|
+
export {};
|
|
32
69
|
//# sourceMappingURL=model-controller.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-controller.d.ts","sourceRoot":"","sources":["../../src/agents/model-controller.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model-controller.d.ts","sourceRoot":"","sources":["../../src/agents/model-controller.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAO7D,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAMD,UAAU,2BAA2B;IACnC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,OAAO,UAAU,CAAA;IAC9B,YAAY,CAAC,EAAE,OAAO,YAAY,CAAA;IAClC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,eAAO,MAAM,gCAAgC,QAAY,CAAA;AAkBzD,qFAAqF;AACrF,qBAAa,eAAe;IAwBxB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,MAAM,CAAC;IA5BjB,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,KAAK,CAAC,CAA+B;IAC7C,OAAO,CAAC,QAAQ,CAAC,CAAkB;IACnC,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,YAAY,CAAqD;IACzE,OAAO,CAAC,KAAK,CAAC,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,WAAW,CAAC,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;gBAG/B,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,eAAe,EACzB,QAAQ,GAAE,YAAY,GAAG,WAA0B,EACnD,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,YAAA,EAC9C,YAAY,GAAE,2BAAgC;IAehD,mBAAmB,CAAC,OAAO,EAAE,OAAO;IAEpC,KAAK;IAOL,IAAI;IAOE,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAUrC,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,aAAa;YAMP,IAAI;YAKJ,YAAY;IAwB1B,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,UAAU;YAKJ,YAAY;IAapB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAE3B,OAAO,CAAC,eAAe;YAMT,aAAa;YAyCb,WAAW;CAyC1B"}
|