@cueai/omni-reader-mcp 1.5.0 → 1.5.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 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.0 setup
67
+ npx -y @cueai/omni-reader-mcp@1.5.2 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.0 setup --client hermes --allowed-root /absolute/minimum/root --yes --json
76
- npx -y @cueai/omni-reader-mcp@1.5.0 setup --client cursor --add-root /absolute/minimum/root --yes --json
77
- npx -y @cueai/omni-reader-mcp@1.5.0 setup --client claude-desktop --allowed-root /absolute/minimum/root --yes --json
75
+ npx -y @cueai/omni-reader-mcp@1.5.2 setup --client hermes --allowed-root /absolute/minimum/root --yes --json
76
+ npx -y @cueai/omni-reader-mcp@1.5.2 setup --client cursor --add-root /absolute/minimum/root --yes --json
77
+ npx -y @cueai/omni-reader-mcp@1.5.2 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.0 setup --client cursor --allowed-root /absolute/minimum/root --headless --json
85
+ npx -y @cueai/omni-reader-mcp@1.5.2 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.2"] }
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.2` 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,47 @@ satisfies the constraints.
221
251
  ## Commands
222
252
 
223
253
  ```sh
224
- npx -y @cueai/omni-reader-mcp@1.5.0 doctor
225
- npx -y @cueai/omni-reader-mcp@1.5.0 doctor --json
226
- npx -y @cueai/omni-reader-mcp@1.5.0 clean
227
- npx -y @cueai/omni-reader-mcp@1.5.0 uninstall --yes --json
254
+ npx -y @cueai/omni-reader-mcp@1.5.2 doctor
255
+ npx -y @cueai/omni-reader-mcp@1.5.2 doctor --json
256
+ npx -y @cueai/omni-reader-mcp@1.5.2 clean
257
+ npx -y @cueai/omni-reader-mcp@1.5.2 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.0
263
+ npx -y @cueai/omni-reader-mcp@1.5.2
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
+ Bridge connects to the Omni parse API at `https://cubefile.ai.iiis.co:9443`. When a
273
+ connection fails, run `doctor` first — its endpoint-compatibility check covers the
274
+ API reachability from your machine. The common symptoms:
275
+
276
+ | Symptom | Most likely cause | Check |
277
+ |---|---|---|
278
+ | `MCP error -32000: Connection closed` on Windows | launcher shape (see Windows configuration) | `doctor` package/client rows |
279
+ | `CUBE_PROTOCOL_ERROR` or connection refused/timeout on connect | the hostname resolves to a private address on your network, or the route cannot reach the public endpoint | `nslookup cubefile.ai.iiis.co` — the public record is `122.200.68.28` |
280
+ | Works in the browser / on another machine but not this one | a local hosts-file override hides a DNS or route problem | check `/etc/hosts` (or `C:\Windows\System32\drivers\etc\hosts`) for a `cubefile.ai.iiis.co` line; test `curl --noproxy '*' -w '%{remote_ip}\n' https://cubefile.ai.iiis.co:9443/omni/health` |
281
+
282
+ If the API is reachable but a specific public URL fails to parse (`parse_timeout` /
283
+ parse failed) while it works in your browser, that is a fetch-layer condition of the
284
+ source on the parse API side — not a Bridge configuration problem — and it affects the
285
+ URL the same way from every client.
286
+
240
287
  ## Uninstall and rollback
241
288
 
242
- `uninstall --yes --json` removes only a trusted 1.4.1 or 1.5.0 Bridge entry; when a
289
+ `uninstall --yes --json` removes only a trusted 1.5.1 or 1.5.2 Bridge entry; when a
243
290
  matching trusted backup exists, it restores the original URL-only `omni-reader` entry.
244
291
  Uninstall never deletes user source files and never silently removes unexpired local
245
292
  results.
246
293
 
247
- To roll back from 1.5.0:
294
+ To roll back from 1.5.2:
248
295
 
