aiot-toolkit 1.2.0-alpha.2 → 1.2.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +5 -2
- package/lib/commands/aa.js +2 -0
- package/lib/commands/ai.js +1 -1
- package/lib/commands/test.js +2 -0
- package/package.json +11 -9
package/bin/index.js
CHANGED
|
@@ -31,9 +31,12 @@ program.version(require('../package').version, '-v, --version').usage('<command>
|
|
|
31
31
|
program
|
|
32
32
|
.command('ai')
|
|
33
33
|
.description('Quick App Smart Programming Assistant.')
|
|
34
|
-
.
|
|
34
|
+
.option('--prompt <name>', 'input prompt, eg: generate a todo quickapp')
|
|
35
|
+
.option('--files <fileData>', 'input files, eg: image/png:cbb08670,application/pdf:e8d2827e')
|
|
36
|
+
.option('--test', 'test mode')
|
|
37
|
+
.action((prompt, options) => {
|
|
35
38
|
const ai = require('../lib/commands/ai')
|
|
36
|
-
ai()
|
|
39
|
+
ai(prompt, options)
|
|
37
40
|
})
|
|
38
41
|
|
|
39
42
|
program
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";const puppeteer=require("puppeteer"),chalk=require("chalk"),fs=require("fs"),path=require("path"),sleep=e=>new Promise((t=>setTimeout(t,e)));async function test(){const e=await puppeteer.launch(),t=await e.newPage();var a;await t.emulate({name:"iPhone X",userAgent:"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1",viewport:{width:375,height:812,deviceScaleFactor:3,isMobile:!0,hasTouch:!0,isLandscape:!1}}),await(a=1e3,new Promise((e=>setTimeout(e,a))));await t.goto("http://localhost:8000/preview/Home",{waitUntil:"load",timeout:6e4});await t.evaluate((()=>{const e=document.querySelector("body");return e&&""===e.innerHTML.trim()}))?console.error(chalk.red("页面白屏")):console.log(chalk.green("页面正常加载"));const o=path.join(__dirname,"screenshots");fs.existsSync(o)||fs.mkdirSync(o,{recursive:!0});const n=path.join(o,"run_1.png");await t.screenshot({path:n})}function extractJSON(e){try{var t;const a=null===(t=(e=e.replace(/```/g,"").trim()).match(/{[\s\S]*}/))||void 0===t?void 0:t[0];if(!a)throw new Error("No JSON content found");const o=a.replace(/,\s*}/g,"}").replace(/,\s*]/g,"]").replace(/\\n/g,"\\\\n").replace(/\\t/g,"\\\\t"),n=JSON.parse(o);return console.log("Extracted JSON:",n),n}catch(e){return console.error("Error extracting JSON:",e.message),null}}function testPath(){const e=path.basename("/home/lvxin/work/gitlab/ai-quickapp/test/screenshots/run_29.png"),t=path.join("screenshots",e);console.log(t)}const mockFiles=[{id:"cbb08670-6e8d-4230-9d04-9d1206b928ba",name:"Cyber_User.png",size:562298,extension:"png",mime_type:"image/png",created_by:"be6afaf3-63ef-42b3-9137-84b326c29e73",created_at:1746777496},{id:"e8d2827e-318b-4885-9def-3abf39af3cb2",name:"拍照解题-手机解题快应用.pdf",size:502150,extension:"pdf",mime_type:"application/pdf",created_by:"be6afaf3-63ef-42b3-9137-84b326c29e73",created_at:1746777522}],filesStr=mockFiles.map((e=>`${e.mime_type}:${e.id}`)).join(",");console.log(filesStr);
|
|
2
|
+
//# sourceMappingURL=aa.js.map
|
package/lib/commands/ai.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";const ora=require("ora"),chalk=require("chalk"),inquirer=require("inquirer"),axios=require("axios"),fs=require("fs"),fsExtra=require("fs-extra"),path=require("path"),DIFY_API_BASE_URL="https://mify-be.pt.xiaomi.com",API_PATH="/api/v1/chat-messages",API_KEY="app-MS7p2SLhiTHDBgR7HqkxNGSh",headers={Authorization:`Bearer ${API_KEY}`,"Content-Type":"application/json"},mockData={project_name:"Calculator",package_name:"com.example.calculator",page_content:"<template>",manifest:{package:"com.example.calculator",name:"Calculator",versionName:"1.0.0",versionCode:1,minPlatformVersion:1070,icon:"/Common/logo.png",features:[],permissions:[],config:{logLevel:"debug",designWidth:750},router:{entry:"Calculator",pages:{Calculator:{component:"index"}}},display:{titleBarBackgroundColor:"#202020",titleBarTextColor:"#ffffff",menu:!0,pages:{Calculator:{titleBarText:"计算器",menu:!1}}}}};async function postData(e){const a={inputs:{},response_mode:"blocking",conversation_id:"",user:"lewis",query:e};try{const e=await axios.post(`${DIFY_API_BASE_URL+API_PATH}`,a,{headers:headers});return writeAnswerToFile(e.data.answer,"/home/lvxin/work/gerrit/aiot-toolkit/answer.log"),cleanJSON(e.data.answer)}catch(e){console.error("POST Error:",e.response?e.response.data:e.message)}}const sleep=e=>new Promise((a=>setTimeout(a,e)));function writeAnswerToFile(e,a){fs.writeFile(a,e,{flag:"a"},(e=>{}))}function cleanJSON(e){try{let a=JSON.parse(e),n=a.page_content.replace(/\\n/g,"\n").replace(/\\"/g,'"').replace(/\\t/g,"\t"),t=JSON.parse(a.manifest.replace(/\\"/g,'"'));return{project_name:a.project_name,package_name:a.package_name,page_content:n,manifest:t}}catch(e){return console.error("\nError parsing JSON:",e),null}}function replacePage(e,a,n){const t=path.join(process.cwd(),n),r=path.join(t,"src",n,"index.ux"),o=path.join(t,"src","manifest.json");fs.mkdirSync(path.dirname(r),{recursive:!0}),fs.mkdirSync(path.dirname(o),{recursive:!0}),fs.writeFileSync(r,a);const i=JSON.stringify(e,null,2);fs.writeFileSync(o,i)}async function ai(){const e=`\n${chalk.bold.green("快应用智能编程助手\n")}\n${chalk.cyan("您好!我是快应用智能编程助手,专注于为您创建高质量的快应用项目文件。\n")}\n${chalk.yellow("您可以试试这样说:")}\n${chalk.magenta(" 做一个天气快应用")}\n${chalk.magenta(" 做一个计算器手表快应用")}\n${chalk.magenta(" 做一个滴答清单手环快应用")}\n `;console.log(e);const a=await inquirer.prompt({type:"input",name:"prompt",message:"请输入您的需求:",prefix:"",validate:e=>e.length>0||"请输入您的需求!"});let n=1;const t=ora({text:chalk.cyan(`\n生成中... ${chalk.green(n)}s \n`),spinner:"dots"});t.start(),setInterval((()=>{t.text=chalk.cyan(`\n生成中... ${chalk.green(n++)}s`)}),1e3);const r=await postData(a.prompt);if(!r)return t.fail(chalk.red("生成失败,请重新生成!")),ai();const{project_name:o,package_name:i,page_content:c,manifest:s}=r;console.log(`${chalk.yellow("\n项目名称:")+o}`),console.log(`${chalk.yellow("包名:")+i}\n`);const l=Array.isArray(s.deviceTypeList)&&s.deviceTypeList.length>0?s.deviceTypeList[0]:"phone",p=require("../../lib/commands/init");await p(o),replacePage(s,c,o),t.succeed(chalk.green("\n恭喜!您的快应用项目已经创建成功!\n"));const m=path.join(process.cwd(),o);inquirer.prompt([{type:"confirm",name:"preview",message:"请问是否进行预览?",prefix:"",default:!0}]).then((e=>{if(e.preview){if("watch"===l){const e="/home/lvxin/work/test/ai-quickapp/velasim";fsExtra.copySync(e,path.join(m,"node_modules/@aiot-toolkit/velasim"));const{compile:a}=require("../../lib/commands/compile");return void a("native","dev",!0,{cwd:m,openNuttx:!0})}const{launchServer:e}=require("@aiot-toolkit/server"),{compile:a}=require("../../lib/commands/compile");e({watch:!0,cwd:m,openBrowser:!0}),a("native","dev",!0,{cwd:m})}else console.log(`${chalk.green("\n您可以使用以下命令进行开发预览调试:")}`),console.log(`${chalk.cyan(" npm run start")}`),console.log(`${chalk.cyan(" npm run build")}`),console.log(`${chalk.cyan(" npm run watch")}`)}))}module.exports=ai;
|
|
1
|
+
"use strict";const ora=require("ora"),chalk=require("chalk"),inquirer=require("inquirer"),axios=require("axios"),fs=require("fs"),fsExtra=require("fs-extra"),path=require("path"),DIFY_API_BASE_URL="https://mify-be.pt.xiaomi.com",API_PATH="/api/v1/chat-messages",API_KEY="app-f4yeq6almP6pVZd0YVFZ2ZcG",headers={Authorization:`Bearer ${API_KEY}`,"Content-Type":"application/json"},mockData={project_name:"WeatherApp",package_name:"com.example.weatherapp",page_content:{home:'<template>\n <div class="page">\n <div class="header">\n <text class="title">天气预报</text>\n </div>\n <div class="location-bar">\n <text class="location-text">{{locationName}}</text>\n <input type="button" class="location-btn" value="切换城市" onclick="switchCity" />\n </div>\n <div class="current-weather">\n <text class="temp">{{currentTemp}}°</text>\n <text class="weather-desc">{{weatherDesc}}</text>\n <text class="high-low">{{lowTemp}}° / {{highTemp}}°</text>\n </div>\n <div class="weather-detail">\n <div class="detail-item">\n <text class="detail-label">湿度</text>\n <text class="detail-value">{{humidity}}%</text>\n </div>\n <div class="detail-item">\n <text class="detail-label">风力</text>\n <text class="detail-value">{{windLevel}}</text>\n </div>\n <div class="detail-item">\n <text class="detail-label">气压</text>\n <text class="detail-value">{{pressure}}hPa</text>\n </div>\n </div>\n <text class="forecast-title">未来天气预报</text>\n <list class="forecast-list">\n <list-item type="forecast" for="{{forecastData}}" class="forecast-item">\n <text class="day">{{$item.day}}</text>\n <text class="forecast-temp">{{$item.lowTemp}}° / {{$item.highTemp}}°</text>\n <text class="forecast-desc">{{$item.desc}}</text>\n </list-item>\n </list>\n <div class="footer">\n <input type="button" class="refresh-btn" value="刷新" onclick="refreshWeather" />\n <input type="button" class="settings-btn" value="设置" onclick="goToSettings" />\n </div>\n </div>\n</template>\n\n<style>\n .page {\n flex-direction: column;\n background-color: #f5f5f5;\n width: 100%;\n }\n .header {\n height: 120px;\n width: 100%;\n background-color: #2196f3;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n }\n .title {\n font-size: 40px;\n color: #ffffff;\n font-weight: bold;\n }\n .location-bar {\n padding: 20px;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n background-color: #ffffff;\n margin: 10px 0;\n }\n .location-text {\n font-size: 34px;\n color: #333333;\n font-weight: bold;\n }\n .location-btn {\n background-color: #2196f3;\n color: #ffffff;\n font-size: 28px;\n padding: 10px 20px;\n border-radius: 4px;\n }\n .current-weather {\n padding: 40px;\n background-color: #ffffff;\n flex-direction: column;\n align-items: center;\n margin-bottom: 10px;\n }\n .temp {\n font-size: 90px;\n color: #333333;\n font-weight: bold;\n }\n .weather-desc {\n font-size: 34px;\n color: #666666;\n margin: 10px 0;\n }\n .high-low {\n font-size: 30px;\n color: #888888;\n }\n .weather-detail {\n flex-direction: row;\n justify-content: space-around;\n background-color: #ffffff;\n padding: 20px;\n margin-bottom: 10px;\n }\n .detail-item {\n flex-direction: column;\n align-items: center;\n }\n .detail-label {\n font-size: 28px;\n color: #888888;\n margin-bottom: 10px;\n }\n .detail-value {\n font-size: 32px;\n color: #333333;\n font-weight: bold;\n }\n .forecast-title {\n font-size: 34px;\n color: #333333;\n font-weight: bold;\n margin: 20px;\n }\n .forecast-list {\n background-color: #ffffff;\n padding: 10px 0;\n }\n .forecast-item {\n height: 100px;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n padding: 0 20px;\n border-bottom-width: 1px;\n border-bottom-color: #eeeeee;\n }\n .day {\n font-size: 30px;\n color: #333333;\n width: 100px;\n }\n .forecast-temp {\n font-size: 28px;\n color: #666666;\n }\n .forecast-desc {\n font-size: 28px;\n color: #888888;\n width: 120px;\n text-align: right;\n }\n .footer {\n flex-direction: row;\n justify-content: center;\n margin-top: 20px;\n margin-bottom: 20px;\n }\n .refresh-btn, .settings-btn {\n background-color: #2196f3;\n color: #ffffff;\n font-size: 28px;\n padding: 10px 30px;\n border-radius: 4px;\n margin: 0 10px;\n }\n</style>\n\n<script>\n export default {\n data: {\n locationName: \'北京\',\n currentTemp: 25,\n weatherDesc: \'晴\',\n lowTemp: 20,\n highTemp: 30,\n humidity: 45,\n windLevel: \'3级\',\n pressure: 1013,\n forecastData: [\n { day: \'明天\', lowTemp: 19, highTemp: 28, desc: \'多云\' },\n { day: \'后天\', lowTemp: 18, highTemp: 27, desc: \'小雨\' },\n { day: \'周三\', lowTemp: 16, highTemp: 24, desc: \'阵雨\' },\n { day: \'周四\', lowTemp: 15, highTemp: 26, desc: \'晴\' },\n { day: \'周五\', lowTemp: 17, highTemp: 28, desc: \'晴\' }\n ]\n },\n onInit() {\n this.getWeatherData()\n },\n getWeatherData() {\n // 在实际项目中,这里应该调用天气API获取真实的数据\n console.log(\'Getting weather data...\')\n // 模拟网络请求延迟\n setTimeout(() => {\n console.log(\'Weather data updated\')\n }, 1000)\n },\n switchCity() {\n // 跳转到城市选择页面\n this.$router.push({\n uri: \'city\'\n })\n },\n refreshWeather() {\n this.getWeatherData()\n },\n goToSettings() {\n // 跳转到设置页面\n this.$router.push({\n uri: \'settings\'\n })\n }\n }\n<\/script>',city:'<template>\n <div class="page">\n <div class="header">\n <text class="title">选择城市</text>\n </div>\n <div class="search-bar">\n <input type="text" class="search-input" placeholder="搜索城市" onchange="onSearch" />\n </div>\n <div class="hot-cities">\n <text class="section-title">热门城市</text>\n <div class="city-grid">\n <div class="city-item" for="{{hotCities}}" onclick="selectCity($item)">\n <text class="city-name">{{$item.name}}</text>\n </div>\n </div>\n </div>\n <list class="city-list">\n <list-item type="city" for="{{cityGroups}}" class="city-group">\n <text class="group-title">{{$item.initial}}</text>\n <div class="cities">\n <div class="city-row" for="{{$item.cities}}" onclick="selectCity($item)">\n <text class="city-name">{{$item.name}}</text>\n </div>\n </div>\n </list-item>\n </list>\n <div class="footer">\n <input type="button" class="back-btn" value="返回" onclick="goBack" />\n </div>\n </div>\n</template>\n\n<style>\n .page {\n flex-direction: column;\n background-color: #f5f5f5;\n width: 100%;\n }\n .header {\n height: 100px;\n width: 100%;\n background-color: #2196f3;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n }\n .title {\n font-size: 36px;\n color: #ffffff;\n font-weight: bold;\n }\n .search-bar {\n padding: 20px;\n background-color: #ffffff;\n margin-bottom: 10px;\n }\n .search-input {\n width: 100%;\n height: 80px;\n border: 1px solid #dddddd;\n border-radius: 4px;\n font-size: 30px;\n padding: 0 20px;\n }\n .hot-cities {\n background-color: #ffffff;\n padding: 20px;\n margin-bottom: 10px;\n }\n .section-title {\n font-size: 32px;\n color: #333333;\n font-weight: bold;\n margin-bottom: 20px;\n }\n .city-grid {\n flex-direction: row;\n flex-wrap: wrap;\n }\n .city-item {\n width: 23%;\n height: 80px;\n background-color: #f5f5f5;\n margin: 1%;\n justify-content: center;\n align-items: center;\n border-radius: 4px;\n }\n .city-name {\n font-size: 28px;\n color: #333333;\n }\n .city-list {\n background-color: #ffffff;\n flex: 1;\n }\n .city-group {\n flex-direction: column;\n }\n .group-title {\n font-size: 30px;\n color: #2196f3;\n background-color: #f5f5f5;\n padding: 10px 20px;\n }\n .cities {\n flex-direction: column;\n }\n .city-row {\n height: 90px;\n padding: 0 20px;\n justify-content: flex-start;\n align-items: center;\n border-bottom-width: 1px;\n border-bottom-color: #eeeeee;\n }\n .footer {\n padding: 20px;\n justify-content: center;\n }\n .back-btn {\n background-color: #2196f3;\n color: #ffffff;\n font-size: 30px;\n padding: 10px 40px;\n border-radius: 4px;\n }\n</style>\n\n<script>\n export default {\n data: {\n searchText: \'\',\n hotCities: [\n { id: 1, name: \'北京\' },\n { id: 2, name: \'上海\' },\n { id: 3, name: \'广州\' },\n { id: 4, name: \'深圳\' },\n { id: 5, name: \'杭州\' },\n { id: 6, name: \'南京\' },\n { id: 7, name: \'武汉\' },\n { id: 8, name: \'成都\' }\n ],\n cityGroups: [\n {\n initial: \'A\',\n cities: [\n { id: 101, name: \'安庆\' },\n { id: 102, name: \'鞍山\' }\n ]\n },\n {\n initial: \'B\',\n cities: [\n { id: 1, name: \'北京\' },\n { id: 201, name: \'保定\' },\n { id: 202, name: \'宝鸡\' }\n ]\n },\n {\n initial: \'C\',\n cities: [\n { id: 301, name: \'长沙\' },\n { id: 302, name: \'长春\' },\n { id: 8, name: \'成都\' }\n ]\n },\n {\n initial: \'G\',\n cities: [\n { id: 3, name: \'广州\' },\n { id: 401, name: \'贵阳\' }\n ]\n }\n ]\n },\n onSearch(e) {\n this.searchText = e.value\n // 在实际项目中,这里应该根据搜索文本过滤城市列表\n console.log(\'Search text:\', this.searchText)\n },\n selectCity(city) {\n // 选择城市后返回主页并更新城市\n this.$router.back({\n params: {\n city: city\n }\n })\n },\n goBack() {\n this.$router.back()\n }\n }\n<\/script>',settings:'<template>\n <div class="page">\n <div class="header">\n <text class="title">设置</text>\n </div>\n <list class="settings-list">\n <list-item type="setting" class="setting-item">\n <text class="setting-label">温度单位</text>\n <div class="unit-toggle">\n <div class="unit-option {{tempUnit === \'c\' ? \'active\' : \'\'}}" onclick="setTempUnit(\'c\')">\n <text class="unit-text {{tempUnit === \'c\' ? \'active-text\' : \'\'}}">°C</text>\n </div>\n <div class="unit-option {{tempUnit === \'f\' ? \'active\' : \'\'}}" onclick="setTempUnit(\'f\')">\n <text class="unit-text {{tempUnit === \'f\' ? \'active-text\' : \'\'}}">°F</text>\n </div>\n </div>\n </list-item>\n <list-item type="setting" class="setting-item">\n <text class="setting-label">自动更新</text>\n <switch class="setting-switch" checked="{{autoUpdate}}" onchange="toggleAutoUpdate"></switch>\n </list-item>\n <list-item type="setting" class="setting-item">\n <text class="setting-label">更新频率</text>\n <div class="frequency-select">\n <text class="frequency-value">{{updateFrequency}}分钟</text>\n <div class="frequency-buttons">\n <input type="button" class="frequency-btn" value="-" onclick="decreaseFrequency" />\n <input type="button" class="frequency-btn" value="+" onclick="increaseFrequency" />\n </div>\n </div>\n </list-item>\n <list-item type="setting" class="setting-item">\n <text class="setting-label">天气预警通知</text>\n <switch class="setting-switch" checked="{{weatherAlert}}" onchange="toggleWeatherAlert"></switch>\n </list-item>\n <list-item type="setting" class="setting-item">\n <text class="setting-label">背景颜色</text>\n <div class="color-options">\n <div class="color-option {{themeColor === \'blue\' ? \'color-selected\' : \'\'}}" style="background-color: #2196f3;" onclick="setThemeColor(\'blue\')"></div>\n <div class="color-option {{themeColor === \'green\' ? \'color-selected\' : \'\'}}" style="background-color: #4caf50;" onclick="setThemeColor(\'green\')"></div>\n <div class="color-option {{themeColor === \'orange\' ? \'color-selected\' : \'\'}}" style="background-color: #ff9800;" onclick="setThemeColor(\'orange\')"></div>\n <div class="color-option {{themeColor === \'purple\' ? \'color-selected\' : \'\'}}" style="background-color: #9c27b0;" onclick="setThemeColor(\'purple\')"></div>\n </div>\n </list-item>\n </list>\n <div class="footer">\n <input type="button" class="save-btn" value="保存设置" onclick="saveSettings" />\n <input type="button" class="back-btn" value="返回" onclick="goBack" />\n </div>\n </div>\n</template>\n\n<style>\n .page {\n flex-direction: column;\n background-color: #f5f5f5;\n width: 100%;\n }\n .header {\n height: 100px;\n width: 100%;\n background-color: #2196f3;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n }\n .title {\n font-size: 36px;\n color: #ffffff;\n font-weight: bold;\n }\n .settings-list {\n margin-top: 10px;\n background-color: #ffffff;\n }\n .setting-item {\n height: 100px;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n padding: 0 20px;\n border-bottom-width: 1px;\n border-bottom-color: #eeeeee;\n }\n .setting-label {\n font-size: 32px;\n color: #333333;\n }\n .unit-toggle {\n flex-direction: row;\n width: 180px;\n height: 60px;\n border-radius: 30px;\n background-color: #eeeeee;\n overflow: hidden;\n }\n .unit-option {\n flex: 1;\n justify-content: center;\n align-items: center;\n }\n .unit-text {\n font-size: 28px;\n color: #666666;\n }\n .active {\n background-color: #2196f3;\n }\n .active-text {\n color: #ffffff;\n }\n .setting-switch {\n width: 100px;\n }\n .frequency-select {\n flex-direction: row;\n align-items: center;\n }\n .frequency-value {\n font-size: 30px;\n color: #666666;\n margin-right: 20px;\n }\n .frequency-buttons {\n flex-direction: row;\n }\n .frequency-btn {\n width: 60px;\n height: 60px;\n background-color: #2196f3;\n color: #ffffff;\n font-size: 36px;\n border-radius: 30px;\n margin: 0 5px;\n text-align: center;\n }\n .color-options {\n flex-direction: row;\n }\n .color-option {\n width: 50px;\n height: 50px;\n border-radius: 25px;\n margin: 0 5px;\n border: 2px solid transparent;\n }\n .color-selected {\n border-color: #333333;\n }\n .footer {\n flex-direction: row;\n justify-content: center;\n margin-top: 40px;\n margin-bottom: 20px;\n }\n .save-btn, .back-btn {\n background-color: #2196f3;\n color: #ffffff;\n font-size: 30px;\n padding: 10px 30px;\n border-radius: 4px;\n margin: 0 10px;\n }\n</style>\n\n<script>\n export default {\n data: {\n tempUnit: \'c\',\n autoUpdate: true,\n updateFrequency: 30,\n weatherAlert: true,\n themeColor: \'blue\'\n },\n onInit() {\n // 从存储加载设置\n this.loadSettings()\n },\n loadSettings() {\n // 在实际项目中,这里应该从本地存储加载用户设置\n console.log(\'Loading settings...\')\n },\n setTempUnit(unit) {\n this.tempUnit = unit\n },\n toggleAutoUpdate(e) {\n this.autoUpdate = e.checked\n },\n increaseFrequency() {\n if (this.updateFrequency < 120) {\n this.updateFrequency += 15\n }\n },\n decreaseFrequency() {\n if (this.updateFrequency > 15) {\n this.updateFrequency -= 15\n }\n },\n toggleWeatherAlert(e) {\n this.weatherAlert = e.checked\n },\n setThemeColor(color) {\n this.themeColor = color\n },\n saveSettings() {\n // 在实际项目中,这里应该将设置保存到本地存储\n console.log(\'Saving settings:\', {\n tempUnit: this.tempUnit,\n autoUpdate: this.autoUpdate,\n updateFrequency: this.updateFrequency,\n weatherAlert: this.weatherAlert,\n themeColor: this.themeColor\n })\n \n // 显示保存成功提示\n this.$app.$def.showToast(\'设置已保存\')\n \n // 返回主页\n setTimeout(() => {\n this.$router.back()\n }, 1500)\n },\n goBack() {\n this.$router.back()\n }\n }\n<\/script>'},manifest:{package:"com.example.weatherapp",name:"WeatherApp",versionName:"1.0.0",versionCode:1,minPlatformVersion:1070,icon:"/common/images/logo.png",features:[],permissions:[{origin:"*"}],config:{logLevel:"debug",designWidth:750},router:{entry:"home",pages:{home:{component:"index"},city:{component:"index"},settings:{component:"index"}}},display:{titleBarBackgroundColor:"#2196f3",titleBarTextColor:"#ffffff",menu:!0,pages:{home:{titleBarText:"天气预报",menu:!1},city:{titleBarText:"选择城市"},settings:{titleBarText:"设置"}}}}},mockFiles=[{id:"cbb08670-6e8d-4230-9d04-9d1206b928ba",name:"Cyber_User.png",size:562298,extension:"png",mime_type:"image/png",created_by:"be6afaf3-63ef-42b3-9137-84b326c29e73",created_at:1746777496},{id:"e8d2827e-318b-4885-9def-3abf39af3cb2",name:"拍照解题-手机解题快应用.pdf",size:502150,extension:"pdf",mime_type:"application/pdf",created_by:"be6afaf3-63ef-42b3-9137-84b326c29e73",created_at:1746777522}];function convertToJsonArray(n){return n.split(",").map((n=>{const[e,t]=n.split(":");return{[e]:t}}))}async function postData(n,e){const t={inputs:{image:[]},response_mode:"blocking",conversation_id:"",user:"aiot-toolkit",query:n,files:[]};if(e&&e.length>1){convertToJsonArray(e).forEach((n=>{const e=Object.keys(n)[0],i=n[e];e.startsWith("image/")&&t.inputs.image.push({type:"image",transfer_method:"local_file",upload_file_id:i}),e.startsWith("application/")&&t.files.push({type:"document",transfer_method:"local_file",upload_file_id:i})})),t.user="IDE"}try{const n=await axios.post(`${DIFY_API_BASE_URL+API_PATH}`,t,{headers:headers});return writeAnswerToFile(n.data.answer,"/home/lvxin/work/gerrit/aiot-toolkit/answer.log"),cleanJSON(n.data.answer)}catch(n){console.error("POST Error:",n.response?n.response.data:n.message)}}const sleep=n=>new Promise((e=>setTimeout(e,n)));function writeAnswerToFile(n,e){fs.writeFile(e,n,{flag:"a"},(n=>{}))}function extractJSON(n){try{var e;const t=null===(e=(n=n.replace(/```/g,"").trim()).match(/{[\s\S]*}/))||void 0===e?void 0:e[0];if(!t)throw new Error("No JSON content found");const i=t.replace(/,\s*}/g,"}").replace(/,\s*]/g,"]");return JSON.parse(i)}catch(n){throw console.error("Error extracting JSON:",n),new Error("Failed to extract valid JSON")}}function cleanJSON(n){try{let e=extractJSON(n);const t=e.page_content;for(const[n,e]of Object.entries(t))t[n]=e.replace(/\\n/g,"\n").replace(/\\"/g,'"').replace(/\\t/g,"\t");let i;return i="string"==typeof e.manifest?JSON.parse(e.manifest.replace(/\\"/g,'"')):e.manifest,{project_name:e.project_name,package_name:e.package_name,page_content:t,manifest:i}}catch(n){return console.error("\nError parsing JSON:",n),null}}function replacePage(n,e,t){const i=path.join(process.cwd(),t);for(const[n,t]of Object.entries(e)){const e=path.join(i,"src",n,"index.ux");fs.mkdirSync(path.dirname(e),{recursive:!0}),fs.writeFileSync(e,t)}n.icon="/Common/logo.png";const o=path.join(i,"src","manifest.json");fs.mkdirSync(path.dirname(o),{recursive:!0});const a=JSON.stringify(n,null,2);fs.writeFileSync(o,a)}async function ai(n){let e=n.prompt;if(!e){const n=`\n ${chalk.bold.green("快应用智能编程助手\n")}\n ${chalk.cyan("您好!我是快应用智能编程助手,专注于为您创建高质量的快应用项目文件。\n")}\n ${chalk.yellow("您可以试试这样说:")}\n ${chalk.magenta(" 做一个天气快应用")}\n ${chalk.magenta(" 做一个计算器手表快应用")}\n ${chalk.magenta(" 做一个滴答清单手环快应用")}\n `;console.log(n);e=(await inquirer.prompt({type:"input",name:"prompt",message:"请输入您的需求:",prefix:"",validate:n=>n.length>0||"请输入您的需求!"})).prompt}const t=ora({spinner:"dots"});t.start(),setInterval((()=>{}),1e3);const i=await postData(e,n.files);if(!i)return t.fail(chalk.red("生成失败,请重新生成!")),ai(n);let{project_name:o,package_name:a,page_content:c,manifest:r}=i;if(fs.existsSync(path.join(process.cwd(),o))){let n=1,e=o;for(;fs.existsSync(path.join(process.cwd(),e));)e=`${o}_${n++}`;o=e}console.log(`${chalk.yellow("\n项目名称:")+o}`),console.log(`${chalk.yellow("包名:")+a}\n`);const s=Array.isArray(r.deviceTypeList)&&r.deviceTypeList.length>0?r.deviceTypeList[0]:"phone",l=require("../../lib/commands/init");if(await l(o),replacePage(r,c,o),t.succeed(chalk.green("\n恭喜!您的快应用项目已经创建成功!\n")),!n.test){const n=[{type:"confirm",name:"preview",message:"请问是否进行预览?",prefix:"",default:!0}],e=path.join(process.cwd(),o);inquirer.prompt(n).then((n=>{if(n.preview){if("watch"===s){const n="/home/lvxin/work/gitlab/ai-quickapp/velasim";fsExtra.copySync(n,path.join(e,"node_modules/@aiot-toolkit/velasim"));const{compile:t}=require("../../lib/commands/compile");return void t("native","dev",!0,{cwd:e,openNuttx:!0})}const{launchServer:n}=require("@aiot-toolkit/server"),{compile:t}=require("../../lib/commands/compile");n({watch:!0,cwd:e,openBrowser:!0}),t("native","dev",!0,{cwd:e})}else console.log(`${chalk.green("\n您可以使用以下命令进行开发预览调试:")}`),console.log(`${chalk.cyan(" npm run start")}`),console.log(`${chalk.cyan(" npm run build")}`),console.log(`${chalk.cyan(" npm run watch")}`)}))}return{projectPath:path.join(process.cwd(),o),projectName:o,packageName:a,router:r.router.entry,deviceType:s}}module.exports=ai;
|
|
2
2
|
//# sourceMappingURL=ai.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.startVvd=startVvd;const puppeteer=require("puppeteer"),fs=require("fs"),path=require("path"),chalk=require("chalk"),os=require("os"),pkg=require("@aiot-toolkit/emulator"),{VvdManager:VvdManager,IVvdArchType:IVvdArchType,VelaImageType:VelaImageType}=pkg,ai=require("../../lib/commands/ai"),templates={phone:"做一个{type}手机快应用",watch:"做一个{type}手表快应用",band:"做一个{type}手环快应用"},appTypes={phone:["天气","计算器","滴答清单","音乐播放器","新闻阅读","健身追踪","日历","记账","翻译","食谱"],watch:["心率血氧检测","一键运动计时","手表遥控拍照","地铁公交扫码","噪音环境监测","手电筒(黑白屏模式)","喝水提醒(震动+图标)","快递取件码速览","航班/列车动态提醒","AI语音备忘录"],band:["运动监测","心率监测","睡眠分析","健身计划","秒表/倒计时","女性健康追踪","实时步数PK","睡眠质量报告","血氧快速检测","天气预警图标"]};function generatePrompts(){const e=[];for(const[t,s]of Object.entries(templates))for(const a of appTypes[t])e.push({deviceType:t,prompt:s.replace("{type}",a)});return e}const prompts=generatePrompts(),results=[];let isVelaEmulatorCreated=!1;const sleep=e=>new Promise((t=>setTimeout(t,e)));async function runTest(){console.log(chalk.green(`开始自动化测试,共运行 ${prompts.length} 次...\n`));const e=Date.now();for(let e=0;e<prompts.length;e++){const{deviceType:t,prompt:s}=prompts[e];console.log(chalk.cyan(`\n运行第 ${e+1} 次测试:${s} (${t})`)),"watch"!==t||isVelaEmulatorCreated||(createVelaEmulator(),isVelaEmulatorCreated=!0),console.log(chalk.yellow("正在生成项目..."));const a=Date.now();let n,r,o,c;try{const l=await ai({prompt:s,test:!0}),p=((Date.now()-a)/1e3).toFixed(2)+"s";if(n=l.projectName||`App_${e+1}`,r=l.projectPath||path.join(process.cwd(),n),o=l.packageName||`com.aiot.${n}`,c=l.router||"Demo",!fs.existsSync(r))throw new Error("项目生成失败");console.log(chalk.green(`项目生成成功:${n}`));const i=path.join(process.cwd(),"screenshots");if(fs.existsSync(i)||fs.mkdirSync(i,{recursive:!0}),"watch"===t||"band"===t){const{compile:a}=require("../../lib/commands/compile");a("native","dev",!1,{cwd:r}),await sleep(2e3);const n=path.join(i,`run_${e+1}.png`);await startApp(t,n,path.join(r,`./dist/${o}.debug.1.0.0.rpk`),o)?(console.error(chalk.red(`第 ${e+1} 次测试失败:页面白屏`)),results.push({run:e+1,deviceType:t,prompt:s,success:!1,error:"模拟器启动失败",generationTime:p,screenshot:path.join("screenshots",path.basename(n))})):(console.log(chalk.green(`第 ${e+1} 次测试成功:页面正常加载`)),results.push({run:e+1,deviceType:t,prompt:s,success:!0,generationTime:p,screenshot:path.join("screenshots",path.basename(n))}))}else{const{launchServer:a,stopServer:n}=require("@aiot-toolkit/server");a({watch:!1,cwd:r,port:9999});const{compile:o}=require("../../lib/commands/compile");o("native","dev",!1,{cwd:r}),await sleep(2e3),console.log(chalk.yellow("启动浏览器进行预览..."));const l=await puppeteer.launch(),h=await l.newPage();try{const a={name:"iPhone X",userAgent:"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1",viewport:{width:375,height:812,deviceScaleFactor:3,isMobile:!0,hasTouch:!0,isLandscape:!1}};await h.emulate(a);const r=`http://localhost:9999/preview/${c}`;await h.goto(r,{waitUntil:"load",timeout:6e4}),await sleep(3e3);const o=await h.evaluate((()=>{const e=document.querySelector("body");return e&&""===e.innerHTML.trim()})),d=path.join(i,`run_${e+1}.png`);await h.screenshot({path:d}),o?(console.error(chalk.red(`第 ${e+1} 次测试失败:页面白屏`)),results.push({run:e+1,deviceType:t,prompt:s,success:!1,error:"页面白屏",generationTime:p,screenshot:path.join("screenshots",path.basename(d))})):(console.log(chalk.green(`第 ${e+1} 次测试成功:页面正常加载`)),results.push({run:e+1,deviceType:t,prompt:s,success:!0,generationTime:p,screenshot:path.join("screenshots",path.basename(d))}))}catch(a){console.error(chalk.red(`第 ${e+1} 次测试失败:${a.message}`)),results.push({run:e+1,deviceType:t,prompt:s,success:!1,error:a.message,generationTime:p})}finally{await l.close(),n()}}}catch(a){console.error(chalk.red(`第 ${e+1} 次测试失败:${a.message}`)),results.push({run:e+1,deviceType:t,prompt:s,success:!1,error:a.message})}await sleep(2e3),r&&fs.existsSync(r)&&(console.log(chalk.yellow(`清理生成的项目:${n}`)),fs.rmSync(r,{recursive:!0,force:!0}))}generateReport(((Date.now()-e)/1e3).toFixed(2))}function generateReport(e){console.log(chalk.green("\n测试完成,生成测试报告...\n"));const t=path.join(process.cwd(),"test-report.html"),s=results.filter((e=>e.success)).length,a=results.length-s;let n=`\n <html>\n <head>\n <title>快应用智能编程助手生成项目测试报告</title>\n <style>\n body { font-family: Arial, sans-serif; margin: 20px; }\n table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }\n th, td { border: 1px solid #ddd; padding: 8px; text-align: center; }\n th { background-color: #f4f4f4; }\n .success { color: green; }\n .failure { color: red; }\n img { max-width: 200px; }\n </style>\n </head>\n <body>\n <h1>快应用智能编程助手生成项目测试报告</h1>\n <p>总运行次数:${prompts.length}</p>\n <p>成功次数:<span class="success">${s}</span></p>\n <p>失败次数:<span class="failure">${a}</span></p>\n <p>总耗时:<span>${e} 秒</span></p>\n <table>\n <thead>\n <tr>\n <th>运行次数</th>\n <th>设备类型</th>\n <th>提示词</th>\n <th>生成时间 (ms)</th>\n <th>结果</th>\n <th>错误信息</th>\n <th>首屏截图</th>\n </tr>\n </thead>\n <tbody>\n `;results.forEach((e=>{n+=`\n <tr>\n <td>${e.run}</td>\n <td>${e.deviceType}</td>\n <td>${e.prompt}</td>\n <td>${e.generationTime||"N/A"}</td>\n <td class="${e.success?"success":"failure"}">\n ${e.success?"成功":"失败"}\n </td>\n <td>${e.error||"N/A"}</td>\n <td>\n ${e.screenshot?`<img src="${e.screenshot}" alt="截图">`:"N/A"}\n </td>\n </tr>\n `})),n+="\n </tbody>\n </table>\n </body>\n </html>\n ",fs.writeFileSync(t,n,"utf8"),console.log(chalk.green(`测试报告已生成:${t}`))}let velaAvdCls=null,emulatorInstance1=null,getAgent1=null,emulatorInstance2=null,getAgent2=null;async function createVelaEmulator(){velaAvdCls=new VvdManager({sdkHome:path.resolve(os.homedir(),".export_dev")}),velaAvdCls.createVvd({name:"O62",arch:IVvdArchType.arm,height:"466",width:"466",imageDir:path.resolve(os.homedir(),".export_dev/system-images/vela-miwear-watch-5.0"),imageType:VelaImageType.VELA_MIWEAR_WATCH_5}),velaAvdCls.createVvd({name:"N66",arch:IVvdArchType.arm,height:"490",width:"192",imageDir:path.resolve(os.homedir(),".export_dev/system-images/vela-miwear-watch-5.0"),imageType:VelaImageType.VELA_MIWEAR_WATCH_5}),await sleep(2e3);const{emulator:e,agent:t}=await startVvd("O62");emulatorInstance1=e,getAgent1=t;const{emulator:s,agent:a}=await startVvd("N66");emulatorInstance2=s,getAgent2=a}async function startVvd(e){const{emulatorInstance:t,getAgent:s}=await velaAvdCls.startVvd({vvdName:e});return{emulator:t,agent:s}}async function startApp(e,t,s,a){let n=!1;try{const n="watch"===e?emulatorInstance1:emulatorInstance2,r="watch"===e?getAgent1:getAgent2;await n.pushAndInstall(s,a),await n.startApp(a,!1);const o=await r();await sleep(2e3);const c=await o.getScreenshot();fs.writeFileSync(t,c)}catch(e){n=!0,console.error(chalk.red(`启动应用失败:${e.message}`))}return n}runTest().then((()=>{velaAvdCls.stopVvd("O62"),velaAvdCls.stopVvd("N66")})).catch((e=>{console.error(chalk.red("测试过程中发生错误:"),e)})).finally((()=>{process.exit(0)}));
|
|
2
|
+
//# sourceMappingURL=test.js.map
|
package/package.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aiot-toolkit",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.4",
|
|
4
4
|
"description": "A command line toolkit for developing Aiot Quick Apps.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=8.0.0"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@aiot-toolkit/compiler": "1.2.0-alpha.
|
|
10
|
-
"@aiot-toolkit/debugger": "1.2.0-alpha.
|
|
11
|
-
"@aiot-toolkit/dsl-vue": "1.2.0-alpha.
|
|
12
|
-
"@aiot-toolkit/dsl-xvm": "1.2.0-alpha.
|
|
13
|
-
"@aiot-toolkit/
|
|
14
|
-
"@aiot-toolkit/
|
|
15
|
-
"@aiot-toolkit/
|
|
9
|
+
"@aiot-toolkit/compiler": "1.2.0-alpha.4",
|
|
10
|
+
"@aiot-toolkit/debugger": "1.2.0-alpha.4",
|
|
11
|
+
"@aiot-toolkit/dsl-vue": "1.2.0-alpha.4",
|
|
12
|
+
"@aiot-toolkit/dsl-xvm": "1.2.0-alpha.4",
|
|
13
|
+
"@aiot-toolkit/emulator": "^2.0.5-beta.13",
|
|
14
|
+
"@aiot-toolkit/packager": "1.2.0-alpha.4",
|
|
15
|
+
"@aiot-toolkit/server": "1.2.0-alpha.4",
|
|
16
|
+
"@aiot-toolkit/shared-utils": "1.2.0-alpha.4",
|
|
16
17
|
"@babel/core": "^7.9.6",
|
|
17
18
|
"@babel/plugin-syntax-jsx": "^7.8.3",
|
|
18
19
|
"@babel/preset-env": "^7.9.6",
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
"jszip": "^3.4.0",
|
|
31
32
|
"lodash": "^4.17.21",
|
|
32
33
|
"ora": "^5.4.1",
|
|
34
|
+
"puppeteer": "^24.6.1",
|
|
33
35
|
"request": "^2.88.2",
|
|
34
36
|
"semver": "^5.7.1",
|
|
35
37
|
"socket.io": "^2.3.0",
|
|
@@ -54,5 +56,5 @@
|
|
|
54
56
|
"supertest": "^3.3.0",
|
|
55
57
|
"webpack-cli": "^4.3.0"
|
|
56
58
|
},
|
|
57
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "e2c4c751558bd88700225b7f9fc36aa8351ad04a"
|
|
58
60
|
}
|