@fias/create-fias-plugin 1.0.0
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/index.js +61 -0
- package/package.json +23 -0
- package/templates/default/.cursorrules +1 -0
- package/templates/default/.github/copilot-instructions.md +1 -0
- package/templates/default/AGENTS.md +375 -0
- package/templates/default/CLAUDE.md +375 -0
- package/templates/default/fias-plugin.json +11 -0
- package/templates/default/index.html +12 -0
- package/templates/default/package.json +26 -0
- package/templates/default/src/App.tsx +12 -0
- package/templates/default/src/index.tsx +12 -0
- package/templates/default/tsconfig.json +17 -0
- package/templates/default/vite.config.ts +18 -0
package/index.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
var fs = require('fs');
|
|
6
|
+
var path = require('path');
|
|
7
|
+
|
|
8
|
+
var TEMPLATE_DIR = path.resolve(__dirname, 'templates/default');
|
|
9
|
+
|
|
10
|
+
function copyDir(src, dest, replacements) {
|
|
11
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
12
|
+
|
|
13
|
+
var entries = fs.readdirSync(src, { withFileTypes: true });
|
|
14
|
+
for (var i = 0; i < entries.length; i++) {
|
|
15
|
+
var entry = entries[i];
|
|
16
|
+
var srcPath = path.join(src, entry.name);
|
|
17
|
+
var destPath = path.join(dest, entry.name);
|
|
18
|
+
|
|
19
|
+
if (entry.isDirectory()) {
|
|
20
|
+
copyDir(srcPath, destPath, replacements);
|
|
21
|
+
} else {
|
|
22
|
+
var content = fs.readFileSync(srcPath, 'utf-8');
|
|
23
|
+
var keys = Object.keys(replacements);
|
|
24
|
+
for (var k = 0; k < keys.length; k++) {
|
|
25
|
+
content = content.replace(new RegExp('\\{\\{' + keys[k] + '\\}\\}', 'g'), replacements[keys[k]]);
|
|
26
|
+
}
|
|
27
|
+
fs.writeFileSync(destPath, content);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var projectName = process.argv[2];
|
|
33
|
+
|
|
34
|
+
if (!projectName) {
|
|
35
|
+
console.error('Usage: npm create @fias/fias-plugin <project-name>');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
var targetDir = path.resolve(process.cwd(), projectName);
|
|
40
|
+
|
|
41
|
+
if (fs.existsSync(targetDir)) {
|
|
42
|
+
console.error('Error: Directory "' + projectName + '" already exists.');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('Creating FIAS plugin project: ' + projectName);
|
|
47
|
+
|
|
48
|
+
copyDir(TEMPLATE_DIR, targetDir, { name: projectName });
|
|
49
|
+
|
|
50
|
+
console.log('\nPlugin created at ./' + projectName + '\n');
|
|
51
|
+
console.log('Next steps:');
|
|
52
|
+
console.log(' cd ' + projectName);
|
|
53
|
+
console.log(' npm install');
|
|
54
|
+
console.log(' npx fias-dev login # Authenticate with FIAS platform (opens browser)');
|
|
55
|
+
console.log(' npm start # Start dev server + real AI harness (uses credits)');
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log(' Or for offline development without credits:');
|
|
58
|
+
console.log(' npm run start:mock # Start dev server + mock AI harness');
|
|
59
|
+
console.log('');
|
|
60
|
+
console.log(' Open http://localhost:3200 in your browser.');
|
|
61
|
+
console.log('\nAI assistants: See CLAUDE.md (or AGENTS.md) for full SDK reference.\n');
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fias/create-fias-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "Scaffold a new FIAS plugin arche project",
|
|
8
|
+
"bin": {
|
|
9
|
+
"create-fias-plugin": "index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"index.js",
|
|
13
|
+
"templates"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"fias",
|
|
17
|
+
"plugin",
|
|
18
|
+
"arche",
|
|
19
|
+
"create",
|
|
20
|
+
"scaffold"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT"
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Read CLAUDE.md in this project root for complete FIAS plugin development instructions, SDK API reference, manifest format, constraints, and patterns.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Read CLAUDE.md in this project root for complete FIAS plugin development instructions, SDK API reference, manifest format, constraints, and patterns.
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# FIAS Plugin Development Guide
|
|
2
|
+
|
|
3
|
+
This project is a FIAS platform plugin — a React application that runs in a sandboxed iframe within the FIAS marketplace. This file provides the context AI coding assistants need to build, test, and submit plugins effectively.
|
|
4
|
+
|
|
5
|
+
For other AI tool instruction files, see `CLAUDE.md` (identical content).
|
|
6
|
+
|
|
7
|
+
## Project Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
fias-plugin.json # Plugin manifest (required) — name, permissions, pricing, AI configs
|
|
11
|
+
package.json # Dependencies and scripts
|
|
12
|
+
src/
|
|
13
|
+
index.tsx # Entry point — must render into #root with <FiasProvider> wrapper
|
|
14
|
+
App.tsx # Main component
|
|
15
|
+
vite.config.ts # Vite dev server config (port 3100)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## SDK API Reference
|
|
19
|
+
|
|
20
|
+
All hooks require the app to be wrapped in `<FiasProvider>`:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { FiasProvider } from '@fias/arche-sdk';
|
|
24
|
+
|
|
25
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
26
|
+
<FiasProvider>
|
|
27
|
+
<App />
|
|
28
|
+
</FiasProvider>,
|
|
29
|
+
);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### `useFiasTheme()` — Platform theme tokens
|
|
33
|
+
|
|
34
|
+
**Permission:** `theme:read`
|
|
35
|
+
**Returns:** `FiasTheme | null` (null while loading)
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useFiasTheme } from '@fias/arche-sdk';
|
|
39
|
+
|
|
40
|
+
function MyComponent() {
|
|
41
|
+
const theme = useFiasTheme();
|
|
42
|
+
if (!theme) return null;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
style={{
|
|
47
|
+
color: theme.colors.text,
|
|
48
|
+
backgroundColor: theme.colors.background,
|
|
49
|
+
fontFamily: theme.fonts.body,
|
|
50
|
+
padding: theme.spacing.md,
|
|
51
|
+
borderRadius: theme.components.cardRadius,
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
{theme.mode === 'dark' ? 'Dark mode' : 'Light mode'}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**FiasTheme shape:**
|
|
61
|
+
|
|
62
|
+
- `mode`: `'light' | 'dark'`
|
|
63
|
+
- `colors`: `{ primary, primaryText, secondary, accent, background, surface, card, cardText, text, textSecondary, muted, mutedText, border, error, warning, success, info }`
|
|
64
|
+
- `spacing`: `{ xs, sm, md, lg, xl }` (CSS values like `'8px'`)
|
|
65
|
+
- `fonts`: `{ body, heading, mono }` (font-family strings)
|
|
66
|
+
- `components`: `{ borderRadius, buttonRadius, cardRadius, inputRadius, shadowSm, shadowMd, shadowLg, borderWidth }`
|
|
67
|
+
|
|
68
|
+
### `useFiasUser()` — Current user profile
|
|
69
|
+
|
|
70
|
+
**Permission:** `user:profile:read`
|
|
71
|
+
**Returns:** `FiasUser | null`
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import { useFiasUser } from '@fias/arche-sdk';
|
|
75
|
+
|
|
76
|
+
const user = useFiasUser();
|
|
77
|
+
// { userId: string, displayName: string, avatar: string | null }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### `useFiasStorage()` — Sandboxed file storage
|
|
81
|
+
|
|
82
|
+
**Permission:** `storage:sandbox`
|
|
83
|
+
**Returns:** `FiasStorageApi`
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { useFiasStorage } from '@fias/arche-sdk';
|
|
87
|
+
|
|
88
|
+
const { readFile, writeFile, listFiles, deleteFile } = useFiasStorage();
|
|
89
|
+
|
|
90
|
+
await writeFile('data/settings.json', JSON.stringify(settings));
|
|
91
|
+
const content = await readFile('data/settings.json'); // string | null
|
|
92
|
+
const files = await listFiles('data/'); // string[]
|
|
93
|
+
await deleteFile('data/old.json');
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `useEntityInvocation()` — Invoke AI models
|
|
97
|
+
|
|
98
|
+
**Permission:** `entities:invoke`
|
|
99
|
+
**Returns:** `EntityInvocationApi`
|
|
100
|
+
|
|
101
|
+
Entities are shared capabilities (model access). Your plugin provides the context (system prompt, input) at invoke time.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { useEntityInvocation } from '@fias/arche-sdk';
|
|
105
|
+
|
|
106
|
+
function AISummarizer() {
|
|
107
|
+
const { invoke, isLoading, result, error, streamingText } = useEntityInvocation();
|
|
108
|
+
|
|
109
|
+
async function summarize(text: string) {
|
|
110
|
+
await invoke({
|
|
111
|
+
entityId: 'ent_model_sonnet_45', // the AI model capability
|
|
112
|
+
input: text, // what to process
|
|
113
|
+
systemPrompt: 'You are a concise summarizer. Return a 2-3 sentence summary.',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div>
|
|
119
|
+
<button onClick={() => summarize('...')} disabled={isLoading}>
|
|
120
|
+
Summarize
|
|
121
|
+
</button>
|
|
122
|
+
{isLoading && <p>{streamingText}</p>}
|
|
123
|
+
{result && <p>{result.output}</p>}
|
|
124
|
+
{error && <p>Error: {error.message}</p>}
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The `entityId` references a published model entity. Browse available models with `npx fias-dev entities`. The `systemPrompt` tells the AI how to behave — this is where your plugin's intelligence lives.
|
|
131
|
+
|
|
132
|
+
### `useFiasNavigation()` — In-plugin routing
|
|
133
|
+
|
|
134
|
+
**Permission:** None required
|
|
135
|
+
**Returns:** `FiasNavigationApi`
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import { useFiasNavigation } from '@fias/arche-sdk';
|
|
139
|
+
|
|
140
|
+
const { navigateTo, currentPath } = useFiasNavigation();
|
|
141
|
+
navigateTo('/settings');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `useStepNavigation()` — Multi-step workflows
|
|
145
|
+
|
|
146
|
+
**Returns:** `StepNavigationApi`
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import { useStepNavigation } from '@fias/arche-sdk';
|
|
150
|
+
|
|
151
|
+
const { currentStep, setCurrentStep } = useStepNavigation('step-1');
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `usePersistentState()` — Auto-saving state
|
|
155
|
+
|
|
156
|
+
**Permission:** `storage:sandbox`
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { usePersistentState } from '@fias/arche-sdk';
|
|
160
|
+
|
|
161
|
+
const [count, setCount] = usePersistentState<number>('counter', 0);
|
|
162
|
+
// Automatically persists to storage on change
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `fias` — Imperative utilities
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
import { fias } from '@fias/arche-sdk';
|
|
169
|
+
|
|
170
|
+
fias.resize(800); // Resize iframe height
|
|
171
|
+
fias.showToast('Saved!', 'success'); // Toast: 'info' | 'success' | 'warning' | 'error'
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Manifest Reference (`fias-plugin.json`)
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"name": "my-plugin",
|
|
179
|
+
"version": "1.0.0",
|
|
180
|
+
"description": "What this plugin does",
|
|
181
|
+
"main": "src/index.tsx",
|
|
182
|
+
"archeType": "tool",
|
|
183
|
+
"tags": ["utility"],
|
|
184
|
+
"pricing": { "model": "free", "currency": "usd" },
|
|
185
|
+
"permissions": ["theme:read", "entities:invoke"],
|
|
186
|
+
"sdk": "^1.0.0",
|
|
187
|
+
"dependencies": { "recharts": "2.15.0" }
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Fields:**
|
|
192
|
+
|
|
193
|
+
| Field | Required | Description |
|
|
194
|
+
| -------------- | -------- | --------------------------------------------------------- |
|
|
195
|
+
| `name` | Yes | Plugin identifier (lowercase, hyphens) |
|
|
196
|
+
| `version` | Yes | Semver (e.g., `"1.0.0"`) |
|
|
197
|
+
| `description` | Yes | Short marketplace description |
|
|
198
|
+
| `main` | Yes | Entry point source file |
|
|
199
|
+
| `archeType` | Yes | `"tool"` or `"site"` |
|
|
200
|
+
| `tags` | No | Discovery tags |
|
|
201
|
+
| `pricing` | Yes | `{ model: "free" }` or `"fixed"`, `"per_use"`, `"tiered"` |
|
|
202
|
+
| `permissions` | Yes | Array of permission scopes |
|
|
203
|
+
| `sdk` | Yes | SDK version range |
|
|
204
|
+
| `dependencies` | No | npm packages with **exact** versions (max 20) |
|
|
205
|
+
|
|
206
|
+
**Permissions:** `theme:read`, `user:profile:read`, `storage:sandbox`, `entities:invoke`
|
|
207
|
+
|
|
208
|
+
**Using AI:** Add `"entities:invoke"` to permissions, then use `useEntityInvocation()` with a model entity ID and your own `systemPrompt`. Browse available models with `npx fias-dev entities`.
|
|
209
|
+
|
|
210
|
+
## Plugin Constraints
|
|
211
|
+
|
|
212
|
+
These are hard limits enforced by the platform. Code that violates these will fail review or be blocked at runtime.
|
|
213
|
+
|
|
214
|
+
### Sandboxing
|
|
215
|
+
|
|
216
|
+
- Plugins run in an iframe with `sandbox="allow-scripts allow-forms allow-same-origin"`
|
|
217
|
+
- **No `fetch()` or `XMLHttpRequest`** — all network access is blocked
|
|
218
|
+
- **No access** to parent DOM, cookies, or localStorage
|
|
219
|
+
- **No external scripts or stylesheets** — everything must be bundled
|
|
220
|
+
- All platform communication goes through the bridge (SDK hooks)
|
|
221
|
+
|
|
222
|
+
### Size and File Limits
|
|
223
|
+
|
|
224
|
+
- **Bundle size:** Max 5 MB compressed
|
|
225
|
+
- **Dependencies:** Max 20, exact semver versions only (e.g., `"4.4.7"`, not `"^4.4.7"`)
|
|
226
|
+
- Platform packages (`react`, `react-dom`, `@fias/arche-sdk`) are provided — do not include in `dependencies`
|
|
227
|
+
|
|
228
|
+
### Rate Limits
|
|
229
|
+
|
|
230
|
+
- `entity_invoke`: 60/minute
|
|
231
|
+
- `storage_write`: 120/minute
|
|
232
|
+
- `storage_read`: 300/minute
|
|
233
|
+
- `storage_list`, `storage_delete`: 60/minute
|
|
234
|
+
|
|
235
|
+
### Security Rules (enforced during review)
|
|
236
|
+
|
|
237
|
+
- No `eval()`, `Function()`, `innerHTML`, or dynamic code execution
|
|
238
|
+
- No attempts to escape the iframe sandbox
|
|
239
|
+
- No credential collection or dark patterns
|
|
240
|
+
- No obfuscated code
|
|
241
|
+
- No accessing `window.parent`, `window.top`, or `document.cookie`
|
|
242
|
+
|
|
243
|
+
## Styling Guidelines
|
|
244
|
+
|
|
245
|
+
- Always use `useFiasTheme()` for colors, fonts, and spacing — never hardcode
|
|
246
|
+
- Support both light and dark modes (check `theme.mode`)
|
|
247
|
+
- Use `theme.components.cardRadius`, `theme.components.shadowMd`, etc. for consistent component styling
|
|
248
|
+
- The plugin renders at full width inside the platform layout
|
|
249
|
+
|
|
250
|
+
## Development Workflow
|
|
251
|
+
|
|
252
|
+
### Starting Development
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
npm start # Starts both Vite + dev harness
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
This starts the plugin in **mock mode** (free, offline). Open **http://localhost:3200** in your browser to see the plugin running inside the harness.
|
|
259
|
+
|
|
260
|
+
Port 3200 is the dev harness that wraps the plugin in the platform iframe. Port 3100 is just the raw Vite server and won't work correctly on its own.
|
|
261
|
+
|
|
262
|
+
### Switching to Live Mode (Real AI)
|
|
263
|
+
|
|
264
|
+
In the harness toolbar, click the **MOCK** badge to switch to **LIVE** mode. If you haven't authenticated yet, a login popup will open automatically. Sign in with your FIAS account and the harness will switch to live mode.
|
|
265
|
+
|
|
266
|
+
You can choose between **Staging** and **Production** environments using the dropdown in the toolbar. Each environment requires separate authentication.
|
|
267
|
+
|
|
268
|
+
**IMPORTANT:** Live mode uses real AI and costs credits. Mock mode returns canned responses and does NOT call real AI models. If you want actual AI-powered features to work during testing, you MUST switch to live mode.
|
|
269
|
+
|
|
270
|
+
### CLI Authentication (Alternative)
|
|
271
|
+
|
|
272
|
+
You can also authenticate via the command line:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npx fias-dev login # Authenticate with staging (default)
|
|
276
|
+
npx fias-dev login --env production # Authenticate with production
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Browsing Available Entities
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
npx fias-dev entities # List all
|
|
283
|
+
npx fias-dev entities --search "text" # Search by keyword
|
|
284
|
+
npx fias-dev entities --type "prompt" # Filter by type
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Validating the Manifest
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
npx fias-dev validate
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Submitting to the Arche Store
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npm run submit
|
|
297
|
+
# Builds, validates, packages, uploads, submits for AI review
|
|
298
|
+
# First listing: 5000 credits ($50). Re-submissions: 100 credits ($1).
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Common Patterns
|
|
302
|
+
|
|
303
|
+
### Theme-Aware Card Component
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
function Card({ children }: { children: React.ReactNode }) {
|
|
307
|
+
const theme = useFiasTheme();
|
|
308
|
+
if (!theme) return null;
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<div
|
|
312
|
+
style={{
|
|
313
|
+
backgroundColor: theme.colors.card,
|
|
314
|
+
color: theme.colors.cardText,
|
|
315
|
+
borderRadius: theme.components.cardRadius,
|
|
316
|
+
boxShadow: theme.components.shadowMd,
|
|
317
|
+
border: `${theme.components.borderWidth} solid ${theme.colors.border}`,
|
|
318
|
+
padding: theme.spacing.lg,
|
|
319
|
+
}}
|
|
320
|
+
>
|
|
321
|
+
{children}
|
|
322
|
+
</div>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Streaming AI Response
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
function AIChat() {
|
|
331
|
+
const { invoke, isLoading, streamingText, result } = useEntityInvocation();
|
|
332
|
+
const [input, setInput] = useState('');
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<div>
|
|
336
|
+
<textarea value={input} onChange={(e) => setInput(e.target.value)} />
|
|
337
|
+
<button
|
|
338
|
+
onClick={() =>
|
|
339
|
+
invoke({
|
|
340
|
+
entityId: 'ent_model_sonnet_45',
|
|
341
|
+
input,
|
|
342
|
+
systemPrompt: 'You are a helpful assistant.',
|
|
343
|
+
})
|
|
344
|
+
}
|
|
345
|
+
disabled={isLoading}
|
|
346
|
+
>
|
|
347
|
+
Send
|
|
348
|
+
</button>
|
|
349
|
+
<div>{isLoading ? streamingText : result?.output}</div>
|
|
350
|
+
</div>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Persistent Settings
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
function Settings() {
|
|
359
|
+
const [settings, setSettings] = usePersistentState('settings', {
|
|
360
|
+
notifications: true,
|
|
361
|
+
fontSize: 14,
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
return (
|
|
365
|
+
<label>
|
|
366
|
+
<input
|
|
367
|
+
type="checkbox"
|
|
368
|
+
checked={settings.notifications}
|
|
369
|
+
onChange={(e) => setSettings({ ...settings, notifications: e.target.checked })}
|
|
370
|
+
/>
|
|
371
|
+
Notifications
|
|
372
|
+
</label>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
```
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
# FIAS Plugin Development Guide
|
|
2
|
+
|
|
3
|
+
This project is a FIAS platform plugin — a React application that runs in a sandboxed iframe within the FIAS marketplace. This file provides the context AI coding assistants need to build, test, and submit plugins effectively.
|
|
4
|
+
|
|
5
|
+
For other AI tool instruction files, see `AGENTS.md` (identical content).
|
|
6
|
+
|
|
7
|
+
## Project Structure
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
fias-plugin.json # Plugin manifest (required) — name, permissions, pricing, AI configs
|
|
11
|
+
package.json # Dependencies and scripts
|
|
12
|
+
src/
|
|
13
|
+
index.tsx # Entry point — must render into #root with <FiasProvider> wrapper
|
|
14
|
+
App.tsx # Main component
|
|
15
|
+
vite.config.ts # Vite dev server config (port 3100)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## SDK API Reference
|
|
19
|
+
|
|
20
|
+
All hooks require the app to be wrapped in `<FiasProvider>`:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { FiasProvider } from '@fias/arche-sdk';
|
|
24
|
+
|
|
25
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
26
|
+
<FiasProvider>
|
|
27
|
+
<App />
|
|
28
|
+
</FiasProvider>,
|
|
29
|
+
);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### `useFiasTheme()` — Platform theme tokens
|
|
33
|
+
|
|
34
|
+
**Permission:** `theme:read`
|
|
35
|
+
**Returns:** `FiasTheme | null` (null while loading)
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useFiasTheme } from '@fias/arche-sdk';
|
|
39
|
+
|
|
40
|
+
function MyComponent() {
|
|
41
|
+
const theme = useFiasTheme();
|
|
42
|
+
if (!theme) return null;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
style={{
|
|
47
|
+
color: theme.colors.text,
|
|
48
|
+
backgroundColor: theme.colors.background,
|
|
49
|
+
fontFamily: theme.fonts.body,
|
|
50
|
+
padding: theme.spacing.md,
|
|
51
|
+
borderRadius: theme.components.cardRadius,
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
{theme.mode === 'dark' ? 'Dark mode' : 'Light mode'}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**FiasTheme shape:**
|
|
61
|
+
|
|
62
|
+
- `mode`: `'light' | 'dark'`
|
|
63
|
+
- `colors`: `{ primary, primaryText, secondary, accent, background, surface, card, cardText, text, textSecondary, muted, mutedText, border, error, warning, success, info }`
|
|
64
|
+
- `spacing`: `{ xs, sm, md, lg, xl }` (CSS values like `'8px'`)
|
|
65
|
+
- `fonts`: `{ body, heading, mono }` (font-family strings)
|
|
66
|
+
- `components`: `{ borderRadius, buttonRadius, cardRadius, inputRadius, shadowSm, shadowMd, shadowLg, borderWidth }`
|
|
67
|
+
|
|
68
|
+
### `useFiasUser()` — Current user profile
|
|
69
|
+
|
|
70
|
+
**Permission:** `user:profile:read`
|
|
71
|
+
**Returns:** `FiasUser | null`
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import { useFiasUser } from '@fias/arche-sdk';
|
|
75
|
+
|
|
76
|
+
const user = useFiasUser();
|
|
77
|
+
// { userId: string, displayName: string, avatar: string | null }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### `useFiasStorage()` — Sandboxed file storage
|
|
81
|
+
|
|
82
|
+
**Permission:** `storage:sandbox`
|
|
83
|
+
**Returns:** `FiasStorageApi`
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { useFiasStorage } from '@fias/arche-sdk';
|
|
87
|
+
|
|
88
|
+
const { readFile, writeFile, listFiles, deleteFile } = useFiasStorage();
|
|
89
|
+
|
|
90
|
+
await writeFile('data/settings.json', JSON.stringify(settings));
|
|
91
|
+
const content = await readFile('data/settings.json'); // string | null
|
|
92
|
+
const files = await listFiles('data/'); // string[]
|
|
93
|
+
await deleteFile('data/old.json');
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `useEntityInvocation()` — Invoke AI models
|
|
97
|
+
|
|
98
|
+
**Permission:** `entities:invoke`
|
|
99
|
+
**Returns:** `EntityInvocationApi`
|
|
100
|
+
|
|
101
|
+
Entities are shared capabilities (model access). Your plugin provides the context (system prompt, input) at invoke time.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { useEntityInvocation } from '@fias/arche-sdk';
|
|
105
|
+
|
|
106
|
+
function AISummarizer() {
|
|
107
|
+
const { invoke, isLoading, result, error, streamingText } = useEntityInvocation();
|
|
108
|
+
|
|
109
|
+
async function summarize(text: string) {
|
|
110
|
+
await invoke({
|
|
111
|
+
entityId: 'ent_model_sonnet_45', // the AI model capability
|
|
112
|
+
input: text, // what to process
|
|
113
|
+
systemPrompt: 'You are a concise summarizer. Return a 2-3 sentence summary.',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div>
|
|
119
|
+
<button onClick={() => summarize('...')} disabled={isLoading}>
|
|
120
|
+
Summarize
|
|
121
|
+
</button>
|
|
122
|
+
{isLoading && <p>{streamingText}</p>}
|
|
123
|
+
{result && <p>{result.output}</p>}
|
|
124
|
+
{error && <p>Error: {error.message}</p>}
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The `entityId` references a published model entity. Browse available models with `npx fias-dev entities`. The `systemPrompt` tells the AI how to behave — this is where your plugin's intelligence lives.
|
|
131
|
+
|
|
132
|
+
### `useFiasNavigation()` — In-plugin routing
|
|
133
|
+
|
|
134
|
+
**Permission:** None required
|
|
135
|
+
**Returns:** `FiasNavigationApi`
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import { useFiasNavigation } from '@fias/arche-sdk';
|
|
139
|
+
|
|
140
|
+
const { navigateTo, currentPath } = useFiasNavigation();
|
|
141
|
+
navigateTo('/settings');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `useStepNavigation()` — Multi-step workflows
|
|
145
|
+
|
|
146
|
+
**Returns:** `StepNavigationApi`
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import { useStepNavigation } from '@fias/arche-sdk';
|
|
150
|
+
|
|
151
|
+
const { currentStep, setCurrentStep } = useStepNavigation('step-1');
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `usePersistentState()` — Auto-saving state
|
|
155
|
+
|
|
156
|
+
**Permission:** `storage:sandbox`
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
import { usePersistentState } from '@fias/arche-sdk';
|
|
160
|
+
|
|
161
|
+
const [count, setCount] = usePersistentState<number>('counter', 0);
|
|
162
|
+
// Automatically persists to storage on change
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `fias` — Imperative utilities
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
import { fias } from '@fias/arche-sdk';
|
|
169
|
+
|
|
170
|
+
fias.resize(800); // Resize iframe height
|
|
171
|
+
fias.showToast('Saved!', 'success'); // Toast: 'info' | 'success' | 'warning' | 'error'
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Manifest Reference (`fias-plugin.json`)
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"name": "my-plugin",
|
|
179
|
+
"version": "1.0.0",
|
|
180
|
+
"description": "What this plugin does",
|
|
181
|
+
"main": "src/index.tsx",
|
|
182
|
+
"archeType": "tool",
|
|
183
|
+
"tags": ["utility"],
|
|
184
|
+
"pricing": { "model": "free", "currency": "usd" },
|
|
185
|
+
"permissions": ["theme:read", "entities:invoke"],
|
|
186
|
+
"sdk": "^1.0.0",
|
|
187
|
+
"dependencies": { "recharts": "2.15.0" }
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Fields:**
|
|
192
|
+
|
|
193
|
+
| Field | Required | Description |
|
|
194
|
+
| -------------- | -------- | --------------------------------------------------------- |
|
|
195
|
+
| `name` | Yes | Plugin identifier (lowercase, hyphens) |
|
|
196
|
+
| `version` | Yes | Semver (e.g., `"1.0.0"`) |
|
|
197
|
+
| `description` | Yes | Short marketplace description |
|
|
198
|
+
| `main` | Yes | Entry point source file |
|
|
199
|
+
| `archeType` | Yes | `"tool"` or `"site"` |
|
|
200
|
+
| `tags` | No | Discovery tags |
|
|
201
|
+
| `pricing` | Yes | `{ model: "free" }` or `"fixed"`, `"per_use"`, `"tiered"` |
|
|
202
|
+
| `permissions` | Yes | Array of permission scopes |
|
|
203
|
+
| `sdk` | Yes | SDK version range |
|
|
204
|
+
| `dependencies` | No | npm packages with **exact** versions (max 20) |
|
|
205
|
+
|
|
206
|
+
**Permissions:** `theme:read`, `user:profile:read`, `storage:sandbox`, `entities:invoke`
|
|
207
|
+
|
|
208
|
+
**Using AI:** Add `"entities:invoke"` to permissions, then use `useEntityInvocation()` with a model entity ID and your own `systemPrompt`. Browse available models with `npx fias-dev entities`.
|
|
209
|
+
|
|
210
|
+
## Plugin Constraints
|
|
211
|
+
|
|
212
|
+
These are hard limits enforced by the platform. Code that violates these will fail review or be blocked at runtime.
|
|
213
|
+
|
|
214
|
+
### Sandboxing
|
|
215
|
+
|
|
216
|
+
- Plugins run in an iframe with `sandbox="allow-scripts allow-forms allow-same-origin"`
|
|
217
|
+
- **No `fetch()` or `XMLHttpRequest`** — all network access is blocked
|
|
218
|
+
- **No access** to parent DOM, cookies, or localStorage
|
|
219
|
+
- **No external scripts or stylesheets** — everything must be bundled
|
|
220
|
+
- All platform communication goes through the bridge (SDK hooks)
|
|
221
|
+
|
|
222
|
+
### Size and File Limits
|
|
223
|
+
|
|
224
|
+
- **Bundle size:** Max 5 MB compressed
|
|
225
|
+
- **Dependencies:** Max 20, exact semver versions only (e.g., `"4.4.7"`, not `"^4.4.7"`)
|
|
226
|
+
- Platform packages (`react`, `react-dom`, `@fias/arche-sdk`) are provided — do not include in `dependencies`
|
|
227
|
+
|
|
228
|
+
### Rate Limits
|
|
229
|
+
|
|
230
|
+
- `entity_invoke`: 60/minute
|
|
231
|
+
- `storage_write`: 120/minute
|
|
232
|
+
- `storage_read`: 300/minute
|
|
233
|
+
- `storage_list`, `storage_delete`: 60/minute
|
|
234
|
+
|
|
235
|
+
### Security Rules (enforced during review)
|
|
236
|
+
|
|
237
|
+
- No `eval()`, `Function()`, `innerHTML`, or dynamic code execution
|
|
238
|
+
- No attempts to escape the iframe sandbox
|
|
239
|
+
- No credential collection or dark patterns
|
|
240
|
+
- No obfuscated code
|
|
241
|
+
- No accessing `window.parent`, `window.top`, or `document.cookie`
|
|
242
|
+
|
|
243
|
+
## Styling Guidelines
|
|
244
|
+
|
|
245
|
+
- Always use `useFiasTheme()` for colors, fonts, and spacing — never hardcode
|
|
246
|
+
- Support both light and dark modes (check `theme.mode`)
|
|
247
|
+
- Use `theme.components.cardRadius`, `theme.components.shadowMd`, etc. for consistent component styling
|
|
248
|
+
- The plugin renders at full width inside the platform layout
|
|
249
|
+
|
|
250
|
+
## Development Workflow
|
|
251
|
+
|
|
252
|
+
### Starting Development
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
npm start # Starts both Vite + dev harness
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
This starts the plugin in **mock mode** (free, offline). Open **http://localhost:3200** in your browser to see the plugin running inside the harness.
|
|
259
|
+
|
|
260
|
+
Port 3200 is the dev harness that wraps the plugin in the platform iframe. Port 3100 is just the raw Vite server and won't work correctly on its own.
|
|
261
|
+
|
|
262
|
+
### Switching to Live Mode (Real AI)
|
|
263
|
+
|
|
264
|
+
In the harness toolbar, click the **MOCK** badge to switch to **LIVE** mode. If you haven't authenticated yet, a login popup will open automatically. Sign in with your FIAS account and the harness will switch to live mode.
|
|
265
|
+
|
|
266
|
+
You can choose between **Staging** and **Production** environments using the dropdown in the toolbar. Each environment requires separate authentication.
|
|
267
|
+
|
|
268
|
+
**IMPORTANT:** Live mode uses real AI and costs credits. Mock mode returns canned responses and does NOT call real AI models. If you want actual AI-powered features to work during testing, you MUST switch to live mode.
|
|
269
|
+
|
|
270
|
+
### CLI Authentication (Alternative)
|
|
271
|
+
|
|
272
|
+
You can also authenticate via the command line:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
npx fias-dev login # Authenticate with staging (default)
|
|
276
|
+
npx fias-dev login --env production # Authenticate with production
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Browsing Available Entities
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
npx fias-dev entities # List all
|
|
283
|
+
npx fias-dev entities --search "text" # Search by keyword
|
|
284
|
+
npx fias-dev entities --type "prompt" # Filter by type
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Validating the Manifest
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
npx fias-dev validate
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Submitting to the Arche Store
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
npm run submit
|
|
297
|
+
# Builds, validates, packages, uploads, submits for AI review
|
|
298
|
+
# First listing: 5000 credits ($50). Re-submissions: 100 credits ($1).
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Common Patterns
|
|
302
|
+
|
|
303
|
+
### Theme-Aware Card Component
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
function Card({ children }: { children: React.ReactNode }) {
|
|
307
|
+
const theme = useFiasTheme();
|
|
308
|
+
if (!theme) return null;
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<div
|
|
312
|
+
style={{
|
|
313
|
+
backgroundColor: theme.colors.card,
|
|
314
|
+
color: theme.colors.cardText,
|
|
315
|
+
borderRadius: theme.components.cardRadius,
|
|
316
|
+
boxShadow: theme.components.shadowMd,
|
|
317
|
+
border: `${theme.components.borderWidth} solid ${theme.colors.border}`,
|
|
318
|
+
padding: theme.spacing.lg,
|
|
319
|
+
}}
|
|
320
|
+
>
|
|
321
|
+
{children}
|
|
322
|
+
</div>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Streaming AI Response
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
function AIChat() {
|
|
331
|
+
const { invoke, isLoading, streamingText, result } = useEntityInvocation();
|
|
332
|
+
const [input, setInput] = useState('');
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<div>
|
|
336
|
+
<textarea value={input} onChange={(e) => setInput(e.target.value)} />
|
|
337
|
+
<button
|
|
338
|
+
onClick={() =>
|
|
339
|
+
invoke({
|
|
340
|
+
entityId: 'ent_model_sonnet_45',
|
|
341
|
+
input,
|
|
342
|
+
systemPrompt: 'You are a helpful assistant.',
|
|
343
|
+
})
|
|
344
|
+
}
|
|
345
|
+
disabled={isLoading}
|
|
346
|
+
>
|
|
347
|
+
Send
|
|
348
|
+
</button>
|
|
349
|
+
<div>{isLoading ? streamingText : result?.output}</div>
|
|
350
|
+
</div>
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Persistent Settings
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
function Settings() {
|
|
359
|
+
const [settings, setSettings] = usePersistentState('settings', {
|
|
360
|
+
notifications: true,
|
|
361
|
+
fontSize: 14,
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
return (
|
|
365
|
+
<label>
|
|
366
|
+
<input
|
|
367
|
+
type="checkbox"
|
|
368
|
+
checked={settings.notifications}
|
|
369
|
+
onChange={(e) => setSettings({ ...settings, notifications: e.target.checked })}
|
|
370
|
+
/>
|
|
371
|
+
Notifications
|
|
372
|
+
</label>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A FIAS plugin arche",
|
|
5
|
+
"main": "src/index.tsx",
|
|
6
|
+
"archeType": "tool",
|
|
7
|
+
"tags": [],
|
|
8
|
+
"pricing": { "model": "free", "currency": "usd" },
|
|
9
|
+
"permissions": ["theme:read"],
|
|
10
|
+
"sdk": "^1.0.0"
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>FIAS Plugin</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/index.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"start": "vite & sleep 2 && fias-dev",
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"validate": "tsc --noEmit",
|
|
10
|
+
"dev:harness": "fias-dev",
|
|
11
|
+
"submit": "fias-dev submit"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@fias/arche-sdk": "^1.0.0",
|
|
15
|
+
"react": "^19.0.0",
|
|
16
|
+
"react-dom": "^19.0.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@fias/plugin-dev-harness": "^1.0.0",
|
|
20
|
+
"@types/react": "^19.0.0",
|
|
21
|
+
"@types/react-dom": "^19.0.0",
|
|
22
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
23
|
+
"typescript": "^5.3.3",
|
|
24
|
+
"vite": "^6.0.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useFiasTheme } from '@fias/arche-sdk';
|
|
2
|
+
|
|
3
|
+
export function App() {
|
|
4
|
+
const theme = useFiasTheme();
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
<div style={{ padding: theme?.spacing.md, fontFamily: theme?.fonts.body }}>
|
|
8
|
+
<h1>My FIAS Plugin</h1>
|
|
9
|
+
<p>Edit src/App.tsx to get started.</p>
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom/client';
|
|
3
|
+
import { FiasProvider } from '@fias/arche-sdk';
|
|
4
|
+
import { App } from './App';
|
|
5
|
+
|
|
6
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
7
|
+
<React.StrictMode>
|
|
8
|
+
<FiasProvider>
|
|
9
|
+
<App />
|
|
10
|
+
</FiasProvider>
|
|
11
|
+
</React.StrictMode>,
|
|
12
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"noEmit": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src"]
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
plugins: [react()],
|
|
6
|
+
build: {
|
|
7
|
+
outDir: 'dist',
|
|
8
|
+
rollupOptions: {
|
|
9
|
+
input: 'index.html',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
server: {
|
|
13
|
+
port: 3100,
|
|
14
|
+
cors: true,
|
|
15
|
+
headers: { 'Access-Control-Allow-Origin': '*' },
|
|
16
|
+
hmr: { host: 'localhost' },
|
|
17
|
+
},
|
|
18
|
+
});
|