flowleap 0.1.0 → 0.1.2

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 (2) hide show
  1. package/install.mjs +79 -3
  2. package/package.json +2 -3
package/install.mjs CHANGED
@@ -1,11 +1,15 @@
1
- import { createWriteStream, chmodSync, mkdirSync, existsSync } from "fs";
1
+ import { createWriteStream, chmodSync, mkdirSync, existsSync, readFileSync } from "fs";
2
2
  import { join, dirname } from "path";
3
3
  import { fileURLToPath } from "url";
4
4
  import { get } from "https";
5
5
 
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
7
  const REPO = "abdullahatrash/flowleap-cli";
8
- const VERSION = "v0.1.0";
8
+
9
+ // Read version from package.json so the release tag stays in sync with the
10
+ // published npm version automatically. Assumes release tags are named "v<version>".
11
+ const pkg = JSON.parse(readFileSync(join(__dirname, "package.json"), "utf8"));
12
+ const VERSION = `v${pkg.version}`;
9
13
 
10
14
  const PLATFORM_MAP = {
11
15
  "darwin-arm64": "flowleap-darwin-aarch64",
@@ -39,10 +43,69 @@ const url = `https://github.com/${REPO}/releases/download/${VERSION}/${asset}`;
39
43
 
40
44
  console.log(`Downloading flowleap ${VERSION} for ${key}...`);
41
45
 
46
+ function formatBytes(n) {
47
+ if (!Number.isFinite(n) || n <= 0) return "0 B";
48
+ const units = ["B", "KB", "MB", "GB"];
49
+ let i = 0;
50
+ while (n >= 1024 && i < units.length - 1) {
51
+ n /= 1024;
52
+ i++;
53
+ }
54
+ return `${n.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
55
+ }
56
+
57
+ function renderBar(fraction, width = 24) {
58
+ const filled = Math.max(0, Math.min(width, Math.round(fraction * width)));
59
+ const empty = width - filled;
60
+ return `[${"=".repeat(filled)}${filled > 0 && empty > 0 ? ">" : ""}${" ".repeat(Math.max(0, empty - (filled > 0 ? 1 : 0)))}]`;
61
+ }
62
+
63
+ function createProgressReporter(total) {
64
+ const isTTY = Boolean(process.stdout.isTTY);
65
+ const hasTotal = Number.isFinite(total) && total > 0;
66
+ let received = 0;
67
+ let lastRenderAt = 0;
68
+ let lastMilestone = -1;
69
+
70
+ const update = (chunkSize) => {
71
+ received += chunkSize;
72
+ const now = Date.now();
73
+
74
+ if (isTTY) {
75
+ // Throttle TTY redraws to ~10 Hz
76
+ if (now - lastRenderAt < 100 && received !== total) return;
77
+ lastRenderAt = now;
78
+ if (hasTotal) {
79
+ const fraction = received / total;
80
+ const pct = Math.floor(fraction * 100);
81
+ const line = ` ${renderBar(fraction)} ${formatBytes(received)} / ${formatBytes(total)} (${pct}%)`;
82
+ process.stdout.write(`\r${line}`);
83
+ } else {
84
+ process.stdout.write(`\r Downloaded ${formatBytes(received)}...`);
85
+ }
86
+ } else if (hasTotal) {
87
+ // Non-TTY: emit milestone lines so CI logs stay readable
88
+ const pct = Math.floor((received / total) * 100);
89
+ const milestone = Math.floor(pct / 25) * 25;
90
+ if (milestone > lastMilestone && milestone <= 100) {
91
+ lastMilestone = milestone;
92
+ console.log(` ${milestone}% (${formatBytes(received)} / ${formatBytes(total)})`);
93
+ }
94
+ }
95
+ };
96
+
97
+ const finish = () => {
98
+ if (isTTY) process.stdout.write("\n");
99
+ };
100
+
101
+ return { update, finish };
102
+ }
103
+
42
104
  function download(url) {
43
105
  return new Promise((resolve, reject) => {
44
106
  get(url, (res) => {
45
107
  if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
108
+ res.resume();
46
109
  download(res.headers.location).then(resolve).catch(reject);
47
110
  return;
48
111
  }
@@ -50,16 +113,29 @@ function download(url) {
50
113
  reject(new Error(`Download failed: HTTP ${res.statusCode}`));
51
114
  return;
52
115
  }
116
+
117
+ const total = parseInt(res.headers["content-length"] || "", 10);
118
+ const progress = createProgressReporter(total);
119
+
53
120
  const file = createWriteStream(binPath);
121
+ res.on("data", (chunk) => progress.update(chunk.length));
54
122
  res.pipe(file);
55
123
  file.on("finish", () => {
56
124
  file.close();
125
+ progress.finish();
57
126
  if (!isWindows) {
58
127
  chmodSync(binPath, 0o755);
59
128
  }
60
129
  resolve();
61
130
  });
62
- file.on("error", reject);
131
+ file.on("error", (err) => {
132
+ progress.finish();
133
+ reject(err);
134
+ });
135
+ res.on("error", (err) => {
136
+ progress.finish();
137
+ reject(err);
138
+ });
63
139
  }).on("error", reject);
64
140
  });
65
141
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowleap",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "One CLI for FlowLeap Patent AI — built for humans and AI agents",
5
5
  "bin": {
6
6
  "flowleap": "bin/flowleap"
@@ -14,8 +14,7 @@
14
14
  "cli",
15
15
  "flowleap",
16
16
  "epo",
17
- "ops",
18
- "ocr"
17
+ "ops"
19
18
  ],
20
19
  "author": "FlowLeap",
21
20
  "type": "module",