@automaton-labs/aib 0.0.2 → 0.0.4
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/dist/bin/cli.js +2 -2
- package/dist/commands/config-command.js +1 -3
- package/dist/commands/config-output.js +8 -0
- package/dist/commands/init-workspace.js +1 -3
- package/dist/commands/inspect-tree.js +3 -3
- package/dist/commands/reveal-command.js +5 -0
- package/dist/commands/sync-command.js +2 -2
- package/dist/config/local-config.js +3 -3
- package/dist/config/resolve-command-alias.js +1 -1
- package/dist/config/tsconfig-discovery.js +1 -0
- package/dist/config/workspace-root.js +3 -3
- package/dist/config/workspace-state.js +1 -1
- package/dist/dsl/aib-dsl.js +1 -1
- package/dist/help/bootstrap.md +21 -11
- package/dist/help/docs/first-setup.md +277 -0
- package/dist/help/docs/inspect.tree.md +8 -6
- package/dist/help/docs/patterns.md +12 -3
- package/dist/help/dsl/bootstrap.md +21 -11
- package/dist/help/dsl/docs/first-setup.md +277 -0
- package/dist/help/dsl/docs/inspect.tree.md +8 -6
- package/dist/help/dsl/docs/patterns.md +12 -3
- package/dist/help/dsl/full.md +21 -11
- package/dist/help/dsl/topics/first-setup.md +275 -0
- package/dist/help/dsl/topics/inspect.tree.md +8 -6
- package/dist/help/dsl/topics/patterns.md +12 -3
- package/dist/help/full.md +21 -11
- package/dist/help/help-meta.json +11 -7
- package/dist/help/index.md +6 -0
- package/dist/help/json/bootstrap.md +21 -11
- package/dist/help/json/docs/first-setup.md +277 -0
- package/dist/help/json/docs/inspect.tree.md +8 -6
- package/dist/help/json/docs/patterns.md +12 -3
- package/dist/help/json/full.md +21 -11
- package/dist/help/json/topics/first-setup.md +275 -0
- package/dist/help/json/topics/inspect.tree.md +8 -6
- package/dist/help/json/topics/patterns.md +12 -3
- package/dist/help/topics/first-setup.md +275 -0
- package/dist/help/topics/inspect.tree.md +8 -6
- package/dist/help/topics/patterns.md +12 -3
- package/dist/runtime/run-command.js +1 -1
- package/extension/vscode-refactor-bridge-extension.vsix +0 -0
- package/package.json +1 -1
- package/runtimes/launcher/win-x64/aib.exe +0 -0
- package/scripts/install-windows-launcher.cjs +1 -0
- package/scripts/postinstall.cjs +5 -4
- package/scripts/provision-runtime.cjs +160 -24
|
@@ -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) {
|
|
@@ -23,9 +24,11 @@ main().catch((error) => {
|
|
|
23
24
|
});
|
|
24
25
|
|
|
25
26
|
async function main() {
|
|
27
|
+
cleanupOldTempDownloads();
|
|
28
|
+
|
|
26
29
|
if (runtimeComplete()) {
|
|
27
30
|
if (!options.quiet) {
|
|
28
|
-
|
|
31
|
+
reporter.info(`aib runtime ready: ${path.relative(process.cwd(), runtimesDir) || runtimesDir}`);
|
|
29
32
|
}
|
|
30
33
|
return;
|
|
31
34
|
}
|
|
@@ -49,31 +52,38 @@ async function main() {
|
|
|
49
52
|
|
|
50
53
|
if (!source && !url && !manifestSource && !manifestUrl) {
|
|
51
54
|
if (!options.quiet) {
|
|
52
|
-
|
|
55
|
+
reporter.info("aib runtime not provisioned: set AIB_RUNTIME_ARCHIVE, AIB_RUNTIME_URL, AIB_RUNTIME_MANIFEST, or AIB_RUNTIME_MANIFEST_URL");
|
|
53
56
|
}
|
|
54
57
|
return;
|
|
55
58
|
}
|
|
56
59
|
|
|
60
|
+
reporter.info(`aib runtime: provisioning ${platformKey}`);
|
|
57
61
|
const resolution = source || url
|
|
58
|
-
? {
|
|
62
|
+
? { ...(source ? { archivePath: path.resolve(source), tempDir: null } : await downloadRuntimeArchive(url, "runtime archive")), expectedSha256: options.sha256 ?? process.env.AIB_RUNTIME_SHA256 ?? null }
|
|
59
63
|
: await resolveFromManifest(manifestSource, manifestUrl);
|
|
60
64
|
const archivePath = resolution.archivePath;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
try {
|
|
66
|
+
if (!fs.existsSync(archivePath)) {
|
|
67
|
+
throw new Error(`Runtime archive not found: ${archivePath}`);
|
|
68
|
+
}
|
|
69
|
+
if (resolution.expectedSha256) {
|
|
70
|
+
reporter.info("aib runtime: verifying checksum");
|
|
71
|
+
assertSha256(archivePath, resolution.expectedSha256);
|
|
72
|
+
}
|
|
67
73
|
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
fs.mkdirSync(runtimesDir, { recursive: true });
|
|
75
|
+
reporter.info("aib runtime: extracting archive");
|
|
76
|
+
extractRuntimeArchive(archivePath);
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
if (!runtimeComplete()) {
|
|
79
|
+
throw new Error(`Runtime archive did not provision a complete runtime under ${runtimesDir}`);
|
|
80
|
+
}
|
|
81
|
+
} finally {
|
|
82
|
+
cleanupOwnedTempDownload(resolution.tempDir);
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
if (!options.quiet) {
|
|
76
|
-
|
|
86
|
+
reporter.info("aib runtime: ready");
|
|
77
87
|
}
|
|
78
88
|
}
|
|
79
89
|
|
|
@@ -124,6 +134,7 @@ function parseArgs(argv) {
|
|
|
124
134
|
}
|
|
125
135
|
|
|
126
136
|
async function resolveFromManifest(manifestSource, manifestUrl) {
|
|
137
|
+
reporter.info("aib runtime: resolving manifest");
|
|
127
138
|
const manifest = manifestSource
|
|
128
139
|
? JSON.parse(fs.readFileSync(path.resolve(manifestSource), "utf8"))
|
|
129
140
|
: await downloadJson(manifestUrl);
|
|
@@ -133,21 +144,72 @@ async function resolveFromManifest(manifestSource, manifestUrl) {
|
|
|
133
144
|
}
|
|
134
145
|
const archivePath = artifact.file && manifestSource
|
|
135
146
|
? path.resolve(path.dirname(path.resolve(manifestSource)), String(artifact.file))
|
|
136
|
-
:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
147
|
+
: null;
|
|
148
|
+
const downloaded = !archivePath && artifact.url
|
|
149
|
+
? await downloadRuntimeArchive(String(artifact.url))
|
|
150
|
+
: null;
|
|
151
|
+
const resolvedArchivePath = archivePath ?? downloaded?.archivePath ?? null;
|
|
152
|
+
const tempDir = downloaded?.tempDir ?? null;
|
|
153
|
+
if (!resolvedArchivePath) {
|
|
140
154
|
throw new Error(`Runtime manifest artifact for ${platformKey} has no url or local file.`);
|
|
141
155
|
}
|
|
142
156
|
return {
|
|
143
|
-
archivePath,
|
|
157
|
+
archivePath: resolvedArchivePath,
|
|
158
|
+
tempDir,
|
|
144
159
|
expectedSha256: typeof artifact.sha256 === "string" ? artifact.sha256 : null
|
|
145
160
|
};
|
|
146
161
|
}
|
|
147
162
|
|
|
148
163
|
async function downloadJson(url) {
|
|
149
|
-
const
|
|
150
|
-
|
|
164
|
+
const download = await downloadRuntimeArchive(url, "runtime manifest");
|
|
165
|
+
try {
|
|
166
|
+
return JSON.parse(fs.readFileSync(download.archivePath, "utf8"));
|
|
167
|
+
} finally {
|
|
168
|
+
cleanupOwnedTempDownload(download.tempDir);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function cleanupOldTempDownloads() {
|
|
173
|
+
const cutoffMs = Date.now() - 24 * 60 * 60 * 1000;
|
|
174
|
+
let entries = [];
|
|
175
|
+
try {
|
|
176
|
+
entries = fs.readdirSync(os.tmpdir(), { withFileTypes: true });
|
|
177
|
+
} catch {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
for (const entry of entries) {
|
|
181
|
+
if (!entry.isDirectory() || !entry.name.startsWith("aib-runtime-")) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
const tempDir = path.join(os.tmpdir(), entry.name);
|
|
185
|
+
try {
|
|
186
|
+
const stat = fs.statSync(tempDir);
|
|
187
|
+
if (stat.mtimeMs < cutoffMs) {
|
|
188
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
// Best-effort cleanup only.
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function cleanupOwnedTempDownload(tempDir) {
|
|
197
|
+
if (!tempDir) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
const resolvedTemp = path.resolve(tempDir);
|
|
202
|
+
const resolvedOsTemp = path.resolve(os.tmpdir());
|
|
203
|
+
const relative = path.relative(resolvedOsTemp, resolvedTemp);
|
|
204
|
+
if (path.basename(resolvedTemp).startsWith("aib-runtime-")
|
|
205
|
+
&& relative
|
|
206
|
+
&& !relative.startsWith("..")
|
|
207
|
+
&& !path.isAbsolute(relative)) {
|
|
208
|
+
fs.rmSync(resolvedTemp, { recursive: true, force: true });
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
// Best-effort cleanup only.
|
|
212
|
+
}
|
|
151
213
|
}
|
|
152
214
|
|
|
153
215
|
function defaultManifestUrl() {
|
|
@@ -262,7 +324,7 @@ function assertSha256(filePath, expectedSha256) {
|
|
|
262
324
|
}
|
|
263
325
|
}
|
|
264
326
|
|
|
265
|
-
function downloadRuntimeArchive(url) {
|
|
327
|
+
function downloadRuntimeArchive(url, label = "runtime archive") {
|
|
266
328
|
return new Promise((resolve, reject) => {
|
|
267
329
|
if (!url) {
|
|
268
330
|
reject(new Error("Runtime URL is empty."));
|
|
@@ -276,24 +338,98 @@ function downloadRuntimeArchive(url) {
|
|
|
276
338
|
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
277
339
|
file.close();
|
|
278
340
|
fs.rmSync(targetPath, { force: true });
|
|
279
|
-
|
|
341
|
+
cleanupOwnedTempDownload(tempDir);
|
|
342
|
+
downloadRuntimeArchive(new URL(response.headers.location, url).toString(), label).then(resolve, reject);
|
|
280
343
|
return;
|
|
281
344
|
}
|
|
282
345
|
if (response.statusCode !== 200) {
|
|
283
346
|
file.close();
|
|
284
347
|
fs.rmSync(targetPath, { force: true });
|
|
348
|
+
cleanupOwnedTempDownload(tempDir);
|
|
285
349
|
reject(new Error(`Runtime download failed with HTTP ${response.statusCode}: ${url}`));
|
|
286
350
|
return;
|
|
287
351
|
}
|
|
352
|
+
const totalBytes = Number.parseInt(String(response.headers["content-length"] ?? ""), 10);
|
|
353
|
+
const progress = reporter.startProgress(`aib runtime: downloading ${label}`, Number.isFinite(totalBytes) ? totalBytes : null);
|
|
354
|
+
response.on("data", (chunk) => progress.add(chunk.length));
|
|
288
355
|
response.pipe(file);
|
|
289
356
|
file.on("finish", () => {
|
|
290
|
-
file.close(() =>
|
|
357
|
+
file.close(() => {
|
|
358
|
+
progress.done();
|
|
359
|
+
resolve({ archivePath: targetPath, tempDir });
|
|
360
|
+
});
|
|
291
361
|
});
|
|
292
362
|
});
|
|
293
363
|
request.on("error", (error) => {
|
|
294
364
|
file.close();
|
|
295
365
|
fs.rmSync(targetPath, { force: true });
|
|
366
|
+
cleanupOwnedTempDownload(tempDir);
|
|
296
367
|
reject(error);
|
|
297
368
|
});
|
|
298
369
|
});
|
|
299
370
|
}
|
|
371
|
+
|
|
372
|
+
function createReporter(enabled) {
|
|
373
|
+
const stream = process.stderr;
|
|
374
|
+
return {
|
|
375
|
+
info(message) {
|
|
376
|
+
if (!enabled) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
stream.write(`${message}\n`);
|
|
380
|
+
},
|
|
381
|
+
startProgress(label, totalBytes) {
|
|
382
|
+
if (!enabled) {
|
|
383
|
+
return { add() {}, done() {} };
|
|
384
|
+
}
|
|
385
|
+
let downloadedBytes = 0;
|
|
386
|
+
let ended = false;
|
|
387
|
+
let nextPercent = 10;
|
|
388
|
+
const totalKnown = Number.isFinite(totalBytes) && totalBytes > 0;
|
|
389
|
+
const showPercentProgress = totalKnown && totalBytes >= 1024 * 1024;
|
|
390
|
+
stream.write(totalKnown
|
|
391
|
+
? `${label} ${formatBytes(totalBytes)}\n`
|
|
392
|
+
: `${label}\n`);
|
|
393
|
+
const writeProgress = () => {
|
|
394
|
+
if (!showPercentProgress) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const percent = Math.floor(Math.min(100, (downloadedBytes / totalBytes) * 100));
|
|
398
|
+
while (percent >= nextPercent && nextPercent <= 100) {
|
|
399
|
+
const thresholdBytes = Math.min(totalBytes, Math.round((totalBytes * nextPercent) / 100));
|
|
400
|
+
stream.write(`aib runtime: ${nextPercent}% ${formatBytes(thresholdBytes)} / ${formatBytes(totalBytes)}\n`);
|
|
401
|
+
nextPercent += 10;
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
return {
|
|
405
|
+
add(byteCount) {
|
|
406
|
+
downloadedBytes += byteCount;
|
|
407
|
+
writeProgress();
|
|
408
|
+
},
|
|
409
|
+
done() {
|
|
410
|
+
if (ended) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
ended = true;
|
|
414
|
+
downloadedBytes = Math.max(downloadedBytes, totalBytes ?? downloadedBytes);
|
|
415
|
+
writeProgress();
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function formatBytes(bytes) {
|
|
423
|
+
if (!Number.isFinite(bytes) || bytes < 0) {
|
|
424
|
+
return "0 B";
|
|
425
|
+
}
|
|
426
|
+
const units = ["B", "KB", "MB", "GB"];
|
|
427
|
+
let value = bytes;
|
|
428
|
+
let unitIndex = 0;
|
|
429
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
430
|
+
value /= 1024;
|
|
431
|
+
unitIndex += 1;
|
|
432
|
+
}
|
|
433
|
+
const digits = value >= 100 || unitIndex === 0 ? 0 : value >= 10 ? 1 : 2;
|
|
434
|
+
return `${value.toFixed(digits)} ${units[unitIndex]}`;
|
|
435
|
+
}
|