@docmana/sdk 0.10.0 → 0.12.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/CHANGELOG.md +19 -5
- package/README.md +13 -11
- package/dist/cli.mjs +41 -11
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +21 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -6
- package/dist/index.mjs.map +1 -1
- package/dist/models/execution-status.enum.d.ts +1 -1
- package/dist/status.d.ts +3 -0
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 0.
|
|
4
|
-
- Add
|
|
5
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.12.0
|
|
4
|
+
- Add `--context <json>` to `docmana flow run`, parsed as a JSON object and
|
|
5
|
+
passed through to `runFlow` as `RunFlowParams.context`.
|
|
6
|
+
- Return CLI usage errors when `--context` is invalid JSON or is not a JSON
|
|
7
|
+
object.
|
|
8
|
+
|
|
9
|
+
## 0.11.0
|
|
10
|
+
- Normalize execution statuses to uppercase SDK values:
|
|
11
|
+
`PENDING`, `RUNNING`, `INCOMPLETED`, `COMPLETED`, and `FAILED`.
|
|
12
|
+
- Normalize raw API and polling statuses before checking terminal or failed
|
|
13
|
+
states, so legacy mixed-case values still map consistently.
|
|
14
|
+
- Rename flow metadata `contextSchema` to `jsonContextSchema` in the public
|
|
15
|
+
`FlowMetadata` shape and README examples.
|
|
16
|
+
|
|
17
|
+
## 0.10.0
|
|
18
|
+
- Add an optional `organization` to flow selection, emitted as a
|
|
19
|
+
`?organization=<org>` query param on the request to
|
|
6
20
|
`docmana-consumer-edge-api`. Purely additive — existing calls are unchanged.
|
|
7
21
|
- `listFlows({ organization })`, `getFlowMetadata(flowId, { organization })`,
|
|
8
22
|
and `getFlowDefinition(flowId, { organization })` accept the option.
|
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ use `RunFlowParams` for request data:
|
|
|
103
103
|
- `listOrganizations()` calls `GET /v1/organizations` and returns
|
|
104
104
|
`{ id, name }[]` for the organizations available to the Bearer token.
|
|
105
105
|
- `getFlowMetadata(flowId)` calls `GET /v1/flows/:flowId/metadata` and returns
|
|
106
|
-
`{ flowId, name?,
|
|
106
|
+
`{ flowId, name?, jsonContextSchema, languages, minScore?, documents }`.
|
|
107
107
|
- `getFlowDefinition(flowId)` calls `GET /v1/flows/:flowId/definition` and
|
|
108
108
|
returns the normalized flow definition `{ flowId, name?, languages, minScore?, nodes }`.
|
|
109
109
|
- `listOrganizationDocuments({ prefix? })` calls
|
|
@@ -121,7 +121,7 @@ use `RunFlowParams` for request data:
|
|
|
121
121
|
`GET /v1/executions/:executionResultId/result`.
|
|
122
122
|
- `runFlow(flowId, params, options?)` starts async execution, polls status, fetches the
|
|
123
123
|
result, respects `X-Poll-After` and `429 Retry-After` while polling, and throws
|
|
124
|
-
`DocmanaExecutionError` if the result status is `
|
|
124
|
+
`DocmanaExecutionError` if the result status is `FAILED`.
|
|
125
125
|
|
|
126
126
|
Consumer is the polling authority. When `GET /status` returns `X-Poll-After`,
|
|
127
127
|
the SDK waits that many milliseconds before the next status call. If the header
|
|
@@ -140,14 +140,14 @@ console.log(organizations.map((org) => `${org.id}: ${org.name}`).join("\n"));
|
|
|
140
140
|
|
|
141
141
|
```ts
|
|
142
142
|
const metadata = await docmana.getFlowMetadata("<flow-id>");
|
|
143
|
-
console.log(metadata.
|
|
143
|
+
console.log(metadata.jsonContextSchema);
|
|
144
144
|
|
|
145
145
|
const definition = await docmana.getFlowDefinition("<flow-id>");
|
|
146
146
|
console.log(definition.nodes.map((node) => node.label).join("\n"));
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
```ts
|
|
150
|
-
const depot = await docmana.listOrganizationDocuments({ prefix: "
|
|
150
|
+
const depot = await docmana.listOrganizationDocuments({ prefix: "folder/" });
|
|
151
151
|
if (depot.configured) {
|
|
152
152
|
const file = await docmana.downloadOrganizationDocument(depot.documents[0].path);
|
|
153
153
|
console.log(file.filename, file.bytes.byteLength);
|
|
@@ -159,8 +159,8 @@ const { executionResultId } = await docmana.runFlowAsync("<flow-id>", {
|
|
|
159
159
|
files: ["./contract.pdf"],
|
|
160
160
|
});
|
|
161
161
|
|
|
162
|
-
let status = "
|
|
163
|
-
while (status !== "
|
|
162
|
+
let status = "PENDING";
|
|
163
|
+
while (status !== "COMPLETED" && status !== "FAILED") {
|
|
164
164
|
await new Promise((r) => setTimeout(r, 2000));
|
|
165
165
|
({ status } = await docmana.getStatus(executionResultId));
|
|
166
166
|
}
|
|
@@ -217,9 +217,9 @@ docmana organization list
|
|
|
217
217
|
|
|
218
218
|
```bash
|
|
219
219
|
DOCMANA_API_URL="https://consumer.example.com/v1"
|
|
220
|
-
docmana organization document list --prefix "
|
|
221
|
-
docmana organization document download "
|
|
222
|
-
docmana organization document download "
|
|
220
|
+
docmana organization document list --prefix "folder/"
|
|
221
|
+
docmana organization document download "folder/invoice.pdf"
|
|
222
|
+
docmana organization document download "folder/invoice.pdf" --output "./invoice.pdf"
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
```bash
|
|
@@ -239,12 +239,14 @@ You can also run without the cache:
|
|
|
239
239
|
docmana flow run "<flow-id>" \
|
|
240
240
|
--api-url "https://consumer.example.com/v1" \
|
|
241
241
|
--access-token "$DOCMANA_ACCESS_TOKEN" \
|
|
242
|
-
--files "./contract.pdf,./
|
|
242
|
+
--files "./contract.pdf,./appendix.pdf" \
|
|
243
|
+
--context '{"contract":{"destination":"Canada"}}' \
|
|
243
244
|
--draft \
|
|
244
245
|
--json
|
|
245
246
|
```
|
|
246
247
|
|
|
247
248
|
Use `--draft` to run the current draft flow version.
|
|
249
|
+
Use `--context` to pass business context as a JSON object.
|
|
248
250
|
Use `docmana flow list --json` to print the raw flow list returned by Consumer.
|
|
249
251
|
Use `docmana organization list --json` to print the organization list as JSON.
|
|
250
252
|
Use `docmana organization document list --json` for raw organization document JSON.
|
|
@@ -264,7 +266,7 @@ optional `.requestId` when available.
|
|
|
264
266
|
| `DocmanaAuthError` | `401` rejected or expired Bearer token. |
|
|
265
267
|
| `DocmanaPermissionError` | `403` missing permission. |
|
|
266
268
|
| `DocmanaNotFoundError` | `404` flow or execution not found. |
|
|
267
|
-
| `DocmanaExecutionError` | The flow finished with status `"
|
|
269
|
+
| `DocmanaExecutionError` | The flow finished with status `"FAILED"`. Carries `.errors` and `.result`. |
|
|
268
270
|
| `DocmanaTimeoutError` | Polling exceeded `timeoutMs`. |
|
|
269
271
|
| `DocmanaAbortError` | The provided `AbortSignal` fired. |
|
|
270
272
|
|
package/dist/cli.mjs
CHANGED
|
@@ -282,13 +282,29 @@ async function runFlow(http, flowId, parts, signal, useDraftVersion = false, con
|
|
|
282
282
|
);
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
+
// src/sdk/status.ts
|
|
286
|
+
function normalizeStatus(status, fallback = "UNKNOWN") {
|
|
287
|
+
if (typeof status !== "string") return fallback;
|
|
288
|
+
const trimmed = status.trim();
|
|
289
|
+
if (!trimmed) return fallback;
|
|
290
|
+
return trimmed.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toUpperCase();
|
|
291
|
+
}
|
|
292
|
+
function isFailedStatus(status) {
|
|
293
|
+
return normalizeStatus(status) === "FAILED";
|
|
294
|
+
}
|
|
295
|
+
function isTerminalStatus(status) {
|
|
296
|
+
const normalized = normalizeStatus(status);
|
|
297
|
+
return normalized === "COMPLETED" || normalized === "FAILED";
|
|
298
|
+
}
|
|
299
|
+
|
|
285
300
|
// src/sdk/api/execution-status.ts
|
|
286
301
|
async function getStatus(http, executionResultId, signal) {
|
|
287
302
|
const res = await http.requestRaw("GET", `/executions/${executionResultId}/status`, { signal });
|
|
288
303
|
const text = await res.text();
|
|
289
304
|
const body = text ? JSON.parse(text) : { status: "" };
|
|
305
|
+
const normalizedBody = { ...body, status: normalizeStatus(body.status) };
|
|
290
306
|
const pollAfterMs = parsePositiveInt(res.headers.get("x-poll-after"));
|
|
291
|
-
return pollAfterMs === void 0 ?
|
|
307
|
+
return pollAfterMs === void 0 ? normalizedBody : { ...normalizedBody, pollAfterMs };
|
|
292
308
|
}
|
|
293
309
|
function parsePositiveInt(value) {
|
|
294
310
|
if (!value) return void 0;
|
|
@@ -430,7 +446,6 @@ function toNumberValue(value) {
|
|
|
430
446
|
}
|
|
431
447
|
|
|
432
448
|
// src/sdk/polling/poll.ts
|
|
433
|
-
var TERMINAL = /* @__PURE__ */ new Set(["Completed", "Failed"]);
|
|
434
449
|
var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
435
450
|
async function pollUntilTerminal(opts) {
|
|
436
451
|
const sleep = opts.sleep ?? defaultSleep;
|
|
@@ -451,8 +466,8 @@ async function pollUntilTerminal(opts) {
|
|
|
451
466
|
await sleep(err.retryAfterMs ?? opts.intervalMs);
|
|
452
467
|
continue;
|
|
453
468
|
}
|
|
454
|
-
const
|
|
455
|
-
if (
|
|
469
|
+
const status = normalizeStatus(result.status);
|
|
470
|
+
if (isTerminalStatus(status)) return status;
|
|
456
471
|
if (elapsed() >= opts.timeoutMs) {
|
|
457
472
|
throw new DocmanaTimeoutError(`Execution did not finish within ${opts.timeoutMs}ms`);
|
|
458
473
|
}
|
|
@@ -547,7 +562,7 @@ var Docmana = class {
|
|
|
547
562
|
signal: options.signal
|
|
548
563
|
});
|
|
549
564
|
const result = await this.getResult(executionResultId, options.signal, params.language);
|
|
550
|
-
if (result.status
|
|
565
|
+
if (isFailedStatus(result.status)) {
|
|
551
566
|
throw new DocmanaExecutionError(
|
|
552
567
|
`Flow ${flowId} execution failed`,
|
|
553
568
|
result.errors ?? [],
|
|
@@ -578,7 +593,7 @@ var Docmana = class {
|
|
|
578
593
|
return downloadOrganizationDocuments(this.http, paths);
|
|
579
594
|
}
|
|
580
595
|
isTerminalStatus(status) {
|
|
581
|
-
return status
|
|
596
|
+
return isTerminalStatus(status);
|
|
582
597
|
}
|
|
583
598
|
};
|
|
584
599
|
|
|
@@ -686,10 +701,10 @@ function formatHumanError(err) {
|
|
|
686
701
|
if (docResult.extractions) nodes.push(...Object.values(docResult.extractions));
|
|
687
702
|
if (docResult.validations) nodes.push(...Object.values(docResult.validations));
|
|
688
703
|
const hasNodeFailures = nodes.some(
|
|
689
|
-
(n) => n.status
|
|
704
|
+
(n) => isFailedStatus(n.status) || n.errors && n.errors.length > 0
|
|
690
705
|
);
|
|
691
706
|
lines.push(` - Document: ${docName} (Status: ${status})`);
|
|
692
|
-
if (docResult.status
|
|
707
|
+
if (!isFailedStatus(docResult.status) && !hasNodeFailures) continue;
|
|
693
708
|
if (docResult.classifications) {
|
|
694
709
|
for (const [nodeName, node] of Object.entries(docResult.classifications)) {
|
|
695
710
|
if (node.errors && node.errors.length > 0) {
|
|
@@ -706,7 +721,7 @@ function formatHumanError(err) {
|
|
|
706
721
|
}
|
|
707
722
|
if (docResult.extractions) {
|
|
708
723
|
for (const [nodeName, node] of Object.entries(docResult.extractions)) {
|
|
709
|
-
if (node.status
|
|
724
|
+
if (isFailedStatus(node.status) || node.errors && node.errors.length > 0) {
|
|
710
725
|
lines.push(` Extraction: ${nodeName} (Status: ${node.status})`);
|
|
711
726
|
if (node.errors) {
|
|
712
727
|
for (const nodeErr of node.errors) lines.push(` - ${nodeErr}`);
|
|
@@ -716,7 +731,7 @@ function formatHumanError(err) {
|
|
|
716
731
|
}
|
|
717
732
|
if (docResult.validations) {
|
|
718
733
|
for (const [nodeName, node] of Object.entries(docResult.validations)) {
|
|
719
|
-
if (node.status
|
|
734
|
+
if (isFailedStatus(node.status) || node.errors && node.errors.length > 0) {
|
|
720
735
|
lines.push(` Validation: ${nodeName} (Status: ${node.status})`);
|
|
721
736
|
if (node.errors) {
|
|
722
737
|
for (const nodeErr of node.errors) lines.push(` - ${nodeErr}`);
|
|
@@ -1255,11 +1270,13 @@ async function listOrganizationsCommand(options, env, io, clientFactory, deps) {
|
|
|
1255
1270
|
async function runFlowCommand(flowId, options, env, io, clientFactory, deps) {
|
|
1256
1271
|
try {
|
|
1257
1272
|
const files = parseFiles(options.files);
|
|
1273
|
+
const context = parseContext(options.context);
|
|
1258
1274
|
const client = await resolveApiClient(options, env, io, clientFactory, deps);
|
|
1259
1275
|
const result = await client.runFlow(
|
|
1260
1276
|
flowId,
|
|
1261
1277
|
{
|
|
1262
1278
|
files: files.map((path) => ({ path })),
|
|
1279
|
+
context,
|
|
1263
1280
|
language: options.language,
|
|
1264
1281
|
useDraftVersion: options.draft === true
|
|
1265
1282
|
},
|
|
@@ -1307,6 +1324,19 @@ async function runFlowCommand(flowId, options, env, io, clientFactory, deps) {
|
|
|
1307
1324
|
throw err;
|
|
1308
1325
|
}
|
|
1309
1326
|
}
|
|
1327
|
+
function parseContext(raw) {
|
|
1328
|
+
if (raw === void 0) return void 0;
|
|
1329
|
+
let parsed;
|
|
1330
|
+
try {
|
|
1331
|
+
parsed = JSON.parse(raw);
|
|
1332
|
+
} catch {
|
|
1333
|
+
throw new CliUsageError("--context must be valid JSON.");
|
|
1334
|
+
}
|
|
1335
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1336
|
+
throw new CliUsageError("--context must be a JSON object.");
|
|
1337
|
+
}
|
|
1338
|
+
return parsed;
|
|
1339
|
+
}
|
|
1310
1340
|
|
|
1311
1341
|
// src/cli/commands/login.ts
|
|
1312
1342
|
async function loginCommand(options, env, io, deps) {
|
|
@@ -1385,7 +1415,7 @@ function buildProgram(env, io, clientFactory, deps) {
|
|
|
1385
1415
|
flow.command("list").description(HELP.list).option("--api-url <url>", "Consumer API base URL; defaults to DOCMANA_API_URL").option("--access-token <token>", "Bearer token; defaults to DOCMANA_ACCESS_TOKEN").option("--config <path>", `Config file path; defaults to ./${DEFAULT_CONFIG_FILE}`).option("--token-cache <path>", `Token cache path; defaults to ./${DEFAULT_TOKEN_CACHE_FILE}`).option("--json", "Print the raw JSON flow list").action(async (options) => {
|
|
1386
1416
|
await listFlowsCommand(options, env, io, clientFactory, deps);
|
|
1387
1417
|
});
|
|
1388
|
-
flow.command("run").description(HELP.run).argument("<flow-id>", "Docmana flow id").requiredOption("--files <csv>", "Comma-separated file paths to upload").option("--api-url <url>", "Consumer API base URL; defaults to DOCMANA_API_URL").option("--access-token <token>", "Bearer token; defaults to DOCMANA_ACCESS_TOKEN").option("--config <path>", `Config file path; defaults to ./${DEFAULT_CONFIG_FILE}`).option("--token-cache <path>", `Token cache path; defaults to ./${DEFAULT_TOKEN_CACHE_FILE}`).option("--json", "Print the raw JSON result").option("--language <lang>", "Translate output to specified language").option("--draft", "Run the current draft flow version").action(async (flowId, options) => {
|
|
1418
|
+
flow.command("run").description(HELP.run).argument("<flow-id>", "Docmana flow id").requiredOption("--files <csv>", "Comma-separated file paths to upload").option("--api-url <url>", "Consumer API base URL; defaults to DOCMANA_API_URL").option("--access-token <token>", "Bearer token; defaults to DOCMANA_ACCESS_TOKEN").option("--config <path>", `Config file path; defaults to ./${DEFAULT_CONFIG_FILE}`).option("--token-cache <path>", `Token cache path; defaults to ./${DEFAULT_TOKEN_CACHE_FILE}`).option("--json", "Print the raw JSON result").option("--context <json>", "Business context as a JSON object").option("--language <lang>", "Translate output to specified language").option("--draft", "Run the current draft flow version").action(async (flowId, options) => {
|
|
1389
1419
|
await runFlowCommand(flowId, options, env, io, clientFactory, deps);
|
|
1390
1420
|
});
|
|
1391
1421
|
flow.command("metadata").description(HELP.metadata).argument("<flow-id>", "Docmana flow id").option("--api-url <url>", "Consumer API base URL; defaults to DOCMANA_API_URL").option("--access-token <token>", "Bearer token; defaults to DOCMANA_ACCESS_TOKEN").option("--config <path>", `Config file path; defaults to ./${DEFAULT_CONFIG_FILE}`).option("--token-cache <path>", `Token cache path; defaults to ./${DEFAULT_TOKEN_CACHE_FILE}`).action(async (flowId, options) => {
|