This commit is contained in:
2025-12-03 16:41:01 +08:00
parent 3d50fde0eb
commit d49ecc83fa
27 changed files with 7542 additions and 43 deletions

3
.gitignore vendored
View File

@@ -6,3 +6,6 @@ node_modules
dist
public/root
.env
!.env*example

6
.gitmodules vendored
View File

@@ -1,6 +0,0 @@
[submodule "frontend"]
path = frontend
url = git@git.xiongxiao.me:template/astro-simple-template.git
[submodule "backend"]
path = backend
url = git@git.xiongxiao.me:template/router-template.git

3
agents/app.ts Normal file
View File

@@ -0,0 +1,3 @@
import { QueryRouterServer } from "@kevisual/router";
import { useContextKey } from "@kevisual/context";

View File

@@ -1,21 +0,0 @@
const frontend = 'git@git.xiongxiao.me:template/astro-simple-template.git';
const backend = 'git@git.xiongxiao.me:template/router-template.git';
// submodule add frontend to frontend and backend to backend
export const cliInitSubmodules = [
`git submodule add ${frontend} frontend`,
`git submodule add ${backend} backend`,
];
cliInitSubmodules.forEach((cmd) => {
console.log(`${cmd}\n`);
});
// init submodules
export const cliUpdateSubmodules = `git submodule update --init --recursive`;
console.log(`${cliUpdateSubmodules}\n`);
// 清理submodule保留模板的内容
export const cliRemoveGitModule = `rm .gitmodules -rf && rm -rf .git/modules`;
console.log(`${cliRemoveGitModule}\n`);

5
agents/modules/query.ts Normal file
View File

@@ -0,0 +1,5 @@
import { Query } from '@kevisual/query'
export const query = new Query({
url: 'https://kevisual.cn/api/router',
})

View File

@@ -0,0 +1,121 @@
import { CreateColumnData } from "@kevisual/noco"
export const columns: CreateColumnData[] = [
{
title: 'Id',
// @ts-ignore
uidt: "ID",
pk: true,
dt: "int4",
dtx: "integer"
},
{
title: '标题',
uidt: 'SingleLineText',
description: '简单的标题',
},
{
title: '标签',
uidt: 'MultiSelect',
description: '标签分类,对每一条数据的标签定义,快速分类和筛选',
},
{
title: '总结',
uidt: 'LongText',
description: '概览性总结',
},
{
title: '描述',
uidt: 'LongText',
description: '长文本描述',
},
{
title: '数据',
uidt: 'JSON',
description: '扩列数据,存储更多的自定义信息',
},
{
title: '链接',
uidt: 'URL',
description: '快速跳转链接,默认为空,比如我这里是一个人生日程的链接,在外部打开',
},
{
title: '类型',
uidt: 'SingleSelect',
description: '任务类型:备忘和其他,如果是备忘,只做记录,如果是其他的,属于任务管理,到达对应的时间,进行任务提醒,归档是自己不再查询。',
cdf: '备忘',
colOptions: {
// 每日,每周,每月,每年,一次性,备忘,归档,智能
options: [
{
title: '每日',
},
{
title: '每周',
},
{
title: '每月',
},
{
title: '每年',
},
{
title: '每年农历',
},
{
title: '备忘',
},
{
title: '归档',
},
{
title: '智能',
},
]
},
},
{
title: '启动时间',
description: '任务启动的时间点, 下次启动的时间点。到达当天,显示当天的任务,然后如果执行了,如果是循环周期任务,更新下次启动时间。',
uidt: 'DateTime',
},
{
title: '任务',
uidt: 'MultiSelect',
description: '任务状态,如果是任务,需要判断运行还是非运行中',
cdf: '非任务',
colOptions: {
// 非任务, 运行中,已停止,个人计划,已完成
options: [
{
title: '非任务',
},
{
title: '运行中',
},
{
title: '已停止',
},
{
title: '个人计划',
},
{
title: '已完成',
},
{
title: 'AI自动化'
}
]
},
},
{
title: '任务结果',
description: '任务结果描述, 执行后回馈',
uidt: 'LongText'
},
{
title: '提示词',
uidt: 'LongText',
description: '和AI交互时候简单的实时提示词',
}]

