zero-vision 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.
Files changed (49) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +5 -0
  3. package/README.md +163 -0
  4. package/dist/cdp/attach.js +121 -0
  5. package/dist/cdp/attach.js.map +1 -0
  6. package/dist/cdp/client.js +53 -0
  7. package/dist/cdp/client.js.map +1 -0
  8. package/dist/cdp/extract.js +197 -0
  9. package/dist/cdp/extract.js.map +1 -0
  10. package/dist/cli.js +223 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/engines/apple-fm.js +103 -0
  13. package/dist/engines/apple-fm.js.map +1 -0
  14. package/dist/engines/apple-vision.js +89 -0
  15. package/dist/engines/apple-vision.js.map +1 -0
  16. package/dist/engines/cloud/index.js +41 -0
  17. package/dist/engines/cloud/index.js.map +1 -0
  18. package/dist/engines/index.js +71 -0
  19. package/dist/engines/index.js.map +1 -0
  20. package/dist/engines/local-vlm.js +46 -0
  21. package/dist/engines/local-vlm.js.map +1 -0
  22. package/dist/engines/tesseract.js +221 -0
  23. package/dist/engines/tesseract.js.map +1 -0
  24. package/dist/mcp.js +225 -0
  25. package/dist/mcp.js.map +1 -0
  26. package/dist/native.js +66 -0
  27. package/dist/native.js.map +1 -0
  28. package/dist/snap.js +30 -0
  29. package/dist/snap.js.map +1 -0
  30. package/dist/test/argv.test.js +22 -0
  31. package/dist/test/argv.test.js.map +1 -0
  32. package/dist/test/ax.test.js +28 -0
  33. package/dist/test/ax.test.js.map +1 -0
  34. package/dist/test/engines.test.js +81 -0
  35. package/dist/test/engines.test.js.map +1 -0
  36. package/dist/test/fetch.test.js +46 -0
  37. package/dist/test/fetch.test.js.map +1 -0
  38. package/dist/test/ocr.test.js +34 -0
  39. package/dist/test/ocr.test.js.map +1 -0
  40. package/dist/test/parity.test.js +33 -0
  41. package/dist/test/parity.test.js.map +1 -0
  42. package/dist/test/port.test.js +55 -0
  43. package/dist/test/port.test.js.map +1 -0
  44. package/dist/test/snap.test.js +13 -0
  45. package/dist/test/snap.test.js.map +1 -0
  46. package/dist/test/tesseract.test.js +17 -0
  47. package/dist/test/tesseract.test.js.map +1 -0
  48. package/docs/spec.md +549 -0
  49. package/package.json +40 -0
