@automaton-labs/aib 0.0.2 → 0.0.3

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "helpVersion": "0.1.0",
4
- "generatedAt": "2026-05-25T01:01:46+03:00",
5
- "sourceCommit": "51c652c",
4
+ "generatedAt": "2026-05-25T01:16:44+03:00",
5
+ "sourceCommit": "682a840",
6
6
  "contentHash": "sha256:fa654cee3287525d7a9dce2fd8d4fa159138898ee4b7c8ebf6fed43bde58074a",
7
7
  "topics": {
8
8
  "help-format": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automaton-labs/aib",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "CLI for discovering and calling the local aib IDE extension runtime.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -6,14 +6,15 @@ const { spawnSync } = require("node:child_process");
6
6
  const packageRoot = path.resolve(__dirname, "..");
7
7
 
8
8
  runBestEffort(path.join(__dirname, "install-windows-launcher.cjs"));
9
- runBestEffort(path.join(__dirname, "provision-runtime.cjs"), ["--quiet"]);
9
+ runBestEffort(path.join(__dirname, "provision-runtime.cjs"), [], { inheritOutput: process.env.AIB_POSTINSTALL_QUIET !== "1" });
10
10
 
11
- function runBestEffort(scriptPath, args = []) {
11
+ function runBestEffort(scriptPath, args = [], options = {}) {
12
+ const inheritOutput = options.inheritOutput === true;
12
13
  const result = spawnSync(process.execPath, [scriptPath, ...args], {
13
14
  cwd: packageRoot,
14
15
  env: process.env,
15
16
  encoding: "utf8",
16
- stdio: ["ignore", "pipe", "pipe"],
17
+ stdio: inheritOutput ? ["ignore", "inherit", "inherit"] : ["ignore", "pipe", "pipe"],
17
18
  windowsHide: true
18
19
  });
19
20
 
@@ -22,7 +23,7 @@ function runBestEffort(scriptPath, args = []) {
22
23
  }
23
24
 
24
25
  if (process.env.AIB_POSTINSTALL_STRICT === "1") {
25
- process.stderr.write(result.stderr || result.stdout || `postinstall helper failed: ${scriptPath}\n`);
26
+ process.stderr.write(result?.stderr || result?.stdout || `postinstall helper failed: ${scriptPath}\n`);
26
27
  process.exit(result.status ?? 1);
27
28
  }
28
29
  }
@@ -14,6 +14,7 @@ const packageJson = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.j
14
14
  const platformKey = `${process.platform}-${process.arch}`;
15
15
 
16
16
  const options = parseArgs(process.argv.slice(2));
17
+ const reporter = createReporter(!options.quiet);
17
18
 
18
19
  main().catch((error) => {
19
20
  if (!options.quiet) {
@@ -25,7 +26,7 @@ main().catch((error) => {
25
26
  async function main() {
26
27
  if (runtimeComplete()) {
27
28
  if (!options.quiet) {
28
- process.stdout.write(`runtime ready: ${path.relative(process.cwd(), runtimesDir) || runtimesDir}\n`);
29
+ reporter.info(`aib runtime ready: ${path.relative(process.cwd(), runtimesDir) || runtimesDir}`);
29
30
  }
30
31
  return;
31
32
  }
@@ -49,23 +50,26 @@ async function main() {
49
50
 
50
51
  if (!source && !url && !manifestSource && !manifestUrl) {
51
52
  if (!options.quiet) {
52
- process.stdout.write("runtime not provisioned: set AIB_RUNTIME_ARCHIVE, AIB_RUNTIME_URL, AIB_RUNTIME_MANIFEST, or AIB_RUNTIME_MANIFEST_URL\n");
53
+ reporter.info("aib runtime not provisioned: set AIB_RUNTIME_ARCHIVE, AIB_RUNTIME_URL, AIB_RUNTIME_MANIFEST, or AIB_RUNTIME_MANIFEST_URL");
53
54
  }
54
55
  return;
55
56
  }
56
57
 
58
+ reporter.info(`aib runtime: provisioning ${platformKey}`);
57
59
  const resolution = source || url
58
- ? { archivePath: source ? path.resolve(source) : await downloadRuntimeArchive(url), expectedSha256: options.sha256 ?? process.env.AIB_RUNTIME_SHA256 ?? null }
60
+ ? { archivePath: source ? path.resolve(source) : await downloadRuntimeArchive(url, "runtime archive"), expectedSha256: options.sha256 ?? process.env.AIB_RUNTIME_SHA256 ?? null }
59
61
  : await resolveFromManifest(manifestSource, manifestUrl);
60
62
  const archivePath = resolution.archivePath;
61
63
  if (!fs.existsSync(archivePath)) {
62
64
  throw new Error(`Runtime archive not found: ${archivePath}`);
63
65
  }
64
66
  if (resolution.expectedSha256) {
67
+ reporter.info("aib runtime: verifying checksum");
65
68
  assertSha256(archivePath, resolution.expectedSha256);
66
69
  }
67
70
 
68
71
  fs.mkdirSync(runtimesDir, { recursive: true });
72
+ reporter.info("aib runtime: extracting archive");
69
73
  extractRuntimeArchive(archivePath);
70
74
 
71
75
  if (!runtimeComplete()) {
@@ -73,7 +77,7 @@ async function main() {
73
77
  }
74
78
 
75
79
  if (!options.quiet) {
76
- process.stdout.write(`runtime provisioned: ${path.relative(process.cwd(), runtimesDir) || runtimesDir}\n`);
80
+ reporter.info(`aib runtime provisioned: ${path.relative(process.cwd(), runtimesDir) || runtimesDir}`);
77
81
  }
78
82
  }
79
83
 
@@ -124,6 +128,7 @@ function parseArgs(argv) {
124
128
  }
125
129
 
126
130
  async function resolveFromManifest(manifestSource, manifestUrl) {
131
+ reporter.info("aib runtime: resolving manifest");
127
132
  const manifest = manifestSource
128
133
  ? JSON.parse(fs.readFileSync(path.resolve(manifestSource), "utf8"))
129
134
  : await downloadJson(manifestUrl);
@@ -146,7 +151,7 @@ async function resolveFromManifest(manifestSource, manifestUrl) {
146
151
  }
147
152
 
148
153
  async function downloadJson(url) {
149
- const filePath = await downloadRuntimeArchive(url);
154
+ const filePath = await downloadRuntimeArchive(url, "runtime manifest");
150
155
  return JSON.parse(fs.readFileSync(filePath, "utf8"));
151
156
  }
152
157
 
@@ -262,7 +267,7 @@ function assertSha256(filePath, expectedSha256) {
262
267
  }
263
268
  }
264
269
 
265
- function downloadRuntimeArchive(url) {
270
+ function downloadRuntimeArchive(url, label = "runtime archive") {
266
271
  return new Promise((resolve, reject) => {
267
272
  if (!url) {
268
273
  reject(new Error("Runtime URL is empty."));
@@ -276,7 +281,7 @@ function downloadRuntimeArchive(url) {
276
281
  if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
277
282
  file.close();
278
283
  fs.rmSync(targetPath, { force: true });
279
- downloadRuntimeArchive(new URL(response.headers.location, url).toString()).then(resolve, reject);
284
+ downloadRuntimeArchive(new URL(response.headers.location, url).toString(), label).then(resolve, reject);
280
285
  return;
281
286
  }
282
287
  if (response.statusCode !== 200) {
@@ -285,9 +290,15 @@ function downloadRuntimeArchive(url) {
285
290
  reject(new Error(`Runtime download failed with HTTP ${response.statusCode}: ${url}`));
286
291
  return;
287
292
  }
293
+ const totalBytes = Number.parseInt(String(response.headers["content-length"] ?? ""), 10);
294
+ const progress = reporter.startProgress(`aib runtime: downloading ${label}`, Number.isFinite(totalBytes) ? totalBytes : null);
295
+ response.on("data", (chunk) => progress.add(chunk.length));
288
296
  response.pipe(file);
289
297
  file.on("finish", () => {
290
- file.close(() => resolve(targetPath));
298
+ file.close(() => {
299
+ progress.done();
300
+ resolve(targetPath);
301
+ });
291
302
  });
292
303
  });
293
304
  request.on("error", (error) => {
@@ -297,3 +308,70 @@ function downloadRuntimeArchive(url) {
297
308
  });
298
309
  });
299
310
  }
311
+
312
+ function createReporter(enabled) {
313
+ const stream = process.stderr;
314
+ const useCarriageReturn = Boolean(stream.isTTY);
315
+ return {
316
+ info(message) {
317
+ if (!enabled) {
318
+ return;
319
+ }
320
+ stream.write(`${message}\n`);
321
+ },
322
+ startProgress(label, totalBytes) {
323
+ if (!enabled) {
324
+ return { add() {}, done() {} };
325
+ }
326
+ let downloadedBytes = 0;
327
+ let lastWriteMs = 0;
328
+ let ended = false;
329
+ const startedMs = Date.now();
330
+ const minIntervalMs = useCarriageReturn ? 750 : 5000;
331
+ const write = (force = false) => {
332
+ const now = Date.now();
333
+ if (!force && now - lastWriteMs < minIntervalMs) {
334
+ return;
335
+ }
336
+ lastWriteMs = now;
337
+ const percent = totalBytes ? Math.min(100, (downloadedBytes / totalBytes) * 100) : null;
338
+ const elapsedSeconds = Math.max(0.001, (now - startedMs) / 1000);
339
+ const rateBytes = downloadedBytes / elapsedSeconds;
340
+ const text = percent === null
341
+ ? `${label}: ${formatBytes(downloadedBytes)} downloaded (${formatBytes(rateBytes)}/s)`
342
+ : `${label}: ${percent.toFixed(1)}% ${formatBytes(downloadedBytes)} / ${formatBytes(totalBytes)} (${formatBytes(rateBytes)}/s)`;
343
+ stream.write(useCarriageReturn ? `\r${text}` : `${text}\n`);
344
+ };
345
+ write(true);
346
+ return {
347
+ add(byteCount) {
348
+ downloadedBytes += byteCount;
349
+ write(false);
350
+ },
351
+ done() {
352
+ if (ended) {
353
+ return;
354
+ }
355
+ ended = true;
356
+ write(true);
357
+ stream.write(useCarriageReturn ? "\n" : "");
358
+ }
359
+ };
360
+ }
361
+ };
362
+ }
363
+
364
+ function formatBytes(bytes) {
365
+ if (!Number.isFinite(bytes) || bytes < 0) {
366
+ return "0 B";
367
+ }
368
+ const units = ["B", "KB", "MB", "GB"];
369
+ let value = bytes;
370
+ let unitIndex = 0;
371
+ while (value >= 1024 && unitIndex < units.length - 1) {
372
+ value /= 1024;
373
+ unitIndex += 1;
374
+ }
375
+ const digits = value >= 100 || unitIndex === 0 ? 0 : value >= 10 ? 1 : 2;
376
+ return `${value.toFixed(digits)} ${units[unitIndex]}`;
377
+ }