View File

@@ -0,0 +1,91 @@
import { NocoApi } from "@kevisual/noco";
import { columns } from "../common/base-table.ts";
type ReponseData<T = {}> = {
code: number,
message?: string,
data?: T
}
export type CoreOptions<T = {}> = {
nocoApi: NocoApi,
baseId?: string
} & T
type CoreItem = {
Id: number,
标题: string,
总结?: string,
启动时间?: string,
标签?: string,
任务?: string, // 运行中,已完成,未开始
链接?: string,
数据?: string,
类型?: string,
提示词?: string,
任务结果?: string,
}
export class Core {
nocoApi: NocoApi;
baseId?: string;
key = 'core';
title = '默认表';
description = '默认表描述';
tableId?: string;
constructor(opts: {
nocoApi: NocoApi,
baseId?: string,
tableId?: string
}) {
this.nocoApi = opts.nocoApi;
this.baseId = opts.baseId;
this.tableId = opts.tableId;
}
async createTable(opts?: { columns?: any[], title?: string, description?: string, baseId?: string }): Promise<ReponseData<{ id: string, title: string }>> {
const baseId = opts?.baseId ?? this.baseId!;
const title = opts?.title ?? this.title;
const description = opts?.description ?? this.description;
const _columns = opts?.columns ?? columns;
let tableId = '';
const res = await this.nocoApi.meta.tables.createTable(baseId, {
title,
description,
columns: _columns,
})
let code = 200;
if (res.code !== 200) {
const res = await this.nocoApi.meta.tables.list(baseId);
const list = res.data?.list || [];
const existTable = list.find(t => t.title === title);
if (existTable) {
tableId = existTable.id;
} else {
return {
code: res.code,
message: `创建表失败,且未找到同名表`,
}
}
} else {
tableId = res?.data?.id;
}
this.tableId = tableId;
return {
code,
data: {
id: tableId,
title,
}
};
}
getItem(id: number): Promise<ReponseData<CoreItem>> {
return this.nocoApi.record.read(id);
}
getList(params: any): Promise<ReponseData<{ list: CoreItem[] }>> {
return this.nocoApi.record.list({
...params,
});
}
updateItem(data: Partial<CoreItem>) {
return this.nocoApi.record.update(data);
}
}

View File

@@ -0,0 +1,2 @@
export * from "./core.ts";
export * from "./base-table.ts";

View File

@@ -0,0 +1,8 @@
import { NocoApi } from "@kevisual/noco";
import { Core } from "../common/index.ts";
export class Control extends Core {
key = 'control';
title = '控制中枢'
description = '管理和控制系统的运行'
}

11
agents/noco/index.ts Normal file
View File

@@ -0,0 +1,11 @@
import { NocoApi } from "@kevisual/noco";
import { columns } from "./common/base-table.ts";
import { Life } from "../noco/life/index.ts";
import { Control } from "../noco/control/index.ts";
export {
NocoApi,
columns,
Control,
Life
}

View File

@@ -0,0 +1,8 @@
import { NocoApi } from "@kevisual/noco";
import { Core } from "../common/index.ts";
export class Life extends Core {
key = 'life';
title = '人生备忘录'
description = '记录和管理你的人生大事小事'
}

View File

