@cueai/omni-reader-mcp 1.5.1 → 1.5.3
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 +69 -15
- package/dist/cli/agent-config.d.ts +1 -0
- package/dist/cli/agent-config.js +103 -49
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/cube-client.js +42 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ differ from the numbers above, report the live values.
|
|
|
64
64
|
Always use an audited exact version, never an implicit `latest`:
|
|
65
65
|
|
|
66
66
|
```sh
|
|
67
|
-
npx -y @cueai/omni-reader-mcp@1.5.
|
|
67
|
+
npx -y @cueai/omni-reader-mcp@1.5.3 setup
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
The interactive setup supports Hermes, Cursor, Claude Desktop, and generic stdio
|
|
@@ -72,9 +72,9 @@ configuration. Non-interactive installation uses the same argument parsing and w
|
|
|
72
72
|
logic:
|
|
73
73
|
|
|
74
74
|
```sh
|
|
75
|
-
npx -y @cueai/omni-reader-mcp@1.5.
|
|
76
|
-
npx -y @cueai/omni-reader-mcp@1.5.
|
|
77
|
-
npx -y @cueai/omni-reader-mcp@1.5.
|
|
75
|
+
npx -y @cueai/omni-reader-mcp@1.5.3 setup --client hermes --allowed-root /absolute/minimum/root --yes --json
|
|
76
|
+
npx -y @cueai/omni-reader-mcp@1.5.3 setup --client cursor --add-root /absolute/minimum/root --yes --json
|
|
77
|
+
npx -y @cueai/omni-reader-mcp@1.5.3 setup --client claude-desktop --allowed-root /absolute/minimum/root --yes --json
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
When an agent or script runs under a pty (stdin is still a TTY), declare non-interactive
|
|
@@ -82,9 +82,39 @@ mode explicitly with `--headless` (alias `--non-interactive`): no `--yes` is req
|
|
|
82
82
|
stdin is never read:
|
|
83
83
|
|
|
84
84
|
```sh
|
|
85
|
-
npx -y @cueai/omni-reader-mcp@1.5.
|
|
85
|
+
npx -y @cueai/omni-reader-mcp@1.5.3 setup --client cursor --allowed-root /absolute/minimum/root --headless --json
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
+
## Windows configuration
|
|
89
|
+
|
|
90
|
+
On Windows, `npx` on the command line is actually `npx.cmd` — a batch-file wrapper, not
|
|
91
|
+
an executable. An MCP client that launches the server with a bare `npx` command and no
|
|
92
|
+
shell therefore fails immediately with `MCP error -32000: Connection closed` before the
|
|
93
|
+
server even starts. This is a launcher-shape problem, not an Omni problem: the same
|
|
94
|
+
entry works unchanged on macOS and Linux.
|
|
95
|
+
|
|
96
|
+
If the agent reports `Connection closed` right after connecting on Windows, use one of
|
|
97
|
+
the runnable forms below. From 1.5.2 on, `setup` writes the first form automatically on
|
|
98
|
+
Windows and migrates an entry that 1.5.1's `setup` wrote (the bare `npx` shape) to it
|
|
99
|
+
automatically; the second is the most robust when you configure by hand.
|
|
100
|
+
|
|
101
|
+
1. **`cmd` + `npx` (what `setup` writes on Windows)** — `cmd.exe` is a real executable,
|
|
102
|
+
so a client's no-shell spawn works:
|
|
103
|
+
```json
|
|
104
|
+
{ "command": "cmd", "args": ["/d", "/c", "npx", "-y", "@cueai/omni-reader-mcp@1.5.3"] }
|
|
105
|
+
```
|
|
106
|
+
2. **`node` + the package entry (most robust)** — skips `npx` entirely; the absolute
|
|
107
|
+
path must be a stable install you control:
|
|
108
|
+
```json
|
|
109
|
+
{ "command": "node", "args": ["C:\\path\\to\\node_modules\\@cueai\\omni-reader-mcp\\dist\\index.js"] }
|
|
110
|
+
```
|
|
111
|
+
A deliberate local `npm install @cueai/omni-reader-mcp@1.5.3` gives you that stable
|
|
112
|
+
path. Do not point the entry at an `_npx` cache directory — its hash subdirectory
|
|
113
|
+
changes between installs.
|
|
114
|
+
3. **Never use a session/timestamp directory** — such paths are cleaned up, and the
|
|
115
|
+
connection silently breaks at the next session. Re-run `setup` or install into a
|
|
116
|
+
stable directory instead.
|
|
117
|
+
|
|
88
118
|
## Cache and journal isolation
|
|
89
119
|
|
|
90
120
|
The local parse cache and operation journal default to the user-level cache directory
|
|
@@ -221,30 +251,54 @@ satisfies the constraints.
|
|
|
221
251
|
## Commands
|
|
222
252
|
|
|
223
253
|
```sh
|
|
224
|
-
npx -y @cueai/omni-reader-mcp@1.5.
|
|
225
|
-
npx -y @cueai/omni-reader-mcp@1.5.
|
|
226
|
-
npx -y @cueai/omni-reader-mcp@1.5.
|
|
227
|
-
npx -y @cueai/omni-reader-mcp@1.5.
|
|
254
|
+
npx -y @cueai/omni-reader-mcp@1.5.3 doctor
|
|
255
|
+
npx -y @cueai/omni-reader-mcp@1.5.3 doctor --json
|
|
256
|
+
npx -y @cueai/omni-reader-mcp@1.5.3 clean
|
|
257
|
+
npx -y @cueai/omni-reader-mcp@1.5.3 uninstall --yes --json
|
|
228
258
|
```
|
|
229
259
|
|
|
230
260
|
Running the pinned version without a command starts the stdio MCP server:
|
|
231
261
|
|
|
232
262
|
```sh
|
|
233
|
-
npx -y @cueai/omni-reader-mcp@1.5.
|
|
263
|
+
npx -y @cueai/omni-reader-mcp@1.5.3
|
|
234
264
|
```
|
|
235
265
|
|
|
236
266
|
`doctor --json` returns package/npm/client adapter, Key present/absent, allowed-root
|
|
237
267
|
safety status, endpoint compatibility, artifacts, cache mode, onboarding, and reload
|
|
238
268
|
status; it never prints the Key, private source paths, or content.
|
|
239
269
|
|
|
270
|
+
## Network diagnostics
|
|
271
|
+
|
|
272
|
+
Run `npx -y @cueai/omni-reader-mcp@1.5.3 doctor --json` first. Diagnose failures by
|
|
273
|
+
stage instead of guessing an endpoint:
|
|
274
|
+
|
|
275
|
+
| Code | Stage | Meaning |
|
|
276
|
+
|---|---|---|
|
|
277
|
+
| `CUBE_UNAVAILABLE` | before upload | the control plane could not create a parse grant |
|
|
278
|
+
| `PRINCIPAL_CONCURRENCY_LIMIT` | before upload | the principal already has the maximum active parses; wait for `retry_after`, then retry the same parse |
|
|
279
|
+
| `IIIS_UNAVAILABLE` | after grant creation | the granted upload stage could not complete |
|
|
280
|
+
| `CUBE_PROTOCOL_ERROR` | control-plane response | the response did not match the required contract |
|
|
281
|
+
| `MCP error -32000: Connection closed` | before Bridge starts | the client launcher shape is invalid; see Windows configuration |
|
|
282
|
+
|
|
283
|
+
Use only the compatibility and endpoint facts reported by `doctor`. Do not guess or
|
|
284
|
+
publish service hosts or ports, hard-code IP addresses, modify hosts files, or probe an
|
|
285
|
+
endpoint copied from logs. If an enterprise network policy blocks a required connection,
|
|
286
|
+
provide the redacted `doctor --json` result to support for the applicable egress
|
|
287
|
+
requirements.
|
|
288
|
+
|
|
289
|
+
If the secure parse environment is reachable but a specific public URL fails to parse
|
|
290
|
+
(`parse_timeout` / parse failed) while it works in a browser, that is a fetch-layer
|
|
291
|
+
condition of the source — not a Bridge configuration problem — and it affects the URL
|
|
292
|
+
the same way from every client.
|
|
293
|
+
|
|
240
294
|
## Uninstall and rollback
|
|
241
295
|
|
|
242
|
-
`uninstall --yes --json` removes only a trusted 1.5.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
results.
|
|
296
|
+
`uninstall --yes --json` removes only a trusted 1.5.2 or 1.5.3 Bridge entry, plus
|
|
297
|
+
the exact broken bare-`npx` Windows entry written by 1.5.1. When a matching trusted
|
|
298
|
+
backup exists, it restores the original URL-only `omni-reader` entry. Uninstall never
|
|
299
|
+
deletes user source files and never silently removes unexpired local results.
|
|
246
300
|
|
|
247
|
-
To roll back from 1.5.
|
|
301
|
+
To roll back from 1.5.3:
|
|
248
302
|
|
|
249
303
|
1. stop recommending or installing that version;
|
|
250
304
|
2. run `uninstall --yes --json` to restore the trusted URL-only entry;
|
|
@@ -41,6 +41,7 @@ export declare function agentConfigPath(target: AgentTarget, environment: AgentC
|
|
|
41
41
|
export declare function agentBackupPath(configPath: string): string;
|
|
42
42
|
export declare function displayConfigPath(configPath: string, environment: AgentConfigEnvironment): string;
|
|
43
43
|
export declare function buildAgentEntry(target: AgentTarget, extraRoots: readonly string[], platform: NodeJS.Platform): Record<string, unknown>;
|
|
44
|
+
export declare function expectedBridgeVersion(target: AgentTarget, value: unknown, platform: NodeJS.Platform): string | undefined;
|
|
44
45
|
export declare function prepareAgentConfig(target: AgentTarget, extraRoots: readonly string[], environment: AgentConfigEnvironment): Promise<PreparedAgentConfig>;
|
|
45
46
|
export declare function verifyPreparedAgentConfig(prepared: PreparedAgentConfig): Promise<void>;
|
|
46
47
|
export declare function writePreparedAgentConfig(prepared: PreparedAgentConfig): Promise<void>;
|
package/dist/cli/agent-config.js
CHANGED
|
@@ -4,11 +4,16 @@ import { chmod, lstat, mkdir, open, realpath, rename, unlink, } from "node:fs/pr
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { BRIDGE_RELEASE_VERSION, REMOTE_OMNI_MCP_URL, } from "../constants.js";
|
|
6
6
|
const PACKAGE_SPEC = `@cueai/omni-reader-mcp@${BRIDGE_RELEASE_VERSION}`;
|
|
7
|
-
// The release that the current version is a trusted upgrade from:
|
|
8
|
-
// rollback recognize exactly {previous, current}
|
|
9
|
-
//
|
|
10
|
-
const PREVIOUS_RELEASE_VERSION = "1.5.
|
|
7
|
+
// The release that the current version is a trusted upgrade from: normal upgrade,
|
|
8
|
+
// uninstall, and rollback recognize exactly {previous, current}. Bump this to the
|
|
9
|
+
// version we just published when BRIDGE_RELEASE_VERSION advances.
|
|
10
|
+
const PREVIOUS_RELEASE_VERSION = "1.5.2";
|
|
11
11
|
const PREVIOUS_PACKAGE_SPEC = `@cueai/omni-reader-mcp@${PREVIOUS_RELEASE_VERSION}`;
|
|
12
|
+
// Preserve the one evidenced two-release migration exception: 1.5.1 setup wrote a
|
|
13
|
+
// bare `npx` launcher on Windows, so later setup/uninstall versions must still be able
|
|
14
|
+
// to identify and replace that otherwise-unrunnable entry. 1.5.2 already writes `cmd`.
|
|
15
|
+
const WINDOWS_BARE_NPX_MIGRATION_VERSION = "1.5.1";
|
|
16
|
+
const WINDOWS_BARE_NPX_MIGRATION_SPEC = `@cueai/omni-reader-mcp@${WINDOWS_BARE_NPX_MIGRATION_VERSION}`;
|
|
12
17
|
const LEGACY_PACKAGE_SPEC = "@cueai/omni-reader-mcp";
|
|
13
18
|
const TRUSTED_EXACT_PACKAGE_SPECS = new Set([PREVIOUS_PACKAGE_SPEC, PACKAGE_SPEC]);
|
|
14
19
|
function isRecord(value) {
|
|
@@ -116,10 +121,14 @@ export function displayConfigPath(configPath, environment) {
|
|
|
116
121
|
return configPath;
|
|
117
122
|
}
|
|
118
123
|
export function buildAgentEntry(target, extraRoots, platform) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
// On Windows `npx` is `npx.cmd` — a batch wrapper a client cannot spawn without a
|
|
125
|
+
// shell (spawn("npx") → ENOENT, spawn("npx.cmd") → EINVAL). `cmd /d /c npx ...` is a
|
|
126
|
+
// real executable, so a no-shell spawn works (`/d` skips the AutoRun registry command,
|
|
127
|
+
// whose stdout would corrupt the MCP stdio frames); macOS/Linux keep the bare `npx`
|
|
128
|
+
// form.
|
|
129
|
+
const entry = platform === "win32"
|
|
130
|
+
? { command: "cmd", args: ["/d", "/c", "npx", "-y", PACKAGE_SPEC] }
|
|
131
|
+
: { command: "npx", args: ["-y", PACKAGE_SPEC] };
|
|
123
132
|
const env = {};
|
|
124
133
|
if (target === "hermes")
|
|
125
134
|
env.CUE_API_KEY = "${CUE_API_KEY}";
|
|
@@ -130,6 +139,48 @@ export function buildAgentEntry(target, extraRoots, platform) {
|
|
|
130
139
|
entry.env = env;
|
|
131
140
|
return entry;
|
|
132
141
|
}
|
|
142
|
+
/** The exact package spec at the invocation's spec position, given its platform shape. */
|
|
143
|
+
function entryPackageSpec(entry, platform) {
|
|
144
|
+
const args = entry.args;
|
|
145
|
+
if (!Array.isArray(args))
|
|
146
|
+
return undefined;
|
|
147
|
+
if (platform !== "win32")
|
|
148
|
+
return args[1];
|
|
149
|
+
return entry.command === "npx" ? args[1] : args[4];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Matches the trusted invocation shape for the platform: `cmd /d /c npx -y <spec>` on
|
|
153
|
+
* Windows (runnable by a no-shell spawn; `/d` skips the AutoRun command whose stdout
|
|
154
|
+
* would corrupt MCP stdio frames), `npx -y <spec>` everywhere else. Windows also keeps
|
|
155
|
+
* the exact 1.5.1 bare-`npx` migration source: that release wrote an unrunnable entry,
|
|
156
|
+
* while 1.5.2 and later write `cmd` and therefore need no widening exception.
|
|
157
|
+
*/
|
|
158
|
+
function entryInvocationMatches(entry, platform) {
|
|
159
|
+
const args = entry.args;
|
|
160
|
+
if (platform === "win32") {
|
|
161
|
+
const cmdForm = entry.command === "cmd"
|
|
162
|
+
&& Array.isArray(args)
|
|
163
|
+
&& args.length === 5
|
|
164
|
+
&& args[0] === "/d"
|
|
165
|
+
&& args[1] === "/c"
|
|
166
|
+
&& args[2] === "npx"
|
|
167
|
+
&& args[3] === "-y"
|
|
168
|
+
&& typeof args[4] === "string"
|
|
169
|
+
&& TRUSTED_EXACT_PACKAGE_SPECS.has(args[4]);
|
|
170
|
+
const migrationNpxForm = entry.command === "npx"
|
|
171
|
+
&& Array.isArray(args)
|
|
172
|
+
&& args.length === 2
|
|
173
|
+
&& args[0] === "-y"
|
|
174
|
+
&& args[1] === WINDOWS_BARE_NPX_MIGRATION_SPEC;
|
|
175
|
+
return cmdForm || migrationNpxForm;
|
|
176
|
+
}
|
|
177
|
+
return entry.command === "npx"
|
|
178
|
+
&& Array.isArray(args)
|
|
179
|
+
&& args.length === 2
|
|
180
|
+
&& args[0] === "-y"
|
|
181
|
+
&& typeof args[1] === "string"
|
|
182
|
+
&& TRUSTED_EXACT_PACKAGE_SPECS.has(args[1]);
|
|
183
|
+
}
|
|
133
184
|
function configFormat(target) {
|
|
134
185
|
return target === "hermes" ? "hermes-yaml" : "json";
|
|
135
186
|
}
|
|
@@ -500,7 +551,7 @@ function yamlEntry(entry, newline) {
|
|
|
500
551
|
const args = entry.args;
|
|
501
552
|
const lines = [
|
|
502
553
|
" omni-reader:",
|
|
503
|
-
|
|
554
|
+
` command: ${entry.command}`,
|
|
504
555
|
" args:",
|
|
505
556
|
...args.map((argument) => ` - ${JSON.stringify(argument)}`),
|
|
506
557
|
];
|
|
@@ -541,16 +592,8 @@ function entryDigest(entry) {
|
|
|
541
592
|
};
|
|
542
593
|
return `sha256:${createHash("sha256").update(JSON.stringify(identity), "utf8").digest("hex")}`;
|
|
543
594
|
}
|
|
544
|
-
function isExpectedOmniEntry(target, value) {
|
|
545
|
-
if (!isRecord(value))
|
|
546
|
-
return false;
|
|
547
|
-
const args = value.args;
|
|
548
|
-
if (value.command !== "npx" ||
|
|
549
|
-
!Array.isArray(args) ||
|
|
550
|
-
args.length !== 2 ||
|
|
551
|
-
args[0] !== "-y" ||
|
|
552
|
-
typeof args[1] !== "string" ||
|
|
553
|
-
!TRUSTED_EXACT_PACKAGE_SPECS.has(args[1]))
|
|
595
|
+
function isExpectedOmniEntry(target, value, platform) {
|
|
596
|
+
if (!isRecord(value) || !entryInvocationMatches(value, platform))
|
|
554
597
|
return false;
|
|
555
598
|
const keys = Object.keys(value).sort();
|
|
556
599
|
if (value.env === undefined) {
|
|
@@ -586,21 +629,32 @@ function isExpectedOmniEntry(target, value) {
|
|
|
586
629
|
&& keys[1] === "command"
|
|
587
630
|
&& keys[2] === "env";
|
|
588
631
|
}
|
|
589
|
-
function expectedBridgeVersion(target, value) {
|
|
590
|
-
if (!isExpectedOmniEntry(target, value))
|
|
632
|
+
export function expectedBridgeVersion(target, value, platform) {
|
|
633
|
+
if (!isExpectedOmniEntry(target, value, platform))
|
|
591
634
|
return undefined;
|
|
592
|
-
const packageSpec = value
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
635
|
+
const packageSpec = entryPackageSpec(value, platform);
|
|
636
|
+
if (packageSpec === PREVIOUS_PACKAGE_SPEC)
|
|
637
|
+
return PREVIOUS_RELEASE_VERSION;
|
|
638
|
+
if (packageSpec === WINDOWS_BARE_NPX_MIGRATION_SPEC) {
|
|
639
|
+
return WINDOWS_BARE_NPX_MIGRATION_VERSION;
|
|
640
|
+
}
|
|
641
|
+
return BRIDGE_RELEASE_VERSION;
|
|
642
|
+
}
|
|
643
|
+
function isLegacyBridgeEntry(target, value, platform) {
|
|
644
|
+
// Keep the historical unpinned package form recognizable for setup migration;
|
|
645
|
+
// every exact-version form delegates to the single platform policy above.
|
|
646
|
+
const legacyUnpinnedNpx = (entry) => {
|
|
647
|
+
const args = entry.args;
|
|
648
|
+
return entry.command === "npx"
|
|
649
|
+
&& Array.isArray(args)
|
|
650
|
+
&& args.length === 2
|
|
651
|
+
&& args[0] === "-y"
|
|
652
|
+
&& args[1] === LEGACY_PACKAGE_SPEC;
|
|
653
|
+
};
|
|
654
|
+
if (!isRecord(value))
|
|
597
655
|
return false;
|
|
598
|
-
const
|
|
599
|
-
if (!
|
|
600
|
-
args.length !== 2 ||
|
|
601
|
-
args[0] !== "-y" ||
|
|
602
|
-
typeof args[1] !== "string" ||
|
|
603
|
-
(args[1] !== LEGACY_PACKAGE_SPEC && !TRUSTED_EXACT_PACKAGE_SPECS.has(args[1])))
|
|
656
|
+
const shapeOk = legacyUnpinnedNpx(value) || entryInvocationMatches(value, platform);
|
|
657
|
+
if (!shapeOk)
|
|
604
658
|
return false;
|
|
605
659
|
if (value.env === undefined)
|
|
606
660
|
return target !== "hermes";
|
|
@@ -640,12 +694,12 @@ function hermesOmniEntry(document, displayPath) {
|
|
|
640
694
|
}
|
|
641
695
|
return entry;
|
|
642
696
|
}
|
|
643
|
-
function previousJsonEntry(value, displayPath) {
|
|
697
|
+
function previousJsonEntry(value, displayPath, platform) {
|
|
644
698
|
if (value === undefined)
|
|
645
699
|
return null;
|
|
646
700
|
if (isCanonicalRemoteEntry(value))
|
|
647
701
|
return { format: "json", value };
|
|
648
|
-
if (isLegacyBridgeEntry("cursor", value))
|
|
702
|
+
if (isLegacyBridgeEntry("cursor", value, platform))
|
|
649
703
|
return null;
|
|
650
704
|
throw fileError(`The existing "omni-reader" entry in ${displayPath} is not a trusted remote or Bridge `
|
|
651
705
|
+ "configuration; no changes were written. Remove that entry manually, or restore a backup, "
|
|
@@ -663,7 +717,7 @@ function hermesCredentialsAreReferences(serialized) {
|
|
|
663
717
|
}
|
|
664
718
|
return true;
|
|
665
719
|
}
|
|
666
|
-
function previousHermesEntry(entry, displayPath) {
|
|
720
|
+
function previousHermesEntry(entry, displayPath, platform) {
|
|
667
721
|
if (entry === undefined)
|
|
668
722
|
return null;
|
|
669
723
|
const value = parseHermesEntry(entry);
|
|
@@ -675,7 +729,7 @@ function previousHermesEntry(entry, displayPath) {
|
|
|
675
729
|
}
|
|
676
730
|
return { format: "hermes-yaml", serialized: entry.serialized };
|
|
677
731
|
}
|
|
678
|
-
if (isLegacyBridgeEntry("hermes", value))
|
|
732
|
+
if (isLegacyBridgeEntry("hermes", value, platform))
|
|
679
733
|
return null;
|
|
680
734
|
throw fileError(`The existing "omni-reader" entry in ${displayPath} is not a trusted remote or Bridge `
|
|
681
735
|
+ "configuration; no changes were written. Remove that entry manually, or restore a backup, "
|
|
@@ -710,7 +764,7 @@ export async function prepareAgentConfig(target, extraRoots, environment) {
|
|
|
710
764
|
}
|
|
711
765
|
const servers = existingServers ?? {};
|
|
712
766
|
const existing = onlyOmniEntry(servers, displayPath);
|
|
713
|
-
const previousEntry = previousJsonEntry(existing.value, displayPath);
|
|
767
|
+
const previousEntry = previousJsonEntry(existing.value, displayPath, environment.platform);
|
|
714
768
|
const afterValue = {
|
|
715
769
|
...value,
|
|
716
770
|
mcpServers: {
|
|
@@ -737,7 +791,7 @@ export async function prepareAgentConfig(target, extraRoots, environment) {
|
|
|
737
791
|
}
|
|
738
792
|
const document = parseHermesDocument(loaded.serialized);
|
|
739
793
|
const existing = hermesOmniEntry(document, displayPath);
|
|
740
|
-
const previousEntry = previousHermesEntry(existing, displayPath);
|
|
794
|
+
const previousEntry = previousHermesEntry(existing, displayPath, environment.platform);
|
|
741
795
|
const serializedAfter = replaceHermesEntry(loaded.serialized, document, yamlEntry(entry, document.newline));
|
|
742
796
|
return {
|
|
743
797
|
target: "hermes",
|
|
@@ -964,7 +1018,7 @@ export async function writePreparedAgentConfig(prepared) {
|
|
|
964
1018
|
const existingBackup = await readTrustedBackup(configPath);
|
|
965
1019
|
let replacementBackup = backup;
|
|
966
1020
|
if (existingBackup !== undefined) {
|
|
967
|
-
const currentEntry = expectedConfigEntry(prepared.target, current);
|
|
1021
|
+
const currentEntry = expectedConfigEntry(prepared.target, current, prepared.environment?.platform ?? "darwin");
|
|
968
1022
|
if (currentEntry !== undefined && existingBackup.target === prepared.target) {
|
|
969
1023
|
if (existingBackup.bridge_entry_digest !== entryDigest(currentEntry)) {
|
|
970
1024
|
throw fileError("A conflicting trusted Omni backup already exists; no changes were written.");
|
|
@@ -1011,21 +1065,21 @@ function jsonConfigEntry(value) {
|
|
|
1011
1065
|
return undefined;
|
|
1012
1066
|
return onlyOmniEntry(value.mcpServers, "the Agent configuration").value;
|
|
1013
1067
|
}
|
|
1014
|
-
function expectedConfigEntry(target, loaded) {
|
|
1068
|
+
function expectedConfigEntry(target, loaded, platform) {
|
|
1015
1069
|
if (target === "hermes") {
|
|
1016
1070
|
const document = parseHermesDocument(loaded.serialized);
|
|
1017
1071
|
const entry = hermesOmniEntry(document, "the Hermes configuration");
|
|
1018
1072
|
if (entry === undefined)
|
|
1019
1073
|
return undefined;
|
|
1020
1074
|
const value = parseHermesEntry(entry);
|
|
1021
|
-
return isExpectedOmniEntry(target, value) ? value : undefined;
|
|
1075
|
+
return isExpectedOmniEntry(target, value, platform) ? value : undefined;
|
|
1022
1076
|
}
|
|
1023
1077
|
assertNoDuplicateJsonOmniEntries(loaded.serialized);
|
|
1024
1078
|
const value = jsonConfigEntry(parseJsonConfig(loaded));
|
|
1025
|
-
return isExpectedOmniEntry(target, value) ? value : undefined;
|
|
1079
|
+
return isExpectedOmniEntry(target, value, platform) ? value : undefined;
|
|
1026
1080
|
}
|
|
1027
|
-
async function configHasExpectedEntry(target, loaded) {
|
|
1028
|
-
return expectedConfigEntry(target, loaded) !== undefined;
|
|
1081
|
+
async function configHasExpectedEntry(target, loaded, platform) {
|
|
1082
|
+
return expectedConfigEntry(target, loaded, platform) !== undefined;
|
|
1029
1083
|
}
|
|
1030
1084
|
export async function inspectAgentConfigDetails(configPath, environment, target) {
|
|
1031
1085
|
try {
|
|
@@ -1041,7 +1095,7 @@ export async function inspectAgentConfigDetails(configPath, environment, target)
|
|
|
1041
1095
|
if (entry === undefined)
|
|
1042
1096
|
return { status: "not configured" };
|
|
1043
1097
|
const value = parseHermesEntry(entry);
|
|
1044
|
-
const version = expectedBridgeVersion("hermes", value);
|
|
1098
|
+
const version = expectedBridgeVersion("hermes", value, environment?.platform ?? "darwin");
|
|
1045
1099
|
if (version !== undefined)
|
|
1046
1100
|
return { status: "configured", version };
|
|
1047
1101
|
return {
|
|
@@ -1055,7 +1109,7 @@ export async function inspectAgentConfigDetails(configPath, environment, target)
|
|
|
1055
1109
|
const entry = onlyOmniEntry(value.mcpServers, configPath).value;
|
|
1056
1110
|
if (entry === undefined)
|
|
1057
1111
|
return { status: "not configured" };
|
|
1058
|
-
const version = expectedBridgeVersion(resolvedTarget, entry);
|
|
1112
|
+
const version = expectedBridgeVersion(resolvedTarget, entry, environment?.platform ?? "darwin");
|
|
1059
1113
|
if (version !== undefined)
|
|
1060
1114
|
return { status: "configured", version };
|
|
1061
1115
|
return {
|
|
@@ -1133,7 +1187,7 @@ export async function rollbackPreparedAgentConfig(prepared) {
|
|
|
1133
1187
|
existed: true,
|
|
1134
1188
|
serialized: prepared.serializedBefore,
|
|
1135
1189
|
fingerprint: prepared.sourceFingerprint ?? "unused",
|
|
1136
|
-
})
|
|
1190
|
+
}, prepared.environment?.platform ?? "darwin")
|
|
1137
1191
|
: undefined;
|
|
1138
1192
|
if (prepared.existed) {
|
|
1139
1193
|
await atomicReplace(prepared.configPath, prepared.serializedBefore);
|
|
@@ -1192,11 +1246,11 @@ export async function uninstallAgentConfig(target, environment) {
|
|
|
1192
1246
|
return undefined;
|
|
1193
1247
|
await validateUserConfigPath(configPath, environment);
|
|
1194
1248
|
const initial = await readConfig(configPath);
|
|
1195
|
-
if (!initial.existed || !await configHasExpectedEntry(target, initial))
|
|
1249
|
+
if (!initial.existed || !await configHasExpectedEntry(target, initial, environment.platform))
|
|
1196
1250
|
return undefined;
|
|
1197
1251
|
return await withConfigLock(configPath, environment, async () => {
|
|
1198
1252
|
const current = await readConfig(configPath);
|
|
1199
|
-
if (!await configHasExpectedEntry(target, current)) {
|
|
1253
|
+
if (!await configHasExpectedEntry(target, current, environment.platform)) {
|
|
1200
1254
|
throw fileError("The Agent configuration changed before uninstall; no changes were written.");
|
|
1201
1255
|
}
|
|
1202
1256
|
const currentEntry = target === "hermes"
|
package/dist/constants.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare const CUBE_GRANT_PROTOCOL_VERSION = "omni.parse_grant.v1";
|
|
|
6
6
|
export declare const GRANTED_STREAM_PROTOCOL_VERSION = "omni.granted_parse_stream.v1";
|
|
7
7
|
export declare const DEFAULT_CUBE_BASE_URL = "https://mcp.cuecue.cn";
|
|
8
8
|
export declare const DEFAULT_IIIS_GRANTED_BASE_URL = "https://cubefile.ai.iiis.co:9443/omni/granted/";
|
|
9
|
-
export declare const BRIDGE_RELEASE_VERSION = "1.5.
|
|
9
|
+
export declare const BRIDGE_RELEASE_VERSION = "1.5.3";
|
|
10
10
|
export declare const REMOTE_OMNI_MCP_URL = "https://mcp.cuecue.cn/api/omni-reader/mcp/";
|
|
11
11
|
export declare const FOREGROUND_BUDGET_MS = 15000;
|
|
12
12
|
export declare const STATUS_LONG_POLL_MAX_MS = 20000;
|
package/dist/constants.js
CHANGED
|
@@ -6,7 +6,7 @@ export const CUBE_GRANT_PROTOCOL_VERSION = "omni.parse_grant.v1";
|
|
|
6
6
|
export const GRANTED_STREAM_PROTOCOL_VERSION = "omni.granted_parse_stream.v1";
|
|
7
7
|
export const DEFAULT_CUBE_BASE_URL = "https://mcp.cuecue.cn";
|
|
8
8
|
export const DEFAULT_IIIS_GRANTED_BASE_URL = "https://cubefile.ai.iiis.co:9443/omni/granted/";
|
|
9
|
-
export const BRIDGE_RELEASE_VERSION = "1.5.
|
|
9
|
+
export const BRIDGE_RELEASE_VERSION = "1.5.3";
|
|
10
10
|
export const REMOTE_OMNI_MCP_URL = "https://mcp.cuecue.cn/api/omni-reader/mcp/";
|
|
11
11
|
export const FOREGROUND_BUDGET_MS = 15_000;
|
|
12
12
|
export const STATUS_LONG_POLL_MAX_MS = 20_000;
|
package/dist/cube-client.js
CHANGED
|
@@ -25,6 +25,16 @@ const grantResponseSchema = z
|
|
|
25
25
|
protocol_version: z.enum([CUBE_GRANT_PROTOCOL_VERSION, "omni.parse_grant.v2"]),
|
|
26
26
|
})
|
|
27
27
|
.strict();
|
|
28
|
+
const principalConcurrencyErrorSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
code: z.literal("PRINCIPAL_CONCURRENCY_LIMIT"),
|
|
31
|
+
message: z.literal("The maximum number of concurrent parsing operations is already active."),
|
|
32
|
+
file_uploaded: z.literal(false),
|
|
33
|
+
billed: z.literal(false),
|
|
34
|
+
content_released: z.literal(false),
|
|
35
|
+
retryable: z.literal(true),
|
|
36
|
+
})
|
|
37
|
+
.strict();
|
|
28
38
|
// Closed v3 grant response: the exact tuple of the selected direct profile is
|
|
29
39
|
// required before the upload phase may start. The tuple is re-checked against
|
|
30
40
|
// the profile after schema parsing so a drifting server cannot slip through.
|
|
@@ -47,7 +57,7 @@ const grantResponseV3Schema = z
|
|
|
47
57
|
approved_result_max_bytes: z.literal(67108864),
|
|
48
58
|
})
|
|
49
59
|
.strict();
|
|
50
|
-
function bridgeError(code, message, retryable) {
|
|
60
|
+
function bridgeError(code, message, retryable, options = {}) {
|
|
51
61
|
return new OmniBridgeError({
|
|
52
62
|
code,
|
|
53
63
|
message,
|
|
@@ -57,6 +67,9 @@ function bridgeError(code, message, retryable) {
|
|
|
57
67
|
billed: false,
|
|
58
68
|
contentReleased: false,
|
|
59
69
|
retryable,
|
|
70
|
+
...(options.failureScope === undefined ? {} : { failureScope: options.failureScope }),
|
|
71
|
+
...(options.userAction === undefined ? {} : { userAction: options.userAction }),
|
|
72
|
+
...(options.retryAfter === undefined ? {} : { retryAfter: options.retryAfter }),
|
|
60
73
|
});
|
|
61
74
|
}
|
|
62
75
|
function grantRequestBody(input, profile) {
|
|
@@ -137,6 +150,33 @@ function responseError(status) {
|
|
|
137
150
|
}
|
|
138
151
|
return bridgeError("CUBE_UNAVAILABLE", "Cube could not create the parse grant. Retry this same grant request later.", status >= 500);
|
|
139
152
|
}
|
|
153
|
+
function retryAfterSeconds(response) {
|
|
154
|
+
const value = response.headers.get("retry-after");
|
|
155
|
+
if (value === null || !/^\d+$/u.test(value))
|
|
156
|
+
return undefined;
|
|
157
|
+
const seconds = Number(value);
|
|
158
|
+
return Number.isSafeInteger(seconds) && seconds > 0 ? seconds : undefined;
|
|
159
|
+
}
|
|
160
|
+
async function grantResponseError(response) {
|
|
161
|
+
const retryAfter = retryAfterSeconds(response);
|
|
162
|
+
if (response.status === 429 && retryAfter !== undefined) {
|
|
163
|
+
let body;
|
|
164
|
+
try {
|
|
165
|
+
body = await response.json();
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return responseError(response.status);
|
|
169
|
+
}
|
|
170
|
+
if (principalConcurrencyErrorSchema.safeParse(body).success) {
|
|
171
|
+
return bridgeError("PRINCIPAL_CONCURRENCY_LIMIT", "The maximum number of concurrent parsing operations is already active.", true, {
|
|
172
|
+
failureScope: "service",
|
|
173
|
+
userAction: "Wait for retry_after seconds, then retry the same local-file parse.",
|
|
174
|
+
retryAfter,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return responseError(response.status);
|
|
179
|
+
}
|
|
140
180
|
export class CubeGrantClient {
|
|
141
181
|
#journal;
|
|
142
182
|
#apiKey;
|
|
@@ -256,7 +296,7 @@ export class CubeGrantClient {
|
|
|
256
296
|
throw bridgeError("CUBE_UNAVAILABLE", "Cube could not create the parse grant. Retry this same grant request later.", true);
|
|
257
297
|
}
|
|
258
298
|
if (!response.ok) {
|
|
259
|
-
throw
|
|
299
|
+
throw await grantResponseError(response);
|
|
260
300
|
}
|
|
261
301
|
// The v3 response tuple is required before this promise resolves: the
|
|
262
302
|
// upload phase can only start after the negotiated representation is
|