@cueai/omni-reader-mcp 1.0.2 → 1.1.0
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 +115 -26
- package/dist/artifact-store.d.ts +11 -0
- package/dist/artifact-store.js +94 -48
- package/dist/cli/agent-config.d.ts +29 -4
- package/dist/cli/agent-config.js +910 -107
- package/dist/cli/arguments.d.ts +32 -0
- package/dist/cli/arguments.js +120 -0
- package/dist/cli/doctor.d.ts +42 -1
- package/dist/cli/doctor.js +109 -36
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.js +103 -18
- package/dist/cli/uninstall.d.ts +6 -0
- package/dist/cli/uninstall.js +37 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +7 -0
- package/dist/cube-client.d.ts +5 -3
- package/dist/cube-client.js +16 -11
- package/dist/cursor.js +2 -0
- package/dist/errors.d.ts +32 -1
- package/dist/errors.js +26 -1
- package/dist/iiis-client.d.ts +18 -4
- package/dist/iiis-client.js +194 -40
- package/dist/index.d.ts +3 -0
- package/dist/index.js +93 -32
- package/dist/multipart-body.js +2 -0
- package/dist/onboarding-policy.d.ts +10 -0
- package/dist/onboarding-policy.js +58 -0
- package/dist/operation-journal.d.ts +50 -1
- package/dist/operation-journal.js +473 -114
- package/dist/operation-manager.d.ts +75 -0
- package/dist/operation-manager.js +1311 -0
- package/dist/path-security.d.ts +1 -0
- package/dist/path-security.js +26 -6
- package/dist/progress.d.ts +6 -1
- package/dist/protocol.d.ts +26 -13
- package/dist/protocol.js +34 -10
- package/dist/remote-client.d.ts +17 -0
- package/dist/remote-client.js +233 -0
- package/dist/result-contract.d.ts +199 -0
- package/dist/result-contract.js +235 -0
- package/dist/server.js +21 -4
- package/dist/source.d.ts +8 -0
- package/dist/source.js +37 -0
- package/dist/task-runtime.d.ts +13 -0
- package/dist/task-runtime.js +94 -0
- package/dist/tools.d.ts +19 -1
- package/dist/tools.js +317 -112
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,49 +1,138 @@
|
|
|
1
1
|
# @cueai/omni-reader-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Omni Bridge 是 Agent 中统一的本地 stdio facade。用户只需提供来源,Bridge 通过同一个 `parse(source)` 自动处理 HTTPS URL 或已授权目录中的本地文件,无需选择“本地/远程”“上传/URL”或内部服务。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 用户旅程
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- A Cue API Key from <https://cuecue.cn/hub/api-key>
|
|
7
|
+
直接告诉 Agent 来源和后续任务:
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
```text
|
|
10
|
+
用 Omni 解析 ./report.pdf
|
|
11
|
+
用 Omni 解析 https://example.com/report.pdf
|
|
12
|
+
用 Omni 解析 ./report.pdf 并总结营收变化
|
|
13
|
+
```
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
URL 不要求安装 Bridge。本地文件首次使用时,Agent 应先说明最小目录授权、数据处理方式并取得确认,再安装固定版本 Bridge。解析完成后,Agent 应继续摘要、问答或字段抽取等原始任务,而不是止于“解析完成”。
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
export CUE_API_KEY="YOUR_CUE_API_KEY"
|
|
16
|
-
npx -y @cueai/omni-reader-mcp setup
|
|
17
|
-
```
|
|
17
|
+
## 要求
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
- Node.js 20.12 或更新版本。
|
|
20
|
+
- Cue API Key:<https://cuecue.cn/api-key>。
|
|
21
|
+
- 新账号有免费积分可体验;当前赠送和每日额度以服务端 onboarding policy 与 `doctor` 输出为准。
|
|
22
|
+
- 请在 Agent 的安全密钥或本机环境设置中配置 `CUE_API_KEY`。**请勿把 API Key 粘贴到对话中。**
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
Bridge 运行时只使用以下产品配置:
|
|
22
25
|
|
|
23
26
|
- `CUE_API_KEY`
|
|
24
27
|
- `OMNI_ALLOWED_ROOTS`
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
`OMNI_ALLOWED_ROOTS` 只用于用户明确授权的额外绝对目录;macOS/Linux 使用冒号分隔,Windows 使用分号分隔。当前 Agent workspace 仍是默认允许范围。
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
## 安装与配置
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
始终使用已审核的精确版本,不使用隐式 `latest`:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npx -y @cueai/omni-reader-mcp@1.1.0 setup
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
交互 setup 支持 Hermes、Cursor、Claude Desktop 和通用 stdio 配置。非交互安装使用同一参数解析和写入逻辑:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npx -y @cueai/omni-reader-mcp@1.1.0 setup --client hermes --allowed-root /absolute/minimum/root --yes --json
|
|
43
|
+
npx -y @cueai/omni-reader-mcp@1.1.0 setup --client cursor --add-root /absolute/minimum/root --yes --json
|
|
44
|
+
npx -y @cueai/omni-reader-mcp@1.1.0 setup --client claude-desktop --allowed-root /absolute/minimum/root --yes --json
|
|
32
45
|
```
|
|
33
46
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
setup 会:
|
|
48
|
+
|
|
49
|
+
1. 展示 user-scope 配置目标和变更;
|
|
50
|
+
2. 写入单一、精确版本的 `omni-reader` facade;
|
|
51
|
+
3. 使用私有 lock、可信备份和原子替换;
|
|
52
|
+
4. 验证 Omni 安全解析环境;
|
|
53
|
+
5. 验证失败时恢复原配置;
|
|
54
|
+
6. 提示对应 Agent reload 或 restart。
|
|
55
|
+
|
|
56
|
+
配置不会写入 API Key 明文。Hermes 使用原生的独立 `command`、`args` 和安全环境变量引用。
|
|
57
|
+
|
|
58
|
+
## Agent 调用规则
|
|
59
|
+
|
|
60
|
+
Agent 将用户提供的来源字符串直接传给 `parse(source)`:
|
|
61
|
+
|
|
62
|
+
- 只有 HTTP(S) 被视为 URL;普通字符串按本地路径处理。
|
|
63
|
+
- Do not read, attach, base64-encode, or insert the file content into the conversation before calling Omni.
|
|
64
|
+
- 不使用 `file://`、localhost 或公共临时上传站点作为回退。
|
|
65
|
+
- 安装 Bridge 或扩大 allowed root 前必须确认。
|
|
66
|
+
- 状态恢复优先使用已有 `operation_id`;不得为超时重复上传或重复创建解析。
|
|
67
|
+
|
|
68
|
+
公开工具固定为:
|
|
69
|
+
|
|
70
|
+
- `parse`
|
|
71
|
+
- `get_parse_status`
|
|
72
|
+
- `cancel_parse`
|
|
73
|
+
- `read_result`
|
|
74
|
+
- `discard_result`
|
|
38
75
|
|
|
39
|
-
|
|
76
|
+
每个工具都返回 `structuredContent` 和严格 `outputSchema`。
|
|
40
77
|
|
|
41
|
-
##
|
|
78
|
+
## 前台、后台和进度
|
|
79
|
+
|
|
80
|
+
每次解析的前台预算固定为 **15 秒**:
|
|
81
|
+
|
|
82
|
+
- 15 秒内完成:直接返回完整结果。
|
|
83
|
+
- 超过 15 秒:返回可恢复的 processing operation。
|
|
84
|
+
- 客户端明确请求且支持 MCP Tasks 时,Bridge 使用 task。
|
|
85
|
+
- 其他客户端使用 `get_parse_status` 的有界轮询;不承诺客户端不支持的主动通知。
|
|
86
|
+
|
|
87
|
+
processing/task 状态只包含 operation、stage、百分比和可选权威单位进度。可用单位仅为 `page`、`sheet`、`slide`、`frame`、`segment`。状态中不包含结果正文、预览、Markdown、页面图片、裁剪图片或累计部分输出。
|
|
88
|
+
|
|
89
|
+
## 数据处理与保留
|
|
90
|
+
|
|
91
|
+
URL 和本地文件都使用强制 no-store 语义并进入 **Omni 安全解析环境**:
|
|
92
|
+
|
|
93
|
+
- 用户电脑上的原始本地文件保持 unchanged;URL 来源也不会被修改或删除。
|
|
94
|
+
- Omni 创建的处理副本和临时数据在完成后主动清理。
|
|
95
|
+
- 加密交付数据在 ACK 后删除;未确认 ACK 时最迟在 **10 分钟**交付窗口到期。
|
|
96
|
+
- 只有远端 ACK 和清理已确认后才返回 `completed`。
|
|
97
|
+
- 本地结果已保留但远端清理仍不确定时返回 `cleanup_pending`,不会提前声称已删除。
|
|
98
|
+
- 超过 64 KiB 的结果可以保存在用户私有 Bridge cache,最长 **24 小时**;这与远端 10 分钟交付窗口是两类不同数据。
|
|
99
|
+
- `read_result` 分块读取本地结果;`discard_result` 可立即删除指定结果;`clean` 清理 Bridge 创建的本地 artifact 和到期记录。
|
|
100
|
+
|
|
101
|
+
状态查询、结果读取和丢弃不会创建新的解析或 usage event。`cancel_parse` 只报告服务端确认的真实结算状态,不会自行造成第二次计费。
|
|
102
|
+
|
|
103
|
+
## 约束错误
|
|
104
|
+
|
|
105
|
+
Bridge 在 grant、上传和计费前验证本地来源:
|
|
106
|
+
|
|
107
|
+
- `SOURCE_TOO_LARGE`:超过当前 256 MiB 上限,并返回机器可读 `constraints.max_bytes`。
|
|
108
|
+
- `UNSUPPORTED_MEDIA_TYPE`:媒体类型不受支持,并返回当前权威 `supported_extensions`。
|
|
109
|
+
|
|
110
|
+
Agent 不应自动拆分、转码或上传公共站点;应请用户提供符合约束的新来源。
|
|
111
|
+
|
|
112
|
+
## 命令
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
npx -y @cueai/omni-reader-mcp@1.1.0 doctor
|
|
116
|
+
npx -y @cueai/omni-reader-mcp@1.1.0 doctor --json
|
|
117
|
+
npx -y @cueai/omni-reader-mcp@1.1.0 clean
|
|
118
|
+
npx -y @cueai/omni-reader-mcp@1.1.0 uninstall --yes --json
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
不带命令运行固定版本会启动 stdio MCP server:
|
|
42
122
|
|
|
43
123
|
```sh
|
|
44
|
-
npx -y @cueai/omni-reader-mcp
|
|
45
|
-
npx -y @cueai/omni-reader-mcp doctor
|
|
46
|
-
npx -y @cueai/omni-reader-mcp clean
|
|
124
|
+
npx -y @cueai/omni-reader-mcp@1.1.0
|
|
47
125
|
```
|
|
48
126
|
|
|
49
|
-
|
|
127
|
+
`doctor --json` 返回 package/npm/client adapter、Key present/absent、allowed-root 安全状态、endpoint compatibility、artifact、cache mode、onboarding 和 reload 状态;不会输出 Key、私有来源路径或内容。
|
|
128
|
+
|
|
129
|
+
## 卸载与回滚
|
|
130
|
+
|
|
131
|
+
`uninstall --yes --json` 只删除当前受信的 1.1.0 Bridge entry;存在匹配的可信备份时恢复原 URL-only `omni-reader` entry。卸载不会删除用户源文件,也不会静默删除未过期的本地结果。
|
|
132
|
+
|
|
133
|
+
需要回滚 1.1.0 时:
|
|
134
|
+
|
|
135
|
+
1. 停止推荐或安装该版本;
|
|
136
|
+
2. 运行 `uninstall --yes --json` 恢复可信 URL-only entry;
|
|
137
|
+
3. 对已经上传的 operation 继续使用状态恢复,让结算和清理完成;
|
|
138
|
+
4. 保留本地结果,或由用户显式运行 `discard_result` / `clean`。
|
package/dist/artifact-store.d.ts
CHANGED
|
@@ -11,8 +11,18 @@ export interface DefaultArtifactRootOptions {
|
|
|
11
11
|
readonly homeDirectory?: string;
|
|
12
12
|
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
13
13
|
}
|
|
14
|
+
export interface SelectArtifactRootOptions extends DefaultArtifactRootOptions {
|
|
15
|
+
readonly projectDirectory: string;
|
|
16
|
+
readonly temporaryDirectory: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SelectedArtifactRoot {
|
|
19
|
+
readonly mode: "default" | "fallback";
|
|
20
|
+
readonly rootDirectory: string;
|
|
21
|
+
}
|
|
14
22
|
export interface InlineResult extends ReleasedMetadata {
|
|
15
23
|
readonly kind: "inline";
|
|
24
|
+
readonly resultId: string;
|
|
25
|
+
readonly expiresAt: string;
|
|
16
26
|
readonly text: string;
|
|
17
27
|
}
|
|
18
28
|
export interface ArtifactResult extends ReleasedMetadata {
|
|
@@ -35,6 +45,7 @@ interface ArtifactFinalizeInput extends ReleasedMetadata {
|
|
|
35
45
|
readonly resultId: string;
|
|
36
46
|
}
|
|
37
47
|
export declare function defaultArtifactRoot(options?: DefaultArtifactRootOptions): string;
|
|
48
|
+
export declare function selectArtifactRoot(options: SelectArtifactRootOptions): Promise<SelectedArtifactRoot>;
|
|
38
49
|
export declare class ArtifactStore {
|
|
39
50
|
#private;
|
|
40
51
|
private constructor();
|
package/dist/artifact-store.js
CHANGED
|
@@ -15,7 +15,9 @@ function artifactError(code, message, retryable = false) {
|
|
|
15
15
|
return new OmniBridgeError({
|
|
16
16
|
code,
|
|
17
17
|
message,
|
|
18
|
+
operationCreated: true,
|
|
18
19
|
fileUploaded: true,
|
|
20
|
+
parserStarted: true,
|
|
19
21
|
billed: true,
|
|
20
22
|
contentReleased: true,
|
|
21
23
|
retryable,
|
|
@@ -25,7 +27,9 @@ function cacheError(code, message) {
|
|
|
25
27
|
return new OmniBridgeError({
|
|
26
28
|
code,
|
|
27
29
|
message,
|
|
30
|
+
operationCreated: false,
|
|
28
31
|
fileUploaded: false,
|
|
32
|
+
parserStarted: false,
|
|
29
33
|
billed: false,
|
|
30
34
|
contentReleased: false,
|
|
31
35
|
retryable: false,
|
|
@@ -174,14 +178,6 @@ async function loadOrCreateKey(rootDirectory) {
|
|
|
174
178
|
}
|
|
175
179
|
return winner;
|
|
176
180
|
}
|
|
177
|
-
function decodeUtf8(bytes) {
|
|
178
|
-
try {
|
|
179
|
-
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
180
|
-
}
|
|
181
|
-
catch {
|
|
182
|
-
throw artifactError("RESULT_ENCODING_INVALID", "The released result is not valid UTF-8.");
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
181
|
async function readUtf8Slice(handle, offset, maximumBytes, totalBytes) {
|
|
186
182
|
const requested = Math.min(maximumBytes, totalBytes - offset);
|
|
187
183
|
if (requested <= 0)
|
|
@@ -218,7 +214,7 @@ function parseMetadata(value, expectedResultId) {
|
|
|
218
214
|
|| record.resultId !== expectedResultId
|
|
219
215
|
|| typeof record.resultBytes !== "number"
|
|
220
216
|
|| !Number.isSafeInteger(record.resultBytes)
|
|
221
|
-
|| record.resultBytes
|
|
217
|
+
|| record.resultBytes < 0
|
|
222
218
|
|| typeof record.mediaType !== "string"
|
|
223
219
|
|| record.mediaType.length === 0
|
|
224
220
|
|| typeof record.resultDigest !== "string"
|
|
@@ -252,6 +248,61 @@ export function defaultArtifactRoot(options = {}) {
|
|
|
252
248
|
: path.posix.join(homeDirectory, ".cache");
|
|
253
249
|
return path.posix.join(cacheHome, "cue", "omni-reader-mcp");
|
|
254
250
|
}
|
|
251
|
+
async function selectionPathIdentity(candidate, platform) {
|
|
252
|
+
const paths = platform === "win32" ? path.win32 : path.posix;
|
|
253
|
+
const resolved = paths.resolve(candidate);
|
|
254
|
+
if (platform !== process.platform)
|
|
255
|
+
return resolved;
|
|
256
|
+
const suffix = [];
|
|
257
|
+
let current = resolved;
|
|
258
|
+
while (true) {
|
|
259
|
+
try {
|
|
260
|
+
return paths.join(await realpath(current), ...suffix);
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
if (!errno(error, "ENOENT")) {
|
|
264
|
+
throw cacheError("ARTIFACT_CACHE_UNSAFE", "The automatic local artifact cache path could not be resolved safely.");
|
|
265
|
+
}
|
|
266
|
+
const parent = paths.dirname(current);
|
|
267
|
+
if (parent === current)
|
|
268
|
+
return resolved;
|
|
269
|
+
suffix.unshift(paths.basename(current));
|
|
270
|
+
current = parent;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function selectionPathsOverlap(left, right, platform) {
|
|
275
|
+
const paths = platform === "win32" ? path.win32 : path.posix;
|
|
276
|
+
const normalizedLeft = paths.resolve(left);
|
|
277
|
+
const normalizedRight = paths.resolve(right);
|
|
278
|
+
const contains = (parent, child) => {
|
|
279
|
+
const relative = paths.relative(parent, child);
|
|
280
|
+
return relative === "" || (relative !== ".." &&
|
|
281
|
+
!relative.startsWith(`..${paths.sep}`) &&
|
|
282
|
+
!paths.isAbsolute(relative));
|
|
283
|
+
};
|
|
284
|
+
return contains(normalizedLeft, normalizedRight) ||
|
|
285
|
+
contains(normalizedRight, normalizedLeft);
|
|
286
|
+
}
|
|
287
|
+
export async function selectArtifactRoot(options) {
|
|
288
|
+
const platform = options.platform ?? process.platform;
|
|
289
|
+
const rootDirectory = defaultArtifactRoot(options);
|
|
290
|
+
const [rootIdentity, projectIdentity, homeIdentity] = await Promise.all([
|
|
291
|
+
selectionPathIdentity(rootDirectory, platform),
|
|
292
|
+
selectionPathIdentity(options.projectDirectory, platform),
|
|
293
|
+
selectionPathIdentity(options.homeDirectory ?? os.homedir(), platform),
|
|
294
|
+
]);
|
|
295
|
+
if (!selectionPathsOverlap(rootIdentity, projectIdentity, platform)) {
|
|
296
|
+
return { mode: "default", rootDirectory };
|
|
297
|
+
}
|
|
298
|
+
const paths = platform === "win32" ? path.win32 : path.posix;
|
|
299
|
+
const fallback = paths.join(options.temporaryDirectory, `cue-omni-reader-${createHash("sha256").update(homeIdentity, "utf8").digest("hex").slice(0, 16)}`);
|
|
300
|
+
const fallbackIdentity = await selectionPathIdentity(fallback, platform);
|
|
301
|
+
if (selectionPathsOverlap(fallbackIdentity, projectIdentity, platform)) {
|
|
302
|
+
throw cacheError("ARTIFACT_CACHE_UNSAFE", "The automatic local artifact cache fallback overlaps the project directory.");
|
|
303
|
+
}
|
|
304
|
+
return { mode: "fallback", rootDirectory: fallback };
|
|
305
|
+
}
|
|
255
306
|
export class ArtifactStore {
|
|
256
307
|
#rootDirectory;
|
|
257
308
|
#resultsDirectory;
|
|
@@ -563,7 +614,6 @@ export class LocalResultRetention {
|
|
|
563
614
|
#decoder;
|
|
564
615
|
#encodingInvalid = false;
|
|
565
616
|
#received = 0;
|
|
566
|
-
#inlineChunks = [];
|
|
567
617
|
#artifact;
|
|
568
618
|
#result;
|
|
569
619
|
constructor(store) {
|
|
@@ -584,16 +634,13 @@ export class LocalResultRetention {
|
|
|
584
634
|
|| metadata.mediaType.length === 0) {
|
|
585
635
|
throw artifactError("LOCAL_RESULT_INTEGRITY_FAILED", "The released result metadata is invalid.");
|
|
586
636
|
}
|
|
637
|
+
const temporary = await this.#store._newTemporaryArtifact();
|
|
587
638
|
this.#start = metadata;
|
|
588
639
|
this.#hash = createHash("sha256");
|
|
589
640
|
this.#decoder = new TextDecoder("utf-8", { fatal: true });
|
|
590
641
|
this.#encodingInvalid = false;
|
|
591
642
|
this.#received = 0;
|
|
592
|
-
this.#
|
|
593
|
-
if (metadata.resultBytes > INLINE_RESULT_MAX_BYTES) {
|
|
594
|
-
const temporary = await this.#store._newTemporaryArtifact();
|
|
595
|
-
this.#artifact = temporary;
|
|
596
|
-
}
|
|
643
|
+
this.#artifact = temporary;
|
|
597
644
|
}
|
|
598
645
|
async write(chunk) {
|
|
599
646
|
if (this.#start === undefined || this.#hash === undefined || this.#decoder === undefined) {
|
|
@@ -614,23 +661,18 @@ export class LocalResultRetention {
|
|
|
614
661
|
this.#encodingInvalid = true;
|
|
615
662
|
}
|
|
616
663
|
}
|
|
617
|
-
|
|
618
|
-
|
|
664
|
+
const handle = this.#artifact?.handle;
|
|
665
|
+
if (handle === undefined) {
|
|
666
|
+
throw artifactError("LOCAL_RESULT_STATE_INVALID", "The local artifact is not writable.");
|
|
619
667
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
while (written < chunk.byteLength) {
|
|
627
|
-
const result = await handle.write(chunk, written, chunk.byteLength - written, null);
|
|
628
|
-
if (result.bytesWritten === 0) {
|
|
629
|
-
await this.abort();
|
|
630
|
-
throw artifactError("LOCAL_ARTIFACT_FAILED", "The local result artifact write stalled.", true);
|
|
631
|
-
}
|
|
632
|
-
written += result.bytesWritten;
|
|
668
|
+
let written = 0;
|
|
669
|
+
while (written < chunk.byteLength) {
|
|
670
|
+
const result = await handle.write(chunk, written, chunk.byteLength - written, null);
|
|
671
|
+
if (result.bytesWritten === 0) {
|
|
672
|
+
await this.abort();
|
|
673
|
+
throw artifactError("LOCAL_ARTIFACT_FAILED", "The local result artifact write stalled.", true);
|
|
633
674
|
}
|
|
675
|
+
written += result.bytesWritten;
|
|
634
676
|
}
|
|
635
677
|
this.#received += chunk.byteLength;
|
|
636
678
|
}
|
|
@@ -665,26 +707,33 @@ export class LocalResultRetention {
|
|
|
665
707
|
if (computedDigest !== metadata.resultDigest) {
|
|
666
708
|
throw artifactError("LOCAL_RESULT_INTEGRITY_FAILED", "The retained result digest did not match.");
|
|
667
709
|
}
|
|
668
|
-
|
|
669
|
-
|
|
710
|
+
const artifact = this.#artifact;
|
|
711
|
+
const handle = artifact?.handle;
|
|
712
|
+
if (artifact === undefined || handle === undefined) {
|
|
713
|
+
throw artifactError("LOCAL_RESULT_STATE_INVALID", "The local artifact is not writable.");
|
|
714
|
+
}
|
|
715
|
+
await handle.sync();
|
|
716
|
+
await handle.close();
|
|
717
|
+
artifact.handle = undefined;
|
|
718
|
+
const stored = await this.#store._finalizeArtifact(artifact.temporaryPath, {
|
|
719
|
+
...metadata,
|
|
720
|
+
resultId: artifact.resultId,
|
|
721
|
+
});
|
|
722
|
+
if (metadata.resultBytes <= INLINE_RESULT_MAX_BYTES) {
|
|
723
|
+
const recovered = await this.#store.read(stored.resultId, undefined, INLINE_RESULT_MAX_BYTES);
|
|
724
|
+
if (recovered.nextCursor !== undefined) {
|
|
725
|
+
throw artifactError("LOCAL_RESULT_INTEGRITY_FAILED", "The retained inline result was not fully readable.");
|
|
726
|
+
}
|
|
670
727
|
this.#result = {
|
|
671
728
|
kind: "inline",
|
|
672
729
|
...metadata,
|
|
673
|
-
|
|
730
|
+
resultId: stored.resultId,
|
|
731
|
+
expiresAt: stored.expiresAt,
|
|
732
|
+
text: recovered.text,
|
|
674
733
|
};
|
|
675
734
|
}
|
|
676
735
|
else {
|
|
677
|
-
|
|
678
|
-
if (handle === undefined) {
|
|
679
|
-
throw artifactError("LOCAL_RESULT_STATE_INVALID", "The local artifact is not writable.");
|
|
680
|
-
}
|
|
681
|
-
await handle.sync();
|
|
682
|
-
await handle.close();
|
|
683
|
-
this.#artifact.handle = undefined;
|
|
684
|
-
this.#result = await this.#store._finalizeArtifact(this.#artifact.temporaryPath, {
|
|
685
|
-
...metadata,
|
|
686
|
-
resultId: this.#artifact.resultId,
|
|
687
|
-
});
|
|
736
|
+
this.#result = stored;
|
|
688
737
|
}
|
|
689
738
|
}
|
|
690
739
|
catch (error) {
|
|
@@ -706,12 +755,9 @@ export class LocalResultRetention {
|
|
|
706
755
|
}
|
|
707
756
|
if (artifact !== undefined) {
|
|
708
757
|
await removeIfPresent(artifact.temporaryPath);
|
|
709
|
-
|
|
710
|
-
await this.#store.discard(this.#result.resultId);
|
|
711
|
-
}
|
|
758
|
+
await this.#store.discard(artifact.resultId);
|
|
712
759
|
}
|
|
713
760
|
this.#artifact = undefined;
|
|
714
|
-
this.#inlineChunks = [];
|
|
715
761
|
this.#hash = undefined;
|
|
716
762
|
this.#decoder = undefined;
|
|
717
763
|
this.#received = 0;
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
export type AgentTarget = "cursor" | "claude-desktop" | "generic";
|
|
1
|
+
export type AgentTarget = "hermes" | "cursor" | "claude-desktop" | "generic";
|
|
2
|
+
type NativeAgentTarget = Exclude<AgentTarget, "generic">;
|
|
3
|
+
type AgentConfigFormat = "json" | "hermes-yaml";
|
|
2
4
|
export interface AgentConfigEnvironment {
|
|
3
5
|
readonly homeDirectory: string;
|
|
4
6
|
readonly platform: NodeJS.Platform;
|
|
5
7
|
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
6
8
|
}
|
|
7
9
|
export type AgentConfigStatus = "configured" | "not configured" | "invalid or unreadable";
|
|
10
|
+
interface JsonPreviousEntry {
|
|
11
|
+
readonly format: "json";
|
|
12
|
+
readonly value: unknown;
|
|
13
|
+
}
|
|
14
|
+
interface HermesPreviousEntry {
|
|
15
|
+
readonly format: "hermes-yaml";
|
|
16
|
+
readonly serialized: string;
|
|
17
|
+
}
|
|
18
|
+
type PreviousEntry = JsonPreviousEntry | HermesPreviousEntry;
|
|
8
19
|
export interface PreparedAgentConfig {
|
|
9
20
|
readonly target: AgentTarget;
|
|
10
21
|
readonly configPath?: string;
|
|
@@ -15,14 +26,28 @@ export interface PreparedAgentConfig {
|
|
|
15
26
|
readonly entry: Record<string, unknown>;
|
|
16
27
|
readonly sourceFingerprint?: string;
|
|
17
28
|
readonly environment?: AgentConfigEnvironment;
|
|
29
|
+
readonly reload: string;
|
|
30
|
+
readonly format?: AgentConfigFormat;
|
|
31
|
+
readonly serializedBefore?: string;
|
|
32
|
+
readonly serializedAfter?: string;
|
|
33
|
+
readonly previousEntry?: PreviousEntry | null;
|
|
18
34
|
}
|
|
19
35
|
export declare function parseAgentTarget(value: string): AgentTarget;
|
|
20
36
|
export declare function agentConfigPath(target: AgentTarget, environment: AgentConfigEnvironment): string | undefined;
|
|
37
|
+
export declare function agentBackupPath(configPath: string): string;
|
|
21
38
|
export declare function displayConfigPath(configPath: string, environment: AgentConfigEnvironment): string;
|
|
22
|
-
export declare function buildAgentEntry(extraRoots: readonly string[], platform: NodeJS.Platform): Record<string, unknown>;
|
|
39
|
+
export declare function buildAgentEntry(target: AgentTarget, extraRoots: readonly string[], platform: NodeJS.Platform): Record<string, unknown>;
|
|
23
40
|
export declare function prepareAgentConfig(target: AgentTarget, extraRoots: readonly string[], environment: AgentConfigEnvironment): Promise<PreparedAgentConfig>;
|
|
24
41
|
export declare function verifyPreparedAgentConfig(prepared: PreparedAgentConfig): Promise<void>;
|
|
25
42
|
export declare function writePreparedAgentConfig(prepared: PreparedAgentConfig): Promise<void>;
|
|
26
|
-
export declare function inspectAgentConfig(configPath: string, environment?: AgentConfigEnvironment): Promise<AgentConfigStatus>;
|
|
27
|
-
export declare function detectAgentTargets(environment: AgentConfigEnvironment): Promise<
|
|
43
|
+
export declare function inspectAgentConfig(configPath: string, environment?: AgentConfigEnvironment, target?: NativeAgentTarget): Promise<AgentConfigStatus>;
|
|
44
|
+
export declare function detectAgentTargets(environment: AgentConfigEnvironment): Promise<NativeAgentTarget[]>;
|
|
28
45
|
export declare function configContainsOmni(configPath: string): Promise<boolean>;
|
|
46
|
+
export declare function configuredAllowedRoots(target: AgentTarget, environment: AgentConfigEnvironment): Promise<string[]>;
|
|
47
|
+
export declare function rollbackPreparedAgentConfig(prepared: PreparedAgentConfig): Promise<void>;
|
|
48
|
+
export interface UninstallAgentResult {
|
|
49
|
+
readonly target: NativeAgentTarget;
|
|
50
|
+
readonly restoredRemote: boolean;
|
|
51
|
+
}
|
|
52
|
+
export declare function uninstallAgentConfig(target: NativeAgentTarget, environment: AgentConfigEnvironment): Promise<UninstallAgentResult | undefined>;
|
|
53
|
+
export {};
|