@@ -0,0 +1,108 @@
import { Query } from '@kevisual/query';
import type { Result } from '@kevisual/query/query';
type QueryConfigOpts = {
query?: Query;
};
export type Config<T = any> = {
id?: string;
title?: string;
key?: string;
description?: string;
data?: T;
createdAt?: string;
updatedAt?: string;
};
export type UploadConfig = {
key?: string;
version?: string;
};
type PostOpts = {
token?: string;
};
export const defaultConfigKeys = ['upload.json', 'workspace.json', 'ai.json', 'user.json', 'life.json'] as const;
type DefaultConfigKey = (typeof defaultConfigKeys)[number];
export class QueryConfig {
query: Query;
constructor(opts?: QueryConfigOpts) {
this.query = opts?.query || new Query();
}
async post<T = Config>(data: any) {
return this.query.post<T>({ path: 'config', ...data });
}
async getConfig({ id, key }: { id?: string; key?: string }, opts?: PostOpts) {
return this.post({
key: 'get',
data: {
id,
key,
},
...opts,
});
}
async updateConfig(data: Config, opts?: PostOpts) {
return this.post({
key: 'update',
data,
...opts,
});
}
async deleteConfig(id: string, opts?: PostOpts) {
return this.post({
key: 'delete',
data: { id },
});
}
async listConfig(opts?: PostOpts) {
return this.post<{ list: Config[] }>({
key: 'list',
...opts,
});
}
/**
* 获取上传配置
* @returns
*/
async getUploadConfig(opts?: PostOpts) {
return this.post<Result<Config<UploadConfig>>>({
key: 'getUploadConfig',
...opts,
});
}
/**
* 更新上传配置
* @param data
* @returns
*/
async updateUploadConfig(data: Config, opts?: PostOpts) {
return this.post<Result<Config<UploadConfig>>>({
key: 'updateUploadConfig',
data,
...opts,
});
}
/**
* 检测配置是否存在
* @param id
* @returns
*/
async detectConfig(opts?: PostOpts) {
return this.post<{ updateList: Config[] }>({
key: 'detect',
...opts,
});
}
/**
* 获取配置, 获取默认的配置项
* @param key
* @returns
*/
async getConfigByKey(key: DefaultConfigKey, opts?: PostOpts) {
return this.post<Result<Config>>({
key: 'defaultConfig',
configKey: key,
...opts,
});
}
}

View File

@@ -27,23 +27,26 @@
"keywords": [],
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
"license": "MIT",
"packageManager": "pnpm@10.22.0",
"packageManager": "pnpm@10.24.0",
"type": "module",
"dependencies": {
"@kevisual/ai": "^0.0.11",
"@kevisual/local-proxy": "^0.0.8",
"@kevisual/noco-auto": "../",
"@kevisual/query": "^0.0.29",
"@kevisual/router": "0.0.33",
"@kevisual/use-config": "^1.0.19",
"@kevisual/use-config": "^1.0.21",
"archiver": "^7.0.1",
"dayjs": "^1.11.19",
"es-toolkit": "^1.42.0",
"lunar": "^2.0.0",
"nanoid": "^5.1.6"
},
"devDependencies": {
"@kevisual/ai": "^0.0.12",
"@kevisual/types": "^0.0.10",
"@types/archiver": "^7.0.0",
"@types/bun": "^1.3.3",
"@types/node": "^24.10.1"
"@types/node": "^24.10.1",
"dotenv": "^17.2.3"
}
}

View File

@@ -1,4 +1,4 @@
import { app } from './app.ts'
import './router/index.ts';
import './routes/index.ts';
export { app }

View File

