generated from template/apps-template
temp
This commit is contained in:
21
agents/noco/callback/index.ts
Normal file
21
agents/noco/callback/index.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
export type BaseNocoItem<T = {}> = {
|
||||||
|
Id: number,
|
||||||
|
CreatedAt: string
|
||||||
|
UpdatedAt: string
|
||||||
|
} & T;
|
||||||
|
export type NocoWehookPayload<NocoItem = {}> = {
|
||||||
|
/** 请求id */
|
||||||
|
id: string;
|
||||||
|
type: "records.after.trigger";
|
||||||
|
/**
|
||||||
|
* 多维表base id
|
||||||
|
**/
|
||||||
|
base_id: string;
|
||||||
|
version: "v3",
|
||||||
|
data: {
|
||||||
|
table_id: string;
|
||||||
|
table_name: string;
|
||||||
|
rows: BaseNocoItem<NocoItem>[];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { CreateColumnData } from "@kevisual/noco"
|
|||||||
export const columns: CreateColumnData[] = [
|
export const columns: CreateColumnData[] = [
|
||||||
{
|
{
|
||||||
title: 'Id',
|
title: 'Id',
|
||||||
|
// @ts-ignore
|
||||||
uidt: "ID",
|
uidt: "ID",
|
||||||
pk: true,
|
pk: true,
|
||||||
pv: true,
|
pv: true,
|
||||||
@@ -10,7 +11,7 @@ export const columns: CreateColumnData[] = [
|
|||||||
|
|
||||||
title: '标题',
|
title: '标题',
|
||||||
uidt: 'SingleLineText',
|
uidt: 'SingleLineText',
|
||||||
description: '简单的标题',
|
description: '简单的标题, 最简单的介绍',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '标签',
|
title: '标签',
|
||||||
@@ -116,3 +117,21 @@ export const columns: CreateColumnData[] = [
|
|||||||
uidt: 'LongText',
|
uidt: 'LongText',
|
||||||
description: '和AI交互时候简单的实时提示词',
|
description: '和AI交互时候简单的实时提示词',
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
|
||||||
|
export type ColumnItem<T = {}> = {
|
||||||
|
'Id': number,
|
||||||
|
'CreatedAt': string,
|
||||||
|
'UpdatedAt': string,
|
||||||
|
"标题": string,
|
||||||
|
"标签"?: string,
|
||||||
|
"总结"?: string,
|
||||||
|
"描述"?: string,
|
||||||
|
"数据"?: string,
|
||||||
|
"链接"?: string,
|
||||||
|
"类型"?: string,
|
||||||
|
"启动时间"?: string,
|
||||||
|
"任务"?: "非任务" | "运行中" | "已停止" | "个人计划" | "已完成" | "AI自动化",
|
||||||
|
"任务结果"?: string,
|
||||||
|
"提示词"?: string,
|
||||||
|
} & T;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { NocoApi } from "@kevisual/noco";
|
import { NocoApi } from "@kevisual/noco";
|
||||||
import { columns } from "../common/base-table.ts";
|
import { columns } from "../common/base-table.ts";
|
||||||
|
import { ColumnItem } from "./base-table.ts";
|
||||||
type ReponseData<T = {}> = {
|
type ReponseData<T = {}> = {
|
||||||
code: number,
|
code: number,
|
||||||
message?: string,
|
message?: string,
|
||||||
@@ -11,19 +11,7 @@ export type CoreOptions<T = {}> = {
|
|||||||
baseId?: string
|
baseId?: string
|
||||||
} & T
|
} & T
|
||||||
|
|
||||||
type CoreItem = {
|
type CoreItem = ColumnItem
|
||||||
Id: number,
|
|
||||||
标题: string,
|
|
||||||
总结?: string,
|
|
||||||
启动时间?: string,
|
|
||||||
标签?: string,
|
|
||||||
任务?: string, // 运行中,已完成,未开始
|
|
||||||
链接?: string,
|
|
||||||
数据?: string,
|
|
||||||
类型?: string,
|
|
||||||
提示词?: string,
|
|
||||||
任务结果?: string,
|
|
||||||
}
|
|
||||||
export class Core {
|
export class Core {
|
||||||
nocoApi: NocoApi;
|
nocoApi: NocoApi;
|
||||||
baseId?: string;
|
baseId?: string;
|
||||||
@@ -68,7 +56,7 @@ export class Core {
|
|||||||
tableId = res?.data?.id;
|
tableId = res?.data?.id;
|
||||||
}
|
}
|
||||||
this.tableId = tableId;
|
this.tableId = tableId;
|
||||||
if(this.nocoApi.record) {
|
if (this.nocoApi.record) {
|
||||||
this.nocoApi.record.table = tableId;
|
this.nocoApi.record.table = tableId;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { NocoApi } from "@kevisual/noco";
|
|
||||||
import { Core } from "../common/index.ts";
|
import { Core } from "../common/index.ts";
|
||||||
|
|
||||||
export class Control extends Core {
|
export class Control extends Core {
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
import { NocoApi } from "@kevisual/noco";
|
import { NocoApi } from "@kevisual/noco";
|
||||||
import { columns } from "./common/base-table.ts";
|
import { columns, ColumnItem } from "./common/base-table.ts";
|
||||||
import { Life } from "../noco/life/index.ts";
|
import { Life } from "../noco/life/index.ts";
|
||||||
import { Control } from "../noco/control/index.ts";
|
import { Control } from "../noco/control/index.ts";
|
||||||
|
import { Core } from "./common/core.ts";
|
||||||
|
|
||||||
|
import { NocoWehookPayload } from "./callback/index.ts";
|
||||||
export {
|
export {
|
||||||
NocoApi,
|
NocoApi,
|
||||||
columns,
|
columns,
|
||||||
|
ColumnItem,
|
||||||
Control,
|
Control,
|
||||||
Life
|
Life,
|
||||||
|
Core,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type {
|
||||||
|
NocoWehookPayload
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import { NocoApi } from "@kevisual/noco";
|
|
||||||
import { Core } from "../common/index.ts";
|
import { Core } from "../common/index.ts";
|
||||||
|
|
||||||
export class Life extends Core {
|
export class Life extends Core {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun --watch src/main.ts ",
|
"dev": "bun --watch src/main.ts ",
|
||||||
"build": "pnpm run clean && bun run bun.config.mjs",
|
"build": "pnpm run clean && bun run bun.config.mjs",
|
||||||
|
"create": "bun run test/create-json.ts",
|
||||||
"clean": "rm -rf dist && rimraf pack-dist",
|
"clean": "rm -rf dist && rimraf pack-dist",
|
||||||
"prepub": "pnpm build",
|
"prepub": "pnpm build",
|
||||||
"pub": "envision pack -p -u"
|
"pub": "envision pack -p -u"
|
||||||
@@ -29,8 +30,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kevisual/local-proxy": "^0.0.8",
|
"@kevisual/local-proxy": "^0.0.8",
|
||||||
"@kevisual/noco-auto": "../",
|
"@kevisual/noco-auto": "../",
|
||||||
"@kevisual/query": "^0.0.29",
|
"@kevisual/query": "^0.0.31",
|
||||||
"@kevisual/router": "0.0.33",
|
"@kevisual/router": "0.0.36",
|
||||||
"@kevisual/use-config": "^1.0.21",
|
"@kevisual/use-config": "^1.0.21",
|
||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
@@ -39,11 +40,12 @@
|
|||||||
"nanoid": "^5.1.6"
|
"nanoid": "^5.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kevisual/ai": "^0.0.15",
|
"@kevisual/ai": "^0.0.16",
|
||||||
"@kevisual/types": "^0.0.10",
|
"@kevisual/types": "^0.0.10",
|
||||||
"@types/archiver": "^7.0.0",
|
"@types/archiver": "^7.0.0",
|
||||||
"@types/bun": "^1.3.3",
|
"@types/bun": "^1.3.3",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"dotenv": "^17.2.3"
|
"dotenv": "^17.2.3",
|
||||||
|
"fast-glob": "^3.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
18
backend/prompts/markdown-files.json
Normal file
18
backend/prompts/markdown-files.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"title": "应用脚本",
|
||||||
|
"content": "应用脚本"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "智能家居HA",
|
||||||
|
"content": "请根据用户输入生成符合规范的快捷键 JSON 数据,格式如下:\n\n```json\n{ \"type\": \"ha\", \"ha\": { \"entity_id\": \"string\", \"task\": \"\" } }\n```\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "快捷键",
|
||||||
|
"content": "请根据用户输入生成符合规范的快捷键 JSON 数据,格式如下:\n```json\n{\"type\":\"hotkeys\",\"hotkeys\":\"ctrlOrCommand+h\"}\n```\n### 规则说明:\n\n1. **数据类型固定为**:`\"hotkeys\"`,字段名为 `hotkeys`,值为**标准快捷键字符串**,使用小写字母和 `+` 连接。\n1. **操作系统适配**:所有修饰符的 `Ctrl` 键必须替换为 `ctrlOrCommand`, 但是win默认为windows的按键,alt键保留\n2. 如果用户没有提供指令,但是说要复制,则根据对应的情况生成一个快捷键,比如ctrlOrCommand+c\n3. *优先级规则**:\n - 如果用户**明确提供了快捷键指令**(如“按 Ctrl+H”、“设置快捷键为 Ctrl+Shift+A”),则**优先解析并生成对应的快捷键**。\n - 如果用户**未明确提供快捷键**,但表达了**常见操作意图**(如“复制”、\"粘贴\"、ps快捷键等),则根据标准自动映射\n\n比如,生成一个ctrl+h的快捷键,生成的json数据是\n```json\n{”type\":\"hotkeys\",\"hotkeys\":\"ctrlOrCommnd+h\"}\n```\n### 用户输入内容是\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "文档",
|
||||||
|
"content": "对当前内容进行美化"
|
||||||
|
}
|
||||||
|
]
|
||||||
1
backend/prompts/应用脚本.md
Normal file
1
backend/prompts/应用脚本.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
应用脚本
|
||||||
18
backend/prompts/快捷键.md
Normal file
18
backend/prompts/快捷键.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
请根据用户输入生成符合规范的快捷键 JSON 数据,格式如下:
|
||||||
|
```json
|
||||||
|
{"type":"hotkeys","hotkeys":"ctrlOrCommand+h"}
|
||||||
|
```
|
||||||
|
### 规则说明:
|
||||||
|
|
||||||
|
1. **数据类型固定为**:`"hotkeys"`,字段名为 `hotkeys`,值为**标准快捷键字符串**,使用小写字母和 `+` 连接。
|
||||||
|
1. **操作系统适配**:所有修饰符的 `Ctrl` 键必须替换为 `ctrlOrCommand`, 但是win默认为windows的按键,alt键保留
|
||||||
|
2. 如果用户没有提供指令,但是说要复制,则根据对应的情况生成一个快捷键,比如ctrlOrCommand+c
|
||||||
|
3. *优先级规则**:
|
||||||
|
- 如果用户**明确提供了快捷键指令**(如“按 Ctrl+H”、“设置快捷键为 Ctrl+Shift+A”),则**优先解析并生成对应的快捷键**。
|
||||||
|
- 如果用户**未明确提供快捷键**,但表达了**常见操作意图**(如“复制”、"粘贴"、ps快捷键等),则根据标准自动映射
|
||||||
|
|
||||||
|
比如,生成一个ctrl+h的快捷键,生成的json数据是
|
||||||
|
```json
|
||||||
|
{”type":"hotkeys","hotkeys":"ctrlOrCommnd+h"}
|
||||||
|
```
|
||||||
|
### 用户输入内容是
|
||||||
1
backend/prompts/文档.md
Normal file
1
backend/prompts/文档.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
对当前内容进行美化
|
||||||
5
backend/prompts/智能家居HA.md
Normal file
5
backend/prompts/智能家居HA.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
请根据用户输入生成符合规范的快捷键 JSON 数据,格式如下:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "type": "ha", "ha": { "entity_id": "string", "task": "" } }
|
||||||
|
```
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
// base
|
// base
|
||||||
import { app } from '../app.ts';
|
import { app } from '../app.ts';
|
||||||
import './noco/index.ts';
|
import './noco/index.ts';
|
||||||
import './noco/config.ts';
|
|
||||||
|
|
||||||
// 添加认证中间件路由
|
// 添加认证中间件路由
|
||||||
const hasAuth = app.router.routes.some(r => r.id === 'auth');
|
const hasAuth = app.router.routes.some(r => r.id === 'auth');
|
||||||
|
|||||||
106
backend/src/routes/noco/auto-generate-data.ts
Normal file
106
backend/src/routes/noco/auto-generate-data.ts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import { app } from '@/app.ts'
|
||||||
|
import { NocoLifeService } from './services/life.ts';
|
||||||
|
import { useContextKey } from '@kevisual/context';
|
||||||
|
import { NocoWehookPayload, ColumnItem } from '@kevisual/noco-auto';
|
||||||
|
import { AIUtils, BaseChat } from '@kevisual/ai';
|
||||||
|
export const reportErrors = (errors: any[]) => {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
app.route({
|
||||||
|
path: 'noco-control',
|
||||||
|
key: 'generateData',
|
||||||
|
description: `多维表格自动生成数据接口, 根据用户需求,自动生成字段"数据"的内容`,
|
||||||
|
middleware: ['auth']
|
||||||
|
}).define(async (ctx) => {
|
||||||
|
const query = ctx.query as NocoWehookPayload<ColumnItem>;
|
||||||
|
const token = ctx.query.token || '';
|
||||||
|
const baseId = query?.base_id;
|
||||||
|
const tableId = query?.data?.table_id;
|
||||||
|
let question = ctx.query.question || '';
|
||||||
|
const lifeService = new NocoLifeService({ token });
|
||||||
|
const config = await lifeService.getLifeConfig();
|
||||||
|
console.log('rows', query.data.rows);
|
||||||
|
if (config.baseId !== baseId) {
|
||||||
|
ctx.throw(400, 'baseId 不匹配');
|
||||||
|
}
|
||||||
|
const life = await lifeService.createLife({ ...config, tableId });
|
||||||
|
|
||||||
|
const row = query.data.rows?.[0];
|
||||||
|
if (!row) {
|
||||||
|
ctx.throw(400, '没有数据行');
|
||||||
|
}
|
||||||
|
const columnKeys = Object.keys(row).filter(k => !['Id', 'CreatedAt', 'UpdatedAt'].includes(k));
|
||||||
|
if (columnKeys.length === 0) {
|
||||||
|
ctx.throw(400, '没有可用的字段');
|
||||||
|
}
|
||||||
|
let prompt = question || row['提示词'] || ''
|
||||||
|
const id = row['Id'];
|
||||||
|
if (!id) {
|
||||||
|
ctx.throw(400, '数据行没有 Id');
|
||||||
|
}
|
||||||
|
const title = row['标题'] || '';
|
||||||
|
const summary = row['总结'] || '';
|
||||||
|
const type = row['类型'] || '';
|
||||||
|
const _data = row['数据'] || '';
|
||||||
|
let systemPrompt = getPrompt({ type });
|
||||||
|
let other = `\n相关资料是:
|
||||||
|
标题: ${title}
|
||||||
|
总结: ${summary}`
|
||||||
|
if (_data) {
|
||||||
|
other += `
|
||||||
|
已有数据: ${_data}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (title) {
|
||||||
|
systemPrompt += other
|
||||||
|
}
|
||||||
|
const ai: BaseChat = useContextKey('ai');
|
||||||
|
const answer = await ai.chat([
|
||||||
|
{ role: 'system', content: systemPrompt },
|
||||||
|
{ role: 'user', content: prompt ? prompt : '请生成对应的数据' }
|
||||||
|
])
|
||||||
|
let msg = ai.responseText || '';
|
||||||
|
console.log('生成的数据内容:', msg);
|
||||||
|
const data = await AIUtils.extractJsonFromMarkdown(msg);
|
||||||
|
if (data == null) {
|
||||||
|
ctx.throw(500, 'AI 返回结果解析失败');
|
||||||
|
}
|
||||||
|
// 更新数据到多维表格
|
||||||
|
const itme = await life.updateItem({
|
||||||
|
Id: id,
|
||||||
|
['数据']: data,
|
||||||
|
});
|
||||||
|
console.log('更新后的数据行:', itme);
|
||||||
|
ctx.body = 'ok'
|
||||||
|
}).addTo(app)
|
||||||
|
|
||||||
|
const DATA_TYPES = ['快捷键', '应用脚本', '智能家居HA', '文档'];
|
||||||
|
type DataType = typeof DATA_TYPES[number];
|
||||||
|
const getPrompt = (opts?: { type: DataType }) => {
|
||||||
|
const type = opts?.type || '通用';
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
"title": "应用脚本",
|
||||||
|
"content": "应用脚本"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "智能家居HA",
|
||||||
|
"content": "请根据用户输入生成符合规范的快捷键 JSON 数据,格式如下:\n\n```json\n{ \"type\": \"ha\", \"ha\": { \"entity_id\": \"string\", \"task\": \"\" } }\n```\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "快捷键",
|
||||||
|
"content": "请根据用户输入生成符合规范的快捷键 JSON 数据,格式如下:\n```json\n{\"type\":\"hotkeys\",\"hotkeys\":\"ctrlOrCommand+h\"}\n```\n### 规则说明:\n\n1. **数据类型固定为**:`\"hotkeys\"`,字段名为 `hotkeys`,值为**标准快捷键字符串**,使用小写字母和 `+` 连接。\n1. **操作系统适配**:所有修饰符的 `Ctrl` 键必须替换为 `ctrlOrCommand`, 但是win默认为windows的按键,alt键保留\n2. 如果用户没有提供指令,但是说要复制,则根据对应的情况生成一个快捷键,比如ctrlOrCommand+c\n3. *优先级规则**:\n - 如果用户**明确提供了快捷键指令**(如“按 Ctrl+H”、“设置快捷键为 Ctrl+Shift+A”),则**优先解析并生成对应的快捷键**。\n - 如果用户**未明确提供快捷键**,但表达了**常见操作意图**(如“复制”、\"粘贴\"、ps快捷键等),则根据标准自动映射\n\n比如,生成一个ctrl+h的快捷键,生成的json数据是\n```json\n{”type\":\"hotkeys\",\"hotkeys\":\"ctrlOrCommnd+h\"}\n```\n### 用户输入内容是\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "文档",
|
||||||
|
"content": "对当前内容进行美化"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const item = data.find(d => d.title === type);
|
||||||
|
if (item) {
|
||||||
|
return item.content;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
@@ -1,188 +1,4 @@
|
|||||||
import { app } from '@/app.ts'
|
import './noco-life.ts'
|
||||||
import { NocoLifeService } from './services/life.ts';
|
import './config.ts'
|
||||||
import { useContextKey } from '@kevisual/context';
|
|
||||||
import { BaseChat } from '@kevisual/ai';
|
|
||||||
import { AIUtils } from '@kevisual/ai';
|
|
||||||
import { createLunarDate, 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 || '';
|
|
||||||
|
|
||||||
if (question.startsWith('配置多维表格')) {
|
|
||||||
const res = await ctx.call({
|
|
||||||
path: 'noco-life',
|
|
||||||
key: 'config-update',
|
|
||||||
token: token,
|
|
||||||
payload: { question }
|
|
||||||
})
|
|
||||||
ctx.body = res.body;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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: BaseChat = useContextKey('ai');
|
|
||||||
const answer = await ai.chat([
|
|
||||||
{ role: 'system', content: `你是一个多维表格助理,你的任务是帮助用户操作和查询多维表格的数据。你可以使用以下工具来完成任务:\n\n${v}` },
|
|
||||||
{ role: 'user', content: question }
|
|
||||||
])
|
|
||||||
let msg = AIUtils.extractJsonFromMarkdown(ai.responseText || '');
|
|
||||||
if (msg == null) {
|
|
||||||
ctx.throw(500, 'AI 返回结果解析失败');
|
|
||||||
}
|
|
||||||
console.log('msg', msg);
|
|
||||||
const route = routes.find(r => r.id === msg.id || r.key === msg.id);
|
|
||||||
console.log('route============', route.id, route.path, route.key);
|
|
||||||
const res = await ctx.call({
|
|
||||||
...msg,
|
|
||||||
token: token
|
|
||||||
});
|
|
||||||
if (res.code !== 200) {
|
|
||||||
console.log('调用工具失败', res.message);
|
|
||||||
ctx.throw(500, res.message || '调用工具失败');
|
|
||||||
}
|
|
||||||
console.log('con=============', res?.data);
|
|
||||||
console.log('res', res.code, res.body?.content);
|
|
||||||
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 tomorrow = dayjs().add(1, 'day').startOf('day').toISOString();
|
|
||||||
const tomorrowDate = dayjs(tomorrow).format('YYYY-MM-DD');
|
|
||||||
const res = await life.getList({
|
|
||||||
fields: ['Id', '标题', '总结', '启动时间', '标签', '任务'],
|
|
||||||
where: `(任务,eq,运行中)~and(启动时间,lt,exactDate,${tomorrowDate})`,
|
|
||||||
// where: "(任务,eq,运行中)~and(启动时间,le,today)",
|
|
||||||
// where: "(任务,eq,运行中)~and(启动时间,le,daysAgo,-1)",
|
|
||||||
sort: '启动时间',
|
|
||||||
});
|
|
||||||
console.log('today res', res.data?.list?.map(i => i['标题']));
|
|
||||||
if (res.code === 200) {
|
|
||||||
const list = res.data.list || []
|
|
||||||
ctx.body = {
|
|
||||||
list,
|
|
||||||
content: list.map(item => {
|
|
||||||
return `任务: ${item['标题']}[${item['Id']}], 启动时间: ${dayjs(item['启动时间']).format('YYYY-MM-DD HH:mm:ss')}, 标签: ${item['标签'] || '无'} \n总结: ${item['总结'] || '无'}`;
|
|
||||||
}).join('\n')
|
|
||||||
};
|
|
||||||
if (list.length === 0) {
|
|
||||||
ctx.body = {
|
|
||||||
list,
|
|
||||||
content: '今天没有需要做的事情了,休息一下吧'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
console.log('id', 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'),
|
|
||||||
content: `任务 "${record['标题']}" 已标记为完成。下一次运行时间是 ${dayjs(nextTime).format('YYYY-MM-DD HH:mm:ss')}`
|
|
||||||
};
|
|
||||||
|
|
||||||
}).addTo(app);
|
|
||||||
|
|
||||||
|
import './auto-generate-data.ts'
|
||||||
188
backend/src/routes/noco/noco-life.ts
Normal file
188
backend/src/routes/noco/noco-life.ts
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
import { app } from '@/app.ts'
|
||||||
|
import { NocoLifeService } from './services/life.ts';
|
||||||
|
import { useContextKey } from '@kevisual/context';
|
||||||
|
import { BaseChat } from '@kevisual/ai';
|
||||||
|
import { AIUtils } from '@kevisual/ai';
|
||||||
|
import { createLunarDate, 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 || '';
|
||||||
|
|
||||||
|
if (question.startsWith('配置多维表格')) {
|
||||||
|
const res = await ctx.call({
|
||||||
|
path: 'noco-life',
|
||||||
|
key: 'config-update',
|
||||||
|
token: token,
|
||||||
|
payload: { question }
|
||||||
|
})
|
||||||
|
ctx.body = res.body;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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: BaseChat = useContextKey('ai');
|
||||||
|
const answer = await ai.chat([
|
||||||
|
{ role: 'system', content: `你是一个多维表格助理,你的任务是帮助用户操作和查询多维表格的数据。你可以使用以下工具来完成任务:\n\n${v}` },
|
||||||
|
{ role: 'user', content: question }
|
||||||
|
])
|
||||||
|
let msg = AIUtils.extractJsonFromMarkdown(ai.responseText || '');
|
||||||
|
if (msg == null) {
|
||||||
|
ctx.throw(500, 'AI 返回结果解析失败');
|
||||||
|
}
|
||||||
|
console.log('msg', msg);
|
||||||
|
const route = routes.find(r => r.id === msg.id || r.key === msg.id);
|
||||||
|
console.log('route============', route.id, route.path, route.key);
|
||||||
|
const res = await ctx.call({
|
||||||
|
...msg,
|
||||||
|
token: token
|
||||||
|
});
|
||||||
|
if (res.code !== 200) {
|
||||||
|
console.log('调用工具失败', res.message);
|
||||||
|
ctx.throw(500, res.message || '调用工具失败');
|
||||||
|
}
|
||||||
|
console.log('con=============', res?.data);
|
||||||
|
console.log('res', res.code, res.body?.content);
|
||||||
|
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 tomorrow = dayjs().add(1, 'day').startOf('day').toISOString();
|
||||||
|
const tomorrowDate = dayjs(tomorrow).format('YYYY-MM-DD');
|
||||||
|
const res = await life.getList({
|
||||||
|
fields: ['Id', '标题', '总结', '启动时间', '标签', '任务'],
|
||||||
|
where: `(任务,eq,运行中)~and(启动时间,lt,exactDate,${tomorrowDate})`,
|
||||||
|
// where: "(任务,eq,运行中)~and(启动时间,le,today)",
|
||||||
|
// where: "(任务,eq,运行中)~and(启动时间,le,daysAgo,-1)",
|
||||||
|
sort: '启动时间',
|
||||||
|
});
|
||||||
|
console.log('today res', res.data?.list?.map(i => i['标题']));
|
||||||
|
if (res.code === 200) {
|
||||||
|
const list = res.data.list || []
|
||||||
|
ctx.body = {
|
||||||
|
list,
|
||||||
|
content: list.map(item => {
|
||||||
|
return `任务: ${item['标题']}[${item['Id']}], 启动时间: ${dayjs(item['启动时间']).format('YYYY-MM-DD HH:mm:ss')}, 标签: ${item['标签'] || '无'} \n总结: ${item['总结'] || '无'}`;
|
||||||
|
}).join('\n')
|
||||||
|
};
|
||||||
|
if (list.length === 0) {
|
||||||
|
ctx.body = {
|
||||||
|
list,
|
||||||
|
content: '今天没有需要做的事情了,休息一下吧'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
console.log('id', 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'),
|
||||||
|
content: `任务 "${record['标题']}" 已标记为完成。下一次运行时间是 ${dayjs(nextTime).format('YYYY-MM-DD HH:mm:ss')}`
|
||||||
|
};
|
||||||
|
|
||||||
|
}).addTo(app);
|
||||||
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Life, NocoApi } from '@kevisual/noco-auto';
|
import { Life, NocoApi } from '@kevisual/noco-auto';
|
||||||
import { initConfig } from "@kevisual/use-config";
|
|
||||||
import { QueryConfig } from "@/query/query-config/query-config.ts";
|
import { QueryConfig } from "@/query/query-config/query-config.ts";
|
||||||
import { Query } from "@kevisual/query/query";
|
import { Query } from "@kevisual/query/query";
|
||||||
import { CustomError } from '@kevisual/router'
|
import { CustomError } from '@kevisual/router'
|
||||||
@@ -43,6 +42,15 @@ export class NocoLifeService {
|
|||||||
}, { token: this.token });
|
}, { token: this.token });
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
async createLife(data: NocoLifeConfig) {
|
||||||
|
const nocoApi = new NocoApi({
|
||||||
|
baseURL: data.baseURL || '',
|
||||||
|
token: data.token || '',
|
||||||
|
table: data.tableId || '',
|
||||||
|
});
|
||||||
|
const life = new Life({ nocoApi, baseId: data.baseId });
|
||||||
|
return life;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 需要从服务端获取自己保存的配置,包括 nocodb 地址,apiKey 等
|
* 需要从服务端获取自己保存的配置,包括 nocodb 地址,apiKey 等
|
||||||
*/
|
*/
|
||||||
|
|||||||
27
backend/test/create-json.ts
Normal file
27
backend/test/create-json.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import fastGlob from 'fast-glob';
|
||||||
|
|
||||||
|
// 匹配所有 markdown 文件
|
||||||
|
const mds = await fastGlob('../prompts/*.md', {
|
||||||
|
cwd: __dirname,
|
||||||
|
absolute: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 生成 JSON 数据
|
||||||
|
const jsonData = mds.map((filePath) => {
|
||||||
|
const fileName = path.basename(filePath, '.md');
|
||||||
|
const content = fs.readFileSync(path.join(__dirname, filePath), 'utf-8');
|
||||||
|
return {
|
||||||
|
title: fileName,
|
||||||
|
content: content,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 输出 JSON
|
||||||
|
const outputPath = path.join(__dirname, '../prompts/markdown-files.json');
|
||||||
|
fs.writeFileSync(outputPath, JSON.stringify(jsonData, null, 2), 'utf-8');
|
||||||
|
|
||||||
|
console.log(`已生成 JSON 文件: ${outputPath}`);
|
||||||
|
console.log(`共找到 ${jsonData.length} 个 markdown 文件`);
|
||||||
|
|
||||||
@@ -19,29 +19,29 @@
|
|||||||
"@astrojs/mdx": "^4.3.12",
|
"@astrojs/mdx": "^4.3.12",
|
||||||
"@astrojs/react": "^4.4.2",
|
"@astrojs/react": "^4.4.2",
|
||||||
"@astrojs/sitemap": "^3.6.0",
|
"@astrojs/sitemap": "^3.6.0",
|
||||||
"@kevisual/query": "^0.0.29",
|
"@kevisual/query": "^0.0.31",
|
||||||
"@kevisual/query-login": "^0.0.7",
|
"@kevisual/query-login": "^0.0.7",
|
||||||
"@kevisual/registry": "^0.0.1",
|
"@kevisual/registry": "^0.0.1",
|
||||||
"@radix-ui/react-slot": "^1.2.4",
|
"@radix-ui/react-slot": "^1.2.4",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"@uiw/react-md-editor": "^4.0.8",
|
"@uiw/react-md-editor": "^4.0.11",
|
||||||
"antd": "^6.0.0",
|
"antd": "^6.0.1",
|
||||||
"astro": "^5.16.0",
|
"astro": "^5.16.4",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"es-toolkit": "^1.42.0",
|
"es-toolkit": "^1.42.0",
|
||||||
"github-markdown-css": "^5.8.1",
|
"github-markdown-css": "^5.8.1",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"lucide-react": "^0.554.0",
|
"lucide-react": "^0.556.0",
|
||||||
"marked": "^17.0.1",
|
"marked": "^17.0.1",
|
||||||
"marked-highlight": "^2.2.3",
|
"marked-highlight": "^2.2.3",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.1",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.1",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
"zustand": "^5.0.8"
|
"zustand": "^5.0.9"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
"tw-animate-css": "^1.4.0"
|
"tw-animate-css": "^1.4.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.23.0",
|
"packageManager": "pnpm@10.24.0",
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@tailwindcss/oxide",
|
"@tailwindcss/oxide",
|
||||||
"esbuild",
|
"esbuild",
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kevisual/context": "^0.0.4",
|
"@kevisual/context": "^0.0.4",
|
||||||
"@kevisual/noco": "^0.0.8",
|
"@kevisual/noco": "^0.0.8",
|
||||||
"@kevisual/query": "^0.0.29",
|
"@kevisual/query": "^0.0.31",
|
||||||
"@kevisual/router": "^0.0.33",
|
"@kevisual/router": "^0.0.36",
|
||||||
"@kevisual/use-config": "^1.0.21"
|
"@kevisual/use-config": "^1.0.21"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
1118
pnpm-lock.yaml
generated
1118
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user