@cueai/omni-reader-mcp 1.1.3 → 1.2.1
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 +154 -85
- package/dist/artifact-store.d.ts +58 -0
- package/dist/artifact-store.js +449 -5
- package/dist/capabilities.d.ts +92 -0
- package/dist/capabilities.js +123 -0
- package/dist/constants.d.ts +34 -1
- package/dist/constants.js +39 -1
- package/dist/cube-client.d.ts +3 -0
- package/dist/cube-client.js +141 -9
- package/dist/cursor.d.ts +12 -0
- package/dist/cursor.js +89 -9
- package/dist/onboarding-policy.d.ts +5 -2
- package/dist/onboarding-policy.js +8 -3
- package/dist/operation-journal.d.ts +19 -5
- package/dist/operation-journal.js +435 -73
- package/dist/operation-manager.d.ts +7 -2
- package/dist/operation-manager.js +215 -16
- package/dist/protocol.d.ts +16 -3
- package/dist/protocol.js +25 -1
- package/dist/remote-client.d.ts +4 -2
- package/dist/remote-client.js +156 -13
- package/dist/result-bundle.d.ts +21 -0
- package/dist/result-bundle.js +320 -0
- package/dist/result-contract.d.ts +398 -4
- package/dist/result-contract.js +169 -11
- package/dist/server.d.ts +17 -0
- package/dist/server.js +45 -1
- package/dist/tools.d.ts +7 -3
- package/dist/tools.js +77 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,87 +1,127 @@
|
|
|
1
1
|
# @cueai/omni-reader-mcp
|
|
2
2
|
|
|
3
|
-
Omni Bridge
|
|
3
|
+
Omni Bridge is the unified local stdio facade for agents. The user provides only the
|
|
4
|
+
source; Bridge handles an HTTPS URL or a local file inside an authorized directory
|
|
5
|
+
through the same `parse(source)` call — no "local/remote", "upload/URL", or internal
|
|
6
|
+
service choice.
|
|
4
7
|
|
|
5
|
-
##
|
|
8
|
+
## User journey
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
Tell the agent the source and the follow-up task:
|
|
8
11
|
|
|
9
12
|
```text
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
Parse ./report.pdf with Omni
|
|
14
|
+
Parse https://example.com/report.pdf with Omni
|
|
15
|
+
Parse ./report.pdf with Omni and summarize the revenue changes
|
|
13
16
|
```
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
URLs never require installing Bridge. For local files, the agent should first explain the
|
|
19
|
+
minimal directory authorization and data handling and obtain confirmation, then install
|
|
20
|
+
a pinned Bridge version. After parsing completes, the agent continues the original task —
|
|
21
|
+
summarization, Q&A, field extraction — rather than stopping at "parse done".
|
|
16
22
|
|
|
17
|
-
##
|
|
23
|
+
## Requirements
|
|
18
24
|
|
|
19
|
-
- Node.js 20.12
|
|
20
|
-
- Cue API Key
|
|
21
|
-
-
|
|
22
|
-
-
|
|
25
|
+
- Node.js 20.12 or newer.
|
|
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.
|
|
30
|
+
- Configure `CUE_API_KEY` in the agent's secret store or local environment.
|
|
31
|
+
**Never paste the API Key into a conversation.**
|
|
23
32
|
|
|
24
|
-
Bridge
|
|
33
|
+
Bridge reads only these product configuration variables at runtime:
|
|
25
34
|
|
|
26
35
|
- `CUE_API_KEY`
|
|
27
36
|
- `OMNI_ALLOWED_ROOTS`
|
|
28
37
|
|
|
29
|
-
`OMNI_ALLOWED_ROOTS`
|
|
38
|
+
`OMNI_ALLOWED_ROOTS` is only for additional absolute directories the user explicitly
|
|
39
|
+
authorizes; colon-separated on macOS/Linux, semicolon-separated on Windows. The current
|
|
40
|
+
agent workspace remains the default allowed scope.
|
|
30
41
|
|
|
31
|
-
##
|
|
42
|
+
## Free credits
|
|
32
43
|
|
|
33
|
-
|
|
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
|
+
|
|
58
|
+
## Installation and configuration
|
|
59
|
+
|
|
60
|
+
Always use an audited exact version, never an implicit `latest`:
|
|
34
61
|
|
|
35
62
|
```sh
|
|
36
|
-
npx -y @cueai/omni-reader-mcp@1.1
|
|
63
|
+
npx -y @cueai/omni-reader-mcp@1.2.1 setup
|
|
37
64
|
```
|
|
38
65
|
|
|
39
|
-
|
|
66
|
+
The interactive setup supports Hermes, Cursor, Claude Desktop, and generic stdio
|
|
67
|
+
configuration. Non-interactive installation uses the same argument parsing and write
|
|
68
|
+
logic:
|
|
40
69
|
|
|
41
70
|
```sh
|
|
42
|
-
npx -y @cueai/omni-reader-mcp@1.1
|
|
43
|
-
npx -y @cueai/omni-reader-mcp@1.1
|
|
44
|
-
npx -y @cueai/omni-reader-mcp@1.1
|
|
71
|
+
npx -y @cueai/omni-reader-mcp@1.2.1 setup --client hermes --allowed-root /absolute/minimum/root --yes --json
|
|
72
|
+
npx -y @cueai/omni-reader-mcp@1.2.1 setup --client cursor --add-root /absolute/minimum/root --yes --json
|
|
73
|
+
npx -y @cueai/omni-reader-mcp@1.2.1 setup --client claude-desktop --allowed-root /absolute/minimum/root --yes --json
|
|
45
74
|
```
|
|
46
75
|
|
|
47
|
-
|
|
76
|
+
When an agent or script runs under a pty (stdin is still a TTY), declare non-interactive
|
|
77
|
+
mode explicitly with `--headless` (alias `--non-interactive`): no `--yes` is required and
|
|
78
|
+
stdin is never read:
|
|
48
79
|
|
|
49
80
|
```sh
|
|
50
|
-
npx -y @cueai/omni-reader-mcp@1.1
|
|
81
|
+
npx -y @cueai/omni-reader-mcp@1.2.1 setup --client cursor --allowed-root /absolute/minimum/root --headless --json
|
|
51
82
|
```
|
|
52
83
|
|
|
53
|
-
##
|
|
84
|
+
## Cache and journal isolation
|
|
54
85
|
|
|
55
|
-
|
|
86
|
+
The local parse cache and operation journal default to the user-level cache directory
|
|
87
|
+
(macOS `~/Library/Caches/cue/omni-reader-mcp`, Windows
|
|
88
|
+
`%LOCALAPPDATA%\Cue\omni-reader-mcp\Cache`, Linux `~/.cache/cue/omni-reader-mcp`). If the
|
|
89
|
+
agent's file watcher (e.g. WorkBuddy) locks files under that directory and writes fail,
|
|
90
|
+
set `OMNI_CACHE_DIR` to isolate cache and journal together into a directory that is not
|
|
91
|
+
watched:
|
|
56
92
|
|
|
57
93
|
```sh
|
|
58
94
|
export OMNI_CACHE_DIR=/var/cache/omni-reader
|
|
59
95
|
```
|
|
60
96
|
|
|
61
|
-
|
|
97
|
+
An explicit `OMNI_CACHE_DIR` is never rewritten to a temporary directory by the
|
|
98
|
+
project-overlap check; mutual containment with a project directory remains forbidden.
|
|
62
99
|
|
|
63
|
-
setup
|
|
100
|
+
`setup` will:
|
|
64
101
|
|
|
65
|
-
1.
|
|
66
|
-
2.
|
|
67
|
-
3.
|
|
68
|
-
4.
|
|
69
|
-
5.
|
|
70
|
-
6.
|
|
102
|
+
1. show the user-scope configuration target and changes;
|
|
103
|
+
2. write a single, exact-version `omni-reader` facade;
|
|
104
|
+
3. use a private lock, trusted backup, and atomic replacement;
|
|
105
|
+
4. verify the Omni secure parse environment;
|
|
106
|
+
5. restore the previous configuration when verification fails;
|
|
107
|
+
6. prompt the corresponding agent to reload or restart.
|
|
71
108
|
|
|
72
|
-
|
|
109
|
+
Configuration never writes the API Key in plaintext. Hermes uses native separate
|
|
110
|
+
`command`, `args`, and safe environment variable references.
|
|
73
111
|
|
|
74
|
-
## Agent
|
|
112
|
+
## Agent calling rules
|
|
75
113
|
|
|
76
|
-
|
|
114
|
+
Agents pass the user-provided source string directly to `parse(source)`:
|
|
77
115
|
|
|
78
|
-
-
|
|
79
|
-
- Do not read, attach, base64-encode, or insert the file content into the conversation
|
|
80
|
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
116
|
+
- Only HTTP(S) is treated as a URL; a plain string is treated as a local path.
|
|
117
|
+
- Do not read, attach, base64-encode, or insert the file content into the conversation
|
|
118
|
+
before calling Omni.
|
|
119
|
+
- Do not use `file://`, localhost, or public temporary upload sites as a fallback.
|
|
120
|
+
- Confirm before installing Bridge or widening the allowed roots.
|
|
121
|
+
- Prefer the existing `operation_id` for status recovery; never re-upload or duplicate a
|
|
122
|
+
parse for a timeout.
|
|
83
123
|
|
|
84
|
-
|
|
124
|
+
The public tools are fixed:
|
|
85
125
|
|
|
86
126
|
- `parse`
|
|
87
127
|
- `get_parse_status`
|
|
@@ -89,73 +129,102 @@ Agent 将用户提供的来源字符串直接传给 `parse(source)`:
|
|
|
89
129
|
- `read_result`
|
|
90
130
|
- `discard_result`
|
|
91
131
|
|
|
92
|
-
|
|
132
|
+
Every tool returns `structuredContent` with a strict `outputSchema`, plus an equivalent
|
|
133
|
+
fallback for clients that only read legacy MCP `content[].text`:
|
|
93
134
|
|
|
94
|
-
- `parse`
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
|
|
135
|
+
- when `parse` completes and the result is inlineable, text returns the full Markdown
|
|
136
|
+
directly;
|
|
137
|
+
- for processing, artifact, cleanup, cancel, expired, and failure states, text returns
|
|
138
|
+
compact JSON equivalent to the `structuredContent` fields;
|
|
139
|
+
- the `read_result` text JSON contains the current `result.text` and an optional
|
|
140
|
+
`next_cursor`; clients must exhaust all cursors before concatenating the body;
|
|
141
|
+
- the `discard_result` text JSON explicitly returns `discarded`; never claim deletion on
|
|
142
|
+
call success alone.
|
|
98
143
|
|
|
99
|
-
|
|
144
|
+
This gives clients that do not render `structuredContent` the operation/result handles
|
|
145
|
+
and the full body without changing the contract for structured-capable clients.
|
|
100
146
|
|
|
101
|
-
##
|
|
147
|
+
## Foreground, background, and progress
|
|
102
148
|
|
|
103
|
-
|
|
149
|
+
The foreground budget of every parse is fixed at **15 seconds**:
|
|
104
150
|
|
|
105
|
-
- 15
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
151
|
+
- completes within 15 seconds: the full result is returned directly;
|
|
152
|
+
- exceeds 15 seconds: a recoverable processing operation is returned;
|
|
153
|
+
- when the client explicitly requests it and supports MCP Tasks, Bridge uses a task;
|
|
154
|
+
- other clients use bounded polling of `get_parse_status`; proactive notifications that
|
|
155
|
+
the client does not support are not promised.
|
|
109
156
|
|
|
110
|
-
processing/task
|
|
157
|
+
processing/task status contains only the operation, stage, percentage, and an optional
|
|
158
|
+
authoritative unit progress. Available units are only `page`, `sheet`, `slide`, `frame`,
|
|
159
|
+
`segment`. Status never contains the result body, previews, Markdown, page images, crop
|
|
160
|
+
images, or accumulated partial output.
|
|
111
161
|
|
|
112
|
-
##
|
|
162
|
+
## Data handling and retention
|
|
113
163
|
|
|
114
|
-
URL
|
|
164
|
+
Both URL and local files use mandatory no-store semantics and enter the
|
|
165
|
+
**Omni secure parse environment**:
|
|
115
166
|
|
|
116
|
-
-
|
|
117
|
-
|
|
118
|
-
-
|
|
119
|
-
|
|
120
|
-
-
|
|
121
|
-
|
|
122
|
-
- `
|
|
167
|
+
- the original local file on the user's machine stays unchanged; URL sources are never
|
|
168
|
+
modified or deleted;
|
|
169
|
+
- processing copies and temporary data created by Omni are actively cleaned after
|
|
170
|
+
completion;
|
|
171
|
+
- encrypted delivery data is deleted after ACK; without a confirmed ACK, at the latest
|
|
172
|
+
when the **10-minute** delivery window expires;
|
|
173
|
+
- `completed` is returned only after the remote ACK and cleanup are confirmed;
|
|
174
|
+
- `cleanup_pending` is returned when the local result is retained but remote cleanup is
|
|
175
|
+
still uncertain — never claim deletion prematurely;
|
|
176
|
+
- results over 64 KiB may be kept in the user's private Bridge cache for up to
|
|
177
|
+
**24 hours**; this is a different class of data from the remote 10-minute delivery
|
|
178
|
+
window;
|
|
179
|
+
- `read_result` reads local results in chunks; `discard_result` deletes a specific result
|
|
180
|
+
immediately; `clean` removes Bridge-created local artifacts and expired records.
|
|
123
181
|
|
|
124
|
-
|
|
182
|
+
Status queries, result reads, and discards never create a new parse or usage event.
|
|
183
|
+
`cancel_parse` reports only the real settlement state confirmed by the server and never
|
|
184
|
+
causes a second billing by itself.
|
|
125
185
|
|
|
126
|
-
##
|
|
186
|
+
## Constraint errors
|
|
127
187
|
|
|
128
|
-
Bridge
|
|
188
|
+
Bridge validates the local source before grant, upload, and billing:
|
|
129
189
|
|
|
130
|
-
- `SOURCE_TOO_LARGE
|
|
131
|
-
|
|
190
|
+
- `SOURCE_TOO_LARGE`: exceeds the current 256 MiB cap, with machine-readable
|
|
191
|
+
`constraints.max_bytes`.
|
|
192
|
+
- `UNSUPPORTED_MEDIA_TYPE`: unsupported media type, with the current authoritative
|
|
193
|
+
`supported_extensions`.
|
|
132
194
|
|
|
133
|
-
|
|
195
|
+
Agents should not auto-split, transcode, or upload to public sites; ask the user for a
|
|
196
|
+
new source that satisfies the constraints.
|
|
134
197
|
|
|
135
|
-
##
|
|
198
|
+
## Commands
|
|
136
199
|
|
|
137
200
|
```sh
|
|
138
|
-
npx -y @cueai/omni-reader-mcp@1.1
|
|
139
|
-
npx -y @cueai/omni-reader-mcp@1.1
|
|
140
|
-
npx -y @cueai/omni-reader-mcp@1.1
|
|
141
|
-
npx -y @cueai/omni-reader-mcp@1.1
|
|
201
|
+
npx -y @cueai/omni-reader-mcp@1.2.1 doctor
|
|
202
|
+
npx -y @cueai/omni-reader-mcp@1.2.1 doctor --json
|
|
203
|
+
npx -y @cueai/omni-reader-mcp@1.2.1 clean
|
|
204
|
+
npx -y @cueai/omni-reader-mcp@1.2.1 uninstall --yes --json
|
|
142
205
|
```
|
|
143
206
|
|
|
144
|
-
|
|
207
|
+
Running the pinned version without a command starts the stdio MCP server:
|
|
145
208
|
|
|
146
209
|
```sh
|
|
147
|
-
npx -y @cueai/omni-reader-mcp@1.1
|
|
210
|
+
npx -y @cueai/omni-reader-mcp@1.2.1
|
|
148
211
|
```
|
|
149
212
|
|
|
150
|
-
`doctor --json`
|
|
213
|
+
`doctor --json` returns package/npm/client adapter, Key present/absent, allowed-root
|
|
214
|
+
safety status, endpoint compatibility, artifacts, cache mode, onboarding, and reload
|
|
215
|
+
status; it never prints the Key, private source paths, or content.
|
|
151
216
|
|
|
152
|
-
##
|
|
217
|
+
## Uninstall and rollback
|
|
153
218
|
|
|
154
|
-
`uninstall --yes --json`
|
|
219
|
+
`uninstall --yes --json` removes only the currently trusted 1.2.1 Bridge entry; when a
|
|
220
|
+
matching trusted backup exists, it restores the original URL-only `omni-reader` entry.
|
|
221
|
+
Uninstall never deletes user source files and never silently removes unexpired local
|
|
222
|
+
results.
|
|
155
223
|
|
|
156
|
-
|
|
224
|
+
To roll back from 1.1.1:
|
|
157
225
|
|
|
158
|
-
1.
|
|
159
|
-
2.
|
|
160
|
-
3.
|
|
161
|
-
|
|
226
|
+
1. stop recommending or installing that version;
|
|
227
|
+
2. run `uninstall --yes --json` to restore the trusted URL-only entry;
|
|
228
|
+
3. for already-uploaded operations, continue using status recovery so settlement and
|
|
229
|
+
cleanup complete;
|
|
230
|
+
4. keep local results, or have the user explicitly run `discard_result` / `clean`.
|
package/dist/artifact-store.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { FileHandle } from "node:fs/promises";
|
|
2
2
|
import type { ReleasedMetadata, ResultRetentionSink, ResultRetentionStart } from "./iiis-client.js";
|
|
3
|
+
import { GROUNDING_SCHEMA_VERSION, RESULT_BUNDLE_PROTOCOL_VERSION } from "./protocol.js";
|
|
4
|
+
import { type VerifiedBundle } from "./result-bundle.js";
|
|
3
5
|
export interface ArtifactStoreOptions {
|
|
4
6
|
readonly rootDirectory?: string;
|
|
5
7
|
readonly projectDirectory?: string;
|
|
@@ -41,6 +43,59 @@ export interface ArtifactReadResult {
|
|
|
41
43
|
readonly text: string;
|
|
42
44
|
readonly nextCursor?: string;
|
|
43
45
|
}
|
|
46
|
+
export type BundlePartName = "content" | "grounding";
|
|
47
|
+
export type BundleContentStorage = {
|
|
48
|
+
readonly kind: "inline";
|
|
49
|
+
readonly text: string;
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: "artifact";
|
|
52
|
+
readonly nextCursor: string;
|
|
53
|
+
};
|
|
54
|
+
export type BundleGroundingStorage = {
|
|
55
|
+
readonly kind: "inline";
|
|
56
|
+
readonly value: unknown;
|
|
57
|
+
} | {
|
|
58
|
+
readonly kind: "artifact";
|
|
59
|
+
readonly nextCursor: string;
|
|
60
|
+
};
|
|
61
|
+
export interface BundleLocalResult {
|
|
62
|
+
readonly kind: "bundle";
|
|
63
|
+
readonly operationId: string;
|
|
64
|
+
readonly resultId: string;
|
|
65
|
+
readonly detail: "grounded" | "layout";
|
|
66
|
+
readonly bundleBytes: number;
|
|
67
|
+
readonly bundleDigest: string;
|
|
68
|
+
readonly expiresAt: string;
|
|
69
|
+
readonly bundleProtocolVersion: typeof RESULT_BUNDLE_PROTOCOL_VERSION;
|
|
70
|
+
readonly groundingSchemaVersion: typeof GROUNDING_SCHEMA_VERSION;
|
|
71
|
+
readonly parts: {
|
|
72
|
+
readonly content: {
|
|
73
|
+
readonly part: "content";
|
|
74
|
+
readonly mediaType: "text/markdown; charset=utf-8";
|
|
75
|
+
readonly resultBytes: number;
|
|
76
|
+
readonly digest: string;
|
|
77
|
+
readonly storage: BundleContentStorage;
|
|
78
|
+
};
|
|
79
|
+
readonly grounding: {
|
|
80
|
+
readonly part: "grounding";
|
|
81
|
+
readonly mediaType: "application/vnd.cue.omni-grounding+json; version=1";
|
|
82
|
+
readonly resultBytes: number;
|
|
83
|
+
readonly digest: string;
|
|
84
|
+
readonly storage: BundleGroundingStorage;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export interface BundlePartReadChunk {
|
|
89
|
+
readonly resultId: string;
|
|
90
|
+
readonly part: BundlePartName;
|
|
91
|
+
readonly mediaType: "text/markdown; charset=utf-8" | "application/vnd.cue.omni-grounding+json; version=1";
|
|
92
|
+
readonly resultBytes: number;
|
|
93
|
+
readonly offset: number;
|
|
94
|
+
readonly decodedBytes: number;
|
|
95
|
+
readonly text: string;
|
|
96
|
+
readonly nextCursor?: string;
|
|
97
|
+
readonly expiresAt: string;
|
|
98
|
+
}
|
|
44
99
|
interface ArtifactFinalizeInput extends ReleasedMetadata {
|
|
45
100
|
readonly resultId: string;
|
|
46
101
|
}
|
|
@@ -54,10 +109,12 @@ export declare class ArtifactStore {
|
|
|
54
109
|
get rootDirectory(): string;
|
|
55
110
|
createRetention(): LocalResultRetention;
|
|
56
111
|
read(resultId: string, cursor?: string, maxBytes?: number): Promise<ArtifactReadResult>;
|
|
112
|
+
readBundlePart(resultId: string, cursor: string, maxBytes?: number): Promise<BundlePartReadChunk>;
|
|
57
113
|
discard(resultId: string): Promise<boolean>;
|
|
58
114
|
cleanupExpired(): Promise<number>;
|
|
59
115
|
close(): Promise<void>;
|
|
60
116
|
_finalizeArtifact(temporaryPath: string, metadata: ArtifactFinalizeInput): Promise<ArtifactResult>;
|
|
117
|
+
_finalizeBundle(resultId: string, verified: VerifiedBundle, metadata: ReleasedMetadata): Promise<BundleLocalResult>;
|
|
61
118
|
_newTemporaryArtifact(): Promise<{
|
|
62
119
|
resultId: string;
|
|
63
120
|
temporaryPath: string;
|
|
@@ -72,6 +129,7 @@ export declare class LocalResultRetention implements ResultRetentionSink {
|
|
|
72
129
|
write(chunk: Uint8Array): Promise<void>;
|
|
73
130
|
complete(metadata: ReleasedMetadata): Promise<void>;
|
|
74
131
|
result(): LocalResult;
|
|
132
|
+
bundleResult(): BundleLocalResult;
|
|
75
133
|
abort(): Promise<void>;
|
|
76
134
|
}
|
|
77
135
|
export {};
|