@@ -0,0 +1,120 @@
/**
* 配置查询
* @updatedAt 2025-12-03 11:05:00
*/
import { Query } from '@kevisual/query';
import type { Result } from '@kevisual/query/query';
type QueryConfigOpts = {
query?: Query;
};
export type Config<T = any> = {
id?: string;
title?: string;
key?: string;
description?: string;
data?: T;
createdAt?: string;
updatedAt?: string;
};
export type UploadConfig = {
key?: string;
version?: string;
};
type PostOpts = {
token?: string;
payload?: Record<string, any>;
};
export const defaultConfigKeys = ['upload.json', 'workspace.json', 'ai.json', 'user.json', 'life.json'] as const;
type DefaultConfigKey = (typeof defaultConfigKeys)[number];
export class QueryConfig {
query: Query;
constructor(opts?: QueryConfigOpts) {
this.query = opts?.query || new Query();
}
async post<T = Config>(data: any) {
return this.query.post<T>({ path: 'config', ...data });
}
async getConfig({ id, key }: { id?: string; key?: string }, opts?: PostOpts) {
return this.post({
key: 'get',
data: {
id,
key,
},
...opts,
});
}
async updateConfig(data: Config, opts?: PostOpts) {
return this.post({
key: 'update',
data,
...opts,
});
}
async deleteConfig(data: { id?: string, key?: string }, opts?: PostOpts) {
return this.post({
key: 'delete',
data,
});
}
async listConfig(opts?: PostOpts) {
return this.post<{ list: Config[] }>({
key: 'list',
...opts,
});
}
/**
* 获取上传配置
* @returns
*/
async getUploadConfig(opts?: PostOpts) {
return this.post<Result<Config<UploadConfig>>>({
key: 'getUploadConfig',
...opts,
});
}
/**
* 更新上传配置
* @param data
* @returns
*/
async updateUploadConfig(data: Config, opts?: PostOpts) {
return this.post<Result<Config<UploadConfig>>>({
key: 'updateUploadConfig',
data,
...opts,
});
}
/**
* 检测配置是否存在
* @param id
* @returns
*/
async detectConfig(opts?: PostOpts) {
return this.post<{ updateList: Config[] }>({
key: 'detect',
...opts,
});
}
/**
* 获取配置, 获取默认的配置项
* @param key
* @returns
*/
async getConfigByKey(key: DefaultConfigKey, opts?: PostOpts) {
return this.post<Result<Config>>({
key: 'defaultConfig',
configKey: key,
...opts,
});
}
async getByKey<T = any>(key: string, opts?: PostOpts) {
return this.post<Result<Config<T>>>({
key: 'get',
...opts,
data: { key },
});
}
}

View File

@@ -1,6 +1,10 @@
// base
import { app } from '../app.ts';
import './noco/index.ts';
import './noco/config.ts';
// 添加认证中间件路由
const hasAuth = app.router.routes.some(r => r.id === 'auth');
if (!hasAuth) {
console.log('添加认证中间件路由');
@@ -11,5 +15,7 @@ if (!hasAuth) {
id: 'auth'
}).define(async (ctx) => {
// 这里可以添加实际的认证逻辑
ctx.query.token = process.env.TOKEN || ' ';
console.log('本地测试认证通过,设置 token');
}).addTo(app);
}

View File

@@ -0,0 +1,54 @@
import { app } from '@/app.ts'
import { NocoLifeService } from './services/life.ts';
import { useContextKey } from '@kevisual/context';
import { BailianProvider } from '@kevisual/ai';
import { createLunarDate, LunarDate, toGregorian } from 'lunar';
import dayjs from 'dayjs';
app.route({
path: 'noco-life',
key: 'config-update',
description: `多维表格配置更新内容, 参数是{data: {baseURL:string; token:string; baseId:string; tableId?:string}}
`,
middleware: ['auth']
}).define(async (ctx) => {
const data = ctx.query.data || {};
if (!data?.baseURL || !data?.token || !data?.baseId) {
ctx.throw(400, '缺少参数 baseURL, token, baseId, tableId');
}
const token = ctx.query.token || '';
const nocoLifeService = new NocoLifeService({ token });
const config = await nocoLifeService.getLifeConfig()
if (data.baseURL) {
config.baseURL = data.baseURL;
}
if (data.token) {
config.token = data.token;
}
if (data.baseId) {
config.baseId = data.baseId;
}
if (data.tableId) {
config.tableId = data.tableId;
}
// 保存配置
const res = await nocoLifeService.updateLifeConfig(config);
if (res.code !== 200) {
ctx.throw(500, '保存配置失败');
}
ctx.body = '配置更新成功';
}).addTo(app);
app.route({
path: 'noco-life',
key: 'config-get',
description: `多维表格配置获取
`,
middleware: ['auth']
}).define(async (ctx) => {
const token = ctx.query.token || '';
const nocoLifeService = new NocoLifeService({ token });
const config = await nocoLifeService.getLifeConfig()
ctx.body = config;
}).addTo(app);

View File

