@cueai/omni-reader-mcp 1.2.0 → 1.2.2
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 +32 -15
- package/dist/cli/agent-config.d.ts +5 -0
- package/dist/cli/agent-config.js +121 -41
- package/dist/cli/doctor.js +5 -9
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/onboarding-policy.d.ts +5 -2
- package/dist/onboarding-policy.js +8 -3
- package/dist/protocol.js +6 -1
- package/dist/remote-client.js +29 -4
- package/dist/result-contract.d.ts +8 -0
- package/dist/result-contract.js +13 -6
- package/dist/tools.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,9 +23,10 @@ summarization, Q&A, field extraction — rather than stopping at "parse done".
|
|
|
23
23
|
## Requirements
|
|
24
24
|
|
|
25
25
|
- Node.js 20.12 or newer.
|
|
26
|
-
- Cue API Key: <https://cuecue.cn/api-key>.
|
|
27
|
-
- New accounts
|
|
28
|
-
|
|
26
|
+
- Cue API Key: <https://cuecue.cn/hub/api-key>.
|
|
27
|
+
- New accounts can try Omni without paying; see [Free credits](#free-credits) below. The
|
|
28
|
+
server-side onboarding policy and the `doctor` output are the live authority for current
|
|
29
|
+
allowances.
|
|
29
30
|
- Configure `CUE_API_KEY` in the agent's secret store or local environment.
|
|
30
31
|
**Never paste the API Key into a conversation.**
|
|
31
32
|
|
|
@@ -38,12 +39,28 @@ Bridge reads only these product configuration variables at runtime:
|
|
|
38
39
|
authorizes; colon-separated on macOS/Linux, semicolon-separated on Windows. The current
|
|
39
40
|
agent workspace remains the default allowed scope.
|
|
40
41
|
|
|
42
|
+
## Free credits
|
|
43
|
+
|
|
44
|
+
As of 2026-08-14:
|
|
45
|
+
|
|
46
|
+
- every account receives 10 free credits daily — roughly 150 pages of ordinary documents,
|
|
47
|
+
75 pages of scanned images or charts, 30 minutes of audio, or 4 minutes of video;
|
|
48
|
+
- new accounts receive a one-time 50-credit gift when obtaining `CUE_API_KEY` (60 credits
|
|
49
|
+
available on day one, including the daily grant);
|
|
50
|
+
- inviting a new user who registers gives both the inviter and the invitee 50 credits, with
|
|
51
|
+
no invite limit; when an invited user subscribes, the inviter additionally receives 10% of
|
|
52
|
+
the invitee's first-month credit quota as a bonus.
|
|
53
|
+
|
|
54
|
+
When available, the `doctor` output reports the live first-registration gift and daily grant;
|
|
55
|
+
referral rules and conversion ratios follow the server-side onboarding policy. If live values
|
|
56
|
+
differ from the numbers above, report the live values.
|
|
57
|
+
|
|
41
58
|
## Installation and configuration
|
|
42
59
|
|
|
43
60
|
Always use an audited exact version, never an implicit `latest`:
|
|
44
61
|
|
|
45
62
|
```sh
|
|
46
|
-
npx -y @cueai/omni-reader-mcp@1.2.
|
|
63
|
+
npx -y @cueai/omni-reader-mcp@1.2.2 setup
|
|
47
64
|
```
|
|
48
65
|
|
|
49
66
|
The interactive setup supports Hermes, Cursor, Claude Desktop, and generic stdio
|
|
@@ -51,9 +68,9 @@ configuration. Non-interactive installation uses the same argument parsing and w
|
|
|
51
68
|
logic:
|
|
52
69
|
|
|
53
70
|
```sh
|
|
54
|
-
npx -y @cueai/omni-reader-mcp@1.2.
|
|
55
|
-
npx -y @cueai/omni-reader-mcp@1.2.
|
|
56
|
-
npx -y @cueai/omni-reader-mcp@1.2.
|
|
71
|
+
npx -y @cueai/omni-reader-mcp@1.2.2 setup --client hermes --allowed-root /absolute/minimum/root --yes --json
|
|
72
|
+
npx -y @cueai/omni-reader-mcp@1.2.2 setup --client cursor --add-root /absolute/minimum/root --yes --json
|
|
73
|
+
npx -y @cueai/omni-reader-mcp@1.2.2 setup --client claude-desktop --allowed-root /absolute/minimum/root --yes --json
|
|
57
74
|
```
|
|
58
75
|
|
|
59
76
|
When an agent or script runs under a pty (stdin is still a TTY), declare non-interactive
|
|
@@ -61,7 +78,7 @@ mode explicitly with `--headless` (alias `--non-interactive`): no `--yes` is req
|
|
|
61
78
|
stdin is never read:
|
|
62
79
|
|
|
63
80
|
```sh
|
|
64
|
-
npx -y @cueai/omni-reader-mcp@1.2.
|
|
81
|
+
npx -y @cueai/omni-reader-mcp@1.2.2 setup --client cursor --allowed-root /absolute/minimum/root --headless --json
|
|
65
82
|
```
|
|
66
83
|
|
|
67
84
|
## Cache and journal isolation
|
|
@@ -181,16 +198,16 @@ new source that satisfies the constraints.
|
|
|
181
198
|
## Commands
|
|
182
199
|
|
|
183
200
|
```sh
|
|
184
|
-
npx -y @cueai/omni-reader-mcp@1.2.
|
|
185
|
-
npx -y @cueai/omni-reader-mcp@1.2.
|
|
186
|
-
npx -y @cueai/omni-reader-mcp@1.2.
|
|
187
|
-
npx -y @cueai/omni-reader-mcp@1.2.
|
|
201
|
+
npx -y @cueai/omni-reader-mcp@1.2.2 doctor
|
|
202
|
+
npx -y @cueai/omni-reader-mcp@1.2.2 doctor --json
|
|
203
|
+
npx -y @cueai/omni-reader-mcp@1.2.2 clean
|
|
204
|
+
npx -y @cueai/omni-reader-mcp@1.2.2 uninstall --yes --json
|
|
188
205
|
```
|
|
189
206
|
|
|
190
207
|
Running the pinned version without a command starts the stdio MCP server:
|
|
191
208
|
|
|
192
209
|
```sh
|
|
193
|
-
npx -y @cueai/omni-reader-mcp@1.2.
|
|
210
|
+
npx -y @cueai/omni-reader-mcp@1.2.2
|
|
194
211
|
```
|
|
195
212
|
|
|
196
213
|
`doctor --json` returns package/npm/client adapter, Key present/absent, allowed-root
|
|
@@ -199,12 +216,12 @@ status; it never prints the Key, private source paths, or content.
|
|
|
199
216
|
|
|
200
217
|
## Uninstall and rollback
|
|
201
218
|
|
|
202
|
-
`uninstall --yes --json` removes only
|
|
219
|
+
`uninstall --yes --json` removes only a trusted 1.2.1 or 1.2.2 Bridge entry; when a
|
|
203
220
|
matching trusted backup exists, it restores the original URL-only `omni-reader` entry.
|
|
204
221
|
Uninstall never deletes user source files and never silently removes unexpired local
|
|
205
222
|
results.
|
|
206
223
|
|
|
207
|
-
To roll back from 1.
|
|
224
|
+
To roll back from 1.2.2:
|
|
208
225
|
|
|
209
226
|
1. stop recommending or installing that version;
|
|
210
227
|
2. run `uninstall --yes --json` to restore the trusted URL-only entry;
|
|
@@ -7,6 +7,10 @@ export interface AgentConfigEnvironment {
|
|
|
7
7
|
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
8
8
|
}
|
|
9
9
|
export type AgentConfigStatus = "configured" | "not configured" | "invalid or unreadable";
|
|
10
|
+
export interface AgentConfigInspection {
|
|
11
|
+
readonly status: AgentConfigStatus;
|
|
12
|
+
readonly version?: string;
|
|
13
|
+
}
|
|
10
14
|
interface JsonPreviousEntry {
|
|
11
15
|
readonly format: "json";
|
|
12
16
|
readonly value: unknown;
|
|
@@ -40,6 +44,7 @@ export declare function buildAgentEntry(target: AgentTarget, extraRoots: readonl
|
|
|
40
44
|
export declare function prepareAgentConfig(target: AgentTarget, extraRoots: readonly string[], environment: AgentConfigEnvironment): Promise<PreparedAgentConfig>;
|
|
41
45
|
export declare function verifyPreparedAgentConfig(prepared: PreparedAgentConfig): Promise<void>;
|
|
42
46
|
export declare function writePreparedAgentConfig(prepared: PreparedAgentConfig): Promise<void>;
|
|
47
|
+
export declare function inspectAgentConfigDetails(configPath: string, environment?: AgentConfigEnvironment, target?: NativeAgentTarget): Promise<AgentConfigInspection>;
|
|
43
48
|
export declare function inspectAgentConfig(configPath: string, environment?: AgentConfigEnvironment, target?: NativeAgentTarget): Promise<AgentConfigStatus>;
|
|
44
49
|
export declare function detectAgentTargets(environment: AgentConfigEnvironment): Promise<NativeAgentTarget[]>;
|
|
45
50
|
export declare function configContainsOmni(configPath: string): Promise<boolean>;
|
package/dist/cli/agent-config.js
CHANGED
|
@@ -4,7 +4,9 @@ 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
|
+
const PREVIOUS_PACKAGE_SPEC = "@cueai/omni-reader-mcp@1.2.1";
|
|
7
8
|
const LEGACY_PACKAGE_SPEC = "@cueai/omni-reader-mcp";
|
|
9
|
+
const TRUSTED_EXACT_PACKAGE_SPECS = new Set([PREVIOUS_PACKAGE_SPEC, PACKAGE_SPEC]);
|
|
8
10
|
function isRecord(value) {
|
|
9
11
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10
12
|
}
|
|
@@ -543,7 +545,8 @@ function isExpectedOmniEntry(target, value) {
|
|
|
543
545
|
!Array.isArray(args) ||
|
|
544
546
|
args.length !== 2 ||
|
|
545
547
|
args[0] !== "-y" ||
|
|
546
|
-
args[1] !==
|
|
548
|
+
typeof args[1] !== "string" ||
|
|
549
|
+
!TRUSTED_EXACT_PACKAGE_SPECS.has(args[1]))
|
|
547
550
|
return false;
|
|
548
551
|
const keys = Object.keys(value).sort();
|
|
549
552
|
if (value.env === undefined) {
|
|
@@ -579,6 +582,12 @@ function isExpectedOmniEntry(target, value) {
|
|
|
579
582
|
&& keys[1] === "command"
|
|
580
583
|
&& keys[2] === "env";
|
|
581
584
|
}
|
|
585
|
+
function expectedBridgeVersion(target, value) {
|
|
586
|
+
if (!isExpectedOmniEntry(target, value))
|
|
587
|
+
return undefined;
|
|
588
|
+
const packageSpec = value.args[1];
|
|
589
|
+
return packageSpec === PREVIOUS_PACKAGE_SPEC ? "1.2.1" : BRIDGE_RELEASE_VERSION;
|
|
590
|
+
}
|
|
582
591
|
function isLegacyBridgeEntry(target, value) {
|
|
583
592
|
if (!isRecord(value) || value.command !== "npx")
|
|
584
593
|
return false;
|
|
@@ -586,7 +595,8 @@ function isLegacyBridgeEntry(target, value) {
|
|
|
586
595
|
if (!Array.isArray(args) ||
|
|
587
596
|
args.length !== 2 ||
|
|
588
597
|
args[0] !== "-y" ||
|
|
589
|
-
|
|
598
|
+
typeof args[1] !== "string" ||
|
|
599
|
+
(args[1] !== LEGACY_PACKAGE_SPEC && !TRUSTED_EXACT_PACKAGE_SPECS.has(args[1])))
|
|
590
600
|
return false;
|
|
591
601
|
if (value.env === undefined)
|
|
592
602
|
return target !== "hermes";
|
|
@@ -742,6 +752,7 @@ export async function prepareAgentConfig(target, extraRoots, environment) {
|
|
|
742
752
|
environment,
|
|
743
753
|
reload: reloadInstruction("hermes"),
|
|
744
754
|
format,
|
|
755
|
+
serializedBefore: loaded.serialized,
|
|
745
756
|
serializedAfter,
|
|
746
757
|
previousEntry,
|
|
747
758
|
};
|
|
@@ -786,9 +797,11 @@ async function atomicReplace(filePath, content) {
|
|
|
786
797
|
await handle.sync();
|
|
787
798
|
await handle.close();
|
|
788
799
|
handle = undefined;
|
|
800
|
+
await chmod(temporaryPath, 0o600);
|
|
789
801
|
await rename(temporaryPath, filePath);
|
|
790
|
-
|
|
791
|
-
|
|
802
|
+
// Rename is the commit point. Do not throw afterward: callers cannot safely
|
|
803
|
+
// roll back a committed user-config write without racing an external editor.
|
|
804
|
+
await syncDirectory(directory).catch(() => undefined);
|
|
792
805
|
}
|
|
793
806
|
finally {
|
|
794
807
|
await handle?.close();
|
|
@@ -912,6 +925,17 @@ export async function verifyPreparedAgentConfig(prepared) {
|
|
|
912
925
|
throw fileError("The Agent configuration changed after preview; no changes were written.");
|
|
913
926
|
}
|
|
914
927
|
}
|
|
928
|
+
async function restoreTrustedBackup(backupPath, backup) {
|
|
929
|
+
if (backup !== undefined) {
|
|
930
|
+
await atomicReplace(backupPath, `${JSON.stringify(backup, null, 2)}\n`);
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
await unlink(backupPath).catch((error) => {
|
|
934
|
+
if (error.code !== "ENOENT")
|
|
935
|
+
throw error;
|
|
936
|
+
});
|
|
937
|
+
await syncDirectory(path.dirname(backupPath));
|
|
938
|
+
}
|
|
915
939
|
export async function writePreparedAgentConfig(prepared) {
|
|
916
940
|
if (prepared.configPath === undefined ||
|
|
917
941
|
prepared.environment === undefined ||
|
|
@@ -919,36 +943,56 @@ export async function writePreparedAgentConfig(prepared) {
|
|
|
919
943
|
prepared.serializedAfter === undefined)
|
|
920
944
|
return;
|
|
921
945
|
await withConfigLock(prepared.configPath, prepared.environment, async () => {
|
|
922
|
-
const
|
|
946
|
+
const configPath = prepared.configPath;
|
|
947
|
+
const current = await readConfig(configPath);
|
|
923
948
|
if (current.fingerprint !== prepared.sourceFingerprint) {
|
|
924
949
|
throw fileError("The Agent configuration changed after preview; no changes were written.");
|
|
925
950
|
}
|
|
926
951
|
const backup = trustedBackup(prepared);
|
|
927
|
-
const backupPath = agentBackupPath(
|
|
928
|
-
const existingBackup = await readTrustedBackup(
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
952
|
+
const backupPath = agentBackupPath(configPath);
|
|
953
|
+
const existingBackup = await readTrustedBackup(configPath);
|
|
954
|
+
let replacementBackup = backup;
|
|
955
|
+
if (existingBackup !== undefined) {
|
|
956
|
+
const currentEntry = expectedConfigEntry(prepared.target, current);
|
|
957
|
+
if (currentEntry !== undefined && existingBackup.target === prepared.target) {
|
|
958
|
+
if (existingBackup.bridge_entry_digest !== entryDigest(currentEntry)) {
|
|
959
|
+
throw fileError("A conflicting trusted Omni backup already exists; no changes were written.");
|
|
960
|
+
}
|
|
961
|
+
replacementBackup = {
|
|
936
962
|
...existingBackup,
|
|
937
963
|
bridge_entry_digest: entryDigest(prepared.entry),
|
|
938
964
|
};
|
|
939
|
-
if (JSON.stringify(updatedBackup) !== JSON.stringify(existingBackup)) {
|
|
940
|
-
await atomicReplace(backupPath, `${JSON.stringify(updatedBackup, null, 2)}\n`);
|
|
941
|
-
}
|
|
942
965
|
}
|
|
943
966
|
else if (JSON.stringify(existingBackup) !== JSON.stringify(backup)) {
|
|
944
967
|
throw fileError("A conflicting trusted Omni backup already exists; no changes were written.");
|
|
945
968
|
}
|
|
969
|
+
else {
|
|
970
|
+
replacementBackup = existingBackup;
|
|
971
|
+
}
|
|
946
972
|
}
|
|
947
|
-
const
|
|
948
|
-
|
|
949
|
-
|
|
973
|
+
const backupNeedsWrite = existingBackup === undefined ||
|
|
974
|
+
JSON.stringify(replacementBackup) !== JSON.stringify(existingBackup);
|
|
975
|
+
try {
|
|
976
|
+
if (backupNeedsWrite) {
|
|
977
|
+
await atomicReplace(backupPath, `${JSON.stringify(replacementBackup, null, 2)}\n`);
|
|
978
|
+
}
|
|
979
|
+
const immediatelyBeforeWrite = await readConfig(configPath);
|
|
980
|
+
if (immediatelyBeforeWrite.fingerprint !== current.fingerprint) {
|
|
981
|
+
throw fileError("The Agent configuration changed during setup; no changes were written.");
|
|
982
|
+
}
|
|
983
|
+
await atomicReplace(configPath, prepared.serializedAfter);
|
|
984
|
+
}
|
|
985
|
+
catch (error) {
|
|
986
|
+
try {
|
|
987
|
+
if (backupNeedsWrite) {
|
|
988
|
+
await restoreTrustedBackup(backupPath, existingBackup);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
catch {
|
|
992
|
+
throw fileError("The Agent configuration write failed and trusted rollback state could not be restored.");
|
|
993
|
+
}
|
|
994
|
+
throw error;
|
|
950
995
|
}
|
|
951
|
-
await atomicReplace(prepared.configPath, prepared.serializedAfter);
|
|
952
996
|
});
|
|
953
997
|
}
|
|
954
998
|
function jsonConfigEntry(value) {
|
|
@@ -956,49 +1000,64 @@ function jsonConfigEntry(value) {
|
|
|
956
1000
|
return undefined;
|
|
957
1001
|
return onlyOmniEntry(value.mcpServers).value;
|
|
958
1002
|
}
|
|
959
|
-
|
|
1003
|
+
function expectedConfigEntry(target, loaded) {
|
|
960
1004
|
if (target === "hermes") {
|
|
961
1005
|
const document = parseHermesDocument(loaded.serialized);
|
|
962
1006
|
const entry = hermesOmniEntry(document);
|
|
963
|
-
|
|
1007
|
+
if (entry === undefined)
|
|
1008
|
+
return undefined;
|
|
1009
|
+
const value = parseHermesEntry(entry);
|
|
1010
|
+
return isExpectedOmniEntry(target, value) ? value : undefined;
|
|
964
1011
|
}
|
|
965
1012
|
assertNoDuplicateJsonOmniEntries(loaded.serialized);
|
|
966
|
-
const value = parseJsonConfig(loaded);
|
|
967
|
-
return isExpectedOmniEntry(target,
|
|
1013
|
+
const value = jsonConfigEntry(parseJsonConfig(loaded));
|
|
1014
|
+
return isExpectedOmniEntry(target, value) ? value : undefined;
|
|
968
1015
|
}
|
|
969
|
-
|
|
1016
|
+
async function configHasExpectedEntry(target, loaded) {
|
|
1017
|
+
return expectedConfigEntry(target, loaded) !== undefined;
|
|
1018
|
+
}
|
|
1019
|
+
export async function inspectAgentConfigDetails(configPath, environment, target) {
|
|
970
1020
|
try {
|
|
971
1021
|
if (environment !== undefined)
|
|
972
1022
|
await validateUserConfigPath(configPath, environment);
|
|
973
1023
|
const loaded = await readConfig(configPath);
|
|
974
1024
|
if (!loaded.existed)
|
|
975
|
-
return "not configured";
|
|
1025
|
+
return { status: "not configured" };
|
|
976
1026
|
const resolvedTarget = target ?? (configPath.endsWith("config.yaml") ? "hermes" : "cursor");
|
|
977
1027
|
if (resolvedTarget === "hermes") {
|
|
978
1028
|
const document = parseHermesDocument(loaded.serialized);
|
|
979
1029
|
const entry = hermesOmniEntry(document);
|
|
980
1030
|
if (entry === undefined)
|
|
981
|
-
return "not configured";
|
|
1031
|
+
return { status: "not configured" };
|
|
982
1032
|
const value = parseHermesEntry(entry);
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1033
|
+
const version = expectedBridgeVersion("hermes", value);
|
|
1034
|
+
if (version !== undefined)
|
|
1035
|
+
return { status: "configured", version };
|
|
1036
|
+
return {
|
|
1037
|
+
status: isCanonicalRemoteEntry(value) ? "not configured" : "invalid or unreadable",
|
|
1038
|
+
};
|
|
986
1039
|
}
|
|
987
1040
|
assertNoDuplicateJsonOmniEntries(loaded.serialized);
|
|
988
1041
|
const value = parseJsonConfig(loaded);
|
|
989
1042
|
if (!isRecord(value.mcpServers))
|
|
990
|
-
return "not configured";
|
|
1043
|
+
return { status: "not configured" };
|
|
991
1044
|
const entry = onlyOmniEntry(value.mcpServers).value;
|
|
992
1045
|
if (entry === undefined)
|
|
993
|
-
return "not configured";
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1046
|
+
return { status: "not configured" };
|
|
1047
|
+
const version = expectedBridgeVersion(resolvedTarget, entry);
|
|
1048
|
+
if (version !== undefined)
|
|
1049
|
+
return { status: "configured", version };
|
|
1050
|
+
return {
|
|
1051
|
+
status: isCanonicalRemoteEntry(entry) ? "not configured" : "invalid or unreadable",
|
|
1052
|
+
};
|
|
997
1053
|
}
|
|
998
1054
|
catch {
|
|
999
|
-
return "invalid or unreadable";
|
|
1055
|
+
return { status: "invalid or unreadable" };
|
|
1000
1056
|
}
|
|
1001
1057
|
}
|
|
1058
|
+
export async function inspectAgentConfig(configPath, environment, target) {
|
|
1059
|
+
return (await inspectAgentConfigDetails(configPath, environment, target)).status;
|
|
1060
|
+
}
|
|
1002
1061
|
export async function detectAgentTargets(environment) {
|
|
1003
1062
|
const detected = [];
|
|
1004
1063
|
for (const target of ["hermes", "cursor", "claude-desktop"]) {
|
|
@@ -1056,6 +1115,15 @@ export async function rollbackPreparedAgentConfig(prepared) {
|
|
|
1056
1115
|
if (!current.existed || current.serialized !== prepared.serializedAfter) {
|
|
1057
1116
|
throw fileError("The Agent configuration changed before rollback; automatic rollback stopped.");
|
|
1058
1117
|
}
|
|
1118
|
+
const backupPath = agentBackupPath(prepared.configPath);
|
|
1119
|
+
const backup = await readTrustedBackup(prepared.configPath);
|
|
1120
|
+
const predecessor = prepared.existed
|
|
1121
|
+
? expectedConfigEntry(prepared.target, {
|
|
1122
|
+
existed: true,
|
|
1123
|
+
serialized: prepared.serializedBefore,
|
|
1124
|
+
fingerprint: prepared.sourceFingerprint ?? "unused",
|
|
1125
|
+
})
|
|
1126
|
+
: undefined;
|
|
1059
1127
|
if (prepared.existed) {
|
|
1060
1128
|
await atomicReplace(prepared.configPath, prepared.serializedBefore);
|
|
1061
1129
|
}
|
|
@@ -1063,10 +1131,22 @@ export async function rollbackPreparedAgentConfig(prepared) {
|
|
|
1063
1131
|
await unlink(prepared.configPath);
|
|
1064
1132
|
await syncDirectory(path.dirname(prepared.configPath));
|
|
1065
1133
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1134
|
+
if (backup !== undefined &&
|
|
1135
|
+
predecessor !== undefined &&
|
|
1136
|
+
backup.target === prepared.target &&
|
|
1137
|
+
backup.source_fingerprint !== prepared.sourceFingerprint) {
|
|
1138
|
+
const restoredBackup = {
|
|
1139
|
+
...backup,
|
|
1140
|
+
bridge_entry_digest: entryDigest(predecessor),
|
|
1141
|
+
};
|
|
1142
|
+
await atomicReplace(backupPath, `${JSON.stringify(restoredBackup, null, 2)}\n`);
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
await unlink(backupPath).catch((error) => {
|
|
1146
|
+
if (error.code !== "ENOENT")
|
|
1147
|
+
throw error;
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1070
1150
|
await syncDirectory(path.dirname(prepared.configPath));
|
|
1071
1151
|
});
|
|
1072
1152
|
}
|
package/dist/cli/doctor.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { constants as fsConstants } from "node:fs";
|
|
2
2
|
import { lstat, open, readdir } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { DEFAULT_CUBE_BASE_URL, GRANTED_STREAM_PROTOCOL_VERSION, MAX_FILE_BYTES, CUBE_GRANT_PROTOCOL_VERSION, } from "../constants.js";
|
|
5
5
|
import { API_KEY_URL, getOnboardingPolicyWithTimeout, onboardingGuidance, } from "../onboarding-policy.js";
|
|
6
|
-
import { agentConfigPath,
|
|
6
|
+
import { agentConfigPath, inspectAgentConfigDetails, } from "./agent-config.js";
|
|
7
7
|
const CUBE_HEALTH_PATH = "/api/omni-reader/direct-upload/v1/health";
|
|
8
8
|
const GRANTED_UPLOAD_HEALTH_URL = "https://cubefile.ai.iiis.co:9443/omni/granted/health";
|
|
9
9
|
function isHealthBody(value) {
|
|
@@ -137,13 +137,9 @@ async function clientAdapterFacts(options) {
|
|
|
137
137
|
["claude_desktop", "claude-desktop"],
|
|
138
138
|
]) {
|
|
139
139
|
const configPath = agentConfigPath(target, options);
|
|
140
|
-
|
|
141
|
-
? "not configured"
|
|
142
|
-
: await
|
|
143
|
-
result[label] = {
|
|
144
|
-
status,
|
|
145
|
-
...(status === "configured" ? { version: BRIDGE_RELEASE_VERSION } : {}),
|
|
146
|
-
};
|
|
140
|
+
result[label] = configPath === undefined
|
|
141
|
+
? { status: "not configured" }
|
|
142
|
+
: await inspectAgentConfigDetails(configPath, options, target);
|
|
147
143
|
}
|
|
148
144
|
return result;
|
|
149
145
|
}
|
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.2.
|
|
9
|
+
export declare const BRIDGE_RELEASE_VERSION = "1.2.2";
|
|
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.2.
|
|
9
|
+
export const BRIDGE_RELEASE_VERSION = "1.2.2";
|
|
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;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
export declare const ONBOARDING_POLICY_URL = "https://cuecue.cn/api/v1/billing/public/onboarding-policy";
|
|
2
|
-
export declare const API_KEY_URL = "https://cuecue.cn/api-key";
|
|
2
|
+
export declare const API_KEY_URL = "https://cuecue.cn/hub/api-key";
|
|
3
|
+
export declare const LEGACY_API_KEY_URL = "https://cuecue.cn/api-key";
|
|
4
|
+
type ApiKeyUrl = typeof API_KEY_URL | typeof LEGACY_API_KEY_URL;
|
|
3
5
|
export interface OnboardingPolicy {
|
|
4
|
-
readonly apiKeyUrl:
|
|
6
|
+
readonly apiKeyUrl: ApiKeyUrl;
|
|
5
7
|
readonly firstRegistrationCredits: number;
|
|
6
8
|
readonly freeDailyCredits: number;
|
|
7
9
|
}
|
|
8
10
|
export declare function getOnboardingPolicy(fetchImpl: typeof fetch, signal: AbortSignal): Promise<OnboardingPolicy | undefined>;
|
|
9
11
|
export declare function onboardingGuidance(policy: OnboardingPolicy | undefined): string;
|
|
10
12
|
export declare function getOnboardingPolicyWithTimeout(fetchImpl: typeof fetch, timeoutMs?: number): Promise<OnboardingPolicy | undefined>;
|
|
13
|
+
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const ONBOARDING_POLICY_URL = "https://cuecue.cn/api/v1/billing/public/onboarding-policy";
|
|
2
|
-
export const API_KEY_URL = "https://cuecue.cn/api-key";
|
|
2
|
+
export const API_KEY_URL = "https://cuecue.cn/hub/api-key";
|
|
3
|
+
export const LEGACY_API_KEY_URL = "https://cuecue.cn/api-key";
|
|
3
4
|
function isRecord(value) {
|
|
4
5
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5
6
|
}
|
|
@@ -9,12 +10,16 @@ function boundedCredits(value) {
|
|
|
9
10
|
function normalizeOnboardingPolicy(value) {
|
|
10
11
|
if (!isRecord(value))
|
|
11
12
|
return undefined;
|
|
12
|
-
|
|
13
|
+
const apiKeyUrl = value.api_key_url;
|
|
14
|
+
const accepted = apiKeyUrl === API_KEY_URL || apiKeyUrl === LEGACY_API_KEY_URL;
|
|
15
|
+
if (!accepted ||
|
|
13
16
|
!boundedCredits(value.first_registration) ||
|
|
14
17
|
!boundedCredits(value.free_daily))
|
|
15
18
|
return undefined;
|
|
16
19
|
return {
|
|
17
|
-
|
|
20
|
+
// Preserve the server's actual accepted URL so a legacy-serving backend
|
|
21
|
+
// keeps working until it flips to the Hub path.
|
|
22
|
+
apiKeyUrl: apiKeyUrl,
|
|
18
23
|
firstRegistrationCredits: value.first_registration,
|
|
19
24
|
freeDailyCredits: value.free_daily,
|
|
20
25
|
};
|
package/dist/protocol.js
CHANGED
|
@@ -64,7 +64,12 @@ export const cancelParseSchema = z
|
|
|
64
64
|
export const readResultSchema = z
|
|
65
65
|
.object({
|
|
66
66
|
result_id: resultIdSchema,
|
|
67
|
-
cursor: z
|
|
67
|
+
cursor: z
|
|
68
|
+
.string()
|
|
69
|
+
.min(1)
|
|
70
|
+
.max(2048)
|
|
71
|
+
.describe("Pass the previous result's next_cursor as this cursor.")
|
|
72
|
+
.optional(),
|
|
68
73
|
max_bytes: z.number().int().min(1).max(RESULT_CHUNK_MAX_BYTES).optional(),
|
|
69
74
|
})
|
|
70
75
|
.strict();
|
package/dist/remote-client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LATEST_PROTOCOL_VERSION } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import { parseReaderCapabilities, selectUrlProfile, } from "./capabilities.js";
|
|
3
3
|
import { BRIDGE_RELEASE_VERSION, REMOTE_CAPABILITIES_CUSTOM_FIELD, REMOTE_OMNI_MCP_URL, } from "./constants.js";
|
|
4
|
+
import { API_KEY_URL } from "./onboarding-policy.js";
|
|
4
5
|
import { OmniBridgeError } from "./errors.js";
|
|
5
6
|
import { parseResultSchema } from "./result-contract.js";
|
|
6
7
|
import { classifySource } from "./source.js";
|
|
@@ -185,8 +186,29 @@ function decodeEnvelope(value, requestId) {
|
|
|
185
186
|
throw protocolError();
|
|
186
187
|
}
|
|
187
188
|
const result = envelope.result;
|
|
189
|
+
let candidate = result.structuredContent;
|
|
190
|
+
if (candidate === undefined) {
|
|
191
|
+
const content = result.content;
|
|
192
|
+
if (!Array.isArray(content) || content.length !== 1) {
|
|
193
|
+
throw protocolError();
|
|
194
|
+
}
|
|
195
|
+
const item = content[0];
|
|
196
|
+
if (item === null
|
|
197
|
+
|| typeof item !== "object"
|
|
198
|
+
|| Array.isArray(item)
|
|
199
|
+
|| item.type !== "text"
|
|
200
|
+
|| typeof item.text !== "string") {
|
|
201
|
+
throw protocolError();
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
candidate = JSON.parse(item.text);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
throw protocolError();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
188
210
|
try {
|
|
189
|
-
return parseResultSchema.parse(
|
|
211
|
+
return parseResultSchema.parse(candidate);
|
|
190
212
|
}
|
|
191
213
|
catch {
|
|
192
214
|
throw protocolError();
|
|
@@ -206,7 +228,7 @@ export class HttpRemoteOmniClient {
|
|
|
206
228
|
code: "API_KEY_REQUIRED",
|
|
207
229
|
message: "A Cue API Key is required for Omni parsing.",
|
|
208
230
|
failureScope: "authentication",
|
|
209
|
-
userAction:
|
|
231
|
+
userAction: `Create or configure an API Key at ${API_KEY_URL} without pasting it into chat.`,
|
|
210
232
|
operationCreated: false,
|
|
211
233
|
retryable: false,
|
|
212
234
|
});
|
|
@@ -301,7 +323,10 @@ export class HttpRemoteOmniClient {
|
|
|
301
323
|
retryable: false,
|
|
302
324
|
});
|
|
303
325
|
}
|
|
304
|
-
let args = {
|
|
326
|
+
let args = {
|
|
327
|
+
source: classified.source,
|
|
328
|
+
wait: false,
|
|
329
|
+
};
|
|
305
330
|
if (detail === "grounded" || detail === "layout") {
|
|
306
331
|
// D2-D Task 14: the URL profile is obtained and selected BEFORE any
|
|
307
332
|
// non-text tools/call is constructed. Detail is sent only for a valid
|
|
@@ -309,7 +334,7 @@ export class HttpRemoteOmniClient {
|
|
|
309
334
|
// UNSUPPORTED_DETAIL and zero tools/call requests.
|
|
310
335
|
const capabilities = await this.initializeCapabilities(signal);
|
|
311
336
|
selectUrlProfile(capabilities, detail);
|
|
312
|
-
args = { source: classified.source, detail };
|
|
337
|
+
args = { source: classified.source, detail, wait: false };
|
|
313
338
|
}
|
|
314
339
|
return this.#call("parse", args, clientRequestId, signal);
|
|
315
340
|
}
|
|
@@ -70,11 +70,19 @@ export type ParseResult = {
|
|
|
70
70
|
status: "canceled";
|
|
71
71
|
operation_id: string;
|
|
72
72
|
cleanup_deadline?: string;
|
|
73
|
+
billing?: {
|
|
74
|
+
credits_charged: number;
|
|
75
|
+
credits_remaining: number;
|
|
76
|
+
};
|
|
73
77
|
data_handling: DataHandling;
|
|
74
78
|
} | {
|
|
75
79
|
status: "expired";
|
|
76
80
|
operation_id: string;
|
|
77
81
|
requires_user_confirmation: true;
|
|
82
|
+
billing?: {
|
|
83
|
+
credits_charged: number;
|
|
84
|
+
credits_remaining: number;
|
|
85
|
+
};
|
|
78
86
|
};
|
|
79
87
|
export declare const stableErrorSchema: z.ZodObject<{
|
|
80
88
|
ok: z.ZodLiteral<false>;
|
package/dist/result-contract.js
CHANGED
|
@@ -2,6 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
import { RESULT_BUNDLE_PROTOCOL_VERSION } from "./protocol.js";
|
|
3
3
|
const operationIdSchema = z.string().min(1).max(128);
|
|
4
4
|
const resultIdSchema = z.string().regex(/^result_[A-Za-z0-9_-]{16,64}$/u);
|
|
5
|
+
const nextCursorSchema = z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.max(2048)
|
|
9
|
+
.describe("Pass this value as cursor in the next read_result call.");
|
|
5
10
|
const constraintsSchema = z
|
|
6
11
|
.object({
|
|
7
12
|
max_bytes: z.number().int().nonnegative().optional(),
|
|
@@ -76,7 +81,7 @@ const artifactResultSchema = z
|
|
|
76
81
|
result_bytes: z.number().int().nonnegative(),
|
|
77
82
|
expires_at: z.string().datetime({ offset: true }),
|
|
78
83
|
preview: z.string(),
|
|
79
|
-
next_cursor:
|
|
84
|
+
next_cursor: nextCursorSchema,
|
|
80
85
|
})
|
|
81
86
|
.strict();
|
|
82
87
|
const billingSchema = z
|
|
@@ -105,7 +110,7 @@ const contentStorageSchema = z.discriminatedUnion("kind", [
|
|
|
105
110
|
z
|
|
106
111
|
.object({
|
|
107
112
|
kind: z.literal("artifact"),
|
|
108
|
-
next_cursor:
|
|
113
|
+
next_cursor: nextCursorSchema,
|
|
109
114
|
preview: z.string().optional(),
|
|
110
115
|
})
|
|
111
116
|
.strict(),
|
|
@@ -120,7 +125,7 @@ const groundingStorageSchema = z.discriminatedUnion("kind", [
|
|
|
120
125
|
z
|
|
121
126
|
.object({
|
|
122
127
|
kind: z.literal("artifact"),
|
|
123
|
-
next_cursor:
|
|
128
|
+
next_cursor: nextCursorSchema,
|
|
124
129
|
})
|
|
125
130
|
.strict(),
|
|
126
131
|
]);
|
|
@@ -218,6 +223,7 @@ export const parseResultSchema = z.discriminatedUnion("status", [
|
|
|
218
223
|
status: z.literal("canceled"),
|
|
219
224
|
operation_id: operationIdSchema,
|
|
220
225
|
cleanup_deadline: z.string().datetime({ offset: true }).optional(),
|
|
226
|
+
billing: billingSchema.optional(),
|
|
221
227
|
data_handling: dataHandlingSchema,
|
|
222
228
|
})
|
|
223
229
|
.strict(),
|
|
@@ -226,6 +232,7 @@ export const parseResultSchema = z.discriminatedUnion("status", [
|
|
|
226
232
|
status: z.literal("expired"),
|
|
227
233
|
operation_id: operationIdSchema,
|
|
228
234
|
requires_user_confirmation: z.literal(true),
|
|
235
|
+
billing: billingSchema.optional(),
|
|
229
236
|
})
|
|
230
237
|
.strict(),
|
|
231
238
|
]);
|
|
@@ -235,7 +242,7 @@ const resultChunkSchema = z
|
|
|
235
242
|
result_bytes: z.number().int().nonnegative(),
|
|
236
243
|
expires_at: z.string().datetime({ offset: true }),
|
|
237
244
|
text: z.string(),
|
|
238
|
-
next_cursor:
|
|
245
|
+
next_cursor: nextCursorSchema.optional(),
|
|
239
246
|
})
|
|
240
247
|
.strict();
|
|
241
248
|
// v3 part read (D2-D item 9): closed chunk with part/media type/offset/
|
|
@@ -253,7 +260,7 @@ const bundlePartReadChunkSchema = z
|
|
|
253
260
|
offset: z.number().int().nonnegative(),
|
|
254
261
|
decoded_bytes: z.number().int().nonnegative(),
|
|
255
262
|
text: z.string(),
|
|
256
|
-
next_cursor:
|
|
263
|
+
next_cursor: nextCursorSchema.optional(),
|
|
257
264
|
expires_at: z.string().datetime({ offset: true }),
|
|
258
265
|
})
|
|
259
266
|
.strict(),
|
|
@@ -266,7 +273,7 @@ const bundlePartReadChunkSchema = z
|
|
|
266
273
|
offset: z.number().int().nonnegative(),
|
|
267
274
|
decoded_bytes: z.number().int().nonnegative(),
|
|
268
275
|
text: z.string(),
|
|
269
|
-
next_cursor:
|
|
276
|
+
next_cursor: nextCursorSchema.optional(),
|
|
270
277
|
expires_at: z.string().datetime({ offset: true }),
|
|
271
278
|
})
|
|
272
279
|
.strict(),
|
package/dist/tools.js
CHANGED
|
@@ -452,7 +452,7 @@ export function registerOmniTools(server, dependencies, taskStore) {
|
|
|
452
452
|
},
|
|
453
453
|
}, (args, extra) => callCancel(args.operation_id, extra, dependencies));
|
|
454
454
|
server.registerTool("read_result", {
|
|
455
|
-
description: "Read one UTF-8 chunk from a Bridge-created local result artifact.",
|
|
455
|
+
description: "Read one UTF-8 chunk from a Bridge-created local result artifact. Pass each returned next_cursor as cursor in the next call until it is absent.",
|
|
456
456
|
inputSchema: readResultSchema,
|
|
457
457
|
outputSchema: readResultToolOutputSchema,
|
|
458
458
|
annotations: {
|