@kmlckj/licos-ai-cli 1.0.18 → 1.0.20

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.
Files changed (27) hide show
  1. package/lib/__templates__/expo/.licosproj/scripts/dev_build.sh +4 -1
  2. package/lib/__templates__/expo/client/app.config.ts +87 -87
  3. package/lib/__templates__/expo/package.json +1 -1
  4. package/lib/__templates__/expo/template.config.js +4 -7
  5. package/lib/__templates__/nextjs/next.config.ts +35 -35
  6. package/lib/__templates__/nextjs/package.json +1 -1
  7. package/lib/__templates__/nextjs/pnpm-lock.yaml +11549 -12072
  8. package/lib/__templates__/nextjs/scripts/prepare.sh +4 -1
  9. package/lib/__templates__/nextjs/src/server.ts +129 -129
  10. package/lib/__templates__/nextjs/template.config.js +4 -7
  11. package/lib/__templates__/nuxt-vue/nuxt.config.ts +180 -180
  12. package/lib/__templates__/nuxt-vue/package.json +1 -1
  13. package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +8586 -8283
  14. package/lib/__templates__/nuxt-vue/scripts/prepare.sh +4 -1
  15. package/lib/__templates__/taro/.licosproj/scripts/dev_build.sh +4 -1
  16. package/lib/__templates__/taro/.licosproj/scripts/dev_run.sh +4 -1
  17. package/lib/__templates__/taro/config/index.ts +352 -352
  18. package/lib/__templates__/taro/package.json +2 -2
  19. package/lib/__templates__/taro/pnpm-lock.yaml +20848 -21212
  20. package/lib/__templates__/vite/package.json +1 -1
  21. package/lib/__templates__/vite/scripts/prepare.sh +4 -1
  22. package/lib/__templates__/vite/server/server.ts +1 -1
  23. package/lib/__templates__/vite/server/vite.ts +11 -3
  24. package/lib/__templates__/vite/template.config.js +4 -7
  25. package/lib/__templates__/vite/vite.config.ts +64 -64
  26. package/lib/cli.js +1 -1
  27. package/package.json +1 -1
@@ -6,4 +6,7 @@ PROJECT_DIR="${LICOS_PROJECT_PATH:-${LICOS_WORKSPACE_PATH:-$(pwd)}}"
6
6
  cd "${PROJECT_DIR}"
7
7
 
8
8
  echo "Installing dependencies..."
