saeeol 1.4.4 → 1.4.6
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/desktop.js +186 -0
- package/package.json +13 -6
- package/web/assets/index-B3kbSgZV.js +48353 -0
- package/web/assets/index-VbqLvtTL.css +277 -0
- package/web/index.html +13 -0
package/desktop.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// fabulist — launches saeeol server + Electron desktop with React UI
|
|
4
|
+
const { existsSync, realpathSync, createReadStream, statSync, readdirSync } = require("fs")
|
|
5
|
+
const { join, dirname, extname, mimeLookup } = require("path")
|
|
6
|
+
const { spawn } = require("child_process")
|
|
7
|
+
const http = require("http")
|
|
8
|
+
|
|
9
|
+
const MIME = {
|
|
10
|
+
".html": "text/html",
|
|
11
|
+
".js": "text/javascript",
|
|
12
|
+
".css": "text/css",
|
|
13
|
+
".json": "application/json",
|
|
14
|
+
".png": "image/png",
|
|
15
|
+
".ico": "image/x-icon",
|
|
16
|
+
".svg": "image/svg+xml",
|
|
17
|
+
".woff2": "font/woff2",
|
|
18
|
+
".wasm": "application/wasm",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getBinaryName() {
|
|
22
|
+
const archMap = { x64: "x64", arm64: "arm64" }
|
|
23
|
+
const platformMap = { win32: "windows", darwin: "darwin", linux: "linux" }
|
|
24
|
+
const p = platformMap[process.platform] || process.platform
|
|
25
|
+
const a = archMap[process.arch] || process.arch
|
|
26
|
+
return `saeeol-${p}-${a}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function findBinary() {
|
|
30
|
+
const binaryName = getBinaryName()
|
|
31
|
+
const binaryRel = process.platform === "win32" ? "bin/saeeol.exe" : "bin/saeeol"
|
|
32
|
+
const scriptDir = dirname(realpathSync(__filename))
|
|
33
|
+
|
|
34
|
+
const local = join(scriptDir, "node_modules", binaryName, binaryRel)
|
|
35
|
+
if (existsSync(local)) return local
|
|
36
|
+
|
|
37
|
+
let current = scriptDir
|
|
38
|
+
for (let i = 0; i < 10; i++) {
|
|
39
|
+
const candidate = join(current, "node_modules", binaryName, binaryRel)
|
|
40
|
+
if (existsSync(candidate)) return candidate
|
|
41
|
+
const parent = dirname(current)
|
|
42
|
+
if (parent === current) break
|
|
43
|
+
current = parent
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const dl = join(scriptDir, "download", process.platform === "win32" ? `${binaryName}.exe` : "saeeol")
|
|
47
|
+
if (existsSync(dl)) return dl
|
|
48
|
+
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function findElectron() {
|
|
53
|
+
const scriptDir = dirname(realpathSync(__filename))
|
|
54
|
+
const exe = process.platform === "win32" ? "electron.exe" : "electron"
|
|
55
|
+
const p = join(scriptDir, "node_modules", "electron", "dist", exe)
|
|
56
|
+
if (existsSync(p)) return p
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function serveStatic(webDir, port) {
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
const server = http.createServer((req, res) => {
|
|
63
|
+
let urlPath = req.url.split("?")[0]
|
|
64
|
+
if (urlPath === "/") urlPath = "/index.html"
|
|
65
|
+
|
|
66
|
+
const filePath = join(webDir, urlPath)
|
|
67
|
+
if (!existsSync(filePath)) {
|
|
68
|
+
res.writeHead(404)
|
|
69
|
+
res.end("not found")
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const ext = extname(filePath)
|
|
74
|
+
const ct = MIME[ext] || "application/octet-stream"
|
|
75
|
+
const stat = statSync(filePath)
|
|
76
|
+
res.writeHead(200, { "Content-Type": ct, "Content-Length": stat.size })
|
|
77
|
+
createReadStream(filePath).pipe(res)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
server.listen(port, "127.0.0.1", () => {
|
|
81
|
+
resolve(server)
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function main() {
|
|
87
|
+
const binaryPath = findBinary()
|
|
88
|
+
if (!binaryPath) {
|
|
89
|
+
console.error(`saeeol binary not found (${getBinaryName()})`)
|
|
90
|
+
console.error(`Run: npm install saeeol`)
|
|
91
|
+
process.exit(1)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const electronPath = findElectron()
|
|
95
|
+
const webDir = join(dirname(realpathSync(__filename)), "web")
|
|
96
|
+
const hasWebUI = existsSync(join(webDir, "index.html"))
|
|
97
|
+
|
|
98
|
+
if (!electronPath || !hasWebUI) {
|
|
99
|
+
// Fallback: terminal mode
|
|
100
|
+
if (!electronPath) {
|
|
101
|
+
console.log("Electron not found, starting in terminal mode.\n")
|
|
102
|
+
} else {
|
|
103
|
+
console.log("Web UI not found, starting in terminal mode.\n")
|
|
104
|
+
}
|
|
105
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
106
|
+
stdio: "inherit",
|
|
107
|
+
env: { ...process.env },
|
|
108
|
+
windowsHide: false,
|
|
109
|
+
})
|
|
110
|
+
child.on("exit", (code) => process.exit(code || 0))
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Find ports
|
|
115
|
+
const webPort = 31000 + Math.floor(Math.random() * 5000)
|
|
116
|
+
const apiPort = 32000 + Math.floor(Math.random() * 5000)
|
|
117
|
+
|
|
118
|
+
// 1. Start saeeol API server
|
|
119
|
+
console.log("Starting saeeol API server...")
|
|
120
|
+
const api = spawn(binaryPath, ["web", "--port", String(apiPort), "--hostname", "127.0.0.1"], {
|
|
121
|
+
stdio: "pipe",
|
|
122
|
+
env: { ...process.env },
|
|
123
|
+
windowsHide: true,
|
|
124
|
+
})
|
|
125
|
+
api.stdout.on("data", (d) => { const m = d.toString().trim(); if (m) console.log(`[api] ${m}`) })
|
|
126
|
+
api.stderr.on("data", (d) => { const m = d.toString().trim(); if (m) console.error(`[api] ${m}`) })
|
|
127
|
+
|
|
128
|
+
// 2. Start static file server for React UI
|
|
129
|
+
const staticServer = await serveStatic(webDir, webPort)
|
|
130
|
+
console.log(`UI server: http://127.0.0.1:${webPort}`)
|
|
131
|
+
|
|
132
|
+
// 3. Wait for API to be ready
|
|
133
|
+
await new Promise((r) => setTimeout(r, 3000))
|
|
134
|
+
|
|
135
|
+
// 4. Launch Electron with inline app
|
|
136
|
+
console.log("Opening FABULIST desktop...")
|
|
137
|
+
|
|
138
|
+
const electronAppDir = join(dirname(realpathSync(__filename__)), "desktop-app")
|
|
139
|
+
const { mkdirSync, writeFileSync } = require("fs")
|
|
140
|
+
mkdirSync(electronAppDir, { recursive: true })
|
|
141
|
+
writeFileSync(join(electronAppDir, "package.json"), '{"name":"fabulist-desktop"}')
|
|
142
|
+
writeFileSync(join(electronAppDir, "main.js"), `
|
|
143
|
+
const { app, BrowserWindow } = require("electron")
|
|
144
|
+
const path = require("path")
|
|
145
|
+
|
|
146
|
+
function createWindow() {
|
|
147
|
+
const win = new BrowserWindow({
|
|
148
|
+
width: 1400,
|
|
149
|
+
height: 900,
|
|
150
|
+
minWidth: 800,
|
|
151
|
+
minHeight: 600,
|
|
152
|
+
title: "파불리스트 — FABULIST",
|
|
153
|
+
autoHideMenuBar: true,
|
|
154
|
+
webPreferences: { nodeIntegration: false, contextIsolation: true },
|
|
155
|
+
})
|
|
156
|
+
win.loadURL("http://127.0.0.1:${webPort}")
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
app.whenReady().then(createWindow)
|
|
160
|
+
app.on("window-all-closed", () => app.quit())
|
|
161
|
+
`)
|
|
162
|
+
|
|
163
|
+
const electron = spawn(electronPath, [join(electronAppDir, "main.js")], {
|
|
164
|
+
stdio: "inherit",
|
|
165
|
+
env: { ...process.env },
|
|
166
|
+
windowsHide: false,
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
electron.on("exit", (code) => {
|
|
170
|
+
api.kill()
|
|
171
|
+
staticServer.close()
|
|
172
|
+
process.exit(code || 0)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
process.on("SIGINT", () => {
|
|
176
|
+
api.kill()
|
|
177
|
+
electron.kill()
|
|
178
|
+
staticServer.close()
|
|
179
|
+
process.exit(0)
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
main().catch((err) => {
|
|
184
|
+
console.error(err.message)
|
|
185
|
+
process.exit(1)
|
|
186
|
+
})
|
package/package.json
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saeeol",
|
|
3
|
-
"version": "1.4.
|
|
4
|
-
"description": "AI agent engine for SAEEOL",
|
|
3
|
+
"version": "1.4.6",
|
|
4
|
+
"description": "AI agent engine for SAEEOL — CLI + Desktop in one package",
|
|
5
|
+
"author": "byfabulist",
|
|
5
6
|
"license": "Apache-2.0",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "https://github.com/byfabulist/fabulist"
|
|
9
10
|
},
|
|
10
11
|
"bin": {
|
|
11
|
-
"saeeol": "cli.js"
|
|
12
|
+
"saeeol": "cli.js",
|
|
13
|
+
"fabulist": "desktop.js"
|
|
12
14
|
},
|
|
13
15
|
"files": [
|
|
14
16
|
"cli.js",
|
|
15
|
-
"
|
|
17
|
+
"desktop.js",
|
|
18
|
+
"postinstall.js",
|
|
19
|
+
"web/**/*"
|
|
16
20
|
],
|
|
17
21
|
"scripts": {
|
|
18
22
|
"postinstall": "node postinstall.js"
|
|
@@ -29,7 +33,8 @@
|
|
|
29
33
|
"saeeol-linux-arm64-musl": "1.4.3",
|
|
30
34
|
"saeeol-linux-x64-musl": "1.4.3",
|
|
31
35
|
"saeeol-linux-x64-musl-baseline": "1.4.3",
|
|
32
|
-
"saeeol-win32-arm64": "1.4.3"
|
|
36
|
+
"saeeol-win32-arm64": "1.4.3",
|
|
37
|
+
"electron": "^35.2.1"
|
|
33
38
|
},
|
|
34
39
|
"publishConfig": {
|
|
35
40
|
"access": "public",
|
|
@@ -44,6 +49,8 @@
|
|
|
44
49
|
"cli",
|
|
45
50
|
"coding",
|
|
46
51
|
"assistant",
|
|
47
|
-
"saeeol"
|
|
52
|
+
"saeeol",
|
|
53
|
+
"fabulist",
|
|
54
|
+
"desktop"
|
|
48
55
|
]
|
|
49
56
|
}
|