sygal-cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,23 @@ 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.2.1
13
+
14
+ - Notice de mise à jour après chaque conversion (terminal interactif
15
+ uniquement) : `Mise à jour disponible : X → Y`, avec proposition
16
+ d'installer directement (`o`/`N`). Jamais affichée en mode `--json`
17
+ (agents), jamais de prompt bloquant dans un script/CI. Vérification
18
+ cachée 24h pour ne pas ralentir les conversions.
19
+
20
+ ## 0.2.0
21
+
22
+ - **Versioning verrouillé (lockstep)** : les 4 paquets montent désormais
23
+ toujours ensemble au même numéro de version, même si un seul a changé.
24
+ - Build des 3 binaires sidecar entièrement automatisé via GitHub Actions
25
+ (`cli-build-sidecars.yml`, 3 jobs parallèles macOS/Windows/Linux) — plus
26
+ besoin de machine physique (Mac local ou PC Windows transporté à la main).
27
+ - Script `scripts/bump-cli-versions.mjs` pour le bump lockstep en une commande.
28
+
12
29
  ## 0.1.3
13
30
 
14
31
  - Ajout du paquet `@sygal/sidecar-linux-x64` (glibc 2.28+, Ubuntu 18.04+,
package/dist/index.js CHANGED
@@ -23,8 +23,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
 
25
25
  // src/index.ts
26
- var import_node_fs7 = require("node:fs");
27
- var import_node_path9 = require("node:path");
26
+ var import_node_fs8 = require("node:fs");
27
+ var import_node_path10 = require("node:path");
28
28
  var import_commander = require("commander");
29
29
  var import_picocolors3 = __toESM(require("picocolors"));
30
30
 
@@ -2452,7 +2452,13 @@ function resolveSidecarCommand() {
2452
2452
 
2453
2453
  // src/update.ts
2454
2454
  var import_node_child_process3 = require("node:child_process");
2455
+ var import_node_fs7 = require("node:fs");
2456
+ var import_node_os5 = require("node:os");
2457
+ var import_node_readline2 = require("node:readline");
2458
+ var import_node_path9 = require("node:path");
2455
2459
  var REGISTRY_URL = "https://registry.npmjs.org/sygal-cli/latest";
2460
+ var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
2461
+ var CHECK_TIMEOUT_MS = 1500;
2456
2462
  function compareVersions(a, b) {
2457
2463
  const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
2458
2464
  const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
@@ -2506,9 +2512,67 @@ function runGlobalInstall() {
2506
2512
  child.on("error", () => resolve2(1));
2507
2513
  });
2508
2514
  }
2515
+ function cachePath() {
2516
+ const configPath2 = process.env.SYGAL_CONFIG_PATH ?? (0, import_node_path9.join)((0, import_node_os5.homedir)(), ".sygal", "config.json");
2517
+ return (0, import_node_path9.join)((0, import_node_path9.dirname)(configPath2), "update-check.json");
2518
+ }
2519
+ function loadCache() {
2520
+ const path = cachePath();
2521
+ if (!(0, import_node_fs7.existsSync)(path)) return null;
2522
+ try {
2523
+ return JSON.parse((0, import_node_fs7.readFileSync)(path, "utf-8"));
2524
+ } catch {
2525
+ return null;
2526
+ }
2527
+ }
2528
+ function saveCache(cache) {
2529
+ try {
2530
+ const path = cachePath();
2531
+ (0, import_node_fs7.mkdirSync)((0, import_node_path9.dirname)(path), { recursive: true });
2532
+ (0, import_node_fs7.writeFileSync)(path, JSON.stringify(cache, null, 2) + "\n");
2533
+ } catch {
2534
+ }
2535
+ }
2536
+ function shouldRecheck(cache, now, ttlMs = CHECK_TTL_MS) {
2537
+ if (!cache) return true;
2538
+ const last = Date.parse(cache.lastCheckedAt);
2539
+ return !Number.isFinite(last) || now - last > ttlMs;
2540
+ }
2541
+ async function checkAndCacheUpdate(currentVersion, fetchImpl = (url) => fetch(url, { signal: AbortSignal.timeout(CHECK_TIMEOUT_MS) })) {
2542
+ try {
2543
+ const cache = loadCache();
2544
+ const now = Date.now();
2545
+ let latestKnown = cache?.latestKnown ?? null;
2546
+ if (shouldRecheck(cache, now)) {
2547
+ const result = await checkForUpdate(currentVersion, fetchImpl);
2548
+ if (!result.error) latestKnown = result.latest;
2549
+ saveCache({ lastCheckedAt: new Date(now).toISOString(), latestKnown });
2550
+ }
2551
+ return {
2552
+ updateAvailable: latestKnown !== null && compareVersions(latestKnown, currentVersion) > 0,
2553
+ latest: latestKnown
2554
+ };
2555
+ } catch {
2556
+ return { updateAvailable: false, latest: null };
2557
+ }
2558
+ }
2559
+ function parseYesNo(answer, defaultNo) {
2560
+ const normalized = answer.trim().toLowerCase();
2561
+ if (!normalized) return !defaultNo;
2562
+ return ["o", "oui", "y", "yes"].includes(normalized);
2563
+ }
2564
+ function promptYesNo(question, defaultNo = true) {
2565
+ return new Promise((resolve2) => {
2566
+ const rl = (0, import_node_readline2.createInterface)({ input: process.stdin, output: process.stdout });
2567
+ rl.question(`${question} ${defaultNo ? "(o/N)" : "(O/n)"} `, (answer) => {
2568
+ rl.close();
2569
+ resolve2(parseYesNo(answer, defaultNo));
2570
+ });
2571
+ });
2572
+ }
2509
2573
 
2510
2574
  // src/index.ts
2511
- var pkg = JSON.parse((0, import_node_fs7.readFileSync)((0, import_node_path9.join)(__dirname, "..", "package.json"), "utf-8"));
2575
+ var pkg = JSON.parse((0, import_node_fs8.readFileSync)((0, import_node_path10.join)(__dirname, "..", "package.json"), "utf-8"));
2512
2576
  var program = new import_commander.Command();
2513
2577
  program.name("sygal").description(
2514
2578
  "Convertit fichiers et pages web en Markdown/JSON via le pipeline local MarkItDown, avec OCR API pour les images."
@@ -2615,7 +2679,7 @@ Exemples :
2615
2679
  let singleFile = false;
2616
2680
  if (paths.length === 1 && !/^https?:\/\//i.test(paths[0])) {
2617
2681
  try {
2618
- singleFile = (0, import_node_fs7.statSync)(paths[0]).isFile();
2682
+ singleFile = (0, import_node_fs8.statSync)(paths[0]).isFile();
2619
2683
  } catch {
2620
2684
  singleFile = true;
2621
2685
  }
@@ -2666,6 +2730,7 @@ Exemples :
2666
2730
  printJson(summary, pkg.version);
2667
2731
  } else {
2668
2732
  reporter?.finish(summary, license.getStatus());
2733
+ await notifyUpdateIfAvailable(pkg.version);
2669
2734
  }
2670
2735
  process.exitCode = summary.failed > 0 ? 1 : 0;
2671
2736
  } finally {
@@ -2689,7 +2754,7 @@ program.command("inject").description(
2689
2754
  }
2690
2755
  let transcripts;
2691
2756
  try {
2692
- const raw = JSON.parse((0, import_node_fs7.readFileSync)(opts.transcripts, "utf-8"));
2757
+ const raw = JSON.parse((0, import_node_fs8.readFileSync)(opts.transcripts, "utf-8"));
2693
2758
  transcripts = Array.isArray(raw) ? raw : [];
2694
2759
  } catch (err) {
2695
2760
  fail(json, "invalid_transcripts", `Fichier de transcriptions illisible : ${String(err)}`);
@@ -2856,6 +2921,25 @@ function readStdin() {
2856
2921
  process.stdin.on("end", () => resolvePromise(data));
2857
2922
  });
2858
2923
  }
2924
+ async function notifyUpdateIfAvailable(currentVersion) {
2925
+ const { updateAvailable, latest } = await checkAndCacheUpdate(currentVersion);
2926
+ if (!updateAvailable || !latest) return;
2927
+ console.log(`
2928
+ ${import_picocolors3.default.magenta("\u2191")} Mise \xE0 jour disponible : ${currentVersion} \u2192 ${latest}`);
2929
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
2930
+ console.log(import_picocolors3.default.dim("\u2192 sygal update pour installer"));
2931
+ return;
2932
+ }
2933
+ const install = await promptYesNo("Installer maintenant ?");
2934
+ if (!install) return;
2935
+ console.log(import_picocolors3.default.dim("Installation via npm..."));
2936
+ const code = await runGlobalInstall();
2937
+ if (code === 0) {
2938
+ console.log(`${import_picocolors3.default.green("\u2713")} Sygal CLI mis \xE0 jour en ${latest}`);
2939
+ } else {
2940
+ console.log(`${import_picocolors3.default.red("\u2717")} \xC9chec de la mise \xE0 jour (npm code ${code})`);
2941
+ }
2942
+ }
2859
2943
  function fail(json, code, message) {
2860
2944
  if (json) {
2861
2945
  process.stdout.write(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sygal-cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
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-darwin-arm64": "0.2.0",
41
- "@sygal/sidecar-win32-x64": "0.2.0",
42
- "@sygal/sidecar-linux-x64": "0.2.0"
40
+ "@sygal/sidecar-linux-x64": "0.3.0",
41
+ "@sygal/sidecar-darwin-arm64": "0.3.0",
42
+ "@sygal/sidecar-win32-x64": "0.3.0"
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",