@@ -0,0 +1,155 @@
import { app } from '@/app.ts'
import { NocoLifeService } from './services/life.ts';
import { useContextKey } from '@kevisual/context';
import { BailianProvider } from '@kevisual/ai';
import { createLunarDate, LunarDate, toGregorian } from 'lunar';
import dayjs from 'dayjs';
app.route({
path: 'noco-life',
key: 'chat',
description: `多维表格聊天接口, 对自己的多维表格的数据进行操作,参数是 question, `,
middleware: ['auth']
}).define(async (ctx) => {
const question = ctx.query.question || '';
if (!question) {
ctx.throw(400, '缺少参数 question');
}
const token = ctx.query.token || '';
const nocoLifeService = new NocoLifeService({ token });
await nocoLifeService.initConfig()
const routes = ctx.queryRouter.getList().filter(r => r.path.startsWith('noco-life') && r.key !== 'chat');
const v = `${routes.map((r, index) => `${index + 1}工具名称: ${r.id}\n描述: ${r.description}\n`).join('\n')}\n\n当用户询问时如果拥有工具请返回 JSON 数据不存在工具则返回分析判断数据JSON数据类型是{id,payload}外面的id是工具的id。如果工具有参数在 payload 当中默认不需要参数如果工具内部需要id在payload当中。`
const ai = useContextKey('ai');
const answer = await ai.chat([
{ role: 'system', content: `你是一个多维表格助理,你的任务是帮助用户操作和查询多维表格的数据。你可以使用以下工具来完成任务:\n\n${v}` },
{ role: 'user', content: question }
])
console.log('answer', ai.responseText);
let msg: any;
try {
msg = JSON.parse(ai.responseText || '{}');
} catch (e) {
ctx.throw(500, 'AI 返回结果解析失败');
}
console.log('msg', msg);
const res = await ctx.call(msg);
if (res.code !== 200) {
ctx.throw(500, '调用工具失败');
}
ctx.body = res.body;
}).addTo(app);
app.route({
path: 'noco-life',
key: 'today',
description: `获取今天需要做的事情列表`,
middleware: ['auth']
}).define(async (ctx) => {
const token = ctx.query.token || '';
const nocoLifeService = new NocoLifeService({ token });
await nocoLifeService.initConfig()
const life = nocoLifeService.life;
const res = await life.getList({
fields: ['Id', '标题', '总结', '启动时间', '标签', '任务'],
where: "(任务,eq,运行中)~and(启动时间,lt,today)",
sort: '启动时间',
});
if (res.code === 200) {
const list = res.data.list || []
ctx.body = list;
if (list.length === 0) {
ctx.message = '今天没有需要做的事情,休息一下吧';
}
return;
}
ctx.throw(500, '获取记录列表失败');
}).addTo(app);
app.route({
path: 'noco-life',
key: 'done',
description: `完成某件事情然后判断下一次运行时间。参数是id数据类型是number。`,
middleware: ['auth']
}).define(async (ctx) => {
const id = ctx.query.id;
if (!id) {
ctx.throw(400, '缺少参数 id');
}
const token = ctx.query.token || '';
const nocoLifeService = new NocoLifeService({ token });
await nocoLifeService.initConfig()
const life = nocoLifeService.life;
// 获取记录详情
const recordRes = await life.getItem(id);
if (recordRes.code !== 200) {
ctx.throw(500, '获取记录详情失败');
}
const record = recordRes.data;
// 检查启动时间是否大于今天
const startTime = record['启动时间'];
const today = dayjs().startOf('day');
const startDate = dayjs(startTime).startOf('day');
if (startDate.isAfter(today)) {
ctx.throw(400, '还没到今天呢,到时候再做吧');
}
// 计算下一次运行时间
// 1. 知道当前时间
// 2. 知道任务类型,如果是每日,则加一天;如果是每周,则加七天;如果是每月,则加一个月,如果是每年农历,需要转为新的,如果是其他,需要智能判断
// 3. 更新记录
const strTime = (time: string) => {
return dayjs(time).format('YYYY-MM-DD HH:mm:ss');
}
const currentTime = strTime(new Date().toISOString());
const isLuar = record['类型']?.includes?.('农历');
let summay = record['总结'] || '无';
if (summay.length > 200) {
summay = summay.substring(0, 200) + '...';
}
const prompt = record['提示词'] || '';
const type = record['类型'] || '';
const content = `上一次执行的时间是${strTime(startTime)},当前时间是${currentTime}请帮我计算下一次的运行时间如果时间不存在默认在8点启动。
${prompt ? `这是我给你的提示词,帮你更好地理解我的需求:${prompt}` : ''}
相关资料是
任务:${record['标题']}
总结:${summay}
类型: ${type}
`
const ai = useContextKey('ai');
await ai.chat([
{ role: 'system', content: `你是一个时间计算专家擅长根据任务类型和时间计算下一次运行时间。只返回我对应的日期的结果格式是YYYY-MM-DD HH:mm:ss。` },
{ role: 'user', content }
])
let nextTime = ai.responseText?.trim();
try {
// 判断返回的时间是否可以格式化
if (nextTime && dayjs(nextTime).isValid()) {
const time = dayjs(nextTime);
if (isLuar) {
const festival = createLunarDate({ year: time.year(), month: time.month() + 1, day: time.date() });
const { date } = toGregorian(festival);
nextTime = dayjs(date).toISOString();
} else {
nextTime = time.toISOString();
}
} else {
ctx.throw(500, 'AI 返回的时间格式无效,无法格式化');
}
} catch (e) {
ctx.throw(500, 'AI 返回结果解析失败');
}
const update = await life.updateItem({ Id: id, '启动时间': nextTime });
if (update.code !== 200) {
ctx.throw(500, '更新记录失败');
}
ctx.body = {
id,
nextTime,
showCNTime: dayjs(nextTime).format('YYYY-MM-DD HH:mm:ss')
};
}).addTo(app);

