@fias/create-fias-plugin 1.0.4 → 1.1.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 +55 -14
- package/package.json +1 -1
- package/templates/default/AGENTS.md +21 -3
- package/templates/default/CLAUDE.md +20 -2
package/index.js
CHANGED
|
@@ -7,7 +7,7 @@ var path = require('path');
|
|
|
7
7
|
|
|
8
8
|
var TEMPLATE_DIR = path.resolve(__dirname, 'templates/default');
|
|
9
9
|
|
|
10
|
-
function copyDir(src, dest, replacements) {
|
|
10
|
+
function copyDir(src, dest, replacements, opts) {
|
|
11
11
|
fs.mkdirSync(dest, { recursive: true });
|
|
12
12
|
|
|
13
13
|
var entries = fs.readdirSync(src, { withFileTypes: true });
|
|
@@ -17,8 +17,17 @@ function copyDir(src, dest, replacements) {
|
|
|
17
17
|
var destPath = path.join(dest, entry.name);
|
|
18
18
|
|
|
19
19
|
if (entry.isDirectory()) {
|
|
20
|
-
copyDir(srcPath, destPath, replacements);
|
|
20
|
+
copyDir(srcPath, destPath, replacements, opts);
|
|
21
21
|
} else {
|
|
22
|
+
if (opts.inPlace && fs.existsSync(destPath)) {
|
|
23
|
+
console.error(
|
|
24
|
+
'Error: refusing to overwrite existing file: ' + path.relative(opts.rootDest, destPath),
|
|
25
|
+
);
|
|
26
|
+
console.error(
|
|
27
|
+
'In --in-place mode, the target directory must not already contain plugin files.',
|
|
28
|
+
);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
22
31
|
var content = fs.readFileSync(srcPath, 'utf-8');
|
|
23
32
|
var keys = Object.keys(replacements);
|
|
24
33
|
for (var k = 0; k < keys.length; k++) {
|
|
@@ -29,27 +38,59 @@ function copyDir(src, dest, replacements) {
|
|
|
29
38
|
}
|
|
30
39
|
}
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (!projectName) {
|
|
41
|
+
function printUsage() {
|
|
35
42
|
console.error('Usage: npm create @fias/fias-plugin <project-name>');
|
|
36
|
-
|
|
43
|
+
console.error(' or: npm create @fias/fias-plugin -- --in-place (scaffold into current directory)');
|
|
44
|
+
console.error(' or: npm create @fias/fias-plugin . (alias for --in-place)');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var args = process.argv.slice(2);
|
|
48
|
+
var inPlace = false;
|
|
49
|
+
var positional = [];
|
|
50
|
+
for (var a = 0; a < args.length; a++) {
|
|
51
|
+
if (args[a] === '--in-place' || args[a] === '.') {
|
|
52
|
+
inPlace = true;
|
|
53
|
+
} else if (args[a].indexOf('--') === 0) {
|
|
54
|
+
console.error('Unknown option: ' + args[a]);
|
|
55
|
+
printUsage();
|
|
56
|
+
process.exit(1);
|
|
57
|
+
} else {
|
|
58
|
+
positional.push(args[a]);
|
|
59
|
+
}
|
|
37
60
|
}
|
|
38
61
|
|
|
39
|
-
var
|
|
62
|
+
var projectName;
|
|
63
|
+
var targetDir;
|
|
40
64
|
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
65
|
+
if (inPlace) {
|
|
66
|
+
targetDir = process.cwd();
|
|
67
|
+
projectName = positional[0] || path.basename(targetDir);
|
|
68
|
+
} else {
|
|
69
|
+
projectName = positional[0];
|
|
70
|
+
if (!projectName) {
|
|
71
|
+
printUsage();
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
targetDir = path.resolve(process.cwd(), projectName);
|
|
75
|
+
if (fs.existsSync(targetDir)) {
|
|
76
|
+
console.error('Error: Directory "' + projectName + '" already exists.');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
44
79
|
}
|
|
45
80
|
|
|
46
|
-
console.log(
|
|
81
|
+
console.log(
|
|
82
|
+
inPlace
|
|
83
|
+
? 'Scaffolding FIAS plugin into current directory (' + projectName + ')'
|
|
84
|
+
: 'Creating FIAS plugin project: ' + projectName,
|
|
85
|
+
);
|
|
47
86
|
|
|
48
|
-
copyDir(TEMPLATE_DIR, targetDir, { name: projectName });
|
|
87
|
+
copyDir(TEMPLATE_DIR, targetDir, { name: projectName }, { inPlace: inPlace, rootDest: targetDir });
|
|
49
88
|
|
|
50
|
-
console.log('\nPlugin
|
|
89
|
+
console.log('\nPlugin scaffolded.\n');
|
|
51
90
|
console.log('Next steps:');
|
|
52
|
-
|
|
91
|
+
if (!inPlace) {
|
|
92
|
+
console.log(' cd ' + projectName);
|
|
93
|
+
}
|
|
53
94
|
console.log(' npm install');
|
|
54
95
|
console.log(' npx fias-dev login # Authenticate with FIAS platform (opens browser)');
|
|
55
96
|
console.log(' npm start # Start dev server + real AI harness (uses credits)');
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
-
For other AI tool instruction files, see `
|
|
5
|
+
For other AI tool instruction files, see `CLAUDE.md` (identical content).
|
|
6
6
|
|
|
7
7
|
## Project Structure
|
|
8
8
|
|
|
@@ -268,6 +268,8 @@ const [count, setCount] = usePersistentState<number>('counter', 0);
|
|
|
268
268
|
// Automatically persists to storage on change
|
|
269
269
|
```
|
|
270
270
|
|
|
271
|
+
**Writes are debounced (SDK ≥ 1.8.0).** The in-memory value updates synchronously, but the underlying `storage_write` is coalesced (250 ms trailing-edge debounce, 1 s max-wait, flushed on unmount). Safe to call from `requestAnimationFrame` and other high-frequency handlers. For state that updates every frame (game positions, drag coordinates), still prefer plain `useState` — persisting transient state is wasteful and not useful on reload.
|
|
272
|
+
|
|
271
273
|
### `fias` — Imperative utilities
|
|
272
274
|
|
|
273
275
|
```tsx
|
|
@@ -317,9 +319,15 @@ fias.showToast('Saved!', 'success'); // Toast: 'info' | 'success' | 'warning' |
|
|
|
317
319
|
|
|
318
320
|
These are hard limits enforced by the platform. Code that violates these will fail review or be blocked at runtime.
|
|
319
321
|
|
|
322
|
+
### Hitting a limit? Tell the user about Entity requests
|
|
323
|
+
|
|
324
|
+
Don't just say "can't do that" when the user asks for something blocked by these rules — most often a direct external API call, a third-party service, or a capability the platform hasn't surfaced yet. The Fias team adds new **Entities** on request: platform-managed wrappers that hold credentials, handle auth, and expose the capability to plugins through `useEntityInvocation()` (or `useImageGeneration()` for image models). The plugin never bypasses the sandbox — it invokes the new Entity like any other model.
|
|
325
|
+
|
|
326
|
+
When you hit a constraint, say so AND tell the user: file an Entity request on the Customer Support arche at `https://platform.fias.io/a/arche_customer_support`. Describe the service, endpoints, and auth method needed; once the Entity ships, the plugin can call it via `useEntityInvocation` with no sandbox change. Then propose a fallback that works inside the current sandbox (mock data, a simpler path, deferring the call) so the build keeps moving while the request is in flight.
|
|
327
|
+
|
|
320
328
|
### Sandboxing
|
|
321
329
|
|
|
322
|
-
- Plugins run in an iframe with `sandbox="allow-scripts allow-forms allow-same-origin"`
|
|
330
|
+
- Plugins run in an iframe with `sandbox="allow-scripts allow-forms allow-same-origin allow-downloads"`
|
|
323
331
|
- **No `fetch()` or `XMLHttpRequest`** — all network access is blocked
|
|
324
332
|
- **No access** to parent DOM, cookies, or localStorage
|
|
325
333
|
- **No external scripts or stylesheets** — everything must be bundled
|
|
@@ -338,6 +346,8 @@ These are hard limits enforced by the platform. Code that violates these will fa
|
|
|
338
346
|
- `storage_read`: 300/minute
|
|
339
347
|
- `storage_list`, `storage_delete`: 60/minute
|
|
340
348
|
|
|
349
|
+
A separate **runaway-loop detector** fires if any method is called >50 times in 5 s and blocks that method for 10 s. Errors include the target, e.g. `Runaway loop detected: "storage_write" (path: __state/gameState) …` — the path tells you where to debounce at the source.
|
|
350
|
+
|
|
341
351
|
### Security Rules (enforced during review)
|
|
342
352
|
|
|
343
353
|
- No `eval()`, `Function()`, `innerHTML`, or dynamic code execution
|
|
@@ -415,9 +425,17 @@ npx fias-dev validate
|
|
|
415
425
|
```bash
|
|
416
426
|
npm run submit
|
|
417
427
|
# Builds, validates, packages, uploads, submits for AI review
|
|
418
|
-
# First listing: 5000 credits ($50). Re-submissions: 100 credits ($1).
|
|
419
428
|
```
|
|
420
429
|
|
|
430
|
+
The first time you list a plugin in the marketplace there is a one-time
|
|
431
|
+
listing fee; subsequent submissions of the same plugin only pay a small
|
|
432
|
+
per-review fee. The harness fetches the exact amount from the server and
|
|
433
|
+
shows it before you confirm — never assume a hardcoded price.
|
|
434
|
+
|
|
435
|
+
If the AI review rejects your submission, the listing-fee portion is
|
|
436
|
+
refunded automatically (the per-review portion is not, since review work
|
|
437
|
+
happened either way).
|
|
438
|
+
|
|
421
439
|
## Common Patterns
|
|
422
440
|
|
|
423
441
|
### Theme-Aware Card Component
|
|
@@ -268,6 +268,8 @@ const [count, setCount] = usePersistentState<number>('counter', 0);
|
|
|
268
268
|
// Automatically persists to storage on change
|
|
269
269
|
```
|
|
270
270
|
|
|
271
|
+
**Writes are debounced (SDK ≥ 1.8.0).** The in-memory value updates synchronously, but the underlying `storage_write` is coalesced (250 ms trailing-edge debounce, 1 s max-wait, flushed on unmount). Safe to call from `requestAnimationFrame` and other high-frequency handlers. For state that updates every frame (game positions, drag coordinates), still prefer plain `useState` — persisting transient state is wasteful and not useful on reload.
|
|
272
|
+
|
|
271
273
|
### `fias` — Imperative utilities
|
|
272
274
|
|
|
273
275
|
```tsx
|
|
@@ -317,9 +319,15 @@ fias.showToast('Saved!', 'success'); // Toast: 'info' | 'success' | 'warning' |
|
|
|
317
319
|
|
|
318
320
|
These are hard limits enforced by the platform. Code that violates these will fail review or be blocked at runtime.
|
|
319
321
|
|
|
322
|
+
### Hitting a limit? Tell the user about Entity requests
|
|
323
|
+
|
|
324
|
+
Don't just say "can't do that" when the user asks for something blocked by these rules — most often a direct external API call, a third-party service, or a capability the platform hasn't surfaced yet. The Fias team adds new **Entities** on request: platform-managed wrappers that hold credentials, handle auth, and expose the capability to plugins through `useEntityInvocation()` (or `useImageGeneration()` for image models). The plugin never bypasses the sandbox — it invokes the new Entity like any other model.
|
|
325
|
+
|
|
326
|
+
When you hit a constraint, say so AND tell the user: file an Entity request on the Customer Support arche at `https://platform.fias.io/a/arche_customer_support`. Describe the service, endpoints, and auth method needed; once the Entity ships, the plugin can call it via `useEntityInvocation` with no sandbox change. Then propose a fallback that works inside the current sandbox (mock data, a simpler path, deferring the call) so the build keeps moving while the request is in flight.
|
|
327
|
+
|
|
320
328
|
### Sandboxing
|
|
321
329
|
|
|
322
|
-
- Plugins run in an iframe with `sandbox="allow-scripts allow-forms allow-same-origin"`
|
|
330
|
+
- Plugins run in an iframe with `sandbox="allow-scripts allow-forms allow-same-origin allow-downloads"`
|
|
323
331
|
- **No `fetch()` or `XMLHttpRequest`** — all network access is blocked
|
|
324
332
|
- **No access** to parent DOM, cookies, or localStorage
|
|
325
333
|
- **No external scripts or stylesheets** — everything must be bundled
|
|
@@ -338,6 +346,8 @@ These are hard limits enforced by the platform. Code that violates these will fa
|
|
|
338
346
|
- `storage_read`: 300/minute
|
|
339
347
|
- `storage_list`, `storage_delete`: 60/minute
|
|
340
348
|
|
|
349
|
+
A separate **runaway-loop detector** fires if any method is called >50 times in 5 s and blocks that method for 10 s. Errors include the target, e.g. `Runaway loop detected: "storage_write" (path: __state/gameState) …` — the path tells you where to debounce at the source.
|
|
350
|
+
|
|
341
351
|
### Security Rules (enforced during review)
|
|
342
352
|
|
|
343
353
|
- No `eval()`, `Function()`, `innerHTML`, or dynamic code execution
|
|
@@ -415,9 +425,17 @@ npx fias-dev validate
|
|
|
415
425
|
```bash
|
|
416
426
|
npm run submit
|
|
417
427
|
# Builds, validates, packages, uploads, submits for AI review
|
|
418
|
-
# First listing: 5000 credits ($50). Re-submissions: 100 credits ($1).
|
|
419
428
|
```
|
|
420
429
|
|
|
430
|
+
The first time you list a plugin in the marketplace there is a one-time
|
|
431
|
+
listing fee; subsequent submissions of the same plugin only pay a small
|
|
432
|
+
per-review fee. The harness fetches the exact amount from the server and
|
|
433
|
+
shows it before you confirm — never assume a hardcoded price.
|
|
434
|
+
|
|
435
|
+
If the AI review rejects your submission, the listing-fee portion is
|
|
436
|
+
refunded automatically (the per-review portion is not, since review work
|
|
437
|
+
happened either way).
|
|
438
|
+
|
|
421
439
|
## Common Patterns
|
|
422
440
|
|
|
423
441
|
### Theme-Aware Card Component
|