nai-aclab 1.0.6 → 1.0.7

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,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { spawn } = require("child_process");
3
+ const { spawn, spawnSync } = require("child_process");
4
4
  const fs = require("fs");
5
5
  const os = require("os");
6
6
  const path = require("path");
@@ -58,19 +58,60 @@ function pythonCandidates() {
58
58
  return candidates;
59
59
  }
60
60
 
61
- function runWithCandidate(candidates, index) {
62
- if (index >= candidates.length) {
63
- console.error("Python 3.10+을 찾을 수 없습니다. Python을 설치한 뒤 다시 실행해주세요.");
61
+ function versionParts(text) {
62
+ const match = String(text || "").match(/(\d+)\.(\d+)\.(\d+)/);
63
+ if (!match) {
64
+ return null;
65
+ }
66
+ return match.slice(1).map((part) => Number(part));
67
+ }
68
+
69
+ function supportsPython(candidate) {
70
+ const probe = spawnSync(
71
+ candidate.command,
72
+ [
73
+ ...candidate.args,
74
+ "-c",
75
+ "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')",
76
+ ],
77
+ {
78
+ encoding: "utf8",
79
+ stdio: ["ignore", "pipe", "pipe"],
80
+ windowsHide: true,
81
+ }
82
+ );
83
+
84
+ if (probe.error || probe.status !== 0) {
85
+ return false;
86
+ }
87
+
88
+ const parts = versionParts(probe.stdout);
89
+ if (!parts) {
90
+ return false;
91
+ }
92
+
93
+ const [major, minor] = parts;
94
+ return major > 3 || (major === 3 && minor >= 10);
95
+ }
96
+
97
+ function choosePython() {
98
+ return pythonCandidates().find((candidate) => supportsPython(candidate));
99
+ }
100
+
101
+ function runWithCandidate(candidate) {
102
+ if (!candidate) {
103
+ console.error("Python 3.10 or newer was not found.");
104
+ console.error("Install Python from https://www.python.org/downloads/ and run npx nai-aclab again.");
64
105
  process.exit(1);
65
106
  }
66
107
 
67
- const candidate = candidates[index];
68
108
  const env = {
69
109
  ...process.env,
70
110
  NAI_ARTIST_LAB_USER_DIR: userDataDir(),
71
111
  };
72
112
 
73
113
  fs.mkdirSync(env.NAI_ARTIST_LAB_USER_DIR, { recursive: true });
114
+ console.log(`Starting NAI Artist Combination Lab ${packageJson.version}...`);
74
115
 
75
116
  const child = spawn(candidate.command, [...candidate.args, launcherPath], {
76
117
  cwd: rootDir,
@@ -79,21 +120,12 @@ function runWithCandidate(candidates, index) {
79
120
  windowsHide: false,
80
121
  });
81
122
 
82
- let failedToStart = false;
83
123
  child.on("error", (error) => {
84
- failedToStart = true;
85
- if (error.code === "ENOENT") {
86
- runWithCandidate(candidates, index + 1);
87
- return;
88
- }
89
124
  console.error(error.message);
90
125
  process.exit(1);
91
126
  });
92
127
 
93
128
  child.on("exit", (code, signal) => {
94
- if (failedToStart) {
95
- return;
96
- }
97
129
  if (signal) {
98
130
  process.kill(process.pid, signal);
99
131
  return;
@@ -112,4 +144,4 @@ if (process.argv.includes("--version") || process.argv.includes("-v")) {
112
144
  process.exit(0);
113
145
  }
114
146
 
115
- runWithCandidate(pythonCandidates(), 0);
147
+ runWithCandidate(choosePython());
package/launcher.py CHANGED
@@ -1,6 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import os
3
4
  import socket
5
+ import subprocess
6
+ import sys
4
7
  import webbrowser
5
8
  from http.server import ThreadingHTTPServer
6
9
 
@@ -21,14 +24,39 @@ def find_port(start: int = 8777) -> int:
21
24
  raise RuntimeError("No available local port found.")
22
25
 
23
26
 
27
+ def open_browser(url: str) -> bool:
28
+ if os.environ.get("NAI_ARTIST_LAB_NO_BROWSER", "").lower() in {"1", "true", "yes"}:
29
+ return False
30
+
31
+ try:
32
+ if sys.platform.startswith("win"):
33
+ os.startfile(url) # type: ignore[attr-defined]
34
+ return True
35
+ if sys.platform == "darwin":
36
+ subprocess.Popen(["open", url], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
37
+ return True
38
+ subprocess.Popen(["xdg-open", url], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
39
+ return True
40
+ except Exception:
41
+ pass
42
+
43
+ try:
44
+ return bool(webbrowser.open(url, new=2))
45
+ except Exception:
46
+ return False
47
+
48
+
24
49
  def main() -> None:
25
50
  port = find_port()
26
51
  server = ThreadingHTTPServer(("127.0.0.1", port), Handler)
27
52
  url = f"http://127.0.0.1:{port}/"
28
53
 
29
- webbrowser.open(url)
30
- print(f"{APP_NAME} is running at {url}")
31
- print("Keep this terminal open while using the app. Press Ctrl+C to stop.")
54
+ print(f"{APP_NAME} is running at {url}", flush=True)
55
+ print("Keep this terminal open while using the app. Press Ctrl+C to stop.", flush=True)
56
+ if open_browser(url):
57
+ print("Opening your browser now...", flush=True)
58
+ else:
59
+ print("Could not open a browser automatically. Copy and paste the URL above into your browser.", flush=True)
32
60
 
33
61
  try:
34
62
  server.serve_forever()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nai-aclab",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Local web UI for experimenting with NovelAI artist tag weight combinations.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",