diff --git a/assistant/package.json b/assistant/package.json
index 9f06e2b..2a21cac 100644
--- a/assistant/package.json
+++ b/assistant/package.json
@@ -47,7 +47,7 @@
"@kevisual/logger": "^0.0.4",
"@kevisual/query": "0.0.33",
"@kevisual/query-login": "0.0.7",
- "@kevisual/router": "^0.0.51",
+ "@kevisual/router": "^0.0.52",
"@kevisual/types": "^0.0.10",
"@kevisual/use-config": "^1.0.21",
"@types/bun": "^1.3.5",
@@ -76,7 +76,7 @@
"access": "public"
},
"dependencies": {
- "@kevisual/ha-api": "^0.0.5",
+ "@kevisual/ha-api": "^0.0.6",
"@kevisual/video-tools": "^0.0.13",
"eventemitter3": "^5.0.1",
"lowdb": "^7.0.1",
diff --git a/assistant/src/app.ts b/assistant/src/app.ts
index b27f79f..b35dc43 100644
--- a/assistant/src/app.ts
+++ b/assistant/src/app.ts
@@ -68,6 +68,9 @@ app.route({
key: 'list',
description: '获取路由列表',
}).define(async (ctx) => {
- const list = ctx.app.getList()
+ const list = ctx.app.getList((item) => {
+ if (item.id === 'auth') return false;
+ return true;
+ })
ctx.body = { list }
}).addTo(app);
\ No newline at end of file
diff --git a/assistant/src/module/assistant/html/login.ts b/assistant/src/module/assistant/html/login.ts
new file mode 100644
index 0000000..d170c24
--- /dev/null
+++ b/assistant/src/module/assistant/html/login.ts
@@ -0,0 +1,52 @@
+export const renderNoAuthAndLogin = (text: string) => {
+ return `
+
+
+
+
+ Login Required
+
+
+
+
+
+`;
+}
\ No newline at end of file
diff --git a/assistant/src/module/local-apps/src/modules/manager.ts b/assistant/src/module/local-apps/src/modules/manager.ts
index 2de18e9..31317e1 100644
--- a/assistant/src/module/local-apps/src/modules/manager.ts
+++ b/assistant/src/module/local-apps/src/modules/manager.ts
@@ -453,7 +453,6 @@ export const LoadApp = async (app: AppInfo, opts?: { mainApp?: any, pm2Connect?:
console.log('gateway app not support');
} else if (app.type === AppType.Pm2SystemApp) {
const pathEntry = path.join(app.path, app.entry);
- console.log('pm2 system app start', pathEntry);
const pm2Manager = new Pm2Manager({
appName: app.key,
script: pathEntry,
@@ -473,7 +472,6 @@ export const LoadApp = async (app: AppInfo, opts?: { mainApp?: any, pm2Connect?:
if (!pm2Options.cwd) {
pm2Options.cwd = path.join(app.path, '../..');
}
- console.log('pm2 start options', pm2Options);
await pm2Manager.start(pm2Options);
} else if (app.type === AppType.ScriptApp) {
// console.log('script app 直接运行,不需要启动');
diff --git a/assistant/src/services/asr/asr-manager.ts b/assistant/src/services/asr/asr-manager.ts
new file mode 100644
index 0000000..4fc9bb5
--- /dev/null
+++ b/assistant/src/services/asr/asr-manager.ts
@@ -0,0 +1,25 @@
+
+import { QwenAsrRelatime } from "@kevisual/video-tools/src/asr/index.ts";
+
+export class AsrManger {
+ map: Map;
+ constructor() {
+ this.map = new Map();
+ }
+
+ getAsr(id: string) {
+ const asr = this.map.get(id);
+ return asr;
+ }
+
+ setAsr(id: string, asr: QwenAsrRelatime) {
+ this.map.set(id, asr);
+ return asr;
+ }
+
+ deleteAsr(id: string) {
+ this.map.delete(id);
+ }
+}
+
+export const arrManager = new AsrManger();
\ No newline at end of file
diff --git a/assistant/src/services/asr/qwen-asr.ts b/assistant/src/services/asr/qwen-asr.ts
index c4bd12b..314caa4 100644
--- a/assistant/src/services/asr/qwen-asr.ts
+++ b/assistant/src/services/asr/qwen-asr.ts
@@ -4,7 +4,7 @@ import { Listener, WebSocketListenerFun, WebSocketReq } from "@kevisual/router";
import { lightHA } from "@/routes/ha-api/ha.ts";
import { assistantConfig } from "@/app.ts";
-const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelatime, msgId: string, startTime?: number }>, res) => {
+const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelatime, msgId: string, startTime?: number, loading?: boolean }>, res) => {
const { ws, emitter, id, data } = req;
let asr = ws.data.asr;
@@ -14,6 +14,9 @@ const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelati
return;
}
if (!asr) {
+ if (ws.data.loading) return;
+ ws.data.loading = true;
+
const confg = assistantConfig.getConfig();
const asrConfig = confg?.asr;
if (!asrConfig?.enabled) {
@@ -27,77 +30,84 @@ const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelati
ws.close();
return;
}
+ const onConnect = () => {
+ const asr = ws.data.asr as QwenAsrRelatime;
+ ws.send(JSON.stringify({ type: 'asr', code: 200, message: 'asr服务已连接', time: Date.now() }));
+ if (!asr) return;
+ asr.emitter.on('message', (message) => {
+ // console.log('ASR message', message);
+ });
+ asr.emitter.on('partial', (message) => {
+ // console.log('ASR message', message);
+ let msgId = ws.data.msgId || Math.random().toString(36).substring(2, 10);
+ ws.data.msgId = msgId;
+ ws.send(JSON.stringify({
+ type: 'partial',
+ msgId: msgId,
+ time: Date.now(),
+ text: message?.text,
+ raw: message?.raw,
+ }));
+ });
+ asr.emitter.on('result', async ({ text, raw }) => {
+ let msgId = ws.data.msgId;
+ ws.data.msgId = Math.random().toString(36).substring(2, 10);
+ const endTime = Date.now();
+ console.log('cost time', ws.data.startTime ? (endTime - ws.data.startTime) : 0);
+ ws.send(JSON.stringify({
+ type: 'result',
+ msgId: msgId,
+ time: Date.now(),
+ text,
+ }));
+ if (!text) return;
+ const command = text?.trim().slice(0, 20);
+ type ParseCommand = {
+ type?: '打开' | '关闭',
+ appName?: string,
+ command?: string,
+ }
+ let obj: ParseCommand = {};
+ if (command.startsWith('打开')) {
+ obj.appName = command.replace('打开', '').trim();
+ obj.type = '打开';
+ } else if (command.startsWith('关闭')) {
+ obj.appName = command.replace('关闭', '').trim();
+ obj.type = '关闭';
+ }
+ if (obj.type) {
+ try {
+ const search = await lightHA.searchLight(obj.appName || '');
+ console.log('searchTime', Date.now() - endTime);
+ if (search.id) {
+ await lightHA.runService({ entity_id: search.id, service: obj.type === '打开' ? 'turn_on' : 'turn_off' });
+ } else if (search.hasMore) {
+ const [first] = search.result;
+ await lightHA.runService({ entity_id: first.entity_id, service: obj.type === '打开' ? 'turn_on' : 'turn_off' });
+ } else {
+ console.log('未找到对应设备:', obj.appName);
+ }
+ console.log('解析到控制指令', obj);
+ } catch (e) {
+ console.error('控制失败', e);
+ }
+ }
+ console.log('toogle light time', Date.now() - endTime);
+ });
+ asr.start();
+ }
// 第一次请求
asr = new QwenAsrRelatime({
token,
- onConnect: () => {
- const asr = ws.data.asr as QwenAsrRelatime;
- ws.send(JSON.stringify({ type: 'asr', code: 200, message: 'asr服务已连接', time: Date.now() }));
- if (!asr) return;
- asr.emitter.on('message', (message) => {
- // console.log('ASR message', message);
- });
- asr.emitter.on('partial', (message) => {
- // console.log('ASR message', message);
- let msgId = ws.data.msgId || Math.random().toString(36).substring(2, 10);
- ws.data.msgId = msgId;
- ws.send(JSON.stringify({
- type: 'partial',
- msgId: msgId,
- time: Date.now(),
- text: message?.text,
- raw: message?.raw,
- }));
- });
- asr.emitter.on('result', async ({ text, raw }) => {
- let msgId = ws.data.msgId;
- ws.data.msgId = Math.random().toString(36).substring(2, 10);
- const endTime = Date.now();
- console.log('cost time', ws.data.startTime ? (endTime - ws.data.startTime) : 0);
- ws.send(JSON.stringify({
- type: 'result',
- msgId: msgId,
- time: Date.now(),
- text,
- }));
- if (!text) return;
- const command = text?.trim().slice(0, 20);
- type ParseCommand = {
- type?: '打开' | '关闭',
- appName?: string,
- command?: string,
- }
- let obj: ParseCommand = {};
- if (command.startsWith('打开')) {
- obj.appName = command.replace('打开', '').trim();
- obj.type = '打开';
- } else if (command.startsWith('关闭')) {
- obj.appName = command.replace('关闭', '').trim();
- obj.type = '关闭';
- }
- if (obj.type) {
- try {
- const search = await lightHA.searchLight(obj.appName || '');
- console.log('searchTime', Date.now() - endTime);
- if (search.id) {
- await lightHA.runService({ entity_id: search.id, service: obj.type === '打开' ? 'turn_on' : 'turn_off' });
- } else if (search.hasMore) {
- const [first] = search.result;
- await lightHA.runService({ entity_id: first.entity_id, service: obj.type === '打开' ? 'turn_on' : 'turn_off' });
- } else {
- console.log('未找到对应设备:', obj.appName);
- }
- console.log('解析到控制指令', obj);
- } catch (e) {
- console.error('控制失败', e);
- }
- }
- console.log('toogle light time', Date.now() - endTime);
- });
- asr.start();
- }
+ emitter,
+ onConnect,
})
ws.data.asr = asr;
+ ws.data.loading = false;
+ emitter.on('close--' + id, () => {
+ console.log('ASR websocket 关闭, 释放资源');
+ asr?.close?.();
+ });
}
const isConnected = await asr.checkConnected();
if (!isConnected) return;
diff --git a/assistant/src/services/proxy/proxy-page-index.ts b/assistant/src/services/proxy/proxy-page-index.ts
index 66b4e52..f9e83d6 100644
--- a/assistant/src/services/proxy/proxy-page-index.ts
+++ b/assistant/src/services/proxy/proxy-page-index.ts
@@ -7,6 +7,7 @@ import { getToken } from '@/module/http-token.ts';
import { getTokenUserCache } from '@/routes/index.ts';
import type { WebSocketListenerFun } from "@kevisual/router";
import WebSocket from 'ws';
+import { renderNoAuthAndLogin } from '@/module/assistant/html/login.ts';
const localProxy = new LocalProxy({});
localProxy.initFromAssistantConfig(assistantConfig);
@@ -161,7 +162,8 @@ export const proxyRoute = async (req: http.IncomingMessage, res: http.ServerResp
}
const filter = await authFilter(req, res);
if (filter) {
- return res.end('Not Authorized Proxy');
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
+ return res.end(renderNoAuthAndLogin('Not Authorized Proxy'));
}
const localProxyProxyList = localProxy.getLocalProxyList();
const localProxyProxy = localProxyProxyList.find((item) => pathname.startsWith(item.path));
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 683f606..9d41ca5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -115,8 +115,8 @@ importers:
assistant:
dependencies:
'@kevisual/ha-api':
- specifier: ^0.0.5
- version: 0.0.5
+ specifier: ^0.0.6
+ version: 0.0.6
'@kevisual/video-tools':
specifier: ^0.0.13
version: 0.0.13(dotenv@17.2.3)(supports-color@10.2.2)
@@ -155,8 +155,8 @@ importers:
specifier: 0.0.7
version: 0.0.7(@kevisual/query@0.0.33)
'@kevisual/router':
- specifier: ^0.0.51
- version: 0.0.51(supports-color@10.2.2)
+ specifier: ^0.0.52
+ version: 0.0.52(supports-color@10.2.2)
'@kevisual/types':
specifier: ^0.0.10
version: 0.0.10
@@ -1308,8 +1308,8 @@ packages:
resolution: {integrity: sha512-4T/m2LqhtwWEW+lWmg7jLxKFW7VtIAftsWFDDZvh10bZunqFf8iXxChHcVSQWikghJb4cq1IkWzPkvc2l+Asdw==}
hasBin: true
- '@kevisual/ha-api@0.0.5':
- resolution: {integrity: sha512-0A4Hmw797yqB9B92G2vPIn4sxIoNYWBm8D3+yWkUkH+XjEBOE65ScK2j4aJVBfwBNd470QqGlK4DcZ5NmQT5iw==}
+ '@kevisual/ha-api@0.0.6':
+ resolution: {integrity: sha512-pZwcE4XYCDItTpMhIP0dIuo2+C07YmhWukVMgTvUuUQBgNo4KJmpItYjeGIvBGsvEM4AjsDGV1mCjTOB1zLu3Q==}
'@kevisual/hot-api@0.0.3':
resolution: {integrity: sha512-qZ4CNK08StZP4+DR1vWwJhKVDoSXXC+PBFG4ZxtkXF5vO2rybE055zp1n3dg5jo8GwW5wxpqMIG3KBp3pYSTkg==}
@@ -1364,6 +1364,9 @@ packages:
'@kevisual/router@0.0.51':
resolution: {integrity: sha512-i9qYBeS/um78oC912oWJD3iElB+5NTKyTrz1Hzf4DckiUFnjLL81UPwjIh5I2l9+ul0IZ/Pxx+sFSF99fJkzKg==}
+ '@kevisual/router@0.0.52':
+ resolution: {integrity: sha512-Qiv3P1XjzD813Tm79S+atrDb2eickGCI9tuy/aCu512LcoYYJqZhwwkeT4ES0DinnA13Ckqd43QWBR6UmuYkHQ==}
+
'@kevisual/types@0.0.10':
resolution: {integrity: sha512-Q73uzzjk9UidumnmCvOpgzqDDvQxsblz22bIFuoiioUFJWwaparx8bpd8ArRyFojicYL1YJoFDzDZ9j9NN8grA==}
@@ -6615,7 +6618,7 @@ snapshots:
transitivePeerDependencies:
- typescript
- '@kevisual/ha-api@0.0.5':
+ '@kevisual/ha-api@0.0.6':
dependencies:
'@kevisual/cache': 0.0.4
fuse.js: 7.1.0
@@ -6759,6 +6762,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@kevisual/router@0.0.52(supports-color@10.2.2)':
+ dependencies:
+ eventemitter3: 5.0.1
+ path-to-regexp: 8.3.0
+ selfsigned: 5.4.0
+ send: 1.2.1(supports-color@10.2.2)
+ transitivePeerDependencies:
+ - supports-color
+
'@kevisual/types@0.0.10': {}
'@kevisual/use-config@1.0.21(dotenv@17.2.3)':
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index ec4051f..f99edec 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,3 +1,7 @@
packages:
- - 'assistant'
- - 'cli-center'
\ No newline at end of file
+ - assistant
+ - cli-center
+
+onlyBuiltDependencies:
+ - esbuild
+ - sharp