@minnai/create-aura-app 0.0.2
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/dist/index.js +108 -0
- package/dist/scaffold.js +61 -0
- package/package.json +36 -0
- package/templates/blank/index.html +12 -0
- package/templates/blank/package.json +23 -0
- package/templates/blank/src/App.tsx +21 -0
- package/templates/blank/src/index.css +40 -0
- package/templates/blank/src/main.tsx +10 -0
- package/templates/blank/tsconfig.json +31 -0
- package/templates/blank/tsconfig.node.json +12 -0
- package/templates/blank/vite.config.ts +7 -0
- package/templates/starter/.env.example +14 -0
- package/templates/starter/README.md +31 -0
- package/templates/starter/_gitignore +24 -0
- package/templates/starter/aura.config.ts +19 -0
- package/templates/starter/eslint.config.js +23 -0
- package/templates/starter/index.html +16 -0
- package/templates/starter/package.json +37 -0
- package/templates/starter/public/favicon.png +0 -0
- package/templates/starter/public/usd.json +9 -0
- package/templates/starter/public/vite.svg +1 -0
- package/templates/starter/src/App.css +32 -0
- package/templates/starter/src/App.tsx +82 -0
- package/templates/starter/src/ambiance/currency-air/index.tsx +25 -0
- package/templates/starter/src/ambiance/currency-air/logic.ts +49 -0
- package/templates/starter/src/ambiance/currency-air/manifest.ts +15 -0
- package/templates/starter/src/ambiance/currency-air/resources.ts +16 -0
- package/templates/starter/src/ambiance/currency-air/ui/index.tsx +42 -0
- package/templates/starter/src/ambiance/index.ts +48 -0
- package/templates/starter/src/ambiance/stocks-air/index.ts +3 -0
- package/templates/starter/src/ambiance/stocks-air/index.tsx +28 -0
- package/templates/starter/src/ambiance/stocks-air/logic.ts +87 -0
- package/templates/starter/src/ambiance/stocks-air/manifest.ts +15 -0
- package/templates/starter/src/ambiance/stocks-air/resources.ts +23 -0
- package/templates/starter/src/ambiance/stocks-air/ui/index.tsx +67 -0
- package/templates/starter/src/assets/react.svg +1 -0
- package/templates/starter/src/components/AnalyticsTracker.tsx +13 -0
- package/templates/starter/src/components/Playground/CodeEditor.tsx +121 -0
- package/templates/starter/src/components/Playground/Debugger.tsx +71 -0
- package/templates/starter/src/components/Playground/Playground.tsx +221 -0
- package/templates/starter/src/components/Playground/Sidebar.tsx +68 -0
- package/templates/starter/src/components/ProjectSidebar/ProjectSidebar.tsx +219 -0
- package/templates/starter/src/components/TourGuide/TourGuide.tsx +16 -0
- package/templates/starter/src/components/TourGuide/index.ts +1 -0
- package/templates/starter/src/components/TourGuide/tour-flow.yaml +137 -0
- package/templates/starter/src/components/TourGuide/useTourEngine.ts +376 -0
- package/templates/starter/src/index.css +68 -0
- package/templates/starter/src/main.tsx +10 -0
- package/templates/starter/src/services/AnalyticsService.ts +181 -0
- package/templates/starter/src/types/ContextHandler.ts +13 -0
- package/templates/starter/tsconfig.app.json +40 -0
- package/templates/starter/tsconfig.json +7 -0
- package/templates/starter/tsconfig.node.json +26 -0
- package/templates/starter/verify_backend.ts +42 -0
- package/templates/starter/vite.config.ts +286 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "ES2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"erasableSyntaxOnly": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
"noUncheckedSideEffectImports": true
|
|
24
|
+
},
|
|
25
|
+
"include": ["vite.config.ts"]
|
|
26
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
const apiUrl = 'https://auraproxy-7bpt7e5tua-uc.a.run.app';
|
|
3
|
+
|
|
4
|
+
async function testBackend() {
|
|
5
|
+
console.log(`Testing backend at: ${apiUrl}/api/chat/reflect`);
|
|
6
|
+
|
|
7
|
+
// Mimic the payload from useControllerLogic.ts
|
|
8
|
+
const payload = {
|
|
9
|
+
message: "Hello, can you hear me? This is a test.",
|
|
10
|
+
messages: [],
|
|
11
|
+
available_airs: [],
|
|
12
|
+
open_airs: [],
|
|
13
|
+
additional_context: {}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const start = Date.now();
|
|
18
|
+
const res = await fetch(`${apiUrl}/api/chat/reflect`, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: { 'Content-Type': 'application/json' },
|
|
21
|
+
body: JSON.stringify(payload)
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const duration = Date.now() - start;
|
|
25
|
+
console.log(`Response status: ${res.status}`);
|
|
26
|
+
console.log(`Duration: ${duration}ms`);
|
|
27
|
+
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
const text = await res.text();
|
|
30
|
+
console.error("Error response:", text);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const data = await res.json();
|
|
35
|
+
console.log("Success! Response data:", JSON.stringify(data, null, 2));
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error("Fetch failed:", err);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
testBackend();
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
|
|
6
|
+
// https://vite.dev/config/
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [
|
|
9
|
+
react(),
|
|
10
|
+
{
|
|
11
|
+
name: 'save-code-plugin',
|
|
12
|
+
configureServer(server: any) {
|
|
13
|
+
server.middlewares.use((req: any, res: any, next: any) => {
|
|
14
|
+
if (req.method === 'POST' && req.url === '/api/save-code') {
|
|
15
|
+
let body = '';
|
|
16
|
+
req.on('data', (chunk: any) => body += chunk);
|
|
17
|
+
req.on('end', () => {
|
|
18
|
+
try {
|
|
19
|
+
const { filePath, content } = JSON.parse(body);
|
|
20
|
+
const absolutePath = path.resolve(__dirname, filePath);
|
|
21
|
+
if (!absolutePath.startsWith(__dirname)) {
|
|
22
|
+
throw new Error("Invalid path");
|
|
23
|
+
}
|
|
24
|
+
fs.writeFileSync(absolutePath, content, 'utf8');
|
|
25
|
+
res.statusCode = 200;
|
|
26
|
+
res.setHeader('Content-Type', 'application/json');
|
|
27
|
+
res.end(JSON.stringify({ success: true }));
|
|
28
|
+
} catch (err: any) {
|
|
29
|
+
res.statusCode = 500;
|
|
30
|
+
res.setHeader('Content-Type', 'application/json');
|
|
31
|
+
res.end(JSON.stringify({ success: false, error: err.message }));
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
} else if (req.method === 'GET' && req.url?.startsWith('/api/list-files')) {
|
|
35
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
36
|
+
const airId = url.searchParams.get('airId');
|
|
37
|
+
if (!airId) {
|
|
38
|
+
res.statusCode = 400;
|
|
39
|
+
res.end("Missing airId");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const airDir = path.resolve(__dirname, 'src/airs', airId);
|
|
44
|
+
if (!fs.existsSync(airDir)) {
|
|
45
|
+
res.statusCode = 200;
|
|
46
|
+
res.setHeader('Content-Type', 'application/json');
|
|
47
|
+
res.end(JSON.stringify({ files: [] }));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const getFiles = (dir: string): string[] => {
|
|
52
|
+
let results: string[] = [];
|
|
53
|
+
if (!fs.existsSync(dir)) return [];
|
|
54
|
+
const list = fs.readdirSync(dir);
|
|
55
|
+
list.forEach(file => {
|
|
56
|
+
const filePath = path.join(dir, file);
|
|
57
|
+
const stat = fs.statSync(filePath);
|
|
58
|
+
if (stat && stat.isDirectory()) {
|
|
59
|
+
results = results.concat(getFiles(filePath));
|
|
60
|
+
} else {
|
|
61
|
+
// Return path relative to project root, prefixed with /
|
|
62
|
+
const relativePath = path.relative(__dirname, filePath).replace(/\\/g, '/');
|
|
63
|
+
results.push(relativePath.startsWith('/') ? relativePath : '/' + relativePath);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return results;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const files = getFiles(airDir);
|
|
70
|
+
res.statusCode = 200;
|
|
71
|
+
res.setHeader('Content-Type', 'application/json');
|
|
72
|
+
res.end(JSON.stringify({ files }));
|
|
73
|
+
} else if (req.method === 'POST' && req.url === '/api/scaffold-air') {
|
|
74
|
+
let body = '';
|
|
75
|
+
req.on('data', (chunk: any) => body += chunk);
|
|
76
|
+
req.on('end', () => {
|
|
77
|
+
try {
|
|
78
|
+
const { airId, name } = JSON.parse(body);
|
|
79
|
+
if (!airId || !name) throw new Error("Missing airId or name");
|
|
80
|
+
|
|
81
|
+
const airDir = path.resolve(__dirname, 'src/airs', airId);
|
|
82
|
+
if (fs.existsSync(airDir)) throw new Error("AIR already exists");
|
|
83
|
+
|
|
84
|
+
fs.mkdirSync(airDir, { recursive: true });
|
|
85
|
+
fs.mkdirSync(path.join(airDir, 'ui'), { recursive: true });
|
|
86
|
+
|
|
87
|
+
const templates: Record<string, string> = {
|
|
88
|
+
'manifest.ts': `export const ${name.replace(/[^a-zA-Z0-9]/g, '')}Manifest = {
|
|
89
|
+
id: '${airId}',
|
|
90
|
+
meta: {
|
|
91
|
+
title: '${name}',
|
|
92
|
+
icon: '🚀',
|
|
93
|
+
description: 'A brand new custom AIR.',
|
|
94
|
+
width: 400,
|
|
95
|
+
height: 500
|
|
96
|
+
}
|
|
97
|
+
};`,
|
|
98
|
+
'logic.ts': `import { useState } from 'react';
|
|
99
|
+
|
|
100
|
+
export function use${name.replace(/[^a-zA-Z0-9]/g, '')}Logic(props: any) {
|
|
101
|
+
const [count, setCount] = useState(0);
|
|
102
|
+
const increment = () => setCount(prev => prev + 1);
|
|
103
|
+
|
|
104
|
+
return { count, increment };
|
|
105
|
+
}`,
|
|
106
|
+
'resources.ts': `export const resources = {};`,
|
|
107
|
+
'ui/index.tsx': `import React from 'react';
|
|
108
|
+
|
|
109
|
+
export default function UI({ count, onIncrement }: any) {
|
|
110
|
+
return (
|
|
111
|
+
<div style={{ padding: 20, textAlign: 'center' }}>
|
|
112
|
+
<h2>${name} UI</h2>
|
|
113
|
+
<p>Count: {count}</p>
|
|
114
|
+
<button
|
|
115
|
+
onClick={onIncrement}
|
|
116
|
+
style={{
|
|
117
|
+
padding: '10px 20px',
|
|
118
|
+
background: '#007aff',
|
|
119
|
+
color: 'white',
|
|
120
|
+
border: 'none',
|
|
121
|
+
borderRadius: 8,
|
|
122
|
+
cursor: 'pointer'
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
Increment
|
|
126
|
+
</button>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
}`,
|
|
130
|
+
'index.tsx': `import React from 'react';
|
|
131
|
+
import { ${name.replace(/[^a-zA-Z0-9]/g, '')}Manifest } from './manifest';
|
|
132
|
+
import { resources } from './resources';
|
|
133
|
+
import UI from './ui';
|
|
134
|
+
import { use${name.replace(/[^a-zA-Z0-9]/g, '')}Logic } from './logic';
|
|
135
|
+
|
|
136
|
+
const Component: React.FC<any> = (props) => {
|
|
137
|
+
const logic = use${name.replace(/[^a-zA-Z0-9]/g, '')}Logic(props);
|
|
138
|
+
return <UI count={logic.count} onIncrement={logic.increment} />;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export default {
|
|
142
|
+
manifest: ${name.replace(/[^a-zA-Z0-9]/g, '')}Manifest,
|
|
143
|
+
resources,
|
|
144
|
+
component: Component
|
|
145
|
+
};`
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
Object.entries(templates).forEach(([filename, content]) => {
|
|
149
|
+
fs.writeFileSync(path.join(airDir, filename), content, 'utf8');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
res.statusCode = 200;
|
|
153
|
+
res.setHeader('Content-Type', 'application/json');
|
|
154
|
+
res.end(JSON.stringify({ success: true }));
|
|
155
|
+
} catch (err: any) {
|
|
156
|
+
res.statusCode = 500;
|
|
157
|
+
res.setHeader('Content-Type', 'application/json');
|
|
158
|
+
res.end(JSON.stringify({ success: false, error: err.message }));
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
} else if (req.method === 'POST' && req.url?.startsWith('/api/storage/documents')) {
|
|
162
|
+
console.log(`[StorageMiddleware] Request Path: ${req.url}`);
|
|
163
|
+
let body = '';
|
|
164
|
+
req.on('data', (chunk: any) => body += chunk);
|
|
165
|
+
req.on('end', () => {
|
|
166
|
+
try {
|
|
167
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
168
|
+
const pathParts = url.pathname.split('/');
|
|
169
|
+
const action = pathParts[4]; // create, update, get, list, delete
|
|
170
|
+
console.log(`[StorageMiddleware] Action: ${action}, Body: ${body.substring(0, 200)}`);
|
|
171
|
+
const { collection, id, data } = JSON.parse(body);
|
|
172
|
+
|
|
173
|
+
const storageDir = path.resolve(__dirname, 'temp_data_storage', collection);
|
|
174
|
+
if (!fs.existsSync(storageDir)) fs.mkdirSync(storageDir, { recursive: true });
|
|
175
|
+
|
|
176
|
+
if (action === 'get') {
|
|
177
|
+
const filePath = path.join(storageDir, `${id}.json`);
|
|
178
|
+
console.log(`[StorageMiddleware] GET File: ${filePath}`);
|
|
179
|
+
if (fs.existsSync(filePath)) {
|
|
180
|
+
res.end(fs.readFileSync(filePath, 'utf8'));
|
|
181
|
+
} else {
|
|
182
|
+
res.statusCode = 200; // Return 200 with null per Aura client expectation
|
|
183
|
+
res.setHeader('Content-Type', 'application/json');
|
|
184
|
+
res.end(JSON.stringify(null));
|
|
185
|
+
}
|
|
186
|
+
} else if (action === 'create' || action === 'update') {
|
|
187
|
+
const docId = id || data.id || Math.random().toString(36).substr(2, 9);
|
|
188
|
+
const filePath = path.join(storageDir, `${docId}.json`);
|
|
189
|
+
const finalData = action === 'update' && fs.existsSync(filePath)
|
|
190
|
+
? { ...JSON.parse(fs.readFileSync(filePath, 'utf8')), ...data }
|
|
191
|
+
: { ...data, id: docId };
|
|
192
|
+
fs.writeFileSync(filePath, JSON.stringify(finalData, null, 2), 'utf8');
|
|
193
|
+
res.end(JSON.stringify({ id: docId, success: true }));
|
|
194
|
+
} else if (action === 'list') {
|
|
195
|
+
const files = fs.readdirSync(storageDir);
|
|
196
|
+
const docs = files.map(f => JSON.parse(fs.readFileSync(path.join(storageDir, f), 'utf8')));
|
|
197
|
+
res.end(JSON.stringify(docs));
|
|
198
|
+
} else if (action === 'delete') {
|
|
199
|
+
const filePath = path.join(storageDir, `${id}.json`);
|
|
200
|
+
console.log(`[StorageMiddleware] DELETE File: ${filePath}`);
|
|
201
|
+
if (fs.existsSync(filePath)) {
|
|
202
|
+
fs.unlinkSync(filePath);
|
|
203
|
+
}
|
|
204
|
+
res.end(JSON.stringify({ success: true }));
|
|
205
|
+
}
|
|
206
|
+
} catch (err: any) {
|
|
207
|
+
res.statusCode = 500;
|
|
208
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
} else if (req.method === 'GET' && req.url?.startsWith('/api/get-file-path')) {
|
|
212
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
213
|
+
const relativePath = url.searchParams.get('path');
|
|
214
|
+
if (!relativePath) {
|
|
215
|
+
res.statusCode = 400;
|
|
216
|
+
res.end(JSON.stringify({ error: "Missing path parameter" }));
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
const absolutePath = path.resolve(__dirname, relativePath);
|
|
222
|
+
// Security check
|
|
223
|
+
if (!absolutePath.startsWith(__dirname)) {
|
|
224
|
+
throw new Error("Invalid path - outside project directory");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Convert to vscode:// URI format
|
|
228
|
+
// Windows: vscode://file/c:/path/to/file
|
|
229
|
+
// macOS/Linux: vscode://file//Users/path/to/file
|
|
230
|
+
const normalizedPath = absolutePath.replace(/\\/g, '/');
|
|
231
|
+
const vscodeUri = `vscode://file/${normalizedPath}`;
|
|
232
|
+
|
|
233
|
+
res.statusCode = 200;
|
|
234
|
+
res.setHeader('Content-Type', 'application/json');
|
|
235
|
+
res.end(JSON.stringify({
|
|
236
|
+
absolutePath: normalizedPath,
|
|
237
|
+
vscodeUri,
|
|
238
|
+
projectRoot: __dirname.replace(/\\/g, '/')
|
|
239
|
+
}));
|
|
240
|
+
} catch (err: any) {
|
|
241
|
+
res.statusCode = 500;
|
|
242
|
+
res.setHeader('Content-Type', 'application/json');
|
|
243
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
244
|
+
}
|
|
245
|
+
} else if (req.url?.startsWith('/api/storage/objects')) {
|
|
246
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
247
|
+
const objectPath = url.searchParams.get('path');
|
|
248
|
+
if (!objectPath) { res.statusCode = 400; res.end("Missing path"); return; }
|
|
249
|
+
|
|
250
|
+
const filePath = path.resolve(__dirname, 'temp_data_storage/objects', objectPath);
|
|
251
|
+
if (req.method === 'GET') {
|
|
252
|
+
if (fs.existsSync(filePath)) {
|
|
253
|
+
res.end(fs.readFileSync(filePath));
|
|
254
|
+
} else {
|
|
255
|
+
res.statusCode = 404;
|
|
256
|
+
res.end("Not found");
|
|
257
|
+
}
|
|
258
|
+
} else if (req.method === 'POST' || req.method === 'PUT') {
|
|
259
|
+
let buffer = Buffer.alloc(0);
|
|
260
|
+
req.on('data', (chunk: Buffer) => buffer = Buffer.concat([buffer, chunk]));
|
|
261
|
+
req.on('end', () => {
|
|
262
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
263
|
+
fs.writeFileSync(filePath, buffer);
|
|
264
|
+
res.end(JSON.stringify({ success: true }));
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
next();
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
],
|
|
274
|
+
resolve: {
|
|
275
|
+
alias: {
|
|
276
|
+
// Alias @minnai/aura to the local source directory for verifying changes
|
|
277
|
+
'@minnai/aura': path.resolve(__dirname, '../../Aura/src'),
|
|
278
|
+
'react': path.resolve(__dirname, './node_modules/react'),
|
|
279
|
+
'react-dom': path.resolve(__dirname, './node_modules/react-dom'),
|
|
280
|
+
},
|
|
281
|
+
dedupe: ['react', 'react-dom']
|
|
282
|
+
},
|
|
283
|
+
server: {
|
|
284
|
+
port: 5175
|
|
285
|
+
}
|
|
286
|
+
})
|