View File

@@ -0,0 +1,91 @@
import { Life, NocoApi } from '@kevisual/noco-auto';
import { initConfig } from "@kevisual/use-config";
import { QueryConfig } from "@/query/query-config/query-config.ts";
import { Query } from "@kevisual/query/query";
import { CustomError } from '@kevisual/router'
type NocoLifeServiceOpts = {
token: string;
}
export type NocoLifeConfig = {
baseURL?: string;
baseId: string;
token: string;
tableId?: string;
}
export class NocoLifeService {
token: string;
nocoApi: NocoApi;
life: Life;
queryConfig: QueryConfig;
constructor(opts: NocoLifeServiceOpts) {
this.token = opts.token;
this.initEnv();
}
initEnv() {
const config = new QueryConfig({
query: new Query({ url: "https://kevisual.xiongxiao.me/api/router" })
});
this.queryConfig = config;
}
async getLifeConfig(): Promise<NocoLifeConfig> {
const res = await this.queryConfig.getByKey('life.json', { token: this.token });
if (res.code !== 200) {
return {} as NocoLifeConfig;
}
return res.data?.data as NocoLifeConfig;
}
async updateLifeConfig(data: NocoLifeConfig) {
const res = await this.queryConfig.updateConfig({
key: 'life.json',
data,
}, { token: this.token });
return res;
}
/**
* 需要从服务端获取自己保存的配置,包括 nocodb 地址apiKey 等
*/
async initConfig() {
const res = await this.queryConfig.getByKey('life.json', { token: this.token });
if (res.code !== 200) {
console.error('获取配置失败', res);
throw new CustomError(res.code, `获取配置失败: ${res.message}`);
}
const lifeConfig: NocoLifeConfig = res.data?.data as NocoLifeConfig;
if (!lifeConfig || !lifeConfig.token || !lifeConfig.baseId || !lifeConfig.baseURL) {
throw new CustomError(400, `配置不完整,请先设置正确的配置, baseURL, baseId, token 都是必须的.`);
}
const nocoApi = new NocoApi({
baseURL: lifeConfig.baseURL || '',
token: lifeConfig.token || '',
});
this.nocoApi = nocoApi;
const life = new Life({ nocoApi, baseId: lifeConfig.baseId });
const tableId = lifeConfig.tableId || '';
if (!tableId) {
const newTable = await life.createTable()
if (newTable.code !== 200) {
throw new CustomError(500, `创建默认表失败: ${newTable.message}`);
}
lifeConfig.tableId = newTable.data?.id;
// 保存 tableId 到配置中
const res = await this.queryConfig.updateConfig({
key: 'life.json',
data: lifeConfig,
}, { token: this.token });
if (res.code === 200) {
console.log('默认表创建成功,配置已更新');
}
}
life.tableId = lifeConfig.tableId || '';
nocoApi.record.table = life.tableId;
this.life = life;
return lifeConfig;
}
initNocoApi() {
}
}

