udpate
This commit is contained in:
2
.cnb.yml
2
.cnb.yml
@@ -16,6 +16,8 @@ $:
|
|||||||
services:
|
services:
|
||||||
- vscode
|
- vscode
|
||||||
- docker
|
- docker
|
||||||
|
env:
|
||||||
|
CNB_BUILD_WORKSPACE: "/root/workspace"
|
||||||
imports: !reference [.common_env, imports]
|
imports: !reference [.common_env, imports]
|
||||||
# 开发环境启动后会执行的任务
|
# 开发环境启动后会执行的任务
|
||||||
# stages:
|
# stages:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from "../../agent/plugin";
|
export { AgentPlugin } from "../../src/main.ts";
|
||||||
35
assistant/.opencode/skills/kill-opencode/SKILL.md
Normal file
35
assistant/.opencode/skills/kill-opencode/SKILL.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
name: kill-opencode
|
||||||
|
description: 自动查找并杀死所有opencode相关的进程,确保系统资源释放。
|
||||||
|
tags:
|
||||||
|
- opencode
|
||||||
|
- process-management
|
||||||
|
- automation
|
||||||
|
---
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
# kill_opencode.sh - 自动杀死所有opencode进程
|
||||||
|
echo "正在查找opencode进程..."
|
||||||
|
ps aux | grep opencode | grep -v grep
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "正在杀死所有opencode进程..."
|
||||||
|
pkill -f opencode
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# 检查是否还有残留进程
|
||||||
|
remaining=$(ps aux | grep opencode | grep -v grep | wc -l)
|
||||||
|
if [ $remaining -gt 0 ]; then
|
||||||
|
echo "发现 $remaining 个顽固进程,使用强制杀死模式..."
|
||||||
|
pkill -9 -f opencode
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "opencode进程清理完成!"
|
||||||
|
else
|
||||||
|
echo "未找到opencode进程"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 验证是否已完全清理
|
||||||
|
echo "当前opencode进程状态:"
|
||||||
|
ps aux | grep opencode | grep -v grep || echo "没有运行中的opencode进程"
|
||||||
|
```
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { app } from '../src/main.ts'
|
|
||||||
|
|
||||||
export { app }
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
import { tool } from "@opencode-ai/plugin/tool"
|
|
||||||
import { type Plugin } from "@opencode-ai/plugin"
|
|
||||||
import { app } from './index.ts';
|
|
||||||
import { Skill } from "@kevisual/router";
|
|
||||||
|
|
||||||
import './call.ts';
|
|
||||||
import './step.ts';
|
|
||||||
|
|
||||||
const routes = app.router.routes.filter(r => {
|
|
||||||
const metadata = r.metadata as Skill
|
|
||||||
if (metadata && metadata.tags && metadata.tags.includes('opencode')) {
|
|
||||||
return !!metadata.skill
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
// opencode run "查看系统信息"
|
|
||||||
export const AgentPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
|
|
||||||
return {
|
|
||||||
'tool': {
|
|
||||||
...routes.reduce((acc, route) => {
|
|
||||||
const metadata = route.metadata as Skill
|
|
||||||
acc[metadata.skill!] = {
|
|
||||||
name: metadata.title || metadata.skill,
|
|
||||||
description: metadata.summary || '',
|
|
||||||
args: metadata.args || {},
|
|
||||||
async execute(args: Record<string, any>) {
|
|
||||||
console.log(`Executing skill ${metadata.skill} with args:`, args);
|
|
||||||
await client.app.log({
|
|
||||||
body: {
|
|
||||||
service: 'cnb',
|
|
||||||
level: 'info',
|
|
||||||
message: `Executing skill ${metadata.skill} with args: ${JSON.stringify(args)}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const res = await app.run({
|
|
||||||
path: route.path,
|
|
||||||
key: route.key,
|
|
||||||
payload: args
|
|
||||||
},
|
|
||||||
// @ts-ignore
|
|
||||||
{ appId: app.appId! });
|
|
||||||
if (res.code === 200) {
|
|
||||||
if (res.data?.content) {
|
|
||||||
return res.data.content;
|
|
||||||
}
|
|
||||||
const str = JSON.stringify(res.data || res, null, 2);
|
|
||||||
if (str.length > 5000) {
|
|
||||||
return str.slice(0, 5000) + '... (truncated)';
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
return `Error: ${res?.message || '无法获取结果'}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, any>)
|
|
||||||
},
|
|
||||||
'tool.execute.before': async (opts) => {
|
|
||||||
// console.log('CnbPlugin: tool.execute.before', opts.tool);
|
|
||||||
// delete toolSkills['cnb-login-verify']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { createSkill } from '@kevisual/router'
|
|
||||||
import { app } from './index.ts'
|
|
||||||
import { tool } from '@opencode-ai/plugin/tool'
|
|
||||||
|
|
||||||
// "调用 path: test key: step"
|
|
||||||
app.route({
|
|
||||||
path: 'test',
|
|
||||||
key: 'step',
|
|
||||||
description: '测试步骤调用',
|
|
||||||
metadata: {
|
|
||||||
tags: ['opencode'],
|
|
||||||
...createSkill({
|
|
||||||
skill: 'test-step',
|
|
||||||
title: '获取系统信息',
|
|
||||||
summary: '根据步骤获取系统信息',
|
|
||||||
args: {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}).define(async (ctx) => {
|
|
||||||
ctx.body = {
|
|
||||||
context: `执行步骤
|
|
||||||
1. 调用 path: client key: system 获取系统信息
|
|
||||||
2. 调用 path: client key: time 获取当前时间
|
|
||||||
3. 返回结果`,
|
|
||||||
};
|
|
||||||
}).addTo(app)
|
|
||||||
@@ -40,6 +40,28 @@ await Bun.build({
|
|||||||
},
|
},
|
||||||
external,
|
external,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await Bun.build({
|
||||||
|
target: 'node',
|
||||||
|
format: 'esm',
|
||||||
|
entrypoints: [w('./src/main.ts')],
|
||||||
|
outdir: w('./dist'),
|
||||||
|
naming: {
|
||||||
|
entry: 'assistant-opencode.js',
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
ENVISION_VERSION: JSON.stringify(pkg.version),
|
||||||
|
},
|
||||||
|
external,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dts = 'dts -i src/main.ts -o assistant-opencode.d.ts';
|
||||||
|
Bun.spawnSync({
|
||||||
|
cmd: ['sh', '-c', dts],
|
||||||
|
cwd: __dirname,
|
||||||
|
stdout: 'inherit',
|
||||||
|
stderr: 'inherit',
|
||||||
|
});
|
||||||
// const copyDist = ['dist', 'bin'];
|
// const copyDist = ['dist', 'bin'];
|
||||||
const copyDist = ['dist'];
|
const copyDist = ['dist'];
|
||||||
export const copyFileToEnvision = async () => {
|
export const copyFileToEnvision = async () => {
|
||||||
|
|||||||
@@ -47,10 +47,10 @@
|
|||||||
"@kevisual/logger": "^0.0.4",
|
"@kevisual/logger": "^0.0.4",
|
||||||
"@kevisual/query": "0.0.35",
|
"@kevisual/query": "0.0.35",
|
||||||
"@kevisual/query-login": "0.0.7",
|
"@kevisual/query-login": "0.0.7",
|
||||||
"@kevisual/router": "^0.0.56",
|
"@kevisual/router": "^0.0.57",
|
||||||
"@kevisual/types": "^0.0.11",
|
"@kevisual/types": "^0.0.11",
|
||||||
"@kevisual/use-config": "^1.0.28",
|
"@kevisual/use-config": "^1.0.28",
|
||||||
"@opencode-ai/plugin": "^1.1.25",
|
"@opencode-ai/plugin": "^1.1.26",
|
||||||
"@types/bun": "^1.3.6",
|
"@types/bun": "^1.3.6",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^25.0.9",
|
"@types/node": "^25.0.9",
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
"@kevisual/ha-api": "^0.0.6",
|
"@kevisual/ha-api": "^0.0.6",
|
||||||
"@kevisual/oss": "^0.0.16",
|
"@kevisual/oss": "^0.0.16",
|
||||||
"@kevisual/video-tools": "^0.0.13",
|
"@kevisual/video-tools": "^0.0.13",
|
||||||
"@opencode-ai/sdk": "^1.1.25",
|
"@opencode-ai/sdk": "^1.1.26",
|
||||||
"es-toolkit": "^1.44.0",
|
"es-toolkit": "^1.44.0",
|
||||||
"eventemitter3": "^5.0.4",
|
"eventemitter3": "^5.0.4",
|
||||||
"lowdb": "^7.0.1",
|
"lowdb": "^7.0.1",
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { App } from '@kevisual/router';
|
|||||||
import { SimpleRouter } from '@kevisual/router/simple'
|
import { SimpleRouter } from '@kevisual/router/simple'
|
||||||
// import { App } from '@kevisual/router/src/app.ts';
|
// import { App } from '@kevisual/router/src/app.ts';
|
||||||
// import { AssistantConfig } from '@/module/assistant/index.ts';
|
// import { AssistantConfig } from '@/module/assistant/index.ts';
|
||||||
|
|
||||||
import { AssistantInit, parseHomeArg } from '@/services/init/index.ts';
|
import { AssistantInit, parseHomeArg } from '@/services/init/index.ts';
|
||||||
import { configDir as HomeConfigDir } from '@/module/assistant/config/index.ts';
|
import { configDir as HomeConfigDir } from '@/module/assistant/config/index.ts';
|
||||||
import { useContextKey } from '@kevisual/use-config/context';
|
import { useContextKey } from '@kevisual/use-config/context';
|
||||||
import { AssistantQuery } from '@/module/assistant/query/index.ts';
|
import { AssistantQuery } from '@/module/assistant/query/index.ts';
|
||||||
|
|
||||||
const manualParse = parseHomeArg(HomeConfigDir);
|
const manualParse = parseHomeArg(HomeConfigDir);
|
||||||
const _configDir = manualParse.configDir;
|
const _configDir = manualParse.configDir;
|
||||||
export const configDir = AssistantInit.detectConfigDir(_configDir);
|
export const configDir = AssistantInit.detectConfigDir(_configDir);
|
||||||
@@ -14,6 +16,7 @@ export const assistantConfig = useContextKey<AssistantInit>('assistantConfig', (
|
|||||||
return new AssistantInit({
|
return new AssistantInit({
|
||||||
path: configDir,
|
path: configDir,
|
||||||
init: isInit,
|
init: isInit,
|
||||||
|
initWorkspace: manualParse.isOpencode ? false : true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -28,8 +31,6 @@ export const runtime = useContextKey('runtime', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const app: App = useContextKey<App>('app', () => {
|
export const app: App = useContextKey<App>('app', () => {
|
||||||
const init = isInit;
|
|
||||||
if (init) {
|
|
||||||
return new App({
|
return new App({
|
||||||
serverOptions: {
|
serverOptions: {
|
||||||
path: '/client/router',
|
path: '/client/router',
|
||||||
@@ -40,7 +41,6 @@ export const app: App = useContextKey<App>('app', () => {
|
|||||||
io: true
|
io: true
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const simpleRouter = useContextKey('simpleRouter', () => {
|
export const simpleRouter = useContextKey('simpleRouter', () => {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { app, assistantConfig } from './app.ts';
|
import { app } from './app.ts';
|
||||||
|
import { type Plugin } from "@opencode-ai/plugin"
|
||||||
|
import { createRouterAgentPluginFn } from '@kevisual/router/opencode';
|
||||||
import './routes/index.ts';
|
import './routes/index.ts';
|
||||||
import './routes-simple/index.ts';
|
import './routes-simple/index.ts';
|
||||||
|
|
||||||
export { app, assistantConfig };
|
export const AgentPlugin: Plugin = createRouterAgentPluginFn({
|
||||||
|
router: app.router,
|
||||||
|
})
|
||||||
@@ -384,6 +384,7 @@ export function parseArgs(args: string[]) {
|
|||||||
*/
|
*/
|
||||||
export const parseHomeArg = (homedir?: string) => {
|
export const parseHomeArg = (homedir?: string) => {
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
|
const execPath = process.execPath;
|
||||||
const options = parseArgs(args);
|
const options = parseArgs(args);
|
||||||
let _configDir = undefined;
|
let _configDir = undefined;
|
||||||
if (options.home && homedir) {
|
if (options.home && homedir) {
|
||||||
@@ -391,7 +392,9 @@ export const parseHomeArg = (homedir?: string) => {
|
|||||||
} else if (options.root) {
|
} else if (options.root) {
|
||||||
_configDir = options.root;
|
_configDir = options.root;
|
||||||
}
|
}
|
||||||
|
const isOpencode = execPath.includes('.opencode') || execPath.includes('opencode.exe');
|
||||||
return {
|
return {
|
||||||
|
isOpencode,
|
||||||
options,
|
options,
|
||||||
configDir: _configDir,
|
configDir: _configDir,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { createSkill } from '@kevisual/router'
|
|
||||||
import { app } from './index.ts'
|
|
||||||
import { tool } from '@opencode-ai/plugin/tool'
|
|
||||||
|
|
||||||
// "调用 path: router key: list"
|
import { app } from '../../app.ts';
|
||||||
|
import { createSkill, tool } from '@kevisual/router';
|
||||||
|
|
||||||
app.route({
|
app.route({
|
||||||
path: 'call',
|
path: 'call',
|
||||||
key: '',
|
key: '',
|
||||||
@@ -1,2 +1,37 @@
|
|||||||
import { LightHA } from "@kevisual/ha-api";
|
import { LightHA } from "@kevisual/ha-api";
|
||||||
export const lightHA = new LightHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL });
|
export const lightHA = new LightHA({ token: process.env.HAAS_TOKEN || '', homeassistantURL: process.env.HAAS_URL });
|
||||||
|
|
||||||
|
export const callText = async (text: string) => {
|
||||||
|
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 = '关闭';
|
||||||
|
}
|
||||||
|
let endTime = Date.now();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,10 +5,10 @@ import './ai/index.ts';
|
|||||||
// TODO:
|
// TODO:
|
||||||
// import './light-code/index.ts';
|
// import './light-code/index.ts';
|
||||||
import './user/index.ts';
|
import './user/index.ts';
|
||||||
|
import './call/index.ts'
|
||||||
|
|
||||||
// TODO: 移除
|
// TODO: 移除
|
||||||
// import './hot-api/key-sender/index.ts';
|
// import './hot-api/key-sender/index.ts';
|
||||||
|
|
||||||
import './opencode/index.ts';
|
import './opencode/index.ts';
|
||||||
|
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { useKey } from "@kevisual/use-config";
|
|
||||||
import { app } from '@/app.ts'
|
import { app } from '@/app.ts'
|
||||||
import { createSkill } from "@kevisual/router";
|
import { createSkill, tool } from "@kevisual/router";
|
||||||
import { opencodeManager } from './module/open.js'
|
import { opencodeManager } from './module/open.ts'
|
||||||
|
import path from "node:path";
|
||||||
|
import { execSync } from "node:child_process";
|
||||||
|
|
||||||
|
// 创建一个opencode 客户端
|
||||||
app.route({
|
app.route({
|
||||||
path: 'opencode',
|
path: 'opencode',
|
||||||
key: 'create',
|
key: 'create',
|
||||||
@@ -21,9 +23,52 @@ app.route({
|
|||||||
},
|
},
|
||||||
}).define(async (ctx) => {
|
}).define(async (ctx) => {
|
||||||
const client = await opencodeManager.getClient();
|
const client = await opencodeManager.getClient();
|
||||||
ctx.body = { success: true, url: opencodeManager.url, message: 'OpenCode 客户端已就绪' };
|
ctx.body = { content: `${opencodeManager.url} OpenCode 客户端已就绪` };
|
||||||
}).addTo(app);
|
}).addTo(app);
|
||||||
|
|
||||||
|
// 关闭 opencode 客户端
|
||||||
|
app.route({
|
||||||
|
path: 'opencode',
|
||||||
|
key: 'close',
|
||||||
|
middleware: ['auth'],
|
||||||
|
description: '关闭 OpenCode 客户端',
|
||||||
|
metadata: {
|
||||||
|
tags: ['opencode'],
|
||||||
|
...createSkill({
|
||||||
|
skill: 'close-opencode-client',
|
||||||
|
title: '关闭 OpenCode 客户端',
|
||||||
|
summary: '关闭 OpenCode 客户端',
|
||||||
|
args: {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}).define(async (ctx) => {
|
||||||
|
await opencodeManager.close();
|
||||||
|
ctx.body = { content: 'OpenCode 客户端已关闭' };
|
||||||
|
}).addTo(app);
|
||||||
|
|
||||||
|
// 调用 path: opencode key: getUrl
|
||||||
|
app.route({
|
||||||
|
path: 'opencode',
|
||||||
|
key: 'getUrl',
|
||||||
|
middleware: ['auth'],
|
||||||
|
description: '获取 OpenCode 服务 URL',
|
||||||
|
metadata: {
|
||||||
|
tags: ['opencode'],
|
||||||
|
...createSkill({
|
||||||
|
skill: 'get-opencode-url',
|
||||||
|
title: '获取 OpenCode 服务 URL',
|
||||||
|
summary: '获取当前 OpenCode 服务的 URL 地址',
|
||||||
|
args: {
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}).define(async (ctx) => {
|
||||||
|
const url = opencodeManager.getUrl();
|
||||||
|
ctx.body = { content: url };
|
||||||
|
}).addTo(app);
|
||||||
// 调用 path: opencode key: ls-projects
|
// 调用 path: opencode key: ls-projects
|
||||||
app.route({
|
app.route({
|
||||||
path: 'opencode',
|
path: 'opencode',
|
||||||
@@ -38,3 +83,39 @@ app.route({
|
|||||||
};
|
};
|
||||||
}).addTo(app);
|
}).addTo(app);
|
||||||
|
|
||||||
|
// 调用 path: opencode key: runProject 参数 /home/ubuntu/cli/assistant
|
||||||
|
app.route({
|
||||||
|
path: 'opencode',
|
||||||
|
key: 'runProject',
|
||||||
|
middleware: ['auth'],
|
||||||
|
metadata: {
|
||||||
|
tags: ['opencode'],
|
||||||
|
...createSkill({
|
||||||
|
skill: 'run-opencode-project',
|
||||||
|
title: '运行 OpenCode 项目',
|
||||||
|
summary: '运行一个已有的 OpenCode 项目',
|
||||||
|
args: {
|
||||||
|
projectPath: tool.schema.string().optional().describe('OpenCode 项目的路径, 默认为 /workspace')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).define(async (ctx) => {
|
||||||
|
const { projectPath = '/workspace' } = ctx.query;
|
||||||
|
try {
|
||||||
|
|
||||||
|
// const directory = path.resolve(projectPath);
|
||||||
|
// const runOpencodeCli = 'opencode run hello';
|
||||||
|
// execSync(runOpencodeCli, { cwd: directory, stdio: 'inherit' });
|
||||||
|
// ctx.body = { content: `OpenCode 项目已在路径 ${directory} 运行` };
|
||||||
|
const client = await opencodeManager.getClient();
|
||||||
|
const session = await client.session.create({
|
||||||
|
query: {
|
||||||
|
directory: projectPath
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log('Created session:', session.data.id);
|
||||||
|
ctx.body = { content: `OpenCode 项目已在路径 ${projectPath} 运行` };
|
||||||
|
} catch (error) {
|
||||||
|
ctx.body = { content: `运行 OpenCode 项目失败, 请手动运行命令初始化: opencode run hello` };
|
||||||
|
}
|
||||||
|
}).addTo(app);
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
import { createOpencode, OpencodeClient } from "@opencode-ai/sdk";
|
import { createOpencode, createOpencodeClient, OpencodeClient, } from "@opencode-ai/sdk";
|
||||||
|
import { randomInt } from "es-toolkit";
|
||||||
|
import getPort from "get-port";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import { execSync } from "node:child_process";
|
||||||
|
|
||||||
|
|
||||||
export class OpencodeManager {
|
export class OpencodeManager {
|
||||||
private static instance: OpencodeManager | null = null;
|
private static instance: OpencodeManager | null = null;
|
||||||
private client: OpencodeClient | null = null;
|
private client: OpencodeClient | null = null;
|
||||||
private server: { url: string; close(): void } | null = null;
|
private server: { url: string; close(): void } | null = null;
|
||||||
private isInitializing: boolean = false;
|
private isInitializing: boolean = false;
|
||||||
|
private currentPort: number | null = null;
|
||||||
|
|
||||||
public url: string = '';
|
public url: string = '';
|
||||||
private constructor() { }
|
private constructor() { }
|
||||||
@@ -31,25 +39,88 @@ export class OpencodeManager {
|
|||||||
// 开始初始化
|
// 开始初始化
|
||||||
this.isInitializing = true;
|
this.isInitializing = true;
|
||||||
try {
|
try {
|
||||||
|
const port = 5000;
|
||||||
|
const currentPort = await getPort({ port: port });
|
||||||
|
if (port === currentPort) {
|
||||||
const result = await createOpencode({
|
const result = await createOpencode({
|
||||||
hostname: '0.0.0.0',
|
hostname: '0.0.0.0',
|
||||||
|
port: port
|
||||||
});
|
});
|
||||||
console.log('OpencodeManager: OpenCode 服务已启动', result.server.url);
|
|
||||||
this.url = result.server.url;
|
this.url = result.server.url;
|
||||||
this.client = result.client;
|
this.client = result.client;
|
||||||
this.server = result.server;
|
this.server = result.server;
|
||||||
return this.client;
|
return this.client;
|
||||||
|
} else {
|
||||||
|
this.client = await this.createOpencodeProject({ port });
|
||||||
|
this.url = `http://localhost:${port}`;
|
||||||
|
return this.client;
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.isInitializing = false;
|
this.isInitializing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async createOpencodeProject({
|
||||||
close(): void {
|
directory,
|
||||||
|
port = 5000
|
||||||
|
}: { directory?: string, port?: number }): Promise<OpencodeClient> {
|
||||||
|
const client = createOpencodeClient({
|
||||||
|
baseUrl: `http://localhost:${port}`,
|
||||||
|
directory
|
||||||
|
});
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
async killPort(port: number): Promise<void> {
|
||||||
|
try {
|
||||||
|
// 尝试 使用命令行去关闭 port为5000的服务
|
||||||
|
if (os.platform() === 'win32') {
|
||||||
|
// Windows 平台
|
||||||
|
execSync(`netstat -ano | findstr :${port} | findstr LISTENING`).toString().split('\n').forEach(line => {
|
||||||
|
const parts = line.trim().split(/\s+/);
|
||||||
|
const pid = parts[parts.length - 1];
|
||||||
|
if (pid) {
|
||||||
|
execSync(`taskkill /PID ${pid} /F`);
|
||||||
|
console.log(`OpencodeManager: 已关闭占用端口 ${port} 的进程 PID ${pid}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Unix-like 平台
|
||||||
|
const result = execSync(`lsof -i :${port} -t`).toString();
|
||||||
|
result.split('\n').forEach(pid => {
|
||||||
|
if (pid) {
|
||||||
|
execSync(`kill -9 ${pid}`);
|
||||||
|
console.log(`OpencodeManager: 已关闭占用端口 ${port} 的进程 PID ${pid}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to close OpenCode server:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async close(): Promise<void> {
|
||||||
if (this.server) {
|
if (this.server) {
|
||||||
this.server.close();
|
this.server.close();
|
||||||
this.server = null;
|
this.server = null;
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
const port = 5000;
|
||||||
|
const currentPort = await getPort({ port: port });
|
||||||
|
if (port === currentPort) {
|
||||||
this.client = null;
|
this.client = null;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
await this.killPort(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client = null;
|
||||||
|
}
|
||||||
|
async getUrl(): Promise<string> {
|
||||||
|
if (this.url) {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
if (!this.url) {
|
||||||
|
await this.getClient();
|
||||||
|
}
|
||||||
|
return 'http://localhost:5000';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { QwenAsrRelatime } from "@kevisual/video-tools/src/asr/index.ts";
|
import { QwenAsrRelatime } from "@kevisual/video-tools/src/asr/index.ts";
|
||||||
|
|
||||||
import { Listener, WebSocketListenerFun, WebSocketReq } from "@kevisual/router";
|
import { Listener, WebSocketListenerFun, WebSocketReq } from "@kevisual/router";
|
||||||
import { lightHA } from "@/routes/ha-api/ha.ts";
|
import { callText } from "@/routes/ha-api/ha.ts";
|
||||||
import { assistantConfig } from "@/app.ts";
|
import { assistantConfig } from "@/app.ts";
|
||||||
|
|
||||||
const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelatime, msgId: string, startTime?: number, loading?: boolean }>, res) => {
|
const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelatime, msgId: string, startTime?: number, loading?: boolean }>, res) => {
|
||||||
@@ -61,37 +61,7 @@ const func: WebSocketListenerFun = async (req: WebSocketReq<{ asr: QwenAsrRelati
|
|||||||
text,
|
text,
|
||||||
}));
|
}));
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
const command = text?.trim().slice(0, 20);
|
await callText(text);
|
||||||
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);
|
console.log('toogle light time', Date.now() - endTime);
|
||||||
});
|
});
|
||||||
asr.start();
|
asr.start();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export { parseHomeArg, parseHelpArg };
|
|||||||
export type AssistantInitOptions = {
|
export type AssistantInitOptions = {
|
||||||
path?: string;
|
path?: string;
|
||||||
init?: boolean;
|
init?: boolean;
|
||||||
|
initWorkspace?: boolean;
|
||||||
};
|
};
|
||||||
const randomId = () => Math.random().toString(36).substring(2, 8);
|
const randomId = () => Math.random().toString(36).substring(2, 8);
|
||||||
/**
|
/**
|
||||||
@@ -16,12 +17,14 @@ const randomId = () => Math.random().toString(36).substring(2, 8);
|
|||||||
*/
|
*/
|
||||||
export class AssistantInit extends AssistantConfig {
|
export class AssistantInit extends AssistantConfig {
|
||||||
#query: Query
|
#query: Query
|
||||||
|
initWorkspace: boolean = false;
|
||||||
constructor(opts?: AssistantInitOptions) {
|
constructor(opts?: AssistantInitOptions) {
|
||||||
const configDir = opts?.path || process.cwd();
|
const configDir = opts?.path || process.cwd();
|
||||||
super({
|
super({
|
||||||
configDir,
|
configDir,
|
||||||
init: false,
|
init: false,
|
||||||
});
|
});
|
||||||
|
this.initWorkspace = opts?.initWorkspace ?? true;
|
||||||
if (opts?.init) {
|
if (opts?.init) {
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
@@ -35,8 +38,10 @@ export class AssistantInit extends AssistantConfig {
|
|||||||
if (!this.checkConfigPath()) {
|
if (!this.checkConfigPath()) {
|
||||||
console.log(chalk.blue('助手路径不存在,正在创建...'));
|
console.log(chalk.blue('助手路径不存在,正在创建...'));
|
||||||
super.init(configDir);
|
super.init(configDir);
|
||||||
|
if (!this.initWorkspace) { return }
|
||||||
} else {
|
} else {
|
||||||
super.init(configDir);
|
super.init(configDir);
|
||||||
|
if (!this.initWorkspace) { return }
|
||||||
const assistantConfig = this;
|
const assistantConfig = this;
|
||||||
console.log(chalk.yellow('助手路径已存在'), chalk.green(assistantConfig.configDir));
|
console.log(chalk.yellow('助手路径已存在'), chalk.green(assistantConfig.configDir));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,9 @@
|
|||||||
"asst": "bin/assistant.js",
|
"asst": "bin/assistant.js",
|
||||||
"asst-server": "bin/assistant-server.js"
|
"asst-server": "bin/assistant-server.js"
|
||||||
},
|
},
|
||||||
|
"exports": {
|
||||||
|
".": "./dist/assistant-opencode.js"
|
||||||
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
"bin",
|
"bin",
|
||||||
@@ -45,7 +48,7 @@
|
|||||||
"@kevisual/app": "^0.0.2",
|
"@kevisual/app": "^0.0.2",
|
||||||
"@kevisual/context": "^0.0.4",
|
"@kevisual/context": "^0.0.4",
|
||||||
"@kevisual/use-config": "^1.0.28",
|
"@kevisual/use-config": "^1.0.28",
|
||||||
"@opencode-ai/sdk": "^1.1.25",
|
"@opencode-ai/sdk": "^1.1.26",
|
||||||
"@types/busboy": "^1.5.4",
|
"@types/busboy": "^1.5.4",
|
||||||
"busboy": "^1.6.0",
|
"busboy": "^1.6.0",
|
||||||
"eventemitter3": "^5.0.4",
|
"eventemitter3": "^5.0.4",
|
||||||
@@ -76,7 +79,7 @@
|
|||||||
"ignore": "^7.0.5",
|
"ignore": "^7.0.5",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
"pm2": "^6.0.14",
|
"pm2": "^6.0.14",
|
||||||
"tar": "^7.5.3",
|
"tar": "^7.5.4",
|
||||||
"zustand": "^5.0.10"
|
"zustand": "^5.0.10"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
54
pnpm-lock.yaml
generated
54
pnpm-lock.yaml
generated
@@ -21,8 +21,8 @@ importers:
|
|||||||
specifier: ^1.0.28
|
specifier: ^1.0.28
|
||||||
version: 1.0.28(dotenv@17.2.3)
|
version: 1.0.28(dotenv@17.2.3)
|
||||||
'@opencode-ai/sdk':
|
'@opencode-ai/sdk':
|
||||||
specifier: ^1.1.25
|
specifier: ^1.1.26
|
||||||
version: 1.1.25
|
version: 1.1.26
|
||||||
'@types/busboy':
|
'@types/busboy':
|
||||||
specifier: ^1.5.4
|
specifier: ^1.5.4
|
||||||
version: 1.5.4
|
version: 1.5.4
|
||||||
@@ -109,8 +109,8 @@ importers:
|
|||||||
specifier: ^6.0.14
|
specifier: ^6.0.14
|
||||||
version: 6.0.14(supports-color@10.2.2)
|
version: 6.0.14(supports-color@10.2.2)
|
||||||
tar:
|
tar:
|
||||||
specifier: ^7.5.3
|
specifier: ^7.5.4
|
||||||
version: 7.5.3
|
version: 7.5.4
|
||||||
zustand:
|
zustand:
|
||||||
specifier: ^5.0.10
|
specifier: ^5.0.10
|
||||||
version: 5.0.10(@types/react@19.2.8)(react@19.2.3)
|
version: 5.0.10(@types/react@19.2.8)(react@19.2.3)
|
||||||
@@ -130,8 +130,8 @@ importers:
|
|||||||
specifier: ^0.0.13
|
specifier: ^0.0.13
|
||||||
version: 0.0.13(dotenv@17.2.3)(supports-color@10.2.2)
|
version: 0.0.13(dotenv@17.2.3)(supports-color@10.2.2)
|
||||||
'@opencode-ai/sdk':
|
'@opencode-ai/sdk':
|
||||||
specifier: ^1.1.25
|
specifier: ^1.1.26
|
||||||
version: 1.1.25
|
version: 1.1.26
|
||||||
es-toolkit:
|
es-toolkit:
|
||||||
specifier: ^1.44.0
|
specifier: ^1.44.0
|
||||||
version: 1.44.0
|
version: 1.44.0
|
||||||
@@ -170,8 +170,8 @@ importers:
|
|||||||
specifier: 0.0.7
|
specifier: 0.0.7
|
||||||
version: 0.0.7(@kevisual/query@0.0.35)
|
version: 0.0.7(@kevisual/query@0.0.35)
|
||||||
'@kevisual/router':
|
'@kevisual/router':
|
||||||
specifier: ^0.0.56
|
specifier: ^0.0.57
|
||||||
version: 0.0.56
|
version: 0.0.57
|
||||||
'@kevisual/types':
|
'@kevisual/types':
|
||||||
specifier: ^0.0.11
|
specifier: ^0.0.11
|
||||||
version: 0.0.11
|
version: 0.0.11
|
||||||
@@ -179,8 +179,8 @@ importers:
|
|||||||
specifier: ^1.0.28
|
specifier: ^1.0.28
|
||||||
version: 1.0.28(dotenv@17.2.3)
|
version: 1.0.28(dotenv@17.2.3)
|
||||||
'@opencode-ai/plugin':
|
'@opencode-ai/plugin':
|
||||||
specifier: ^1.1.25
|
specifier: ^1.1.26
|
||||||
version: 1.1.25
|
version: 1.1.26
|
||||||
'@types/bun':
|
'@types/bun':
|
||||||
specifier: ^1.3.6
|
specifier: ^1.3.6
|
||||||
version: 1.3.6
|
version: 1.3.6
|
||||||
@@ -1344,8 +1344,8 @@ packages:
|
|||||||
'@kevisual/router@0.0.51':
|
'@kevisual/router@0.0.51':
|
||||||
resolution: {integrity: sha512-i9qYBeS/um78oC912oWJD3iElB+5NTKyTrz1Hzf4DckiUFnjLL81UPwjIh5I2l9+ul0IZ/Pxx+sFSF99fJkzKg==}
|
resolution: {integrity: sha512-i9qYBeS/um78oC912oWJD3iElB+5NTKyTrz1Hzf4DckiUFnjLL81UPwjIh5I2l9+ul0IZ/Pxx+sFSF99fJkzKg==}
|
||||||
|
|
||||||
'@kevisual/router@0.0.56':
|
'@kevisual/router@0.0.57':
|
||||||
resolution: {integrity: sha512-3k+wRUNT+kHqoA3r+6+lJRVHvbDMqNW75iWcYrzRFbf9lkEADYXzdIXHrOj/0Dk1EiTuLpK1i1e5dpWNpqlegA==}
|
resolution: {integrity: sha512-ZkbzsZD0FEXKBwLfjT4Evki8akHTPWUTZmnz7eGYnfloWDGudRtPJxqSLhzQ1AqfFuSDKJ8XR52Aa4CKdR05lA==}
|
||||||
|
|
||||||
'@kevisual/types@0.0.11':
|
'@kevisual/types@0.0.11':
|
||||||
resolution: {integrity: sha512-idNLDTEKVdNXZHFQq8PTN62nflh94kvGtx+v8YDcMxt0Zo+HWVZTFElm+dMQxAs/vn4wo8F2r3VwzWNX/vcqwQ==}
|
resolution: {integrity: sha512-idNLDTEKVdNXZHFQq8PTN62nflh94kvGtx+v8YDcMxt0Zo+HWVZTFElm+dMQxAs/vn4wo8F2r3VwzWNX/vcqwQ==}
|
||||||
@@ -1411,11 +1411,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
'@opencode-ai/plugin@1.1.25':
|
'@opencode-ai/plugin@1.1.26':
|
||||||
resolution: {integrity: sha512-oTUWS446H/j7z3pdzo3cOrB5N87XZ/RKdgPD8yHv/rLX92B4YQHjOqggVQ56Q+1VEnN0jxzhoqRylv/0ZEts/Q==}
|
resolution: {integrity: sha512-GnhLZuw9NHDJwY5msUZnFIWiLc0SnoBXWZNfnWF5+hu0fcVLgul8gqB2VK4kUv+KOQlzCVMILYikSSQkMYp7RQ==}
|
||||||
|
|
||||||
'@opencode-ai/sdk@1.1.25':
|
'@opencode-ai/sdk@1.1.26':
|
||||||
resolution: {integrity: sha512-mWUX489ArEF2ICg3iZsx2VQaGS3Z2j/dwAJDacao9t7dGDzjOIaacPw2weZ10zld7XmT9V9C0PM/A5lDZ52J+w==}
|
resolution: {integrity: sha512-5s+yxNJy7DpWwDq0L//F2sqKERz4640DmDLyrRlFXmckx5prnzFl3liEOOTySOL2WaxgVwjYw8OBiT15v2mAgA==}
|
||||||
|
|
||||||
'@oslojs/encoding@1.1.0':
|
'@oslojs/encoding@1.1.0':
|
||||||
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
|
resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==}
|
||||||
@@ -4662,8 +4662,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
|
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
tar@7.5.3:
|
tar@7.5.4:
|
||||||
resolution: {integrity: sha512-ENg5JUHUm2rDD7IvKNFGzyElLXNjachNLp6RaGf4+JOgxXHkqA+gq81ZAMCUmtMtqBsoU62lcp6S27g1LCYGGQ==}
|
resolution: {integrity: sha512-AN04xbWGrSTDmVwlI4/GTlIIwMFk/XEv7uL8aa57zuvRy6s4hdBed+lVq2fAZ89XDa7Us3ANXcE3Tvqvja1kTA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
throttle-debounce@5.0.2:
|
throttle-debounce@5.0.2:
|
||||||
@@ -6557,7 +6557,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@kevisual/router@0.0.56':
|
'@kevisual/router@0.0.57':
|
||||||
dependencies:
|
dependencies:
|
||||||
hono: 4.11.4
|
hono: 4.11.4
|
||||||
|
|
||||||
@@ -6672,12 +6672,12 @@ snapshots:
|
|||||||
'@nodelib/fs.scandir': 2.1.5
|
'@nodelib/fs.scandir': 2.1.5
|
||||||
fastq: 1.17.1
|
fastq: 1.17.1
|
||||||
|
|
||||||
'@opencode-ai/plugin@1.1.25':
|
'@opencode-ai/plugin@1.1.26':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@opencode-ai/sdk': 1.1.25
|
'@opencode-ai/sdk': 1.1.26
|
||||||
zod: 4.1.8
|
zod: 4.1.8
|
||||||
|
|
||||||
'@opencode-ai/sdk@1.1.25': {}
|
'@opencode-ai/sdk@1.1.26': {}
|
||||||
|
|
||||||
'@oslojs/encoding@1.1.0': {}
|
'@oslojs/encoding@1.1.0': {}
|
||||||
|
|
||||||
@@ -6810,7 +6810,7 @@ snapshots:
|
|||||||
async: 2.6.4
|
async: 2.6.4
|
||||||
debug: 4.3.7(supports-color@10.2.2)
|
debug: 4.3.7(supports-color@10.2.2)
|
||||||
eventemitter2: 6.4.9
|
eventemitter2: 6.4.9
|
||||||
extrareqp2: 1.0.0(debug@4.3.7)
|
extrareqp2: 1.0.0(debug@4.3.7(supports-color@10.2.2))
|
||||||
ws: 7.5.10
|
ws: 7.5.10
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
@@ -8785,9 +8785,9 @@ snapshots:
|
|||||||
|
|
||||||
extend@3.0.2: {}
|
extend@3.0.2: {}
|
||||||
|
|
||||||
extrareqp2@1.0.0(debug@4.3.7):
|
extrareqp2@1.0.0(debug@4.3.7(supports-color@10.2.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.9(debug@4.3.7)
|
follow-redirects: 1.15.9(debug@4.3.7(supports-color@10.2.2))
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- debug
|
- debug
|
||||||
|
|
||||||
@@ -8841,7 +8841,7 @@ snapshots:
|
|||||||
|
|
||||||
flattie@1.1.1: {}
|
flattie@1.1.1: {}
|
||||||
|
|
||||||
follow-redirects@1.15.9(debug@4.3.7):
|
follow-redirects@1.15.9(debug@4.3.7(supports-color@10.2.2)):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
debug: 4.3.7(supports-color@10.2.2)
|
debug: 4.3.7(supports-color@10.2.2)
|
||||||
|
|
||||||
@@ -10827,7 +10827,7 @@ snapshots:
|
|||||||
|
|
||||||
tapable@2.3.0: {}
|
tapable@2.3.0: {}
|
||||||
|
|
||||||
tar@7.5.3:
|
tar@7.5.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@isaacs/fs-minipass': 4.0.1
|
'@isaacs/fs-minipass': 4.0.1
|
||||||
chownr: 3.0.0
|
chownr: 3.0.0
|
||||||
|
|||||||
Reference in New Issue
Block a user