@vibes.diy/prompts 14.1.32 → 14.1.33
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/llms/access.md +25 -23
- package/llms/connections.md +24 -6
- package/package.json +4 -4
- package/system-prompt.md +1 -1
package/llms/access.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Access Control (`access.js`) — permission design
|
|
2
2
|
|
|
3
|
-
This doc is in view because the app is getting its `access.js`: either the prompt names sharing, privacy, teams, members, roles, approval or who-can-see-what, or the platform is running the second pass every app gets — a first build drafts the records with their keys already in place (`creatorHandle` on the object, the parent id on each child, `authorHandle` on every record) and leaves the rules and the invite screen to this pass. Every app gets record rules unless the person asked for none in their own words, and the group shape taught below is the resting default. A person the ask names is let in by
|
|
3
|
+
This doc is in view because the app is getting its `access.js`: either the prompt names sharing, privacy, teams, members, roles, approval or who-can-see-what, or the platform is running the second pass every app gets — a first build drafts the records with their keys already in place (`creatorHandle` on the object, the parent id on each child, `authorHandle` on every record) and leaves the rules and the invite screen to this pass. Every app gets record rules unless the person asked for none in their own words, and the group shape taught below is the resting default. A person the ask names is let in by a record — see _A person the ask names is let in by a record_ below — so the build states the membership record's shape and the invite handler that writes it.
|
|
4
4
|
|
|
5
5
|
**On a second pass over an app that already exists, this is a whole-app upgrade, applied in full.** Read `App.jsx` for the object type the app is about, the field that names its creator and the parent id its children carry; name the channels from those records; write the complete `access.js`; then change `App.jsx` to match it — the invite screen, the `can` gates, the object id on every child — keeping everything the app already does.
|
|
6
6
|
|
|
@@ -845,7 +845,14 @@ const { HandleInput } = useViewer();
|
|
|
845
845
|
const { me, can } = useVibe("boards");
|
|
846
846
|
const canInvite = (boardId) => can.create({ type: "member", boardId, userHandle: "x", addedBy: me?.userHandle }).ok;
|
|
847
847
|
const addMember = (boardId, handle) =>
|
|
848
|
-
handle &&
|
|
848
|
+
handle &&
|
|
849
|
+
database.put({
|
|
850
|
+
_id: `member:${boardId}:${handle}`,
|
|
851
|
+
type: "member",
|
|
852
|
+
boardId,
|
|
853
|
+
userHandle: handle,
|
|
854
|
+
addedBy: me.userHandle,
|
|
855
|
+
});
|
|
849
856
|
// Every card names its board at creation — the person's own default board when they picked none.
|
|
850
857
|
const addCard = (text) => database.put({ type: "card", boardId: pickedBoardId || `default-${me.userHandle}`, text });
|
|
851
858
|
// …
|
|
@@ -854,33 +861,28 @@ const addCard = (text) => database.put({ type: "card", boardId: pickedBoardId ||
|
|
|
854
861
|
|
|
855
862
|
Where the app's own maintainer needs to move records through the gate — a CLI migration re-homing cards — `!user.isOwner` reads as a bypass written beside a check (`if (!user.isOwner) ctx.requireAccess(chan)`), never as the arm that decides who may act.
|
|
856
863
|
|
|
857
|
-
#### A person the ask names is let in by
|
|
864
|
+
#### A person the ask names is let in by a record
|
|
858
865
|
|
|
859
|
-
When the instruction names somebody by their handle — "let my friend priya-k see and add to my chores list", "an expedition notebook for me and theo" — **the build
|
|
866
|
+
When the instruction names somebody by their handle — "let my friend priya-k see and add to my chores list", "an expedition notebook for me and theo" — **the build's part is the rule that admits a membership and the screen that writes one; the membership itself is a record.** It is the same `member` doc the invite surface above writes, saved for that handle on the asker's own default object once the rules are live, by whoever is driving the build acting as the maker, read back, and only then spoken of as in.
|
|
860
867
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
The shape: the handles the ask named live in one constant at the top of `App.jsx`, keyed by the asker's own handle (the app context names it), and a first-render effect writes one `member` doc per name onto the asker's own default object — a fixed `_id`, read back first so a mount after the first one finds the record and writes nothing, gated on `can.create` so it waits for the rules to admit it, and run as the asker (the access function above accepts `addedBy === user.userHandle` on their own default board with no admin grant to bootstrap). The `member` rule admits an identical re-put on purpose: a cold replica can miss a record it holds (`ready` is hydration, not sync), and a replay that changes nothing must land quietly rather than refuse — and toast — a membership that is already fine. The effect keys on `ready` and the handle, not on `can`, so it runs once per identity rather than once per render. Anyone else who opens the app finds nothing in the constant under their handle and writes nothing.
|
|
868
|
+
So the record's shape is a contract the files state plainly. The `_id` is fixed by the object and the handle — `` `member:${boardId}:${userHandle}` `` — so a second attempt is the same record rather than a twin, and a read by that id answers "is this person already in?" on its own. The fields are the ones the rule reads: `type`, `boardId`, `userHandle`, and `addedBy` set to the writer's own handle. The `member` rule above admits the board's creator writing on their own default board with nothing to bootstrap, and admits an identical re-put, so a replay lands quietly. One invite handler in `App.jsx` writes exactly that shape, which is what makes the app's own screen and any later save agree on one record.
|
|
864
869
|
|
|
865
870
|
```jsx
|
|
866
|
-
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
const
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
database.get(member._id).catch(() => {
|
|
877
|
-
if (can.create(member).ok) return database.put(member);
|
|
878
|
-
});
|
|
879
|
-
}
|
|
880
|
-
}, [ready, me?.userHandle]);
|
|
871
|
+
const { HandleInput } = useViewer();
|
|
872
|
+
const { database } = useFireproof("boards");
|
|
873
|
+
const { me, can } = useVibe("boards");
|
|
874
|
+
const addMember = (boardId, handle) => {
|
|
875
|
+
// One fixed id per (board, handle): the same person added twice is the same record.
|
|
876
|
+
const member = { _id: `member:${boardId}:${handle}`, type: "member", boardId, userHandle: handle, addedBy: me.userHandle };
|
|
877
|
+
if (can.create(member).ok) database.put(member);
|
|
878
|
+
};
|
|
879
|
+
// …
|
|
880
|
+
<HandleInput onChange={(h) => h && addMember(board._id, h)} placeholder="Add a friend…" />;
|
|
881
881
|
```
|
|
882
882
|
|
|
883
|
-
|
|
883
|
+
**A handle is the thing the person typed as one — a platform handle, the same token `HandleInput` would resolve.** A plain first name ("share it with Priya") is a person the maker adds by handle through the invite screen: the build has no way to know which account that name means, so the invite screen ships as usual and the narration says the maker can add that person there.
|
|
884
|
+
|
|
885
|
+
That bounds what the build's own closing line says: the rules and the screen it just wrote are what the files carry, so the honest sentence is _"your chores list now admits a membership for priya-k, and the invite screen writes one"_. _"priya-k is in"_ belongs to the save that follows — the read for an existing membership, the write in this shape, and the read that finds it afterwards.
|
|
884
886
|
|
|
885
887
|
## More worked round-trip examples
|
|
886
888
|
|
package/llms/connections.md
CHANGED
|
@@ -74,20 +74,38 @@ for the rest.
|
|
|
74
74
|
import { useConnection } from "use-vibes";
|
|
75
75
|
|
|
76
76
|
function ConnectInstagram() {
|
|
77
|
-
const { state, consented, accountLabel, link, refresh } = useConnection("instagram");
|
|
77
|
+
const { state, consented, available, accountLabel, link, refresh } = useConnection("instagram");
|
|
78
78
|
if (state === "pending") return null; // still resolving — never flash a Connect button
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
// A connection that already works keeps working, whatever `available` says.
|
|
80
|
+
if (consented && (state === "live" || state === "expiring"))
|
|
81
|
+
return <p>Connected as {accountLabel}</p>;
|
|
82
|
+
// Everything below asks the visitor to start or redo a connection, which is
|
|
83
|
+
// the only thing `available` can stop.
|
|
84
|
+
if (!available) return <p>Connecting Instagram is not available right now.</p>;
|
|
81
85
|
if (state === "dead") return <p>Your Instagram connection expired. Reconnect it in Settings.</p>;
|
|
82
|
-
return <
|
|
86
|
+
return <button onClick={link}>Connect Instagram</button>;
|
|
83
87
|
}
|
|
84
88
|
```
|
|
85
89
|
|
|
86
90
|
`useConnection(provider)` returns `state` (`pending` | `absent` | `live` |
|
|
87
91
|
`expiring` | `dead`), `consented` (whether _this_ app has been approved — a
|
|
88
92
|
`live` account with `consented: false` is a one-click ask, not a full
|
|
89
|
-
sign-in), `accountLabel` and `expiresAt` for display, `link()` to
|
|
90
|
-
platform's consent card, and `refresh()` to re-read.
|
|
93
|
+
sign-in), `available`, `accountLabel` and `expiresAt` for display, `link()` to
|
|
94
|
+
open the platform's consent card, and `refresh()` to re-read.
|
|
95
|
+
|
|
96
|
+
`available` is about the platform, not the person: it is false when we have not
|
|
97
|
+
finished setting that provider up, or when it is switched off for a while. It
|
|
98
|
+
is not the visitor's fault and it is not permanent.
|
|
99
|
+
|
|
100
|
+
**It stops a connection being _started_, never one that already works.** A
|
|
101
|
+
visitor who connected earlier keeps their connection and your backend keeps
|
|
102
|
+
spending it, so render the connected branch first and let `available` gate only
|
|
103
|
+
the branches that ask somebody to connect or reconnect. Getting that order
|
|
104
|
+
wrong takes a working account away from a visitor who still has one. Where it
|
|
105
|
+
does apply: hide or disable the Connect button, say plainly that connecting is
|
|
106
|
+
not available right now, and let the rest of the app keep working. Never leave
|
|
107
|
+
a Connect button standing over it — pressing it asks the visitor to approve
|
|
108
|
+
something that then fails. `link()` resolves when the
|
|
91
109
|
flow **starts**, not when it finishes — poll `refresh()` a few times while the
|
|
92
110
|
surface is visible if you want to react to completion. Disconnecting lives in
|
|
93
111
|
the platform's Settings page, not in your app.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibes.diy/prompts",
|
|
3
|
-
"version": "14.1.
|
|
3
|
+
"version": "14.1.33",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"exports": {
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"license": "Apache-2.0",
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@adviser/cement": "~0.5.34",
|
|
37
|
-
"@vibes.diy/call-ai-v2": "14.1.
|
|
38
|
-
"@vibes.diy/identity": "14.1.
|
|
39
|
-
"@vibes.diy/use-vibes-types": "14.1.
|
|
37
|
+
"@vibes.diy/call-ai-v2": "14.1.33",
|
|
38
|
+
"@vibes.diy/identity": "14.1.33",
|
|
39
|
+
"@vibes.diy/use-vibes-types": "14.1.33",
|
|
40
40
|
"arktype": "~2.2.3",
|
|
41
41
|
"json-schema-faker": "~0.6.3"
|
|
42
42
|
},
|
package/system-prompt.md
CHANGED
|
@@ -91,7 +91,7 @@ The sandbox serves raw ES modules, so `App.jsx` can import local `.js`/`.jsx` fi
|
|
|
91
91
|
- **Load Google Fonts with `&display=swap` (or `&display=optional`), never `&display=block`.** Append it to the Fonts URL so text paints immediately in a fallback instead of staying invisible for seconds on slow connections (flash of invisible text) — e.g. `https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap`.
|
|
92
92
|
- **The bottom-right corner belongs to the platform — never pin your own control there.** The Vibes Switch (the logo) floats over every app in that corner, so a `fixed` element anchored to both `bottom` and `right` lands underneath it: no floating add/compose button, no chat bubble, no scroll-to-top disc in that spot. Anchor a floating action bottom-left or bottom-center instead, or fold it into the layout — a header button, or a full-width sticky bar (the platform already reserves scroll clearance below your app for one).
|
|
93
93
|
|
|
94
|
-
**Every app gets an `access.js`** unless the person asked for none in their own words, and the access skill doc carries the emit format, placement, and worked examples whenever a turn writes one. When the instruction is the platform's second pass over an app drafted without one, that is a whole-app upgrade applied in full: read `App.jsx` for the records it already writes, emit the complete `access.js` first, then the `App.jsx` edits it needs — the invite screen, the `can` gates, the object id on every child. On any turn, emit `access.js` before any `App.jsx` edit that writes a doc type it gates. Gate every write surface on `useVibe(dbName).can` regardless of whether the app has an `access.js` yet. When the instruction names a person to let in, the build
|
|
94
|
+
**Every app gets an `access.js`** unless the person asked for none in their own words, and the access skill doc carries the emit format, placement, and worked examples whenever a turn writes one. When the instruction is the platform's second pass over an app drafted without one, that is a whole-app upgrade applied in full: read `App.jsx` for the records it already writes, emit the complete `access.js` first, then the `App.jsx` edits it needs — the invite screen, the `can` gates, the object id on every child. On any turn, emit `access.js` before any `App.jsx` edit that writes a doc type it gates. Gate every write surface on `useVibe(dbName).can` regardless of whether the app has an `access.js` yet. When the instruction names a person to let in, the build's part is the rule and the invite handler that write a membership record under an id fixed by the object and the handle, as the access skill doc's _A person the ask names is let in by a record_ shows — the record for that person is saved through that same shape once the rules are live, so the files carry the shape and the database carries the name.
|
|
95
95
|
|
|
96
96
|
**Keep `access.js` in step with the data model.** The app's current `access.js` and `seed.json` are always in view (the `APP_STATE` block). When an edit adds a new written doc `type` — or a field the rules key on — update `access.js` in the same reply so the new writes are allowed; an existing terminal branch that rejects unknown types will reject them at runtime.
|
|
97
97
|
|