9
- pnpm install --registry=https://registry.npmmirror.com --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only
9
+ pnpm install --registry=https://registry.npmmirror.com --prefer-frozen-lockfile --prefer-offline --loglevel debug --reporter=append-only || {
10
+ echo "Primary registry install failed; retrying with npmjs registry..."
11
+ pnpm install --registry=https://registry.npmjs.org --no-frozen-lockfile --reporter=append-only
12
+ }
@@ -1,129 +1,129 @@
1
- import { createServer, type ServerResponse } from 'http';
2
- import { createReadStream, existsSync, statSync } from 'fs';
3
- import { extname, resolve, sep } from 'path';
4
- import { parse } from 'url';
5
- import next from 'next';
6
-
7
- function isProdProjectEnv(value?: string): boolean {
8
- const normalized = value?.trim().toLowerCase();
9
- return normalized === 'prod' || normalized === 'production' || normalized === 'release';
10
- }
11
-
12
- const dev = !isProdProjectEnv(process.env.LICOS_PROJECT_ENV);
13
- const hostname = process.env.HOSTNAME || 'localhost';
14
- const port = parseInt(process.env.PORT || '<%= port %>', 10);
15
- const projectAssetsDir = resolve(process.cwd(), 'assets');
16
-
17
- function normalizeBasePath(value?: string): string | undefined {
18
- const trimmed = value?.trim();
19
- if (!trimmed || trimmed === '/') return undefined;
20
- return `/${trimmed.replace(/^\/+|\/+$/g, '')}`;
21
- }
22
-
23
- const basePath = dev ? undefined : normalizeBasePath(process.env.BASE_PATH);
24
-
25
- function getContentType(filePath: string): string {
26
- const ext = extname(filePath).toLowerCase();
27
- const types: Record<string, string> = {
28
- '.avif': 'image/avif',
29
- '.css': 'text/css; charset=utf-8',
30
- '.gif': 'image/gif',
31
- '.html': 'text/html; charset=utf-8',
32
- '.ico': 'image/x-icon',
33
- '.jpg': 'image/jpeg',
34
- '.jpeg': 'image/jpeg',
35
- '.js': 'text/javascript; charset=utf-8',
36
- '.json': 'application/json; charset=utf-8',
37
- '.map': 'application/json; charset=utf-8',
38
- '.mp3': 'audio/mpeg',
39
- '.mp4': 'video/mp4',
40
- '.otf': 'font/otf',
41
- '.png': 'image/png',
42
- '.svg': 'image/svg+xml',
43
- '.ttf': 'font/ttf',
44
- '.txt': 'text/plain; charset=utf-8',
45
- '.wasm': 'application/wasm',
46
- '.webm': 'video/webm',
47
- '.webp': 'image/webp',
48
- '.woff': 'font/woff',
49
- '.woff2': 'font/woff2',
50
- '.xml': 'application/xml; charset=utf-8',
51
- };
52
- return types[ext] || 'application/octet-stream';
53
- }
54
-
55
- function normalizeAssetPath(pathname: string): string | null {
56
- if (pathname.startsWith('/assets/')) {
57
- return pathname;
58
- }
59
- if (basePath && pathname.startsWith(`${basePath}/assets/`)) {
60
- return pathname.slice(basePath.length);
61
- }
62
- return null;
63
- }
64
-
65
- function serveProjectAsset(pathname: string, res: ServerResponse): boolean {
66
- const assetPath = normalizeAssetPath(pathname);
67
- if (!assetPath || !existsSync(projectAssetsDir)) {
68
- return false;
69
- }
70
-
71
- let decodedPath: string;
72
- try {
73
- decodedPath = decodeURIComponent(assetPath.replace(/^\/assets\/+/, ''));
74
- } catch {
75
- return false;
76
- }
77
-
78
- const filePath = resolve(projectAssetsDir, decodedPath);
79
- if (filePath !== projectAssetsDir && !filePath.startsWith(`${projectAssetsDir}${sep}`)) {
80
- res.statusCode = 403;
81
- res.end('Forbidden');
82
- return true;
83
- }
84
-
85
- try {
86
- const stat = statSync(filePath);
87
- if (!stat.isFile()) {
88
- return false;
89
- }
90
- res.statusCode = 200;
91
- res.setHeader('Content-Type', getContentType(filePath));
92
- res.setHeader('Content-Length', stat.size);
93
- createReadStream(filePath).pipe(res);
94
- return true;
95
- } catch {
96
- return false;
97
- }
98
- }
99
-
100
- // Create Next.js app
101
- const app = next({ dev, hostname, port });
102
- const handle = app.getRequestHandler();
103
-
104
- app.prepare().then(() => {
105
- const server = createServer(async (req, res) => {
106
- try {
107
- const parsedUrl = parse(req.url!, true);
108
- if (serveProjectAsset(parsedUrl.pathname || '', res)) {
109
- return;
110
- }
111
- await handle(req, res, parsedUrl);
112
- } catch (err) {
113
- console.error('Error occurred handling', req.url, err);
114
- res.statusCode = 500;
115
- res.end('Internal server error');
116
- }
117
- });
118
- server.once('error', err => {
119
- console.error(err);
120
- process.exit(1);
121
- });
122
- server.listen(port, () => {
123
- console.log(
124
- `> Server listening at http://${hostname}:${port} as ${
125
- dev ? 'development' : process.env.LICOS_PROJECT_ENV
126
- }`,
127
- );
128
- });
129
- });
1
+ import { createServer, type ServerResponse } from 'http';
2
+ import { createReadStream, existsSync, statSync } from 'fs';
3
+ import { extname, resolve, sep } from 'path';
4
+ import { parse } from 'url';
5
+ import next from 'next';
6
+
7
+ function isProdProjectEnv(value?: string): boolean {
8
+ const normalized = value?.trim().toLowerCase();
9
+ return normalized === 'prod' || normalized === 'production' || normalized === 'release';
10
+ }
11
+
12
+ const dev = !isProdProjectEnv(process.env.LICOS_PROJECT_ENV);
13
+ const hostname = process.env.HOSTNAME || 'localhost';
14
+ const port = parseInt(process.env.PORT || '<%= port %>', 10);
15
+ const projectAssetsDir = resolve(process.cwd(), 'assets');
16
+
17
+ function normalizeBasePath(value?: string): string | undefined {
18
+ const trimmed = value?.trim();
19
+ if (!trimmed || trimmed === '/') return undefined;
20
+ return `/${trimmed.replace(/^\/+|\/+$/g, '')}`;
21
+ }
22
+
23
+ const basePath = dev ? undefined : normalizeBasePath(process.env.BASE_PATH);
24
+
25
+ function getContentType(filePath: string): string {
26
+ const ext = extname(filePath).toLowerCase();
27
+ const types: Record<string, string> = {
28
+ '.avif': 'image/avif',
29
+ '.css': 'text/css; charset=utf-8',
30
+ '.gif': 'image/gif',
31
+ '.html': 'text/html; charset=utf-8',
32
+ '.ico': 'image/x-icon',
33
+ '.jpg': 'image/jpeg',
34
+ '.jpeg': 'image/jpeg',
35
+ '.js': 'text/javascript; charset=utf-8',
36
+ '.json': 'application/json; charset=utf-8',
37
+ '.map': 'application/json; charset=utf-8',
38
+ '.mp3': 'audio/mpeg',
39
+ '.mp4': 'video/mp4',
40
+ '.otf': 'font/otf',
41
+ '.png': 'image/png',
42
+ '.svg': 'image/svg+xml',
43
+ '.ttf': 'font/ttf',
44
+ '.txt': 'text/plain; charset=utf-8',
45
+ '.wasm': 'application/wasm',
46
+ '.webm': 'video/webm',
47
+ '.webp': 'image/webp',
48
+ '.woff': 'font/woff',
49
+ '.woff2': 'font/woff2',
50
+ '.xml': 'application/xml; charset=utf-8',
51
+ };
52
+ return types[ext] || 'application/octet-stream';
53
+ }
54
+
55
+ function normalizeAssetPath(pathname: string): string | null {
56
+ if (pathname.startsWith('/assets/')) {
57
+ return pathname;
58
+ }
59
+ if (basePath && pathname.startsWith(`${basePath}/assets/`)) {
60
+ return pathname.slice(basePath.length);
61
+ }
62
+ return null;
63
+ }
64
+
65
+ function serveProjectAsset(pathname: string, res: ServerResponse): boolean {
66
+ const assetPath = normalizeAssetPath(pathname);
67
+ if (!assetPath || !existsSync(projectAssetsDir)) {
68
+ return false;
69
+ }
70
+
71
+ let decodedPath: string;
72
+ try {
73
+ decodedPath = decodeURIComponent(assetPath.replace(/^\/assets\/+/, ''));
74
+ } catch {
75
+ return false;
76
+ }
77
+
78
+ const filePath = resolve(projectAssetsDir, decodedPath);
79
+ if (filePath !== projectAssetsDir && !filePath.startsWith(`${projectAssetsDir}${sep}`)) {
80
+ res.statusCode = 403;
81
+ res.end('Forbidden');
82
+ return true;
83
+ }
84
+
85
+ try {
86
+ const stat = statSync(filePath);
87
+ if (!stat.isFile()) {
88
+ return false;
89
+ }
90
+ res.statusCode = 200;
91
+ res.setHeader('Content-Type', getContentType(filePath));
92
+ res.setHeader('Content-Length', stat.size);
93
+ createReadStream(filePath).pipe(res);
94
+ return true;
95
+ } catch {
96
+ return false;
97
+ }
98
+ }
99
+
100
+ // Create Next.js app
101
+ const app = next({ dev, hostname, port });
102
+ const handle = app.getRequestHandler();
103
+
104
+ app.prepare().then(() => {
105
+ const server = createServer(async (req, res) => {
106
+ try {
107
+ const parsedUrl = parse(req.url!, true);
108
+ if (serveProjectAsset(parsedUrl.pathname || '', res)) {
109
+ return;
110
+ }
111
+ await handle(req, res, parsedUrl);
112
+ } catch (err) {
113
+ console.error('Error occurred handling', req.url, err);
114
+ res.statusCode = 500;
115
+ res.end('Internal server error');
116
+ }
117
+ });
118
+ server.once('error', err => {
119
+ console.error(err);
120
+ process.exit(1);
121
+ });
122
+ server.listen(port, () => {
123
+ console.log(
124
+ `> Server listening at http://${hostname}:${port} as ${
125
+ dev ? 'development' : process.env.LICOS_PROJECT_ENV
126
+ }`,
127
+ );
128
+ });
129
+ });
@@ -76,13 +76,10 @@ const config = {
76
76
  return;
77
77
  }
