@docmana/sdk 0.2.1 → 0.3.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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+ - Relocated mapper logic and tests to `src/mappers/`.
5
+ - Renamed SDK models to `DocmanaX` and API models to `DocmanaApiX` to distinguish raw backend types from clean public SDK types.
6
+ - Pluralized `classification` results into a `classifications` key-value record on document results.
7
+ - Nested dynamic extraction fields inside a dedicated `data` record on node results to prevent property name collisions.
8
+ - Restructured node result interfaces to remove `score` and `feedback` from `DocmanaExtractionResult` and `DocmanaMetadataExtractionResult` (via new `DocmanaScoreNodeResult` interface).
9
+ - Excluded internal fields `result` and `documentType` from mapped SDK results.
10
+
11
+ ## 0.2.2
12
+ - Fix `package-lock.json`.
13
+
3
14
  ## 0.2.1
4
15
  - 400 responses now throw `DocmanaRequestError` (was `DocmanaAuthError`); add `Incompleted` to `ExecutionStatus`.
5
16
 
package/dist/cli.mjs CHANGED
@@ -44,9 +44,11 @@ var DocmanaRequestError = class extends DocmanaError {
44
44
  };
45
45
  var DocmanaExecutionError = class extends DocmanaError {
46
46
  errors;
47
- constructor(message, errors = []) {
47
+ result;
48
+ constructor(message, errors = [], result) {
48
49
  super(message, { code: "execution_failed" });
49
50
  this.errors = errors;
51
+ this.result = result;
50
52
  }
51
53
  };
52
54
  var DocmanaTimeoutError = class extends DocmanaError {
@@ -356,6 +358,156 @@ async function pollUntilTerminal(opts) {
356
358
  }
357
359
  }
358
360
 
361
+ // src/mappers/mapper.ts
362
+ function extractNodeFields(rawNode, targetLanguage) {
363
+ const metadataFields = /* @__PURE__ */ new Set([
364
+ "node_id",
365
+ "node_name",
366
+ "node_type",
367
+ "feature_name",
368
+ "running_time",
369
+ "status",
370
+ "score",
371
+ "feedback",
372
+ "translations",
373
+ "tokens",
374
+ "input_tokens",
375
+ "output_tokens",
376
+ "errors",
377
+ "mana",
378
+ "model",
379
+ "tool_calls",
380
+ "prompt_id",
381
+ "prompt",
382
+ "prompt_name",
383
+ "label",
384
+ "scoreDescription",
385
+ "score_description",
386
+ "result",
387
+ "documentType"
388
+ ]);
389
+ let sourceObj = rawNode;
390
+ if (targetLanguage) {
391
+ const translations = rawNode.translations;
392
+ if (!translations || !translations[targetLanguage]) {
393
+ throw new DocmanaError(
394
+ `Translation for language '${targetLanguage}' is missing on node '${rawNode.node_name || rawNode.node_id}'`,
395
+ { code: "translation_missing", status: 400 }
396
+ );
397
+ }
398
+ sourceObj = translations[targetLanguage];
399
+ }
400
+ const dynamicFields = {};
401
+ for (const [key, value] of Object.entries(sourceObj)) {
402
+ if (!metadataFields.has(key)) {
403
+ dynamicFields[key] = value;
404
+ }
405
+ }
406
+ const sdkNode = {
407
+ status: rawNode.status || "Unknown"
408
+ };
409
+ if (rawNode.errors && rawNode.errors.length > 0) {
410
+ sdkNode.errors = rawNode.errors;
411
+ }
412
+ const nodeType = rawNode.node_type || "";
413
+ const isScoreNode = /^(Validation|Classification|CrossValidation|Conclusion)$/i.test(nodeType);
414
+ if (isScoreNode) {
415
+ if (rawNode.score !== void 0 && rawNode.score !== null) {
416
+ sdkNode.score = rawNode.score;
417
+ }
418
+ if (rawNode.feedback !== void 0 && rawNode.feedback !== null) {
419
+ sdkNode.feedback = rawNode.feedback;
420
+ }
421
+ }
422
+ if (Object.keys(dynamicFields).length > 0) {
423
+ sdkNode.data = dynamicFields;
424
+ }
425
+ return sdkNode;
426
+ }
427
+ function mapExecutionResult(raw, targetLanguage) {
428
+ const sdkResult = {
429
+ status: raw.status || "Unknown",
430
+ executionId: String(raw.execution_id || raw.executionResultId || raw.execution_result_id || ""),
431
+ documents: {
432
+ mappings: []
433
+ }
434
+ };
435
+ if (raw.errors && raw.errors.length > 0) {
436
+ sdkResult.errors = raw.errors;
437
+ }
438
+ const results = raw.results || [];
439
+ for (const item of results) {
440
+ const isDocSpecific = !!item.document;
441
+ if (isDocSpecific && item.document) {
442
+ const docReq = item.document;
443
+ const docKey = docReq.doc_key || "unknown_doc";
444
+ const docName = docReq.name || "";
445
+ sdkResult.documents.mappings.push({
446
+ name: docName,
447
+ reference: docKey
448
+ });
449
+ const docResult = {
450
+ status: item.status || docReq.status || "Unknown",
451
+ extractions: {},
452
+ validations: {}
453
+ };
454
+ const nodeResults = item.node_results || [];
455
+ for (const node of nodeResults) {
456
+ const nodeType = node.node_type || "";
457
+ const nodeName = node.node_name || node.node_id || "unknown_node";
458
+ if (/^Classification$/i.test(nodeType)) {
459
+ const mappedNode = extractNodeFields(node, targetLanguage);
460
+ if (!docResult.classifications) {
461
+ docResult.classifications = {};
462
+ }
463
+ docResult.classifications[nodeName] = mappedNode;
464
+ } else if (/^MetadataExtraction$/i.test(nodeType)) {
465
+ docResult.metadataExtraction = extractNodeFields(node, targetLanguage);
466
+ } else if (/^Extraction$/i.test(nodeType)) {
467
+ if (docResult.extractions) {
468
+ docResult.extractions[nodeName] = extractNodeFields(node, targetLanguage);
469
+ }
470
+ } else if (/^Validation$/i.test(nodeType)) {
471
+ if (docResult.validations) {
472
+ docResult.validations[nodeName] = extractNodeFields(node, targetLanguage);
473
+ }
474
+ }
475
+ }
476
+ if (docResult.extractions && Object.keys(docResult.extractions).length === 0) {
477
+ delete docResult.extractions;
478
+ }
479
+ if (docResult.validations && Object.keys(docResult.validations).length === 0) {
480
+ delete docResult.validations;
481
+ }
482
+ sdkResult.documents[docKey] = docResult;
483
+ } else {
484
+ const nodeResults = item.node_results || [];
485
+ for (const node of nodeResults) {
486
+ const nodeType = node.node_type || "";
487
+ const nodeName = node.node_name || node.node_id || "unknown_node";
488
+ if (/^CrossValidation$/i.test(nodeType)) {
489
+ if (!sdkResult.crossValidations) {
490
+ sdkResult.crossValidations = {};
491
+ }
492
+ sdkResult.crossValidations[nodeName] = extractNodeFields(
493
+ node,
494
+ targetLanguage
495
+ );
496
+ } else if (/^(Conclusion|Finish)$/i.test(nodeType)) {
497
+ if (!sdkResult.conclusions) {
498
+ sdkResult.conclusions = {};
499
+ }
500
+ sdkResult.conclusions[nodeName] = extractNodeFields(
501
+ node,
502
+ targetLanguage
503
+ );
504
+ }
505
+ }
506
+ }
507
+ }
508
+ return sdkResult;
509
+ }
510
+
359
511
  // src/client.ts
360
512
  var Docmana = class {
361
513
  config;
@@ -392,8 +544,9 @@ var Docmana = class {
392
544
  async getStatus(executionResultId, signal) {
393
545
  return getStatus(this.http, executionResultId, signal);
394
546
  }
395
- async getResult(executionResultId, signal) {
396
- return getResult(this.http, executionResultId, signal);
547
+ async getResult(executionResultId, signal, language) {
548
+ const rawResult = await getResult(this.http, executionResultId, signal);
549
+ return mapExecutionResult(rawResult, language);
397
550
  }
398
551
  async runFlow(flowId, input) {
399
552
  const { executionResultId } = await this.runFlowAsync(flowId, input);
@@ -403,9 +556,13 @@ var Docmana = class {
403
556
  timeoutMs: input.timeoutMs ?? this.config.timeoutMs,
404
557
  signal: input.signal
405
558
  });
406
- const result = await this.getResult(executionResultId, input.signal);
559
+ const result = await this.getResult(executionResultId, input.signal, input.language);
407
560
  if (result.status === "Failed") {
408
- throw new DocmanaExecutionError(`Flow ${flowId} execution failed`, result.errors ?? []);
561
+ throw new DocmanaExecutionError(
562
+ `Flow ${flowId} execution failed`,
563
+ result.errors ?? [],
564
+ result
565
+ );
409
566
  }
410
567
  return result;
411
568
  }
@@ -418,6 +575,15 @@ var CliUsageError = class extends Error {
418
575
  this.name = "CliUsageError";
419
576
  }
420
577
  };
578
+ var CliSilentError = class extends Error {
579
+ exitCode;
580
+ constructor(exitCode = 1) {
581
+ super();
582
+ this.name = "CliSilentError";
583
+ this.exitCode = exitCode;
584
+ Object.setPrototypeOf(this, new.target.prototype);
585
+ }
586
+ };
421
587
  var HELP = {
422
588
  root: "Run Docmana document-analysis flows.",
423
589
  flow: "Manage and run Docmana flows.",
@@ -441,6 +607,9 @@ async function runCli(argv = process.argv.slice(2), env = process.env, io = {
441
607
  await program.parseAsync(argv, { from: "user" });
442
608
  return 0;
443
609
  } catch (err) {
610
+ if (err instanceof CliSilentError) {
611
+ return err.exitCode;
612
+ }
444
613
  if (err instanceof CliUsageError) {
445
614
  io.stderr(`Error: ${err.message}
446
615
  `);
@@ -467,7 +636,7 @@ function buildProgram(env, io, clientFactory, deps) {
467
636
  const flow = program.command("flow").description(HELP.flow).action(() => {
468
637
  io.stdout(flow.helpInformation());
469
638
  });
470
- flow.command("run").description(HELP.run).argument("<flow-id>", "Docmana flow id").requiredOption("--files <csv>", "Comma-separated file paths to upload").option("--client-id <id>", "OAuth2 client id; defaults to DOCMANA_CLIENT_ID").option("--client-secret <secret>", "OAuth2 client secret; defaults to DOCMANA_CLIENT_SECRET").option("--api-url <url>", "Docmana API base URL; defaults to DOCMANA_API_URL").option("--token-url <url>", "OAuth2 token endpoint; defaults to DOCMANA_TOKEN_URL").option("--scope <scope>", "OAuth2 scope; defaults to DOCMANA_SCOPE").option("--organisation <id>", "Docmana organisation id; defaults to DOCMANA_ORGANISATION").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").action(async (flowId, options) => {
639
+ flow.command("run").description(HELP.run).argument("<flow-id>", "Docmana flow id").requiredOption("--files <csv>", "Comma-separated file paths to upload").option("--client-id <id>", "OAuth2 client id; defaults to DOCMANA_CLIENT_ID").option("--client-secret <secret>", "OAuth2 client secret; defaults to DOCMANA_CLIENT_SECRET").option("--api-url <url>", "Docmana API base URL; defaults to DOCMANA_API_URL").option("--token-url <url>", "OAuth2 token endpoint; defaults to DOCMANA_TOKEN_URL").option("--scope <scope>", "OAuth2 scope; defaults to DOCMANA_SCOPE").option("--organisation <id>", "Docmana organisation id; defaults to DOCMANA_ORGANISATION").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").action(async (flowId, options) => {
471
640
  await runFlowCommand(flowId, options, env, io, clientFactory, deps);
472
641
  });
473
642
  const config = program.command("config").description(HELP.config).action(() => {
@@ -479,50 +648,77 @@ function buildProgram(env, io, clientFactory, deps) {
479
648
  return program;
480
649
  }
481
650
  async function runFlowCommand(flowId, options, env, io, clientFactory, deps) {
482
- const files = parseFiles(options.files);
483
- const cliConfig = await loadCliConfig(options.config, getCwd(deps), Boolean(options.config));
484
- const clientId = firstNonEmpty(options.clientId, env.DOCMANA_CLIENT_ID, cliConfig.clientId);
485
- const clientSecret = firstNonEmpty(
486
- options.clientSecret,
487
- env.DOCMANA_CLIENT_SECRET,
488
- cliConfig.clientSecret
489
- );
490
- const organisation = firstNonEmpty(
491
- options.organisation,
492
- env.DOCMANA_ORGANISATION,
493
- cliConfig.organisation
494
- );
495
- const apiBaseUrl = firstNonEmpty(options.apiUrl, env.DOCMANA_API_URL, cliConfig.apiBaseUrl);
496
- const tokenEndpoint = firstNonEmpty(
497
- options.tokenUrl,
498
- env.DOCMANA_TOKEN_URL,
499
- cliConfig.tokenEndpoint
500
- );
501
- const scope = firstNonEmpty(options.scope, env.DOCMANA_SCOPE, cliConfig.scope);
502
- if (!clientId)
503
- throw new CliUsageError("Missing client id. Use --client-id or DOCMANA_CLIENT_ID.");
504
- if (!clientSecret) {
505
- throw new CliUsageError("Missing client secret. Use --client-secret or DOCMANA_CLIENT_SECRET.");
506
- }
507
- if (!organisation) {
508
- throw new CliUsageError("Missing organisation. Use --organisation or DOCMANA_ORGANISATION.");
509
- }
510
- const client = clientFactory({
511
- clientId,
512
- clientSecret,
513
- apiBaseUrl,
514
- tokenEndpoint,
515
- scope,
516
- headers: { "X-Selected-Organization": organisation },
517
- tokenCache: createFileTokenCache(resolveTokenCachePath(options.tokenCache, getCwd(deps)))
518
- });
519
- const result = await client.runFlow(flowId, { files: files.map((path) => ({ path })) });
520
- if (options.json) {
521
- io.stdout(`${JSON.stringify(result, null, 2)}
651
+ try {
652
+ const files = parseFiles(options.files);
653
+ const cliConfig = await loadCliConfig(options.config, getCwd(deps), Boolean(options.config));
654
+ const clientId = firstNonEmpty(options.clientId, env.DOCMANA_CLIENT_ID, cliConfig.clientId);
655
+ const clientSecret = firstNonEmpty(
656
+ options.clientSecret,
657
+ env.DOCMANA_CLIENT_SECRET,
658
+ cliConfig.clientSecret
659
+ );
660
+ const organisation = firstNonEmpty(
661
+ options.organisation,
662
+ env.DOCMANA_ORGANISATION,
663
+ cliConfig.organisation
664
+ );
665
+ const apiBaseUrl = firstNonEmpty(options.apiUrl, env.DOCMANA_API_URL, cliConfig.apiBaseUrl);
666
+ const tokenEndpoint = firstNonEmpty(
667
+ options.tokenUrl,
668
+ env.DOCMANA_TOKEN_URL,
669
+ cliConfig.tokenEndpoint
670
+ );
671
+ const scope = firstNonEmpty(options.scope, env.DOCMANA_SCOPE, cliConfig.scope);
672
+ if (!clientId)
673
+ throw new CliUsageError("Missing client id. Use --client-id or DOCMANA_CLIENT_ID.");
674
+ if (!clientSecret) {
675
+ throw new CliUsageError(
676
+ "Missing client secret. Use --client-secret or DOCMANA_CLIENT_SECRET."
677
+ );
678
+ }
679
+ if (!organisation) {
680
+ throw new CliUsageError("Missing organisation. Use --organisation or DOCMANA_ORGANISATION.");
681
+ }
682
+ const client = clientFactory({
683
+ clientId,
684
+ clientSecret,
685
+ apiBaseUrl,
686
+ tokenEndpoint,
687
+ scope,
688
+ headers: { "X-Selected-Organization": organisation },
689
+ tokenCache: createFileTokenCache(resolveTokenCachePath(options.tokenCache, getCwd(deps)))
690
+ });
691
+ const result = await client.runFlow(flowId, {
692
+ files: files.map((path) => ({ path })),
693
+ language: options.language
694
+ });
695
+ if (options.json) {
696
+ io.stdout(`${JSON.stringify(result, null, 2)}
697
+ `);
698
+ return;
699
+ }
700
+ io.stdout(formatHumanResult(result));
701
+ } catch (err) {
702
+ if (options.json) {
703
+ if (err instanceof DocmanaExecutionError && err.result) {
704
+ io.stdout(`${JSON.stringify(err.result, null, 2)}
705
+ `);
706
+ } else {
707
+ const errorPayload = {
708
+ error: err instanceof Error ? err.message : String(err)
709
+ };
710
+ if (err instanceof DocmanaError) {
711
+ errorPayload.code = err.code;
712
+ if (err.status !== void 0) errorPayload.status = err.status;
713
+ if (err.requestId !== void 0) errorPayload.requestId = err.requestId;
714
+ }
715
+ io.stdout(`${JSON.stringify(errorPayload, null, 2)}
522
716
  `);
523
- return;
717
+ }
718
+ throw new CliSilentError(err instanceof CliUsageError ? 2 : 1);
719
+ }
720
+ throw err;
524
721
  }
525
- io.stdout(formatHumanResult(result));
526
722
  }
527
723
  async function loginCommand(options, env, io, deps) {
528
724
  const auth = await resolveAuthConfig(options, env, deps);
@@ -726,31 +922,141 @@ function firstNonEmpty(...values) {
726
922
  }
727
923
  function formatHumanResult(result) {
728
924
  const lines = ["Docmana flow completed", `Status: ${String(result.status ?? "Unknown")}`];
729
- const executionId = result.execution_id ?? result.executionResultId ?? result.execution_result_id;
925
+ const executionId = result.executionId;
730
926
  if (executionId) lines.push(`Execution id: ${String(executionId)}`);
731
- if (Array.isArray(result.results)) lines.push(`Results: ${result.results.length}`);
732
- if (Array.isArray(result.errors)) lines.push(`Errors: ${result.errors.length}`);
733
- const preview = formatResultsPreview(result.results);
927
+ if (result.documents && result.documents.mappings) {
928
+ lines.push(`Results: ${result.documents.mappings.length}`);
929
+ }
930
+ if (result.errors && result.errors.length > 0) {
931
+ lines.push(`Errors: ${result.errors.length}`);
932
+ }
933
+ const previewData = {};
934
+ if (result.documents && result.documents.mappings) {
935
+ for (const mapping of result.documents.mappings.slice(0, 3)) {
936
+ const docKey = mapping.reference;
937
+ previewData[mapping.name] = result.documents[docKey];
938
+ }
939
+ }
940
+ const preview = formatResultsPreview(previewData);
734
941
  if (preview) lines.push("", "Result preview:", preview);
735
942
  lines.push("", "Use --json to print the complete result payload.");
736
943
  return `${lines.join("\n")}
737
944
  `;
738
945
  }
739
946
  function formatResultsPreview(results) {
740
- if (!Array.isArray(results) || results.length === 0) return void 0;
741
- const preview = JSON.stringify(results.slice(0, 3), null, 2);
947
+ if (!results) return void 0;
948
+ const preview = JSON.stringify(results, null, 2);
742
949
  if (!preview) return void 0;
743
950
  return preview.length > 2e3 ? `${preview.slice(0, 2e3)}
744
951
  ...` : preview;
745
952
  }
746
953
  function formatRuntimeError(err) {
954
+ return formatHumanError(err);
955
+ }
956
+ function formatHumanError(err) {
747
957
  if (err instanceof DocmanaExecutionError) {
748
- const details = err.errors.length > 0 ? ` (${err.errors.length} error(s))` : "";
749
- return `${err.message}${details}`;
958
+ const lines = [`Error: ${err.message}`];
959
+ if (err.errors && err.errors.length > 0) {
960
+ lines.push("Global errors:");
961
+ for (const e of err.errors) {
962
+ lines.push(` - ${formatSingleError(e)}`);
963
+ }
964
+ }
965
+ const result = err.result;
966
+ if (result) {
967
+ if (result.documents && result.documents.mappings && result.documents.mappings.length > 0) {
968
+ lines.push("Document execution results:");
969
+ for (const mapping of result.documents.mappings) {
970
+ const docKey = mapping.reference;
971
+ const docName = mapping.name;
972
+ const docResult = result.documents[docKey];
973
+ if (docResult) {
974
+ const status = docResult.status || "Unknown";
975
+ const nodes = [];
976
+ if (docResult.classifications) nodes.push(...Object.values(docResult.classifications));
977
+ if (docResult.metadataExtraction) nodes.push(docResult.metadataExtraction);
978
+ if (docResult.extractions) nodes.push(...Object.values(docResult.extractions));
979
+ if (docResult.validations) nodes.push(...Object.values(docResult.validations));
980
+ const hasNodeFailures = nodes.some(
981
+ (n) => n.status === "Failed" || n.errors && n.errors.length > 0
982
+ );
983
+ if (docResult.status === "Failed" || hasNodeFailures) {
984
+ lines.push(` - Document: ${docName} (Status: ${status})`);
985
+ if (docResult.classifications) {
986
+ for (const [nodeName, node] of Object.entries(docResult.classifications)) {
987
+ if (node.errors && node.errors.length > 0) {
988
+ lines.push(` Classification: ${nodeName} (Status: ${node.status})`);
989
+ for (const nodeErr of node.errors) {
990
+ lines.push(` - ${nodeErr}`);
991
+ }
992
+ }
993
+ }
994
+ }
995
+ if (docResult.metadataExtraction?.errors && docResult.metadataExtraction.errors.length > 0) {
996
+ lines.push(
997
+ ` MetadataExtraction: (Status: ${docResult.metadataExtraction.status})`
998
+ );
999
+ for (const nodeErr of docResult.metadataExtraction.errors) {
1000
+ lines.push(` - ${nodeErr}`);
1001
+ }
1002
+ }
1003
+ if (docResult.extractions) {
1004
+ for (const [nodeName, node] of Object.entries(docResult.extractions)) {
1005
+ if (node.status === "Failed" || node.errors && node.errors.length > 0) {
1006
+ lines.push(` Extraction: ${nodeName} (Status: ${node.status})`);
1007
+ if (node.errors) {
1008
+ for (const nodeErr of node.errors) {
1009
+ lines.push(` - ${nodeErr}`);
1010
+ }
1011
+ }
1012
+ }
1013
+ }
1014
+ }
1015
+ if (docResult.validations) {
1016
+ for (const [nodeName, node] of Object.entries(docResult.validations)) {
1017
+ if (node.status === "Failed" || node.errors && node.errors.length > 0) {
1018
+ lines.push(` Validation: ${nodeName} (Status: ${node.status})`);
1019
+ if (node.errors) {
1020
+ for (const nodeErr of node.errors) {
1021
+ lines.push(` - ${nodeErr}`);
1022
+ }
1023
+ }
1024
+ }
1025
+ }
1026
+ }
1027
+ } else {
1028
+ lines.push(` - Document: ${docName} (Status: ${status})`);
1029
+ }
1030
+ } else {
1031
+ lines.push(` - Document: ${docName} (Status: Unknown)`);
1032
+ }
1033
+ }
1034
+ }
1035
+ }
1036
+ return lines.join("\n");
1037
+ }
1038
+ if (err instanceof DocmanaError) {
1039
+ return `Error: ${err.message} (Code: ${err.code})`;
1040
+ }
1041
+ if (err instanceof Error) {
1042
+ return `Error: ${err.message}`;
1043
+ }
1044
+ return `Error: ${String(err)}`;
1045
+ }
1046
+ function formatSingleError(e) {
1047
+ if (typeof e === "string") return e;
1048
+ if (e && typeof e === "object") {
1049
+ const obj = e;
1050
+ if (typeof obj.msg === "string") return obj.msg;
1051
+ if (typeof obj.message === "string") return obj.message;
1052
+ if (typeof obj.error === "string") return obj.error;
1053
+ try {
1054
+ return JSON.stringify(e);
1055
+ } catch {
1056
+ return String(e);
1057
+ }
750
1058
  }
751
- if (err instanceof DocmanaError) return err.message;
752
- if (err instanceof Error) return err.message;
753
- return "Unexpected error";
1059
+ return String(e);
754
1060
  }
755
1061
  function isDirectRun() {
756
1062
  const entry = process.argv[1];