@mindstudio-ai/remy 0.1.250 → 0.1.252
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/headless.js +305 -239
- package/dist/index.js +332 -262
- package/dist/prompt/compiled/files.md +95 -0
- package/dist/subagents/codeSanityCheck/prompt.md +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Files & Storage
|
|
2
|
+
|
|
3
|
+
Per-app blob storage — the twin of `db` (`db` stores rows; `files` stores files: user uploads,
|
|
4
|
+
generated documents, images, marketing assets). **Private by default.** Files serve on the app's own
|
|
5
|
+
domain.
|
|
6
|
+
|
|
7
|
+
**File stores are always live — there is no dev copy.** Every `put`/`delete`/overwrite hits
|
|
8
|
+
production storage immediately and irreversibly. And unlike the database, **scenarios never reset file
|
|
9
|
+
stores** — a scenario truncates DB tables but leaves files untouched, so files are not a "clean slate"
|
|
10
|
+
you can re-seed, and orphaned files accumulate across runs. Delete deliberately.
|
|
11
|
+
|
|
12
|
+
## Defining a store
|
|
13
|
+
|
|
14
|
+
Like `db.defineTable`, define at module scope and import into methods. Access is pinned at define
|
|
15
|
+
time.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { files } from '@mindstudio-ai/agent';
|
|
19
|
+
|
|
20
|
+
export const Uploads = files.defineStore('uploads'); // private (default)
|
|
21
|
+
export const Assets = files.defineStore('assets', { access: 'public' }); // world-readable + CDN
|
|
22
|
+
// optional upload policy: files.defineStore('uploads', { maxSize, contentTypes })
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Store names: lowercase `[a-z0-9_-]`, ≤ 64 chars. Keys are paths within the store (`reports/q1.pdf`).
|
|
26
|
+
|
|
27
|
+
## Backend (`@mindstudio-ai/agent`)
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { Uploads } from './files/uploads';
|
|
31
|
+
|
|
32
|
+
// Store bytes the backend produced; hand file.url to the frontend.
|
|
33
|
+
const file = await Reports.put(pdfBuffer, { contentType: 'application/pdf', filename: 'q1.pdf' });
|
|
34
|
+
file.url; // stable, on-domain URL for <img>/<a>/fetch (private → app-session-authed)
|
|
35
|
+
|
|
36
|
+
const bytes = await Uploads.get(key); // Buffer — backend-side read (parse it, feed a model)
|
|
37
|
+
await Uploads.head(key); // metadata; .exists(key) → boolean
|
|
38
|
+
const { files, cursor } = await Uploads.list({ prefix: 'reports/', limit: 100 });
|
|
39
|
+
await Uploads.delete(key);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- `put(content, { key?, contentType?, filename?, contentAddressed? })` → `StoredFile`
|
|
43
|
+
(`{ key, url, size?, contentType?, updatedAt?, shareUrl() }`). Omit `key` → UUID;
|
|
44
|
+
`contentAddressed: true` → a `<sha256>.<ext>` key (immutable/idempotent, for baked-in public assets).
|
|
45
|
+
- **`file.url`** is a plain relative string — don't await it. To display a user *their own* file, hand
|
|
46
|
+
them `file.url`; don't `get()` the bytes and stream them yourself.
|
|
47
|
+
- **`await file.shareUrl({ expiresIn })`** → an absolute signed link that works with **no session**
|
|
48
|
+
(email / cross-site embed). Private stores only; default 24h.
|
|
49
|
+
|
|
50
|
+
## User uploads (client-direct — bytes never go through the backend)
|
|
51
|
+
|
|
52
|
+
Backend mints a token; the browser uploads straight to storage:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// backend method
|
|
56
|
+
export async function getUploadSlot(input: { filename: string; contentType: string }) {
|
|
57
|
+
return Uploads.createUploadToken({ contentType: input.contentType, maxSize: 25 * 1024 * 1024 });
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
```typescript
|
|
61
|
+
// frontend (@mindstudio-ai/interface)
|
|
62
|
+
import { createClient, platform } from '@mindstudio-ai/interface';
|
|
63
|
+
const api = createClient();
|
|
64
|
+
const token = await api.getUploadSlot({ filename: file.name, contentType: file.type });
|
|
65
|
+
const { key, url } = await platform.upload(token, file, { onProgress: (f) => setProgress(f) });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Public assets + image resizing
|
|
69
|
+
|
|
70
|
+
Public files are world-readable, on the app's domain, and **images resize via query params**
|
|
71
|
+
(`?w=&h=&fit=&crop=&fm=&dpr=&q=&blur=&sharpen=` — same vocabulary as the image CDN; set `dpr=2/3` for
|
|
72
|
+
retina). Request the size you need rather than CSS-scaling a full-res original.
|
|
73
|
+
|
|
74
|
+
Lightweight-config pattern: a public store + a stable `key` is a file the frontend can `fetch` with no
|
|
75
|
+
DB hit and the backend can overwrite (`Config.put(json, { key: 'config/latest.json' })`).
|
|
76
|
+
|
|
77
|
+
## Build-time / marketing assets
|
|
78
|
+
|
|
79
|
+
Need an image on the site (hero, logo, OG image)? **Never commit binaries to the repo** — it bloats
|
|
80
|
+
git. Upload once and embed the returned URL:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
mindstudio-prod files put --public ./hero.jpg # → { url, key } — content-addressed, immutable
|
|
84
|
+
```
|
|
85
|
+
Write that URL into your JSX/HTML. Also: `files list`, `files rm --store … --key …` (`--help` for flags).
|
|
86
|
+
|
|
87
|
+
## When public vs private
|
|
88
|
+
|
|
89
|
+
- **Private (default):** user uploads, generated docs, anything not world-readable. Reads are authed
|
|
90
|
+
(the app session) or a short-lived `shareUrl`.
|
|
91
|
+
- **Public:** marketing images, resizable media, config the frontend reads. Deliberate `access:
|
|
92
|
+
'public'`.
|
|
93
|
+
|
|
94
|
+
Per-user access is the app's job — key files per user (`{userId}/…`) and hand each user only their own
|
|
95
|
+
URLs; the platform authorizes at the app level, not per file.
|
|
@@ -88,6 +88,8 @@ When a plan includes multiple screens/API calls, always note this item for the d
|
|
|
88
88
|
|
|
89
89
|
- **Hardcoded credentials.** If the plan or code contains API keys, tokens, or connection strings inline, flag it — these should be `process.env` secrets managed via the dashboard. Also flag if the plan uses `process.env` for something the MindStudio SDK already handles (AI model keys, email/SMS sending, etc.).
|
|
90
90
|
|
|
91
|
+
- **Don't use `agent.uploadFile()` from @mindstudio-ai/agent SDK** — that's the legacy v1 public CDN. Use `files`.
|
|
92
|
+
|
|
91
93
|
### Other things to note
|
|
92
94
|
|
|
93
95
|
If you get a whiff of any of the following, make a note for the developer:
|