78
78
 
79
- const cmd = 'pnpm';
79
+ const cmd = 'bash';
80
80
  const args = [
81
- 'install',
82
- '--registry=https://registry.npmmirror.com',
83
- '--prefer-frozen-lockfile',
84
- '--prefer-offline',
85
- '--reporter=append-only',
81
+ '-lc',
82
+ 'pnpm install --registry=https://registry.npmmirror.com --prefer-frozen-lockfile --prefer-offline --reporter=append-only || pnpm install --registry=https://registry.npmjs.org --no-frozen-lockfile --reporter=append-only',
86
83
  ];
87
84
  console.log(
88
85
  `\nTriggering: ${cmd} ${args.join(' ')} (running in background)`,
@@ -118,7 +115,7 @@ const config = {
118
115
  console.log(` Log file: ${logFile}`);
119
116
  } catch (error) {
120
117
  console.error('✗ Failed to trigger pnpm install:', error);
121
- console.log(' You can manually run: pnpm install --registry=https://registry.npmmirror.com');
118
+ console.log(' You can manually run: pnpm install --registry=https://registry.npmmirror.com || pnpm install --registry=https://registry.npmjs.org --no-frozen-lockfile');
122
119
  }
123
120
  },
124
121
  };
@@ -1,180 +1,180 @@
1
- // https://nuxt.com/docs/api/configuration/nuxt-config
2
- import { fileURLToPath } from 'url';
3
- import { dirname, resolve } from 'path';
4
-
5
- const __filename = fileURLToPath(import.meta.url);
6
- const __dirname = dirname(__filename);
7
-
8
- function normalizeBaseURL(value?: string): string {
9
- const trimmed = value?.trim();
10
- if (!trimmed || trimmed === '/') return '/';
11
- return `/${trimmed.replace(/^\/+|\/+$/g, '')}/`;
12
- }
13
-
14
- function parsePort(value?: string): number | undefined {
15
- const port = Number.parseInt(value || '', 10);
16
- return Number.isInteger(port) && port > 0 ? port : undefined;
17
- }
18
-
19
- function createPreviewHmrConfig() {
20
- if (!process.env.LICOS_PREVIEW_BASE_PATH) {
21
- return undefined;
22
- }
23
- const hmr: Record<string, unknown> = {
24
- overlay: true,
25
- path: process.env.LICOS_PREVIEW_HMR_PATH || '/hot/vite-hmr',
26
- timeout: 30000,
27
- };
28
- if (process.env.LICOS_PREVIEW_HMR_HOST) {
29
- hmr.host = process.env.LICOS_PREVIEW_HMR_HOST;
30
- }
31
- if (process.env.LICOS_PREVIEW_HMR_PROTOCOL) {
32
- hmr.protocol = process.env.LICOS_PREVIEW_HMR_PROTOCOL;
33
- }
34
- const clientPort = parsePort(process.env.LICOS_PREVIEW_HMR_CLIENT_PORT);
35
- if (clientPort) {
36
- hmr.clientPort = clientPort;
37
- }
38
- return hmr;
39
- }
40
-
41
- function isProdProjectEnv(value?: string): boolean {
42
- const normalized = value?.trim().toLowerCase();
43
- return normalized === 'prod' || normalized === 'production' || normalized === 'release';
44
- }
45
-
46
- const isProd = isProdProjectEnv(process.env.LICOS_PROJECT_ENV);
47
- const deployBaseURL = process.env.NUXT_PUBLIC_BASE_URL || process.env.BASE_PATH;
48
- const shouldUseBaseURL = Boolean(deployBaseURL) || isProd;
49
-
50
- const publicBaseURL = shouldUseBaseURL ? normalizeBaseURL(deployBaseURL) : '/';
51
- const publicAssetCdnURL = publicBaseURL === '/' ? '' : publicBaseURL.replace(/\/$/, '');
52
- const previewHmrConfig = createPreviewHmrConfig();
53
-
54
- export default defineNuxtConfig({
55
- compatibilityDate: '2025-07-15',
56
- // Disable devtools in CI/test environments (prevents hanging in headless mode)
57
- devtools: {
58
- enabled: process.env.CI !== 'true' && !isProd,
59
- },
60
- telemetry: false,
61
-
62
- // App head configuration
63
- app: {
64
- // The cluster ingress rewrites /p/{projectId}/... to backend /...
65
- // Keep Nuxt's server asset route at /_nuxt and prefix browser URLs via cdnURL.
66
- baseURL: '/',
67
- buildAssetsDir: '/_nuxt/',
68
- cdnURL: publicAssetCdnURL,
69
- head: {
70
- title: '新应用 | LICOS AI',
71
- titleTemplate: '%s | LICOS AI',
72
- meta: [
73
- { charset: 'utf-8' },
74
- { name: 'viewport', content: 'width=device-width, initial-scale=1' },
75
- {
76
- name: 'description',
77
- content:
78
- 'LICOS AI是一款一站式云端 Vibe Coding 开发平台。通过对话轻松构建智能体、工作流和网站,实现从创意到上线的无缝衔接。',
79
- },
80
- {
81
- name: 'keywords',
82
- content:
83
- 'LICOS AI,LICOS AI,Vibe Coding,AI 编程,智能体搭建,工作流搭建,网站搭建,网站部署,全栈开发,AI 工程师',
84
- },
85
- { name: 'author', content: 'LICOS Team' },
86
- { name: 'generator', content: 'LICOS AI' },
87
- // Open Graph
88
- { property: 'og:title', content: 'LICOS AI | 你的 AI 工程师已就位' },
89
- {
90
- property: 'og:description',
91
- content:
92
- '我正在使用LICOS AI Vibe Coding,让创意瞬间上线。告别拖拽,拥抱心流。',
93
- },
94
- { property: 'og:url', content: 'https://licos.ai' },
95
- { property: 'og:site_name', content: 'LICOS AI' },
96
- { property: 'og:locale', content: 'zh_CN' },
97
- { property: 'og:type', content: 'website' },
98
- // Robots
99
- { name: 'robots', content: 'index, follow' },
100
- ],
101
- link: [{ rel: 'canonical', href: 'https://licos.ai' }],
102
- htmlAttrs: {
103
- lang: 'zh-CN',
104
- },
105
- },
106
- },
107
-
108
- // Nuxt modules
109
- modules: ['@nuxt/image', '@nuxtjs/tailwindcss'],
110
-
111
- // Global CSS
112
- css: [resolve(__dirname, 'assets/css/main.css')],
113
-
114
- runtimeConfig: {
115
- public: {
116
- baseURL: publicBaseURL,
117
- },
118
- },
119
-
120
- // Development server configuration
121
- devServer: {
122
- port: parseInt(process.env.PORT || '<%= port %>', 10),
123
- host: process.env.HOST || '0.0.0.0',
124
- },
125
-
126
- // TypeScript configuration
127
- typescript: {
128
- strict: false,
129
- typeCheck: false,
130
- },
131
-
132
- // Vite configuration (similar to Next.js allowedDevOrigins)
133
- vite: {
134
- server: {
135
- host: '0.0.0.0',
136
- cors: {
137
- origin: ['*.dev.licos.local'],
138
- credentials: true,
139
- },
140
- hmr: previewHmrConfig || {
141
- overlay: true,
142
- path: '/hot/vite-hmr',
143
- port: parseInt(process.env.HMR_PORT || '<%= hmrPort %>', 10),
144
- clientPort: 443,
145
- timeout: 30000,
146
- },
147
- // Fix EMFILE: too many open files error
148
- // Exclude large directories from file watching to avoid exceeding system limits
149
- watch: {
150
- ignored: [
151
- '**/node_modules/**',
152
- '**/.nuxt/**',
153
- '**/.output/**',
154
- '**/dist/**',
155
- '**/.git/**',
156
- '**/coverage/**',
157
- '**/.cache/**',
158
- ],
159
- },
160
- },
161
- },
162
-
163
- // Security headers (Content Security Policy)
164
- nitro: {
165
- publicAssets: [
166
- {
167
- baseURL: '/assets',
168
- dir: resolve(__dirname, 'assets'),
169
- },
170
- ],
171
- routeRules: {
172
- '/**': {
173
- headers: {
174
- 'Content-Security-Policy':
175
- "img-src 'self' data:;",
176
- },
177
- },
178
- },
179
- },
180
- });
1
+ // https://nuxt.com/docs/api/configuration/nuxt-config
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, resolve } from 'path';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+
8
+ function normalizeBaseURL(value?: string): string {
9
+ const trimmed = value?.trim();
10
+ if (!trimmed || trimmed === '/') return '/';
11
+ return `/${trimmed.replace(/^\/+|\/+$/g, '')}/`;
12
+ }
13
+
14
+ function parsePort(value?: string): number | undefined {
15
+ const port = Number.parseInt(value || '', 10);
16
+ return Number.isInteger(port) && port > 0 ? port : undefined;
17
+ }
18
+
19
+ function createPreviewHmrConfig() {
20
+ if (!process.env.LICOS_PREVIEW_BASE_PATH) {
21
+ return undefined;
22
+ }
23
+ const hmr: Record<string, unknown> = {
24
+ overlay: true,
25
+ path: process.env.LICOS_PREVIEW_HMR_PATH || '/hot/vite-hmr',
26
+ timeout: 30000,
27
+ };
28
+ if (process.env.LICOS_PREVIEW_HMR_HOST) {
29
+ hmr.host = process.env.LICOS_PREVIEW_HMR_HOST;
30
+ }
31
+ if (process.env.LICOS_PREVIEW_HMR_PROTOCOL) {
32
+ hmr.protocol = process.env.LICOS_PREVIEW_HMR_PROTOCOL;
33
+ }
34
+ const clientPort = parsePort(process.env.LICOS_PREVIEW_HMR_CLIENT_PORT);
35
+ if (clientPort) {
36
+ hmr.clientPort = clientPort;
37
+ }
38
+ return hmr;
39
+ }
40
+
41
+ function isProdProjectEnv(value?: string): boolean {
42
+ const normalized = value?.trim().toLowerCase();
43
+ return normalized === 'prod' || normalized === 'production' || normalized === 'release';
44
+ }
45
+
46
+ const isProd = isProdProjectEnv(process.env.LICOS_PROJECT_ENV);
47
+ const deployBaseURL = process.env.NUXT_PUBLIC_BASE_URL || process.env.BASE_PATH;
48
+ const shouldUseBaseURL = Boolean(deployBaseURL) || isProd;
49
+
50
+ const publicBaseURL = shouldUseBaseURL ? normalizeBaseURL(deployBaseURL) : '/';
51
+ const publicAssetCdnURL = publicBaseURL === '/' ? '' : publicBaseURL.replace(/\/$/, '');
52
+ const previewHmrConfig = createPreviewHmrConfig();
53
+
54
+ export default defineNuxtConfig({
55
+ compatibilityDate: '2025-07-15',
56
+ // Disable devtools in CI/test environments (prevents hanging in headless mode)
57
+ devtools: {
58
+ enabled: process.env.CI !== 'true' && !isProd,
59
+ },
60
+ telemetry: false,
61
+
62
+ // App head configuration
63
+ app: {
64
+ // The cluster ingress rewrites /p/{projectId}/... to backend /...
65
+ // Keep Nuxt's server asset route at /_nuxt and prefix browser URLs via cdnURL.
66
+ baseURL: '/',
67
+ buildAssetsDir: '/_nuxt/',
68
+ cdnURL: publicAssetCdnURL,
69
+ head: {
70
+ title: '新应用 | LICOS AI',
71
+ titleTemplate: '%s | LICOS AI',
72
+ meta: [
73
+ { charset: 'utf-8' },
74
+ { name: 'viewport', content: 'width=device-width, initial-scale=1' },
75
+ {
76
+ name: 'description',
77
+ content:
78
+ 'LICOS AI是一款一站式云端 Vibe Coding 开发平台。通过对话轻松构建智能体、工作流和网站,实现从创意到上线的无缝衔接。',
79
+ },
80
+ {
81
+ name: 'keywords',
82
+ content:
83
+ 'LICOS AI,LICOS AI,Vibe Coding,AI 编程,智能体搭建,工作流搭建,网站搭建,网站部署,全栈开发,AI 工程师',
84
+ },
85
+ { name: 'author', content: 'LICOS Team' },
86
+ { name: 'generator', content: 'LICOS AI' },
87
+ // Open Graph
88
+ { property: 'og:title', content: 'LICOS AI | 你的 AI 工程师已就位' },
89
+ {
90
+ property: 'og:description',
91
+ content:
92
+ '我正在使用LICOS AI Vibe Coding,让创意瞬间上线。告别拖拽,拥抱心流。',
93
+ },
94
+ { property: 'og:url', content: 'https://licos.ai' },
95
+ { property: 'og:site_name', content: 'LICOS AI' },
96
+ { property: 'og:locale', content: 'zh_CN' },
97
+ { property: 'og:type', content: 'website' },
98
+ // Robots
99
+ { name: 'robots', content: 'index, follow' },
100
+ ],
101
+ link: [{ rel: 'canonical', href: 'https://licos.ai' }],
102
+ htmlAttrs: {
103
+ lang: 'zh-CN',
104
+ },
105
+ },
106
+ },
107
+
108
+ // Nuxt modules
109
+ modules: ['@nuxt/image', '@nuxtjs/tailwindcss'],
110
+
111
+ // Global CSS
112
+ css: [resolve(__dirname, 'assets/css/main.css')],
113
+
114
+ runtimeConfig: {
115
+ public: {
116
+ baseURL: publicBaseURL,
117
+ },
118
+ },
119
+
120
+ // Development server configuration
121
+ devServer: {
122
+ port: parseInt(process.env.PORT || '<%= port %>', 10),
123
+ host: process.env.HOST || '0.0.0.0',
124
+ },
125
+
126
+ // TypeScript configuration
127
+ typescript: {
128
+ strict: false,
129
+ typeCheck: false,
130
+ },
131
+
132
+ // Vite configuration (similar to Next.js allowedDevOrigins)
133
+ vite: {
134
+ server: {
135
+ host: '0.0.0.0',
136
+ cors: {
137
+ origin: ['*.dev.licos.local'],
138
+ credentials: true,
139
+ },
140
+ hmr: previewHmrConfig || {
141
+ overlay: true,
142
+ path: '/hot/vite-hmr',
143
+ port: parseInt(process.env.HMR_PORT || '<%= hmrPort %>', 10),
144
+ clientPort: 443,
145
+ timeout: 30000,
146
+ },
147
+ // Fix EMFILE: too many open files error
148
+ // Exclude large directories from file watching to avoid exceeding system limits
149
+ watch: {
150
+ ignored: [
151
+ '**/node_modules/**',
152
+ '**/.nuxt/**',
153
+ '**/.output/**',
154
+ '**/dist/**',
155
+ '**/.git/**',
156
+ '**/coverage/**',
157
+ '**/.cache/**',
158
+ ],
159
+ },
160
+ },
161
+ },
162
+
163
+ // Security headers (Content Security Policy)
164
+ nitro: {
165
+ publicAssets: [
166
+ {
167
+ baseURL: '/assets',
168
+ dir: resolve(__dirname, 'assets'),
169
+ },
170
+ ],
171
+ routeRules: {
172
+ '/**': {
173
+ headers: {
174
+ 'Content-Security-Policy':
175
+ "img-src 'self' data:;",
176
+ },
177
+ },
178
+ },
179
+ },
180
+ });
@@ -6,7 +6,7 @@
6
6
  "scripts": {
7
7
  "build": "bash ./scripts/build.sh",
8
8
  "dev": "bash ./scripts/dev.sh",
9
- "ensure:deps": "node -e \"const fs=require('fs'); const bins=['node_modules/.bin/nuxt']; process.exit(bins.every((p)=>fs.existsSync(p)||fs.existsSync(p+'.cmd'))?0:1)\" || pnpm install --registry=https://registry.npmmirror.com --prefer-frozen-lockfile --prefer-offline --reporter=append-only",
9
+ "ensure:deps": "node -e \"const fs=require('fs'); const bins=['node_modules/.bin/nuxt']; process.exit(bins.every((p)=>fs.existsSync(p)||fs.existsSync(p+'.cmd'))?0:1)\" || pnpm install --registry=https://registry.npmmirror.com --prefer-frozen-lockfile --prefer-offline --reporter=append-only || pnpm install --registry=https://registry.npmjs.org --no-frozen-lockfile --reporter=append-only",
10
10
  "generate": "nuxt generate",
11
11
  "prebuild": "pnpm run ensure:deps",
12
12
  "predev": "pnpm run ensure:deps",