@pikku/skills 0.12.43 → 0.12.46
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 +3 -2
- package/dist/skills.gen.js +2 -2
- package/package.json +1 -1
- package/skills/pikku-auth/references/better-auth.md +8 -7
- package/skills/pikku-blueprint-to-fabric/SKILL.md +72 -72
- package/skills/pikku-build/SKILL.md +3 -2
- package/skills/pikku-build/references/app.md +24 -13
- package/skills/pikku-build/references/multi-app.md +10 -5
- package/skills/pikku-build/references/native-app.md +148 -0
- package/skills/pikku-build/references/ship.md +18 -8
- package/skills/pikku-changes/SKILL.md +37 -15
- package/skills/pikku-concepts/references/bootstrap.md +2 -3
- package/skills/pikku-fabric/SKILL.md +111 -44
- package/skills/pikku-kysely/SKILL.md +29 -4
- package/skills/pikku-react/SKILL.md +1 -1
- package/skills/pikku-react/references/client.md +15 -22
- package/skills/pikku-scenario/references/personas.md +20 -52
- package/skills/pikku-service-backends/SKILL.md +1 -0
- package/skills/pikku-service-backends/references/mongodb.md +17 -2
- package/skills/pikku-service-backends/references/redis.md +50 -12
|
@@ -8,8 +8,8 @@ the last phase, and nothing in it is needed before then.
|
|
|
8
8
|
`pikku deploy` builds and ships without any hosted service:
|
|
9
9
|
|
|
10
10
|
```sh
|
|
11
|
-
bunx --bun pikku deploy plan --provider standalone
|
|
12
|
-
bunx --bun pikku deploy apply --provider standalone
|
|
11
|
+
bunx --bun pikku deploy plan --provider standalone
|
|
12
|
+
bunx --bun pikku deploy apply --provider standalone
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
`standalone` comes from the installed `@pikku/deploy-standalone` adapter: it
|
|
@@ -108,6 +108,14 @@ workspace). Serve each behind its own hostname, and put the API behind `/api` on
|
|
|
108
108
|
prefix, everything else under `/api/*` reaches the pikku server unprefixed. Get
|
|
109
109
|
this wrong and sign-in fails on one app only, which is a miserable thing to debug.
|
|
110
110
|
|
|
111
|
+
To serve one frontend from the pikku server's own origin instead — one binary,
|
|
112
|
+
one hostname, first-party cookies — give its `frontends` entry `"serve": {}`
|
|
113
|
+
(`urlPrefix` defaults to `/`, `spaFallback` to true). `pikku serve`, `pikku dev`
|
|
114
|
+
and a standalone deploy then mount its built `dist`. Pikku never builds it, so
|
|
115
|
+
build the frontend first. Only one entry may set `serve`.
|
|
116
|
+
|
|
117
|
+
To ship an app as a desktop or Android app, read `references/native-app.md`.
|
|
118
|
+
|
|
111
119
|
Before shipping, run the full gate:
|
|
112
120
|
|
|
113
121
|
```sh
|
|
@@ -129,7 +137,7 @@ out of.** The server-side pass proves the functions; it renders nothing. The
|
|
|
129
137
|
pages are client-rendered, so a component that throws still returns HTTP 200
|
|
130
138
|
with an empty shell — the same trap §6 warns about, and the release gate is
|
|
131
139
|
exactly where it gets shipped past. Run the browser pass, and run it **for every
|
|
132
|
-
environment in `
|
|
140
|
+
environment in `pikku.config.json`**, not just the first:
|
|
133
141
|
|
|
134
142
|
```sh
|
|
135
143
|
bunx --bun pikku scenario run local --spawn --run browser
|
|
@@ -150,11 +158,13 @@ every save — but run it.
|
|
|
150
158
|
Everything above is open source. This is the contract that keeps
|
|
151
159
|
`pikku fabric init` a one-command import later, instead of a migration.
|
|
152
160
|
|
|
153
|
-
- **`
|
|
154
|
-
the right `cwd`, `
|
|
155
|
-
`serves` and `personas` name real personas from the personas
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
- **`frontends` in `pikku.config.json` describes reality.** Every app has an
|
|
162
|
+
entry with the right `cwd`, `kind` and `dev` (`command`, `port`); exactly one
|
|
163
|
+
is `primary`; `serves` and `personas` name real personas from the personas
|
|
164
|
+
section.
|
|
165
|
+
- **`fabric.projectId` in `pikku.config.json` is optional.** Leave it out unless you
|
|
166
|
+
have the real id; the CLI writes it after finding the project from the git
|
|
167
|
+
remote. Do not invent a value.
|
|
158
168
|
- **One `definePersonas` call**, every persona reachable through exactly one
|
|
159
169
|
frontend. Fabric materialises these as its virtual users; a persona nobody
|
|
160
170
|
serves imports as a person with no way in.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-changes
|
|
3
|
-
description: 'Work a Fabric project''s changes queue — the todo list someone filed by circling things on a deployed stage. Covers `pikku fabric changes next|claim|show|ask|shot|done`: waiting for work without polling, asking instead of guessing, offering options as images, one commit per item. TRIGGER when: the user says "run the pikkufabric changes", "run the changes against <stage>", "work the changes (queue)", "watch the changes", "pick up the changes", names a change by its #number, or you are otherwise idle in a
|
|
3
|
+
description: 'Work a Fabric project''s changes queue — the todo list someone filed by circling things on a deployed stage. Covers `pikku fabric changes next|claim|show|ask|reply|shot|done`: waiting for work without polling, asking instead of guessing, saying why an item is left undone, offering options as images, one commit per item. TRIGGER when: the user says "run the pikkufabric changes", "run the changes against <stage>", "work the changes (queue)", "watch the changes", "pick up the changes", names a change by its #number, or you are otherwise idle in a checkout linked to a Fabric project (`pikku fabric config` shows one). DO NOT TRIGGER for git changes, diffs or changelogs, and not for deploying or debugging a stage — use pikku-fabric for those.'
|
|
4
4
|
installGroups: [fabric]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ Someone walked the deployed app and circled things. Each item is their words, a
|
|
|
10
10
|
screenshot of what they saw, and the elements the circle enclosed. You have the repo.
|
|
11
11
|
Empty the queue without making them regret filing.
|
|
12
12
|
|
|
13
|
-
Run every command from the checkout: the project comes from `
|
|
13
|
+
Run every command from the checkout: the project comes from its git remote (`pikku fabric config` shows which).
|
|
14
14
|
`--json` works on all of them. Items are addressed as `2`, `#2` or their uuid.
|
|
15
15
|
|
|
16
16
|
## Which stage
|
|
@@ -33,19 +33,21 @@ something changed. `next` does the waiting and exits only when there is work.
|
|
|
33
33
|
It waits out the grace window (a just-filed item is held about a minute so a batch
|
|
34
34
|
being typed arrives together), claims what is ready as one group, prints it, and
|
|
35
35
|
exits. It also wakes when someone answers a question you asked under that
|
|
36
|
-
`--claimed-by`.
|
|
36
|
+
`--claimed-by`. It is woken by fabric's change events the moment an item is filed
|
|
37
|
+
or answered, and sleeps exactly until a held item becomes claimable; when the event
|
|
38
|
+
stream is unavailable it falls back to checking every `--interval` seconds.
|
|
37
39
|
|
|
38
40
|
2. When it exits, read the exit code:
|
|
39
41
|
|
|
40
|
-
| code | meaning
|
|
41
|
-
|
|
|
42
|
-
| 0
|
|
43
|
-
| 2
|
|
44
|
-
| 3
|
|
45
|
-
| 1
|
|
42
|
+
| code | meaning | do |
|
|
43
|
+
| ---- | ------------------------------------------------------ | ----------------------------------------------- |
|
|
44
|
+
| 0 | work printed (claimed, and/or `Answered`) | work it, then step 3 |
|
|
45
|
+
| 2 | `--timeout`/`--once` found nothing | stop, or restart `next` |
|
|
46
|
+
| 3 | session refused | tell the user to run `pikku fabric login`; stop |
|
|
47
|
+
| 1 | anything else (bad `--stage`, fabric down for minutes) | report the message; stop |
|
|
46
48
|
|
|
47
|
-
3. For each item: `show` → fix → commit → `done
|
|
48
|
-
`next` again, in the background.
|
|
49
|
+
3. For each item: `show` → fix → commit → `done`; or `ask` and move on; or `reply`
|
|
50
|
+
saying why you are leaving it. Then start `next` again, in the background.
|
|
49
51
|
|
|
50
52
|
Without `--claim` it only reports what is claimable; claim it yourself:
|
|
51
53
|
|
|
@@ -53,8 +55,10 @@ Without `--claim` it only reports what is claimable; claim it yourself:
|
|
|
53
55
|
pikku fabric changes claim --change-ids 3,4 --title "Checkout pass" --claimed-by claude-code
|
|
54
56
|
```
|
|
55
57
|
|
|
56
|
-
A `claim` refused with a 409 says per item why
|
|
57
|
-
|
|
58
|
+
A `claim` refused with a 409 says per item why — held for the filer, inside another
|
|
59
|
+
group's lease, done — and when a held or leased item is **claimable at** (a local
|
|
60
|
+
`HH:MM`; `list` and `show` print the same). An item inside someone else's live lease
|
|
61
|
+
cannot be taken. For held items, run `next --claim` rather than retrying. The lease is 30 minutes
|
|
58
62
|
(`--lease-minutes`); an abandoned claim returns to the queue by itself.
|
|
59
63
|
|
|
60
64
|
## Reading an item
|
|
@@ -67,7 +71,7 @@ For held items, run `next --claim` rather than retrying. The lease is 30 minutes
|
|
|
67
71
|
3. **The circled elements**: a testid (greps straight to a component, it is the i18n
|
|
68
72
|
key), a source anchor, a CSS path.
|
|
69
73
|
4. **The source anchor**, `src/routes/app.orders.tsx:42 as of a91c4e2`, is where the JSX
|
|
70
|
-
was at
|
|
74
|
+
was at _that_ commit. Find today's equivalent; never edit line 42 because it said 42.
|
|
71
75
|
|
|
72
76
|
## When to ask
|
|
73
77
|
|
|
@@ -97,6 +101,24 @@ pikku fabric changes shot --change-id 3 --label "Bigger" --kind option --image a
|
|
|
97
101
|
|
|
98
102
|
`--kind evidence` is a picture that proves something, shown inline.
|
|
99
103
|
|
|
104
|
+
## Replying without asking
|
|
105
|
+
|
|
106
|
+
`ask` is for a decision you need from them: it parks the item as needing an answer.
|
|
107
|
+
`done` closes it. Everything else you have to say goes in a `reply`, which leaves the
|
|
108
|
+
item's status exactly where it was:
|
|
109
|
+
|
|
110
|
+
- you are not doing it, and why ("the copy comes from the CMS, not the app");
|
|
111
|
+
- it is blocked on something that is not a question ("needs STRIPE_KEY set on the stage");
|
|
112
|
+
- you could not reproduce it — attach what you saw.
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pikku fabric changes reply 3 --message "Cannot reproduce on develop @ a91c4e2 — this is what I see." \
|
|
116
|
+
--image seen.png --image-label "develop @ a91c4e2" --author-name claude-code
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Never `ask` a question you do not need answered just to leave a note, and never
|
|
120
|
+
`done --note` an item you did not do — both tell the filer the wrong thing.
|
|
121
|
+
|
|
100
122
|
## Committing
|
|
101
123
|
|
|
102
124
|
One item, one commit — `done` records one sha, and that is what a human reverts. The
|
|
@@ -117,7 +139,7 @@ pikku fabric changes done --change-id 7 --note "What you did, for whoever reads
|
|
|
117
139
|
```
|
|
118
140
|
|
|
119
141
|
Branch and commit default to the checkout you are in — run it there, never type a sha.
|
|
120
|
-
An item you decided not to do is not `done`:
|
|
142
|
+
An item you decided not to do is not `done`: `reply` with why and leave it for a
|
|
121
143
|
human to dismiss.
|
|
122
144
|
|
|
123
145
|
Writes need the `changes:project:write` scope; `list`, `show` and `next` without
|
|
@@ -8,10 +8,9 @@ There are two ways to start a Pikku app. Pick based on whether you need to own t
|
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
10
|
// src/lifecycle.ts
|
|
11
|
-
import { pikkuServerLifecycle } from '
|
|
12
|
-
import type { SingletonServices } from '../types/application-types.js'
|
|
11
|
+
import { pikkuServerLifecycle } from '#pikku/setup'
|
|
13
12
|
|
|
14
|
-
export const lifecycle = pikkuServerLifecycle
|
|
13
|
+
export const lifecycle = pikkuServerLifecycle({
|
|
15
14
|
beforeStart: async ({ kysely }) => {
|
|
16
15
|
await runMigrations(kysely)
|
|
17
16
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pikku-fabric
|
|
3
|
-
description: 'Build, convert and debug apps on the Pikku Fabric platform. Covers SQLite/libSQL database setup with Kysely, fabric project layout, deploy provider config, `
|
|
3
|
+
description: 'Build, convert and debug apps on the Pikku Fabric platform. Covers SQLite/libSQL database setup with Kysely, fabric project layout, deploy provider config, the optional `fabric.projectId` in `pikku.config.json`, the `pikku all` + `tsc` verification loop, and reading logs, traces and metrics from a deployed stage. TRIGGER when: user is working on a Fabric-hosted Pikku project, converting an app to Fabric format, asking about Fabric deployment, database or project conventions, asking about a `pikku fabric validate` finding including app-missing-actor-quick-login, or a deployed stage is erroring, timing out or behaving differently than local ("why is prod failing", "check the logs"). DO NOT TRIGGER when: user is working on a generic (non-Fabric) Pikku deployment — use pikku-deploy instead — or the failure reproduces locally, which is where to debug it.'
|
|
4
4
|
installGroups: [fabric]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -91,6 +91,31 @@ Numbers must be consecutive and gap-free, and an applied migration is frozen —
|
|
|
91
91
|
correct a mistake with a new forward migration, never by editing or renaming one
|
|
92
92
|
that has already run (the recorded hash will no longer match).
|
|
93
93
|
|
|
94
|
+
**Forward-only rule.** Once a migration exists on the base branch (or any stage
|
|
95
|
+
has applied it), never edit, rename or delete it — add a NEW numbered migration
|
|
96
|
+
that makes the change. A stage records a migration by name and never re-runs it,
|
|
97
|
+
so an edit never reaches a database that already applied it. Two guards enforce
|
|
98
|
+
this:
|
|
99
|
+
|
|
100
|
+
- `pikku fabric validate` reports `migration-modified-after-base-*` (error) for
|
|
101
|
+
any `db/<engine>/*.sql` that exists on the base ref (default `origin/main`, else `main`, `origin/master`, `master`,
|
|
102
|
+
compared at the branch's merge-base; override with `--migrations-base <ref>`
|
|
103
|
+
or `PIKKU_MIGRATIONS_BASE`) but was modified, deleted or renamed in the working
|
|
104
|
+
tree. New files are fine. It is skipped outside a git repo or with no base ref.
|
|
105
|
+
In CI use a full clone (`fetch-depth: 0`) so the base ref exists.
|
|
106
|
+
- `pikku fabric deploy apply` runs the migration-history checks first and
|
|
107
|
+
refuses to create a deployment if any fail. It compares against the stage
|
|
108
|
+
being deployed and the production (`main`) stage's applied ledger, and against
|
|
109
|
+
the base ref — a branch stage can be reset at will, but main's history reaches
|
|
110
|
+
production. Findings: `migration-applied-file-missing-*`, `migration-drift-*`,
|
|
111
|
+
`migration-gap`, `migration-modified-after-base-*`. Unlike `validate`, a check
|
|
112
|
+
that cannot run refuses too: an unreadable ledger (`migration-drift-unchecked`)
|
|
113
|
+
or a base ref that does not resolve (`migration-base-unresolved`). There is no
|
|
114
|
+
override flag — fix the history.
|
|
115
|
+
|
|
116
|
+
Fix a finding by restoring the file (`git checkout origin/main -- db/sqlite/<file>`)
|
|
117
|
+
and putting the change in a new migration.
|
|
118
|
+
|
|
94
119
|
Run migrations: `pikku db migrate`. It also regenerates `.pikku/db/schema.gen.ts`
|
|
95
120
|
(Kysely types) and `.pikku/db/zod.gen.ts` — there is no separate types step.
|
|
96
121
|
|
|
@@ -111,8 +136,10 @@ to be a migration instead.
|
|
|
111
136
|
|
|
112
137
|
This is **local dev data only**: enough rows that a fresh dev database isn't an
|
|
113
138
|
empty app. Nothing else ever runs it. A deployed stage applies `db/<engine>/*.sql`
|
|
114
|
-
and stops there —
|
|
115
|
-
|
|
139
|
+
and stops there — the one exception is a disposable stage deployed with
|
|
140
|
+
`pikku fabric deploy apply <branch> --reset` (see Deploy), which is never
|
|
141
|
+
production. Local reset refuses `NODE_ENV=production` and refuses a database
|
|
142
|
+
outside the runtime directory.
|
|
116
143
|
|
|
117
144
|
So the test is not "is this row realistic?", it is **"would the app be broken
|
|
118
145
|
without it in production?"** If yes, it is configuration and belongs in a
|
|
@@ -186,47 +213,70 @@ packages/functions/
|
|
|
186
213
|
apps/app/ # Frontend(s)
|
|
187
214
|
db/sqlite/ # Plain .sql migrations, numbered, gap-free (project root)
|
|
188
215
|
db/sqlite-dev-seed.sql # Dev-only test data, applied by `pikku db reset`
|
|
189
|
-
pikku.config.json # Pikku + deploy config (project root)
|
|
190
|
-
pikkufabric.config.json # Fabric project link + frontends (project root)
|
|
216
|
+
pikku.config.json # Pikku + deploy config, and the frontends (project root)
|
|
191
217
|
```
|
|
192
218
|
|
|
193
|
-
##
|
|
219
|
+
## How a checkout is linked to its Fabric project
|
|
220
|
+
|
|
221
|
+
`fabric.projectId` in `pikku.config.json` names the project. It is optional. Order
|
|
222
|
+
tried:
|
|
223
|
+
|
|
224
|
+
1. `FABRIC_PROJECT_ID` env var — CI and scripts.
|
|
225
|
+
2. `fabric.projectId` in `pikku.config.json`.
|
|
226
|
+
3. The git remote, matched against Fabric's projects. The id found is written
|
|
227
|
+
into `pikku.config.json` so the lookup happens once. It is never committed
|
|
228
|
+
for you; commit it or not. A deploy ignores a `pikku.config.json` whose only
|
|
229
|
+
change is `fabric.projectId`. If two projects share one repo the CLI refuses; pick
|
|
230
|
+
one with `FABRIC_PROJECT_ID=<projectId>`.
|
|
231
|
+
|
|
232
|
+
`pikku fabric config` prints which project, api url and login the current
|
|
233
|
+
checkout resolves to, and where each came from, and the project's settings.
|
|
234
|
+
The settings Fabric needs that `pikku.config.json` does not describe are stored
|
|
235
|
+
on the project, not in a file, and set with `key=value` arguments:
|
|
194
236
|
|
|
195
|
-
|
|
237
|
+
```bash
|
|
238
|
+
pikku fabric config showcase.name="Watering Log" showcase.tags=voice,realtime
|
|
239
|
+
pikku fabric config guide.docs=docs/guide guide.theme.primaryColor=teal
|
|
240
|
+
pikku fabric config scenarios.env.STRIPE_MODE=test # the scenario run only
|
|
241
|
+
pikku fabric config showcase.tint= # an empty value clears it
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The keys are `showcase.name` (≤60), `showcase.description` (≤160),
|
|
245
|
+
`showcase.tags` (comma-separated, ≤6, from Fabric's fixed list),
|
|
246
|
+
`showcase.tint` (`#rrggbb`), `guide.docs`, `guide.theme.primaryColor`,
|
|
247
|
+
`guide.theme.fontFamily`, `guide.theme.headingFontFamily` and
|
|
248
|
+
`scenarios.env.<NAME>`. Every assignment is checked before any is applied, so a
|
|
249
|
+
refused one changes nothing. Whether the project appears on the public
|
|
250
|
+
showcase is Fabric's decision, not a setting.
|
|
251
|
+
|
|
252
|
+
`pikku fabric link` creates
|
|
253
|
+
`origin` (with `--gitea`) if there is none, imports the project, writes
|
|
254
|
+
`projectId`, and queues the first deploy; it commits and pushes nothing. A
|
|
255
|
+
custom production domain is set with `pikku fabric domains add`. Production
|
|
256
|
+
always maps to `main`; without a domain it lives on the platform
|
|
257
|
+
`*.pikkufabric.app` hostnames.
|
|
258
|
+
|
|
259
|
+
The apps are the `frontends` in `pikku.config.json`,
|
|
260
|
+
the same list `pikku serve`, `pikku app` and native builds read, and
|
|
261
|
+
`pikku fabric validate` and `smoke` read them from there:
|
|
196
262
|
|
|
197
263
|
```json
|
|
198
|
-
{
|
|
199
|
-
"
|
|
200
|
-
|
|
201
|
-
"
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
"
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
208
|
-
"kind": "ssr",
|
|
209
|
-
"dev": {
|
|
210
|
-
"command": ["yarn", "dev"],
|
|
211
|
-
"port": 7105,
|
|
212
|
-
"healthPath": "/"
|
|
213
|
-
}
|
|
264
|
+
"frontends": {
|
|
265
|
+
"app": {
|
|
266
|
+
"cwd": "apps/app",
|
|
267
|
+
"primary": true,
|
|
268
|
+
"deploy": true,
|
|
269
|
+
"kind": "ssr",
|
|
270
|
+
"dev": {
|
|
271
|
+
"command": ["yarn", "dev"],
|
|
272
|
+
"port": 7105,
|
|
273
|
+
"healthPath": "/"
|
|
214
274
|
}
|
|
215
275
|
}
|
|
216
276
|
}
|
|
217
277
|
```
|
|
218
278
|
|
|
219
|
-
|
|
220
|
-
`__PROJECT_ID__` placeholder — that is _not_ a link, and the CLI treats it as
|
|
221
|
-
unlinked.
|
|
222
|
-
- `production.domain`: optional custom domain. Production always maps to `main`;
|
|
223
|
-
without a domain it lives on the platform `*.pikkufabric.app` hostnames.
|
|
224
|
-
- `frontends`: each entry declares a frontend app with its dev command and port
|
|
225
|
-
|
|
226
|
-
Several CLI messages call this file `fabric.config.json` — `fabric init --force`,
|
|
227
|
-
`fabric link --apiUrl`, and the `domains` commands' "No fabric.config.json found".
|
|
228
|
-
The file the CLI actually reads and writes is `pikkufabric.config.json`; don't
|
|
229
|
-
create the shorter name to satisfy an error message.
|
|
279
|
+
Each `frontends` entry declares a frontend app with its dev command and port.
|
|
230
280
|
|
|
231
281
|
## RPC is the default transport
|
|
232
282
|
|
|
@@ -352,6 +402,24 @@ pikku fabric deploy apply my-branch -y # a named one
|
|
|
352
402
|
that drop or rewrite data — that stays `--allow-destructive`, typed out on
|
|
353
403
|
purpose.
|
|
354
404
|
|
|
405
|
+
### Rebuilding a disposable stage: `--reset`
|
|
406
|
+
|
|
407
|
+
A non-production stage whose migration history or schema has drifted (an app's
|
|
408
|
+
`develop` branch) can be wiped and rebuilt in one deploy:
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
pikku fabric deploy apply develop --reset # asks first, naming app + stage
|
|
412
|
+
pikku fabric deploy apply develop --reset -y # prints the warning, skips the prompt
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
It is the deployed counterpart of `pikku db reset`: **all data on that stage is
|
|
416
|
+
deleted**, every migration is re-applied and `db/<engine>-dev-seed.sql` is
|
|
417
|
+
loaded. It is refused for `--production`, for `main`, and with `--deployment-id`
|
|
418
|
+
(it only applies to a deploy it creates). It needs a fabric server that reports
|
|
419
|
+
the reset back on the deployment; against one that does not, the command fails
|
|
420
|
+
naming the deployment it created without a reset, and wipes nothing. `-y` does
|
|
421
|
+
not imply `--allow-destructive`.
|
|
422
|
+
|
|
355
423
|
Inferring the branch is safe because the git safety check refuses any branch
|
|
356
424
|
without an upstream or out of sync with it, so it cannot ship an unpushed
|
|
357
425
|
commit; the branch it picked is printed before the build starts. A detached
|
|
@@ -430,8 +498,8 @@ repo, and if it is installed with "selected repositories" this one must be in
|
|
|
430
498
|
the selection. There is no CLI flag that works around a missing installation:
|
|
431
499
|
`init` returns "Connect the GitHub account '<owner>'". Send the user to install
|
|
432
500
|
it, or create the project in the console instead (which provisions a Fabric-hosted
|
|
433
|
-
git repo you push to) and
|
|
434
|
-
`
|
|
501
|
+
git repo you push to) and clone it — the remote links the checkout, or put
|
|
502
|
+
the id in `fabric.projectId` in `pikku.config.json`.
|
|
435
503
|
|
|
436
504
|
Deploy refuses to run unless the target branch equals its upstream — the guard
|
|
437
505
|
compares `main` against `main@{upstream}`. So the remote you pushed to must be
|
|
@@ -477,17 +545,16 @@ reviewer has no seed password, so without the control they are locked out of the
|
|
|
477
545
|
app they were asked to look at.
|
|
478
546
|
|
|
479
547
|
Satisfy it with `<DevActorSwitcher />` from `@pikku/mantine/dev`, or with your
|
|
480
|
-
own UI built on `useDevActors()` from `@pikku/react` —
|
|
481
|
-
call
|
|
482
|
-
|
|
548
|
+
own UI built on `useDevActors()` or `signInAsPersona()` from `@pikku/react` —
|
|
549
|
+
validate accepts any of those call sites as evidence, so custom rendering
|
|
550
|
+
passes. Either way the server needs `personaSignIn` on `pikkuActor`; see
|
|
551
|
+
**pikku-scenario** for it and **pikku-react** for the props.
|
|
483
552
|
|
|
484
553
|
The validator also accepts the shapes that predate the package — a hand-rolled
|
|
485
554
|
`signInAsActor()` or a literal `POST /auth/sign-in/actor` — so an older app does
|
|
486
555
|
not fail the build. **Treat that as a grace period, not the target: migrate those
|
|
487
|
-
to `<DevActorSwitcher />`.**
|
|
488
|
-
|
|
489
|
-
had already diverged on the `import.meta.env.DEV` gate that keeps the shared
|
|
490
|
-
secret out of production bundles.
|
|
556
|
+
to `<DevActorSwitcher />`.** They put a per-persona credential in the frontend
|
|
557
|
+
bundle, which the persona endpoint exists to avoid.
|
|
491
558
|
|
|
492
559
|
Do **not** satisfy it with Better Auth's `/dev/quick-login`. That is a different
|
|
493
560
|
endpoint with a different purpose — one fixed admin, not the declared personas —
|
|
@@ -521,7 +588,7 @@ Fix every `error` and `warn` in the output before continuing. Then:
|
|
|
521
588
|
3. **Replace DI/IoC with pikkuServices**: move service construction to `createSingletonServices` in `services.ts`.
|
|
522
589
|
4. **Replace `process.env` calls**: plain config becomes `defineVariable` + `variables.get()`, anything sensitive becomes `defineSecret` + `secrets.getSecret()`.
|
|
523
590
|
5. **Add `pikku.config.json`** at project root with `srcDirectories`, `outDir`, and `clientFiles` — plus `metaLocale` if the team does not work in English, which is the language every `description`, `title` and step `template` is then authored in.
|
|
524
|
-
6. **
|
|
591
|
+
6. **Declare the apps** as `frontends` in `pikku.config.json`. There is no fabric config file; `projectId` is optional; without it the CLI finds the project from the git remote.
|
|
525
592
|
7. **Run `pikku all`** — verify codegen succeeds and there are no type errors.
|
|
526
593
|
8. **Run `pikku fabric validate`** once more to confirm no structural issues remain.
|
|
527
594
|
|
|
@@ -374,6 +374,7 @@ a log line or an audit row. See `pikku-services` for the reveal rules.
|
|
|
374
374
|
import {
|
|
375
375
|
PikkuKysely,
|
|
376
376
|
PgKyselyChannelStore,
|
|
377
|
+
PgKyselyLeaseService,
|
|
377
378
|
PgKyselyWorkflowService,
|
|
378
379
|
} from '@pikku/kysely-postgres'
|
|
379
380
|
|
|
@@ -385,10 +386,22 @@ const createSingletonServices = pikkuServices(async (config) => {
|
|
|
385
386
|
const channelStore = new PgKyselyChannelStore(db.kysely)
|
|
386
387
|
await channelStore.init()
|
|
387
388
|
|
|
388
|
-
const
|
|
389
|
+
const leaseService = new PgKyselyLeaseService(db.kysely)
|
|
390
|
+
await leaseService.init()
|
|
391
|
+
|
|
392
|
+
const workflowService = new PgKyselyWorkflowService(db.kysely, {
|
|
393
|
+
leaseService,
|
|
394
|
+
})
|
|
389
395
|
await workflowService.init()
|
|
390
396
|
|
|
391
|
-
return {
|
|
397
|
+
return {
|
|
398
|
+
config,
|
|
399
|
+
logger,
|
|
400
|
+
database: db,
|
|
401
|
+
channelStore,
|
|
402
|
+
leaseService,
|
|
403
|
+
workflowService,
|
|
404
|
+
}
|
|
392
405
|
})
|
|
393
406
|
```
|
|
394
407
|
|
|
@@ -409,8 +422,20 @@ await channelStore.init()
|
|
|
409
422
|
### MySQL Setup
|
|
410
423
|
|
|
411
424
|
```typescript
|
|
412
|
-
import {
|
|
425
|
+
import {
|
|
426
|
+
MySQLKyselyLeaseService,
|
|
427
|
+
MySQLKyselyWorkflowService,
|
|
428
|
+
} from '@pikku/kysely-mysql'
|
|
429
|
+
|
|
430
|
+
const leaseService = new MySQLKyselyLeaseService(kyselyInstance)
|
|
431
|
+
await leaseService.init()
|
|
413
432
|
|
|
414
|
-
const workflowService = new MySQLKyselyWorkflowService(kyselyInstance
|
|
433
|
+
const workflowService = new MySQLKyselyWorkflowService(kyselyInstance, {
|
|
434
|
+
leaseService,
|
|
435
|
+
})
|
|
415
436
|
await workflowService.init()
|
|
416
437
|
```
|
|
438
|
+
|
|
439
|
+
Every persistent workflow service takes a required `leaseService` and locks runs
|
|
440
|
+
and steps on it. Pass the same instance you register as the app's
|
|
441
|
+
`leaseService`.
|
|
@@ -6,7 +6,7 @@ description: >-
|
|
|
6
6
|
direct usePikkuRPC / usePikkuFetch calls, realtime subscriptions, agent and workflow hooks, and
|
|
7
7
|
the dev actor switcher. TRIGGER when: writing a React component that fetches or mutates backend
|
|
8
8
|
data, wiring PikkuProvider, paginating, running or tracking a workflow from the client, or
|
|
9
|
-
asking about useDevActors /
|
|
9
|
+
asking about useDevActors / DevActorSwitcher / quick login. DO NOT TRIGGER when: working on the
|
|
10
10
|
backend (use pikku-wiring), defining the workflow itself (use pikku-workflow), or writing
|
|
11
11
|
user-facing copy (use pikku-i18n).
|
|
12
12
|
installGroups: [client]
|
|
@@ -251,14 +251,9 @@ their own sandbox.
|
|
|
251
251
|
```tsx
|
|
252
252
|
import { useDevActors } from '@pikku/react'
|
|
253
253
|
|
|
254
|
-
const { actors, signInAs,
|
|
255
|
-
// Gate both reads on the bundler's dev flag so no credential can reach a
|
|
256
|
-
// production bundle. The sandbox dev server bakes them from your personas.
|
|
257
|
-
actors: import.meta.env.DEV ? import.meta.env.VITE_DEV_ACTORS : undefined,
|
|
258
|
-
secrets: import.meta.env.DEV
|
|
259
|
-
? import.meta.env.VITE_DEV_ACTOR_SECRETS
|
|
260
|
-
: undefined,
|
|
254
|
+
const { actors, signInAs, pendingId, isPending, error } = useDevActors({
|
|
261
255
|
apiUrl: apiUrl(),
|
|
256
|
+
app: appSlug,
|
|
262
257
|
onSignedIn: () => navigate({ to: '/' }),
|
|
263
258
|
})
|
|
264
259
|
```
|
|
@@ -267,21 +262,19 @@ const { actors, signInAs, pendingEmail, isPending, error } = useDevActors({
|
|
|
267
262
|
`<DevActorSwitcher />` from `@pikku/mantine/dev` — a separate entry point from
|
|
268
263
|
`@pikku/mantine/core`, whose contract is "drop-in alias for `@mantine/core`"
|
|
269
264
|
and so must not export components Mantine has no counterpart for.
|
|
270
|
-
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
copy-paste, including the `import.meta.env.DEV` gate, is exactly what this
|
|
284
|
-
replaced.
|
|
265
|
+
- **No credential reaches the bundle.** It lists from `/auth/sign-in/personas`
|
|
266
|
+
and `signInAs(id)` posts only the persona id to `/auth/sign-in/persona`. The
|
|
267
|
+
server decides who is offered and who may sign in, and offers nobody in
|
|
268
|
+
production — see **pikku-scenario** for `personaSignIn`.
|
|
269
|
+
- **It takes `onSignedIn` rather than a router**, since every app lands
|
|
270
|
+
somewhere different.
|
|
271
|
+
- `listDevActors()` and `signInAsPersona()` are exported too, for a non-React
|
|
272
|
+
caller. The endpoint only
|
|
273
|
+
signs in rows flagged `actor: true`, so it can never impersonate a real user —
|
|
274
|
+
see **pikku-auth**.
|
|
275
|
+
|
|
276
|
+
Do not hand-write the list-and-sign-in pair per app; that copy-paste is exactly
|
|
277
|
+
what this replaced.
|
|
285
278
|
|
|
286
279
|
### Linking from a Mantine element: `renderRoot`, not `component`
|
|
287
280
|
|
|
@@ -83,13 +83,17 @@ Declared actors are not only for automated runs. `signInPath` is Better Auth's
|
|
|
83
83
|
frontend gets a one-click "Sign in as …" switcher over the **same** list, and an
|
|
84
84
|
app can be reviewed as each kind of user without anyone knowing a seed password.
|
|
85
85
|
|
|
86
|
-
The
|
|
87
|
-
personas
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
The switcher holds no credential. It lists personas from
|
|
87
|
+
`/auth/sign-in/personas` and signs in by posting only a persona id to
|
|
88
|
+
`/auth/sign-in/persona`; the server resolves the address. One server piece
|
|
89
|
+
serves both, in the auth config:
|
|
90
|
+
|
|
91
|
+
```ts snippet:personaSignIn
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
It is always open under `pikku dev`. A deployed stage needs actor
|
|
95
|
+
sign-in opted in **and** its `devSwitcher` feature flag on; production never
|
|
96
|
+
has the opt-in, so it lists nobody and refuses every persona sign-in.
|
|
93
97
|
|
|
94
98
|
Do not hand-roll the switcher: `useDevActors()` (`pikku-react`, a separate install) is the logic and
|
|
95
99
|
`<DevActorSwitcher />` from `@pikku/mantine/dev` is a ready rendering of it.
|
|
@@ -98,52 +102,16 @@ one — without it a reviewer is locked out of their own sandbox.
|
|
|
98
102
|
When the switcher is missing, it is one of three things, and none of them
|
|
99
103
|
errors:
|
|
100
104
|
|
|
101
|
-
- **The
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
- **The list is empty.** On a deployed stage that is the gate
|
|
106
|
+
doing its job — check the opt-in and the `devSwitcher` flag. Locally, check
|
|
107
|
+
the personas declare an `email` (via `scenarios.emailDomain`) and are not
|
|
108
|
+
`runnable: false`.
|
|
109
|
+
- **`personaSignIn` is missing, or the frontend calls another API.** A dev
|
|
110
|
+
proxy (`VITE_API_PROXY`, default `http://localhost:3000`) that points at
|
|
111
|
+
another project's API lists that project's personas, or none.
|
|
106
112
|
- **It is not mounted on the page you are looking at.** The template mounts it
|
|
107
113
|
on the login screen. A public homepage that replaces the `/` → `/app`
|
|
108
114
|
redirect needs its own `<DevActorSwitcher />` in the public layout.
|
|
109
115
|
|
|
110
|
-
When the switcher
|
|
111
|
-
|
|
112
|
-
whose dev proxy (`VITE_API_PROXY`, default `http://localhost:3000`) points at
|
|
113
|
-
another project's API sends the sign-in there.
|
|
114
|
-
|
|
115
|
-
**A runner of your own that starts vite has to bake them itself**, from the
|
|
116
|
-
generated persona meta (`<outDir>/workflow/personas.gen.json`, which already
|
|
117
|
-
carries the derived `email`):
|
|
118
|
-
|
|
119
|
-
```js
|
|
120
|
-
const personas = Object.values(JSON.parse(readFileSync(personasPath, 'utf8')))
|
|
121
|
-
|
|
122
|
-
env.VITE_DEV_ACTORS = JSON.stringify(
|
|
123
|
-
personas.map(({ id, email, name, jobTitle }) => ({
|
|
124
|
-
key: id,
|
|
125
|
-
email,
|
|
126
|
-
name,
|
|
127
|
-
jobTitle: jobTitle ?? '',
|
|
128
|
-
}))
|
|
129
|
-
)
|
|
130
|
-
env.VITE_DEV_ACTOR_SECRETS = JSON.stringify(
|
|
131
|
-
Object.fromEntries(
|
|
132
|
-
await Promise.all(
|
|
133
|
-
personas.map(async ({ email }) => [
|
|
134
|
-
email,
|
|
135
|
-
await deriveActorSecret(env.SCENARIO_ACTOR_SECRET, email),
|
|
136
|
-
])
|
|
137
|
-
)
|
|
138
|
-
)
|
|
139
|
-
)
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
**Set `SCENARIO_ACTOR_SECRET` yourself**, at least 32 characters, in the
|
|
143
|
-
environment both processes read. Left unset, `pikku dev` mints an ephemeral root
|
|
144
|
-
for its own run that a separately spawned vite cannot see, so the two derive
|
|
145
|
-
from different roots: the switcher renders every persona and each click is
|
|
146
|
-
refused, which reads as a broken login rather than missing configuration. On a
|
|
147
|
-
brand-new project the persona file does not exist until the first `pikku dev`
|
|
148
|
-
codegen, after vite has baked an empty list — watch it and restart the frontend
|
|
149
|
-
when it changes.
|
|
116
|
+
When the switcher lists personas but a click 404s, that persona has no `email`
|
|
117
|
+
or is `runnable: false`.
|
|
@@ -46,6 +46,7 @@ behind the interface.
|
|
|
46
46
|
| `PikkuWorkflowService`, `WorkflowRunService` | Redis, MongoDB | `@pikku/redis`, `@pikku/mongodb` |
|
|
47
47
|
| `SessionStore`, `AgentRunService`, `DeploymentService` | Redis, MongoDB | `@pikku/redis`, `@pikku/mongodb` |
|
|
48
48
|
| `AgentStorageService`, `AgentRunStateService` | MongoDB **only** | `@pikku/mongodb` |
|
|
49
|
+
| `LeaseService` | `RedisLeaseService` | `@pikku/redis` |
|
|
49
50
|
|
|
50
51
|
SQL is the third option for every store interface in that table —
|
|
51
52
|
`KyselyChannelStore`, `KyselyWorkflowService`, `KyselySecretService` and friends
|