15
backend/test/chat.ts Normal file
View File

@@ -0,0 +1,15 @@
import { app, sleep } from './common';
const res = await app.call({
path: 'noco-life',
key: "chat",
payload: {
// question: '今天我需要做什么事情?',
// question: '任务5 完成了,帮我判断下一次运行时间应该是什么时候?',
// question: '任务59 完成了',
question: '我的多维表格配置'
}
})
console.log('res', res.code, res.body, res.message);

18
backend/test/common.ts Normal file
View File

@@ -0,0 +1,18 @@
import { app } from '../src/index.ts';
export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
// await sleep(1000); // 等待服务启动
import { useContextKey } from '@kevisual/context';
import { BailianProvider } from '@kevisual/ai';
import dotenv from 'dotenv';
dotenv.config();
console.log('process.env.BAILIAN_API_KEY', process.env.BAILIAN_API_KEY);
const ai = useContextKey('ai', () => {
return new BailianProvider({
apiKey: process.env.BAILIAN_API_KEY || '',
model: 'qwen-turbo'
});
});
export {
app,
ai,
}

13
backend/test/done.ts Normal file
View File

@@ -0,0 +1,13 @@
import { app, sleep } from './common';
const res = await app.call({
path: 'noco-life',
key: "done",
payload: {
id: 59, // 洗漱
// id:4, // 爸爸
}
})
console.log('res', res.code, res.message, res.body);

9
backend/test/today.ts Normal file
View File

@@ -0,0 +1,9 @@
import { app, sleep } from './common';
const res = await app.call({
path: 'noco-life',
key: "today"
})
console.log('res', res.body);

View File

@@ -1,3 +0,0 @@
# !/bin/sh
## 功能:彻底删除 git 子模块相关信息
git submodule deinit -f . && rm -rf .git/modules && git rm -f $(git config --file .gitmodules --get-regexp path | awk '{print $2}') && rm -f .gitmodules

1
mod.ts Normal file
View File

@@ -0,0 +1 @@
export * from './agents/noco/index.ts';

View File

@@ -2,21 +2,29 @@
"name": "@kevisual/noco-auto",
"version": "0.0.1",
"description": "",
"main": "index.js",
"main": "mod.ts",
"scripts": {
"build": "pnpm build:web && pnpm build:backend",
"build:backend": "cd backend && bun run src/main.ts",
"build:web": "cd frontend && pnpm build",
"serve": "cd backend && bun run src/main.ts",
"init": "git submodule update --init --recursive",
"clean:module": "sh demodules.sh"
"serve": "cd backend && bun run src/main.ts"
},
"keywords": [],
"files": [
"agents/noco"
],
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
"license": "MIT",
"packageManager": "pnpm@10.19.0",
"packageManager": "pnpm@10.24.0",
"type": "module",
"dependencies": {
"@kevisual/router": "^0.0.33"
"@kevisual/context": "^0.0.4",
"@kevisual/noco": "^0.0.8",
"@kevisual/query": "^0.0.29",
"@kevisual/router": "^0.0.33",
"@kevisual/use-config": "^1.0.21"
},
"exports": {
".": "./mod.ts"
}
}

6676
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff