@cueai/omni-reader-mcp 1.5.4 → 1.6.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 +72 -39
- package/dist/artifact-store.d.ts +2 -0
- package/dist/artifact-store.js +45 -10
- package/dist/cli/agent-config.js +2 -2
- package/dist/cli/arguments.d.ts +8 -4
- package/dist/cli/arguments.js +62 -7
- package/dist/cli/config-inspection.d.ts +59 -0
- package/dist/cli/config-inspection.js +307 -0
- package/dist/cli/doctor.d.ts +7 -0
- package/dist/cli/doctor.js +37 -2
- package/dist/cli/setup.js +13 -2
- package/dist/constants.d.ts +2 -1
- package/dist/constants.js +4 -3
- package/dist/cube-client.js +1 -1
- package/dist/cursor.d.ts +4 -0
- package/dist/cursor.js +11 -13
- package/dist/index.js +10 -1
- package/dist/operation-journal.d.ts +4 -1
- package/dist/operation-journal.js +85 -15
- package/dist/operation-manager.d.ts +6 -2
- package/dist/operation-manager.js +246 -63
- package/dist/path-normalization.d.ts +5 -0
- package/dist/path-normalization.js +25 -0
- package/dist/path-security.d.ts +2 -1
- package/dist/path-security.js +49 -28
- package/dist/protocol.d.ts +10 -2
- package/dist/protocol.js +19 -7
- package/dist/result-contract.d.ts +46 -6
- package/dist/result-contract.js +118 -3
- package/dist/task-runtime.d.ts +3 -2
- package/dist/task-runtime.js +2 -2
- package/dist/tools.d.ts +4 -0
- package/dist/tools.js +37 -16
- package/package.json +14 -1
package/README.md
CHANGED
|
@@ -48,31 +48,25 @@ agent workspace remains the default allowed scope.
|
|
|
48
48
|
For a local file, Bridge requests a Cube parse grant and uses the returned controlled
|
|
49
49
|
granted-data-plane URL to upload the file and stream the result. A `BRIDGE_UPGRADE_REQUIRED`
|
|
50
50
|
error means the running Bridge release is not accepted for direct local-file parsing —
|
|
51
|
-
install `@cueai/omni-reader-mcp@1.
|
|
51
|
+
install `@cueai/omni-reader-mcp@1.6.0` using the official setup instructions, then retry the
|
|
52
52
|
same parse. Always run `doctor --json` first when diagnosing a parse failure.
|
|
53
53
|
|
|
54
54
|
## Free credits
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
The server-side onboarding policy is the authority for the current first-registration gift
|
|
57
|
+
and daily grant. When reachable, `doctor` reports those live policy values; when unavailable,
|
|
58
|
+
it reports that status instead of copying a stale number.
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
available on day one, including the daily grant);
|
|
62
|
-
- inviting a new user who registers gives both the inviter and the invitee 50 credits, with
|
|
63
|
-
no invite limit; when an invited user subscribes, the inviter additionally receives 10% of
|
|
64
|
-
the invitee's first-month credit quota as a bonus.
|
|
65
|
-
|
|
66
|
-
When available, the `doctor` output reports the live first-registration gift and daily grant;
|
|
67
|
-
referral rules and conversion ratios follow the server-side onboarding policy. If live values
|
|
68
|
-
differ from the numbers above, report the live values.
|
|
60
|
+
Bridge does not publish static document-page or media-duration conversions. Rates can change,
|
|
61
|
+
and Phase 1 has no public rate response carrying an `effective_at` timestamp. Use only current
|
|
62
|
+
authoritative billing/policy facts rather than estimating a charge from copied arithmetic.
|
|
69
63
|
|
|
70
64
|
## Installation and configuration
|
|
71
65
|
|
|
72
66
|
Always use an audited exact version, never an implicit `latest`:
|
|
73
67
|
|
|
74
68
|
```sh
|
|
75
|
-
npx -y @cueai/omni-reader-mcp@1.
|
|
69
|
+
npx -y @cueai/omni-reader-mcp@1.6.0 setup
|
|
76
70
|
```
|
|
77
71
|
|
|
78
72
|
The interactive setup supports Hermes, Cursor, Claude Desktop, and generic stdio
|
|
@@ -80,9 +74,9 @@ configuration. Non-interactive installation uses the same argument parsing and w
|
|
|
80
74
|
logic:
|
|
81
75
|
|
|
82
76
|
```sh
|
|
83
|
-
npx -y @cueai/omni-reader-mcp@1.
|
|
84
|
-
npx -y @cueai/omni-reader-mcp@1.
|
|
85
|
-
npx -y @cueai/omni-reader-mcp@1.
|
|
77
|
+
npx -y @cueai/omni-reader-mcp@1.6.0 setup --client hermes --allowed-root /absolute/minimum/root --yes --json
|
|
78
|
+
npx -y @cueai/omni-reader-mcp@1.6.0 setup --client cursor --add-root /absolute/minimum/root --yes --json
|
|
79
|
+
npx -y @cueai/omni-reader-mcp@1.6.0 setup --client claude-desktop --allowed-root /absolute/minimum/root --yes --json
|
|
86
80
|
```
|
|
87
81
|
|
|
88
82
|
When an agent or script runs under a pty (stdin is still a TTY), declare non-interactive
|
|
@@ -90,7 +84,7 @@ mode explicitly with `--headless` (alias `--non-interactive`): no `--yes` is req
|
|
|
90
84
|
stdin is never read:
|
|
91
85
|
|
|
92
86
|
```sh
|
|
93
|
-
npx -y @cueai/omni-reader-mcp@1.
|
|
87
|
+
npx -y @cueai/omni-reader-mcp@1.6.0 setup --client cursor --allowed-root /absolute/minimum/root --headless --json
|
|
94
88
|
```
|
|
95
89
|
|
|
96
90
|
## Windows configuration
|
|
@@ -109,14 +103,14 @@ automatically; the second is the most robust when you configure by hand.
|
|
|
109
103
|
1. **`cmd` + `npx` (what `setup` writes on Windows)** — `cmd.exe` is a real executable,
|
|
110
104
|
so a client's no-shell spawn works:
|
|
111
105
|
```json
|
|
112
|
-
{ "command": "cmd", "args": ["/d", "/c", "npx", "-y", "@cueai/omni-reader-mcp@1.
|
|
106
|
+
{ "command": "cmd", "args": ["/d", "/c", "npx", "-y", "@cueai/omni-reader-mcp@1.6.0"] }
|
|
113
107
|
```
|
|
114
108
|
2. **`node` + the package entry (most robust)** — skips `npx` entirely; the absolute
|
|
115
109
|
path must be a stable install you control:
|
|
116
110
|
```json
|
|
117
111
|
{ "command": "node", "args": ["C:\\path\\to\\node_modules\\@cueai\\omni-reader-mcp\\dist\\index.js"] }
|
|
118
112
|
```
|
|
119
|
-
A deliberate local `npm install @cueai/omni-reader-mcp@1.
|
|
113
|
+
A deliberate local `npm install @cueai/omni-reader-mcp@1.6.0` gives you that stable
|
|
120
114
|
path. Do not point the entry at an `_npx` cache directory — its hash subdirectory
|
|
121
115
|
changes between installs.
|
|
122
116
|
3. **Never use a session/timestamp directory** — such paths are cleaned up, and the
|
|
@@ -165,6 +159,31 @@ Agents pass the user-provided source string directly to `parse(source)`:
|
|
|
165
159
|
- Prefer the existing `operation_id` for status recovery; never re-upload or duplicate a
|
|
166
160
|
parse for a timeout.
|
|
167
161
|
|
|
162
|
+
Use the result contract in this order:
|
|
163
|
+
|
|
164
|
+
```text
|
|
165
|
+
Answer directly → use inline text when present; otherwise read_result
|
|
166
|
+
Find one section → read_outline, then pass its cursor to read_result
|
|
167
|
+
Read all content → read_result until next_cursor is absent
|
|
168
|
+
Deliver a file → save_result
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Outline navigation does not require `save_result`: call `read_outline`, then pass a selected
|
|
172
|
+
node cursor directly to `read_result`. `result_delivery="artifact"` is appropriate for saving, section navigation, multiple documents, or strict context control; omitted or `auto` preserves
|
|
173
|
+
inline delivery when possible. For multiple sources, use bounded concurrent independent `parse` calls and keep each operation/result handle separate. There is no `batch_parse` tool,
|
|
174
|
+
and Bridge does not invent one universal concurrency number before client evidence exists.
|
|
175
|
+
|
|
176
|
+
Client capabilities are classified exactly as `supported | unsupported | unknown`; they are
|
|
177
|
+
discovered from the actual initialize exchange, client documentation/settings, a local
|
|
178
|
+
non-billable harness, or explicit redacted config facts—not guessed from another MCP host.
|
|
179
|
+
Safe fallbacks are:
|
|
180
|
+
|
|
181
|
+
- Tasks unknown → ordinary `parse` / `get_parse_status` polling.
|
|
182
|
+
- Roots unknown → process cwd and explicitly configured roots only.
|
|
183
|
+
- Host timeout unknown → retain the bounded 20-second status wait.
|
|
184
|
+
- Process cwd versus active workspace unknown → do not widen authorization; use cwd and
|
|
185
|
+
explicit roots until the client provides evidence.
|
|
186
|
+
|
|
168
187
|
The public tools are fixed:
|
|
169
188
|
|
|
170
189
|
- `parse`
|
|
@@ -261,37 +280,50 @@ satisfies the constraints.
|
|
|
261
280
|
## Commands
|
|
262
281
|
|
|
263
282
|
```sh
|
|
264
|
-
npx -y @cueai/omni-reader-mcp@1.
|
|
265
|
-
npx -y @cueai/omni-reader-mcp@1.
|
|
266
|
-
npx -y @cueai/omni-reader-mcp@1.
|
|
267
|
-
npx -y @cueai/omni-reader-mcp@1.
|
|
283
|
+
npx -y @cueai/omni-reader-mcp@1.6.0 doctor
|
|
284
|
+
npx -y @cueai/omni-reader-mcp@1.6.0 doctor --json
|
|
285
|
+
npx -y @cueai/omni-reader-mcp@1.6.0 clean
|
|
286
|
+
npx -y @cueai/omni-reader-mcp@1.6.0 uninstall --yes --json
|
|
268
287
|
```
|
|
269
288
|
|
|
270
289
|
Running the pinned version without a command starts the stdio MCP server:
|
|
271
290
|
|
|
272
291
|
```sh
|
|
273
|
-
npx -y @cueai/omni-reader-mcp@1.
|
|
292
|
+
npx -y @cueai/omni-reader-mcp@1.6.0
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Default `doctor --json` reports `inspection_scope: "current_process_env"`: its Key and
|
|
296
|
+
allowed-root facts describe the doctor process, not a separately running agent child. To
|
|
297
|
+
inspect one caller-selected JSON entry without scanning or writing configuration, use:
|
|
298
|
+
|
|
299
|
+
```sh
|
|
300
|
+
npx -y @cueai/omni-reader-mcp@1.6.0 doctor --json --config-path /absolute/client-config.json --server-name omni-reader
|
|
274
301
|
```
|
|
275
302
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
303
|
+
The optional `explicit_config` block reports only closed redacted facts: entry/transport
|
|
304
|
+
shape, command basename/category, exact package version when derivable, Key configuration
|
|
305
|
+
shape, and allowed-root count/safety. It never resolves or prints a Key, uses a configured
|
|
306
|
+
Key for a network call, prints the config path, scans unrelated entries, or claims that the
|
|
307
|
+
running child process can use the selected entry.
|
|
308
|
+
|
|
309
|
+
`doctor --json` also returns package/npm/client adapter, authenticated Cube
|
|
310
|
+
control/configuration facts, artifacts, cache mode, onboarding, and reload status. The
|
|
311
|
+
`endpoints.direct_upload` field is a fact about the release, not a probe: the granted data
|
|
312
|
+
plane is reached only through a Cube grant and is never
|
|
281
313
|
definitively "available" until a real local-file parse validates the route.
|
|
282
314
|
|
|
283
315
|
|
|
284
316
|
## Network diagnostics
|
|
285
317
|
|
|
286
|
-
Run `npx -y @cueai/omni-reader-mcp@1.
|
|
318
|
+
Run `npx -y @cueai/omni-reader-mcp@1.6.0 doctor --json` first. Diagnose failures by
|
|
287
319
|
stage instead of guessing an endpoint:
|
|
288
320
|
|
|
289
|
-
|
|
|
321
|
+
| Failure | Stage | Meaning |
|
|
290
322
|
|---|---|---|
|
|
291
323
|
| `CUBE_UNAVAILABLE` | before upload | the control plane could not create a parse grant |
|
|
292
324
|
| `PRINCIPAL_CONCURRENCY_LIMIT` | before upload | the principal already has the maximum active parses; wait for `retry_after`, then retry the same parse |
|
|
293
|
-
| `BRIDGE_UPGRADE_REQUIRED` | before upload | this Bridge release is too old; install `@cueai/omni-reader-mcp@1.
|
|
294
|
-
|
|
|
325
|
+
| `BRIDGE_UPGRADE_REQUIRED` | before upload | this Bridge release is too old; install `@cueai/omni-reader-mcp@1.6.0` via the official setup, then retry the same parse |
|
|
326
|
+
| a secure upload-stage failure | after grant creation | the file was granted, but the secure upload stage did not complete |
|
|
295
327
|
| `CUBE_PROTOCOL_ERROR` | control-plane response | the response did not match the required contract |
|
|
296
328
|
| `MCP error -32000: Connection closed` | before Bridge starts | the client launcher shape is invalid; see Windows configuration |
|
|
297
329
|
|
|
@@ -309,15 +341,16 @@ the same way from every client.
|
|
|
309
341
|
|
|
310
342
|
## Uninstall and rollback
|
|
311
343
|
|
|
312
|
-
`uninstall --yes --json` removes only a trusted 1.5.
|
|
344
|
+
`uninstall --yes --json` removes only a trusted 1.5.5 or 1.6.0 Bridge entry, plus
|
|
313
345
|
the exact broken bare-`npx` Windows entry written by 1.5.1. When a matching trusted
|
|
314
346
|
backup exists, it restores the original URL-only `omni-reader` entry. Uninstall never
|
|
315
347
|
deletes user source files and never silently removes unexpired local results.
|
|
316
348
|
|
|
317
|
-
To roll back from 1.5.
|
|
349
|
+
To roll back from 1.6.0 to 1.5.5:
|
|
318
350
|
|
|
319
|
-
1. stop recommending or installing
|
|
351
|
+
1. stop recommending or installing 1.6.0;
|
|
320
352
|
2. run `uninstall --yes --json` to restore the trusted URL-only entry;
|
|
321
|
-
3.
|
|
353
|
+
3. if the local facade is still required, run `npx -y @cueai/omni-reader-mcp@1.5.5 setup`;
|
|
354
|
+
4. for already-uploaded operations, continue using status recovery so settlement and
|
|
322
355
|
cleanup complete;
|
|
323
|
-
|
|
356
|
+
5. keep local results, or have the user explicitly run `discard_result` / `clean`.
|
package/dist/artifact-store.d.ts
CHANGED
|
@@ -109,8 +109,10 @@ export declare class ArtifactStore {
|
|
|
109
109
|
static open(options?: ArtifactStoreOptions): Promise<ArtifactStore>;
|
|
110
110
|
get rootDirectory(): string;
|
|
111
111
|
createRetention(): LocalResultRetention;
|
|
112
|
+
preflightWrite(): Promise<void>;
|
|
112
113
|
read(resultId: string, cursor?: string, maxBytes?: number): Promise<ArtifactReadResult>;
|
|
113
114
|
readOutline(resultId: string): Promise<OutlineResult>;
|
|
115
|
+
mintReadCursor(resultId: string, byteOffset: number): Promise<string>;
|
|
114
116
|
mintOutlineCursor(resultId: string, byteOffset: number): Promise<string>;
|
|
115
117
|
exportResult(resultId: string): Promise<{
|
|
116
118
|
path: string;
|
package/dist/artifact-store.js
CHANGED
|
@@ -5,7 +5,7 @@ import os from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { ARTIFACT_TTL_MS, INLINE_RESULT_MAX_BYTES, RESULT_CHUNK_MAX_BYTES } from "./constants.js";
|
|
8
|
-
import { CursorCodec } from "./cursor.js";
|
|
8
|
+
import { CursorCodec, CursorCodecError } from "./cursor.js";
|
|
9
9
|
import { OmniBridgeError } from "./errors.js";
|
|
10
10
|
import { extractOutline } from "./outline.js";
|
|
11
11
|
import { GROUNDING_SCHEMA_VERSION, RESULT_BUNDLE_PROTOCOL_VERSION } from "./protocol.js";
|
|
@@ -28,11 +28,11 @@ function artifactError(code, message, retryable = false) {
|
|
|
28
28
|
return new OmniBridgeError({
|
|
29
29
|
code,
|
|
30
30
|
message,
|
|
31
|
-
operationCreated:
|
|
32
|
-
fileUploaded:
|
|
33
|
-
parserStarted:
|
|
34
|
-
billed:
|
|
35
|
-
contentReleased:
|
|
31
|
+
operationCreated: false,
|
|
32
|
+
fileUploaded: false,
|
|
33
|
+
parserStarted: false,
|
|
34
|
+
billed: false,
|
|
35
|
+
contentReleased: false,
|
|
36
36
|
retryable,
|
|
37
37
|
});
|
|
38
38
|
}
|
|
@@ -48,6 +48,17 @@ function cacheError(code, message) {
|
|
|
48
48
|
retryable: false,
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
+
function decodeCursor(action) {
|
|
52
|
+
try {
|
|
53
|
+
return action();
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error instanceof CursorCodecError) {
|
|
57
|
+
throw artifactError(error.code, error.message);
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
51
62
|
function errno(error, code) {
|
|
52
63
|
return error instanceof Error && error.code === code;
|
|
53
64
|
}
|
|
@@ -475,6 +486,27 @@ export class ArtifactStore {
|
|
|
475
486
|
this.#requireOpen();
|
|
476
487
|
return new LocalResultRetention(this);
|
|
477
488
|
}
|
|
489
|
+
async preflightWrite() {
|
|
490
|
+
this.#requireOpen();
|
|
491
|
+
const probePath = path.join(this.#resultsDirectory, `.tmp-preflight-${randomBytes(18).toString("base64url")}`);
|
|
492
|
+
let handle;
|
|
493
|
+
try {
|
|
494
|
+
handle = await open(probePath, "wx", 0o600);
|
|
495
|
+
await handle.writeFile("omni-artifact-preflight-v1\n", "utf8");
|
|
496
|
+
await handle.sync();
|
|
497
|
+
await handle.close();
|
|
498
|
+
handle = undefined;
|
|
499
|
+
await removeIfPresent(probePath);
|
|
500
|
+
await syncDirectory(this.#resultsDirectory);
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
await handle?.close().catch(() => undefined);
|
|
504
|
+
await removeIfPresent(probePath).catch(() => false);
|
|
505
|
+
if (error instanceof OmniBridgeError)
|
|
506
|
+
throw error;
|
|
507
|
+
throw artifactError("ARTIFACT_PREFLIGHT_FAILED", "The local result cache is not writable.", true);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
478
510
|
async read(resultId, cursor, maxBytes = RESULT_CHUNK_MAX_BYTES) {
|
|
479
511
|
this.#requireOpen();
|
|
480
512
|
let metadata;
|
|
@@ -496,7 +528,7 @@ export class ArtifactStore {
|
|
|
496
528
|
}
|
|
497
529
|
let offset = 0;
|
|
498
530
|
if (cursor !== undefined) {
|
|
499
|
-
const payload = this.#cursor.decode(cursor);
|
|
531
|
+
const payload = decodeCursor(() => this.#cursor.decode(cursor));
|
|
500
532
|
if (payload.resultId !== resultId || payload.expiresAt !== metadata.expiresAt) {
|
|
501
533
|
throw artifactError("RESULT_CURSOR_MISMATCH", "The result cursor does not match this artifact.");
|
|
502
534
|
}
|
|
@@ -559,7 +591,7 @@ export class ArtifactStore {
|
|
|
559
591
|
}
|
|
560
592
|
return extractOutline(fullText);
|
|
561
593
|
}
|
|
562
|
-
async
|
|
594
|
+
async mintReadCursor(resultId, byteOffset) {
|
|
563
595
|
this.#requireOpen();
|
|
564
596
|
const metadata = await this.#loadMetadata(resultId);
|
|
565
597
|
if (Date.parse(metadata.expiresAt) <= this.#now().getTime()) {
|
|
@@ -567,10 +599,13 @@ export class ArtifactStore {
|
|
|
567
599
|
throw artifactError("RESULT_EXPIRED", "The local result artifact has expired.");
|
|
568
600
|
}
|
|
569
601
|
if (!Number.isSafeInteger(byteOffset) || byteOffset < 0 || byteOffset > metadata.resultBytes) {
|
|
570
|
-
throw artifactError("INVALID_RESULT_CURSOR", "The
|
|
602
|
+
throw artifactError("INVALID_RESULT_CURSOR", "The result cursor offset is invalid.");
|
|
571
603
|
}
|
|
572
604
|
return this.#cursor.encode({ resultId, offset: byteOffset, expiresAt: metadata.expiresAt });
|
|
573
605
|
}
|
|
606
|
+
async mintOutlineCursor(resultId, byteOffset) {
|
|
607
|
+
return this.mintReadCursor(resultId, byteOffset);
|
|
608
|
+
}
|
|
574
609
|
// The full content text in one read, regardless of storage shape (a flat
|
|
575
610
|
// v1 artifact, or a v2 bundle's "content" part). save_result's only
|
|
576
611
|
// consumer: read_result's chunked/cursor contract stays the single way an
|
|
@@ -657,7 +692,7 @@ export class ArtifactStore {
|
|
|
657
692
|
// against the actual bytes before any chunk is returned.
|
|
658
693
|
async readBundlePart(resultId, cursor, maxBytes = RESULT_CHUNK_MAX_BYTES) {
|
|
659
694
|
this.#requireOpen();
|
|
660
|
-
const payload = this.#cursor.decodeBundle(cursor);
|
|
695
|
+
const payload = decodeCursor(() => this.#cursor.decodeBundle(cursor));
|
|
661
696
|
if (payload.resultId !== resultId) {
|
|
662
697
|
throw artifactError("RESULT_CURSOR_MISMATCH", "The result cursor does not match this artifact.");
|
|
663
698
|
}
|
package/dist/cli/agent-config.js
CHANGED
|
@@ -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: normal upgrade,
|
|
8
8
|
// uninstall, and rollback recognize exactly {previous, current}. Bump this to the
|
|
9
9
|
// version we just published when BRIDGE_RELEASE_VERSION advances.
|
|
10
|
-
const PREVIOUS_RELEASE_VERSION = "1.5.
|
|
10
|
+
const PREVIOUS_RELEASE_VERSION = "1.5.5";
|
|
11
11
|
const PREVIOUS_PACKAGE_SPEC = `@cueai/omni-reader-mcp@${PREVIOUS_RELEASE_VERSION}`;
|
|
12
12
|
// Preserve the one evidenced two-release migration exception: 1.5.1 setup wrote a
|
|
13
13
|
// bare `npx` launcher on Windows, so later setup/uninstall versions must still be able
|
|
@@ -641,7 +641,7 @@ export function expectedBridgeVersion(target, value, platform) {
|
|
|
641
641
|
}
|
|
642
642
|
function isLegacyBridgeEntry(target, value, platform) {
|
|
643
643
|
// Defer the entire invocation-shape decision to the single platform policy:
|
|
644
|
-
// exact pinned {1.5.
|
|
644
|
+
// exact pinned {1.5.5 previous, 1.6.0 current} plus the exact Windows 1.5.1
|
|
645
645
|
// bare-npx migration source. The historical platform-agnostic acceptance of a
|
|
646
646
|
// bare unpinned `npx -y @cueai/omni-reader-mcp` is removed: it widened the
|
|
647
647
|
// frozen trust set to a floating latest on every OS, which expectedBridgeVersion
|
package/dist/cli/arguments.d.ts
CHANGED
|
@@ -7,18 +7,22 @@ export interface SetupArguments {
|
|
|
7
7
|
readonly headless: boolean;
|
|
8
8
|
readonly json: boolean;
|
|
9
9
|
}
|
|
10
|
+
export interface DoctorArguments {
|
|
11
|
+
readonly json: boolean;
|
|
12
|
+
readonly configPath?: string;
|
|
13
|
+
readonly serverName?: string;
|
|
14
|
+
}
|
|
10
15
|
export type ParsedCommand = {
|
|
11
16
|
readonly command: "server";
|
|
12
17
|
} | {
|
|
13
18
|
readonly command: "help";
|
|
14
19
|
} | {
|
|
15
20
|
readonly command: "version";
|
|
16
|
-
} | {
|
|
21
|
+
} | ({
|
|
22
|
+
readonly command: "doctor";
|
|
23
|
+
} & DoctorArguments) | {
|
|
17
24
|
readonly command: "setup";
|
|
18
25
|
readonly arguments: SetupArguments;
|
|
19
|
-
} | {
|
|
20
|
-
readonly command: "doctor";
|
|
21
|
-
readonly json: boolean;
|
|
22
26
|
} | {
|
|
23
27
|
readonly command: "clean";
|
|
24
28
|
} | {
|
package/dist/cli/arguments.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { PathNormalizationError, normalizePlatformPath, } from "../path-normalization.js";
|
|
2
3
|
import { parseAgentTarget } from "./agent-config.js";
|
|
3
4
|
export class CliUsageError extends Error {
|
|
4
5
|
exitCode = 2;
|
|
@@ -10,6 +11,26 @@ export class CliUsageError extends Error {
|
|
|
10
11
|
function pathsFor(platform) {
|
|
11
12
|
return platform === "win32" ? path.win32 : path.posix;
|
|
12
13
|
}
|
|
14
|
+
function normalizeAbsoluteCliPath(raw, flag, platform) {
|
|
15
|
+
if (raw.length === 0 || raw.includes("\0")) {
|
|
16
|
+
throw new CliUsageError(`${flag} requires one valid absolute path.`);
|
|
17
|
+
}
|
|
18
|
+
let normalizedInput;
|
|
19
|
+
try {
|
|
20
|
+
normalizedInput = normalizePlatformPath(raw, platform);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error instanceof PathNormalizationError) {
|
|
24
|
+
throw new CliUsageError(error.message);
|
|
25
|
+
}
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
const paths = pathsFor(platform);
|
|
29
|
+
if (!paths.isAbsolute(normalizedInput)) {
|
|
30
|
+
throw new CliUsageError(`${flag} requires an absolute path.`);
|
|
31
|
+
}
|
|
32
|
+
return paths.normalize(normalizedInput);
|
|
33
|
+
}
|
|
13
34
|
function requireNoArguments(command, values) {
|
|
14
35
|
if (values.length > 0)
|
|
15
36
|
throw new CliUsageError(`${command} does not accept arguments.`);
|
|
@@ -35,7 +56,6 @@ function parseSetupArguments(values, platform) {
|
|
|
35
56
|
let headless = false;
|
|
36
57
|
let json = false;
|
|
37
58
|
const seen = new Set();
|
|
38
|
-
const paths = pathsFor(platform);
|
|
39
59
|
for (let index = 0; index < values.length; index += 1) {
|
|
40
60
|
const flag = values[index];
|
|
41
61
|
if (!["--client", "--allowed-root", "--add-root", "--yes", "--headless", "--non-interactive", "--json"].includes(flag)) {
|
|
@@ -71,10 +91,7 @@ function parseSetupArguments(values, platform) {
|
|
|
71
91
|
client = parsed;
|
|
72
92
|
continue;
|
|
73
93
|
}
|
|
74
|
-
|
|
75
|
-
throw new CliUsageError(`${flag} requires an absolute path.`);
|
|
76
|
-
}
|
|
77
|
-
const normalized = paths.normalize(raw);
|
|
94
|
+
const normalized = normalizeAbsoluteCliPath(raw, flag, platform);
|
|
78
95
|
if (flag === "--allowed-root")
|
|
79
96
|
allowedRoot = normalized;
|
|
80
97
|
if (flag === "--add-root")
|
|
@@ -92,6 +109,45 @@ function parseSetupArguments(values, platform) {
|
|
|
92
109
|
json,
|
|
93
110
|
};
|
|
94
111
|
}
|
|
112
|
+
function parseDoctorArguments(values, platform) {
|
|
113
|
+
let json = false;
|
|
114
|
+
let configPath;
|
|
115
|
+
let serverName;
|
|
116
|
+
const seen = new Set();
|
|
117
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
118
|
+
const flag = values[index];
|
|
119
|
+
if (!["--json", "--config-path", "--server-name"].includes(flag)) {
|
|
120
|
+
throw new CliUsageError(`Unknown doctor flag: ${flag}`);
|
|
121
|
+
}
|
|
122
|
+
if (seen.has(flag))
|
|
123
|
+
throw new CliUsageError(`Duplicate doctor flag: ${flag}`);
|
|
124
|
+
seen.add(flag);
|
|
125
|
+
if (flag === "--json") {
|
|
126
|
+
json = true;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const raw = values[index + 1];
|
|
130
|
+
if (raw === undefined || raw.startsWith("--")) {
|
|
131
|
+
throw new CliUsageError(`${flag} requires a value.`);
|
|
132
|
+
}
|
|
133
|
+
index += 1;
|
|
134
|
+
if (flag === "--config-path") {
|
|
135
|
+
configPath = normalizeAbsoluteCliPath(raw, flag, platform);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (raw.length === 0 || raw.includes("\0")) {
|
|
139
|
+
throw new CliUsageError("--server-name requires one valid entry name.");
|
|
140
|
+
}
|
|
141
|
+
serverName = raw;
|
|
142
|
+
}
|
|
143
|
+
if ((configPath === undefined) !== (serverName === undefined)) {
|
|
144
|
+
throw new CliUsageError("--config-path and --server-name must be provided together.");
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
json,
|
|
148
|
+
...(configPath === undefined ? {} : { configPath, serverName: serverName }),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
95
151
|
export function parseCliArguments(argv, platform = process.platform) {
|
|
96
152
|
if (argv.length === 0)
|
|
97
153
|
return { command: "server" };
|
|
@@ -108,8 +164,7 @@ export function parseCliArguments(argv, platform = process.platform) {
|
|
|
108
164
|
return { command: "setup", arguments: parseSetupArguments(values, platform) };
|
|
109
165
|
}
|
|
110
166
|
if (command === "doctor") {
|
|
111
|
-
|
|
112
|
-
return { command: "doctor", json: flags.has("--json") };
|
|
167
|
+
return { command: "doctor", ...parseDoctorArguments(values, platform) };
|
|
113
168
|
}
|
|
114
169
|
if (command === "clean") {
|
|
115
170
|
requireNoArguments("clean", values);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type BigIntStats } from "node:fs";
|
|
2
|
+
export interface ConfigInspectionHandle {
|
|
3
|
+
stat(options: {
|
|
4
|
+
bigint: true;
|
|
5
|
+
}): Promise<BigIntStats>;
|
|
6
|
+
read(buffer: Buffer, offset: number, length: number, position: number): Promise<{
|
|
7
|
+
bytesRead: number;
|
|
8
|
+
buffer: Buffer;
|
|
9
|
+
}>;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export interface ConfigInspectionFileSystem {
|
|
13
|
+
lstat(candidate: string): Promise<BigIntStats>;
|
|
14
|
+
open(candidate: string, flags: number): Promise<ConfigInspectionHandle>;
|
|
15
|
+
}
|
|
16
|
+
export declare class ConfigInspectionError extends Error {
|
|
17
|
+
readonly code: string;
|
|
18
|
+
readonly constraints?: {
|
|
19
|
+
readonly max_bytes: number;
|
|
20
|
+
};
|
|
21
|
+
constructor(code: string, message: string, constraints?: {
|
|
22
|
+
readonly max_bytes: number;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export type ConfigEntryStatus = "present" | "missing" | "invalid";
|
|
26
|
+
export type ConfigTransportShape = "local_command" | "remote_url" | "unknown";
|
|
27
|
+
export type ConfiguredApiKeyStatus = "literal_present" | "reference_present" | "absent" | "unknown";
|
|
28
|
+
export type AllowedRootSafety = "safe" | "unsafe" | "unknown";
|
|
29
|
+
export type CommandCategory = "package_runner" | "shell" | "runtime" | "executable";
|
|
30
|
+
export interface ExplicitConfigInspection {
|
|
31
|
+
readonly inspection_scope: "explicit_config";
|
|
32
|
+
readonly entry: {
|
|
33
|
+
readonly status: ConfigEntryStatus;
|
|
34
|
+
};
|
|
35
|
+
readonly transport: {
|
|
36
|
+
readonly shape: ConfigTransportShape;
|
|
37
|
+
readonly command?: {
|
|
38
|
+
readonly basename: string;
|
|
39
|
+
readonly category: CommandCategory;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
readonly package_version?: string;
|
|
43
|
+
readonly api_key: {
|
|
44
|
+
readonly status: ConfiguredApiKeyStatus;
|
|
45
|
+
};
|
|
46
|
+
readonly allowed_roots: {
|
|
47
|
+
readonly count: number;
|
|
48
|
+
readonly safety: Exclude<AllowedRootSafety, "unknown">;
|
|
49
|
+
} | {
|
|
50
|
+
readonly safety: "unknown";
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export interface InspectExplicitConfigOptions {
|
|
54
|
+
readonly configPath: string;
|
|
55
|
+
readonly serverName: string;
|
|
56
|
+
readonly platform?: NodeJS.Platform;
|
|
57
|
+
readonly fileSystem?: ConfigInspectionFileSystem;
|
|
58
|
+
}
|
|
59
|
+
export declare function inspectExplicitConfig(options: InspectExplicitConfigOptions): Promise<ExplicitConfigInspection>;
|