@typed-assistant/builder 0.0.18 → 0.0.19
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/.eslintrc.js +1 -0
- package/package.json +7 -4
- package/src/appProcess.tsx +18 -17
- package/src/setupWebserver.tsx +81 -25
- package/src/webserver/App.tsx +64 -0
- package/src/webserver/Terminal.tsx +10 -34
- package/src/webserver/api.tsx +7 -0
- package/src/webserver/index.tsx +2 -2
- package/src/webserver/useWS.tsx +34 -0
- package/tsconfig.json +6 -1
- package/src/webserver/terminal.html +0 -55
package/.eslintrc.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typed-assistant/builder",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"exports": {
|
|
5
5
|
"./appProcess": "./src/appProcess.tsx",
|
|
6
6
|
"./bunInstall": "./src/bunInstall.tsx",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"ansi-to-html": "^0.7.2",
|
|
12
|
+
"@elysiajs/eden": "^0.8.1",
|
|
12
13
|
"elysia": "^0.8.9",
|
|
13
14
|
"@mdi/svg": "^7.3.67",
|
|
14
15
|
"ignore": "^5.3.0",
|
|
@@ -18,13 +19,15 @@
|
|
|
18
19
|
"devDependencies": {
|
|
19
20
|
"@types/node": "^20.10.6",
|
|
20
21
|
"@types/eslint": "^8.56.1",
|
|
22
|
+
"eslint-plugin-html": "^7.1.0",
|
|
21
23
|
"eslint": "^8.56.0",
|
|
22
24
|
"home-assistant-js-websocket": "^8.2.0",
|
|
25
|
+
"ts-toolbelt": "^9.6.0",
|
|
23
26
|
"typescript": "^5.3.3",
|
|
27
|
+
"@typed-assistant/utils": "0.0.7",
|
|
24
28
|
"@typed-assistant/eslint-config": "0.0.4",
|
|
25
|
-
"@typed-assistant/logger": "0.0.
|
|
26
|
-
"@typed-assistant/typescript-config": "0.0.4"
|
|
27
|
-
"@typed-assistant/utils": "0.0.7"
|
|
29
|
+
"@typed-assistant/logger": "0.0.6",
|
|
30
|
+
"@typed-assistant/typescript-config": "0.0.4"
|
|
28
31
|
},
|
|
29
32
|
"peerDependencies": {
|
|
30
33
|
"home-assistant-js-websocket": "^8.2.0"
|
package/src/appProcess.tsx
CHANGED
|
@@ -46,16 +46,6 @@ async function killAndRestartApp(subprocesses: Processes) {
|
|
|
46
46
|
return newSubprocesses
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function setupWatcherInternal(...args: Parameters<typeof watch>) {
|
|
50
|
-
const [directory, callback] = args
|
|
51
|
-
if (typeof directory !== "string") throw new Error("Directory must be string")
|
|
52
|
-
|
|
53
|
-
log("👀 Watching directory:", directory)
|
|
54
|
-
const watcher = watch(directory, { recursive: true }, callback)
|
|
55
|
-
|
|
56
|
-
return watcher
|
|
57
|
-
}
|
|
58
|
-
|
|
59
49
|
export async function setupWatcher(
|
|
60
50
|
...args: Parameters<typeof buildAndStartAppProcess>
|
|
61
51
|
) {
|
|
@@ -63,14 +53,17 @@ export async function setupWatcher(
|
|
|
63
53
|
if (addonInfoError) {
|
|
64
54
|
log(`🚨 Failed to get addon info: ${addonInfoError}`)
|
|
65
55
|
}
|
|
66
|
-
console.log("😅😅😅 ~ addonInfo:", Object.keys(addonInfo))
|
|
67
56
|
await setupGitSync()
|
|
68
57
|
|
|
69
58
|
let subprocesses = await buildAndStartAppProcess(...args)
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
60
|
+
const directory = join(process.cwd(), "./src")
|
|
61
|
+
log("👀 Watching directory:", directory)
|
|
62
|
+
const watcher = watch(
|
|
63
|
+
directory,
|
|
64
|
+
{ recursive: true },
|
|
73
65
|
async function onFileChange(event, filename) {
|
|
66
|
+
console.log("😅😅😅 ~ filename:", filename)
|
|
74
67
|
if (!filename) return
|
|
75
68
|
if (shouldIgnoreFileOrFolder(filename)) return
|
|
76
69
|
log(`⚠️ Change to ${filename} detected.`)
|
|
@@ -82,15 +75,16 @@ export async function setupWatcher(
|
|
|
82
75
|
},
|
|
83
76
|
)
|
|
84
77
|
|
|
85
|
-
console.log(
|
|
86
|
-
"😅😅😅 ~ addonInfo?.ingress_entry:",
|
|
87
|
-
addonInfo?.data.ingress_entry,
|
|
88
|
-
)
|
|
89
78
|
startWebappServer({
|
|
90
79
|
basePath: addonInfo?.data.ingress_entry ?? "",
|
|
91
80
|
getSubprocesses: () => subprocesses,
|
|
92
81
|
})
|
|
93
82
|
|
|
83
|
+
process.on("SIGINT", () => {
|
|
84
|
+
console.log("👋 Closing watcher...")
|
|
85
|
+
watcher.close()
|
|
86
|
+
})
|
|
87
|
+
|
|
94
88
|
return subprocesses
|
|
95
89
|
}
|
|
96
90
|
|
|
@@ -133,3 +127,10 @@ const getAddonInfo = async () => {
|
|
|
133
127
|
data: { ingress_entry: string }
|
|
134
128
|
}>("/addons/self/info")
|
|
135
129
|
}
|
|
130
|
+
|
|
131
|
+
process.on("SIGINT", () => {
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
console.log("👋 ...")
|
|
134
|
+
process.exit(0)
|
|
135
|
+
}, 0)
|
|
136
|
+
})
|
package/src/setupWebserver.tsx
CHANGED
|
@@ -2,23 +2,24 @@ import { log } from "@typed-assistant/logger"
|
|
|
2
2
|
import Convert from "ansi-to-html"
|
|
3
3
|
import type { Subprocess } from "bun"
|
|
4
4
|
import { $ } from "bun"
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
5
|
+
import { Elysia, t } from "elysia"
|
|
6
|
+
import { watch } from "fs"
|
|
7
7
|
import { basename, join } from "path"
|
|
8
|
+
import type { List, String } from "ts-toolbelt"
|
|
8
9
|
|
|
9
|
-
const indexHtmlFilePath = `${import.meta.dir}/webserver/index.html`
|
|
10
|
-
const cssFile = `${import.meta.dir}/webserver/input.css`
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const cssOutputFile = join(
|
|
10
|
+
const indexHtmlFilePath = `${import.meta.dir}/webserver/index.html` as const
|
|
11
|
+
const cssFile = `${import.meta.dir}/webserver/input.css` as const
|
|
12
|
+
const tsEntryPoint = `${import.meta.dir}/webserver/index.tsx` as const
|
|
13
|
+
const tailwindConfig =
|
|
14
|
+
`${import.meta.dir}/webserver/tailwind.config.js` as const
|
|
15
|
+
const cssOutputFile = join(
|
|
16
|
+
process.cwd(),
|
|
17
|
+
`./build/output.css`,
|
|
18
|
+
) as `${string}/output.css`
|
|
15
19
|
|
|
16
20
|
const convert = new Convert()
|
|
17
21
|
const decoder = new TextDecoder()
|
|
18
22
|
|
|
19
|
-
const getIngressPath = (req: Context["request"]) =>
|
|
20
|
-
req.headers.get("x-ingress-path") ?? ""
|
|
21
|
-
|
|
22
23
|
const readers = new Map<
|
|
23
24
|
ReadableStream<Uint8Array>,
|
|
24
25
|
ReadableStreamDefaultReader<Uint8Array>
|
|
@@ -37,6 +38,7 @@ const getReader = (stream: ReadableStream<Uint8Array>) => {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
const subscribers = new Map<number, (message: string) => void>()
|
|
41
|
+
const logSubscribers = new Map<number, () => void>()
|
|
40
42
|
|
|
41
43
|
let lastMessage = ""
|
|
42
44
|
|
|
@@ -49,7 +51,6 @@ export const startWebappServer = async ({
|
|
|
49
51
|
app: Subprocess<"ignore", "pipe", "pipe">
|
|
50
52
|
}
|
|
51
53
|
}) => {
|
|
52
|
-
console.log("😅😅😅 ~ basePath:", basePath)
|
|
53
54
|
const buildResult = await Bun.build({
|
|
54
55
|
entrypoints: [tsEntryPoint],
|
|
55
56
|
outdir: "./build",
|
|
@@ -72,14 +73,14 @@ export const startWebappServer = async ({
|
|
|
72
73
|
const indexHtml = (await Bun.file(indexHtmlFilePath).text())
|
|
73
74
|
.replace(
|
|
74
75
|
"{{ STYLESHEET }}",
|
|
75
|
-
`${basePath}/assets/${
|
|
76
|
+
`${basePath}/assets/${getBaseName(cssOutputFile)}`,
|
|
76
77
|
)
|
|
77
78
|
.replace(
|
|
78
79
|
"{{ SCRIPTS }}",
|
|
79
80
|
buildResult.outputs
|
|
80
81
|
.map(
|
|
81
82
|
(output) =>
|
|
82
|
-
`<script type="module" src="${basePath}/assets/${
|
|
83
|
+
`<script type="module" src="${basePath}/assets/${getBaseName(output.path)}"></script>`,
|
|
83
84
|
)
|
|
84
85
|
.join("\n"),
|
|
85
86
|
)
|
|
@@ -92,9 +93,34 @@ export const startWebappServer = async ({
|
|
|
92
93
|
headers: { "content-type": "text/html" },
|
|
93
94
|
}),
|
|
94
95
|
)
|
|
95
|
-
.get(
|
|
96
|
-
|
|
96
|
+
.get(
|
|
97
|
+
"/log.txt",
|
|
98
|
+
async ({ query }) => {
|
|
99
|
+
return getLogsFromFile(query.limit)
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
query: t.Object({
|
|
103
|
+
limit: t.Optional(t.String()),
|
|
104
|
+
}),
|
|
105
|
+
},
|
|
106
|
+
)
|
|
107
|
+
.ws("/logsws", {
|
|
108
|
+
query: t.Object({
|
|
109
|
+
limit: t.Optional(t.String()),
|
|
110
|
+
}),
|
|
111
|
+
response: t.Object({ logs: t.Array(t.String()) }),
|
|
112
|
+
async open(ws) {
|
|
113
|
+
ws.send(await getLogsFromFile(ws.data.query.limit))
|
|
114
|
+
logSubscribers.set(ws.id, async () => {
|
|
115
|
+
ws.send(await getLogsFromFile(ws.data.query.limit))
|
|
116
|
+
})
|
|
117
|
+
},
|
|
118
|
+
close(ws) {
|
|
119
|
+
logSubscribers.delete(ws.id)
|
|
120
|
+
},
|
|
121
|
+
})
|
|
97
122
|
.ws("/ws", {
|
|
123
|
+
response: t.String(),
|
|
98
124
|
async open(ws) {
|
|
99
125
|
ws.send("Connected successfully. Awaiting messages...")
|
|
100
126
|
subscribers.set(ws.id, (message) => {
|
|
@@ -105,17 +131,16 @@ export const startWebappServer = async ({
|
|
|
105
131
|
subscribers.delete(ws.id)
|
|
106
132
|
},
|
|
107
133
|
})
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
134
|
+
.get(
|
|
135
|
+
`/assets/${getBaseName(cssOutputFile)}`,
|
|
136
|
+
() =>
|
|
137
|
+
new Response(Bun.file(cssOutputFile), {
|
|
138
|
+
headers: { "content-type": "text/css" },
|
|
139
|
+
}),
|
|
140
|
+
)
|
|
116
141
|
buildResult.outputs.forEach((output) => {
|
|
117
142
|
server.get(
|
|
118
|
-
`/assets/${
|
|
143
|
+
`/assets/${getBaseName(output.path)}`,
|
|
119
144
|
() =>
|
|
120
145
|
new Response(Bun.file(output.path), {
|
|
121
146
|
headers: { "content-type": "text/javascript" },
|
|
@@ -126,6 +151,19 @@ export const startWebappServer = async ({
|
|
|
126
151
|
server.listen(8099)
|
|
127
152
|
log("🌐 Web server listening on port 8099")
|
|
128
153
|
|
|
154
|
+
const directory = join(process.cwd(), ".")
|
|
155
|
+
log("👀 Watching log.txt")
|
|
156
|
+
const watcher = watch(directory, function onFileChange(_event, filename) {
|
|
157
|
+
if (filename === "log.txt") {
|
|
158
|
+
logSubscribers.forEach((send) => send())
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
process.on("SIGINT", () => {
|
|
163
|
+
console.log("👋 Closing log watcher...")
|
|
164
|
+
watcher.close()
|
|
165
|
+
})
|
|
166
|
+
|
|
129
167
|
// eslint-disable-next-line no-constant-condition
|
|
130
168
|
while (true) {
|
|
131
169
|
const stdoutReader = getReader(getSubprocesses().app.stdout)
|
|
@@ -147,4 +185,22 @@ export const startWebappServer = async ({
|
|
|
147
185
|
}
|
|
148
186
|
subscribers.forEach((send) => send(convertedMessage))
|
|
149
187
|
}
|
|
188
|
+
|
|
189
|
+
return server
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export type WebServer = Awaited<ReturnType<typeof startWebappServer>>
|
|
193
|
+
|
|
194
|
+
const getLogsFromFile = async (limit?: string) => {
|
|
195
|
+
try {
|
|
196
|
+
const lines = (await Bun.file("./log.txt").text()).split("\n")
|
|
197
|
+
const logFile = limit ? lines.slice(0, Number(limit)) : lines
|
|
198
|
+
return { logs: logFile }
|
|
199
|
+
} catch (e) {
|
|
200
|
+
return { logs: ["Error reading log.txt file"] }
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const getBaseName = <const TString extends string>(path: TString) => {
|
|
205
|
+
return basename(path) as List.Last<String.Split<TString, "/">>
|
|
150
206
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react"
|
|
2
|
+
import { Terminal } from "./Terminal"
|
|
3
|
+
import { app } from "./api"
|
|
4
|
+
import { useWS } from "./useWS"
|
|
5
|
+
|
|
6
|
+
const basePath = process.env.BASE_PATH ?? ""
|
|
7
|
+
|
|
8
|
+
const App = () => {
|
|
9
|
+
return (
|
|
10
|
+
<div className="grid grid-cols-3">
|
|
11
|
+
<div className="col-span-2">
|
|
12
|
+
<Terminal basePath={basePath} />
|
|
13
|
+
</div>
|
|
14
|
+
<div className="col-span-1">
|
|
15
|
+
<Logs />
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const Logs = () => {
|
|
22
|
+
const [limit, setLimit] = useState(20)
|
|
23
|
+
const [logs, setLogs] = useState<string[]>([])
|
|
24
|
+
|
|
25
|
+
const ws = useWS({
|
|
26
|
+
subscribe: useCallback(
|
|
27
|
+
() => app.logsws.subscribe({ $query: { limit: limit.toString() } }),
|
|
28
|
+
[limit],
|
|
29
|
+
),
|
|
30
|
+
onMessage: useCallback((event) => {
|
|
31
|
+
setLogs(JSON.parse(event.data).logs)
|
|
32
|
+
}, []),
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="p-4 text-xs">
|
|
37
|
+
<div className="flex gap-4 mb-4 items-center justify-between">
|
|
38
|
+
<h2 className="mb-2 text-2xl">Logs</h2>
|
|
39
|
+
<div>
|
|
40
|
+
<div className="flex gap-2">
|
|
41
|
+
<label htmlFor="limit">Limit</label>
|
|
42
|
+
<input
|
|
43
|
+
className="border border-gray-300 rounded-md text-slate-800 px-2"
|
|
44
|
+
id="limit"
|
|
45
|
+
onChange={(e) => setLimit(Number(e.target.value))}
|
|
46
|
+
size={8}
|
|
47
|
+
value={limit}
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<pre className="overflow-x-auto">
|
|
54
|
+
<ul>
|
|
55
|
+
{logs.map((log) => (
|
|
56
|
+
<li key={log}>{log}</li>
|
|
57
|
+
))}
|
|
58
|
+
</ul>
|
|
59
|
+
</pre>
|
|
60
|
+
</div>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default App
|
|
@@ -1,43 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useState } from "react"
|
|
2
|
+
import { app } from "./api"
|
|
3
|
+
import { useWS } from "./useWS"
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
const url = new URL(window.location.href)
|
|
5
|
-
url.pathname = `${process.env.BASE_PATH}/ws`
|
|
6
|
-
url.protocol = "ws:"
|
|
7
|
-
const ws = new WebSocket(url)
|
|
8
|
-
|
|
9
|
-
return ws
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function Terminal() {
|
|
5
|
+
export function Terminal({ basePath }: { basePath: string }) {
|
|
13
6
|
const [content, setContent] = useState("")
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
ws.onclose = function () {
|
|
20
|
-
timeout = setTimeout(() => {
|
|
21
|
-
if (ws.readyState === WebSocket.OPEN) return
|
|
22
|
-
setWS(getWS())
|
|
23
|
-
}, 1000)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
ws.onmessage = function (event) {
|
|
27
|
-
setContent(event.data)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return () => {
|
|
31
|
-
clearTimeout(timeout)
|
|
32
|
-
ws.close()
|
|
33
|
-
}
|
|
34
|
-
}, [ws])
|
|
7
|
+
const ws = useWS({
|
|
8
|
+
subscribe: useCallback(() => app.ws.subscribe(), []),
|
|
9
|
+
onMessage: useCallback((event) => setContent(event.data), []),
|
|
10
|
+
})
|
|
35
11
|
|
|
36
12
|
return (
|
|
37
13
|
<>
|
|
38
14
|
<h1 className="text-white text-2xl">
|
|
39
15
|
Terminal{" "}
|
|
40
|
-
{ws.readyState === WebSocket.OPEN ? (
|
|
16
|
+
{ws.ws.readyState === WebSocket.OPEN ? (
|
|
41
17
|
<span className="py-1 px-2 rounded-sm bg-emerald-300 text-emerald-800 text-xs uppercase">
|
|
42
18
|
Connected
|
|
43
19
|
</span>
|
|
@@ -48,7 +24,7 @@ export function Terminal() {
|
|
|
48
24
|
)}
|
|
49
25
|
</h1>
|
|
50
26
|
<p>
|
|
51
|
-
|
|
27
|
+
<a href={`${basePath}/log.txt`}>View log.txt</a>
|
|
52
28
|
</p>
|
|
53
29
|
|
|
54
30
|
<pre className="" dangerouslySetInnerHTML={{ __html: content }} />
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { edenTreaty } from "@elysiajs/eden"
|
|
2
|
+
import type { WebServer } from "../setupWebserver"
|
|
3
|
+
import type { EdenTreaty } from "@elysiajs/eden/treaty"
|
|
4
|
+
|
|
5
|
+
export const app: EdenTreaty.Create<WebServer> = edenTreaty<WebServer>(
|
|
6
|
+
"http://localhost:8099" + process.env.BASE_PATH,
|
|
7
|
+
)
|
package/src/webserver/index.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as ReactDOM from "react-dom/client"
|
|
2
|
-
import
|
|
2
|
+
import App from "./App"
|
|
3
3
|
|
|
4
4
|
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
|
|
5
|
-
root.render(<
|
|
5
|
+
root.render(<App />)
|
|
6
6
|
|
|
7
7
|
export default {}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useEffect, useState } from "react"
|
|
2
|
+
import { app } from "./api"
|
|
3
|
+
|
|
4
|
+
export function useWS({
|
|
5
|
+
onMessage,
|
|
6
|
+
subscribe,
|
|
7
|
+
}: {
|
|
8
|
+
onMessage: (event: MessageEvent) => void
|
|
9
|
+
subscribe: () => ReturnType<(typeof app.ws | typeof app.logsws)["subscribe"]>
|
|
10
|
+
}) {
|
|
11
|
+
const [ws, setWS] = useState(subscribe)
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
let timeout: NodeJS.Timeout
|
|
15
|
+
|
|
16
|
+
ws.ws.onclose = function () {
|
|
17
|
+
timeout = setTimeout(() => {
|
|
18
|
+
if (ws.ws.readyState === WebSocket.OPEN) return
|
|
19
|
+
setWS(subscribe)
|
|
20
|
+
}, 1000)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ws.ws.onmessage = function (event) {
|
|
24
|
+
onMessage(event)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return () => {
|
|
28
|
+
clearTimeout(timeout)
|
|
29
|
+
ws.ws.close()
|
|
30
|
+
}
|
|
31
|
+
}, [ws, onMessage, subscribe])
|
|
32
|
+
|
|
33
|
+
return ws
|
|
34
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": "@typed-assistant/typescript-config/react-app.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"paths": {
|
|
5
|
+
"@elysiajs/eden": ["./node_modules/@elysiajs/eden/"],
|
|
6
|
+
},
|
|
7
|
+
},
|
|
3
8
|
"include": ["src"],
|
|
4
|
-
"exclude": ["node_modules", "dist"]
|
|
9
|
+
"exclude": ["node_modules", "dist"],
|
|
5
10
|
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<h1>Terminal</h1>
|
|
3
|
-
<pre id="log"></pre>
|
|
4
|
-
<style>
|
|
5
|
-
html {
|
|
6
|
-
background: #000;
|
|
7
|
-
color: #fff;
|
|
8
|
-
}
|
|
9
|
-
</style>
|
|
10
|
-
<script>
|
|
11
|
-
function log(msg) {
|
|
12
|
-
document.getElementById("log").innerHTML = msg + "\n"
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const getWS = () => {
|
|
16
|
-
const url = new URL(window.location.href)
|
|
17
|
-
url.pathname = url.pathname.replace("/terminal", "/ws")
|
|
18
|
-
url.protocol = "ws:"
|
|
19
|
-
return new WebSocket(
|
|
20
|
-
// "ws://192.168.86.11:8123/api/hassio_ingress/wmQTEkorulChwnbeWV1GvPSwBsEpGzYlyLR70rdHzH0/ws",
|
|
21
|
-
// "ws://localhost:8099/ws",
|
|
22
|
-
url,
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const setupWs = (ws) => {
|
|
27
|
-
ws.onopen = function () {
|
|
28
|
-
console.log("CONNECT")
|
|
29
|
-
}
|
|
30
|
-
ws.onclose = function () {
|
|
31
|
-
console.log("DISCONNECT")
|
|
32
|
-
document.getElementById("log").innerHTML +=
|
|
33
|
-
"Disconnected. Retrying..." + "\n"
|
|
34
|
-
retry()
|
|
35
|
-
}
|
|
36
|
-
ws.onmessage = function (event) {
|
|
37
|
-
log(event.data)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
var ws = getWS()
|
|
42
|
-
console.log("😅😅😅 ~ ws:", ws)
|
|
43
|
-
setupWs(ws)
|
|
44
|
-
|
|
45
|
-
const retry = () => {
|
|
46
|
-
console.log("Retrying in 1 second...")
|
|
47
|
-
setTimeout(() => {
|
|
48
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
ws = getWS()
|
|
52
|
-
setupWs(ws)
|
|
53
|
-
}, 1000)
|
|
54
|
-
}
|
|
55
|
-
</script>
|