@fias/arche-sdk 1.13.0 → 1.13.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/README.md +23 -407
- package/dist/bridge.d.ts +3 -1
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +21 -3
- package/dist/bridge.js.map +1 -1
- package/dist/bridge.test.js +4 -1
- package/dist/bridge.test.js.map +1 -1
- package/dist/fias.test.js +4 -1
- package/dist/fias.test.js.map +1 -1
- package/dist/generated/permissions.d.ts +1 -1
- package/dist/generated/permissions.d.ts.map +1 -1
- package/dist/generated/permissions.js +1 -0
- package/dist/generated/permissions.js.map +1 -1
- package/dist/hooks.d.ts +9 -6
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +21 -8
- package/dist/hooks.js.map +1 -1
- package/dist/hooks.test.js +2 -2
- package/dist/hooks.test.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +23 -6
- package/dist/protocol.d.ts +37 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +40 -0
- package/dist/protocol.js.map +1 -0
- package/dist/types.d.ts +35 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -10
- package/templates/default/AGENTS.md +319 -208
- package/templates/default/CLAUDE.md +319 -208
|
@@ -2,8 +2,6 @@
|
|
|
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 `AGENTS.md` (identical content).
|
|
6
|
-
|
|
7
5
|
## Project Structure
|
|
8
6
|
|
|
9
7
|
```
|
|
@@ -183,11 +181,11 @@ function AISummarizer() {
|
|
|
183
181
|
});
|
|
184
182
|
}
|
|
185
183
|
|
|
186
|
-
// streamingText
|
|
187
|
-
//
|
|
188
|
-
// also contains the full text once invoke() resolves. Render ONE slot only
|
|
189
|
-
// streamingText while loading, result.output afterwards
|
|
190
|
-
//
|
|
184
|
+
// `streamingText` accumulates tokens during the call AND continues to hold
|
|
185
|
+
// the full text after the call resolves — it is NOT cleared. `result.output`
|
|
186
|
+
// also contains the full text once `invoke()` resolves. Render ONE slot only
|
|
187
|
+
// — `streamingText` while loading, `result.output` afterwards — or the
|
|
188
|
+
// response appears twice.
|
|
191
189
|
return (
|
|
192
190
|
<div>
|
|
193
191
|
<button onClick={() => summarize('...')} disabled={isLoading}>
|
|
@@ -222,24 +220,24 @@ generate({
|
|
|
222
220
|
});
|
|
223
221
|
|
|
224
222
|
// 2. A specific provider's blessed pick. Use when your plugin's value prop
|
|
225
|
-
// depends on a particular provider's quirks
|
|
223
|
+
// depends on a particular provider's quirks.
|
|
226
224
|
generate({
|
|
227
225
|
entityId: { providerRecommended: { provider: 'openai', mode: 'image-to-image' } },
|
|
228
226
|
prompt: 'Make it look like an oil painting',
|
|
229
227
|
referenceImage: { fileId: previousImage.fileId },
|
|
230
228
|
});
|
|
231
229
|
|
|
232
|
-
// 3. Explicit entityId — back-compat path. Use only when you want a
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
//
|
|
230
|
+
// 3. Explicit entityId — back-compat path. Use only when you want a specific
|
|
231
|
+
// model. If the entity has been deprecated, the response includes
|
|
232
|
+
// `deprecation: { requestedEntityId, resolvedEntityId, deprecatedAt }` and
|
|
233
|
+
// you get the successor.
|
|
236
234
|
generate({
|
|
237
235
|
entityId: 'ent_modeldef_gpt_image_1_5',
|
|
238
236
|
prompt: 'A serene mountain landscape at sunset',
|
|
239
237
|
});
|
|
240
238
|
```
|
|
241
239
|
|
|
242
|
-
Mode values: `'text-to-image'` (
|
|
240
|
+
Mode values: `'text-to-image'` (baseline), `'image-to-image'` (requires `referenceImage`), `'vector'` (SVG output). Style values: `'illustration'` (stylized / digital-art) or `'photoreal'`.
|
|
243
241
|
|
|
244
242
|
Use `npx fias-dev entities --detail <entityId>` to check supported sizes, qualities, and styles for each model.
|
|
245
243
|
|
|
@@ -283,91 +281,210 @@ function ImageMaker() {
|
|
|
283
281
|
}
|
|
284
282
|
```
|
|
285
283
|
|
|
286
|
-
### `useImageEntities()` — Discover image
|
|
284
|
+
### `useImageEntities()` — Discover image-generation entities
|
|
287
285
|
|
|
288
|
-
**Permission:** `entities:image_generate`
|
|
286
|
+
**Permission:** `entities:image_generate`
|
|
289
287
|
**Returns:** `{ entities, isLoading, error, refetch }`
|
|
290
288
|
|
|
291
|
-
|
|
289
|
+
Use to render a "let the user pick a model" picker. For the common case of "just give me something good," skip discovery and pass a selector directly to `useImageGeneration`:
|
|
292
290
|
|
|
293
291
|
```tsx
|
|
294
|
-
|
|
292
|
+
await generate({
|
|
293
|
+
entityId: { platformRecommended: { mode: 'text-to-image' } },
|
|
294
|
+
prompt: '...',
|
|
295
|
+
});
|
|
296
|
+
```
|
|
295
297
|
|
|
296
|
-
|
|
297
|
-
const { entities, isLoading } = useImageEntities({
|
|
298
|
-
modes: ['image-to-image'],
|
|
299
|
-
supportsReferenceImage: true,
|
|
300
|
-
});
|
|
301
|
-
const { generate } = useImageGeneration();
|
|
298
|
+
Discovery hook usage:
|
|
302
299
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
300
|
+
```tsx
|
|
301
|
+
import { useImageEntities } from '@fias/arche-sdk';
|
|
302
|
+
|
|
303
|
+
const { entities, isLoading } = useImageEntities({
|
|
304
|
+
modes: ['image-to-image'],
|
|
305
|
+
supportsReferenceImage: true,
|
|
306
|
+
});
|
|
307
|
+
// entities[i]: { entityId, displayName, provider, modes, supportedSizes,
|
|
308
|
+
// supportsReferenceImage, isPlatformRecommended, ... }
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Each entry is scrubbed — only fields a UI legitimately needs (no provider model strings, no endpoints, no execution config).
|
|
312
|
+
|
|
313
|
+
### `useBackgroundRemoval()` — Client-side background removal
|
|
314
|
+
|
|
315
|
+
**Permission:** `entities:image_remove_background`
|
|
316
|
+
**Returns:** `BackgroundRemovalApi`
|
|
317
|
+
|
|
318
|
+
Runs the imgly WASM model inside the host page — bytes never leave the user's device. Returns a transparent PNG.
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
import { useBackgroundRemoval } from '@fias/arche-sdk';
|
|
322
|
+
|
|
323
|
+
const { removeBackground, isLoading } = useBackgroundRemoval();
|
|
324
|
+
const transparentPng = await removeBackground(jpegBlob);
|
|
317
325
|
```
|
|
318
326
|
|
|
319
|
-
|
|
327
|
+
**Limits:** input ≤ 25 MiB, PNG/JPEG only, rate-limited to 10/min per arche per user. Throws on size violation rather than returning a result.
|
|
328
|
+
|
|
329
|
+
### Audio capability hooks (auto-generated)
|
|
320
330
|
|
|
321
|
-
|
|
331
|
+
**Permission:** `entities:audio_generate`
|
|
332
|
+
**Returns (per hook):** `{ invoke, isLoading, result, error }`
|
|
322
333
|
|
|
323
|
-
**
|
|
324
|
-
**Returns:** `FiasFilesApi`
|
|
334
|
+
Specialty capabilities expose **typed convenience hooks** generated from the platform's surface registry. Each hook bakes in a `surfaceKey` and infers params/result types from the surface's schemas.
|
|
325
335
|
|
|
326
|
-
|
|
336
|
+
Currently shipped audio hooks: `useLyriaMusic`, `useElevenLabsTts`, `useElevenLabsMusic`, `useElevenLabsSfx`, `useElevenLabsStt`, `useElevenLabsAudioIsolation`, `useElevenLabsForcedAlignment`, `useElevenLabsVoiceChanger`, `useElevenLabsVoiceDesign`. Browse the full surface list with `npx fias-dev entities`.
|
|
327
337
|
|
|
328
|
-
|
|
338
|
+
A legacy `useAudioGeneration()` hook still ships for back-compat with code written before the typed-surface migration. New plugins should use the typed hooks above — the legacy hook will be removed in a future major version.
|
|
329
339
|
|
|
330
340
|
```tsx
|
|
331
|
-
import {
|
|
341
|
+
import { useElevenLabsTts } from '@fias/arche-sdk';
|
|
332
342
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
343
|
+
const { invoke, isLoading, result } = useElevenLabsTts();
|
|
344
|
+
await invoke({ text: 'Hello world', voiceId: '...' });
|
|
345
|
+
// result: { audioUrl, audioRef, durationMinutes, costCredits, ... }
|
|
346
|
+
```
|
|
336
347
|
|
|
337
|
-
|
|
338
|
-
let cancelled = false;
|
|
339
|
-
getDownloadUrl(savedFileId).then((r) => {
|
|
340
|
-
if (!cancelled) setUrl(r.url);
|
|
341
|
-
});
|
|
342
|
-
return () => {
|
|
343
|
-
cancelled = true;
|
|
344
|
-
};
|
|
345
|
-
}, [savedFileId, getDownloadUrl]);
|
|
348
|
+
For surfaces without a typed wrapper yet, the generic substrate:
|
|
346
349
|
|
|
347
|
-
|
|
348
|
-
}
|
|
350
|
+
```tsx
|
|
351
|
+
import { useSurface } from '@fias/arche-sdk';
|
|
352
|
+
|
|
353
|
+
const { invoke } = useSurface<MyParams, MyResult>('my.surface-key');
|
|
349
354
|
```
|
|
350
355
|
|
|
351
|
-
|
|
356
|
+
Default per-request timeout is 180 s (covers TTS for long text and Lyria multi-clip generation).
|
|
357
|
+
|
|
358
|
+
### `useVaultDocuments()` — User-owned Vault documents
|
|
352
359
|
|
|
353
|
-
**
|
|
360
|
+
**Permissions:** `vault:documents:read` (list / read / getDownloadUrl / search), `vault:documents:write` (write / update / delete / attach / detach / upload)
|
|
361
|
+
**Returns:** `VaultDocumentsApi`
|
|
354
362
|
|
|
355
|
-
|
|
363
|
+
The Vault is the platform's persistent document store for the signed-in user. Distinct from `useFiasStorage` (per-plugin sandbox): Vault docs live in the user's My Data, survive plugin uninstall, and can be cross-referenced from other arches. Documents this arche creates are scoped to it (`source_arche_id`) and surfaced to the user under `/My-Fias/Arches/<archeId>/`.
|
|
364
|
+
|
|
365
|
+
**Most common use:** images returned from `useImageGeneration()` are auto-saved here. The result includes a `documentId` — pass it to `getDownloadUrl()` later to refresh the presigned URL after the original ~1h TTL expires.
|
|
356
366
|
|
|
357
367
|
```tsx
|
|
358
|
-
|
|
368
|
+
import { useVaultDocuments } from '@fias/arche-sdk';
|
|
369
|
+
|
|
370
|
+
const vault = useVaultDocuments();
|
|
371
|
+
|
|
372
|
+
// Refresh a presigned URL (cached client-side, auto-refreshed before expiry)
|
|
373
|
+
const { url, expiresAt, contentType } = await vault.getDownloadUrl(documentId);
|
|
374
|
+
|
|
375
|
+
// List documents this arche owns
|
|
376
|
+
const { documents, nextCursor } = await vault.list({ search: 'invoice', limit: 50 });
|
|
359
377
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
378
|
+
// Write a small text/JSON document (UTF-8, ≤ 1 MB)
|
|
379
|
+
const { documentId } = await vault.write({
|
|
380
|
+
name: 'settings.json',
|
|
381
|
+
content: JSON.stringify(settings),
|
|
363
382
|
contentType: 'application/json',
|
|
383
|
+
tags: ['config'],
|
|
364
384
|
});
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
await
|
|
368
|
-
|
|
385
|
+
|
|
386
|
+
// Upload a binary or larger document — wraps the two-step presigned-PUT flow
|
|
387
|
+
const result = await vault.upload(bytes, {
|
|
388
|
+
name: 'photo.jpg',
|
|
389
|
+
mimeType: 'image/jpeg',
|
|
390
|
+
});
|
|
391
|
+
// result.status transitions 'uploaded' → 'available' once indexed for search
|
|
392
|
+
|
|
393
|
+
// Semantic search across documents this arche owns
|
|
394
|
+
// (burns user credits per the AI Markup Invariant — call sparingly)
|
|
395
|
+
const { matches } = await vault.search('quarterly revenue', { topK: 5 });
|
|
396
|
+
|
|
397
|
+
// Update / delete
|
|
398
|
+
await vault.update(documentId, { name: 'new-name.json', tags: ['archived'] });
|
|
399
|
+
await vault.delete(documentId); // soft-delete, recoverable from My Data Trash
|
|
369
400
|
```
|
|
370
401
|
|
|
402
|
+
**Cross-arche attachment.** A document can be referenced from another arche's record (Business Contact, Company, Fias 360 Renewal in V-1). The reference points at the document but does NOT expose its contents to the target arche.
|
|
403
|
+
|
|
404
|
+
```tsx
|
|
405
|
+
const { referenceId } = await vault.attach(documentId, {
|
|
406
|
+
regardingType: 'vault_business_contact',
|
|
407
|
+
regardingId: contactId,
|
|
408
|
+
});
|
|
409
|
+
await vault.detach(documentId, referenceId);
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
**Sensitivity tiers** (`'standard'` | `'confidential'` | `'proprietary'`) gate downstream read access per the Vault's standard matrix. Defaults to `'standard'`.
|
|
413
|
+
|
|
414
|
+
**`fetchVaultDocumentDownloadUrl(documentId)`** — non-React function that shares the hook's client-side URL cache. Use from PDF exporters, image preloaders, or other non-component code paths.
|
|
415
|
+
|
|
416
|
+
### `useArcheAssets()` — Contributor-published asset library
|
|
417
|
+
|
|
418
|
+
**Permission:** `assets:read`
|
|
419
|
+
**Returns:** `ArcheAssetsApi`
|
|
420
|
+
|
|
421
|
+
A frozen set of images the arche's contributor uploaded and pinned to the published version — logos, brand images, hole maps, reference photos. Each entry has an immutable `assetId` and a short-lived `signedUrl` (~5 min) suitable for `<img src>`.
|
|
422
|
+
|
|
423
|
+
```tsx
|
|
424
|
+
import { useArcheAssets } from '@fias/arche-sdk';
|
|
425
|
+
|
|
426
|
+
const { list, getUrl } = useArcheAssets();
|
|
427
|
+
|
|
428
|
+
const { entries, nextCursor } = await list({ limit: 50 });
|
|
429
|
+
// entries[i]: { assetId, signedUrl, expiresAt, contentType, width, height,
|
|
430
|
+
// sizeBytes, name, altText, tags }
|
|
431
|
+
|
|
432
|
+
// Refresh a signed URL when it's about to expire
|
|
433
|
+
const fresh = await getUrl(assetId);
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Cache the `assetId`; refresh `signedUrl` via `getUrl()` rather than persisting URLs across sessions.
|
|
437
|
+
|
|
438
|
+
### `useFiasStore()` — In-app purchases (IAP)
|
|
439
|
+
|
|
440
|
+
**Permission:** `store:purchase`
|
|
441
|
+
**Returns:** `FiasStoreApi`
|
|
442
|
+
|
|
443
|
+
How a plugin charges the signed-in user for anything inside the arche — one-time unlocks, consumables, subscriptions, paid sign-ups, donations. **All in-arche payments go through this hook.** The arche never handles payment directly: the platform shows a confirmation overlay, debits the user's Fias credit balance, and returns a result. Do not import Stripe or any external payment SDK — they are sandboxed out and not needed.
|
|
444
|
+
|
|
445
|
+
Products are declared in `fias-plugin.json` under a `products` array (see Manifest Reference below). Each product has a stable `identifier` your code passes to `purchase()`. Prices are denominated in **Fias credits**, not USD.
|
|
446
|
+
|
|
447
|
+
```tsx
|
|
448
|
+
import { useFiasStore } from '@fias/arche-sdk';
|
|
449
|
+
|
|
450
|
+
function UnlockButton() {
|
|
451
|
+
const { products, purchase, hasEntitlement, isLoading } = useFiasStore();
|
|
452
|
+
const product = products.find((p) => p.productIdentifier === 'pro_unlock');
|
|
453
|
+
|
|
454
|
+
if (isLoading || !product) return null;
|
|
455
|
+
if (hasEntitlement('pro_unlock')) return <p>Pro features unlocked.</p>;
|
|
456
|
+
|
|
457
|
+
async function buy() {
|
|
458
|
+
const result = await purchase('pro_unlock');
|
|
459
|
+
if (!result.success) {
|
|
460
|
+
// Common codes: INSUFFICIENT_CREDITS, USER_CANCELLED, PRODUCT_NOT_FOUND
|
|
461
|
+
console.error(result.error);
|
|
462
|
+
}
|
|
463
|
+
// On success, `entitlements` updates automatically.
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return <button onClick={buy}>Unlock for {product.priceCredits} credits</button>;
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
**Product types:**
|
|
471
|
+
|
|
472
|
+
- `consumable` — buyable repeatedly (tokens, energy, single donations, sign-up entries). Does NOT create an entitlement; track usage in `useFiasDataStore`.
|
|
473
|
+
- `non_consumable` — one-time purchase, permanent entitlement.
|
|
474
|
+
- `auto_renewable` — subscription that renews automatically. Requires `subscriptionPeriod`: `weekly` | `monthly` | `quarterly` | `yearly`.
|
|
475
|
+
- `non_renewing` — time-limited access that does NOT renew (season pass).
|
|
476
|
+
|
|
477
|
+
**API surface:**
|
|
478
|
+
|
|
479
|
+
- `products: StoreProduct[]` — loaded on mount.
|
|
480
|
+
- `purchase(productIdentifier, { quantity? }) → StorePurchaseResult` — triggers the platform confirmation overlay. `quantity` only applies to consumables.
|
|
481
|
+
- `entitlements: StoreEntitlement[]` — current non-consumable + subscription entitlements; updated automatically after a successful purchase.
|
|
482
|
+
- `hasEntitlement(productIdentifier) → boolean` — convenience check.
|
|
483
|
+
- `getPurchaseHistory() → Promise<StorePurchase[]>` — full log including consumables.
|
|
484
|
+
- `restorePurchases() → Promise<StoreEntitlement[]>` — re-sync entitlements from the platform.
|
|
485
|
+
|
|
486
|
+
**Error handling:** `purchase()` returns `{ success: false, error, code }` rather than throwing — always branch on `result.success`. The user must be signed in to a Fias account; IAP is not available to anonymous visitors on public arches.
|
|
487
|
+
|
|
371
488
|
### `useFiasNavigation()` — In-plugin routing
|
|
372
489
|
|
|
373
490
|
**Permission:** None required
|
|
@@ -382,23 +499,21 @@ navigateTo('/settings');
|
|
|
382
499
|
|
|
383
500
|
### `useStepNavigation()` — Multi-step workflows
|
|
384
501
|
|
|
502
|
+
**Permission:** None for in-memory use; `storage:sandbox` when `persistKey` is set (it reads/writes `__state/<persistKey>` via the storage bridge).
|
|
385
503
|
**Returns:** `StepNavigationApi`
|
|
386
504
|
|
|
387
|
-
Call **once** in the top-level App component. Share `currentStep` and `setCurrentStep` with step components via React context — do not call `useStepNavigation` from step components (each call creates an independent state).
|
|
388
|
-
|
|
389
505
|
```tsx
|
|
390
506
|
import { useStepNavigation } from '@fias/arche-sdk';
|
|
391
507
|
|
|
392
|
-
//
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
// With persistence — currentStep survives preview rebuilds (SDK ≥ 1.7.0)
|
|
508
|
+
// Pass `persistKey` to survive preview rebuilds (do NOT additionally wrap
|
|
509
|
+
// currentStep in usePersistentState — that creates two state sources and
|
|
510
|
+
// a bidirectional sync loop).
|
|
396
511
|
const { currentStep, setCurrentStep } = useStepNavigation('step-1', {
|
|
397
|
-
persistKey: '
|
|
512
|
+
persistKey: 'workflow-step',
|
|
398
513
|
});
|
|
399
514
|
```
|
|
400
515
|
|
|
401
|
-
|
|
516
|
+
The host can also drive step changes from outside the iframe — for example, when the user clicks a node in an external graph view. The hook listens for those `step_navigate` messages and updates `currentStep` automatically, so unexpected step transitions aren't a bug to chase.
|
|
402
517
|
|
|
403
518
|
### `usePersistentState()` — Auto-saving state
|
|
404
519
|
|
|
@@ -413,21 +528,40 @@ const [count, setCount] = usePersistentState<number>('counter', 0);
|
|
|
413
528
|
// Automatically persists to storage on change
|
|
414
529
|
```
|
|
415
530
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
**Writes are debounced (SDK ≥ 1.8.0).** The in-memory value updates synchronously on every setter call, but the underlying `storage_write` is coalesced with a 250 ms trailing-edge debounce and a 1 s max-wait, and flushed on unmount. This makes the hook safe to call from `requestAnimationFrame`, `mousemove`, and other high-frequency handlers — bursts no longer trip the `storage_write` rate limit.
|
|
419
|
-
|
|
420
|
-
Trade-off: reading the same key via `useFiasStorage().readFile('__state/foo')` immediately after a `setValue` can see a stale value for up to ~250 ms. Read through the hook instead of bypassing it.
|
|
531
|
+
**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.
|
|
421
532
|
|
|
422
533
|
### `fias` — Imperative utilities
|
|
423
534
|
|
|
535
|
+
Use the `fias` namespace when you need bridge operations from outside a React component (event handlers attached imperatively, utility modules, non-React entry points).
|
|
536
|
+
|
|
424
537
|
```tsx
|
|
425
538
|
import { fias } from '@fias/arche-sdk';
|
|
426
539
|
|
|
540
|
+
fias.ready(); // Manual ready signal (FiasProvider does this automatically)
|
|
427
541
|
fias.resize(800); // Resize iframe height
|
|
428
542
|
fias.showToast('Saved!', 'success'); // Toast: 'info' | 'success' | 'warning' | 'error'
|
|
543
|
+
|
|
544
|
+
// fias.dataStore mirrors useFiasDataStore() — same operations, same limits.
|
|
545
|
+
// Requires the `data:store` permission.
|
|
546
|
+
await fias.dataStore.createCollection('scores', { userScope: 'user' });
|
|
547
|
+
await fias.dataStore.put('scores', 'doc-key', { score: 100 });
|
|
548
|
+
const doc = await fias.dataStore.get('scores', 'doc-key');
|
|
549
|
+
await fias.dataStore.query('scores', {
|
|
550
|
+
filters: [{ field: 'score', op: 'gte', value: 50 }],
|
|
551
|
+
});
|
|
552
|
+
await fias.dataStore.delete('scores', 'doc-key');
|
|
553
|
+
await fias.dataStore.listCollections();
|
|
554
|
+
await fias.dataStore.deleteCollection('scores');
|
|
429
555
|
```
|
|
430
556
|
|
|
557
|
+
### Advanced exports (rarely needed)
|
|
558
|
+
|
|
559
|
+
The SDK also exports the following for advanced use cases. Most plugins don't need them.
|
|
560
|
+
|
|
561
|
+
- `VALID_PLUGIN_PERMISSIONS`, `isValidPluginPermission(perm)` — runtime permission validation.
|
|
562
|
+
- Theme catalog: `DARK_THEME`, `LIGHT_THEME`, `getDefaultTheme()`, `THEME_CATALOG`, `getThemeById()`, `FONT_PAIRINGS`, `getFontPairingById()` — useful when the plugin lets the user pick from preset themes/fonts.
|
|
563
|
+
- Bridge introspection: `FiasBridge`, `getBridge()`, `resetBridge()`, `SDK_BRIDGE_PROTOCOL_VERSION`, `SDK_REQUIRES_HOST_PROTOCOL_VERSION` — testing and advanced cases that need direct bridge control.
|
|
564
|
+
|
|
431
565
|
## Manifest Reference (`fias-plugin.json`)
|
|
432
566
|
|
|
433
567
|
```json
|
|
@@ -447,23 +581,78 @@ fias.showToast('Saved!', 'success'); // Toast: 'info' | 'success' | 'warning' |
|
|
|
447
581
|
|
|
448
582
|
**Fields:**
|
|
449
583
|
|
|
450
|
-
| Field
|
|
451
|
-
|
|
|
452
|
-
| `name`
|
|
453
|
-
| `version`
|
|
454
|
-
| `description`
|
|
455
|
-
| `
|
|
456
|
-
| `
|
|
457
|
-
| `
|
|
458
|
-
| `
|
|
459
|
-
| `
|
|
460
|
-
| `
|
|
461
|
-
| `
|
|
462
|
-
|
|
463
|
-
|
|
584
|
+
| Field | Req | Description |
|
|
585
|
+
| --------------------- | ---- | ------------------------------------------------------------------------------------------------------- |
|
|
586
|
+
| `name` | Yes | Plugin identifier (lowercase, hyphens) |
|
|
587
|
+
| `version` | Yes | Semver (e.g., `"1.0.0"`) |
|
|
588
|
+
| `description` | Yes | Short marketplace description |
|
|
589
|
+
| `expandedDescription` | No | Long-form marketplace copy |
|
|
590
|
+
| `main` | Yes | Entry point source file |
|
|
591
|
+
| `archeType` | Yes | `"tool"` or `"site"` |
|
|
592
|
+
| `categorySlug` | No | Marketplace category for discovery |
|
|
593
|
+
| `icon` | No | Relative path to icon asset (shown in marketplace + arche header) |
|
|
594
|
+
| `tags` | No | Discovery tags |
|
|
595
|
+
| `pricing` | Yes | Marketplace **listing** pricing (see "Pricing detail" below). NOT for in-arche charges — use `products` |
|
|
596
|
+
| `permissions` | Yes | Array of permission scopes (see below) |
|
|
597
|
+
| `products` | No | IAP product catalog for `useFiasStore()` (see "Defining IAP products" below) |
|
|
598
|
+
| `isPublic` | No | Default `false`. Anonymous visitors can view (see "Public arches" below) |
|
|
599
|
+
| `isFullScreen` | No | Default `false`. Renders without platform chrome — implies `archeType: "site"` |
|
|
600
|
+
| `isListed` | No | Default `true`. Visible in marketplace store |
|
|
601
|
+
| `sdk` | Yes | SDK version range |
|
|
602
|
+
| `dependencies` | No | npm packages with **exact** versions (max 20) |
|
|
603
|
+
| `archeIds` | Auto | Per-environment IDs written by the CLI after first publish — don't hand-edit |
|
|
604
|
+
|
|
605
|
+
**Pricing detail:**
|
|
606
|
+
|
|
607
|
+
`pricing` is the marketplace **listing** model — controls what the platform charges users to access the arche itself. It does NOT cover charges the plugin makes from inside (entry fees, unlocks, donations) — for those, use IAP via `products`.
|
|
608
|
+
|
|
609
|
+
| `pricing.model` | Required sub-fields |
|
|
610
|
+
| --------------- | ------------------------------- |
|
|
611
|
+
| `"free"` | none |
|
|
612
|
+
| `"fixed"` | `priceCents` |
|
|
613
|
+
| `"per_use"` | `baseFeeCents` |
|
|
614
|
+
| `"tiered"` | `tiers: [{ upTo, priceCents }]` |
|
|
615
|
+
|
|
616
|
+
Other optional sub-fields: `currency: "usd"` (only USD supported today), `platformFeePercent` (override the default platform cut — non-standard).
|
|
617
|
+
|
|
618
|
+
**Permissions:** `theme:read`, `user:profile:read`, `storage:sandbox`, `data:store`, `entities:invoke`, `entities:image_generate`, `entities:image_remove_background`, `entities:audio_generate`, `store:purchase`, `vault:documents:read`, `vault:documents:write`, `assets:read`
|
|
464
619
|
|
|
465
620
|
**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`.
|
|
466
621
|
|
|
622
|
+
**Defining IAP products (for `useFiasStore`):**
|
|
623
|
+
|
|
624
|
+
Add a `products` array to declare what users can buy inside the arche. Each entry needs a stable `identifier` your code passes to `purchase()`. Prices are in Fias credits.
|
|
625
|
+
|
|
626
|
+
```json
|
|
627
|
+
{
|
|
628
|
+
"products": [
|
|
629
|
+
{
|
|
630
|
+
"identifier": "pro_unlock",
|
|
631
|
+
"type": "non_consumable",
|
|
632
|
+
"name": "Pro features",
|
|
633
|
+
"description": "Permanent unlock for advanced features.",
|
|
634
|
+
"priceCredits": 500
|
|
635
|
+
},
|
|
636
|
+
{
|
|
637
|
+
"identifier": "monthly_sub",
|
|
638
|
+
"type": "auto_renewable",
|
|
639
|
+
"name": "Monthly subscription",
|
|
640
|
+
"description": "Renewing access.",
|
|
641
|
+
"priceCredits": 200,
|
|
642
|
+
"subscriptionPeriod": "monthly",
|
|
643
|
+
"subscriptionGroup": "pro"
|
|
644
|
+
}
|
|
645
|
+
],
|
|
646
|
+
"permissions": ["store:purchase"]
|
|
647
|
+
}
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
`store:purchase` must be in `permissions` whenever `products` is non-empty. Use `consumable` for things bought repeatedly (single donations, sign-up entries), `non_consumable` for one-time unlocks, `auto_renewable` / `non_renewing` for time-bounded access.
|
|
651
|
+
|
|
652
|
+
**Public arches (`isPublic: true`):**
|
|
653
|
+
|
|
654
|
+
Setting `isPublic: true` lets anonymous visitors (no Fias account) view the arche at `/a/<archeId>`. The UI renders without the platform sidebar. **Bridge operations still require auth** — `useFiasDataStore` writes, `useFiasStore.purchase()`, `useFiasStorage` reads, vault operations, etc. all require the visitor to be signed in. Public mode is for read-only landing pages, marketing pages, and shared-content viewers — not for collecting registrations or payments from anonymous users.
|
|
655
|
+
|
|
467
656
|
## Plugin Constraints
|
|
468
657
|
|
|
469
658
|
These are hard limits enforced by the platform. Code that violates these will fail review or be blocked at runtime.
|
|
@@ -495,7 +684,7 @@ When you hit a constraint, say so AND tell the user: file an Entity request on t
|
|
|
495
684
|
- `storage_read`: 300/minute
|
|
496
685
|
- `storage_list`, `storage_delete`: 60/minute
|
|
497
686
|
|
|
498
|
-
A separate **runaway-loop detector**
|
|
687
|
+
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.
|
|
499
688
|
|
|
500
689
|
### Security Rules (enforced during review)
|
|
501
690
|
|
|
@@ -516,9 +705,16 @@ A separate **runaway-loop detector** also fires if any method is called >50 time
|
|
|
516
705
|
|
|
517
706
|
### Check for Updates (DO THIS FIRST)
|
|
518
707
|
|
|
519
|
-
**At the start of every new session**, check if the FIAS packages and tooling are up to date:
|
|
708
|
+
**At the start of every new session**, install dependencies if they aren't already, then check if the FIAS packages and tooling are up to date:
|
|
520
709
|
|
|
521
710
|
```bash
|
|
711
|
+
# 1. Install deps first if node_modules/ is missing.
|
|
712
|
+
# fias-dev is a binary inside @fias/plugin-dev-harness, not a standalone
|
|
713
|
+
# npm package — `npx fias-dev …` before `npm install` falls through to
|
|
714
|
+
# the registry and prints a misleading "404 Not Found" for fias-dev.
|
|
715
|
+
[ -d node_modules ] || npm install
|
|
716
|
+
|
|
717
|
+
# 2. Then check for updates.
|
|
522
718
|
npm outdated @fias/arche-sdk @fias/plugin-dev-harness
|
|
523
719
|
npx fias-dev sync --dry-run
|
|
524
720
|
```
|
|
@@ -527,18 +723,6 @@ If newer package versions are available, tell the user and ask if they want to u
|
|
|
527
723
|
|
|
528
724
|
If `sync --dry-run` shows pending changes, tell the user and offer to run `npx fias-dev sync` to update AI instruction files and config from the latest SDK templates. This never touches source code or project-specific files (`package.json`, `fias-plugin.json`, `src/`).
|
|
529
725
|
|
|
530
|
-
#### UI cache after a harness upgrade
|
|
531
|
-
|
|
532
|
-
The harness pulls its host-page UI (toolbar, mode toggle, sign-in modal) from a CDN and caches it in `~/.fias/harness-cache/`. The cached UI takes priority over the bundled copy that ships in the npm package.
|
|
533
|
-
|
|
534
|
-
When you upgrade `@fias/plugin-dev-harness` but the CDN hasn't been redeployed yet, the cached UI can be older than the CLI. fias-dev now detects this case automatically and falls back to the bundled UI — look for this line in the startup log:
|
|
535
|
-
|
|
536
|
-
```
|
|
537
|
-
ℹ Cached harness UI v1.14.3 is older than CLI v1.18.0 — using bundled UI.
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
That message means everything is fine; the bundled UI exactly matches your CLI version. If you ever see stale behavior persist across an upgrade (and the line above didn't appear), delete `~/.fias/harness-cache/` and restart.
|
|
541
|
-
|
|
542
726
|
### Starting Development
|
|
543
727
|
|
|
544
728
|
```bash
|
|
@@ -575,39 +759,6 @@ npx fias-dev entities --type "model-definition" # Filter by type
|
|
|
575
759
|
npx fias-dev entities --detail ent_modeldef_gpt_image_1_5 # Full entity details (capabilities, sizes, pricing)
|
|
576
760
|
```
|
|
577
761
|
|
|
578
|
-
### Debugging — Read the Browser's View Directly
|
|
579
|
-
|
|
580
|
-
The harness mirrors every browser-side event to a per-port log file at `/tmp/fias-dev-<port>.log` (so usually `/tmp/fias-dev-3200.log`). The file path is also printed at startup:
|
|
581
|
-
|
|
582
|
-
```
|
|
583
|
-
Browser dev-log mirror: /tmp/fias-dev-3200.log
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
What's captured:
|
|
587
|
-
|
|
588
|
-
- Every Dev Console entry (`send` / `recv` / `info` / `error` / `toast` / `cost` / `nav`)
|
|
589
|
-
- Every uncaught JS error (`window.error`)
|
|
590
|
-
- Every unhandled promise rejection (`unhandledrejection`)
|
|
591
|
-
- Bridge requests + responses with payload previews
|
|
592
|
-
|
|
593
|
-
Format is one line per event:
|
|
594
|
-
|
|
595
|
-
```
|
|
596
|
-
2026-05-24T07:14:33.123Z [ERROR] window.error {"message":"...","file":"...","line":42}
|
|
597
|
-
2026-05-24T07:14:35.456Z [SEND] image_generate {"entityId":"ent_modeldef_..."}
|
|
598
|
-
2026-05-24T07:14:36.789Z [RECV] response {...}
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
**For AI agents pair-debugging with a developer:** tail this file instead of asking the developer to open DevTools and copy-paste output. Reproduce the failing action in the browser, then read the file — you'll see exactly what the iframe and harness exchanged.
|
|
602
|
-
|
|
603
|
-
```bash
|
|
604
|
-
tail -f /tmp/fias-dev-3200.log
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
The file is truncated on every `fias-dev` start, so it always reflects the current session.
|
|
608
|
-
|
|
609
|
-
The mirror uses `navigator.sendBeacon` (with a `fetch` fallback) so there's a sub-second delay before the most recent events land in the file. If you're chasing a race, give it a beat before reading.
|
|
610
|
-
|
|
611
762
|
### Validating the Manifest
|
|
612
763
|
|
|
613
764
|
```bash
|
|
@@ -630,82 +781,42 @@ If the AI review rejects your submission, the listing-fee portion is
|
|
|
630
781
|
refunded automatically (the per-review portion is not, since review work
|
|
631
782
|
happened either way).
|
|
632
783
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
These are real bugs that have shipped from AI-generated plugins. Avoid them.
|
|
636
|
-
|
|
637
|
-
### Don't navigate from a `useEffect` that watches derived state
|
|
638
|
-
|
|
639
|
-
If a step component derives a value from context state and uses `useEffect` to redirect when that derived value is missing, the redirect can fire mid-update and yank the user away from a working screen. Common shape:
|
|
784
|
+
### Managing the asset library (for `useArcheAssets`)
|
|
640
785
|
|
|
641
|
-
|
|
642
|
-
// ❌ BUG: redirects whenever conversations changes between renders
|
|
643
|
-
const currentConversation = conversations.find((c) => c.id === currentConversationId);
|
|
644
|
-
|
|
645
|
-
useEffect(() => {
|
|
646
|
-
if (!currentConversation) {
|
|
647
|
-
setCurrentStep('list');
|
|
648
|
-
}
|
|
649
|
-
}, [currentConversation, setCurrentStep]); // fires on every conversations change
|
|
650
|
-
```
|
|
786
|
+
`useArcheAssets()` reads a contributor-curated set of images pinned to the arche's published version. Populate that library with the `assets` subcommands. Each command accepts `--env <staging|prod|local>` (default `staging`) and `--arche-id <id>` (auto-resolved from the manifest when omitted).
|
|
651
787
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
if (!currentConversation) return <div>Loading conversation…</div>;
|
|
788
|
+
```bash
|
|
789
|
+
npx fias-dev assets enable # Turn on the asset library for this arche
|
|
790
|
+
npx fias-dev assets status # Quota usage + billing summary
|
|
791
|
+
npx fias-dev assets upload <dir> # Bulk-upload images; subfolder name → default tag
|
|
792
|
+
# Flags: --concurrency <1-16>, --dry-run
|
|
793
|
+
npx fias-dev assets list # List assets; --tag <tag>, --limit <1-100>
|
|
794
|
+
npx fias-dev assets tag <assetId> --add <t> # Add or remove tags; --remove <t> also supported
|
|
795
|
+
npx fias-dev assets delete <assetId> # Soft-delete (excluded from future publishes); -y to skip confirm
|
|
796
|
+
npx fias-dev assets disable # Turn off the asset library
|
|
663
797
|
```
|
|
664
798
|
|
|
665
|
-
|
|
799
|
+
Uploads stage into the contributor's draft set; they only become visible to plugin runtimes after the next `npm run submit` publishes a new arche version. Existing published versions keep their original frozen snapshot.
|
|
666
800
|
|
|
667
|
-
|
|
801
|
+
### Managing collaborators
|
|
668
802
|
|
|
669
|
-
|
|
670
|
-
// ❌ BUG: `messages` here is the snapshot from before await
|
|
671
|
-
const messages = currentConversation.messages;
|
|
672
|
-
const response = await invoke({ entityId, input });
|
|
673
|
-
setConversations(
|
|
674
|
-
conversations.map((c) => (c.id === id ? { ...c, messages: [...messages, response] } : c)),
|
|
675
|
-
);
|
|
803
|
+
Multi-developer plugins use `arche_collaborators` for operational ownership (publishing, team management) — distinct from financial ownership (`contributor_id`, which is immutable in v1). Every arche must keep ≥1 active owner; revokes that would drop to zero return `MUST_KEEP_ONE_OWNER`.
|
|
676
804
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
805
|
+
```bash
|
|
806
|
+
npx fias-dev collaborators list # List active collaborators
|
|
807
|
+
npx fias-dev collaborators add <email|username|userId> \ # Default role: publisher
|
|
808
|
+
--role <owner|publisher|viewer> --expires <iso> # Owners cannot have expiry
|
|
809
|
+
npx fias-dev collaborators set <identifier> \ # Change role and/or expiry
|
|
810
|
+
--role <role> --expires <iso> # --no-expires clears expiry
|
|
811
|
+
npx fias-dev collaborators remove <identifier> # Revoke a collaborator
|
|
682
812
|
```
|
|
683
813
|
|
|
684
|
-
|
|
814
|
+
All commands accept `--env <staging|production|local>` (default `staging`).
|
|
685
815
|
|
|
686
|
-
###
|
|
816
|
+
### Other CLI diagnostics
|
|
687
817
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
### Verify visually when the user reports a UI bug
|
|
691
|
-
|
|
692
|
-
If a user says "I don't see the response" or "the layout's broken", don't rely solely on `console.log` and `useFiasStorage` reads. Inspect the actual rendered output. The platform exposes `get_preview_screenshot` to AI builders for exactly this reason.
|
|
693
|
-
|
|
694
|
-
### Don't persist per-frame state
|
|
695
|
-
|
|
696
|
-
For state that updates every frame (game position, drag coordinates, animated values), use plain `useState` — not `usePersistentState`. Persisting every ball-position tick writes unbounded data to durable storage and doesn't survive reloads in any useful way anyway (the ball will be somewhere different when the player resumes).
|
|
697
|
-
|
|
698
|
-
Hook-level debouncing (SDK ≥ 1.8.0) makes `usePersistentState` safe under bursts, but you still shouldn't persist what you'll discard on reload. Keep ephemeral state in `useState` and persist only meaningful checkpoints (final score, selected difficulty, high-score list).
|
|
699
|
-
|
|
700
|
-
```tsx
|
|
701
|
-
// ❌ Persisting ball position every frame — meaningless on reload
|
|
702
|
-
const [gameState, setGameState] = usePersistentState<GameState>('gameState', initial);
|
|
703
|
-
requestAnimationFrame(() => setGameState(advance(gameState)));
|
|
704
|
-
|
|
705
|
-
// ✅ Ephemeral for live gameplay, persisted for results
|
|
706
|
-
const [gameState, setGameState] = useState<GameState>(initial);
|
|
707
|
-
const [highScores, setHighScores] = usePersistentState<Score[]>('highScores', []);
|
|
708
|
-
// persist highScores only when a run completes
|
|
818
|
+
```bash
|
|
819
|
+
npx fias-dev check-auth # Print per-environment auth status (which envs have saved keys)
|
|
709
820
|
```
|
|
710
821
|
|
|
711
822
|
## Common Patterns
|