@okrapdf/cli 0.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/dist/bin.d.ts +1 -0
- package/dist/bin.js +152 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-A6YTW4WL.js +499 -0
- package/dist/chunk-A6YTW4WL.js.map +1 -0
- package/dist/index.d.ts +418 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
find,
|
|
4
|
+
formatFindOutput,
|
|
5
|
+
formatHistoryOutput,
|
|
6
|
+
formatPageOutput,
|
|
7
|
+
formatSearchOutput,
|
|
8
|
+
formatStats,
|
|
9
|
+
formatTablesOutput,
|
|
10
|
+
formatTreeOutput,
|
|
11
|
+
formatVersionsOutput,
|
|
12
|
+
history,
|
|
13
|
+
pageEdit,
|
|
14
|
+
pageGet,
|
|
15
|
+
pageResolve,
|
|
16
|
+
pageVersions,
|
|
17
|
+
search,
|
|
18
|
+
tables,
|
|
19
|
+
tree
|
|
20
|
+
} from "./chunk-A6YTW4WL.js";
|
|
21
|
+
|
|
22
|
+
// src/bin.ts
|
|
23
|
+
import { Command } from "commander";
|
|
24
|
+
import { OkraClient } from "@okrapdf/sdk";
|
|
25
|
+
var program = new Command();
|
|
26
|
+
function getClient() {
|
|
27
|
+
return new OkraClient({
|
|
28
|
+
apiKey: process.env.OKRA_API_KEY,
|
|
29
|
+
baseUrl: process.env.OKRA_BASE_URL || "https://app.okrapdf.com"
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
program.name("okra").description("CLI for OkraPDF document review operations").version("0.1.0");
|
|
33
|
+
program.command("tree <jobId>").description("Show document verification tree").option("-s, --status <status>", "Filter by status (complete|partial|pending|flagged|empty|gap)").option("-e, --entity <type>", "Filter by entity type (table|figure|footnote)").option("-f, --format <format>", "Output format (text|json|markdown)", "text").action(async (jobId, options) => {
|
|
34
|
+
try {
|
|
35
|
+
const client = getClient();
|
|
36
|
+
const result = await tree(client, jobId, {
|
|
37
|
+
status: options.status,
|
|
38
|
+
entity: options.entity
|
|
39
|
+
});
|
|
40
|
+
console.log(formatTreeOutput(result, options.format));
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error("Error:", error.message);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
program.command("find <jobId> <selector>").description("Find entities using jQuery-like selectors").option("-k, --top-k <n>", "Limit results", parseInt).option("-c, --min-confidence <n>", "Minimum confidence (0-1)", parseFloat).option("-p, --pages <range>", "Page range (e.g., 1-10)").option("--sort <by>", "Sort by (confidence|page|type)").option("--stats", "Show aggregate statistics").option("-f, --format <format>", "Output format (text|json|entities|ids)", "text").action(async (jobId, selector, options) => {
|
|
47
|
+
try {
|
|
48
|
+
const client = getClient();
|
|
49
|
+
const pageRange = options.pages ? options.pages.split("-").map(Number) : void 0;
|
|
50
|
+
const result = await find(client, jobId, selector, {
|
|
51
|
+
topK: options.topK,
|
|
52
|
+
minConfidence: options.minConfidence,
|
|
53
|
+
pageRange,
|
|
54
|
+
sortBy: options.sort
|
|
55
|
+
});
|
|
56
|
+
if (options.stats && options.format === "text") {
|
|
57
|
+
console.log(formatStats(result.stats));
|
|
58
|
+
} else {
|
|
59
|
+
console.log(formatFindOutput(result, options.format, options.stats));
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error("Error:", error.message);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
var pageCmd = program.command("page").description("Page content operations");
|
|
67
|
+
pageCmd.command("get <jobId> <pageNum>").description("Get page content").option("-v, --version <n>", "Specific version", parseInt).option("-f, --format <format>", "Output format (text|json|markdown)", "markdown").action(async (jobId, pageNum, options) => {
|
|
68
|
+
try {
|
|
69
|
+
const client = getClient();
|
|
70
|
+
const content = await pageGet(client, jobId, parseInt(pageNum), {
|
|
71
|
+
version: options.version
|
|
72
|
+
});
|
|
73
|
+
console.log(formatPageOutput(content, options.format));
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error("Error:", error.message);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
pageCmd.command("edit <jobId> <pageNum>").description("Edit page content (reads from stdin)").action(async (jobId, pageNum) => {
|
|
80
|
+
try {
|
|
81
|
+
const chunks = [];
|
|
82
|
+
for await (const chunk of process.stdin) {
|
|
83
|
+
chunks.push(chunk);
|
|
84
|
+
}
|
|
85
|
+
const content = Buffer.concat(chunks).toString("utf8");
|
|
86
|
+
const client = getClient();
|
|
87
|
+
const result = await pageEdit(client, jobId, parseInt(pageNum), content);
|
|
88
|
+
console.log(`Saved as version ${result.version}`);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error("Error:", error.message);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
pageCmd.command("resolve <jobId> <pageNum> <resolution>").description("Resolve page verification status").option("-c, --classification <class>", "Classification").option("-r, --reason <reason>", "Reason").action(async (jobId, pageNum, resolution, options) => {
|
|
95
|
+
try {
|
|
96
|
+
const client = getClient();
|
|
97
|
+
const result = await pageResolve(client, jobId, parseInt(pageNum), {
|
|
98
|
+
resolution,
|
|
99
|
+
classification: options.classification,
|
|
100
|
+
reason: options.reason
|
|
101
|
+
});
|
|
102
|
+
console.log(result.success ? "Resolved" : "Failed");
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error("Error:", error.message);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
pageCmd.command("versions <jobId> <pageNum>").description("List page versions").option("-f, --format <format>", "Output format (text|json)", "text").action(async (jobId, pageNum, options) => {
|
|
109
|
+
try {
|
|
110
|
+
const client = getClient();
|
|
111
|
+
const versions = await pageVersions(client, jobId, parseInt(pageNum));
|
|
112
|
+
console.log(formatVersionsOutput(versions, options.format));
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error("Error:", error.message);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
program.command("search <jobId> <query>").description("Search page content").option("-f, --format <format>", "Output format (text|json)", "text").action(async (jobId, query, options) => {
|
|
119
|
+
try {
|
|
120
|
+
const client = getClient();
|
|
121
|
+
const result = await search(client, jobId, query);
|
|
122
|
+
console.log(formatSearchOutput(result, options.format));
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error("Error:", error.message);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
program.command("tables <jobId>").description("List extracted tables").option("-p, --page <n>", "Filter by page", parseInt).option("-s, --status <status>", "Filter by status (pending|verified|flagged|rejected)").option("-f, --format <format>", "Output format (text|json|markdown)", "text").action(async (jobId, options) => {
|
|
129
|
+
try {
|
|
130
|
+
const client = getClient();
|
|
131
|
+
const result = await tables(client, jobId, {
|
|
132
|
+
page: options.page,
|
|
133
|
+
status: options.status
|
|
134
|
+
});
|
|
135
|
+
console.log(formatTablesOutput(result, options.format));
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error("Error:", error.message);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
program.command("history <jobId>").description("Show verification history").option("-l, --limit <n>", "Limit entries", parseInt, 50).option("-f, --format <format>", "Output format (text|json)", "text").action(async (jobId, options) => {
|
|
142
|
+
try {
|
|
143
|
+
const client = getClient();
|
|
144
|
+
const result = await history(client, jobId, { limit: options.limit });
|
|
145
|
+
console.log(formatHistoryOutput(result, options.format));
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error("Error:", error.message);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
program.parse();
|
|
152
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * okra CLI - Command line interface for OkraPDF review operations\n *\n * Usage:\n * okra tree <jobId> # Document verification tree\n * okra find <jobId> <selector> # jQuery-like entity search\n * okra page get <jobId> <pageNum> # Get page content\n * okra page edit <jobId> <pageNum> <content> # Edit page content\n * okra page resolve <jobId> <pageNum> <res> # Resolve page status\n * okra search <jobId> <query> # Full-text search\n * okra tables <jobId> # List tables\n * okra history <jobId> # Verification history\n */\n\nimport { Command } from 'commander';\nimport { OkraClient } from '@okrapdf/sdk';\nimport {\n tree,\n formatTreeOutput,\n find,\n formatFindOutput,\n formatStats,\n pageGet,\n pageEdit,\n pageResolve,\n pageVersions,\n formatPageOutput,\n formatVersionsOutput,\n search,\n formatSearchOutput,\n tables,\n formatTablesOutput,\n history,\n formatHistoryOutput,\n} from './commands';\n\nconst program = new Command();\n\n// Create client (uses OKRA_API_KEY env var by default)\nfunction getClient(): OkraClient {\n return new OkraClient({\n apiKey: process.env.OKRA_API_KEY,\n baseUrl: process.env.OKRA_BASE_URL || 'https://app.okrapdf.com',\n });\n}\n\nprogram\n .name('okra')\n .description('CLI for OkraPDF document review operations')\n .version('0.1.0');\n\n// ============================================================================\n// tree command - Document verification tree\n// ============================================================================\nprogram\n .command('tree <jobId>')\n .description('Show document verification tree')\n .option('-s, --status <status>', 'Filter by status (complete|partial|pending|flagged|empty|gap)')\n .option('-e, --entity <type>', 'Filter by entity type (table|figure|footnote)')\n .option('-f, --format <format>', 'Output format (text|json|markdown)', 'text')\n .action(async (jobId, options) => {\n try {\n const client = getClient();\n const result = await tree(client, jobId, {\n status: options.status,\n entity: options.entity,\n });\n console.log(formatTreeOutput(result, options.format));\n } catch (error: any) {\n console.error('Error:', error.message);\n process.exit(1);\n }\n });\n\n// ============================================================================\n// find command - jQuery-like entity search\n// ============================================================================\nprogram\n .command('find <jobId> <selector>')\n .description('Find entities using jQuery-like selectors')\n .option('-k, --top-k <n>', 'Limit results', parseInt)\n .option('-c, --min-confidence <n>', 'Minimum confidence (0-1)', parseFloat)\n .option('-p, --pages <range>', 'Page range (e.g., 1-10)')\n .option('--sort <by>', 'Sort by (confidence|page|type)')\n .option('--stats', 'Show aggregate statistics')\n .option('-f, --format <format>', 'Output format (text|json|entities|ids)', 'text')\n .action(async (jobId, selector, options) => {\n try {\n const client = getClient();\n const pageRange = options.pages\n ? options.pages.split('-').map(Number) as [number, number]\n : undefined;\n\n const result = await find(client, jobId, selector, {\n topK: options.topK,\n minConfidence: options.minConfidence,\n pageRange,\n sortBy: options.sort,\n });\n\n if (options.stats && options.format === 'text') {\n console.log(formatStats(result.stats));\n } else {\n console.log(formatFindOutput(result, options.format, options.stats));\n }\n } catch (error: any) {\n console.error('Error:', error.message);\n process.exit(1);\n }\n });\n\n// ============================================================================\n// page command - Page content operations\n// ============================================================================\nconst pageCmd = program.command('page').description('Page content operations');\n\npageCmd\n .command('get <jobId> <pageNum>')\n .description('Get page content')\n .option('-v, --version <n>', 'Specific version', parseInt)\n .option('-f, --format <format>', 'Output format (text|json|markdown)', 'markdown')\n .action(async (jobId, pageNum, options) => {\n try {\n const client = getClient();\n const content = await pageGet(client, jobId, parseInt(pageNum), {\n version: options.version,\n });\n console.log(formatPageOutput(content, options.format));\n } catch (error: any) {\n console.error('Error:', error.message);\n process.exit(1);\n }\n });\n\npageCmd\n .command('edit <jobId> <pageNum>')\n .description('Edit page content (reads from stdin)')\n .action(async (jobId, pageNum) => {\n try {\n // Read content from stdin\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk);\n }\n const content = Buffer.concat(chunks).toString('utf8');\n\n const client = getClient();\n const result = await pageEdit(client, jobId, parseInt(pageNum), content);\n console.log(`Saved as version ${result.version}`);\n } catch (error: any) {\n console.error('Error:', error.message);\n process.exit(1);\n }\n });\n\npageCmd\n .command('resolve <jobId> <pageNum> <resolution>')\n .description('Resolve page verification status')\n .option('-c, --classification <class>', 'Classification')\n .option('-r, --reason <reason>', 'Reason')\n .action(async (jobId, pageNum, resolution, options) => {\n try {\n const client = getClient();\n const result = await pageResolve(client, jobId, parseInt(pageNum), {\n resolution,\n classification: options.classification,\n reason: options.reason,\n });\n console.log(result.success ? 'Resolved' : 'Failed');\n } catch (error: any) {\n console.error('Error:', error.message);\n process.exit(1);\n }\n });\n\npageCmd\n .command('versions <jobId> <pageNum>')\n .description('List page versions')\n .option('-f, --format <format>', 'Output format (text|json)', 'text')\n .action(async (jobId, pageNum, options) => {\n try {\n const client = getClient();\n const versions = await pageVersions(client, jobId, parseInt(pageNum));\n console.log(formatVersionsOutput(versions, options.format));\n } catch (error: any) {\n console.error('Error:', error.message);\n process.exit(1);\n }\n });\n\n// ============================================================================\n// search command - Full-text search\n// ============================================================================\nprogram\n .command('search <jobId> <query>')\n .description('Search page content')\n .option('-f, --format <format>', 'Output format (text|json)', 'text')\n .action(async (jobId, query, options) => {\n try {\n const client = getClient();\n const result = await search(client, jobId, query);\n console.log(formatSearchOutput(result, options.format));\n } catch (error: any) {\n console.error('Error:', error.message);\n process.exit(1);\n }\n });\n\n// ============================================================================\n// tables command - List tables\n// ============================================================================\nprogram\n .command('tables <jobId>')\n .description('List extracted tables')\n .option('-p, --page <n>', 'Filter by page', parseInt)\n .option('-s, --status <status>', 'Filter by status (pending|verified|flagged|rejected)')\n .option('-f, --format <format>', 'Output format (text|json|markdown)', 'text')\n .action(async (jobId, options) => {\n try {\n const client = getClient();\n const result = await tables(client, jobId, {\n page: options.page,\n status: options.status,\n });\n console.log(formatTablesOutput(result, options.format));\n } catch (error: any) {\n console.error('Error:', error.message);\n process.exit(1);\n }\n });\n\n// ============================================================================\n// history command - Verification audit trail\n// ============================================================================\nprogram\n .command('history <jobId>')\n .description('Show verification history')\n .option('-l, --limit <n>', 'Limit entries', parseInt, 50)\n .option('-f, --format <format>', 'Output format (text|json)', 'text')\n .action(async (jobId, options) => {\n try {\n const client = getClient();\n const result = await history(client, jobId, { limit: options.limit });\n console.log(formatHistoryOutput(result, options.format));\n } catch (error: any) {\n console.error('Error:', error.message);\n process.exit(1);\n }\n });\n\nprogram.parse();\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
// src/commands/tree.ts
|
|
2
|
+
async function tree(client, jobId, options = {}) {
|
|
3
|
+
const treeData = await client.ocrJobs.getVerificationTree(jobId);
|
|
4
|
+
let filteredPages = treeData.pages.map((p) => p.page);
|
|
5
|
+
if (options.status) {
|
|
6
|
+
filteredPages = treeData.pages.filter((p) => p.status === options.status).map((p) => p.page);
|
|
7
|
+
}
|
|
8
|
+
if (options.entity) {
|
|
9
|
+
const entitiesData = await client.ocrJobs.getEntities(jobId, options.entity);
|
|
10
|
+
const pagesWithEntity = new Set(entitiesData.entities.map((e) => e.page));
|
|
11
|
+
filteredPages = filteredPages.filter((p) => pagesWithEntity.has(p));
|
|
12
|
+
}
|
|
13
|
+
return { tree: treeData, filteredPages };
|
|
14
|
+
}
|
|
15
|
+
function formatTreeOutput(result, format = "text") {
|
|
16
|
+
if (format === "json") {
|
|
17
|
+
return JSON.stringify(result, null, 2);
|
|
18
|
+
}
|
|
19
|
+
const { tree: treeData, filteredPages } = result;
|
|
20
|
+
const lines = [];
|
|
21
|
+
if (format === "markdown") {
|
|
22
|
+
lines.push(`# Verification Tree: ${treeData.jobId}`);
|
|
23
|
+
lines.push("");
|
|
24
|
+
lines.push(`**Total Pages:** ${treeData.totalPages}`);
|
|
25
|
+
lines.push("");
|
|
26
|
+
lines.push("## Summary");
|
|
27
|
+
lines.push("| Status | Count |");
|
|
28
|
+
lines.push("|--------|-------|");
|
|
29
|
+
lines.push(`| Complete | ${treeData.summary.complete} |`);
|
|
30
|
+
lines.push(`| Partial | ${treeData.summary.partial} |`);
|
|
31
|
+
lines.push(`| Pending | ${treeData.summary.pending} |`);
|
|
32
|
+
lines.push(`| Flagged | ${treeData.summary.flagged} |`);
|
|
33
|
+
lines.push(`| Empty | ${treeData.summary.empty} |`);
|
|
34
|
+
lines.push(`| Gap | ${treeData.summary.gap} |`);
|
|
35
|
+
lines.push("");
|
|
36
|
+
lines.push("## Pages");
|
|
37
|
+
lines.push("| Page | Status | Total | Verified | Pending | Flagged |");
|
|
38
|
+
lines.push("|------|--------|-------|----------|---------|---------|");
|
|
39
|
+
for (const page of treeData.pages) {
|
|
40
|
+
if (filteredPages.includes(page.page)) {
|
|
41
|
+
lines.push(
|
|
42
|
+
`| ${page.page} | ${page.status} | ${page.total} | ${page.verified} | ${page.pending} | ${page.flagged} |`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
lines.push(`Verification Tree: ${treeData.jobId}`);
|
|
48
|
+
lines.push(`Total Pages: ${treeData.totalPages}`);
|
|
49
|
+
lines.push("");
|
|
50
|
+
lines.push("Summary:");
|
|
51
|
+
lines.push(` Complete: ${treeData.summary.complete}`);
|
|
52
|
+
lines.push(` Partial: ${treeData.summary.partial}`);
|
|
53
|
+
lines.push(` Pending: ${treeData.summary.pending}`);
|
|
54
|
+
lines.push(` Flagged: ${treeData.summary.flagged}`);
|
|
55
|
+
lines.push(` Empty: ${treeData.summary.empty}`);
|
|
56
|
+
lines.push(` Gap: ${treeData.summary.gap}`);
|
|
57
|
+
lines.push("");
|
|
58
|
+
lines.push("Pages:");
|
|
59
|
+
for (const page of treeData.pages) {
|
|
60
|
+
if (filteredPages.includes(page.page)) {
|
|
61
|
+
const statusIcon = getStatusIcon(page.status);
|
|
62
|
+
const counts = `[${page.verified}/${page.total}]`;
|
|
63
|
+
const flags = page.flagged > 0 ? ` (${page.flagged} flagged)` : "";
|
|
64
|
+
const gaps = page.hasCoverageGaps ? " [GAP]" : "";
|
|
65
|
+
lines.push(` ${statusIcon} p${page.page.toString().padStart(3)} ${counts}${flags}${gaps}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return lines.join("\n");
|
|
70
|
+
}
|
|
71
|
+
function getStatusIcon(status) {
|
|
72
|
+
switch (status) {
|
|
73
|
+
case "complete":
|
|
74
|
+
return "\u2713";
|
|
75
|
+
case "partial":
|
|
76
|
+
return "\u25D0";
|
|
77
|
+
case "pending":
|
|
78
|
+
return "\u25CB";
|
|
79
|
+
case "flagged":
|
|
80
|
+
return "\u2691";
|
|
81
|
+
case "empty":
|
|
82
|
+
return "\xB7";
|
|
83
|
+
case "gap":
|
|
84
|
+
return "!";
|
|
85
|
+
case "error":
|
|
86
|
+
return "\u2717";
|
|
87
|
+
default:
|
|
88
|
+
return "?";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/query-engine.ts
|
|
93
|
+
function parseSelector(selector) {
|
|
94
|
+
const parts = { types: [] };
|
|
95
|
+
if (selector.includes(",") && !selector.includes("[")) {
|
|
96
|
+
const segments = selector.split(",").map((s) => s.trim());
|
|
97
|
+
for (const seg of segments) {
|
|
98
|
+
const subParts = parseSelector(seg);
|
|
99
|
+
parts.types.push(...subParts.types);
|
|
100
|
+
}
|
|
101
|
+
return parts;
|
|
102
|
+
}
|
|
103
|
+
const typeMatches = selector.match(/\.([a-zA-Z][a-zA-Z0-9_]*)/g);
|
|
104
|
+
if (typeMatches) {
|
|
105
|
+
parts.types = typeMatches.map((m) => m.slice(1));
|
|
106
|
+
}
|
|
107
|
+
const idMatch = selector.match(/#([\w-]+)/);
|
|
108
|
+
if (idMatch) {
|
|
109
|
+
parts.id = idMatch[1];
|
|
110
|
+
}
|
|
111
|
+
const pageMatch = selector.match(/:pages?\((\d+)(?:-(\d+))?\)/);
|
|
112
|
+
if (pageMatch) {
|
|
113
|
+
if (pageMatch[2]) {
|
|
114
|
+
parts.pageFilter = {
|
|
115
|
+
type: "range",
|
|
116
|
+
value: [parseInt(pageMatch[1], 10), parseInt(pageMatch[2], 10)]
|
|
117
|
+
};
|
|
118
|
+
} else {
|
|
119
|
+
parts.pageFilter = {
|
|
120
|
+
type: "single",
|
|
121
|
+
value: parseInt(pageMatch[1], 10)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const confMatch = selector.match(/\[confidence(>=?|<=?|>|<)(\d+\.?\d*)\]/);
|
|
126
|
+
if (confMatch) {
|
|
127
|
+
parts.confidenceFilter = {
|
|
128
|
+
op: confMatch[1],
|
|
129
|
+
value: parseFloat(confMatch[2])
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const verifyMatch = selector.match(/\[(?:verified|status)=(\w+)\]/);
|
|
133
|
+
if (verifyMatch) {
|
|
134
|
+
const val = verifyMatch[1].toLowerCase();
|
|
135
|
+
if (val === "true") {
|
|
136
|
+
parts.verificationFilter = "verified";
|
|
137
|
+
} else if (["pending", "verified", "flagged", "rejected"].includes(val)) {
|
|
138
|
+
parts.verificationFilter = val;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const containsMatch = selector.match(/:contains\(([^)]+)\)/);
|
|
142
|
+
if (containsMatch) {
|
|
143
|
+
parts.textContains = containsMatch[1];
|
|
144
|
+
}
|
|
145
|
+
if (parts.types.length === 0 && selector.includes("*")) {
|
|
146
|
+
parts.types = ["table", "figure", "footnote", "summary", "signature", "paragraph"];
|
|
147
|
+
}
|
|
148
|
+
return parts;
|
|
149
|
+
}
|
|
150
|
+
function filterEntities(entities, parts) {
|
|
151
|
+
return entities.filter((entity) => {
|
|
152
|
+
if (parts.types.length > 0 && !parts.types.includes(entity.type)) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if (parts.id && entity.id !== parts.id) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
if (parts.pageFilter) {
|
|
159
|
+
if (parts.pageFilter.type === "single") {
|
|
160
|
+
if (entity.page !== parts.pageFilter.value) return false;
|
|
161
|
+
} else {
|
|
162
|
+
const [start, end] = parts.pageFilter.value;
|
|
163
|
+
if (entity.page < start || entity.page > end) return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (parts.confidenceFilter && entity.confidence !== void 0) {
|
|
167
|
+
const { op, value } = parts.confidenceFilter;
|
|
168
|
+
switch (op) {
|
|
169
|
+
case ">":
|
|
170
|
+
if (!(entity.confidence > value)) return false;
|
|
171
|
+
break;
|
|
172
|
+
case ">=":
|
|
173
|
+
if (!(entity.confidence >= value)) return false;
|
|
174
|
+
break;
|
|
175
|
+
case "<":
|
|
176
|
+
if (!(entity.confidence < value)) return false;
|
|
177
|
+
break;
|
|
178
|
+
case "<=":
|
|
179
|
+
if (!(entity.confidence <= value)) return false;
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (parts.verificationFilter && entity.verificationStatus !== parts.verificationFilter) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
if (parts.textContains && entity.title) {
|
|
187
|
+
if (!entity.title.toLowerCase().includes(parts.textContains.toLowerCase())) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function executeQuery(entities, selector, options = {}) {
|
|
195
|
+
const parts = parseSelector(selector);
|
|
196
|
+
if (options.minConfidence !== void 0) {
|
|
197
|
+
parts.confidenceFilter = { op: ">=", value: options.minConfidence };
|
|
198
|
+
}
|
|
199
|
+
if (options.pageRange) {
|
|
200
|
+
parts.pageFilter = { type: "range", value: options.pageRange };
|
|
201
|
+
}
|
|
202
|
+
let results = filterEntities(entities, parts);
|
|
203
|
+
if (options.sortBy) {
|
|
204
|
+
results = [...results].sort((a, b) => {
|
|
205
|
+
switch (options.sortBy) {
|
|
206
|
+
case "confidence":
|
|
207
|
+
return (b.confidence ?? 0) - (a.confidence ?? 0);
|
|
208
|
+
case "page":
|
|
209
|
+
return a.page - b.page;
|
|
210
|
+
case "type":
|
|
211
|
+
return a.type.localeCompare(b.type);
|
|
212
|
+
default:
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
const stats = calculateStats(results);
|
|
218
|
+
if (options.topK && options.topK > 0) {
|
|
219
|
+
results = results.slice(0, options.topK);
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
entities: results,
|
|
223
|
+
total: results.length,
|
|
224
|
+
stats
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function calculateStats(entities) {
|
|
228
|
+
const byType = {};
|
|
229
|
+
const byPage = {};
|
|
230
|
+
let totalConfidence = 0;
|
|
231
|
+
let minConfidence = Infinity;
|
|
232
|
+
let maxConfidence = -Infinity;
|
|
233
|
+
let confidenceCount = 0;
|
|
234
|
+
for (const entity of entities) {
|
|
235
|
+
byType[entity.type] = (byType[entity.type] || 0) + 1;
|
|
236
|
+
byPage[entity.page] = (byPage[entity.page] || 0) + 1;
|
|
237
|
+
if (entity.confidence !== void 0) {
|
|
238
|
+
totalConfidence += entity.confidence;
|
|
239
|
+
confidenceCount++;
|
|
240
|
+
if (entity.confidence < minConfidence) minConfidence = entity.confidence;
|
|
241
|
+
if (entity.confidence > maxConfidence) maxConfidence = entity.confidence;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
total: entities.length,
|
|
246
|
+
byType,
|
|
247
|
+
byPage,
|
|
248
|
+
avgConfidence: confidenceCount > 0 ? totalConfidence / confidenceCount : 0,
|
|
249
|
+
minConfidence: minConfidence === Infinity ? 0 : minConfidence,
|
|
250
|
+
maxConfidence: maxConfidence === -Infinity ? 0 : maxConfidence
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// src/commands/find.ts
|
|
255
|
+
async function find(client, jobId, selector, options = {}) {
|
|
256
|
+
const entitiesData = await client.ocrJobs.getEntities(jobId);
|
|
257
|
+
return executeQuery(entitiesData.entities, selector, options);
|
|
258
|
+
}
|
|
259
|
+
function formatFindOutput(result, format = "text", showStats = false) {
|
|
260
|
+
if (format === "json") {
|
|
261
|
+
return JSON.stringify(showStats ? result : result.entities, null, 2);
|
|
262
|
+
}
|
|
263
|
+
if (format === "ids") {
|
|
264
|
+
return result.entities.map((e) => e.id).join("\n");
|
|
265
|
+
}
|
|
266
|
+
if (format === "entities") {
|
|
267
|
+
return result.entities.map((e) => `${e.type} ${e.page} ${e.id} ${e.title || ""}`).join("\n");
|
|
268
|
+
}
|
|
269
|
+
const lines = [];
|
|
270
|
+
lines.push(`Found ${result.total} entities`);
|
|
271
|
+
lines.push("");
|
|
272
|
+
if (showStats) {
|
|
273
|
+
lines.push("Stats:");
|
|
274
|
+
lines.push(` By Type:`);
|
|
275
|
+
for (const [type, count] of Object.entries(result.stats.byType)) {
|
|
276
|
+
lines.push(` ${type}: ${count}`);
|
|
277
|
+
}
|
|
278
|
+
lines.push(` Confidence: avg=${result.stats.avgConfidence.toFixed(2)}, min=${result.stats.minConfidence.toFixed(2)}, max=${result.stats.maxConfidence.toFixed(2)}`);
|
|
279
|
+
lines.push(` Pages: ${Object.keys(result.stats.byPage).length}`);
|
|
280
|
+
lines.push("");
|
|
281
|
+
}
|
|
282
|
+
lines.push("Entities:");
|
|
283
|
+
for (const entity of result.entities) {
|
|
284
|
+
const conf = entity.confidence !== void 0 ? ` (${(entity.confidence * 100).toFixed(0)}%)` : "";
|
|
285
|
+
const title = entity.title ? ` "${entity.title.slice(0, 40)}${entity.title.length > 40 ? "..." : ""}"` : "";
|
|
286
|
+
lines.push(` [p${entity.page}] ${entity.type}${title}${conf}`);
|
|
287
|
+
}
|
|
288
|
+
return lines.join("\n");
|
|
289
|
+
}
|
|
290
|
+
function formatStats(stats) {
|
|
291
|
+
const lines = [];
|
|
292
|
+
lines.push(`Total: ${stats.total}`);
|
|
293
|
+
lines.push("");
|
|
294
|
+
lines.push("By Type:");
|
|
295
|
+
for (const [type, count] of Object.entries(stats.byType)) {
|
|
296
|
+
const pct = (count / stats.total * 100).toFixed(1);
|
|
297
|
+
lines.push(` ${type.padEnd(12)} ${count.toString().padStart(4)} (${pct}%)`);
|
|
298
|
+
}
|
|
299
|
+
lines.push("");
|
|
300
|
+
lines.push("By Page:");
|
|
301
|
+
const pageCounts = Object.entries(stats.byPage).sort((a, b) => parseInt(a[0]) - parseInt(b[0])).slice(0, 20);
|
|
302
|
+
for (const [page, count] of pageCounts) {
|
|
303
|
+
lines.push(` p${page.padStart(3)}: ${"\u2588".repeat(Math.min(count, 40))} ${count}`);
|
|
304
|
+
}
|
|
305
|
+
if (Object.keys(stats.byPage).length > 20) {
|
|
306
|
+
lines.push(` ... and ${Object.keys(stats.byPage).length - 20} more pages`);
|
|
307
|
+
}
|
|
308
|
+
lines.push("");
|
|
309
|
+
lines.push("Confidence:");
|
|
310
|
+
lines.push(` Average: ${(stats.avgConfidence * 100).toFixed(1)}%`);
|
|
311
|
+
lines.push(` Min: ${(stats.minConfidence * 100).toFixed(1)}%`);
|
|
312
|
+
lines.push(` Max: ${(stats.maxConfidence * 100).toFixed(1)}%`);
|
|
313
|
+
return lines.join("\n");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/commands/page.ts
|
|
317
|
+
async function pageGet(client, jobId, pageNum, options = {}) {
|
|
318
|
+
if (options.version) {
|
|
319
|
+
return client.ocrJobs.getPageVersionContent(jobId, pageNum, options.version);
|
|
320
|
+
}
|
|
321
|
+
return client.ocrJobs.getPageContent(jobId, pageNum);
|
|
322
|
+
}
|
|
323
|
+
async function pageEdit(client, jobId, pageNum, content) {
|
|
324
|
+
const result = await client.ocrJobs.savePageContent(jobId, pageNum, content);
|
|
325
|
+
return { success: result.success, version: result.version };
|
|
326
|
+
}
|
|
327
|
+
async function pageResolve(client, jobId, pageNum, options) {
|
|
328
|
+
return client.ocrJobs.resolvePageStatus(jobId, pageNum, options);
|
|
329
|
+
}
|
|
330
|
+
async function pageVersions(client, jobId, pageNum) {
|
|
331
|
+
return client.ocrJobs.listPageVersions(jobId, pageNum);
|
|
332
|
+
}
|
|
333
|
+
function formatPageOutput(content, format = "markdown") {
|
|
334
|
+
if (format === "json") {
|
|
335
|
+
return JSON.stringify(content, null, 2);
|
|
336
|
+
}
|
|
337
|
+
if (format === "markdown") {
|
|
338
|
+
return content.content;
|
|
339
|
+
}
|
|
340
|
+
const lines = [];
|
|
341
|
+
lines.push(`Page ${content.page}`);
|
|
342
|
+
if (content.version) {
|
|
343
|
+
lines.push(`Version: ${content.version}`);
|
|
344
|
+
}
|
|
345
|
+
lines.push(`Length: ${content.content.length} chars`);
|
|
346
|
+
lines.push("");
|
|
347
|
+
lines.push("---");
|
|
348
|
+
lines.push(content.content);
|
|
349
|
+
return lines.join("\n");
|
|
350
|
+
}
|
|
351
|
+
function formatVersionsOutput(versions, format = "text") {
|
|
352
|
+
if (format === "json") {
|
|
353
|
+
return JSON.stringify(versions, null, 2);
|
|
354
|
+
}
|
|
355
|
+
const lines = [];
|
|
356
|
+
lines.push(`Page ${versions.page} - ${versions.versions.length} versions`);
|
|
357
|
+
lines.push(`Current: v${versions.current_version}`);
|
|
358
|
+
lines.push("");
|
|
359
|
+
for (const v of versions.versions) {
|
|
360
|
+
const current = v.version === versions.current_version ? " *" : "";
|
|
361
|
+
const date = v.created_at ? new Date(v.created_at).toLocaleString() : "unknown";
|
|
362
|
+
lines.push(` v${v.version}${current} [${v.edit_source}] ${date}`);
|
|
363
|
+
if (v.preview) {
|
|
364
|
+
lines.push(` "${v.preview.slice(0, 60)}${v.preview.length > 60 ? "..." : ""}"`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return lines.join("\n");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/commands/search.ts
|
|
371
|
+
async function search(client, jobId, query) {
|
|
372
|
+
return client.ocrJobs.search(jobId, query);
|
|
373
|
+
}
|
|
374
|
+
function formatSearchOutput(result, format = "text") {
|
|
375
|
+
if (format === "json") {
|
|
376
|
+
return JSON.stringify(result, null, 2);
|
|
377
|
+
}
|
|
378
|
+
const lines = [];
|
|
379
|
+
lines.push(`Search: "${result.query}"`);
|
|
380
|
+
lines.push(`Found ${result.total_matches} matches in ${result.results.length} pages`);
|
|
381
|
+
lines.push("");
|
|
382
|
+
for (const r of result.results) {
|
|
383
|
+
const source = r.match_source ? ` [${r.match_source}]` : "";
|
|
384
|
+
lines.push(`p${r.page.toString().padStart(3)} (${r.match_count} matches)${source}`);
|
|
385
|
+
if (r.snippet) {
|
|
386
|
+
lines.push(` "${r.snippet.slice(0, 80)}${r.snippet.length > 80 ? "..." : ""}"`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return lines.join("\n");
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/commands/tables.ts
|
|
393
|
+
async function tables(client, jobId, options = {}) {
|
|
394
|
+
const result = await client.ocrJobs.getTables(jobId, options.page);
|
|
395
|
+
if (options.status) {
|
|
396
|
+
result.tables = result.tables.filter((t) => t.verification_status === options.status);
|
|
397
|
+
}
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
function formatTablesOutput(result, format = "text") {
|
|
401
|
+
if (format === "json") {
|
|
402
|
+
return JSON.stringify(result, null, 2);
|
|
403
|
+
}
|
|
404
|
+
if (format === "markdown") {
|
|
405
|
+
return result.tables.map((t) => {
|
|
406
|
+
return `## Table (p${t.page_number})
|
|
407
|
+
|
|
408
|
+
${t.markdown}`;
|
|
409
|
+
}).join("\n\n---\n\n");
|
|
410
|
+
}
|
|
411
|
+
const lines = [];
|
|
412
|
+
lines.push(`Tables: ${result.tables.length}`);
|
|
413
|
+
lines.push("");
|
|
414
|
+
const byPage = /* @__PURE__ */ new Map();
|
|
415
|
+
for (const t of result.tables) {
|
|
416
|
+
const pageGroup = byPage.get(t.page_number) || [];
|
|
417
|
+
pageGroup.push(t);
|
|
418
|
+
byPage.set(t.page_number, pageGroup);
|
|
419
|
+
}
|
|
420
|
+
for (const [page, pageTables] of [...byPage.entries()].sort((a, b) => a[0] - b[0])) {
|
|
421
|
+
lines.push(`Page ${page}:`);
|
|
422
|
+
for (const t of pageTables) {
|
|
423
|
+
const status = getStatusIcon2(t.verification_status);
|
|
424
|
+
const conf = t.confidence !== null ? ` (${(t.confidence * 100).toFixed(0)}%)` : "";
|
|
425
|
+
const preview = t.markdown.split("\n")[0].slice(0, 50);
|
|
426
|
+
lines.push(` ${status} ${t.id}${conf}`);
|
|
427
|
+
lines.push(` ${preview}${t.markdown.length > 50 ? "..." : ""}`);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return lines.join("\n");
|
|
431
|
+
}
|
|
432
|
+
function getStatusIcon2(status) {
|
|
433
|
+
switch (status) {
|
|
434
|
+
case "verified":
|
|
435
|
+
return "\u2713";
|
|
436
|
+
case "pending":
|
|
437
|
+
return "\u25CB";
|
|
438
|
+
case "flagged":
|
|
439
|
+
return "\u2691";
|
|
440
|
+
case "rejected":
|
|
441
|
+
return "\u2717";
|
|
442
|
+
default:
|
|
443
|
+
return "?";
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// src/commands/history.ts
|
|
448
|
+
async function history(client, jobId, options = {}) {
|
|
449
|
+
return client.ocrJobs.getHistory(jobId, options.limit || 50);
|
|
450
|
+
}
|
|
451
|
+
function formatHistoryOutput(result, format = "text") {
|
|
452
|
+
if (format === "json") {
|
|
453
|
+
return JSON.stringify(result, null, 2);
|
|
454
|
+
}
|
|
455
|
+
const lines = [];
|
|
456
|
+
lines.push(`History: ${result.history.length} entries`);
|
|
457
|
+
lines.push("");
|
|
458
|
+
for (const entry of result.history) {
|
|
459
|
+
const date = new Date(entry.createdAt).toLocaleString();
|
|
460
|
+
const page = entry.pageNum !== null ? ` p${entry.pageNum}` : "";
|
|
461
|
+
const transition = entry.transitionName || `${entry.previousState || "?"} -> ${entry.state}`;
|
|
462
|
+
const by = entry.triggeredByName || entry.triggeredBy || "system";
|
|
463
|
+
lines.push(`[${date}] ${entry.entityType}${page}`);
|
|
464
|
+
lines.push(` ${transition} by ${by}`);
|
|
465
|
+
if (entry.reason) {
|
|
466
|
+
lines.push(` Reason: ${entry.reason}`);
|
|
467
|
+
}
|
|
468
|
+
if (entry.resolution) {
|
|
469
|
+
lines.push(` Resolution: ${entry.resolution}`);
|
|
470
|
+
}
|
|
471
|
+
lines.push("");
|
|
472
|
+
}
|
|
473
|
+
return lines.join("\n");
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export {
|
|
477
|
+
tree,
|
|
478
|
+
formatTreeOutput,
|
|
479
|
+
parseSelector,
|
|
480
|
+
filterEntities,
|
|
481
|
+
executeQuery,
|
|
482
|
+
calculateStats,
|
|
483
|
+
find,
|
|
484
|
+
formatFindOutput,
|
|
485
|
+
formatStats,
|
|
486
|
+
pageGet,
|
|
487
|
+
pageEdit,
|
|
488
|
+
pageResolve,
|
|
489
|
+
pageVersions,
|
|
490
|
+
formatPageOutput,
|
|
491
|
+
formatVersionsOutput,
|
|
492
|
+
search,
|
|
493
|
+
formatSearchOutput,
|
|
494
|
+
tables,
|
|
495
|
+
formatTablesOutput,
|
|
496
|
+
history,
|
|
497
|
+
formatHistoryOutput
|
|
498
|
+
};
|
|
499
|
+
//# sourceMappingURL=chunk-A6YTW4WL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/tree.ts","../src/query-engine.ts","../src/commands/find.ts","../src/commands/page.ts","../src/commands/search.ts","../src/commands/tables.ts","../src/commands/history.ts"],"sourcesContent":["/**\n * okra tree - Document verification tree command\n *\n * Maps to: Left panel of review page\n * Shows page-level verification status and entity counts.\n *\n * Usage:\n * okra tree <jobId>\n * okra tree <jobId> --status pending\n * okra tree <jobId> --entity table\n * okra tree <jobId> --format json\n */\n\nimport { OkraClient } from '@okrapdf/sdk';\nimport type {\n VerificationPageStatus,\n EntityType,\n VerificationTreeResponse,\n} from '@okrapdf/sdk';\n\nexport interface TreeOptions {\n status?: VerificationPageStatus;\n entity?: EntityType;\n format?: 'text' | 'json' | 'markdown';\n}\n\nexport interface TreeResult {\n tree: VerificationTreeResponse;\n filteredPages: number[];\n}\n\n/**\n * Get the verification tree for a job.\n */\nexport async function tree(\n client: OkraClient,\n jobId: string,\n options: TreeOptions = {}\n): Promise<TreeResult> {\n const treeData = await client.ocrJobs.getVerificationTree(jobId);\n\n // Filter pages if status filter specified\n let filteredPages = treeData.pages.map((p) => p.page);\n\n if (options.status) {\n filteredPages = treeData.pages\n .filter((p) => p.status === options.status)\n .map((p) => p.page);\n }\n\n // If entity filter specified, we need to fetch entities and filter\n if (options.entity) {\n const entitiesData = await client.ocrJobs.getEntities(jobId, options.entity);\n const pagesWithEntity = new Set(entitiesData.entities.map((e) => e.page));\n filteredPages = filteredPages.filter((p) => pagesWithEntity.has(p));\n }\n\n return { tree: treeData, filteredPages };\n}\n\n/**\n * Format tree result for output.\n */\nexport function formatTreeOutput(\n result: TreeResult,\n format: 'text' | 'json' | 'markdown' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n const { tree: treeData, filteredPages } = result;\n const lines: string[] = [];\n\n if (format === 'markdown') {\n lines.push(`# Verification Tree: ${treeData.jobId}`);\n lines.push('');\n lines.push(`**Total Pages:** ${treeData.totalPages}`);\n lines.push('');\n lines.push('## Summary');\n lines.push('| Status | Count |');\n lines.push('|--------|-------|');\n lines.push(`| Complete | ${treeData.summary.complete} |`);\n lines.push(`| Partial | ${treeData.summary.partial} |`);\n lines.push(`| Pending | ${treeData.summary.pending} |`);\n lines.push(`| Flagged | ${treeData.summary.flagged} |`);\n lines.push(`| Empty | ${treeData.summary.empty} |`);\n lines.push(`| Gap | ${treeData.summary.gap} |`);\n lines.push('');\n lines.push('## Pages');\n lines.push('| Page | Status | Total | Verified | Pending | Flagged |');\n lines.push('|------|--------|-------|----------|---------|---------|');\n\n for (const page of treeData.pages) {\n if (filteredPages.includes(page.page)) {\n lines.push(\n `| ${page.page} | ${page.status} | ${page.total} | ${page.verified} | ${page.pending} | ${page.flagged} |`\n );\n }\n }\n } else {\n // Text format\n lines.push(`Verification Tree: ${treeData.jobId}`);\n lines.push(`Total Pages: ${treeData.totalPages}`);\n lines.push('');\n lines.push('Summary:');\n lines.push(` Complete: ${treeData.summary.complete}`);\n lines.push(` Partial: ${treeData.summary.partial}`);\n lines.push(` Pending: ${treeData.summary.pending}`);\n lines.push(` Flagged: ${treeData.summary.flagged}`);\n lines.push(` Empty: ${treeData.summary.empty}`);\n lines.push(` Gap: ${treeData.summary.gap}`);\n lines.push('');\n lines.push('Pages:');\n\n for (const page of treeData.pages) {\n if (filteredPages.includes(page.page)) {\n const statusIcon = getStatusIcon(page.status);\n const counts = `[${page.verified}/${page.total}]`;\n const flags = page.flagged > 0 ? ` (${page.flagged} flagged)` : '';\n const gaps = page.hasCoverageGaps ? ' [GAP]' : '';\n lines.push(` ${statusIcon} p${page.page.toString().padStart(3)} ${counts}${flags}${gaps}`);\n }\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction getStatusIcon(status: VerificationPageStatus): string {\n switch (status) {\n case 'complete':\n return '✓';\n case 'partial':\n return '◐';\n case 'pending':\n return '○';\n case 'flagged':\n return '⚑';\n case 'empty':\n return '·';\n case 'gap':\n return '!';\n case 'error':\n return '✗';\n default:\n return '?';\n }\n}\n","/**\n * Query Engine - jQuery-like entity selector\n *\n * Inspired by okra-jquery from ~/dev/okrapdf/lib/okra-jquery\n * Supports selectors like:\n * - Type: .table, .figure, .footnote\n * - ID: #entity_123\n * - Attributes: [confidence>0.9], [verified=true]\n * - Page: :page(5), :pages(1-10)\n * - Combinators: .table[confidence>0.9], .table, .figure\n */\n\nimport type { Entity, EntityType } from './types';\n\nexport interface SelectorParts {\n types: EntityType[];\n id?: string;\n pageFilter?: { type: 'single' | 'range'; value: number | [number, number] };\n confidenceFilter?: { op: '>' | '<' | '>=' | '<='; value: number };\n verificationFilter?: 'pending' | 'verified' | 'flagged' | 'rejected';\n textContains?: string;\n}\n\n/**\n * Parse a jQuery-like selector string into parts.\n *\n * Examples:\n * - \".table\" -> { types: ['table'] }\n * - \".table, .figure\" -> { types: ['table', 'figure'] }\n * - \".table:page(5)\" -> { types: ['table'], pageFilter: { type: 'single', value: 5 } }\n * - \"[confidence>0.9]\" -> { confidenceFilter: { op: '>', value: 0.9 } }\n * - \".table[confidence>=0.8]:page(1-10)\" -> complex filter\n */\nexport function parseSelector(selector: string): SelectorParts {\n const parts: SelectorParts = { types: [] };\n\n // Handle OR combinator first (comma-separated)\n if (selector.includes(',') && !selector.includes('[')) {\n const segments = selector.split(',').map((s) => s.trim());\n for (const seg of segments) {\n const subParts = parseSelector(seg);\n parts.types.push(...subParts.types);\n }\n return parts;\n }\n\n // Extract type selectors (.table, .figure, etc)\n // Must start with letter (not number) to avoid matching .8 in [confidence>=0.8]\n const typeMatches = selector.match(/\\.([a-zA-Z][a-zA-Z0-9_]*)/g);\n if (typeMatches) {\n parts.types = typeMatches.map((m) => m.slice(1) as EntityType);\n }\n\n // Extract ID selector (#entity_123)\n const idMatch = selector.match(/#([\\w-]+)/);\n if (idMatch) {\n parts.id = idMatch[1];\n }\n\n // Extract page filter (:page(5) or :pages(1-10))\n const pageMatch = selector.match(/:pages?\\((\\d+)(?:-(\\d+))?\\)/);\n if (pageMatch) {\n if (pageMatch[2]) {\n parts.pageFilter = {\n type: 'range',\n value: [parseInt(pageMatch[1], 10), parseInt(pageMatch[2], 10)],\n };\n } else {\n parts.pageFilter = {\n type: 'single',\n value: parseInt(pageMatch[1], 10),\n };\n }\n }\n\n // Extract confidence filter ([confidence>0.9])\n const confMatch = selector.match(/\\[confidence(>=?|<=?|>|<)(\\d+\\.?\\d*)\\]/);\n if (confMatch) {\n parts.confidenceFilter = {\n op: confMatch[1] as '>' | '<' | '>=' | '<=',\n value: parseFloat(confMatch[2]),\n };\n }\n\n // Extract verification filter ([verified=true], [status=pending])\n const verifyMatch = selector.match(/\\[(?:verified|status)=(\\w+)\\]/);\n if (verifyMatch) {\n const val = verifyMatch[1].toLowerCase();\n if (val === 'true') {\n parts.verificationFilter = 'verified';\n } else if (['pending', 'verified', 'flagged', 'rejected'].includes(val)) {\n parts.verificationFilter = val as typeof parts.verificationFilter;\n }\n }\n\n // Extract text contains filter (:contains(text))\n const containsMatch = selector.match(/:contains\\(([^)]+)\\)/);\n if (containsMatch) {\n parts.textContains = containsMatch[1];\n }\n\n // If no types specified and selector is \"*\", match all\n if (parts.types.length === 0 && selector.includes('*')) {\n parts.types = ['table', 'figure', 'footnote', 'summary', 'signature', 'paragraph'];\n }\n\n return parts;\n}\n\n/**\n * Filter entities based on parsed selector parts.\n */\nexport function filterEntities(entities: Entity[], parts: SelectorParts): Entity[] {\n return entities.filter((entity) => {\n // Type filter - only apply if types are specified\n if (parts.types.length > 0 && !parts.types.includes(entity.type)) {\n return false;\n }\n\n // ID filter\n if (parts.id && entity.id !== parts.id) {\n return false;\n }\n\n // Page filter\n if (parts.pageFilter) {\n if (parts.pageFilter.type === 'single') {\n if (entity.page !== parts.pageFilter.value) return false;\n } else {\n const [start, end] = parts.pageFilter.value as [number, number];\n if (entity.page < start || entity.page > end) return false;\n }\n }\n\n // Confidence filter\n if (parts.confidenceFilter && entity.confidence !== undefined) {\n const { op, value } = parts.confidenceFilter;\n switch (op) {\n case '>':\n if (!(entity.confidence > value)) return false;\n break;\n case '>=':\n if (!(entity.confidence >= value)) return false;\n break;\n case '<':\n if (!(entity.confidence < value)) return false;\n break;\n case '<=':\n if (!(entity.confidence <= value)) return false;\n break;\n }\n }\n\n // Verification filter\n if (parts.verificationFilter && entity.verificationStatus !== parts.verificationFilter) {\n return false;\n }\n\n // Text contains filter\n if (parts.textContains && entity.title) {\n if (!entity.title.toLowerCase().includes(parts.textContains.toLowerCase())) {\n return false;\n }\n }\n\n return true;\n });\n}\n\nexport interface QueryOptions {\n topK?: number;\n minConfidence?: number;\n pageRange?: [number, number];\n sortBy?: 'confidence' | 'page' | 'type';\n}\n\nexport interface QueryStats {\n total: number;\n byType: Record<string, number>;\n byPage: Record<number, number>;\n avgConfidence: number;\n minConfidence: number;\n maxConfidence: number;\n}\n\nexport interface QueryResult {\n entities: Entity[];\n total: number;\n stats: QueryStats;\n}\n\n/**\n * Execute a query against entities.\n */\nexport function executeQuery(\n entities: Entity[],\n selector: string,\n options: QueryOptions = {}\n): QueryResult {\n const parts = parseSelector(selector);\n\n // Apply min confidence if specified\n if (options.minConfidence !== undefined) {\n parts.confidenceFilter = { op: '>=', value: options.minConfidence };\n }\n\n // Apply page range if specified\n if (options.pageRange) {\n parts.pageFilter = { type: 'range', value: options.pageRange };\n }\n\n let results = filterEntities(entities, parts);\n\n // Sort\n if (options.sortBy) {\n results = [...results].sort((a, b) => {\n switch (options.sortBy) {\n case 'confidence':\n return (b.confidence ?? 0) - (a.confidence ?? 0);\n case 'page':\n return a.page - b.page;\n case 'type':\n return a.type.localeCompare(b.type);\n default:\n return 0;\n }\n });\n }\n\n // Calculate stats\n const stats = calculateStats(results);\n\n // Apply top-k limit\n if (options.topK && options.topK > 0) {\n results = results.slice(0, options.topK);\n }\n\n return {\n entities: results,\n total: results.length,\n stats,\n };\n}\n\n/**\n * Calculate aggregate statistics for entities.\n */\nexport function calculateStats(entities: Entity[]): QueryStats {\n const byType: Record<string, number> = {};\n const byPage: Record<number, number> = {};\n let totalConfidence = 0;\n let minConfidence = Infinity;\n let maxConfidence = -Infinity;\n let confidenceCount = 0;\n\n for (const entity of entities) {\n // Count by type\n byType[entity.type] = (byType[entity.type] || 0) + 1;\n\n // Count by page\n byPage[entity.page] = (byPage[entity.page] || 0) + 1;\n\n // Confidence stats\n if (entity.confidence !== undefined) {\n totalConfidence += entity.confidence;\n confidenceCount++;\n if (entity.confidence < minConfidence) minConfidence = entity.confidence;\n if (entity.confidence > maxConfidence) maxConfidence = entity.confidence;\n }\n }\n\n return {\n total: entities.length,\n byType,\n byPage,\n avgConfidence: confidenceCount > 0 ? totalConfidence / confidenceCount : 0,\n minConfidence: minConfidence === Infinity ? 0 : minConfidence,\n maxConfidence: maxConfidence === -Infinity ? 0 : maxConfidence,\n };\n}\n","/**\n * okra find - Entity search with jQuery-like selectors\n *\n * Maps to: Middle panel of review page (entity overlays)\n * Find entities using CSS-like selectors.\n *\n * Usage:\n * okra find <jobId> \".table\" # Find all tables\n * okra find <jobId> \".figure:page(5)\" # Figures on page 5\n * okra find <jobId> \"[confidence>0.9]\" # High confidence entities\n * okra find <jobId> \".table, .figure\" # Tables OR figures\n * okra find <jobId> \"*\" --stats # All entities with stats\n * okra find <jobId> \".table\" --top-k 10 # Top 10 tables\n */\n\nimport { OkraClient } from '@okrapdf/sdk';\nimport type { Entity } from '@okrapdf/sdk';\nimport { executeQuery, QueryOptions, QueryResult, QueryStats } from '../query-engine';\n\nexport interface FindOptions extends QueryOptions {\n stats?: boolean;\n format?: 'text' | 'json' | 'entities' | 'ids';\n}\n\n/**\n * Find entities matching a selector.\n */\nexport async function find(\n client: OkraClient,\n jobId: string,\n selector: string,\n options: FindOptions = {}\n): Promise<QueryResult> {\n const entitiesData = await client.ocrJobs.getEntities(jobId);\n return executeQuery(entitiesData.entities, selector, options);\n}\n\n/**\n * Format find result for output.\n */\nexport function formatFindOutput(\n result: QueryResult,\n format: 'text' | 'json' | 'entities' | 'ids' = 'text',\n showStats = false\n): string {\n if (format === 'json') {\n return JSON.stringify(showStats ? result : result.entities, null, 2);\n }\n\n if (format === 'ids') {\n return result.entities.map((e) => e.id).join('\\n');\n }\n\n if (format === 'entities') {\n return result.entities\n .map((e) => `${e.type}\\t${e.page}\\t${e.id}\\t${e.title || ''}`)\n .join('\\n');\n }\n\n // Text format\n const lines: string[] = [];\n lines.push(`Found ${result.total} entities`);\n lines.push('');\n\n if (showStats) {\n lines.push('Stats:');\n lines.push(` By Type:`);\n for (const [type, count] of Object.entries(result.stats.byType)) {\n lines.push(` ${type}: ${count}`);\n }\n lines.push(` Confidence: avg=${result.stats.avgConfidence.toFixed(2)}, min=${result.stats.minConfidence.toFixed(2)}, max=${result.stats.maxConfidence.toFixed(2)}`);\n lines.push(` Pages: ${Object.keys(result.stats.byPage).length}`);\n lines.push('');\n }\n\n lines.push('Entities:');\n for (const entity of result.entities) {\n const conf = entity.confidence !== undefined ? ` (${(entity.confidence * 100).toFixed(0)}%)` : '';\n const title = entity.title ? ` \"${entity.title.slice(0, 40)}${entity.title.length > 40 ? '...' : ''}\"` : '';\n lines.push(` [p${entity.page}] ${entity.type}${title}${conf}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format stats only.\n */\nexport function formatStats(stats: QueryStats): string {\n const lines: string[] = [];\n lines.push(`Total: ${stats.total}`);\n lines.push('');\n lines.push('By Type:');\n for (const [type, count] of Object.entries(stats.byType)) {\n const pct = ((count / stats.total) * 100).toFixed(1);\n lines.push(` ${type.padEnd(12)} ${count.toString().padStart(4)} (${pct}%)`);\n }\n lines.push('');\n lines.push('By Page:');\n const pageCounts = Object.entries(stats.byPage)\n .sort((a, b) => parseInt(a[0]) - parseInt(b[0]))\n .slice(0, 20); // Show first 20 pages\n for (const [page, count] of pageCounts) {\n lines.push(` p${page.padStart(3)}: ${'█'.repeat(Math.min(count, 40))} ${count}`);\n }\n if (Object.keys(stats.byPage).length > 20) {\n lines.push(` ... and ${Object.keys(stats.byPage).length - 20} more pages`);\n }\n lines.push('');\n lines.push('Confidence:');\n lines.push(` Average: ${(stats.avgConfidence * 100).toFixed(1)}%`);\n lines.push(` Min: ${(stats.minConfidence * 100).toFixed(1)}%`);\n lines.push(` Max: ${(stats.maxConfidence * 100).toFixed(1)}%`);\n\n return lines.join('\\n');\n}\n","/**\n * okra page - Page content operations\n *\n * Maps to: Right panel of review page (markdown editor)\n * Get, edit, and resolve page content.\n *\n * Usage:\n * okra page get <jobId> <pageNum> # Get page markdown\n * okra page get <jobId> <pageNum> --version 2 # Get specific version\n * okra page edit <jobId> <pageNum> <content> # Edit page content\n * okra page resolve <jobId> <pageNum> reviewed # Mark as reviewed\n * okra page versions <jobId> <pageNum> # List versions\n */\n\nimport { OkraClient } from '@okrapdf/sdk';\nimport type { PageContentResponse, PageVersionsResponse } from '@okrapdf/sdk';\n\nexport interface PageGetOptions {\n version?: number;\n format?: 'text' | 'json' | 'markdown';\n}\n\nexport interface PageResolveOptions {\n resolution: string;\n classification?: string;\n reason?: string;\n}\n\n/**\n * Get page content.\n */\nexport async function pageGet(\n client: OkraClient,\n jobId: string,\n pageNum: number,\n options: PageGetOptions = {}\n): Promise<PageContentResponse> {\n if (options.version) {\n return client.ocrJobs.getPageVersionContent(jobId, pageNum, options.version);\n }\n return client.ocrJobs.getPageContent(jobId, pageNum);\n}\n\n/**\n * Edit page content.\n */\nexport async function pageEdit(\n client: OkraClient,\n jobId: string,\n pageNum: number,\n content: string\n): Promise<{ success: boolean; version: number }> {\n const result = await client.ocrJobs.savePageContent(jobId, pageNum, content);\n return { success: result.success, version: result.version };\n}\n\n/**\n * Resolve page verification status.\n */\nexport async function pageResolve(\n client: OkraClient,\n jobId: string,\n pageNum: number,\n options: PageResolveOptions\n): Promise<{ success: boolean }> {\n return client.ocrJobs.resolvePageStatus(jobId, pageNum, options);\n}\n\n/**\n * List page versions.\n */\nexport async function pageVersions(\n client: OkraClient,\n jobId: string,\n pageNum: number\n): Promise<PageVersionsResponse> {\n return client.ocrJobs.listPageVersions(jobId, pageNum);\n}\n\n/**\n * Format page content for output.\n */\nexport function formatPageOutput(\n content: PageContentResponse,\n format: 'text' | 'json' | 'markdown' = 'markdown'\n): string {\n if (format === 'json') {\n return JSON.stringify(content, null, 2);\n }\n\n if (format === 'markdown') {\n return content.content;\n }\n\n // Text format with metadata\n const lines: string[] = [];\n lines.push(`Page ${content.page}`);\n if (content.version) {\n lines.push(`Version: ${content.version}`);\n }\n lines.push(`Length: ${content.content.length} chars`);\n lines.push('');\n lines.push('---');\n lines.push(content.content);\n\n return lines.join('\\n');\n}\n\n/**\n * Format versions list for output.\n */\nexport function formatVersionsOutput(\n versions: PageVersionsResponse,\n format: 'text' | 'json' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(versions, null, 2);\n }\n\n const lines: string[] = [];\n lines.push(`Page ${versions.page} - ${versions.versions.length} versions`);\n lines.push(`Current: v${versions.current_version}`);\n lines.push('');\n\n for (const v of versions.versions) {\n const current = v.version === versions.current_version ? ' *' : '';\n const date = v.created_at ? new Date(v.created_at).toLocaleString() : 'unknown';\n lines.push(` v${v.version}${current} [${v.edit_source}] ${date}`);\n if (v.preview) {\n lines.push(` \"${v.preview.slice(0, 60)}${v.preview.length > 60 ? '...' : ''}\"`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * okra search - Full-text search command\n *\n * Search page content across all pages.\n *\n * Usage:\n * okra search <jobId> \"revenue\"\n * okra search <jobId> \"total\" --format json\n */\n\nimport { OkraClient } from '@okrapdf/sdk';\nimport type { SearchResponse } from '@okrapdf/sdk';\n\nexport interface SearchOptions {\n format?: 'text' | 'json';\n limit?: number;\n}\n\n/**\n * Search page content.\n */\nexport async function search(\n client: OkraClient,\n jobId: string,\n query: string\n): Promise<SearchResponse> {\n return client.ocrJobs.search(jobId, query);\n}\n\n/**\n * Format search results for output.\n */\nexport function formatSearchOutput(\n result: SearchResponse,\n format: 'text' | 'json' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n const lines: string[] = [];\n lines.push(`Search: \"${result.query}\"`);\n lines.push(`Found ${result.total_matches} matches in ${result.results.length} pages`);\n lines.push('');\n\n for (const r of result.results) {\n const source = r.match_source ? ` [${r.match_source}]` : '';\n lines.push(`p${r.page.toString().padStart(3)} (${r.match_count} matches)${source}`);\n if (r.snippet) {\n lines.push(` \"${r.snippet.slice(0, 80)}${r.snippet.length > 80 ? '...' : ''}\"`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * okra tables - List and filter tables\n *\n * Usage:\n * okra tables <jobId>\n * okra tables <jobId> --page 5\n * okra tables <jobId> --status pending\n * okra tables <jobId> --format json\n */\n\nimport { OkraClient } from '@okrapdf/sdk';\nimport type { TablesResponse, TableRecord } from '@okrapdf/sdk';\n\nexport interface TablesOptions {\n page?: number;\n status?: 'pending' | 'verified' | 'flagged' | 'rejected';\n format?: 'text' | 'json' | 'markdown';\n}\n\n/**\n * Get tables for a job.\n */\nexport async function tables(\n client: OkraClient,\n jobId: string,\n options: TablesOptions = {}\n): Promise<TablesResponse> {\n const result = await client.ocrJobs.getTables(jobId, options.page);\n\n // Filter by status if specified\n if (options.status) {\n result.tables = result.tables.filter((t) => t.verification_status === options.status);\n }\n\n return result;\n}\n\n/**\n * Format tables for output.\n */\nexport function formatTablesOutput(\n result: TablesResponse,\n format: 'text' | 'json' | 'markdown' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n if (format === 'markdown') {\n // Output each table's markdown\n return result.tables.map((t) => {\n return `## Table (p${t.page_number})\\n\\n${t.markdown}`;\n }).join('\\n\\n---\\n\\n');\n }\n\n // Text format\n const lines: string[] = [];\n lines.push(`Tables: ${result.tables.length}`);\n lines.push('');\n\n // Group by page\n const byPage = new Map<number, TableRecord[]>();\n for (const t of result.tables) {\n const pageGroup = byPage.get(t.page_number) || [];\n pageGroup.push(t);\n byPage.set(t.page_number, pageGroup);\n }\n\n for (const [page, pageTables] of [...byPage.entries()].sort((a, b) => a[0] - b[0])) {\n lines.push(`Page ${page}:`);\n for (const t of pageTables) {\n const status = getStatusIcon(t.verification_status);\n const conf = t.confidence !== null ? ` (${(t.confidence * 100).toFixed(0)}%)` : '';\n const preview = t.markdown.split('\\n')[0].slice(0, 50);\n lines.push(` ${status} ${t.id}${conf}`);\n lines.push(` ${preview}${t.markdown.length > 50 ? '...' : ''}`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction getStatusIcon(status: string): string {\n switch (status) {\n case 'verified':\n return '✓';\n case 'pending':\n return '○';\n case 'flagged':\n return '⚑';\n case 'rejected':\n return '✗';\n default:\n return '?';\n }\n}\n","/**\n * okra history - Verification audit trail\n *\n * Usage:\n * okra history <jobId>\n * okra history <jobId> --limit 20\n * okra history <jobId> --format json\n */\n\nimport { OkraClient } from '@okrapdf/sdk';\nimport type { HistoryResponse } from '@okrapdf/sdk';\n\nexport interface HistoryOptions {\n limit?: number;\n format?: 'text' | 'json';\n}\n\n/**\n * Get verification history.\n */\nexport async function history(\n client: OkraClient,\n jobId: string,\n options: HistoryOptions = {}\n): Promise<HistoryResponse> {\n return client.ocrJobs.getHistory(jobId, options.limit || 50);\n}\n\n/**\n * Format history for output.\n */\nexport function formatHistoryOutput(\n result: HistoryResponse,\n format: 'text' | 'json' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n const lines: string[] = [];\n lines.push(`History: ${result.history.length} entries`);\n lines.push('');\n\n for (const entry of result.history) {\n const date = new Date(entry.createdAt).toLocaleString();\n const page = entry.pageNum !== null ? ` p${entry.pageNum}` : '';\n const transition = entry.transitionName || `${entry.previousState || '?'} -> ${entry.state}`;\n const by = entry.triggeredByName || entry.triggeredBy || 'system';\n\n lines.push(`[${date}] ${entry.entityType}${page}`);\n lines.push(` ${transition} by ${by}`);\n if (entry.reason) {\n lines.push(` Reason: ${entry.reason}`);\n }\n if (entry.resolution) {\n lines.push(` Resolution: ${entry.resolution}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n"],"mappings":"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","names":["getStatusIcon"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { OkraClient, VerificationPageStatus as VerificationPageStatus$1, EntityType as EntityType$1, VerificationTreeResponse, PageContentResponse, PageVersionsResponse as PageVersionsResponse$1, SearchResponse as SearchResponse$1, TablesResponse as TablesResponse$1, HistoryResponse as HistoryResponse$1 } from '@okrapdf/sdk';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CLI Types for OkraPDF Review Operations
|
|
5
|
+
*
|
|
6
|
+
* These types mirror the review page UI interactions:
|
|
7
|
+
* - Left panel: Document tree (verification status, entity counts)
|
|
8
|
+
* - Middle panel: PDF viewer (entities, overlays)
|
|
9
|
+
* - Right panel: Page content (markdown, versions)
|
|
10
|
+
*/
|
|
11
|
+
type VerificationPageStatus = 'complete' | 'partial' | 'flagged' | 'pending' | 'empty' | 'gap' | 'error';
|
|
12
|
+
interface VerificationTreePage {
|
|
13
|
+
page: number;
|
|
14
|
+
status: VerificationPageStatus;
|
|
15
|
+
total: number;
|
|
16
|
+
verified: number;
|
|
17
|
+
pending: number;
|
|
18
|
+
flagged: number;
|
|
19
|
+
rejected: number;
|
|
20
|
+
avgConfidence: number;
|
|
21
|
+
hasOcr: boolean;
|
|
22
|
+
ocrLineCount: number;
|
|
23
|
+
hasCoverageGaps: boolean;
|
|
24
|
+
uncoveredCount: number;
|
|
25
|
+
resolution: string | null;
|
|
26
|
+
classification: string | null;
|
|
27
|
+
isStale: boolean;
|
|
28
|
+
}
|
|
29
|
+
interface VerificationTreeSummary {
|
|
30
|
+
complete: number;
|
|
31
|
+
partial: number;
|
|
32
|
+
flagged: number;
|
|
33
|
+
pending: number;
|
|
34
|
+
empty: number;
|
|
35
|
+
gap: number;
|
|
36
|
+
resolved?: number;
|
|
37
|
+
stale?: number;
|
|
38
|
+
}
|
|
39
|
+
interface VerificationTree {
|
|
40
|
+
jobId: string;
|
|
41
|
+
documentId: string;
|
|
42
|
+
totalPages: number;
|
|
43
|
+
summary: VerificationTreeSummary;
|
|
44
|
+
pages: VerificationTreePage[];
|
|
45
|
+
}
|
|
46
|
+
type EntityType = 'table' | 'figure' | 'footnote' | 'summary' | 'signature' | 'paragraph';
|
|
47
|
+
interface EntityBBox {
|
|
48
|
+
x: number;
|
|
49
|
+
y: number;
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
}
|
|
53
|
+
interface Entity {
|
|
54
|
+
id: string;
|
|
55
|
+
type: EntityType;
|
|
56
|
+
title: string | null;
|
|
57
|
+
page: number;
|
|
58
|
+
schema?: string[];
|
|
59
|
+
isComplete?: boolean;
|
|
60
|
+
bbox?: EntityBBox;
|
|
61
|
+
confidence?: number;
|
|
62
|
+
verificationStatus?: 'pending' | 'verified' | 'flagged' | 'rejected';
|
|
63
|
+
}
|
|
64
|
+
interface EntitiesResponse {
|
|
65
|
+
jobId: string;
|
|
66
|
+
entities: Entity[];
|
|
67
|
+
counts: {
|
|
68
|
+
tables: number;
|
|
69
|
+
figures: number;
|
|
70
|
+
footnotes: number;
|
|
71
|
+
summaries: number;
|
|
72
|
+
signatures?: number;
|
|
73
|
+
};
|
|
74
|
+
extractionStatus?: 'not_started' | 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' | 'paused';
|
|
75
|
+
totalPages?: number;
|
|
76
|
+
}
|
|
77
|
+
interface TextBlock {
|
|
78
|
+
text: string;
|
|
79
|
+
bbox?: EntityBBox;
|
|
80
|
+
confidence?: number;
|
|
81
|
+
}
|
|
82
|
+
interface PageDimension {
|
|
83
|
+
width: number | null;
|
|
84
|
+
height: number | null;
|
|
85
|
+
}
|
|
86
|
+
interface PageContent {
|
|
87
|
+
page: number;
|
|
88
|
+
content: string;
|
|
89
|
+
version?: number;
|
|
90
|
+
blocks?: TextBlock[];
|
|
91
|
+
dimension?: PageDimension | null;
|
|
92
|
+
}
|
|
93
|
+
interface PageVersionInfo {
|
|
94
|
+
version: number;
|
|
95
|
+
editSource: 'ocr_extraction' | 'user_edit' | 'ai_correction';
|
|
96
|
+
createdAt: string | null;
|
|
97
|
+
preview: string;
|
|
98
|
+
}
|
|
99
|
+
interface PageVersionsResponse {
|
|
100
|
+
page: number;
|
|
101
|
+
currentVersion: number;
|
|
102
|
+
versions: PageVersionInfo[];
|
|
103
|
+
}
|
|
104
|
+
interface Table {
|
|
105
|
+
id: string;
|
|
106
|
+
pageNumber: number;
|
|
107
|
+
markdown: string;
|
|
108
|
+
bbox: {
|
|
109
|
+
xmin: number;
|
|
110
|
+
ymin: number;
|
|
111
|
+
xmax: number;
|
|
112
|
+
ymax: number;
|
|
113
|
+
};
|
|
114
|
+
confidence: number | null;
|
|
115
|
+
verificationStatus: 'pending' | 'verified' | 'flagged' | 'rejected';
|
|
116
|
+
verifiedBy: string | null;
|
|
117
|
+
verifiedAt: string | null;
|
|
118
|
+
wasCorrected?: boolean;
|
|
119
|
+
createdAt: string;
|
|
120
|
+
}
|
|
121
|
+
interface TablesResponse {
|
|
122
|
+
tables: Table[];
|
|
123
|
+
source: 'job_id' | 'document_uuid';
|
|
124
|
+
}
|
|
125
|
+
type MatchSource = 'content' | 'table_title' | 'table_schema' | 'table_row' | 'figure' | 'footnote' | 'summary' | 'signature' | 'paragraph';
|
|
126
|
+
interface SearchResult {
|
|
127
|
+
page: number;
|
|
128
|
+
snippet: string;
|
|
129
|
+
matchCount: number;
|
|
130
|
+
matchSource?: MatchSource;
|
|
131
|
+
}
|
|
132
|
+
interface SearchResponse {
|
|
133
|
+
query: string;
|
|
134
|
+
totalMatches: number;
|
|
135
|
+
results: SearchResult[];
|
|
136
|
+
}
|
|
137
|
+
interface HistoryEntry {
|
|
138
|
+
id: string;
|
|
139
|
+
entityType: string;
|
|
140
|
+
entityId: string;
|
|
141
|
+
state: string;
|
|
142
|
+
previousState: string | null;
|
|
143
|
+
transitionName: string | null;
|
|
144
|
+
triggeredBy: string | null;
|
|
145
|
+
triggeredByName: string | null;
|
|
146
|
+
reason: string | null;
|
|
147
|
+
resolution: string | null;
|
|
148
|
+
classification: string | null;
|
|
149
|
+
pageNum: number | null;
|
|
150
|
+
createdAt: string;
|
|
151
|
+
}
|
|
152
|
+
interface HistoryResponse {
|
|
153
|
+
history: HistoryEntry[];
|
|
154
|
+
}
|
|
155
|
+
type OutputFormat = 'text' | 'json' | 'markdown';
|
|
156
|
+
interface QueryConfig {
|
|
157
|
+
selector: string;
|
|
158
|
+
topK?: number;
|
|
159
|
+
minConfidence?: number;
|
|
160
|
+
pageRange?: [number, number];
|
|
161
|
+
sortBy?: 'confidence' | 'page' | 'type';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Query Engine - jQuery-like entity selector
|
|
166
|
+
*
|
|
167
|
+
* Inspired by okra-jquery from ~/dev/okrapdf/lib/okra-jquery
|
|
168
|
+
* Supports selectors like:
|
|
169
|
+
* - Type: .table, .figure, .footnote
|
|
170
|
+
* - ID: #entity_123
|
|
171
|
+
* - Attributes: [confidence>0.9], [verified=true]
|
|
172
|
+
* - Page: :page(5), :pages(1-10)
|
|
173
|
+
* - Combinators: .table[confidence>0.9], .table, .figure
|
|
174
|
+
*/
|
|
175
|
+
|
|
176
|
+
interface SelectorParts {
|
|
177
|
+
types: EntityType[];
|
|
178
|
+
id?: string;
|
|
179
|
+
pageFilter?: {
|
|
180
|
+
type: 'single' | 'range';
|
|
181
|
+
value: number | [number, number];
|
|
182
|
+
};
|
|
183
|
+
confidenceFilter?: {
|
|
184
|
+
op: '>' | '<' | '>=' | '<=';
|
|
185
|
+
value: number;
|
|
186
|
+
};
|
|
187
|
+
verificationFilter?: 'pending' | 'verified' | 'flagged' | 'rejected';
|
|
188
|
+
textContains?: string;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Parse a jQuery-like selector string into parts.
|
|
192
|
+
*
|
|
193
|
+
* Examples:
|
|
194
|
+
* - ".table" -> { types: ['table'] }
|
|
195
|
+
* - ".table, .figure" -> { types: ['table', 'figure'] }
|
|
196
|
+
* - ".table:page(5)" -> { types: ['table'], pageFilter: { type: 'single', value: 5 } }
|
|
197
|
+
* - "[confidence>0.9]" -> { confidenceFilter: { op: '>', value: 0.9 } }
|
|
198
|
+
* - ".table[confidence>=0.8]:page(1-10)" -> complex filter
|
|
199
|
+
*/
|
|
200
|
+
declare function parseSelector(selector: string): SelectorParts;
|
|
201
|
+
/**
|
|
202
|
+
* Filter entities based on parsed selector parts.
|
|
203
|
+
*/
|
|
204
|
+
declare function filterEntities(entities: Entity[], parts: SelectorParts): Entity[];
|
|
205
|
+
interface QueryOptions {
|
|
206
|
+
topK?: number;
|
|
207
|
+
minConfidence?: number;
|
|
208
|
+
pageRange?: [number, number];
|
|
209
|
+
sortBy?: 'confidence' | 'page' | 'type';
|
|
210
|
+
}
|
|
211
|
+
interface QueryStats {
|
|
212
|
+
total: number;
|
|
213
|
+
byType: Record<string, number>;
|
|
214
|
+
byPage: Record<number, number>;
|
|
215
|
+
avgConfidence: number;
|
|
216
|
+
minConfidence: number;
|
|
217
|
+
maxConfidence: number;
|
|
218
|
+
}
|
|
219
|
+
interface QueryResult {
|
|
220
|
+
entities: Entity[];
|
|
221
|
+
total: number;
|
|
222
|
+
stats: QueryStats;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Execute a query against entities.
|
|
226
|
+
*/
|
|
227
|
+
declare function executeQuery(entities: Entity[], selector: string, options?: QueryOptions): QueryResult;
|
|
228
|
+
/**
|
|
229
|
+
* Calculate aggregate statistics for entities.
|
|
230
|
+
*/
|
|
231
|
+
declare function calculateStats(entities: Entity[]): QueryStats;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* okra tree - Document verification tree command
|
|
235
|
+
*
|
|
236
|
+
* Maps to: Left panel of review page
|
|
237
|
+
* Shows page-level verification status and entity counts.
|
|
238
|
+
*
|
|
239
|
+
* Usage:
|
|
240
|
+
* okra tree <jobId>
|
|
241
|
+
* okra tree <jobId> --status pending
|
|
242
|
+
* okra tree <jobId> --entity table
|
|
243
|
+
* okra tree <jobId> --format json
|
|
244
|
+
*/
|
|
245
|
+
|
|
246
|
+
interface TreeOptions {
|
|
247
|
+
status?: VerificationPageStatus$1;
|
|
248
|
+
entity?: EntityType$1;
|
|
249
|
+
format?: 'text' | 'json' | 'markdown';
|
|
250
|
+
}
|
|
251
|
+
interface TreeResult {
|
|
252
|
+
tree: VerificationTreeResponse;
|
|
253
|
+
filteredPages: number[];
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Get the verification tree for a job.
|
|
257
|
+
*/
|
|
258
|
+
declare function tree(client: OkraClient, jobId: string, options?: TreeOptions): Promise<TreeResult>;
|
|
259
|
+
/**
|
|
260
|
+
* Format tree result for output.
|
|
261
|
+
*/
|
|
262
|
+
declare function formatTreeOutput(result: TreeResult, format?: 'text' | 'json' | 'markdown'): string;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* okra find - Entity search with jQuery-like selectors
|
|
266
|
+
*
|
|
267
|
+
* Maps to: Middle panel of review page (entity overlays)
|
|
268
|
+
* Find entities using CSS-like selectors.
|
|
269
|
+
*
|
|
270
|
+
* Usage:
|
|
271
|
+
* okra find <jobId> ".table" # Find all tables
|
|
272
|
+
* okra find <jobId> ".figure:page(5)" # Figures on page 5
|
|
273
|
+
* okra find <jobId> "[confidence>0.9]" # High confidence entities
|
|
274
|
+
* okra find <jobId> ".table, .figure" # Tables OR figures
|
|
275
|
+
* okra find <jobId> "*" --stats # All entities with stats
|
|
276
|
+
* okra find <jobId> ".table" --top-k 10 # Top 10 tables
|
|
277
|
+
*/
|
|
278
|
+
|
|
279
|
+
interface FindOptions extends QueryOptions {
|
|
280
|
+
stats?: boolean;
|
|
281
|
+
format?: 'text' | 'json' | 'entities' | 'ids';
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Find entities matching a selector.
|
|
285
|
+
*/
|
|
286
|
+
declare function find(client: OkraClient, jobId: string, selector: string, options?: FindOptions): Promise<QueryResult>;
|
|
287
|
+
/**
|
|
288
|
+
* Format find result for output.
|
|
289
|
+
*/
|
|
290
|
+
declare function formatFindOutput(result: QueryResult, format?: 'text' | 'json' | 'entities' | 'ids', showStats?: boolean): string;
|
|
291
|
+
/**
|
|
292
|
+
* Format stats only.
|
|
293
|
+
*/
|
|
294
|
+
declare function formatStats(stats: QueryStats): string;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* okra page - Page content operations
|
|
298
|
+
*
|
|
299
|
+
* Maps to: Right panel of review page (markdown editor)
|
|
300
|
+
* Get, edit, and resolve page content.
|
|
301
|
+
*
|
|
302
|
+
* Usage:
|
|
303
|
+
* okra page get <jobId> <pageNum> # Get page markdown
|
|
304
|
+
* okra page get <jobId> <pageNum> --version 2 # Get specific version
|
|
305
|
+
* okra page edit <jobId> <pageNum> <content> # Edit page content
|
|
306
|
+
* okra page resolve <jobId> <pageNum> reviewed # Mark as reviewed
|
|
307
|
+
* okra page versions <jobId> <pageNum> # List versions
|
|
308
|
+
*/
|
|
309
|
+
|
|
310
|
+
interface PageGetOptions {
|
|
311
|
+
version?: number;
|
|
312
|
+
format?: 'text' | 'json' | 'markdown';
|
|
313
|
+
}
|
|
314
|
+
interface PageResolveOptions {
|
|
315
|
+
resolution: string;
|
|
316
|
+
classification?: string;
|
|
317
|
+
reason?: string;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Get page content.
|
|
321
|
+
*/
|
|
322
|
+
declare function pageGet(client: OkraClient, jobId: string, pageNum: number, options?: PageGetOptions): Promise<PageContentResponse>;
|
|
323
|
+
/**
|
|
324
|
+
* Edit page content.
|
|
325
|
+
*/
|
|
326
|
+
declare function pageEdit(client: OkraClient, jobId: string, pageNum: number, content: string): Promise<{
|
|
327
|
+
success: boolean;
|
|
328
|
+
version: number;
|
|
329
|
+
}>;
|
|
330
|
+
/**
|
|
331
|
+
* Resolve page verification status.
|
|
332
|
+
*/
|
|
333
|
+
declare function pageResolve(client: OkraClient, jobId: string, pageNum: number, options: PageResolveOptions): Promise<{
|
|
334
|
+
success: boolean;
|
|
335
|
+
}>;
|
|
336
|
+
/**
|
|
337
|
+
* List page versions.
|
|
338
|
+
*/
|
|
339
|
+
declare function pageVersions(client: OkraClient, jobId: string, pageNum: number): Promise<PageVersionsResponse$1>;
|
|
340
|
+
/**
|
|
341
|
+
* Format page content for output.
|
|
342
|
+
*/
|
|
343
|
+
declare function formatPageOutput(content: PageContentResponse, format?: 'text' | 'json' | 'markdown'): string;
|
|
344
|
+
/**
|
|
345
|
+
* Format versions list for output.
|
|
346
|
+
*/
|
|
347
|
+
declare function formatVersionsOutput(versions: PageVersionsResponse$1, format?: 'text' | 'json'): string;
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* okra search - Full-text search command
|
|
351
|
+
*
|
|
352
|
+
* Search page content across all pages.
|
|
353
|
+
*
|
|
354
|
+
* Usage:
|
|
355
|
+
* okra search <jobId> "revenue"
|
|
356
|
+
* okra search <jobId> "total" --format json
|
|
357
|
+
*/
|
|
358
|
+
|
|
359
|
+
interface SearchOptions {
|
|
360
|
+
format?: 'text' | 'json';
|
|
361
|
+
limit?: number;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Search page content.
|
|
365
|
+
*/
|
|
366
|
+
declare function search(client: OkraClient, jobId: string, query: string): Promise<SearchResponse$1>;
|
|
367
|
+
/**
|
|
368
|
+
* Format search results for output.
|
|
369
|
+
*/
|
|
370
|
+
declare function formatSearchOutput(result: SearchResponse$1, format?: 'text' | 'json'): string;
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* okra tables - List and filter tables
|
|
374
|
+
*
|
|
375
|
+
* Usage:
|
|
376
|
+
* okra tables <jobId>
|
|
377
|
+
* okra tables <jobId> --page 5
|
|
378
|
+
* okra tables <jobId> --status pending
|
|
379
|
+
* okra tables <jobId> --format json
|
|
380
|
+
*/
|
|
381
|
+
|
|
382
|
+
interface TablesOptions {
|
|
383
|
+
page?: number;
|
|
384
|
+
status?: 'pending' | 'verified' | 'flagged' | 'rejected';
|
|
385
|
+
format?: 'text' | 'json' | 'markdown';
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Get tables for a job.
|
|
389
|
+
*/
|
|
390
|
+
declare function tables(client: OkraClient, jobId: string, options?: TablesOptions): Promise<TablesResponse$1>;
|
|
391
|
+
/**
|
|
392
|
+
* Format tables for output.
|
|
393
|
+
*/
|
|
394
|
+
declare function formatTablesOutput(result: TablesResponse$1, format?: 'text' | 'json' | 'markdown'): string;
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* okra history - Verification audit trail
|
|
398
|
+
*
|
|
399
|
+
* Usage:
|
|
400
|
+
* okra history <jobId>
|
|
401
|
+
* okra history <jobId> --limit 20
|
|
402
|
+
* okra history <jobId> --format json
|
|
403
|
+
*/
|
|
404
|
+
|
|
405
|
+
interface HistoryOptions {
|
|
406
|
+
limit?: number;
|
|
407
|
+
format?: 'text' | 'json';
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Get verification history.
|
|
411
|
+
*/
|
|
412
|
+
declare function history(client: OkraClient, jobId: string, options?: HistoryOptions): Promise<HistoryResponse$1>;
|
|
413
|
+
/**
|
|
414
|
+
* Format history for output.
|
|
415
|
+
*/
|
|
416
|
+
declare function formatHistoryOutput(result: HistoryResponse$1, format?: 'text' | 'json'): string;
|
|
417
|
+
|
|
418
|
+
export { type EntitiesResponse, type Entity, type EntityBBox, type EntityType, type FindOptions, type HistoryEntry, type HistoryOptions, type HistoryResponse, type MatchSource, type OutputFormat, type PageContent, type PageDimension, type PageGetOptions, type PageResolveOptions, type PageVersionInfo, type PageVersionsResponse, type QueryConfig, type QueryOptions, type QueryResult, type QueryStats, type SearchOptions, type SearchResponse, type SearchResult, type SelectorParts, type Table, type TablesOptions, type TablesResponse, type TextBlock, type TreeOptions, type TreeResult, type VerificationPageStatus, type VerificationTree, type VerificationTreePage, type VerificationTreeSummary, calculateStats, executeQuery, filterEntities, find, formatFindOutput, formatHistoryOutput, formatPageOutput, formatSearchOutput, formatStats, formatTablesOutput, formatTreeOutput, formatVersionsOutput, history, pageEdit, pageGet, pageResolve, pageVersions, parseSelector, search, tables, tree };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
calculateStats,
|
|
3
|
+
executeQuery,
|
|
4
|
+
filterEntities,
|
|
5
|
+
find,
|
|
6
|
+
formatFindOutput,
|
|
7
|
+
formatHistoryOutput,
|
|
8
|
+
formatPageOutput,
|
|
9
|
+
formatSearchOutput,
|
|
10
|
+
formatStats,
|
|
11
|
+
formatTablesOutput,
|
|
12
|
+
formatTreeOutput,
|
|
13
|
+
formatVersionsOutput,
|
|
14
|
+
history,
|
|
15
|
+
pageEdit,
|
|
16
|
+
pageGet,
|
|
17
|
+
pageResolve,
|
|
18
|
+
pageVersions,
|
|
19
|
+
parseSelector,
|
|
20
|
+
search,
|
|
21
|
+
tables,
|
|
22
|
+
tree
|
|
23
|
+
} from "./chunk-A6YTW4WL.js";
|
|
24
|
+
export {
|
|
25
|
+
calculateStats,
|
|
26
|
+
executeQuery,
|
|
27
|
+
filterEntities,
|
|
28
|
+
find,
|
|
29
|
+
formatFindOutput,
|
|
30
|
+
formatHistoryOutput,
|
|
31
|
+
formatPageOutput,
|
|
32
|
+
formatSearchOutput,
|
|
33
|
+
formatStats,
|
|
34
|
+
formatTablesOutput,
|
|
35
|
+
formatTreeOutput,
|
|
36
|
+
formatVersionsOutput,
|
|
37
|
+
history,
|
|
38
|
+
pageEdit,
|
|
39
|
+
pageGet,
|
|
40
|
+
pageResolve,
|
|
41
|
+
pageVersions,
|
|
42
|
+
parseSelector,
|
|
43
|
+
search,
|
|
44
|
+
tables,
|
|
45
|
+
tree
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@okrapdf/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for OkraPDF document review operations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"okra": "./dist/bin.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"commander": "^12.0.0",
|
|
22
|
+
"@okrapdf/sdk": "0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^20.0.0",
|
|
26
|
+
"tsup": "^8.0.0",
|
|
27
|
+
"typescript": "^5.0.0",
|
|
28
|
+
"vitest": "^4.0.0"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@okrapdf/sdk": "0.1.0"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsup",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest"
|
|
37
|
+
}
|
|
38
|
+
}
|