@@ -0,0 +1,197 @@
1
+ const SKIP_ROLES = new Set(["none", "presentation", "InlineTextBox", "generic"]);
2
+ function prop(node, name) {
3
+ return node.properties?.find((p) => p.name === name)?.value?.value;
4
+ }
5
+ function interesting(n) {
6
+ if (n.ignored)
7
+ return false;
8
+ const role = String(n.role?.value ?? "");
9
+ if (SKIP_ROLES.has(role) && !n.name?.value)
10
+ return false;
11
+ return true;
12
+ }
13
+ export function axToText(nodes) {
14
+ const byId = new Map(nodes.map((n) => [n.nodeId, n]));
15
+ const childOf = new Set();
16
+ for (const n of nodes)
17
+ for (const c of n.childIds ?? [])
18
+ childOf.add(c);
19
+ const roots = nodes.filter((n) => !childOf.has(n.nodeId));
20
+ let uid = 0;
21
+ const textLines = [];
22
+ const mdLines = [];
23
+ const yamlLines = [];
24
+ const walk = (id, depth) => {
25
+ const n = byId.get(id);
26
+ if (!n)
27
+ return;
28
+ const role = String(n.role?.value ?? "generic");
29
+ const name = String(n.name?.value ?? "").trim();
30
+ const value = n.value?.value != null ? String(n.value.value) : "";
31
+ const url = prop(n, "url");
32
+ const keep = interesting(n);
33
+ if (keep &&
34
+ role !== "RootWebArea" &&
35
+ role !== "WebArea" &&
36
+ (name || value || ["heading", "link", "button", "textbox", "checkbox"].includes(role))) {
37
+ const idn = `e${++uid}`;
38
+ const level = Number(prop(n, "level") ?? 0);
39
+ if (role === "heading") {
40
+ const hashes = "#".repeat(Math.min(Math.max(level || 1, 1), 6));
41
+ textLines.push(name);
42
+ mdLines.push(`${hashes} ${name}`);
43
+ yamlLines.push(`${" ".repeat(depth)}- heading ${JSON.stringify(name)} [level=${level || 1}] [uid=${idn}]`);
44
+ }
45
+ else if (role === "link") {
46
+ textLines.push(name);
47
+ mdLines.push(typeof url === "string" && url ? `[${name}](${url})` : name);
48
+ yamlLines.push(`${" ".repeat(depth)}- link ${JSON.stringify(name)} [uid=${idn}]`);
49
+ }
50
+ else if (role === "textbox") {
51
+ textLines.push(value ? `${name}: ${value}` : name);
52
+ mdLines.push(`**${name}:** ${value}`);
53
+ yamlLines.push(`${" ".repeat(depth)}- textbox ${JSON.stringify(name)} [uid=${idn}] [value=${JSON.stringify(value)}]`);
54
+ }
55
+ else if (role === "checkbox") {
56
+ const checked = Boolean(prop(n, "checked"));
57
+ textLines.push(`${checked ? "[x]" : "[ ]"} ${name}`);
58
+ mdLines.push(`- [${checked ? "x" : " "}] ${name}`);
59
+ yamlLines.push(`${" ".repeat(depth)}- checkbox ${JSON.stringify(name)} [uid=${idn}]`);
60
+ }
61
+ else if (role === "button") {
62
+ textLines.push(name);
63
+ mdLines.push(name);
64
+ yamlLines.push(`${" ".repeat(depth)}- button ${JSON.stringify(name)} [uid=${idn}]`);
65
+ }
66
+ else if (name) {
67
+ textLines.push(name);
68
+ mdLines.push(name);
69
+ yamlLines.push(`${" ".repeat(depth)}- ${role} ${JSON.stringify(name)} [uid=${idn}]`);
70
+ }
71
+ }
72
+ for (const c of n.childIds ?? [])
73
+ walk(c, depth + (keep ? 1 : 0));
74
+ };
75
+ for (const r of roots)
76
+ walk(r.nodeId, 0);
77
+ const cap = (s) => (s.length > 24000 ? s.slice(0, 24000) + "\n…[truncated]" : s);
78
+ return {
79
+ text: cap(textLines.join("\n")),
80
+ markdown: cap(mdLines.join("\n")),
81
+ a11y: cap(yamlLines.join("\n")),
82
+ };
83
+ }
84
+ const OPAQUE_JS = `() => {
85
+ const area = (el) => {
86
+ const r = el.getBoundingClientRect();
87
+ return Math.max(0, r.width) * Math.max(0, r.height);
88
+ };
89
+ const vp = innerWidth * innerHeight || 1;
90
+ const nodes = [...document.querySelectorAll('canvas, video')];
91
+ let px = 0;
92
+ const reasons = [];
93
+ for (const el of nodes) {
94
+ const a = area(el);
95
+ if (a / vp >= 0.05) {
96
+ px += a;
97
+ reasons.push(el.tagName.toLowerCase());
98
+ }
99
+ }
100
+ return { fraction: px / vp, reasons: [...new Set(reasons)] };
101
+ }`;
102
+ export async function extractPage(client, sessionId, opts = {}) {
103
+ await client.send("Runtime.enable", {}, sessionId);
104
+ await client.send("Page.enable", {}, sessionId);
105
+ await client.send("Accessibility.enable", {}, sessionId);
106
+ const ready = (await client.send("Runtime.evaluate", { expression: "document.readyState", returnByValue: true }, sessionId));
107
+ if (ready.result?.value !== "complete") {
108
+ await Promise.race([
109
+ new Promise((resolve) => {
110
+ const onLoad = () => {
111
+ client.off("Page.loadEventFired", onLoad);
112
+ resolve();
113
+ };
114
+ client.on("Page.loadEventFired", onLoad);
115
+ }),
116
+ new Promise((resolve) => setTimeout(resolve, 8000)),
117
+ ]);
118
+ }
119
+ await Promise.race([
120
+ new Promise((resolve) => {
121
+ const onAx = () => {
122
+ client.off("Accessibility.loadComplete", onAx);
123
+ resolve();
124
+ };
125
+ client.on("Accessibility.loadComplete", onAx);
126
+ }),
127
+ new Promise((resolve) => setTimeout(resolve, 2000)),
128
+ ]);
129
+ if (opts.waitMs)
130
+ await new Promise((r) => setTimeout(r, opts.waitMs));
131
+ if (opts.waitText) {
132
+ const deadline = Date.now() + 8000;
133
+ while (Date.now() < deadline) {
134
+ const ev = (await client.send("Runtime.evaluate", { expression: "document.body && document.body.innerText || ''", returnByValue: true }, sessionId));
135
+ if ((ev.result?.value ?? "").includes(opts.waitText))
136
+ break;
137
+ await new Promise((r) => setTimeout(r, 200));
138
+ }
139
+ }
140
+ if (opts.scroll) {
141
+ await client.send("Runtime.evaluate", {
142
+ expression: "(async () => { const h = document.body.scrollHeight; for (let y=0; y<h; y+=800) { scrollTo(0,y); await new Promise(r=>setTimeout(r,150)); } })()",
143
+ awaitPromise: true,
144
+ }, sessionId);
145
+ }
146
+ await new Promise((r) => setTimeout(r, 250));
147
+ const { nodes } = (await client.send("Accessibility.getFullAXTree", {}, sessionId));
148
+ const walked = axToText(nodes);
149
+ let opaque = { fraction: 0, reasons: [] };
150
+ try {
151
+ const ev = (await client.send("Runtime.evaluate", { expression: `(${OPAQUE_JS})()`, returnByValue: true }, sessionId));
152
+ if (ev.result?.value)
153
+ opaque = ev.result.value;
154
+ }
155
+ catch {
156
+ /* page may deny */
157
+ }
158
+ try {
159
+ await client.send("Accessibility.disable", {}, sessionId);
160
+ }
161
+ catch {
162
+ /* ignore */
163
+ }
164
+ let a11y = walked.a11y;
165
+ if (opaque.fraction >= 0.3) {
166
+ a11y += `\nopaque: ${opaque.reasons[0] ?? "canvas"} ${opaque.fraction.toFixed(2)} viewport`;
167
+ }
168
+ if (opts.verbose) {
169
+ a11y += "\n--- raw ---\n" + JSON.stringify(nodes).slice(0, 8000);
170
+ }
171
+ return { text: walked.text, markdown: walked.markdown, a11y, opaque };
172
+ }
173
+ export async function captureScreenshot(client, sessionId) {
174
+ const result = (await client.send("Page.captureScreenshot", { format: "png" }, sessionId));
175
+ if (!result?.data)
176
+ throw new Error("Page.captureScreenshot returned no data");
177
+ return Buffer.from(result.data, "base64");
178
+ }
179
+ export async function fetchUrl(url) {
180
+ const res = await fetch(url, { redirect: "follow" });
181
+ const html = await res.text();
182
+ const stripped = html
183
+ .replace(/<script[\s\S]*?<\/script>/gi, " ")
184
+ .replace(/<style[\s\S]*?<\/style>/gi, " ")
185
+ .replace(/<[^>]+>/g, " ")
186
+ .replace(/&nbsp;/g, " ")
187
+ .replace(/&amp;/g, "&")
188
+ .replace(/&lt;/g, "<")
189
+ .replace(/&gt;/g, ">")
190
+ .replace(/\s+/g, " ")
191
+ .trim();
192
+ if (stripped.length < 200 && /id=["']root["']/.test(html)) {
193
+ throw Object.assign(new Error("page looks like a client render; open it in a debug Chrome and retry without --fetch"), { code: 1 });
194
+ }
195
+ return { text: stripped.slice(0, 24000), markdown: stripped.slice(0, 24000) };
196
+ }
197
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/cdp/extract.ts"],"names":[],"mappings":"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"}
package/dist/cli.js ADDED
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { perceive, resolveEngine } from "./engines/index.js";
4
+ import { attach, findOpenPort, listTabs } from "./cdp/attach.js";
5
+ import { captureScreenshot, extractPage, fetchUrl } from "./cdp/extract.js";
6
+ import { interactiveCapture, runScreencapture } from "./snap.js";
7
+ const BOOL = new Set([
8
+ "md",
9
+ "a11y",
10
+ "json",
11
+ "navigate",
12
+ "fetch",
13
+ "ocr",
14
+ "ocr-opaque",
15
+ "scroll",
16
+ "clipboard",
17
+ "tabs",
18
+ "verbose",
19
+ "help",
20
+ ]);
21
+ function usage() {
22
+ process.stderr.write(`zrv — read a page, a screenshot, or a video as text
23
+ zrv [--md|--a11y] [--tab id] [--url u] [--engine e] [--json]
24
+ zrv --tabs
25
+ zrv ocr <file> [--engine e] [--task transcribe|describe] [--mode scene|interval|all-idr] [--interval s] [--max-frames n]
26
+ zrv snap [--ocr] [--save path]
27
+ zrv mcp
28
+ `);
29
+ process.exit(1);
30
+ }
31
+ function isVideo(p) {
32
+ return /\.(mp4|mov|m4v|webm)$/i.test(p);
33
+ }
34
+ async function copyText(text) {
35
+ await new Promise((resolve) => {
36
+ const pb = spawn("pbcopy");
37
+ pb.stdin.write(text);
38
+ pb.stdin.end();
39
+ pb.on("close", () => resolve());
40
+ });
41
+ }
42
+ function failCode(result) {
43
+ const err = result.error ?? "";
44
+ if (err.includes("cannot describe"))
45
+ return 1;
46
+ if (err.includes("unavailable") || err.includes("weights missing") || err.includes("unset"))
47
+ return 3;
48
+ if (err.includes("input missing") || err.includes("path required"))
49
+ return 2;
50
+ return 4;
51
+ }
52
+ function outResult(result, json) {
53
+ if (json) {
54
+ process.stdout.write(JSON.stringify(result) + "\n");
55
+ }
56
+ else if (result.ok) {
57
+ process.stdout.write(result.text + (result.text.endsWith("\n") ? "" : "\n"));
58
+ }
59
+ else {
60
+ process.stderr.write((result.error ?? "failed") + "\n");
61
+ }
62
+ process.exit(result.ok ? 0 : failCode(result));
63
+ }
64
+ export function parseArgv(raw) {
65
+ const flags = {};
66
+ const pos = [];
67
+ const langs = [];
68
+ for (let i = 0; i < raw.length; i++) {
69
+ const a = raw[i];
70
+ if (a === "-h" || a === "--help")
71
+ flags.help = true;
72
+ else if (a === "--lang" && i + 1 < raw.length)
73
+ langs.push(raw[++i]);
74
+ else if (a.startsWith("--") && BOOL.has(a.slice(2)))
75
+ flags[a.slice(2)] = true;
76
+ else if (a.startsWith("--") && i + 1 < raw.length)
77
+ flags[a.slice(2)] = raw[++i];
78
+ else if (a.startsWith("--"))
79
+ usage();
80
+ else
81
+ pos.push(a);
82
+ }
83
+ const cmd = pos[0] === "ocr" || pos[0] === "snap" || pos[0] === "mcp" ? pos.shift() : "page";
84
+ return { cmd, pos, flags, langs };
85
+ }
86
+ async function main() {
87
+ const argv0 = process.argv[1] ?? "";
88
+ const raw = process.argv.slice(2);
89
+ if (argv0.endsWith("/snap") || argv0 === "snap" || argv0.endsWith("\\snap"))
90
+ raw.unshift("snap");
91
+ const { cmd, pos, flags, langs } = parseArgv(raw);
92
+ if (flags.help)
93
+ usage();
94
+ if (cmd === "mcp") {
95
+ const { startMcp } = await import("./mcp.js");
96
+ await startMcp();
97
+ return;
98
+ }
99
+ const engine = resolveEngine(typeof flags.engine === "string" ? flags.engine : undefined);
100
+ if (cmd === "snap") {
101
+ if (!flags.ocr && !flags.save) {
102
+ const code = await runScreencapture();
103
+ process.exit(code === 0 ? 0 : 130);
104
+ }
105
+ const save = typeof flags.save === "string" ? flags.save : undefined;
106
+ const file = await interactiveCapture({ save });
107
+ if (!file)
108
+ process.exit(130);
109
+ if (flags.ocr) {
110
+ const result = await perceive(engine, { kind: "image", path: file, task: "transcribe" });
111
+ if (!result.ok) {
112
+ process.stderr.write((result.error ?? "ocr failed") + "\n");
113
+ process.exit(failCode(result));
114
+ }
115
+ process.stdout.write(result.text + (result.text.endsWith("\n") ? "" : "\n"));
116
+ await copyText(result.text);
117
+ }
118
+ return;
119
+ }
120
+ if (cmd === "ocr") {
121
+ const file = pos[0];
122
+ const task = flags.task === "describe" ? "describe" : "transcribe";
123
+ if (flags.clipboard) {
124
+ const result = await perceive(engine, {
125
+ kind: "clipboard",
126
+ task,
127
+ level: flags.level === "fast" ? "fast" : "accurate",
128
+ lang: langs.length ? langs : undefined,
129
+ });
130
+ outResult(result, Boolean(flags.json));
131
+ }
132
+ if (!file)
133
+ usage();
134
+ const kind = isVideo(file) ? "video" : "image";
135
+ const modeRaw = typeof flags.mode === "string" ? flags.mode : "scene";
136
+ const mode = modeRaw === "interval" || modeRaw === "all-idr" ? modeRaw : "scene";
137
+ const result = await perceive(engine, {
138
+ kind,
139
+ path: file,
140
+ task,
141
+ level: flags.level === "fast" ? "fast" : "accurate",
142
+ lang: langs.length ? langs : undefined,
143
+ ...(kind === "video"
144
+ ? {
145
+ video: {
146
+ mode,
147
+ interval: flags.interval ? Number(flags.interval) : 2,
148
+ maxFrames: flags["max-frames"] ? Number(flags["max-frames"]) : 60,
149
+ },
150
+ }
151
+ : {}),
152
+ });
153
+ outResult(result, Boolean(flags.json));
154
+ }
155
+ if (flags.tabs) {
156
+ const { port } = await findOpenPort(flags.port ? Number(flags.port) : undefined);
157
+ const tabs = await listTabs(port);
158
+ for (const t of tabs)
159
+ process.stdout.write(`${t.id}\t${t.title}\t${t.url}\n`);
160
+ return;
161
+ }
162
+ if (flags.fetch && typeof flags.url === "string") {
163
+ const { text, markdown } = await fetchUrl(flags.url);
164
+ process.stdout.write((flags.md ? markdown : text) + "\n");
165
+ return;
166
+ }
167
+ const attached = await attach({
168
+ port: flags.port ? Number(flags.port) : undefined,
169
+ tab: typeof flags.tab === "string" ? flags.tab : undefined,
170
+ url: typeof flags.url === "string" ? flags.url : undefined,
171
+ navigate: Boolean(flags.navigate),
172
+ });
173
+ process.stderr.write(`${attached.title}\t${attached.url}\n`);
174
+ try {
175
+ const extracted = await extractPage(attached.client, attached.sessionId, {
176
+ waitMs: flags["wait-ms"] ? Number(flags["wait-ms"]) : undefined,
177
+ waitText: typeof flags["wait-text"] === "string" ? flags["wait-text"] : undefined,
178
+ scroll: Boolean(flags.scroll),
179
+ verbose: Boolean(flags.verbose),
180
+ });
181
+ let body = extracted.text;
182
+ if (flags.a11y)
183
+ body = extracted.a11y;
184
+ else if (flags.md)
185
+ body = extracted.markdown;
186
+ if (flags["ocr-opaque"] && extracted.opaque.fraction >= 0.3) {
187
+ const png = await captureScreenshot(attached.client, attached.sessionId);
188
+ const ocr = await perceive(engine, { kind: "image", bytes: png, task: "transcribe" });
189
+ if (ocr.ok && ocr.text)
190
+ body += (body.endsWith("\n") ? "" : "\n") + "--- opaque ocr ---\n" + ocr.text;
191
+ }
192
+ if (flags.json) {
193
+ process.stdout.write(JSON.stringify({
194
+ ok: true,
195
+ source: "cdp",
196
+ port: attached.port,
197
+ targetId: attached.targetId,
198
+ title: attached.title,
199
+ url: attached.url,
200
+ text: body,
201
+ opaque: extracted.opaque,
202
+ }) + "\n");
203
+ }
204
+ else {
205
+ process.stdout.write(body + (body.endsWith("\n") ? "" : "\n"));
206
+ }
207
+ }
208
+ finally {
209
+ attached.client.close();
210
+ }
211
+ }
212
+ const isMain = process.argv[1]?.endsWith("cli.js") ||
213
+ process.argv[1]?.endsWith("cli.ts") ||
214
+ process.argv[1]?.endsWith("/zrv") ||
215
+ process.argv[1]?.endsWith("/snap");
216
+ if (isMain) {
217
+ main().catch((err) => {
218
+ const code = typeof err === "object" && err && "code" in err ? Number(err.code) : 1;
219
+ process.stderr.write((err instanceof Error ? err.message : String(err)) + "\n");
220
+ process.exit(Number.isFinite(code) && code > 0 ? code : 1);
221
+ });
222
+ }
223
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ function runFm(argv) {
4
+ return new Promise((resolve, reject) => {
5
+ const child = spawn("fm", argv, { stdio: ["ignore", "pipe", "pipe"] });
6
+ let stdout = "";
7
+ let stderr = "";
8
+ child.stdout.setEncoding("utf8");
9
+ child.stderr.setEncoding("utf8");
10
+ child.stdout.on("data", (c) => {
11
+ stdout += c;
12
+ });
13
+ child.stderr.on("data", (c) => {
14
+ stderr += c;
15
+ });
16
+ child.on("error", (err) => reject(err));
17
+ child.on("close", (code) => resolve({ code: code ?? 1, stdout, stderr }));
18
+ });
19
+ }
20
+ export const appleFm = {
21
+ id: "apple-fm",
22
+ capabilities: ["transcribe", "describe"],
23
+ async perceive(input) {
24
+ const t0 = Date.now();
25
+ const task = input.task ?? "transcribe";
26
+ if (!existsSync("/usr/bin/fm")) {
27
+ return {
28
+ ok: false,
29
+ engine: "apple-fm",
30
+ task,
31
+ text: "",
32
+ ms: 0,
33
+ error: "apple-fm unavailable",
34
+ };
35
+ }
36
+ if (input.kind === "video") {
37
+ return {
38
+ ok: false,
39
+ engine: "apple-fm",
40
+ task,
41
+ text: "",
42
+ ms: 0,
43
+ error: "apple-fm does not OCR video; use apple-vision",
44
+ };
45
+ }
46
+ if (input.kind === "clipboard" || !input.path) {
47
+ return {
48
+ ok: false,
49
+ engine: "apple-fm",
50
+ task,
51
+ text: "",
52
+ ms: 0,
53
+ error: "apple-fm needs --image path (clipboard: snap to a file first)",
54
+ };
55
+ }
56
+ const prompt = task === "describe"
57
+ ? "Describe this image. If it contains text, quote the text."
58
+ : "Transcribe all text verbatim. Do not paraphrase.";
59
+ try {
60
+ const { code, stdout, stderr } = await runFm([
61
+ "respond",
62
+ "--image",
63
+ input.path,
64
+ "--text",
65
+ prompt,
66
+ "--tool",
67
+ "ocr",
68
+ "--no-stream",
69
+ ]);
70
+ const text = stdout.trim();
71
+ if (code !== 0 || !text) {
72
+ return {
73
+ ok: false,
74
+ engine: "apple-fm",
75
+ task,
76
+ text: "",
77
+ ms: Date.now() - t0,
78
+ model: "system",
79
+ error: stderr.trim() || `fm exit ${code}`,
80
+ };
81
+ }
82
+ return {
83
+ ok: true,
84
+ engine: "apple-fm",
85
+ task,
86
+ text,
87
+ ms: Date.now() - t0,
88
+ model: "system",
89
+ };
90
+ }
91
+ catch (err) {
92
+ return {
93
+ ok: false,
94
+ engine: "apple-fm",
95
+ task,
96
+ text: "",
97
+ ms: Date.now() - t0,
98
+ error: err instanceof Error ? err.message : String(err),
99
+ };
100
+ }
101
+ },
102
+ };
103
+ //# sourceMappingURL=apple-fm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apple-fm.js","sourceRoot":"","sources":["../../src/engines/apple-fm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import { writeFileSync, mkdtempSync, rmSync, realpathSync, statSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { spawnNative } from "../native.js";
5
+ function parseJson(stdout) {
6
+ const line = stdout.trim().split("\n").filter(Boolean).at(-1) ?? "{}";
7
+ try {
8
+ const raw = JSON.parse(line);
9
+ return { ...raw, engine: "apple-vision", task: raw.task ?? "transcribe" };
10
+ }
11
+ catch {
12
+ return {
13
+ ok: false,
14
+ engine: "apple-vision",
15
+ task: "transcribe",
16
+ text: "",
17
+ ms: 0,
18
+ error: `native json parse failed: ${line.slice(0, 200)}`,
19
+ };
20
+ }
21
+ }
22
+ export const appleVision = {
23
+ id: "apple-vision",
24
+ capabilities: ["transcribe"],
25
+ async perceive(input) {
26
+ const task = input.task ?? "transcribe";
27
+ const level = input.level ?? "accurate";
28
+ const langs = input.lang ?? ["en-US"];
29
+ const langArgs = langs.flatMap((l) => ["--lang", l]);
30
+ if (input.kind === "clipboard") {
31
+ const { stdout, stderr } = await spawnNative(["ocr-clipboard", "--level", level, ...langArgs, "--json"]);
32
+ return parseJson(stdout || stderr);
33
+ }
34
+ let path = input.path;
35
+ let tmp;
36
+ if (input.bytes) {
37
+ if (input.bytes.length > 30 * 1024 * 1024) {
38
+ return { ok: false, engine: "apple-vision", task, text: "", ms: 0, error: "input exceeds 30 MB" };
39
+ }
40
+ tmp = mkdtempSync(join(tmpdir(), "zrv-"));
41
+ path = join(tmp, "in.bin");
42
+ writeFileSync(path, input.bytes);
43
+ }
44
+ if (!path) {
45
+ return { ok: false, engine: "apple-vision", task, text: "", ms: 0, error: "path required" };
46
+ }
47
+ if (!tmp) {
48
+ try {
49
+ path = realpathSync(path);
50
+ if (!statSync(path).isFile()) {
51
+ return { ok: false, engine: "apple-vision", task, text: "", ms: 0, error: "path required" };
52
+ }
53
+ }
54
+ catch {
55
+ return { ok: false, engine: "apple-vision", task, text: "", ms: 0, error: "input missing" };
56
+ }
57
+ }
58
+ try {
59
+ if (input.kind === "video") {
60
+ const mode = input.video?.mode ?? "scene";
61
+ const interval = String(input.video?.interval ?? 2);
62
+ const maxFrames = String(input.video?.maxFrames ?? 60);
63
+ const { stdout, stderr } = await spawnNative([
64
+ "ocr-video",
65
+ "--input",
66
+ path,
67
+ "--mode",
68
+ mode,
69
+ "--interval",
70
+ interval,
71
+ "--max-frames",
72
+ maxFrames,
73
+ "--level",
74
+ level,
75
+ ...langArgs,
76
+ "--json",
77
+ ], { timeoutMs: 180_000 });
78
+ return parseJson(stdout || stderr);
79
+ }
80
+ const { stdout, stderr } = await spawnNative(["ocr-image", "--input", path, "--level", level, ...langArgs, "--json"]);
81
+ return parseJson(stdout || stderr);
82
+ }
83
+ finally {
84
+ if (tmp)
85
+ rmSync(tmp, { recursive: true, force: true });
86
+ }
87
+ },
88
+ };
89
+ //# sourceMappingURL=apple-vision.js.map