inferay 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inferay",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Command-line installer and launcher for Inferay.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/src/cli.js CHANGED
@@ -2,10 +2,8 @@ import { install } from "./install.js";
2
2
  import { launchApp } from "./launch.js";
3
3
  import { doctor } from "./doctor.js";
4
4
  import { getChannel, setChannel } from "./config.js";
5
- import { fetchRelease, findAsset } from "./releases.js";
6
- import { platformInfo } from "./platform.js";
7
5
 
8
- const VERSION = "0.1.0";
6
+ const VERSION = "0.1.2";
9
7
 
10
8
  function printHelp() {
11
9
  console.log(`inferay ${VERSION}
@@ -43,19 +41,8 @@ async function printDoctor(args) {
43
41
  }
44
42
 
45
43
  async function update() {
46
- const channel = await getChannel();
47
- const release = await fetchRelease(channel);
48
- const asset = findAsset(release, platformInfo());
49
- if (!asset) {
50
- throw new Error(
51
- `no installable asset found for ${release.tag_name || channel}`
52
- );
53
- }
54
- console.log(
55
- `Latest ${channel}: ${release.tag_name || release.name || "unknown"}`
56
- );
57
- console.log(`Asset: ${asset.name}`);
58
- console.log("Run `inferay install` to download and open the installer.");
44
+ const result = await install({ force: true });
45
+ console.log(result.message);
59
46
  }
60
47
 
61
48
  async function installAndReport(args) {
package/src/install.js CHANGED
@@ -1,6 +1,8 @@
1
- import { cp, mkdir } from "node:fs/promises";
1
+ import { execFile } from "node:child_process";
2
+ import { cp, mkdir, readdir, rm } from "node:fs/promises";
2
3
  import { existsSync } from "node:fs";
3
- import { basename, dirname, resolve } from "node:path";
4
+ import { basename, dirname, join, resolve } from "node:path";
5
+ import { promisify } from "node:util";
4
6
  import { getChannel } from "./config.js";
5
7
  import {
6
8
  defaultInstallPath,
@@ -10,16 +12,60 @@ import {
10
12
  import { downloadAsset, fetchRelease, findAsset } from "./releases.js";
11
13
  import { openFile } from "./launch.js";
12
14
 
15
+ const execFileAsync = promisify(execFile);
16
+
13
17
  async function copyAppBundle(source, destination = defaultInstallPath()) {
14
18
  if (!source.endsWith(".app")) {
15
19
  throw new Error("local install source must be a .app bundle");
16
20
  }
17
21
  await mkdir(dirname(destination), { recursive: true });
22
+ await rm(destination, { recursive: true, force: true });
18
23
  await cp(source, destination, { recursive: true, force: true });
19
24
  return destination;
20
25
  }
21
26
 
22
- export async function install({ local, launch = true } = {}) {
27
+ function parseMountPoint(output) {
28
+ return output
29
+ .split("\n")
30
+ .map((line) => line.trim().split(/\s+/).at(-1))
31
+ .find((part) => part?.startsWith("/Volumes/"));
32
+ }
33
+
34
+ async function findAppBundle(directory) {
35
+ const entries = await readdir(directory, { withFileTypes: true });
36
+ const app = entries.find(
37
+ (entry) => entry.isDirectory() && entry.name.endsWith(".app")
38
+ );
39
+ if (!app) {
40
+ throw new Error(`no .app bundle found in ${directory}`);
41
+ }
42
+ return join(directory, app.name);
43
+ }
44
+
45
+ async function installDmg(dmgPath, { launch = true } = {}) {
46
+ const { stdout } = await execFileAsync("hdiutil", [
47
+ "attach",
48
+ "-nobrowse",
49
+ "-readonly",
50
+ dmgPath,
51
+ ]);
52
+ const mountPoint = parseMountPoint(stdout);
53
+ if (!mountPoint) {
54
+ throw new Error("could not determine mounted DMG path");
55
+ }
56
+ try {
57
+ const appBundle = await findAppBundle(mountPoint);
58
+ const destination = await copyAppBundle(appBundle);
59
+ if (launch) {
60
+ await openFile(destination);
61
+ }
62
+ return destination;
63
+ } finally {
64
+ await execFileAsync("hdiutil", ["detach", mountPoint]).catch(() => {});
65
+ }
66
+ }
67
+
68
+ export async function install({ local, launch = true, force = false } = {}) {
23
69
  const platform = platformInfo();
24
70
  if (!platform.supported) {
25
71
  throw new Error(`unsupported platform ${platform.os}-${platform.cpu}`);
@@ -40,11 +86,13 @@ export async function install({ local, launch = true } = {}) {
40
86
 
41
87
  const existing = findExistingApp();
42
88
  if (existing) {
43
- return {
44
- kind: "already-installed",
45
- message: `Inferay is already available at ${existing}`,
46
- installedPath: existing,
47
- };
89
+ if (!force) {
90
+ return {
91
+ kind: "already-installed",
92
+ message: `Inferay is already available at ${existing}`,
93
+ installedPath: existing,
94
+ };
95
+ }
48
96
  }
49
97
 
50
98
  const channel = await getChannel();
@@ -58,13 +106,12 @@ export async function install({ local, launch = true } = {}) {
58
106
 
59
107
  const downloadedPath = await downloadAsset(asset);
60
108
  if (downloadedPath.endsWith(".dmg")) {
61
- if (launch) {
62
- await openFile(downloadedPath);
63
- }
109
+ const installedPath = await installDmg(downloadedPath, { launch });
64
110
  return {
65
- kind: "dmg",
66
- message: `Downloaded ${asset.name}. Drag Inferay to Applications from the opened DMG.`,
111
+ kind: "dmg-installed",
112
+ message: `Installed ${asset.name} to ${installedPath}`,
67
113
  downloadedPath,
114
+ installedPath,
68
115
  };
69
116
  }
70
117