sygal-cli 0.3.0 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/index.js +123 -21
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,21 @@ partagent toujours le même numéro de version, publiés ensemble, même si un
|
|
|
9
9
|
seul a réellement changé de contenu — même principe que l'app desktop.
|
|
10
10
|
Détails du process : `CLI_RELEASE_GUIDE.md` à la racine du repo.
|
|
11
11
|
|
|
12
|
+
## 0.3.1
|
|
13
|
+
|
|
14
|
+
- **Corrige la 0.3.0**, publiée avec un bundle `sygal-cli` (dist/index.js)
|
|
15
|
+
périmé — construit avant les changements ci-dessous, donc `.rtf` restait
|
|
16
|
+
"Extension non supportée" et aucun des correctifs OCR n'était réellement
|
|
17
|
+
présent malgré le numéro de version. Ajout d'un hook `prepublishOnly`
|
|
18
|
+
(rebuild automatique avant toute publication) pour que ça ne puisse plus
|
|
19
|
+
se reproduire. **Si vous avez installé la 0.3.0, mettez à jour vers la
|
|
20
|
+
0.3.1.**
|
|
21
|
+
- Nouveau : support des formats **AVIF** et **RTF** (texte brut, tableaux
|
|
22
|
+
RTF rendus lisibles ligne par ligne).
|
|
23
|
+
- Fix : messages d'erreur clairs pour HEIC/HEIF (HEVC non pris en charge),
|
|
24
|
+
AVIF "séquence"/rafale, et WebP envoyé au fournisseur NVIDIA — au lieu
|
|
25
|
+
d'erreurs génériques ou d'un faux "erreur réseau".
|
|
26
|
+
|
|
12
27
|
## 0.2.1
|
|
13
28
|
|
|
14
29
|
- Notice de mise à jour après chaque conversion (terminal interactif
|
package/dist/index.js
CHANGED
|
@@ -58,6 +58,7 @@ var SUPPORTED_EXTENSIONS = {
|
|
|
58
58
|
eml: "message/rfc822",
|
|
59
59
|
ipynb: "application/x-ipynb+json",
|
|
60
60
|
zip: "application/zip",
|
|
61
|
+
rtf: "application/rtf",
|
|
61
62
|
// OCR (images) — HEIC/TIFF normalized to PNG before the API call (sharp)
|
|
62
63
|
png: "image/png",
|
|
63
64
|
jpg: "image/jpeg",
|
|
@@ -67,8 +68,13 @@ var SUPPORTED_EXTENSIONS = {
|
|
|
67
68
|
tiff: "image/tiff",
|
|
68
69
|
tif: "image/tiff",
|
|
69
70
|
heic: "image/heic",
|
|
70
|
-
heif: "image/heif"
|
|
71
|
+
heif: "image/heif",
|
|
72
|
+
avif: "image/avif"
|
|
71
73
|
};
|
|
74
|
+
var PACKAGE_DIR_EXTENSIONS = /* @__PURE__ */ new Set(["epub"]);
|
|
75
|
+
function isPackageDirExtension(extension) {
|
|
76
|
+
return PACKAGE_DIR_EXTENSIONS.has(extension.toLowerCase().replace(/^\./, ""));
|
|
77
|
+
}
|
|
72
78
|
function mimeForExtension(extension) {
|
|
73
79
|
return SUPPORTED_EXTENSIONS[extension.toLowerCase()] ?? null;
|
|
74
80
|
}
|
|
@@ -89,7 +95,7 @@ function countTokens(text) {
|
|
|
89
95
|
|
|
90
96
|
// ../core/src/image-normalize.ts
|
|
91
97
|
var import_sharp = __toESM(require("sharp"));
|
|
92
|
-
var NEEDS_PNG = /* @__PURE__ */ new Set([".heic", ".heif", ".tiff", ".tif"]);
|
|
98
|
+
var NEEDS_PNG = /* @__PURE__ */ new Set([".heic", ".heif", ".tiff", ".tif", ".avif"]);
|
|
93
99
|
function needsPngNormalization(ext) {
|
|
94
100
|
return NEEDS_PNG.has(ext.toLowerCase());
|
|
95
101
|
}
|
|
@@ -119,10 +125,16 @@ var OCR_MODELS = {
|
|
|
119
125
|
openai: "gpt-4o",
|
|
120
126
|
mistral: "mistral-ocr-latest",
|
|
121
127
|
claude: "claude-sonnet-4-6",
|
|
122
|
-
// Bascule
|
|
123
|
-
// remplace
|
|
124
|
-
//
|
|
125
|
-
|
|
128
|
+
// Bascule 27/07/2026 (banc comparatif sur images réelles, validation Cyrill) :
|
|
129
|
+
// nemotron-nano-12b-v2-vl remplace llama-3.1-nemotron-nano-vl-8b-v1. Sur une
|
|
130
|
+
// capture de dossier de 22 lignes, le 8b s'arrêtait au premier en-tête (« Nom »,
|
|
131
|
+
// 1 token) 3 fois sur 3 ; le 12b la transcrit intégralement 3 fois sur 3, accents
|
|
132
|
+
// préservés. Il respecte aussi la consigne de langue sur une image sans texte
|
|
133
|
+
// (description en français, là où le 8b répondait en anglais), sans confabuler.
|
|
134
|
+
// Entraîné sur DocLayNet / PubTables-1M / PDF CommonCrawl — OCRBench 85,6.
|
|
135
|
+
// Précédemment : llama-3.2-11b-vision-instruct, écarté le 18/07/2026 (5/5
|
|
136
|
+
// confabulations sur un portrait sans texte).
|
|
137
|
+
nvidia: "nvidia/nemotron-nano-12b-v2-vl",
|
|
126
138
|
gemini: "gemini-2.0-flash"
|
|
127
139
|
};
|
|
128
140
|
var MISTRAL_TRANSLATE_MODEL = "mistral-large-latest";
|
|
@@ -219,7 +231,9 @@ var SidecarManager = class {
|
|
|
219
231
|
const response = await this.request({ id: (0, import_node_crypto.randomUUID)(), cmd: "pdf_probe", path });
|
|
220
232
|
return {
|
|
221
233
|
pageCount: response.page_count ?? 0,
|
|
222
|
-
pagesTextLength: response.pages_text_length ?? []
|
|
234
|
+
pagesTextLength: response.pages_text_length ?? [],
|
|
235
|
+
pageWidth: response.page_width ?? void 0,
|
|
236
|
+
pageHeight: response.page_height ?? void 0
|
|
223
237
|
};
|
|
224
238
|
}
|
|
225
239
|
async renderPdfPage(path, page) {
|
|
@@ -406,7 +420,7 @@ var LOCALE_LANGUAGE = {
|
|
|
406
420
|
es: "Spanish"
|
|
407
421
|
};
|
|
408
422
|
function buildOcrPrompt(labelLanguage = "French") {
|
|
409
|
-
return `Transcribe ALL visible text in this image verbatim as clean Markdown, preserving the document layout: headings, lists and tables exactly as shown. Transcribe EVERY line, row and table cell faithfully \u2014 never summarize, skip, merge, reorder, or invent rows, values, or text. If a cell is empty, leave it empty. Keep the document text in its ORIGINAL language exactly as written \u2014 do not translate it. Do NOT invent a table of contents, section titles, or any heading/structure that is not visually present in the document. If you must add a label of your own that is not in the document, write it in ${labelLanguage}. While transcribing, do NOT describe the scene, the people, or any visual elements that are not text. ONLY IF the image contains no readable text at all: instead of an empty document, describe the image in ${labelLanguage} as clean Markdown \u2014 one short paragraph covering the subject, the setting and the dominant colors, factual and concise, without inventing any text that is not visible. When describing, NEVER invent or guess names, ages, occupations, employers, biographies, locations, dates, or any information about people or organizations. Describe only what is physically visible in the image; if something is not visible, do not mention it. Output only the Markdown.`;
|
|
423
|
+
return `Transcribe ALL visible text in this image verbatim as clean Markdown, preserving the document layout: headings, lists and tables exactly as shown. Render every table as a GitHub-flavored Markdown table using pipes (| \u2026 | \u2026 |) with a header separator row \u2014 NEVER output LaTeX, never use \\begin{tabular}, \\multicolumn or any TeX command. Transcribe EVERY line, row and table cell faithfully \u2014 never summarize, skip, merge, reorder, or invent rows, values, or text. If a cell is empty, leave it empty. Keep the document text in its ORIGINAL language exactly as written \u2014 do not translate it. Do NOT invent a table of contents, section titles, or any heading/structure that is not visually present in the document. If you must add a label of your own that is not in the document, write it in ${labelLanguage}. While transcribing, do NOT describe the scene, the people, or any visual elements that are not text. ONLY IF the image contains no readable text at all: instead of an empty document, describe the image in ${labelLanguage} as clean Markdown \u2014 one short paragraph covering the subject, the setting and the dominant colors, factual and concise, without inventing any text that is not visible. When describing, NEVER invent or guess names, ages, occupations, employers, biographies, locations, dates, or any information about people or organizations. Describe only what is physically visible in the image; if something is not visible, do not mention it. Output only the Markdown.`;
|
|
410
424
|
}
|
|
411
425
|
function buildTranslatePrompt(targetLocale) {
|
|
412
426
|
const language = LOCALE_LANGUAGE[targetLocale] ?? "English";
|
|
@@ -664,6 +678,30 @@ var mistralProvider = {
|
|
|
664
678
|
}
|
|
665
679
|
};
|
|
666
680
|
|
|
681
|
+
// ../core/src/ocr/latex-tables.ts
|
|
682
|
+
var TABULAR_RE = /\\begin\{tabular\}(?:\{[^}]*\})?([\s\S]*?)\\end\{tabular\}/g;
|
|
683
|
+
function cleanCell(cell) {
|
|
684
|
+
return cell.replace(/\\multicolumn\{\d+\}\{[^}]*\}\{([\s\S]*?)\}/g, "$1").replace(/\\multirow\{\d+\}\{[^}]*\}\{([\s\S]*?)\}/g, "$1").replace(/\\textbf\{([\s\S]*?)\}/g, "**$1**").replace(/\\textit\{([\s\S]*?)\}/g, "*$1*").replace(/\\(?:hline|toprule|midrule|bottomrule)\b/g, "").replace(/\\\\/g, "").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
685
|
+
}
|
|
686
|
+
function tabularToMarkdown(body) {
|
|
687
|
+
const rows = body.split(/\\\\/).map((row) => row.trim()).filter((row) => row.replace(/\\(?:hline|toprule|midrule|bottomrule)\b/g, "").trim().length > 0).map((row) => row.split("&").map(cleanCell)).filter((cells) => cells.some((cell) => cell.length > 0));
|
|
688
|
+
if (rows.length === 0) return null;
|
|
689
|
+
const columns = Math.max(...rows.map((row) => row.length));
|
|
690
|
+
if (columns < 2) return rows.map((row) => row[0]).join("\n\n");
|
|
691
|
+
const pad = (cells) => `| ${[...cells, ...Array(columns - cells.length).fill("")].join(" | ")} |`;
|
|
692
|
+
const [header, ...body_] = rows;
|
|
693
|
+
return [pad(header), `| ${Array(columns).fill("---").join(" | ")} |`, ...body_.map(pad)].join(
|
|
694
|
+
"\n"
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
function convertLatexTables(markdown) {
|
|
698
|
+
if (!markdown.includes("\\begin{tabular}")) return markdown;
|
|
699
|
+
return markdown.replace(TABULAR_RE, (whole, body) => {
|
|
700
|
+
const converted = tabularToMarkdown(body);
|
|
701
|
+
return converted ?? whole;
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
|
|
667
705
|
// ../core/src/ocr/nvidia-provider.ts
|
|
668
706
|
var NVIDIA_OCR_MODEL = OCR_MODELS.nvidia;
|
|
669
707
|
var ENDPOINT4 = "https://integrate.api.nvidia.com/v1/chat/completions";
|
|
@@ -674,7 +712,7 @@ var nvidiaProvider = {
|
|
|
674
712
|
async convertImage(img, mime, apiKey, signal, prompt) {
|
|
675
713
|
let payloadImg = img;
|
|
676
714
|
let payloadMime = mime;
|
|
677
|
-
if (payloadImg.length > NVIDIA_MAX_IMAGE_BYTES) {
|
|
715
|
+
if (payloadMime === "image/webp" || payloadImg.length > NVIDIA_MAX_IMAGE_BYTES) {
|
|
678
716
|
payloadImg = await shrinkToMaxBytes(payloadImg, NVIDIA_MAX_IMAGE_BYTES);
|
|
679
717
|
payloadMime = "image/jpeg";
|
|
680
718
|
}
|
|
@@ -808,6 +846,28 @@ function collapseRepeatedLines(markdown) {
|
|
|
808
846
|
}
|
|
809
847
|
return kept.join("\n");
|
|
810
848
|
}
|
|
849
|
+
var TRUNCATED_COMPLETION_TOKENS = 3;
|
|
850
|
+
function completionTokensOf(raw) {
|
|
851
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
852
|
+
const usage = raw.usage;
|
|
853
|
+
if (typeof usage !== "object" || usage === null) return null;
|
|
854
|
+
const value = usage.completion_tokens ?? usage.output_tokens;
|
|
855
|
+
return typeof value === "number" ? value : null;
|
|
856
|
+
}
|
|
857
|
+
function looksTruncated(result) {
|
|
858
|
+
const tokens = completionTokensOf(result.raw);
|
|
859
|
+
return tokens !== null && tokens <= TRUNCATED_COMPLETION_TOKENS;
|
|
860
|
+
}
|
|
861
|
+
function stripEnclosingCodeFence(markdown) {
|
|
862
|
+
const trimmed = markdown.trim();
|
|
863
|
+
const opening = /^```([a-zA-Z]*)\r?\n?/.exec(trimmed);
|
|
864
|
+
if (!opening || !trimmed.endsWith("```") || trimmed.length < 6) return markdown;
|
|
865
|
+
const language = opening[1].toLowerCase();
|
|
866
|
+
if (language && language !== "markdown" && language !== "md") return markdown;
|
|
867
|
+
const inner = trimmed.slice(opening[0].length, -3);
|
|
868
|
+
if (inner.includes("```")) return markdown;
|
|
869
|
+
return inner.trim();
|
|
870
|
+
}
|
|
811
871
|
var REFUSAL_RE = /^(i['’]?m sorry|i cannot|i can['’]?t|i am (unable|not able)|sorry,|je suis désolé|désolé,|je ne peux pas|lo siento|mi dispiace|es tut mir leid|ich kann)/i;
|
|
812
872
|
function isProviderRefusal(markdown) {
|
|
813
873
|
return REFUSAL_RE.test(markdown.trim().slice(0, 80));
|
|
@@ -829,7 +889,8 @@ var IMAGE_MIME = {
|
|
|
829
889
|
".tiff": "image/tiff",
|
|
830
890
|
".tif": "image/tiff",
|
|
831
891
|
".heic": "image/heic",
|
|
832
|
-
".heif": "image/heif"
|
|
892
|
+
".heif": "image/heif",
|
|
893
|
+
".avif": "image/avif"
|
|
833
894
|
};
|
|
834
895
|
var ApiOcrRunner = class {
|
|
835
896
|
constructor(settings, options = {}) {
|
|
@@ -850,7 +911,21 @@ var ApiOcrRunner = class {
|
|
|
850
911
|
let img = await (0, import_promises.readFile)(sourcePath);
|
|
851
912
|
let sendMime = mime;
|
|
852
913
|
if (needsPngNormalization(ext)) {
|
|
853
|
-
|
|
914
|
+
try {
|
|
915
|
+
img = await toPngBuffer(img);
|
|
916
|
+
} catch (err) {
|
|
917
|
+
const detail = err instanceof Error ? err.message.split("\n")[0] : String(err);
|
|
918
|
+
if (ext === ".heic" || ext === ".heif") {
|
|
919
|
+
throw new ConversionError(
|
|
920
|
+
"HEIC_HEVC_UNSUPPORTED",
|
|
921
|
+
`D\xE9codage ${ext.slice(1).toUpperCase()} (codec HEVC) impossible : ${detail}`
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
throw new ConversionError(
|
|
925
|
+
"IMAGE_DECODE_UNSUPPORTED",
|
|
926
|
+
`D\xE9codage ${ext.slice(1).toUpperCase()} impossible (format ou variante non pris en charge) : ${detail}`
|
|
927
|
+
);
|
|
928
|
+
}
|
|
854
929
|
sendMime = "image/png";
|
|
855
930
|
}
|
|
856
931
|
if (settings.customProviderId) {
|
|
@@ -866,13 +941,23 @@ var ApiOcrRunner = class {
|
|
|
866
941
|
if (!apiKey2) {
|
|
867
942
|
throw new ConversionError("API_KEY_MISSING", `Aucune cl\xE9 API pour ${custom.name}`);
|
|
868
943
|
}
|
|
869
|
-
|
|
944
|
+
let result2 = await this.callCustomWithRetry(custom, img, sendMime, apiKey2, prompt);
|
|
945
|
+
if (looksTruncated(result2) && !isProviderRefusal(result2.markdown)) {
|
|
946
|
+
const second = await this.callCustomWithRetry(custom, img, sendMime, apiKey2, prompt);
|
|
947
|
+
if (second.markdown.trim().length > result2.markdown.trim().length) result2 = second;
|
|
948
|
+
}
|
|
870
949
|
if (isProviderRefusal(result2.markdown)) {
|
|
871
950
|
throw new ConversionError("OCR_REFUSED", `${custom.name} : refus du mod\xE8le`);
|
|
872
951
|
}
|
|
873
952
|
return {
|
|
874
|
-
markdown:
|
|
875
|
-
|
|
953
|
+
markdown: convertLatexTables(
|
|
954
|
+
stripEnclosingCodeFence(collapseRepeatedLines(result2.markdown))
|
|
955
|
+
),
|
|
956
|
+
meta: {
|
|
957
|
+
provider: custom.name,
|
|
958
|
+
model: custom.model,
|
|
959
|
+
...looksTruncated(result2) ? { truncated: true } : {}
|
|
960
|
+
}
|
|
876
961
|
};
|
|
877
962
|
}
|
|
878
963
|
const providerId = settings.ocrProvider;
|
|
@@ -884,13 +969,24 @@ var ApiOcrRunner = class {
|
|
|
884
969
|
`Aucune cl\xE9 API enregistr\xE9e pour le provider ${providerId}`
|
|
885
970
|
);
|
|
886
971
|
}
|
|
887
|
-
|
|
972
|
+
let result = await this.callWithRetry(provider, img, sendMime, apiKey, prompt);
|
|
973
|
+
if (looksTruncated(result) && !isProviderRefusal(result.markdown)) {
|
|
974
|
+
const second = await this.callWithRetry(provider, img, sendMime, apiKey, prompt);
|
|
975
|
+
if (second.markdown.trim().length > result.markdown.trim().length) result = second;
|
|
976
|
+
}
|
|
888
977
|
if (isProviderRefusal(result.markdown)) {
|
|
889
978
|
throw new ConversionError("OCR_REFUSED", `${provider.id} : refus du mod\xE8le`);
|
|
890
979
|
}
|
|
891
980
|
return {
|
|
892
|
-
markdown: collapseRepeatedLines(result.markdown),
|
|
893
|
-
meta: {
|
|
981
|
+
markdown: convertLatexTables(stripEnclosingCodeFence(collapseRepeatedLines(result.markdown))),
|
|
982
|
+
meta: {
|
|
983
|
+
provider: provider.id,
|
|
984
|
+
model: provider.model,
|
|
985
|
+
// Rejeu compris, le modèle s'est arrêté au premier libellé : le
|
|
986
|
+
// ConversionService en fait un avertissement dans le journal plutôt
|
|
987
|
+
// que d'enregistrer silencieusement une transcription amputée.
|
|
988
|
+
...looksTruncated(result) ? { truncated: true } : {}
|
|
989
|
+
}
|
|
894
990
|
};
|
|
895
991
|
}
|
|
896
992
|
/** Tentative initiale + retries backoff sur erreurs retryable uniquement */
|
|
@@ -1193,7 +1289,7 @@ var TranslationRunner = class {
|
|
|
1193
1289
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1194
1290
|
try {
|
|
1195
1291
|
const result = await attempt(controller.signal);
|
|
1196
|
-
return collapseRepeatedLines(result.markdown);
|
|
1292
|
+
return convertLatexTables(stripEnclosingCodeFence(collapseRepeatedLines(result.markdown)));
|
|
1197
1293
|
} catch (err) {
|
|
1198
1294
|
if (controller.signal.aborted) {
|
|
1199
1295
|
throw new ConversionError("TIMEOUT", `${providerLabel} : d\xE9lai de traduction d\xE9pass\xE9`);
|
|
@@ -1550,6 +1646,8 @@ var CONVERSION_CODE_MAP = {
|
|
|
1550
1646
|
OCR_REFUSED: "ocr_refused",
|
|
1551
1647
|
URL_FORBIDDEN: "url_forbidden",
|
|
1552
1648
|
URL_UNREACHABLE: "url_unreachable",
|
|
1649
|
+
HEIC_HEVC_UNSUPPORTED: "heic_hevc_unsupported",
|
|
1650
|
+
IMAGE_DECODE_UNSUPPORTED: "image_decode_unsupported",
|
|
1553
1651
|
UNKNOWN: "unknown"
|
|
1554
1652
|
};
|
|
1555
1653
|
var CliCodedError = class extends Error {
|
|
@@ -1646,7 +1744,7 @@ async function expandInputs(inputs) {
|
|
|
1646
1744
|
});
|
|
1647
1745
|
continue;
|
|
1648
1746
|
}
|
|
1649
|
-
if (stat.isDirectory()) {
|
|
1747
|
+
if (stat.isDirectory() && !isPackageDirExtension((0, import_node_path5.extname)(path).slice(1))) {
|
|
1650
1748
|
await collectFromDir(path, jobs);
|
|
1651
1749
|
} else {
|
|
1652
1750
|
jobs.push({ input: path, kind: "file" });
|
|
@@ -1666,7 +1764,11 @@ async function collectFromDir(dir, jobs) {
|
|
|
1666
1764
|
if (entry.name.startsWith(".")) continue;
|
|
1667
1765
|
const full = (0, import_node_path5.join)(dir, entry.name);
|
|
1668
1766
|
if (entry.isDirectory()) {
|
|
1669
|
-
|
|
1767
|
+
if (isPackageDirExtension((0, import_node_path5.extname)(entry.name).slice(1))) {
|
|
1768
|
+
jobs.push({ input: full, kind: "file" });
|
|
1769
|
+
} else {
|
|
1770
|
+
await collectFromDir(full, jobs);
|
|
1771
|
+
}
|
|
1670
1772
|
} else if (entry.isFile() && mimeForExtension((0, import_node_path5.extname)(entry.name).slice(1))) {
|
|
1671
1773
|
jobs.push({ input: full, kind: "file" });
|
|
1672
1774
|
}
|
|
@@ -2679,7 +2781,7 @@ Exemples :
|
|
|
2679
2781
|
let singleFile = false;
|
|
2680
2782
|
if (paths.length === 1 && !/^https?:\/\//i.test(paths[0])) {
|
|
2681
2783
|
try {
|
|
2682
|
-
singleFile = (0, import_node_fs8.statSync)(paths[0]).isFile();
|
|
2784
|
+
singleFile = (0, import_node_fs8.statSync)(paths[0]).isFile() || isPackageDirExtension((0, import_node_path10.extname)(paths[0]).slice(1));
|
|
2683
2785
|
} catch {
|
|
2684
2786
|
singleFile = true;
|
|
2685
2787
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sygal-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Sygal CLI - convert files and web pages to Markdown/JSON (local MarkItDown pipeline, OCR for images)",
|
|
5
5
|
"author": "Cyrill Semah",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"@sygal/core": "0.1.0"
|
|
38
38
|
},
|
|
39
39
|
"optionalDependencies": {
|
|
40
|
-
"@sygal/sidecar-
|
|
41
|
-
"@sygal/sidecar-darwin-arm64": "0.3.
|
|
42
|
-
"@sygal/sidecar-
|
|
40
|
+
"@sygal/sidecar-win32-x64": "0.3.5",
|
|
41
|
+
"@sygal/sidecar-darwin-arm64": "0.3.5",
|
|
42
|
+
"@sygal/sidecar-linux-x64": "0.3.5"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/index.js --external:sharp --external:gpt-tokenizer --external:commander --external:picocolors",
|