@typed-assistant/builder 0.0.48 → 0.0.49
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 +3 -3
- package/src/appProcess.tsx +18 -14
- package/src/bunInstall.tsx +2 -2
- package/src/killProcess.tsx +1 -1
- package/src/pullChanges.tsx +6 -5
- package/src/restartAddon.tsx +2 -2
- package/src/setupGitPoller.tsx +5 -4
- package/src/setupWebhook.tsx +19 -11
- package/src/setupWebserver.tsx +4 -4
- package/src/webserver/Logs.tsx +30 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typed-assistant/builder",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.49",
|
|
4
4
|
"exports": {
|
|
5
5
|
"./appProcess": "./src/appProcess.tsx",
|
|
6
6
|
"./bunInstall": "./src/bunInstall.tsx",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
"home-assistant-js-websocket": "^8.2.0",
|
|
25
25
|
"ts-toolbelt": "^9.6.0",
|
|
26
26
|
"typescript": "^5.3.3",
|
|
27
|
-
"@typed-assistant/eslint-config": "0.0.8",
|
|
28
|
-
"@typed-assistant/logger": "0.0.13",
|
|
29
27
|
"@typed-assistant/typescript-config": "0.0.8",
|
|
28
|
+
"@typed-assistant/eslint-config": "0.0.8",
|
|
29
|
+
"@typed-assistant/logger": "0.0.14",
|
|
30
30
|
"@typed-assistant/utils": "0.0.14"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
package/src/appProcess.tsx
CHANGED
|
@@ -68,7 +68,7 @@ async function buildAndStartAppProcess(
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
async function startApp(appSourceFile: string) {
|
|
71
|
-
logger.info("🚀 Starting app...")
|
|
71
|
+
logger.info({ emoji: "🚀" }, "Starting app...")
|
|
72
72
|
const path = join(process.cwd(), appSourceFile)
|
|
73
73
|
return Bun.spawn(["bun", path], {
|
|
74
74
|
stderr: "pipe",
|
|
@@ -83,7 +83,7 @@ async function killAndRestartApp(
|
|
|
83
83
|
subprocesses: Processes,
|
|
84
84
|
) {
|
|
85
85
|
if (settingUp.current) return subprocesses
|
|
86
|
-
logger.fatal("♻️ Restarting app...")
|
|
86
|
+
logger.fatal({ emoji: "♻️" }, "Restarting app...")
|
|
87
87
|
settingUp.current = true
|
|
88
88
|
if (subprocesses.app) await killSubprocess(subprocesses.app)
|
|
89
89
|
const newSubprocesses = await buildAndStartAppProcess(entryFile, options)
|
|
@@ -109,8 +109,8 @@ const checkProcesses = async (
|
|
|
109
109
|
if (matches.length > 1) {
|
|
110
110
|
multipleProcessesErrorCount++
|
|
111
111
|
if (multipleProcessesErrorCount > 5) {
|
|
112
|
-
const message =
|
|
113
|
-
logger.fatal(
|
|
112
|
+
const message = `Multiple processes detected. Check the logs...`
|
|
113
|
+
logger.fatal({ additionalDetails: ps, emoji: "🚨" }, message)
|
|
114
114
|
onProcessError?.(message, addonUrl)
|
|
115
115
|
return
|
|
116
116
|
}
|
|
@@ -121,8 +121,8 @@ const checkProcesses = async (
|
|
|
121
121
|
if (matches.length === 0) {
|
|
122
122
|
noProcessesErrorCount++
|
|
123
123
|
if (noProcessesErrorCount > 5) {
|
|
124
|
-
const message =
|
|
125
|
-
logger.fatal(
|
|
124
|
+
const message = `No processes detected. Check the logs...`
|
|
125
|
+
logger.fatal({ additionalDetails: ps, emoji: "🚨" }, message)
|
|
126
126
|
onProcessError?.(message, addonUrl)
|
|
127
127
|
return
|
|
128
128
|
}
|
|
@@ -137,11 +137,11 @@ const checkProcesses = async (
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
const getAddonInfo = async () => {
|
|
140
|
-
logger.
|
|
140
|
+
logger.debug({ emoji: "🔍" }, "Getting addon info...")
|
|
141
141
|
|
|
142
142
|
const { data, error } = await getAddonInfoAPI()
|
|
143
143
|
|
|
144
|
-
if (error) logger.error(
|
|
144
|
+
if (error) logger.error({ emoji: "🚨" }, `Failed to get addon info: ${error}`)
|
|
145
145
|
|
|
146
146
|
return data
|
|
147
147
|
}
|
|
@@ -153,7 +153,8 @@ const setupGitSync = async (webhookUrl: string) => {
|
|
|
153
153
|
!process.env.GITHUB_REPO
|
|
154
154
|
) {
|
|
155
155
|
logger.warn(
|
|
156
|
-
"⚠️
|
|
156
|
+
{ emoji: "⚠️" },
|
|
157
|
+
"Cannot sync with Github without Github token, username, and repo details. Add these in the add-on configuration.",
|
|
157
158
|
)
|
|
158
159
|
return { error: {} }
|
|
159
160
|
}
|
|
@@ -161,7 +162,10 @@ const setupGitSync = async (webhookUrl: string) => {
|
|
|
161
162
|
await setupWebhook(webhookUrl)
|
|
162
163
|
return
|
|
163
164
|
}
|
|
164
|
-
logger.warn(
|
|
165
|
+
logger.warn(
|
|
166
|
+
{ emoji: "⚠️" },
|
|
167
|
+
"No HASS_EXTERNAL_URL found. Setting up git poller...",
|
|
168
|
+
)
|
|
165
169
|
await setupGitPoller()
|
|
166
170
|
}
|
|
167
171
|
|
|
@@ -188,14 +192,14 @@ function setupWatcher({
|
|
|
188
192
|
app: Subprocess<"ignore", "pipe", "pipe">
|
|
189
193
|
}
|
|
190
194
|
}) {
|
|
191
|
-
logger.
|
|
195
|
+
logger.debug({ emoji: "👀" }, "Watching directory: ${directoryToWatch}")
|
|
192
196
|
const watcher = watch(
|
|
193
197
|
directoryToWatch,
|
|
194
198
|
{ recursive: true },
|
|
195
199
|
debounce(async function onFileChange(event, filename) {
|
|
196
200
|
if (!filename) return
|
|
197
201
|
if (shouldIgnoreFileOrFolder(filename)) return
|
|
198
|
-
logger.info(
|
|
202
|
+
logger.info({ emoji: "⚠️" }, `Change to ${filename} detected.`)
|
|
199
203
|
if (filename.endsWith("process.tsx")) {
|
|
200
204
|
await killSubprocess(getSubprocesses().app)
|
|
201
205
|
await restartAddon()
|
|
@@ -215,13 +219,13 @@ function setupWatcher({
|
|
|
215
219
|
}
|
|
216
220
|
|
|
217
221
|
process.on("SIGINT", async () => {
|
|
218
|
-
logger.fatal("👋 Exiting...")
|
|
222
|
+
logger.fatal({ emoji: "👋" }, "Exiting...")
|
|
219
223
|
await callSoftKillListeners()
|
|
220
224
|
await callKillListeners()
|
|
221
225
|
process.exit(0)
|
|
222
226
|
})
|
|
223
227
|
process.on("SIGTERM", async () => {
|
|
224
|
-
logger.fatal("👋 Exiting...")
|
|
228
|
+
logger.fatal({ emoji: "👋" }, "Exiting...")
|
|
225
229
|
await callSoftKillListeners()
|
|
226
230
|
await callKillListeners()
|
|
227
231
|
process.exit(0)
|
package/src/bunInstall.tsx
CHANGED
|
@@ -2,10 +2,10 @@ import { logger } from "@typed-assistant/logger"
|
|
|
2
2
|
import { $ } from "bun"
|
|
3
3
|
|
|
4
4
|
export async function bunInstall() {
|
|
5
|
-
logger.info("🏗️ Running bun install...")
|
|
5
|
+
logger.info({ emoji: "🏗️" }, "Running bun install...")
|
|
6
6
|
return $`bun install --frozen-lockfile --cache-dir=.bun-cache`
|
|
7
7
|
.text()
|
|
8
8
|
.catch((error) => {
|
|
9
|
-
logger.error(
|
|
9
|
+
logger.error({ emoji: "🚨" }, `Failed to run bun install: ${error}`)
|
|
10
10
|
})
|
|
11
11
|
}
|
package/src/killProcess.tsx
CHANGED
|
@@ -21,7 +21,7 @@ export async function callSoftKillListeners() {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export async function killSubprocess(subprocess: Subprocess) {
|
|
24
|
-
logger.fatal(
|
|
24
|
+
logger.fatal({ emoji: "💀" }, `Killing process: ${subprocess.pid}`)
|
|
25
25
|
await callSoftKillListeners()
|
|
26
26
|
subprocess.kill()
|
|
27
27
|
await subprocess.exited
|
package/src/pullChanges.tsx
CHANGED
|
@@ -3,24 +3,25 @@ import { $ } from "bun"
|
|
|
3
3
|
import { bunInstall } from "./bunInstall"
|
|
4
4
|
|
|
5
5
|
export const pullChanges = async () => {
|
|
6
|
-
logger.
|
|
6
|
+
logger.debug({ emoji: "⬇️" }, "Pulling changes...")
|
|
7
7
|
const { stderr, stdout } = await $`git pull`.quiet()
|
|
8
8
|
if (stderr.length > 0) {
|
|
9
9
|
logger.error(
|
|
10
|
-
|
|
10
|
+
{ emoji: "⬇️🚨" },
|
|
11
|
+
`Failed to pull changes.\n (${stderr.toString().trim()})`,
|
|
11
12
|
)
|
|
12
13
|
}
|
|
13
14
|
const gitPullText = stdout.toString()
|
|
14
15
|
const packageJSONUpdated = /package.json/.test(gitPullText)
|
|
15
16
|
const nothingNew = /Already up to date./.test(gitPullText)
|
|
16
17
|
if (nothingNew) {
|
|
17
|
-
logger.
|
|
18
|
+
logger.debug({ emoji: "⬇️👌" }, "No new changes.")
|
|
18
19
|
return {}
|
|
19
20
|
} else {
|
|
20
|
-
logger.info("⬇️🆕 Changes pulled.")
|
|
21
|
+
logger.info({ emoji: "⬇️🆕" }, "Changes pulled.")
|
|
21
22
|
}
|
|
22
23
|
if (packageJSONUpdated) {
|
|
23
|
-
logger.info("⬇️📦 package.json updated.")
|
|
24
|
+
logger.info({ emoji: "⬇️📦" }, "package.json updated.")
|
|
24
25
|
await bunInstall()
|
|
25
26
|
}
|
|
26
27
|
return {}
|
package/src/restartAddon.tsx
CHANGED
|
@@ -3,10 +3,10 @@ import { getSupervisorAPI } from "@typed-assistant/utils/getHassAPI"
|
|
|
3
3
|
|
|
4
4
|
export const restartAddon = async () => {
|
|
5
5
|
if (!process.env.SUPERVISOR_TOKEN) {
|
|
6
|
-
logger.fatal("♻️ Can't restart addon. Exiting...")
|
|
6
|
+
logger.fatal({ emoji: "♻️" }, "Can't restart addon. Exiting...")
|
|
7
7
|
process.exit(1)
|
|
8
8
|
return
|
|
9
9
|
}
|
|
10
|
-
logger.fatal("♻️ Restarting addon...")
|
|
10
|
+
logger.fatal({ emoji: "♻️" }, "Restarting addon...")
|
|
11
11
|
await getSupervisorAPI(`/addons/self/restart`, { method: "POST" })
|
|
12
12
|
}
|
package/src/setupGitPoller.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { logger } from "@typed-assistant/logger"
|
|
2
2
|
import { ONE_SECOND } from "@typed-assistant/utils/durations"
|
|
3
3
|
import { pullChanges } from "./pullChanges"
|
|
4
|
-
import { withErrorHandling } from "@typed-assistant/utils/withErrorHandling"
|
|
5
4
|
|
|
6
5
|
export const setupGitPoller = async ({
|
|
7
6
|
gitPullPollDuration,
|
|
@@ -10,9 +9,11 @@ export const setupGitPoller = async ({
|
|
|
10
9
|
gitPullPollDuration?: number
|
|
11
10
|
} = {}) => {
|
|
12
11
|
const duration = gitPullPollDuration ?? 30
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
await pullChanges()
|
|
13
|
+
logger.debug(
|
|
14
|
+
{ emoji: "⬇️⏳" },
|
|
15
|
+
`Pulling changes again in ${duration} seconds...`,
|
|
16
|
+
)
|
|
16
17
|
|
|
17
18
|
setTimeout(() => {
|
|
18
19
|
setupGitPoller({ gitPullPollDuration })
|
package/src/setupWebhook.tsx
CHANGED
|
@@ -20,12 +20,14 @@ const webhookIsSetup = async (webhookUrl: string) => {
|
|
|
20
20
|
.then((d) => d.json())
|
|
21
21
|
|
|
22
22
|
if (error) {
|
|
23
|
-
logger.error(
|
|
24
|
-
|
|
23
|
+
logger.error(
|
|
24
|
+
{ additionalDetails: error.message, emoji: "🚨" },
|
|
25
|
+
`Failed to reach webhook "${webhookUrl}"`,
|
|
26
|
+
)
|
|
25
27
|
return false
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
logger.
|
|
30
|
+
logger.debug({ emoji: "🪝" }, "Webhook reached successfully: ")
|
|
29
31
|
return true
|
|
30
32
|
}
|
|
31
33
|
|
|
@@ -124,13 +126,16 @@ export const setupWebhook = async (webhookUrl: string): Promise<void> => {
|
|
|
124
126
|
if (retries < 5) {
|
|
125
127
|
retries++
|
|
126
128
|
logger.error(
|
|
127
|
-
|
|
129
|
+
{ emoji: "🔁" },
|
|
130
|
+
`Failed fetching webhooks. Retrying setup in ${retryTimeout / 1000}s...`,
|
|
128
131
|
)
|
|
129
132
|
setTimeout(setupWebhook, retryTimeout)
|
|
130
133
|
return
|
|
131
134
|
}
|
|
132
|
-
logger.error(
|
|
133
|
-
|
|
135
|
+
logger.error(
|
|
136
|
+
{ additionalDetails: error.message, emoji: "🚨" },
|
|
137
|
+
"Failed fetching webhooks. Giving up.",
|
|
138
|
+
)
|
|
134
139
|
return
|
|
135
140
|
}
|
|
136
141
|
|
|
@@ -139,7 +144,7 @@ export const setupWebhook = async (webhookUrl: string): Promise<void> => {
|
|
|
139
144
|
)
|
|
140
145
|
|
|
141
146
|
if (webhookAlreadyExists) {
|
|
142
|
-
logger.info("🪝 Webhook already set up")
|
|
147
|
+
logger.info({ emoji: "🪝" }, "Webhook already set up")
|
|
143
148
|
return
|
|
144
149
|
}
|
|
145
150
|
|
|
@@ -152,15 +157,18 @@ export const setupWebhook = async (webhookUrl: string): Promise<void> => {
|
|
|
152
157
|
if (retries < 5) {
|
|
153
158
|
retries++
|
|
154
159
|
logger.error(
|
|
155
|
-
|
|
160
|
+
{ emoji: "🔁" },
|
|
161
|
+
`Failed creating webhook. Retrying setup in ${retryTimeout / 1000}s...`,
|
|
156
162
|
)
|
|
157
163
|
setTimeout(setupWebhook, retryTimeout)
|
|
158
164
|
return
|
|
159
165
|
}
|
|
160
|
-
logger.error(
|
|
161
|
-
|
|
166
|
+
logger.error(
|
|
167
|
+
{ additionalDetails: createError.message, emoji: "🚨" },
|
|
168
|
+
"Failed creating webhook. Giving up.",
|
|
169
|
+
)
|
|
162
170
|
return
|
|
163
171
|
}
|
|
164
172
|
|
|
165
|
-
logger.info("🪝 Webhook created: " + webhook.config.url)
|
|
173
|
+
logger.info({ emoji: "🪝" }, "Webhook created: " + webhook.config.url)
|
|
166
174
|
}
|
package/src/setupWebserver.tsx
CHANGED
|
@@ -77,10 +77,10 @@ export const startWebappServer = async ({
|
|
|
77
77
|
}
|
|
78
78
|
throw new Error("Build failed")
|
|
79
79
|
}
|
|
80
|
-
logger.
|
|
80
|
+
logger.debug({ emoji: "🛠️" }, "Web server built successfully")
|
|
81
81
|
|
|
82
82
|
await $`bunx tailwindcss -c ${tailwindConfig} -i ${cssFile} -o ${cssOutputFile}`.quiet()
|
|
83
|
-
logger.
|
|
83
|
+
logger.debug({ emoji: "💄" }, "Tailwind built successfully")
|
|
84
84
|
|
|
85
85
|
const indexHtml = (await Bun.file(indexHtmlFilePath).text())
|
|
86
86
|
.replace(
|
|
@@ -189,10 +189,10 @@ export const startWebappServer = async ({
|
|
|
189
189
|
})
|
|
190
190
|
|
|
191
191
|
server.listen(8099)
|
|
192
|
-
logger.info("🌐 Web server listening on port 8099")
|
|
192
|
+
logger.info({ emoji: "🌐" }, "Web server listening on port 8099")
|
|
193
193
|
|
|
194
194
|
const directory = join(process.cwd(), ".")
|
|
195
|
-
logger.
|
|
195
|
+
logger.debug({ emoji: "👀" }, "Watching log.txt")
|
|
196
196
|
const watcher = watch(directory, function onFileChange(_event, filename) {
|
|
197
197
|
if (filename === "log.txt") {
|
|
198
198
|
logSubscribers.forEach((send) => send())
|
package/src/webserver/Logs.tsx
CHANGED
|
@@ -6,22 +6,13 @@ import { app } from "./api"
|
|
|
6
6
|
import { useWS } from "./useWS"
|
|
7
7
|
import { getPrettyTimestamp } from "@typed-assistant/utils/getPrettyTimestamp"
|
|
8
8
|
import { levels } from "@typed-assistant/logger/levels"
|
|
9
|
-
|
|
10
|
-
const LogSchema = z.object({
|
|
11
|
-
level: z.number(),
|
|
12
|
-
time: z.number(),
|
|
13
|
-
pid: z.number(),
|
|
14
|
-
hostname: z.string(),
|
|
15
|
-
msg: z.string(),
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
type LogSchema = z.infer<typeof LogSchema>
|
|
9
|
+
import type { LogSchema } from "@typed-assistant/logger"
|
|
19
10
|
|
|
20
11
|
export const Logs = () => {
|
|
21
12
|
const [limit, setLimit] = useState(200)
|
|
22
13
|
const [level, setLevel] = useState<
|
|
23
14
|
"trace" | "debug" | "info" | "warn" | "error" | "fatal"
|
|
24
|
-
>("
|
|
15
|
+
>("info")
|
|
25
16
|
const [dateTimeVisibility, setDateTimeVisibility] = useState<
|
|
26
17
|
"hidden" | "timeOnly" | "visible"
|
|
27
18
|
>("timeOnly")
|
|
@@ -35,7 +26,7 @@ export const Logs = () => {
|
|
|
35
26
|
onMessage: useCallback((event) => {
|
|
36
27
|
setLogs(
|
|
37
28
|
(JSON.parse(event.data).logs as string[]).map((log: string) =>
|
|
38
|
-
|
|
29
|
+
JSON.parse(log),
|
|
39
30
|
),
|
|
40
31
|
)
|
|
41
32
|
}, []),
|
|
@@ -101,18 +92,33 @@ export const Logs = () => {
|
|
|
101
92
|
{logs
|
|
102
93
|
.filter((log) => log.level >= (levels[level] ?? 0))
|
|
103
94
|
.sort((a, b) => b.time - a.time)
|
|
104
|
-
.map((log, index) =>
|
|
105
|
-
|
|
106
|
-
<
|
|
107
|
-
{
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
95
|
+
.map((log, index) => {
|
|
96
|
+
return (
|
|
97
|
+
<li
|
|
98
|
+
key={(log.time ?? index) + log.time + log.msg}
|
|
99
|
+
className="flex gap-1"
|
|
100
|
+
>
|
|
101
|
+
<span className="text-slate-400 mr-2">
|
|
102
|
+
{dateTimeVisibility === "hidden"
|
|
103
|
+
? null
|
|
104
|
+
: dateTimeVisibility === "timeOnly"
|
|
105
|
+
? new Date(log.time).toLocaleTimeString("en-GB")
|
|
106
|
+
: getPrettyTimestamp(log.time)}
|
|
107
|
+
</span>
|
|
108
|
+
<div className="flex">
|
|
109
|
+
{log.emoji}{" "}
|
|
110
|
+
{log.additionalDetails ? (
|
|
111
|
+
<details>
|
|
112
|
+
<summary>{log.msg}</summary>
|
|
113
|
+
<pre>{log.additionalDetails}</pre>
|
|
114
|
+
</details>
|
|
115
|
+
) : (
|
|
116
|
+
log.msg
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
</li>
|
|
120
|
+
)
|
|
121
|
+
})}
|
|
116
122
|
</ul>
|
|
117
123
|
</pre>
|
|
118
124
|
</AppSection>
|