saeeol 1.4.5 → 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 +91 -47
- package/package.json +5 -3
- 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
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
const { existsSync, realpathSync } = require("fs")
|
|
5
|
-
const { join, dirname } = require("path")
|
|
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
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
|
+
}
|
|
7
20
|
|
|
8
21
|
function getBinaryName() {
|
|
9
22
|
const archMap = { x64: "x64", arm64: "arm64" }
|
|
@@ -18,11 +31,9 @@ function findBinary() {
|
|
|
18
31
|
const binaryRel = process.platform === "win32" ? "bin/saeeol.exe" : "bin/saeeol"
|
|
19
32
|
const scriptDir = dirname(realpathSync(__filename))
|
|
20
33
|
|
|
21
|
-
// 1. node_modules of this package
|
|
22
34
|
const local = join(scriptDir, "node_modules", binaryName, binaryRel)
|
|
23
35
|
if (existsSync(local)) return local
|
|
24
36
|
|
|
25
|
-
// 2. Walk up
|
|
26
37
|
let current = scriptDir
|
|
27
38
|
for (let i = 0; i < 10; i++) {
|
|
28
39
|
const candidate = join(current, "node_modules", binaryName, binaryRel)
|
|
@@ -32,7 +43,6 @@ function findBinary() {
|
|
|
32
43
|
current = parent
|
|
33
44
|
}
|
|
34
45
|
|
|
35
|
-
// 3. Download dir
|
|
36
46
|
const dl = join(scriptDir, "download", process.platform === "win32" ? `${binaryName}.exe` : "saeeol")
|
|
37
47
|
if (existsSync(dl)) return dl
|
|
38
48
|
|
|
@@ -41,11 +51,38 @@ function findBinary() {
|
|
|
41
51
|
|
|
42
52
|
function findElectron() {
|
|
43
53
|
const scriptDir = dirname(realpathSync(__filename))
|
|
44
|
-
const
|
|
45
|
-
|
|
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
|
|
46
57
|
return null
|
|
47
58
|
}
|
|
48
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
|
+
|
|
49
86
|
async function main() {
|
|
50
87
|
const binaryPath = findBinary()
|
|
51
88
|
if (!binaryPath) {
|
|
@@ -55,11 +92,16 @@ async function main() {
|
|
|
55
92
|
}
|
|
56
93
|
|
|
57
94
|
const electronPath = findElectron()
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
+
}
|
|
63
105
|
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
64
106
|
stdio: "inherit",
|
|
65
107
|
env: { ...process.env },
|
|
@@ -69,32 +111,37 @@ async function main() {
|
|
|
69
111
|
return
|
|
70
112
|
}
|
|
71
113
|
|
|
72
|
-
// Find
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
console.log(`Starting saeeol server on port ${port}...`)
|
|
114
|
+
// Find ports
|
|
115
|
+
const webPort = 31000 + Math.floor(Math.random() * 5000)
|
|
116
|
+
const apiPort = 32000 + Math.floor(Math.random() * 5000)
|
|
76
117
|
|
|
77
|
-
// Start saeeol
|
|
78
|
-
|
|
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"], {
|
|
79
121
|
stdio: "pipe",
|
|
80
122
|
env: { ...process.env },
|
|
81
123
|
windowsHide: true,
|
|
82
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}`) })
|
|
83
127
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
})
|
|
88
|
-
server.stderr.on("data", (data) => {
|
|
89
|
-
const msg = data.toString().trim()
|
|
90
|
-
if (msg) console.error(`[server] ${msg}`)
|
|
91
|
-
})
|
|
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}`)
|
|
92
131
|
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
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"), `
|
|
96
143
|
const { app, BrowserWindow } = require("electron")
|
|
97
|
-
const
|
|
144
|
+
const path = require("path")
|
|
98
145
|
|
|
99
146
|
function createWindow() {
|
|
100
147
|
const win = new BrowserWindow({
|
|
@@ -106,34 +153,31 @@ function createWindow() {
|
|
|
106
153
|
autoHideMenuBar: true,
|
|
107
154
|
webPreferences: { nodeIntegration: false, contextIsolation: true },
|
|
108
155
|
})
|
|
109
|
-
win.loadURL(
|
|
156
|
+
win.loadURL("http://127.0.0.1:${webPort}")
|
|
110
157
|
}
|
|
111
158
|
|
|
112
159
|
app.whenReady().then(createWindow)
|
|
113
|
-
app.on("window-all-closed", () =>
|
|
114
|
-
|
|
115
|
-
process.kill(${server.pid})
|
|
116
|
-
process.exit(0)
|
|
117
|
-
})
|
|
118
|
-
`
|
|
119
|
-
require("fs").writeFileSync(electronApp, desktopAppCode)
|
|
160
|
+
app.on("window-all-closed", () => app.quit())
|
|
161
|
+
`)
|
|
120
162
|
|
|
121
|
-
|
|
122
|
-
await new Promise((r) => setTimeout(r, 3000))
|
|
123
|
-
|
|
124
|
-
console.log("Opening FABULIST desktop...")
|
|
125
|
-
|
|
126
|
-
const electron = spawn(electronPath, [electronApp], {
|
|
163
|
+
const electron = spawn(electronPath, [join(electronAppDir, "main.js")], {
|
|
127
164
|
stdio: "inherit",
|
|
128
165
|
env: { ...process.env },
|
|
129
166
|
windowsHide: false,
|
|
130
167
|
})
|
|
131
168
|
|
|
132
169
|
electron.on("exit", (code) => {
|
|
133
|
-
|
|
134
|
-
|
|
170
|
+
api.kill()
|
|
171
|
+
staticServer.close()
|
|
135
172
|
process.exit(code || 0)
|
|
136
173
|
})
|
|
174
|
+
|
|
175
|
+
process.on("SIGINT", () => {
|
|
176
|
+
api.kill()
|
|
177
|
+
electron.kill()
|
|
178
|
+
staticServer.close()
|
|
179
|
+
process.exit(0)
|
|
180
|
+
})
|
|
137
181
|
}
|
|
138
182
|
|
|
139
183
|
main().catch((err) => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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",
|
|
@@ -14,7 +15,8 @@
|
|
|
14
15
|
"files": [
|
|
15
16
|
"cli.js",
|
|
16
17
|
"desktop.js",
|
|
17
|
-
"postinstall.js"
|
|
18
|
+
"postinstall.js",
|
|
19
|
+
"web/**/*"
|
|
18
20
|
],
|
|
19
21
|
"scripts": {
|
|
20
22
|
"postinstall": "node postinstall.js"
|