249
296
  1. stop recommending or installing that version;
250
297
  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>;
@@ -7,7 +7,7 @@ const PACKAGE_SPEC = `@cueai/omni-reader-mcp@${BRIDGE_RELEASE_VERSION}`;
7
7
  // The release that the current version is a trusted upgrade from: uninstall and
8
8
  // rollback recognize exactly {previous, current} and nothing else. Bump this to
9
9
  // the version we just published when BRIDGE_RELEASE_VERSION advances.
10
- const PREVIOUS_RELEASE_VERSION = "1.4.1";
10
+ const PREVIOUS_RELEASE_VERSION = "1.5.1";
11
11
  const PREVIOUS_PACKAGE_SPEC = `@cueai/omni-reader-mcp@${PREVIOUS_RELEASE_VERSION}`;
12
12
  const LEGACY_PACKAGE_SPEC = "@cueai/omni-reader-mcp";
13
13
  const TRUSTED_EXACT_PACKAGE_SPECS = new Set([PREVIOUS_PACKAGE_SPEC, PACKAGE_SPEC]);
@@ -116,10 +116,14 @@ export function displayConfigPath(configPath, environment) {
116
116
  return configPath;
117
117
  }
118
118
  export function buildAgentEntry(target, extraRoots, platform) {
119
- const entry = {
120
- command: "npx",
121
- args: ["-y", PACKAGE_SPEC],
122
- };
119
+ // On Windows `npx` is `npx.cmd` — a batch wrapper a client cannot spawn without a
120
+ // shell (spawn("npx") → ENOENT, spawn("npx.cmd") → EINVAL). `cmd /d /c npx ...` is a
121
+ // real executable, so a no-shell spawn works (`/d` skips the AutoRun registry command,
122
+ // whose stdout would corrupt the MCP stdio frames); macOS/Linux keep the bare `npx`
123
+ // form.
124
+ const entry = platform === "win32"
125
+ ? { command: "cmd", args: ["/d", "/c", "npx", "-y", PACKAGE_SPEC] }
126
+ : { command: "npx", args: ["-y", PACKAGE_SPEC] };
123
127
  const env = {};
124
128
  if (target === "hermes")
125
129
  env.CUE_API_KEY = "${CUE_API_KEY}";
@@ -130,6 +134,49 @@ export function buildAgentEntry(target, extraRoots, platform) {
130
134
  entry.env = env;
131
135
  return entry;
132
136
  }
137
+ /** The exact package spec at the invocation's spec position, given its platform shape. */
138
+ function entryPackageSpec(entry, platform) {
139
+ const args = entry.args;
140
+ if (!Array.isArray(args))
141
+ return undefined;
142
+ if (platform !== "win32")
143
+ return args[1];
144
+ return entry.command === "npx" ? args[1] : args[4];
145
+ }
146
+ /**
147
+ * Matches the trusted invocation shape for the platform: `cmd /d /c npx -y <spec>` on
148
+ * Windows (runnable by a no-shell spawn; `/d` skips the AutoRun command whose stdout
149
+ * would corrupt MCP stdio frames), `npx -y <spec>` everywhere else. On Windows the
150
+ * previous release's bare-`npx` shape is additionally recognized as the migration
151
+ * source — 1.5.1's setup wrote it there and it can never run, but upgrade and
152
+ * uninstall must still identify it as a trusted entry so 1.5.2 can replace it.
153
+ */
154
+ function entryInvocationMatches(entry, platform) {
155
+ const args = entry.args;
156
+ if (platform === "win32") {
157
+ const cmdForm = entry.command === "cmd"
158
+ && Array.isArray(args)
159
+ && args.length === 5
160
+ && args[0] === "/d"
161
+ && args[1] === "/c"
162
+ && args[2] === "npx"
163
+ && args[3] === "-y"
164
+ && typeof args[4] === "string"
165
+ && TRUSTED_EXACT_PACKAGE_SPECS.has(args[4]);
166
+ const previousNpxForm = entry.command === "npx"
167
+ && Array.isArray(args)
168
+ && args.length === 2
169
+ && args[0] === "-y"
170
+ && args[1] === PREVIOUS_PACKAGE_SPEC;
171
+ return cmdForm || previousNpxForm;
172
+ }
173
+ return entry.command === "npx"
174
+ && Array.isArray(args)
175
+ && args.length === 2
176
+ && args[0] === "-y"
177
+ && typeof args[1] === "string"
178
+ && TRUSTED_EXACT_PACKAGE_SPECS.has(args[1]);
179
+ }
133
180
  function configFormat(target) {
134
181
  return target === "hermes" ? "hermes-yaml" : "json";
135
182
  }
@@ -500,7 +547,7 @@ function yamlEntry(entry, newline) {
500
547
  const args = entry.args;
501
548
  const lines = [
502
549
  " omni-reader:",
503
- " command: npx",
550
+ ` command: ${entry.command}`,
504
551
  " args:",
505
552
  ...args.map((argument) => ` - ${JSON.stringify(argument)}`),
506
553
  ];
@@ -541,16 +588,8 @@ function entryDigest(entry) {
541
588
  };
542
589
  return `sha256:${createHash("sha256").update(JSON.stringify(identity), "utf8").digest("hex")}`;
543
590
  }
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]))
591
+ function isExpectedOmniEntry(target, value, platform) {
592
+ if (!isRecord(value) || !entryInvocationMatches(value, platform))
554
593
  return false;
555
594
  const keys = Object.keys(value).sort();
556
595
  if (value.env === undefined) {
@@ -586,21 +625,30 @@ function isExpectedOmniEntry(target, value) {
586
625
  && keys[1] === "command"
587
626
  && keys[2] === "env";
588
627
  }
589
- function expectedBridgeVersion(target, value) {
590
- if (!isExpectedOmniEntry(target, value))
628
+ export function expectedBridgeVersion(target, value, platform) {
629
+ if (!isExpectedOmniEntry(target, value, platform))
591
630
  return undefined;
592
- const packageSpec = value.args[1];
631
+ const packageSpec = entryPackageSpec(value, platform);
593
632
  return packageSpec === PREVIOUS_PACKAGE_SPEC ? PREVIOUS_RELEASE_VERSION : BRIDGE_RELEASE_VERSION;
594
633
  }
595
- function isLegacyBridgeEntry(target, value) {
596
- if (!isRecord(value) || value.command !== "npx")
634
+ function isLegacyBridgeEntry(target, value, platform) {
635
+ // The legacy npx form (what every pre-1.5.2 setup wrote, incl. on Windows) plus, on
636
+ // Windows, the current cmd form — both are recognizable Bridge entries.
637
+ const legacyNpx = (entry) => {
638
+ const args = entry.args;
639
+ return entry.command === "npx"
640
+ && Array.isArray(args)
641
+ && args.length === 2
642
+ && args[0] === "-y"
643
+ && typeof args[1] === "string"
644
+ && (args[1] === LEGACY_PACKAGE_SPEC || TRUSTED_EXACT_PACKAGE_SPECS.has(args[1]));
645
+ };
646
+ if (!isRecord(value))
597
647
  return false;
598
- const args = value.args;
599
- if (!Array.isArray(args) ||
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])))
648
+ const shapeOk = platform === "win32"
649
+ ? (legacyNpx(value) || entryInvocationMatches(value, platform))
650
+ : legacyNpx(value);
651
+ if (!shapeOk)
604
652
  return false;
605
653
  if (value.env === undefined)
606
654
  return target !== "hermes";
@@ -640,12 +688,12 @@ function hermesOmniEntry(document, displayPath) {
640
688
  }
641
689
  return entry;
642
690
  }
643
- function previousJsonEntry(value, displayPath) {
691
+ function previousJsonEntry(value, displayPath, platform) {
644
692
  if (value === undefined)
645
693
  return null;
646
694
  if (isCanonicalRemoteEntry(value))
647
695
  return { format: "json", value };
648
- if (isLegacyBridgeEntry("cursor", value))
696
+ if (isLegacyBridgeEntry("cursor", value, platform))
649
697
  return null;
650
698
  throw fileError(`The existing "omni-reader" entry in ${displayPath} is not a trusted remote or Bridge `
651
699
  + "configuration; no changes were written. Remove that entry manually, or restore a backup, "
@@ -663,7 +711,7 @@ function hermesCredentialsAreReferences(serialized) {
663
711
  }
664
712
  return true;
665
713
  }
666
- function previousHermesEntry(entry, displayPath) {
714
+ function previousHermesEntry(entry, displayPath, platform) {
667
715
  if (entry === undefined)
668
716
  return null;
669
717
  const value = parseHermesEntry(entry);
@@ -675,7 +723,7 @@ function previousHermesEntry(entry, displayPath) {
675
723
  }
676
724
  return { format: "hermes-yaml", serialized: entry.serialized };
677
725
  }
678
- if (isLegacyBridgeEntry("hermes", value))
726
+ if (isLegacyBridgeEntry("hermes", value, platform))
679
727
  return null;
680
728
  throw fileError(`The existing "omni-reader" entry in ${displayPath} is not a trusted remote or Bridge `
681
729
  + "configuration; no changes were written. Remove that entry manually, or restore a backup, "
@@ -710,7 +758,7 @@ export async function prepareAgentConfig(target, extraRoots, environment) {
710
758
  }
711
759
  const servers = existingServers ?? {};
712
760
  const existing = onlyOmniEntry(servers, displayPath);
713
- const previousEntry = previousJsonEntry(existing.value, displayPath);
761
+ const previousEntry = previousJsonEntry(existing.value, displayPath, environment.platform);
714
762
  const afterValue = {
715
763
  ...value,
716
764
  mcpServers: {
@@ -737,7 +785,7 @@ export async function prepareAgentConfig(target, extraRoots, environment) {
737
785
  }
738
786
  const document = parseHermesDocument(loaded.serialized);
739
787
  const existing = hermesOmniEntry(document, displayPath);
740
- const previousEntry = previousHermesEntry(existing, displayPath);
788
+ const previousEntry = previousHermesEntry(existing, displayPath, environment.platform);
741
789
  const serializedAfter = replaceHermesEntry(loaded.serialized, document, yamlEntry(entry, document.newline));
742
790
  return {
743
791
  target: "hermes",
@@ -964,7 +1012,7 @@ export async function writePreparedAgentConfig(prepared) {
964
1012
  const existingBackup = await readTrustedBackup(configPath);
965
1013
  let replacementBackup = backup;
966
1014
  if (existingBackup !== undefined) {
967
- const currentEntry = expectedConfigEntry(prepared.target, current);
1015
+ const currentEntry = expectedConfigEntry(prepared.target, current, prepared.environment?.platform ?? "darwin");
968
1016
  if (currentEntry !== undefined && existingBackup.target === prepared.target) {
969
1017
  if (existingBackup.bridge_entry_digest !== entryDigest(currentEntry)) {
970
1018
  throw fileError("A conflicting trusted Omni backup already exists; no changes were written.");
@@ -1011,21 +1059,21 @@ function jsonConfigEntry(value) {
1011
1059
  return undefined;
1012
1060
  return onlyOmniEntry(value.mcpServers, "the Agent configuration").value;
1013
1061
  }
1014
- function expectedConfigEntry(target, loaded) {
1062
+ function expectedConfigEntry(target, loaded, platform) {
1015
1063
  if (target === "hermes") {
1016
1064
  const document = parseHermesDocument(loaded.serialized);
1017
1065
  const entry = hermesOmniEntry(document, "the Hermes configuration");
1018
1066
  if (entry === undefined)
1019
1067
  return undefined;
1020
1068
  const value = parseHermesEntry(entry);
1021
- return isExpectedOmniEntry(target, value) ? value : undefined;
1069
+ return isExpectedOmniEntry(target, value, platform) ? value : undefined;
1022
1070
  }
1023
1071
  assertNoDuplicateJsonOmniEntries(loaded.serialized);
1024
1072
  const value = jsonConfigEntry(parseJsonConfig(loaded));
1025
- return isExpectedOmniEntry(target, value) ? value : undefined;
1073
+ return isExpectedOmniEntry(target, value, platform) ? value : undefined;
1026
1074
  }
1027
- async function configHasExpectedEntry(target, loaded) {
1028
- return expectedConfigEntry(target, loaded) !== undefined;
1075
+ async function configHasExpectedEntry(target, loaded, platform) {
1076
+ return expectedConfigEntry(target, loaded, platform) !== undefined;
1029
1077
  }
1030
1078
  export async function inspectAgentConfigDetails(configPath, environment, target) {
1031
1079
  try {
@@ -1041,7 +1089,7 @@ export async function inspectAgentConfigDetails(configPath, environment, target)
1041
1089
  if (entry === undefined)
1042
1090
  return { status: "not configured" };
1043
1091
  const value = parseHermesEntry(entry);
1044
- const version = expectedBridgeVersion("hermes", value);
1092
+ const version = expectedBridgeVersion("hermes", value, environment?.platform ?? "darwin");
1045
1093
  if (version !== undefined)
1046
1094
  return { status: "configured", version };
1047
1095
  return {
@@ -1055,7 +1103,7 @@ export async function inspectAgentConfigDetails(configPath, environment, target)
1055
1103
  const entry = onlyOmniEntry(value.mcpServers, configPath).value;
1056
1104
  if (entry === undefined)
1057
1105
  return { status: "not configured" };
1058
- const version = expectedBridgeVersion(resolvedTarget, entry);
1106
+ const version = expectedBridgeVersion(resolvedTarget, entry, environment?.platform ?? "darwin");
1059
1107
  if (version !== undefined)
1060
1108
  return { status: "configured", version };
1061
1109
  return {
@@ -1133,7 +1181,7 @@ export async function rollbackPreparedAgentConfig(prepared) {
1133
1181
  existed: true,
1134
1182
  serialized: prepared.serializedBefore,
1135
1183
  fingerprint: prepared.sourceFingerprint ?? "unused",
1136
- })
1184
+ }, prepared.environment?.platform ?? "darwin")
1137
1185
  : undefined;
1138
1186
  if (prepared.existed) {
1139
1187
  await atomicReplace(prepared.configPath, prepared.serializedBefore);
@@ -1192,11 +1240,11 @@ export async function uninstallAgentConfig(target, environment) {
1192
1240
  return undefined;
1193
1241
  await validateUserConfigPath(configPath, environment);
1194
1242
  const initial = await readConfig(configPath);
1195
- if (!initial.existed || !await configHasExpectedEntry(target, initial))
1243
+ if (!initial.existed || !await configHasExpectedEntry(target, initial, environment.platform))
1196
1244
  return undefined;
1197
1245
  return await withConfigLock(configPath, environment, async () => {
1198
1246
  const current = await readConfig(configPath);
1199
- if (!await configHasExpectedEntry(target, current)) {
1247
+ if (!await configHasExpectedEntry(target, current, environment.platform)) {
1200
1248
  throw fileError("The Agent configuration changed before uninstall; no changes were written.");
1201
1249
  }
1202
1250
  const currentEntry = target === "hermes"
@@ -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.0";
9
+ export declare const BRIDGE_RELEASE_VERSION = "1.5.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.5.0";
9
+ export const BRIDGE_RELEASE_VERSION = "1.5.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;
@@ -760,7 +760,10 @@ function remoteResultUpdate(result, expectedOperationId) {
760
760
  operationId: result.operation_id,
761
761
  errorCode: null,
762
762
  parserStarted: true,
763
- billed: true,
763
+ // Owner ruling: credits are deducted only on successful completion — the public
764
+ // billed flag mirrors the remote settlement facts (zero-charge completion is a
765
+ // settled fact, not an uncharged one). Never write true unconditionally.
766
+ billed: (result.billing?.credits_charged ?? 0) > 0,
764
767
  contentReleased: true,
765
768
  stage: "completed",
766
769
  progressPercent: 100,
@@ -778,7 +781,11 @@ function remoteResultUpdate(result, expectedOperationId) {
778
781
  operationId: result.operation_id,
779
782
  errorCode: null,
780
783
  parserStarted: true,
781
- billed: true,
784
+ // The cleanup_pending wire carries no settlement fact (the closed v3/v4 contract
785
+ // exposes usage/billing only on released/delivered/delivery_expired terminals) —
786
+ // report false rather than inventing a charge; the journal allows a later
787
+ // false->true completion fact (assertStableTransition only blocks true->false).
788
+ billed: false,
782
789
  contentReleased: result.result !== undefined,
783
790
  stage: "cleanup_pending",
784
791
  progressPercent: result.result === undefined ? 99 : 100,
@@ -869,7 +876,8 @@ function localResultValue(local) {
869
876
  operationCreated: true,
870
877
  fileUploaded: true,
871
878
  parserStarted: true,
872
- billed: true,
879
+ // Owner ruling: an integrity failure settled nothing — never report billed:true.
880
+ billed: false,
873
881
  contentReleased: true,
874
882
  });
875
883
  }
@@ -996,7 +1004,8 @@ function requireBundleResult(retention) {
996
1004
  operationCreated: true,
997
1005
  fileUploaded: true,
998
1006
  parserStarted: true,
999
- billed: true,
1007
+ // Owner ruling: an integrity failure settled nothing — never report billed:true.
1008
+ billed: false,
1000
1009
  contentReleased: false,
1001
1010
  });
1002
1011
  }
@@ -1429,12 +1438,32 @@ export function createLocalParseOperationManager(options) {
1429
1438
  result: processingParse(operationId, stage, latestPercent, patch.contentReleased === true),
1430
1439
  };
1431
1440
  }
1441
+ const checkpointBilled = snapshot.patch?.billed === true;
1432
1442
  return {
1433
1443
  update: {
1434
1444
  state: "FAILED",
1435
- patch: failurePatch(stable),
1445
+ patch: {
1446
+ ...failurePatch(stable),
1447
+ // The ACK checkpoint may already have recorded a settled charge
1448
+ // (snapshot.patch at :1751 carries resultPatch's billed fact).
1449
+ // Never regress it — assertStableTransition blocks true->false —
1450
+ // the charge happened; this failure is about the local artifact,
1451
+ // not the settlement.
1452
+ billed: checkpointBilled || stable.billed,
1453
+ },
1454
+ },
1455
+ result: {
1456
+ status: "failed",
1457
+ error: {
1458
+ ...stable.toJSON(),
1459
+ // Deliberately the checkpoint fact only (not the OR): the failed
1460
+ // public result reports exactly what this run settled — false
1461
+ // when nothing was settled (owner ruling), true when the ACK
1462
+ // checkpoint already recorded the charge and the failure is
1463
+ // about local delivery, not the settlement.
1464
+ billed: checkpointBilled,
1465
+ },
1436
1466
  },
1437
- result: { status: "failed", error: stable.toJSON() },
1438
1467
  };
1439
1468
  }
1440
1469
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cueai/omni-reader-mcp",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Local stdio MCP bridge for direct Omni document parsing",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",