@cueai/omni-reader-mcp 1.0.1 → 1.1.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 +115 -26
- package/dist/artifact-store.d.ts +11 -0
- package/dist/artifact-store.js +94 -48
- package/dist/cli/agent-config.d.ts +29 -4
- package/dist/cli/agent-config.js +910 -107
- package/dist/cli/arguments.d.ts +32 -0
- package/dist/cli/arguments.js +120 -0
- package/dist/cli/doctor.d.ts +42 -1
- package/dist/cli/doctor.js +110 -37
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.js +103 -18
- package/dist/cli/uninstall.d.ts +6 -0
- package/dist/cli/uninstall.js +37 -0
- package/dist/constants.d.ts +8 -1
- package/dist/constants.js +8 -1
- package/dist/cube-client.d.ts +5 -3
- package/dist/cube-client.js +17 -12
- package/dist/cursor.js +2 -0
- package/dist/errors.d.ts +32 -1
- package/dist/errors.js +26 -1
- package/dist/iiis-client.d.ts +19 -5
- package/dist/iiis-client.js +206 -49
- package/dist/index.d.ts +3 -0
- package/dist/index.js +93 -32
- package/dist/multipart-body.js +2 -0
- package/dist/onboarding-policy.d.ts +10 -0
- package/dist/onboarding-policy.js +58 -0
- package/dist/operation-journal.d.ts +50 -1
- package/dist/operation-journal.js +473 -114
- package/dist/operation-manager.d.ts +75 -0
- package/dist/operation-manager.js +1311 -0
- package/dist/path-security.d.ts +1 -0
- package/dist/path-security.js +26 -6
- package/dist/progress.d.ts +6 -1
- package/dist/protocol.d.ts +26 -13
- package/dist/protocol.js +34 -10
- package/dist/remote-client.d.ts +17 -0
- package/dist/remote-client.js +233 -0
- package/dist/result-contract.d.ts +199 -0
- package/dist/result-contract.js +235 -0
- package/dist/server.js +21 -4
- package/dist/source.d.ts +8 -0
- package/dist/source.js +37 -0
- package/dist/task-runtime.d.ts +13 -0
- package/dist/task-runtime.js +94 -0
- package/dist/tools.d.ts +19 -1
- package/dist/tools.js +317 -112
- package/package.json +3 -3
package/dist/iiis-client.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { GRANTED_STREAM_PROTOCOL_VERSION, RESULT_CHUNK_MAX_BYTES, } from "./constants.js";
|
|
2
|
+
import { DEFAULT_IIIS_GRANTED_BASE_URL, GRANTED_STREAM_PROTOCOL_VERSION, RESULT_CHUNK_MAX_BYTES, } from "./constants.js";
|
|
3
3
|
import { OmniBridgeError } from "./errors.js";
|
|
4
4
|
import { createMultipartBody } from "./multipart-body.js";
|
|
5
|
-
import { NOOP_PROGRESS } from "./progress.js";
|
|
5
|
+
import { NOOP_PROGRESS, } from "./progress.js";
|
|
6
6
|
class TransportFailure extends Error {
|
|
7
7
|
}
|
|
8
8
|
const ACTIVE_STATUSES = new Set(["CLAIMED", "UPLOADING", "PROCESSING", "SETTLING"]);
|
|
@@ -14,6 +14,15 @@ const TERMINAL_STATUSES = new Set([
|
|
|
14
14
|
"DELIVERY_EXPIRED",
|
|
15
15
|
"DELIVERED",
|
|
16
16
|
]);
|
|
17
|
+
const OPERATION_STATUSES = new Set([
|
|
18
|
+
"ISSUED",
|
|
19
|
+
"CLAIMED",
|
|
20
|
+
"UPLOADING",
|
|
21
|
+
"PROCESSING",
|
|
22
|
+
"SETTLING",
|
|
23
|
+
"RELEASED",
|
|
24
|
+
...TERMINAL_STATUSES,
|
|
25
|
+
]);
|
|
17
26
|
const RECOVERABLE_SETTLEMENT_ERRORS = new Set([
|
|
18
27
|
"SETTLEMENT_IN_PROGRESS",
|
|
19
28
|
"SETTLEMENT_RETRYABLE",
|
|
@@ -24,11 +33,24 @@ const RECOVERABLE_SETTLEMENT_ERRORS = new Set([
|
|
|
24
33
|
]);
|
|
25
34
|
const BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
26
35
|
const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
36
|
+
const ERROR_CODE_PATTERN = /^[A-Z][A-Z0-9_]{0,127}$/;
|
|
37
|
+
const RFC3339_PATTERN = /^(\d{4})-(\d{2})-(\d{2})[Tt](?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:[Zz]|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/u;
|
|
38
|
+
const PROGRESS_UNITS = new Set([
|
|
39
|
+
"page",
|
|
40
|
+
"sheet",
|
|
41
|
+
"slide",
|
|
42
|
+
"frame",
|
|
43
|
+
"segment",
|
|
44
|
+
]);
|
|
27
45
|
function bridgeError(code, message, facts = {}) {
|
|
28
46
|
return new OmniBridgeError({
|
|
29
47
|
code,
|
|
30
48
|
message,
|
|
49
|
+
operationCreated: facts.operationCreated ?? true,
|
|
31
50
|
fileUploaded: facts.fileUploaded ?? false,
|
|
51
|
+
parserStarted: (facts.parserStarted ?? false) ||
|
|
52
|
+
facts.billed === true ||
|
|
53
|
+
facts.contentReleased === true,
|
|
32
54
|
billed: facts.billed ?? false,
|
|
33
55
|
contentReleased: facts.contentReleased ?? false,
|
|
34
56
|
retryable: facts.retryable ?? false,
|
|
@@ -66,11 +88,17 @@ function throwIfCanceled(input) {
|
|
|
66
88
|
if (input.signal?.aborted)
|
|
67
89
|
throw canceledAfterUpload();
|
|
68
90
|
}
|
|
69
|
-
function
|
|
70
|
-
const
|
|
71
|
-
if (
|
|
72
|
-
throw bridgeError("INSECURE_IIIS_URL", "The IIIS
|
|
91
|
+
function secureIiisUrl(value) {
|
|
92
|
+
const url = new URL(value);
|
|
93
|
+
if (url.protocol !== "https:" || url.username !== "" || url.password !== "") {
|
|
94
|
+
throw bridgeError("INSECURE_IIIS_URL", "The IIIS endpoint must use credential-free HTTPS.");
|
|
73
95
|
}
|
|
96
|
+
return url;
|
|
97
|
+
}
|
|
98
|
+
function operationUrl(input, operationBaseUrl, suffix = "") {
|
|
99
|
+
const base = input.uploadUrl === undefined
|
|
100
|
+
? secureIiisUrl(operationBaseUrl)
|
|
101
|
+
: secureIiisUrl(input.uploadUrl);
|
|
74
102
|
return new URL(`operations/${encodeURIComponent(input.operationId)}${suffix}`, base).toString();
|
|
75
103
|
}
|
|
76
104
|
function operationHeaders(input) {
|
|
@@ -84,37 +112,108 @@ function safeInteger(value, minimum = 0) {
|
|
|
84
112
|
&& Number.isSafeInteger(value)
|
|
85
113
|
&& value >= minimum;
|
|
86
114
|
}
|
|
115
|
+
function isRfc3339(value) {
|
|
116
|
+
if (typeof value !== "string")
|
|
117
|
+
return false;
|
|
118
|
+
const match = RFC3339_PATTERN.exec(value);
|
|
119
|
+
if (match === null)
|
|
120
|
+
return false;
|
|
121
|
+
const year = Number(match[1]);
|
|
122
|
+
const month = Number(match[2]);
|
|
123
|
+
const day = Number(match[3]);
|
|
124
|
+
if (month < 1 || month > 12 || day < 1)
|
|
125
|
+
return false;
|
|
126
|
+
const monthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
127
|
+
const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
128
|
+
const days = month === 2 && leapYear ? 29 : monthDays[month - 1];
|
|
129
|
+
return day <= days;
|
|
130
|
+
}
|
|
87
131
|
function validateProtocol(event) {
|
|
88
132
|
if (event.protocol_version !== GRANTED_STREAM_PROTOCOL_VERSION) {
|
|
89
133
|
throw bridgeError("PROTOCOL_MISMATCH", "IIIS returned an unsupported protocol version.");
|
|
90
134
|
}
|
|
91
135
|
}
|
|
136
|
+
function stableErrorCode(value, fallback) {
|
|
137
|
+
return typeof value === "string" && ERROR_CODE_PATTERN.test(value)
|
|
138
|
+
? value
|
|
139
|
+
: fallback;
|
|
140
|
+
}
|
|
141
|
+
function stableErrorMessage(code) {
|
|
142
|
+
if (code === "SETTLEMENT_DENIED")
|
|
143
|
+
return "Billing did not authorize content release.";
|
|
144
|
+
if (code === "DELIVERY_EXPIRED")
|
|
145
|
+
return "The temporary delivery result has expired.";
|
|
146
|
+
if (code === "CLEANUP_PENDING")
|
|
147
|
+
return "Omni cleanup is still pending.";
|
|
148
|
+
if (code === "CANCELED")
|
|
149
|
+
return "The Omni operation was canceled.";
|
|
150
|
+
return "The Omni parsing service could not complete the operation.";
|
|
151
|
+
}
|
|
152
|
+
function stableProgressMessage(stage) {
|
|
153
|
+
if (stage === "parse" || stage === "parsing")
|
|
154
|
+
return "Parsing document";
|
|
155
|
+
if (stage === "settling" || stage === "settlement")
|
|
156
|
+
return "Finalizing result";
|
|
157
|
+
return "Processing document";
|
|
158
|
+
}
|
|
92
159
|
function progressValue(event) {
|
|
93
160
|
validateProtocol(event);
|
|
94
|
-
if (!safeInteger(event.done) ||
|
|
161
|
+
if (!safeInteger(event.done) ||
|
|
162
|
+
!safeInteger(event.total) ||
|
|
163
|
+
event.done > event.total) {
|
|
95
164
|
throw bridgeError("INVALID_PROGRESS_EVENT", "IIIS returned an invalid progress event.");
|
|
96
165
|
}
|
|
166
|
+
const unit = typeof event.unit === "string" &&
|
|
167
|
+
PROGRESS_UNITS.has(event.unit)
|
|
168
|
+
? event.unit
|
|
169
|
+
: undefined;
|
|
97
170
|
return {
|
|
98
171
|
done: event.done,
|
|
99
172
|
total: event.total,
|
|
100
|
-
message:
|
|
173
|
+
message: stableProgressMessage(event.stage),
|
|
174
|
+
...(unit === undefined || event.total <= 0
|
|
175
|
+
? {}
|
|
176
|
+
: {
|
|
177
|
+
detail: {
|
|
178
|
+
unit,
|
|
179
|
+
completed: Math.min(event.done, event.total),
|
|
180
|
+
total: event.total,
|
|
181
|
+
},
|
|
182
|
+
}),
|
|
101
183
|
};
|
|
102
184
|
}
|
|
103
185
|
function monotonicProgress(sink) {
|
|
104
186
|
let last = 0;
|
|
187
|
+
let lastDetail;
|
|
105
188
|
return {
|
|
106
|
-
async report(progress, total, message) {
|
|
189
|
+
async report(progress, total, message, detail) {
|
|
107
190
|
const next = Math.min(total, Math.max(last, progress));
|
|
108
191
|
last = next;
|
|
109
|
-
|
|
192
|
+
const nextDetail = detail === undefined
|
|
193
|
+
? undefined
|
|
194
|
+
: lastDetail?.unit === detail.unit
|
|
195
|
+
? {
|
|
196
|
+
...detail,
|
|
197
|
+
completed: Math.min(detail.total, Math.max(lastDetail.completed, detail.completed)),
|
|
198
|
+
}
|
|
199
|
+
: detail;
|
|
200
|
+
if (nextDetail !== undefined)
|
|
201
|
+
lastDetail = nextDetail;
|
|
202
|
+
await sink.report(next, total, message, nextDetail);
|
|
110
203
|
},
|
|
111
204
|
};
|
|
112
205
|
}
|
|
206
|
+
function parserProgress(done, total) {
|
|
207
|
+
return total > 0
|
|
208
|
+
? 40 + Math.floor(Math.min(done / total, 1) * 59)
|
|
209
|
+
: 40;
|
|
210
|
+
}
|
|
113
211
|
async function errorFromResponse(response, input) {
|
|
114
212
|
try {
|
|
115
213
|
const body = await response.json();
|
|
116
|
-
if (isRecord(body) && typeof body.code === "string"
|
|
117
|
-
|
|
214
|
+
if (isRecord(body) && typeof body.code === "string") {
|
|
215
|
+
const code = stableErrorCode(body.code, "IIIS_UNAVAILABLE");
|
|
216
|
+
return bridgeError(code, stableErrorMessage(code), {
|
|
118
217
|
fileUploaded: body.file_uploaded === true,
|
|
119
218
|
billed: body.billed === true,
|
|
120
219
|
contentReleased: body.content_released === true,
|
|
@@ -139,6 +238,33 @@ async function protocolJson(response, input, description) {
|
|
|
139
238
|
throw bridgeError("PROTOCOL_MISMATCH", `IIIS returned invalid ${description} JSON.`);
|
|
140
239
|
}
|
|
141
240
|
}
|
|
241
|
+
function operationStatusValue(value, operationId) {
|
|
242
|
+
if (!isRecord(value)
|
|
243
|
+
|| value.protocol_version !== GRANTED_STREAM_PROTOCOL_VERSION
|
|
244
|
+
|| value.operation_id !== operationId
|
|
245
|
+
|| typeof value.status !== "string"
|
|
246
|
+
|| !OPERATION_STATUSES.has(value.status)
|
|
247
|
+
|| typeof value.parser_started !== "boolean"
|
|
248
|
+
|| typeof value.file_uploaded !== "boolean"
|
|
249
|
+
|| typeof value.billed !== "boolean"
|
|
250
|
+
|| typeof value.content_released !== "boolean"
|
|
251
|
+
|| typeof value.retryable !== "boolean"
|
|
252
|
+
|| !(value.expires_at === null || isRfc3339(value.expires_at))) {
|
|
253
|
+
throw bridgeError("PROTOCOL_MISMATCH", "IIIS returned an invalid operation status.");
|
|
254
|
+
}
|
|
255
|
+
return value;
|
|
256
|
+
}
|
|
257
|
+
function operationSnapshot(status) {
|
|
258
|
+
return {
|
|
259
|
+
status: status.status,
|
|
260
|
+
parserStarted: status.parser_started,
|
|
261
|
+
fileUploaded: status.file_uploaded,
|
|
262
|
+
billed: status.billed,
|
|
263
|
+
contentReleased: status.content_released,
|
|
264
|
+
retryable: status.retryable,
|
|
265
|
+
expiresAt: status.expires_at,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
142
268
|
async function* sseEvents(response) {
|
|
143
269
|
if (response.body === null)
|
|
144
270
|
throw new TransportFailure("missing SSE body");
|
|
@@ -193,10 +319,17 @@ export class IiisClient {
|
|
|
193
319
|
#fetch;
|
|
194
320
|
#pollIntervalMs;
|
|
195
321
|
#maxPolls;
|
|
322
|
+
#operationBaseUrl;
|
|
196
323
|
constructor(options = {}) {
|
|
197
324
|
this.#fetch = options.fetchImpl ?? fetch;
|
|
198
325
|
this.#pollIntervalMs = Math.max(0, options.pollIntervalMs ?? 250);
|
|
199
326
|
this.#maxPolls = Math.max(1, options.maxPolls ?? 240);
|
|
327
|
+
const operationBase = secureIiisUrl(options.operationBaseUrl ?? DEFAULT_IIIS_GRANTED_BASE_URL);
|
|
328
|
+
operationBase.search = "";
|
|
329
|
+
operationBase.hash = "";
|
|
330
|
+
if (!operationBase.pathname.endsWith("/"))
|
|
331
|
+
operationBase.pathname += "/";
|
|
332
|
+
this.#operationBaseUrl = operationBase.toString();
|
|
200
333
|
}
|
|
201
334
|
async uploadAndWait(input) {
|
|
202
335
|
const progress = monotonicProgress(input.progress ?? NOOP_PROGRESS);
|
|
@@ -217,7 +350,35 @@ export class IiisClient {
|
|
|
217
350
|
return this.#recover(input, true, progress);
|
|
218
351
|
}
|
|
219
352
|
}
|
|
220
|
-
async
|
|
353
|
+
async recoverAndWait(input) {
|
|
354
|
+
return this.#recover(input, false, monotonicProgress(input.progress ?? NOOP_PROGRESS));
|
|
355
|
+
}
|
|
356
|
+
async inspectOperation(input) {
|
|
357
|
+
return operationSnapshot(await this.#status({ ...input, signal: undefined }));
|
|
358
|
+
}
|
|
359
|
+
async cancelOperation(input) {
|
|
360
|
+
throwIfCanceled(input);
|
|
361
|
+
let response;
|
|
362
|
+
try {
|
|
363
|
+
response = await this.#fetch(operationUrl(input, this.#operationBaseUrl, "/cancel"), {
|
|
364
|
+
method: "POST",
|
|
365
|
+
headers: operationHeaders(input),
|
|
366
|
+
signal: input.signal,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
throwIfCanceled(input);
|
|
371
|
+
throw bridgeError("IIIS_UNAVAILABLE", "IIIS operation cancellation is unavailable.", { retryable: true });
|
|
372
|
+
}
|
|
373
|
+
if (!response.ok)
|
|
374
|
+
throw await errorFromResponse(response, input);
|
|
375
|
+
if (response.status === 202 || response.status === 204) {
|
|
376
|
+
return this.inspectOperation(input);
|
|
377
|
+
}
|
|
378
|
+
const value = await protocolJson(response, input, "operation cancellation");
|
|
379
|
+
return operationSnapshot(operationStatusValue(value, input.operationId));
|
|
380
|
+
}
|
|
381
|
+
async downloadResult(input, progress = NOOP_PROGRESS) {
|
|
221
382
|
throwIfCanceled(input);
|
|
222
383
|
const confirmedFacts = recoveryFacts({
|
|
223
384
|
fileUploaded: true,
|
|
@@ -244,7 +405,7 @@ export class IiisClient {
|
|
|
244
405
|
}
|
|
245
406
|
let response;
|
|
246
407
|
try {
|
|
247
|
-
response = await this.#fetch(operationUrl(input, "/result"), {
|
|
408
|
+
response = await this.#fetch(operationUrl(input, this.#operationBaseUrl, "/result"), {
|
|
248
409
|
headers: operationHeaders(input),
|
|
249
410
|
signal: input.signal,
|
|
250
411
|
});
|
|
@@ -316,6 +477,7 @@ export class IiisClient {
|
|
|
316
477
|
};
|
|
317
478
|
await input.retention.complete(metadata);
|
|
318
479
|
throwIfCanceled(input);
|
|
480
|
+
await progress.report(100, 100, "Completed");
|
|
319
481
|
completed = true;
|
|
320
482
|
return metadata;
|
|
321
483
|
}
|
|
@@ -341,7 +503,7 @@ export class IiisClient {
|
|
|
341
503
|
throwIfCanceled(input);
|
|
342
504
|
let response;
|
|
343
505
|
try {
|
|
344
|
-
response = await this.#fetch(operationUrl(input, "/ack"), {
|
|
506
|
+
response = await this.#fetch(operationUrl(input, this.#operationBaseUrl, "/ack"), {
|
|
345
507
|
method: "POST",
|
|
346
508
|
headers: operationHeaders(input),
|
|
347
509
|
signal: input.signal,
|
|
@@ -360,21 +522,27 @@ export class IiisClient {
|
|
|
360
522
|
throw await errorFromResponse(response, input);
|
|
361
523
|
}
|
|
362
524
|
async #uploadOnce(input, progress) {
|
|
363
|
-
const
|
|
525
|
+
const openedFile = input.openedFile;
|
|
526
|
+
const parseGrant = input.parseGrant;
|
|
527
|
+
const uploadUrl = input.uploadUrl;
|
|
528
|
+
if (openedFile === undefined || parseGrant === undefined || uploadUrl === undefined) {
|
|
529
|
+
throw bridgeError("UPLOAD_CONTEXT_UNAVAILABLE", "The local upload context is unavailable for this recovery operation.", { retryable: true });
|
|
530
|
+
}
|
|
531
|
+
const body = createMultipartBody(openedFile, {
|
|
364
532
|
signal: input.signal,
|
|
365
533
|
onProgress: async (bytes) => {
|
|
366
|
-
const scaled =
|
|
534
|
+
const scaled = openedFile.size === 0
|
|
367
535
|
? 40
|
|
368
|
-
: Math.floor(bytes * 40 /
|
|
369
|
-
await progress.report(scaled, 100, `Uploading ${bytes}/${
|
|
536
|
+
: Math.floor(bytes * 40 / openedFile.size);
|
|
537
|
+
await progress.report(scaled, 100, `Uploading ${bytes}/${openedFile.size} bytes`);
|
|
370
538
|
},
|
|
371
539
|
});
|
|
372
540
|
let response;
|
|
373
541
|
try {
|
|
374
|
-
response = await this.#fetch(
|
|
542
|
+
response = await this.#fetch(uploadUrl, {
|
|
375
543
|
method: "POST",
|
|
376
544
|
headers: {
|
|
377
|
-
authorization: `Bearer ${
|
|
545
|
+
authorization: `Bearer ${parseGrant}`,
|
|
378
546
|
"content-type": body.contentType,
|
|
379
547
|
"content-length": String(body.contentLength),
|
|
380
548
|
},
|
|
@@ -415,14 +583,12 @@ export class IiisClient {
|
|
|
415
583
|
}
|
|
416
584
|
if (event.type === "progress") {
|
|
417
585
|
const parsed = progressValue(event);
|
|
418
|
-
|
|
419
|
-
? 40 + Math.floor(Math.min(parsed.done / parsed.total, 1) * 60)
|
|
420
|
-
: 40;
|
|
421
|
-
await progress.report(scaled, 100, parsed.message);
|
|
586
|
+
await progress.report(parserProgress(parsed.done, parsed.total), 100, parsed.message, parsed.detail);
|
|
422
587
|
continue;
|
|
423
588
|
}
|
|
424
589
|
if (event.type === "error") {
|
|
425
|
-
|
|
590
|
+
const code = stableErrorCode(event.code, "IIIS_PARSE_FAILED");
|
|
591
|
+
throw bridgeError(code, stableErrorMessage(code), {
|
|
426
592
|
fileUploaded: event.file_uploaded === true,
|
|
427
593
|
billed: event.billed === true,
|
|
428
594
|
contentReleased: event.content_released === true,
|
|
@@ -519,8 +685,11 @@ export class IiisClient {
|
|
|
519
685
|
}
|
|
520
686
|
throw bridgeError("INVALID_SSE_EVENT", "IIIS returned an unsupported progress event.");
|
|
521
687
|
}
|
|
522
|
-
if (completedMetadata !== undefined)
|
|
688
|
+
if (completedMetadata !== undefined) {
|
|
689
|
+
throwIfCanceled(input);
|
|
690
|
+
await progress.report(100, 100, "Completed");
|
|
523
691
|
return completedMetadata;
|
|
692
|
+
}
|
|
524
693
|
throw new TransportFailure("SSE ended before completion");
|
|
525
694
|
}
|
|
526
695
|
catch (error) {
|
|
@@ -549,7 +718,7 @@ export class IiisClient {
|
|
|
549
718
|
throwIfCanceled(input);
|
|
550
719
|
let response;
|
|
551
720
|
try {
|
|
552
|
-
response = await this.#fetch(operationUrl(input), {
|
|
721
|
+
response = await this.#fetch(operationUrl(input, this.#operationBaseUrl), {
|
|
553
722
|
headers: operationHeaders(input),
|
|
554
723
|
signal: input.signal,
|
|
555
724
|
});
|
|
@@ -561,25 +730,13 @@ export class IiisClient {
|
|
|
561
730
|
if (!response.ok)
|
|
562
731
|
throw await errorFromResponse(response, input);
|
|
563
732
|
const value = await protocolJson(response, input, "operation status");
|
|
564
|
-
|
|
565
|
-
|| value.protocol_version !== GRANTED_STREAM_PROTOCOL_VERSION
|
|
566
|
-
|| value.operation_id !== input.operationId
|
|
567
|
-
|| typeof value.status !== "string"
|
|
568
|
-
|| typeof value.parser_started !== "boolean"
|
|
569
|
-
|| typeof value.file_uploaded !== "boolean"
|
|
570
|
-
|| typeof value.billed !== "boolean"
|
|
571
|
-
|| typeof value.content_released !== "boolean"
|
|
572
|
-
|| typeof value.retryable !== "boolean"
|
|
573
|
-
|| !(value.expires_at === null || typeof value.expires_at === "string")) {
|
|
574
|
-
throw bridgeError("PROTOCOL_MISMATCH", "IIIS returned an invalid operation status.");
|
|
575
|
-
}
|
|
576
|
-
return value;
|
|
733
|
+
return operationStatusValue(value, input.operationId);
|
|
577
734
|
}
|
|
578
735
|
async #events(input, afterSequence) {
|
|
579
736
|
throwIfCanceled(input);
|
|
580
737
|
let response;
|
|
581
738
|
try {
|
|
582
|
-
response = await this.#fetch(operationUrl(input, `/events?after_sequence=${afterSequence}`), { headers: operationHeaders(input), signal: input.signal });
|
|
739
|
+
response = await this.#fetch(operationUrl(input, this.#operationBaseUrl, `/events?after_sequence=${afterSequence}`), { headers: operationHeaders(input), signal: input.signal });
|
|
583
740
|
}
|
|
584
741
|
catch {
|
|
585
742
|
throwIfCanceled(input);
|
|
@@ -631,7 +788,10 @@ export class IiisClient {
|
|
|
631
788
|
await progress.report(45, 100, "Parser started");
|
|
632
789
|
parserStartedReported = true;
|
|
633
790
|
}
|
|
634
|
-
if (status.status === "ISSUED" &&
|
|
791
|
+
if (status.status === "ISSUED" &&
|
|
792
|
+
mayRetryUpload &&
|
|
793
|
+
input.expiresAt !== undefined &&
|
|
794
|
+
Date.parse(input.expiresAt) > Date.now()) {
|
|
635
795
|
try {
|
|
636
796
|
return await this.#uploadOnce(input, progress);
|
|
637
797
|
}
|
|
@@ -643,21 +803,18 @@ export class IiisClient {
|
|
|
643
803
|
}
|
|
644
804
|
}
|
|
645
805
|
else if (status.status === "RELEASED") {
|
|
646
|
-
return await this.downloadResult(input);
|
|
806
|
+
return await this.downloadResult(input, progress);
|
|
647
807
|
}
|
|
648
808
|
else if (ACTIVE_STATUSES.has(status.status)) {
|
|
649
809
|
const page = await this.#events(input, eventCursor);
|
|
650
810
|
for (const event of page.events) {
|
|
651
811
|
const parsed = progressValue(event);
|
|
652
|
-
|
|
653
|
-
? 40 + Math.floor(Math.min(parsed.done / parsed.total, 1) * 60)
|
|
654
|
-
: 40;
|
|
655
|
-
await progress.report(scaled, 100, parsed.message);
|
|
812
|
+
await progress.report(parserProgress(parsed.done, parsed.total), 100, parsed.message, parsed.detail);
|
|
656
813
|
}
|
|
657
814
|
eventCursor = page.nextSequence;
|
|
658
815
|
}
|
|
659
816
|
else if (TERMINAL_STATUSES.has(status.status)) {
|
|
660
|
-
throw bridgeError(status.status,
|
|
817
|
+
throw bridgeError(status.status, stableErrorMessage(status.status), {
|
|
661
818
|
fileUploaded: status.file_uploaded,
|
|
662
819
|
billed: status.billed,
|
|
663
820
|
contentReleased: status.content_released,
|
package/dist/index.d.ts
CHANGED
|
@@ -8,10 +8,13 @@ export interface RunCliOptions {
|
|
|
8
8
|
readonly cwd?: string;
|
|
9
9
|
readonly artifactRoot?: string;
|
|
10
10
|
readonly platform?: NodeJS.Platform;
|
|
11
|
+
readonly temporaryDirectory?: string;
|
|
12
|
+
readonly stdinIsTTY?: boolean;
|
|
11
13
|
readonly now?: () => Date;
|
|
12
14
|
readonly npmVersion?: string;
|
|
13
15
|
readonly packageVersion?: string;
|
|
14
16
|
readonly fetchImpl?: typeof fetch;
|
|
17
|
+
readonly iiisOperationBaseUrl?: string;
|
|
15
18
|
readonly stdout?: WritableLike;
|
|
16
19
|
readonly stderr?: WritableLike;
|
|
17
20
|
readonly ask?: (question: string) => Promise<string>;
|
package/dist/index.js
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execFile as execFileCallback } from "node:child_process";
|
|
3
3
|
import { readFile, realpath } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { createInterface } from "node:readline/promises";
|
|
6
7
|
import { stdin, stdout as processStdout } from "node:process";
|
|
7
8
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
-
import { ArtifactStore,
|
|
9
|
+
import { ArtifactStore, selectArtifactRoot, } from "./artifact-store.js";
|
|
10
|
+
import { CliUsageError, parseCliArguments } from "./cli/arguments.js";
|
|
9
11
|
import { runClean } from "./cli/clean.js";
|
|
10
|
-
import { runDoctor } from "./cli/doctor.js";
|
|
12
|
+
import { renderDoctor, runDoctor } from "./cli/doctor.js";
|
|
11
13
|
import { runSetup } from "./cli/setup.js";
|
|
14
|
+
import { runUninstall } from "./cli/uninstall.js";
|
|
12
15
|
import { CubeGrantClient } from "./cube-client.js";
|
|
13
16
|
import { IiisClient } from "./iiis-client.js";
|
|
17
|
+
import { createLocalParseOperationManager } from "./operation-manager.js";
|
|
14
18
|
import { OperationJournal } from "./operation-journal.js";
|
|
15
19
|
import { splitAllowedRoots } from "./path-security.js";
|
|
20
|
+
import { HttpRemoteOmniClient } from "./remote-client.js";
|
|
16
21
|
import { createOmniMcpServer } from "./server.js";
|
|
17
22
|
function helpText() {
|
|
18
23
|
return [
|
|
19
|
-
"Usage: omni-reader-mcp [setup|doctor|clean|--help|--version]",
|
|
24
|
+
"Usage: omni-reader-mcp [setup|doctor|clean|uninstall|--help|--version]",
|
|
20
25
|
"",
|
|
21
26
|
"No arguments start the stdio MCP server.",
|
|
22
27
|
"setup Configure a supported user-scope Agent",
|
|
23
28
|
"doctor Check local configuration and protocol health",
|
|
24
29
|
"clean Delete Bridge-created local artifacts and expired records",
|
|
30
|
+
"uninstall Restore a trusted URL-only Agent entry without deleting artifacts",
|
|
25
31
|
"",
|
|
26
32
|
].join("\n");
|
|
27
33
|
}
|
|
@@ -60,16 +66,26 @@ async function installedNpmVersion(env) {
|
|
|
60
66
|
});
|
|
61
67
|
});
|
|
62
68
|
}
|
|
69
|
+
async function artifactSelectionFor(options, env, homeDirectory, cwd, platform) {
|
|
70
|
+
if (options.artifactRoot !== undefined) {
|
|
71
|
+
return { mode: "default", rootDirectory: options.artifactRoot };
|
|
72
|
+
}
|
|
73
|
+
return await selectArtifactRoot({
|
|
74
|
+
platform,
|
|
75
|
+
homeDirectory,
|
|
76
|
+
env,
|
|
77
|
+
projectDirectory: cwd,
|
|
78
|
+
temporaryDirectory: options.temporaryDirectory ?? tmpdir(),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
63
81
|
export async function startStdioServer(options = {}) {
|
|
64
82
|
const env = options.env ?? process.env;
|
|
83
|
+
const homeDirectory = options.homeDirectory ?? (await import("node:os")).homedir();
|
|
65
84
|
const cwd = options.cwd ?? process.cwd();
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
homeDirectory: options.homeDirectory,
|
|
69
|
-
env,
|
|
70
|
-
});
|
|
85
|
+
const platform = options.platform ?? process.platform;
|
|
86
|
+
const artifactSelection = await artifactSelectionFor(options, env, homeDirectory, cwd, platform);
|
|
71
87
|
const artifactStore = await ArtifactStore.open({
|
|
72
|
-
rootDirectory:
|
|
88
|
+
rootDirectory: artifactSelection.rootDirectory,
|
|
73
89
|
projectDirectory: cwd,
|
|
74
90
|
now: options.now,
|
|
75
91
|
});
|
|
@@ -79,12 +95,33 @@ export async function startStdioServer(options = {}) {
|
|
|
79
95
|
environment: { CUE_API_KEY: env.CUE_API_KEY },
|
|
80
96
|
fetchImpl: options.fetchImpl,
|
|
81
97
|
});
|
|
98
|
+
const iiisClient = new IiisClient({
|
|
99
|
+
fetchImpl: options.fetchImpl,
|
|
100
|
+
operationBaseUrl: options.iiisOperationBaseUrl,
|
|
101
|
+
});
|
|
102
|
+
const extraRoots = splitAllowedRoots(env.OMNI_ALLOWED_ROOTS);
|
|
103
|
+
const remoteClient = new HttpRemoteOmniClient({
|
|
104
|
+
apiKey: env.CUE_API_KEY,
|
|
105
|
+
fetchImpl: options.fetchImpl,
|
|
106
|
+
});
|
|
107
|
+
const operationManager = createLocalParseOperationManager({
|
|
108
|
+
journal,
|
|
109
|
+
workspace: cwd,
|
|
110
|
+
extraRoots,
|
|
111
|
+
cubeClient,
|
|
112
|
+
iiisClient,
|
|
113
|
+
artifactStore,
|
|
114
|
+
remoteClient,
|
|
115
|
+
now: options.now,
|
|
116
|
+
});
|
|
82
117
|
const server = createOmniMcpServer({
|
|
83
118
|
workspace: cwd,
|
|
84
|
-
extraRoots
|
|
119
|
+
extraRoots,
|
|
85
120
|
cubeClient,
|
|
86
|
-
iiisClient
|
|
121
|
+
iiisClient,
|
|
87
122
|
artifactStore,
|
|
123
|
+
operationManager,
|
|
124
|
+
remoteClient,
|
|
88
125
|
});
|
|
89
126
|
const close = async () => {
|
|
90
127
|
await server.close().catch(() => undefined);
|
|
@@ -99,7 +136,6 @@ export async function runCli(args, options = {}) {
|
|
|
99
136
|
const homeDirectory = options.homeDirectory ?? (await import("node:os")).homedir();
|
|
100
137
|
const cwd = options.cwd ?? process.cwd();
|
|
101
138
|
const platform = options.platform ?? process.platform;
|
|
102
|
-
const artifactRoot = options.artifactRoot ?? defaultArtifactRoot({ platform, homeDirectory, env });
|
|
103
139
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
104
140
|
const output = options.stdout ?? process.stdout;
|
|
105
141
|
const errorOutput = options.stderr ?? process.stderr;
|
|
@@ -107,57 +143,82 @@ export async function runCli(args, options = {}) {
|
|
|
107
143
|
const write = (text) => { output.write(text); };
|
|
108
144
|
const packageVersion = options.packageVersion ?? await installedPackageVersion();
|
|
109
145
|
try {
|
|
110
|
-
|
|
146
|
+
const parsed = parseCliArguments(args, platform);
|
|
147
|
+
if (parsed.command === "server") {
|
|
111
148
|
await (options.startServer ?? (() => startStdioServer({
|
|
112
149
|
...options,
|
|
113
150
|
env,
|
|
114
151
|
homeDirectory,
|
|
115
152
|
cwd,
|
|
116
153
|
platform,
|
|
117
|
-
artifactRoot,
|
|
118
154
|
fetchImpl,
|
|
119
155
|
})))();
|
|
120
156
|
return 0;
|
|
121
157
|
}
|
|
122
|
-
|
|
123
|
-
if (command === "--help" || command === "-h" || command === "help") {
|
|
158
|
+
if (parsed.command === "help") {
|
|
124
159
|
write(helpText());
|
|
125
160
|
return 0;
|
|
126
161
|
}
|
|
127
|
-
if (command === "
|
|
162
|
+
if (parsed.command === "version") {
|
|
128
163
|
write(`${packageVersion}\n`);
|
|
129
164
|
return 0;
|
|
130
165
|
}
|
|
131
|
-
if (command === "setup") {
|
|
132
|
-
await runSetup({
|
|
166
|
+
if (parsed.command === "setup") {
|
|
167
|
+
await runSetup({
|
|
168
|
+
env,
|
|
169
|
+
homeDirectory,
|
|
170
|
+
platform,
|
|
171
|
+
fetchImpl,
|
|
172
|
+
ask,
|
|
173
|
+
write,
|
|
174
|
+
stdinIsTTY: options.stdinIsTTY ?? stdin.isTTY === true,
|
|
175
|
+
arguments: parsed.arguments,
|
|
176
|
+
});
|
|
177
|
+
return 0;
|
|
178
|
+
}
|
|
179
|
+
if (parsed.command === "uninstall") {
|
|
180
|
+
await runUninstall({
|
|
181
|
+
args: [
|
|
182
|
+
...(parsed.yes ? ["--yes"] : []),
|
|
183
|
+
...(parsed.json ? ["--json"] : []),
|
|
184
|
+
],
|
|
185
|
+
env,
|
|
186
|
+
homeDirectory,
|
|
187
|
+
platform,
|
|
188
|
+
write,
|
|
189
|
+
});
|
|
133
190
|
return 0;
|
|
134
191
|
}
|
|
135
|
-
if (command === "doctor") {
|
|
136
|
-
const
|
|
192
|
+
if (parsed.command === "doctor") {
|
|
193
|
+
const artifactSelection = await artifactSelectionFor(options, env, homeDirectory, cwd, platform);
|
|
194
|
+
const report = await runDoctor({
|
|
137
195
|
env,
|
|
138
196
|
homeDirectory,
|
|
139
197
|
platform,
|
|
140
|
-
artifactRoot,
|
|
198
|
+
artifactRoot: artifactSelection.rootDirectory,
|
|
199
|
+
cacheMode: artifactSelection.mode,
|
|
141
200
|
fetchImpl,
|
|
142
201
|
npmVersion: options.npmVersion ?? await installedNpmVersion(env),
|
|
143
202
|
packageVersion,
|
|
144
203
|
});
|
|
145
|
-
write(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (command === "clean") {
|
|
149
|
-
const result = await runClean({ artifactRoot, projectDirectory: cwd, now: options.now });
|
|
150
|
-
write(`Removed ${result.removed} Bridge cache item(s).\n`);
|
|
204
|
+
write(parsed.json
|
|
205
|
+
? `${JSON.stringify(report)}\n`
|
|
206
|
+
: `${renderDoctor(report).join("\n")}\n`);
|
|
151
207
|
return 0;
|
|
152
208
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
209
|
+
const artifactSelection = await artifactSelectionFor(options, env, homeDirectory, cwd, platform);
|
|
210
|
+
const result = await runClean({
|
|
211
|
+
artifactRoot: artifactSelection.rootDirectory,
|
|
212
|
+
projectDirectory: cwd,
|
|
213
|
+
now: options.now,
|
|
214
|
+
});
|
|
215
|
+
write(`Removed ${result.removed} Bridge cache item(s).\n`);
|
|
216
|
+
return 0;
|
|
156
217
|
}
|
|
157
218
|
catch (error) {
|
|
158
219
|
const message = error instanceof Error ? error.message : "The Bridge command failed.";
|
|
159
220
|
errorOutput.write(`Error: ${message}\n`);
|
|
160
|
-
return 1;
|
|
221
|
+
return error instanceof CliUsageError ? error.exitCode : 1;
|
|
161
222
|
}
|
|
162
223
|
}
|
|
163
224
|
async function main() {
|
package/dist/multipart-body.js
CHANGED