omnius 1.0.10 → 1.0.12

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.
@@ -47,6 +47,16 @@ function log(msg) {
47
47
  function warn(msg) {
48
48
  process.stdout.write(" [daemon] " + msg + "\n");
49
49
  }
50
+ function quietInstall() {
51
+ var level = String(process.env.npm_config_loglevel || "").toLowerCase();
52
+ return process.env.OMNIUS_QUIET_INSTALL === "1" || level === "silent";
53
+ }
54
+ function progress(step, total, msg) {
55
+ if (quietInstall()) return;
56
+ var width = 18;
57
+ var filled = Math.max(0, Math.min(width, Math.round((step / total) * width)));
58
+ process.stdout.write(" [" + "#".repeat(filled) + ".".repeat(width - filled) + "] " + step + "/" + total + " " + msg + "\n");
59
+ }
50
60
 
51
61
  function runQuiet(cmd, opts) {
52
62
  try {
@@ -669,10 +679,12 @@ function repairBrokenWrappers() {
669
679
 
670
680
  function main() {
671
681
  // Always do the nexus cleanup first, regardless of opt-out.
682
+ progress(1, 5, "Cleaning stale Nexus runtime state");
672
683
  cleanNexus();
673
684
 
674
685
  // Auto-repair stale Omnius model wrappers BEFORE restarting the daemon, so the
675
686
  // freshly-restarted daemon picks up the rebuilt models on first inference.
687
+ progress(2, 5, "Checking local model wrappers");
676
688
  try { repairBrokenWrappers(); } catch (e) {
677
689
  warn("wrapper auto-repair crashed (non-fatal): " + (e && e.message));
678
690
  }
@@ -688,6 +700,7 @@ function main() {
688
700
  // restart can't reach it, so we have to clean up explicitly. Without this,
689
701
  // the new service-managed daemon fails to bind port 11435 and the user
690
702
  // ends up running stale in-memory code from the previous version.
703
+ progress(3, 5, "Clearing daemon port");
691
704
  forceKillPortHolder(PORT, function (killedCount) {
692
705
  if (killedCount > 0) {
693
706
  log("Killed " + killedCount + " stale daemon process(es) holding port " + PORT + ".");
@@ -718,6 +731,7 @@ function runMainAfterKill() {
718
731
  // idempotent and ensures the unit file matches the current bundle.
719
732
  }
720
733
 
734
+ progress(4, 5, "Installing daemon service");
721
735
  log("Installing Omnius API daemon service for port " + PORT + " ...");
722
736
  log(" node: " + nodeBin);
723
737
  log(" omnius script: " + omniusScript);
@@ -748,6 +762,7 @@ function runMainAfterKill() {
748
762
  }
749
763
 
750
764
  // Wait up to 15s for /health to come up, but don't fail npm install.
765
+ progress(5, 5, "Verifying daemon health");
751
766
  waitForHealth(15000, function (healthy) {
752
767
  if (healthy) {
753
768
  log("Omnius API daemon is live: http://127.0.0.1:" + PORT + "/health");
@@ -29,12 +29,34 @@ var HOME = os.homedir();
29
29
  var SERVICE_LABEL = "omnius-daemon";
30
30
  var LAUNCHD_LABEL = "ai.omnius.daemon";
31
31
  var WIN_TASK_NAME = "OmniusDaemon";
32
+ var BANNER = [
33
+ "",
34
+ "░░ ░░░ ░░░░ ░░ ░░░ ░░ ░░ ░░░░ ░░░ ░░",
35
+ "▒ ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒ ▒▒ ▒▒▒▒▒▒▒",
36
+ "▓ ▓▓▓▓ ▓▓ ▓▓ ▓ ▓ ▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓ ▓▓▓ ▓▓",
37
+ "█ ████ ██ █ █ ██ ██ █████ █████ ████ ████████ █",
38
+ "██ ███ ████ ██ ███ ██ ███ ████ ██",
39
+ ""
40
+ ].join("\n");
41
+
42
+ function quietInstall() {
43
+ var level = String(process.env.npm_config_loglevel || "").toLowerCase();
44
+ return process.env.OMNIUS_QUIET_INSTALL === "1" || level === "silent";
45
+ }
32
46
 
33
47
  function runQuiet(cmd) {
34
48
  try { cp.execSync(cmd, { stdio: "pipe", timeout: 8000 }); return true; } catch (e) { return false; }
35
49
  }
36
50
 
37
51
  function log(msg) { process.stdout.write(" [preinstall] " + msg + "\n"); }
52
+ function progress(step, total, msg) {
53
+ if (quietInstall()) return;
54
+ var width = 18;
55
+ var filled = Math.max(0, Math.min(width, Math.round((step / total) * width)));
56
+ process.stdout.write(" [" + "#".repeat(filled) + ".".repeat(width - filled) + "] " + step + "/" + total + " " + msg + "\n");
57
+ }
58
+
59
+ if (!quietInstall()) process.stdout.write(BANNER + "\n");
38
60
 
39
61
  function stopServiceManager() {
40
62
  // Stop via the registered service manager if one exists. This is
@@ -65,8 +87,10 @@ function killPidFile(pidFile) {
65
87
  return false;
66
88
  }
67
89
 
90
+ progress(1, 3, "Preparing Omnius service handoff");
68
91
  stopServiceManager();
69
92
  killPidFile(path.join(HOME, ".omnius", "daemon.pid"));
93
+ progress(2, 3, "Stopped old daemon handles");
70
94
 
71
95
  // Final: lsof-based sweep for any process still holding 11435.
72
96
  try {
@@ -88,5 +112,5 @@ try {
88
112
  } catch (e) {}
89
113
 
90
114
  // 1.5s grace so SIGTERM handlers can flush state.
115
+ progress(3, 3, "Install handoff ready");
91
116
  setTimeout(function () { process.exit(0); }, 1500);
92
-
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ var imageToAscii = require("../index.cjs");
5
+
6
+ var source = process.argv[2];
7
+ if (!source) {
8
+ process.stderr.write("Usage: image-to-ascii <image-path-or-url>\n");
9
+ process.exit(2);
10
+ }
11
+
12
+ imageToAscii(source, { colored: false }, function (err, converted) {
13
+ if (err) {
14
+ process.stderr.write((err && err.message ? err.message : String(err)) + "\n");
15
+ process.exit(1);
16
+ }
17
+ process.stdout.write(String(converted || "") + "\n");
18
+ });
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+
3
+ var childProcess = require("node:child_process");
4
+ var crypto = require("node:crypto");
5
+ var fs = require("node:fs");
6
+ var os = require("node:os");
7
+ var path = require("node:path");
8
+
9
+ var DEFAULT_PIXELS = " .,:;i1tfLCG08@";
10
+
11
+ function isObject(value) {
12
+ return value !== null && typeof value === "object" && !Buffer.isBuffer(value);
13
+ }
14
+
15
+ function clamp(value, min, max) {
16
+ var n = Number(value);
17
+ if (!Number.isFinite(n)) return min;
18
+ n = Math.floor(n);
19
+ return Math.max(min, Math.min(max, n));
20
+ }
21
+
22
+ function percentOrNumber(value, basis, fallback) {
23
+ if (typeof value === "number" && Number.isFinite(value)) return value;
24
+ if (typeof value === "string") {
25
+ var trimmed = value.trim();
26
+ if (trimmed.endsWith("%")) {
27
+ var pct = Number(trimmed.slice(0, -1));
28
+ if (Number.isFinite(pct)) return Math.max(1, Math.floor((basis * pct) / 100));
29
+ }
30
+ var parsed = Number(trimmed);
31
+ if (Number.isFinite(parsed)) return parsed;
32
+ }
33
+ return fallback;
34
+ }
35
+
36
+ function resolveSize(options) {
37
+ var size = isObject(options.size) ? options.size : {};
38
+ var sizeOptions = isObject(options.size_options) ? options.size_options : {};
39
+ var screen = isObject(sizeOptions.screen_size) ? sizeOptions.screen_size : {};
40
+ var screenWidth = clamp(screen.width || process.stdout.columns || 80, 20, 240);
41
+ var screenHeight = clamp(screen.height || process.stdout.rows || 32, 8, 120);
42
+ var width = percentOrNumber(size.width, screenWidth, Math.min(80, screenWidth));
43
+ var height = percentOrNumber(size.height, screenHeight, Math.max(8, Math.round(width * 0.42)));
44
+ return {
45
+ width: clamp(width, 8, 240),
46
+ height: clamp(height, 4, 120)
47
+ };
48
+ }
49
+
50
+ function normalizeOptions(options) {
51
+ if (!isObject(options)) options = {};
52
+ var pixels = options.pixels;
53
+ if (Array.isArray(pixels)) pixels = pixels.join("");
54
+ if (typeof pixels !== "string" || pixels.length < 2) pixels = DEFAULT_PIXELS;
55
+ if (options.reverse === true) pixels = pixels.split("").reverse().join("");
56
+ return {
57
+ pixels: pixels,
58
+ colored: options.colored === true,
59
+ concat: options.concat !== false,
60
+ stringify: options.stringify !== false,
61
+ timeoutMs: clamp(options.timeoutMs || options.timeout || 5000, 500, 60000),
62
+ preserveAspectRatio: !options.size_options || options.size_options.preserve_aspect_ratio !== false,
63
+ size: resolveSize(options),
64
+ stringifyFn: typeof options.stringify_fn === "function" ? options.stringify_fn : null
65
+ };
66
+ }
67
+
68
+ function tempFilePath(ext) {
69
+ var suffix = ext || ".img";
70
+ if (!suffix.startsWith(".")) suffix = "." + suffix;
71
+ return path.join(os.tmpdir(), "omnius-image-to-ascii-" + crypto.randomUUID() + suffix);
72
+ }
73
+
74
+ function extensionFromUrl(url) {
75
+ try {
76
+ var parsed = new URL(url);
77
+ var ext = path.extname(parsed.pathname || "");
78
+ return ext || ".img";
79
+ } catch (e) {
80
+ return ".img";
81
+ }
82
+ }
83
+
84
+ function sourceToFile(source, callback) {
85
+ if (Buffer.isBuffer(source)) {
86
+ var bufPath = tempFilePath(".img");
87
+ fs.writeFile(bufPath, source, function (err) {
88
+ callback(err, bufPath, true);
89
+ });
90
+ return;
91
+ }
92
+
93
+ if (typeof source !== "string" || source.trim().length === 0) {
94
+ callback(new Error("image-to-ascii source must be a file path, URL, or Buffer"));
95
+ return;
96
+ }
97
+
98
+ if (/^https?:\/\//i.test(source)) {
99
+ if (typeof fetch !== "function") {
100
+ callback(new Error("image-to-ascii URL input requires Node fetch support"));
101
+ return;
102
+ }
103
+ fetch(source).then(function (res) {
104
+ if (!res.ok) throw new Error("image-to-ascii failed to fetch image: HTTP " + res.status);
105
+ return res.arrayBuffer();
106
+ }).then(function (arrayBuffer) {
107
+ var urlPath = tempFilePath(extensionFromUrl(source));
108
+ fs.writeFile(urlPath, Buffer.from(arrayBuffer), function (err) {
109
+ callback(err, urlPath, true);
110
+ });
111
+ }).catch(function (err) {
112
+ callback(err);
113
+ });
114
+ return;
115
+ }
116
+
117
+ callback(null, source, false);
118
+ }
119
+
120
+ function buildFilter(width, height, preserveAspectRatio) {
121
+ if (preserveAspectRatio) {
122
+ return [
123
+ "scale=" + width + ":" + height + ":force_original_aspect_ratio=decrease",
124
+ "pad=" + width + ":" + height + ":(ow-iw)/2:(oh-ih)/2:color=black",
125
+ "format=gray"
126
+ ].join(",");
127
+ }
128
+ return "scale=" + width + ":" + height + ",format=gray";
129
+ }
130
+
131
+ function matrixFromRaw(raw, width, height) {
132
+ var matrix = [];
133
+ for (var y = 0; y < height; y++) {
134
+ var row = [];
135
+ for (var x = 0; x < width; x++) {
136
+ var value = raw[y * width + x] || 0;
137
+ row.push({ r: value, g: value, b: value, a: 255, value: value });
138
+ }
139
+ matrix.push(row);
140
+ }
141
+ return matrix;
142
+ }
143
+
144
+ function stringifyMatrix(matrix, options) {
145
+ var lines = [];
146
+ var pixels = options.pixels;
147
+ var maxIdx = pixels.length - 1;
148
+ for (var y = 0; y < matrix.length; y++) {
149
+ var line = "";
150
+ for (var x = 0; x < matrix[y].length; x++) {
151
+ var value = matrix[y][x].value || 0;
152
+ var idx = Math.round((value / 255) * maxIdx);
153
+ var ch = pixels[idx] || " ";
154
+ if (options.colored) {
155
+ line += "\x1b[38;2;" + value + ";" + value + ";" + value + "m" + ch + "\x1b[0m";
156
+ } else {
157
+ line += ch;
158
+ }
159
+ }
160
+ lines.push(line.replace(/\s+$/g, ""));
161
+ }
162
+ return options.concat ? lines.join("\n") : lines;
163
+ }
164
+
165
+ function renderFile(filePath, options, callback) {
166
+ var width = options.size.width;
167
+ var height = options.size.height;
168
+ var args = [
169
+ "-hide_banner",
170
+ "-loglevel",
171
+ "error",
172
+ "-i",
173
+ filePath,
174
+ "-vf",
175
+ buildFilter(width, height, options.preserveAspectRatio),
176
+ "-frames:v",
177
+ "1",
178
+ "-f",
179
+ "rawvideo",
180
+ "-pix_fmt",
181
+ "gray",
182
+ "-"
183
+ ];
184
+
185
+ childProcess.execFile("ffmpeg", args, {
186
+ encoding: "buffer",
187
+ maxBuffer: width * height + 4096,
188
+ timeout: options.timeoutMs
189
+ }, function (err, stdout, stderr) {
190
+ if (err) {
191
+ var detail = stderr && stderr.length ? String(stderr).trim() : err.message;
192
+ callback(new Error("image-to-ascii failed via ffmpeg: " + detail));
193
+ return;
194
+ }
195
+ if (!stdout || stdout.length < width * height) {
196
+ callback(new Error("image-to-ascii decoded too few pixels for " + width + "x" + height));
197
+ return;
198
+ }
199
+ var matrix = matrixFromRaw(stdout, width, height);
200
+ if (!options.stringify) {
201
+ callback(null, matrix);
202
+ return;
203
+ }
204
+ if (options.stringifyFn) {
205
+ try {
206
+ callback(null, options.stringifyFn(matrix, options));
207
+ } catch (stringifyErr) {
208
+ callback(stringifyErr);
209
+ }
210
+ return;
211
+ }
212
+ callback(null, stringifyMatrix(matrix, options));
213
+ });
214
+ }
215
+
216
+ function imageToAscii(source, options, callback) {
217
+ if (typeof options === "function") {
218
+ callback = options;
219
+ options = {};
220
+ }
221
+ if (typeof callback !== "function") {
222
+ return new Promise(function (resolve, reject) {
223
+ imageToAscii(source, options || {}, function (err, converted) {
224
+ if (err) reject(err);
225
+ else resolve(converted);
226
+ });
227
+ });
228
+ }
229
+
230
+ var normalized = normalizeOptions(options || {});
231
+ sourceToFile(source, function (sourceErr, filePath, cleanup) {
232
+ if (sourceErr) {
233
+ callback(sourceErr);
234
+ return;
235
+ }
236
+ renderFile(filePath, normalized, function (renderErr, converted) {
237
+ if (cleanup) {
238
+ fs.unlink(filePath, function () {});
239
+ }
240
+ callback(renderErr, converted);
241
+ });
242
+ });
243
+ }
244
+
245
+ imageToAscii.defaults = {
246
+ size_options: {
247
+ px_size: { width: 1 }
248
+ },
249
+ stringify: true,
250
+ concat: true,
251
+ size: {
252
+ height: "100%"
253
+ }
254
+ };
255
+
256
+ module.exports = imageToAscii;
257
+ module.exports.default = imageToAscii;
@@ -0,0 +1,48 @@
1
+ declare namespace imageToAscii {
2
+ interface Size {
3
+ width?: number | string;
4
+ height?: number | string;
5
+ }
6
+
7
+ interface SizeOptions {
8
+ screen_size?: {
9
+ width?: number;
10
+ height?: number;
11
+ };
12
+ preserve_aspect_ratio?: boolean;
13
+ fit_screen?: boolean;
14
+ px_size?: {
15
+ width?: number;
16
+ height?: number;
17
+ };
18
+ }
19
+
20
+ interface Options {
21
+ pxWidth?: number;
22
+ size?: Size;
23
+ size_options?: SizeOptions;
24
+ stringify?: boolean;
25
+ concat?: boolean;
26
+ pixels?: string | string[];
27
+ reverse?: boolean;
28
+ colored?: boolean;
29
+ bg?: boolean;
30
+ fg?: boolean;
31
+ white_bg?: boolean;
32
+ px_background?: { r: number; g: number; b: number };
33
+ image_type?: string;
34
+ timeout?: number;
35
+ timeoutMs?: number;
36
+ stringify_fn?: (pixels: unknown, options: Options) => unknown;
37
+ }
38
+
39
+ type Callback = (err: Error | null, converted?: unknown) => void;
40
+ }
41
+
42
+ declare function imageToAscii(
43
+ source: string | Buffer,
44
+ options: imageToAscii.Options | imageToAscii.Callback,
45
+ callback?: imageToAscii.Callback,
46
+ ): void | Promise<unknown>;
47
+
48
+ export = imageToAscii;
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "image-to-ascii",
3
+ "version": "3.3.0-omnius.1",
4
+ "description": "Omnius bundled image-to-ascii compatibility renderer without the legacy lwip native dependency chain.",
5
+ "main": "index.cjs",
6
+ "types": "index.d.ts",
7
+ "bin": {
8
+ "image-to-ascii": "bin/image-to-ascii.cjs"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./index.d.ts",
13
+ "require": "./index.cjs",
14
+ "default": "./index.cjs"
15
+ }
16
+ },
17
+ "license": "MIT",
18
+ "engines": {
19
+ "node": ">=22.0.0"
20
+ },
21
+ "dependencies": {}
22
+ }