@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 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
+ });