@pikku/cli 0.12.85 → 0.12.88

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.
Files changed (138) hide show
  1. package/console-app/assets/{index-CEv-kj3J.js → index-C3FPwdl7.js} +161 -147
  2. package/console-app/index.html +1 -1
  3. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  4. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  5. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  6. package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
  7. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  8. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  9. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  10. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  11. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +4 -0
  12. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  13. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  14. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  15. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +4 -0
  16. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  17. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  18. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  19. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  20. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  21. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  22. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  23. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  24. package/dist/.pikku/function/pikku-functions-meta.gen.json +41 -17
  25. package/dist/.pikku/function/pikku-functions.gen.js +3 -1
  26. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  27. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  28. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  29. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  30. package/dist/.pikku/pikku-bootstrap.gen.d.ts +3 -1
  31. package/dist/.pikku/pikku-bootstrap.gen.js +3 -1
  32. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  33. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  34. package/dist/.pikku/pikku-services.gen.d.ts +2 -1
  35. package/dist/.pikku/pikku-services.gen.js +1 -0
  36. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  37. package/dist/.pikku/pikku-types.gen.js +1 -1
  38. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  39. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  40. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.d.ts +1 -0
  41. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +6 -0
  42. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.json +6 -0
  43. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +4 -0
  44. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +5 -0
  45. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  46. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -0
  47. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  48. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  49. package/dist/.pikku/schemas/register.gen.js +3 -1
  50. package/dist/.pikku/schemas/schemas/FabricLoginInput.schema.json +1 -1
  51. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  52. package/dist/.pikku/schemas/schemas/PikkuUserAdminFunctionsOutput.schema.json +1 -0
  53. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  54. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  55. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  56. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  57. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  58. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  59. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  60. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  61. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  62. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  63. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  64. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  65. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  66. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +8 -2
  67. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  68. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  69. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  70. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  71. package/dist/bin/pikku-bin.mjs +2 -2
  72. package/dist/bin/pikku.js +22 -0
  73. package/dist/src/fabric/fabric-commands.d.ts +6 -3
  74. package/dist/src/fabric/fabric-commands.js +4 -0
  75. package/dist/src/fabric/functions/deploy.function.js +6 -0
  76. package/dist/src/fabric/functions/init.function.js +2 -2
  77. package/dist/src/fabric/functions/llm-key.function.d.ts +4 -4
  78. package/dist/src/fabric/functions/login.function.d.ts +4 -0
  79. package/dist/src/fabric/functions/login.function.js +45 -4
  80. package/dist/src/fabric/functions/validate.function.js +12 -2
  81. package/dist/src/fabric/lib/config.d.ts +8 -0
  82. package/dist/src/fabric/lib/config.js +15 -1
  83. package/dist/src/fabric/lib/git.js +2 -2
  84. package/dist/src/functions/wirings/ai-agent/pikku-command-public-agent.js +6 -2
  85. package/dist/src/functions/wirings/ai-agent/serialize-public-agent.d.ts +5 -1
  86. package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +81 -48
  87. package/dist/src/functions/wirings/auth/pikku-command-auth.js +4 -0
  88. package/dist/src/functions/wirings/console/pikku-command-console-functions.js +6 -2
  89. package/dist/src/functions/wirings/console/serialize-console-functions.d.ts +5 -1
  90. package/dist/src/functions/wirings/console/serialize-console-functions.js +55 -23
  91. package/dist/src/functions/wirings/functions/schemas.js +10 -1
  92. package/dist/src/functions/wirings/graph/pikku-command-graph-wirings.js +2 -0
  93. package/dist/src/functions/wirings/queue/pikku-command-queue.js +10 -2
  94. package/dist/src/functions/wirings/realtime/pikku-command-events-scaffold.js +8 -2
  95. package/dist/src/functions/wirings/realtime/serialize-events-scaffold.d.ts +5 -4
  96. package/dist/src/functions/wirings/realtime/serialize-events-scaffold.js +17 -7
  97. package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.js +6 -2
  98. package/dist/src/functions/wirings/rpc/pikku-command-remote-rpc.js +6 -2
  99. package/dist/src/functions/wirings/rpc/serialize-public-rpc.d.ts +5 -1
  100. package/dist/src/functions/wirings/rpc/serialize-public-rpc.js +16 -5
  101. package/dist/src/functions/wirings/rpc/serialize-remote-rpc.d.ts +5 -1
  102. package/dist/src/functions/wirings/rpc/serialize-remote-rpc.js +16 -5
  103. package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.js +8 -2
  104. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.d.ts +5 -1
  105. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.js +22 -16
  106. package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.d.ts +1 -0
  107. package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.js +49 -0
  108. package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.d.ts +24 -0
  109. package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.js +283 -0
  110. package/dist/src/functions/wirings/webhook/pikku-command-webhook.js +6 -2
  111. package/dist/src/functions/wirings/webhook/serialize-webhook.d.ts +5 -8
  112. package/dist/src/functions/wirings/webhook/serialize-webhook.js +19 -12
  113. package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.js +8 -2
  114. package/dist/src/functions/wirings/workflow/serialize-workflow-routes.d.ts +5 -1
  115. package/dist/src/functions/wirings/workflow/serialize-workflow-routes.js +61 -31
  116. package/dist/src/functions/workflows/all.workflow.js +8 -0
  117. package/dist/src/utils/pikku-cli-config.js +40 -10
  118. package/dist/src/utils/remove-legacy-scaffold-file.d.ts +3 -0
  119. package/dist/src/utils/remove-legacy-scaffold-file.js +43 -0
  120. package/dist/src/utils/serialize-schemas.js +57 -2
  121. package/dist/tsconfig.tsbuildinfo +1 -1
  122. package/package.json +7 -7
  123. package/skills/pikku-addon/SKILL.md +1 -0
  124. package/skills/pikku-ai-agent/SKILL.md +2 -1
  125. package/skills/pikku-ai-vercel/SKILL.md +1 -0
  126. package/skills/pikku-better-auth/SKILL.md +57 -0
  127. package/skills/pikku-concepts/SKILL.md +17 -1
  128. package/skills/pikku-concepts/references/concept-mapping.md +11 -9
  129. package/skills/pikku-emails/SKILL.md +1 -0
  130. package/skills/pikku-fabric/SKILL.md +92 -19
  131. package/skills/pikku-fabric-debug/SKILL.md +112 -0
  132. package/skills/pikku-permissions/SKILL.md +28 -12
  133. package/skills/pikku-react/SKILL.md +32 -3
  134. package/skills/pikku-react-query/SKILL.md +38 -1
  135. package/skills/pikku-realtime/SKILL.md +1 -1
  136. package/skills/pikku-security/SKILL.md +3 -3
  137. package/skills/pikku-template-clone/SKILL.md +1 -0
  138. package/cli.schema.json +0 -1
@@ -55,7 +55,7 @@ Fabric apps use SQLite, accessed via Kysely with the libSQL HTTP adapter. NOT Po
55
55
  ```typescript
56
56
  import { Kysely, CamelCasePlugin } from 'kysely'
57
57
  import { LibsqlWebDialect } from '@pikku/kysely-sqlite'
58
- import type { DB } from './types/db.types.js'
58
+ import type { DB } from '#pikku/db/schema.gen.js'
59
59
 
60
60
  const databaseUrl = await variables.get('DATABASE_URL')
61
61
  let kysely: Kysely<DB>
@@ -75,18 +75,31 @@ Fabric injects `DATABASE_URL` as a variable binding when the stage starts. In lo
75
75
 
76
76
  ### Migrations
77
77
 
78
- Migrations are plain `.sql` files in `packages/functions/db/migrations/`, numbered sequentially:
78
+ Migrations are plain `.sql` files at the **project root**, in a directory named
79
+ for the engine — `db/sqlite/` for SQLite/libSQL stages, `db/postgres/` for
80
+ Postgres ones. Never `db/migrations/`, and never under `packages/functions/`:
81
+ the deploy pipeline stages `db/<engine>/*.sql` from the root and applies them
82
+ after upload, so a migration anywhere else is silently never run.
79
83
 
80
84
  ```
81
- db/migrations/
85
+ db/sqlite/
82
86
  0001-init.sql
83
87
  0002-add-users.sql
84
88
  ```
85
89
 
86
- Run migrations: `pikku db migrate`
87
- Generate Kysely types after migrations: `yarn db:types` (uses kysely-codegen)
90
+ Numbers must be consecutive and gap-free, and an applied migration is frozen —
91
+ correct a mistake with a new forward migration, never by editing or renaming one
92
+ that has already run (the recorded hash will no longer match).
88
93
 
89
- **NEVER hand-edit `src/types/db.types.ts`** it is generated by kysely-codegen.
94
+ Run migrations: `pikku db migrate`. It also regenerates `.pikku/db/schema.gen.ts`
95
+ (Kysely types) and `.pikku/db/zod.gen.ts` — there is no separate types step.
96
+
97
+ **NEVER hand-edit the generated schema** — write a migration and re-run.
98
+
99
+ A Better Auth app has a second constraint: the plugins you enable (`admin()`,
100
+ `actor()`, …) each declare columns, and `pikku db migrate` refuses to run while
101
+ the applied schema is missing any of them. `pikku db generate` writes the
102
+ migration that closes the gap.
90
103
 
91
104
  ### Column conventions
92
105
 
@@ -135,28 +148,27 @@ packages/functions/
135
148
  services.ts # pikkuServices factory (singleton)
136
149
  middleware.ts # Shared middleware
137
150
  permissions.ts # Shared permissions
138
- types/
139
- db.types.ts # Generated by kysely-codegen — NEVER hand-edit
140
- db/
141
- migrations/ # Plain .sql files, numbered sequentially
142
- .pikku/ # Generated by pikku all — gitignored
151
+ .pikku/
152
+ db/schema.gen.ts # Kysely types, written by `pikku db migrate` — NEVER hand-edit
153
+ apps/app/ # Frontend(s)
154
+ db/sqlite/ # Plain .sql migrations, numbered, gap-free (project root)
143
155
  pikku.config.json # Pikku + deploy config (project root)
144
- fabric.config.json # Fabric environment + frontend config (project root)
156
+ pikkufabric.config.json # Fabric project link + frontends (project root)
145
157
  ```
146
158
 
147
- ## `fabric.config.json`
159
+ ## `pikkufabric.config.json`
148
160
 
149
- Declares environments and frontend dev servers:
161
+ Links the repo to a Fabric project and declares its frontends:
150
162
 
151
163
  ```json
152
164
  {
153
165
  "projectId": "my-project-id",
154
166
  "production": {
155
- "branch": "main"
167
+ "domain": "example.com"
156
168
  },
157
169
  "frontends": {
158
170
  "app": {
159
- "cwd": "apps/next-app",
171
+ "cwd": "apps/app",
160
172
  "primary": true,
161
173
  "deploy": true,
162
174
  "kind": "ssr",
@@ -170,7 +182,11 @@ Declares environments and frontend dev servers:
170
182
  }
171
183
  ```
172
184
 
173
- - `production.branch`: the git branch that maps to the production stage
185
+ - `projectId`: written by `pikku fabric init` / `link`. Templates ship the
186
+ `__PROJECT_ID__` placeholder — that is *not* a link, and the CLI treats it as
187
+ unlinked.
188
+ - `production.domain`: optional custom domain. Production always maps to `main`;
189
+ without a domain it lives on the platform `*.pikkufabric.app` hostnames.
174
190
  - `frontends`: each entry declares a frontend app with its dev command and port
175
191
 
176
192
  ## RPC is the default transport
@@ -198,6 +214,63 @@ Add `wireHTTP` only when you need a specific REST shape (webhooks, third-party c
198
214
  - Every new or changed function must have a real description.
199
215
  - If function metadata would show `missing description`, the work is not finished yet.
200
216
 
217
+ ## Run it locally
218
+
219
+ A Fabric app is two processes: the pikku API server (`:3000`) and the frontend
220
+ (vite). The starter template's `bun run dev` starts **both** and takes the whole
221
+ session down if either dies — a frontend running against a dead API looks like an
222
+ app bug and is the single most common way to waste an hour here.
223
+
224
+ ```bash
225
+ bun run prebuild # pikku all — codegen must be current before the server boots
226
+ bun run dev
227
+ ```
228
+
229
+ Then open the app, sign up as a real user, and click through what you built.
230
+ **HTTP 200 is not evidence.** These are client-rendered pages: the server returns
231
+ 200 with an empty shell, so a page whose component throws still looks fine to
232
+ curl. Either open it in a browser or drive it headlessly and assert on rendered
233
+ text.
234
+
235
+ Secrets come from `process.env`, which the CLI populates from a `.env` in the
236
+ working directory. `BETTER_AUTH_SECRET` is required — without it the first
237
+ sign-up fails with `Requested secret not found`, which names no key and points at
238
+ no file. The starter template generates one on first `bun run dev`.
239
+
240
+ If you are running the two processes yourself rather than through the template's
241
+ script, run `pikku dev` from the **project root** (it resolves `srcDirectories`
242
+ relative to the config, so a nested cwd yields a doubled watch path and no hot
243
+ reload).
244
+
245
+ ## Deploy
246
+
247
+ ```bash
248
+ pikku fabric login # opens a browser; needs a human, wait for it
249
+ pikku fabric init https://github.com/<owner>/<repo>
250
+ pikku fabric validate # must pass clean
251
+ pikku fabric deploy plan --production
252
+ pikku fabric deploy apply --production --auto-apply
253
+ ```
254
+
255
+ `apply` confirms before deploying, and with no TTY to ask — CI, an agent shell —
256
+ it refuses rather than hangs. `--auto-apply` supplies that confirmation; drop it
257
+ only when a human is at a real terminal.
258
+
259
+ `init` adopts a **GitHub** repo, and adoption goes through the Pikku Fabric
260
+ GitHub App — the app has to be installed on the account or org that owns the
261
+ repo, and if it is installed with "selected repositories" this one must be in
262
+ the selection. There is no CLI flag that works around a missing installation:
263
+ `init` returns "Connect the GitHub account '<owner>'". Send the user to install
264
+ it, or create the project in the console instead (which provisions a Fabric-hosted
265
+ git repo you push to) and write the returned `projectId` into
266
+ `pikkufabric.config.json` yourself.
267
+
268
+ Deploy refuses to run unless the target branch equals its upstream — the guard
269
+ compares `main` against `main@{upstream}`. So the remote you pushed to must be
270
+ the one the branch tracks; a stale `origin` left over from scaffolding blocks
271
+ the deploy with "local HEAD … ≠ remote …" even though your code is pushed.
272
+ `git branch --set-upstream-to=<remote>/main main` before deploying.
273
+
201
274
  ## Versioning
202
275
 
203
276
  Functions with `expose: true` are versioned via `versions.pikku.json`. When you change a function's input or output schema, you must bump its version number — otherwise `pikku all` will report a breaking change and callers' generated clients become stale.
@@ -221,7 +294,7 @@ These apply in every Fabric app:
221
294
  - **No `as any`** — fix types properly.
222
295
  - **No generic `Error`** — throw `NotFoundError`, `ConflictError`, `BadRequestError`, `UnauthorizedError` from `@pikku/core/errors`.
223
296
  - **No auth checks in function bodies** — use `permissions:` field on the function config with a `pikkuPermission` factory.
224
- - **No hand-editing `db.types.ts`** — always regenerate with `yarn db:types` after migrations.
297
+ - **No hand-editing `.pikku/db/schema.gen.ts`** — write a migration and re-run `pikku db migrate`.
225
298
  - **One runtime unit per file** — never define multiple functions/workflows in a single source file.
226
299
  - **Workflow steps don't need manual wiring** — `pikkuSessionlessFunc` step functions in `*.steps.ts` files are auto-discovered by codegen.
227
300
 
@@ -235,7 +308,7 @@ pikku fabric validate --json
235
308
 
236
309
  Fix every `error` and `warn` in the output before continuing. Then:
237
310
 
238
- 1. **Replace the database layer**: swap PostgreSQL/MySQL queries for Kysely + libSQL. Convert schema to SQLite-compatible SQL migrations in `db/migrations/`.
311
+ 1. **Replace the database layer**: swap PostgreSQL/MySQL queries for Kysely + libSQL. Convert schema to SQLite-compatible SQL migrations in `db/sqlite/`.
239
312
  2. **Replace route handlers with pikkuFuncs**: extract business logic into `pikkuFunc`/`pikkuSessionlessFunc`, add `wireHTTP` or `expose: true` for transport.
240
313
  3. **Replace DI/IoC with pikkuServices**: move service construction to `createSingletonServices` in `services.ts`.
241
314
  4. **Replace `process.env` calls** with `wireVariable`/`wireSecret` + `variables.get()`.
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: pikku-fabric-debug
3
+ description: 'Debug a deployed Fabric stage from the CLI — read logs, find recent errors, follow a single request end-to-end by traceId, and check request/error/latency metrics. TRIGGER when: a deployed Fabric app is erroring, timing out, or behaving differently than local; the user asks "why is prod failing", "check the logs", "what happened to this request"; or a deploy succeeded but the app misbehaves. DO NOT TRIGGER when: the failure reproduces locally (debug it locally), the deploy itself failed (use pikku-fabric — that is a build/config problem, not a runtime one), or the project is not deployed to Fabric.'
4
+ installGroups: [fabric]
5
+ ---
6
+
7
+ # Debugging a deployed Fabric stage
8
+
9
+ ## Agent Operating Procedure
10
+
11
+ Use this skill as an execution checklist, not reference material.
12
+
13
+ 1. Reproduce locally first. If it fails locally too, debug it there — the
14
+ deployed stage adds cost and latency to every iteration.
15
+ 2. Start from `errors`, not `logs`. Errors are already filtered and carry the
16
+ traceId that unlocks the rest.
17
+ 3. Follow one trace end-to-end before forming a theory. A single failing request
18
+ tells you more than a hundred unrelated log lines.
19
+ 4. Fix the source cause and redeploy. Never leave the diagnosis at "it is flaky".
20
+ 5. Confirm the fix against the same stage — recheck `errors` for the function.
21
+
22
+ Every command below requires a logged-in CLI and a linked project. Both fail
23
+ with the exact remediation if not:
24
+
25
+ ```
26
+ Not logged in. Run `pikku fabric login` first.
27
+ No fabric project linked. Run `pikku fabric link` first.
28
+ ```
29
+
30
+ ## The loop
31
+
32
+ **1 — What is broken?**
33
+
34
+ ```bash
35
+ pikku fabric errors -b main # branch defaults to main
36
+ pikku fabric errors -b main --function createOrder
37
+ ```
38
+
39
+ Prints a `WHEN | FUNCTION | TRACE | MESSAGE` table. The message is **truncated
40
+ to 100 characters** — treat it as a label, not the full error. The TRACE column
41
+ is the input to the next step.
42
+
43
+ **2 — What happened in that one request?**
44
+
45
+ ```bash
46
+ pikku fabric trace <traceId> -b main
47
+ pikku fabric trace <traceId> -b main --json
48
+ ```
49
+
50
+ `--branch` is **required** here (no default). Each event prints as:
51
+
52
+ ```
53
+ <timestamp> <scriptName> <wireType>:<wireId> <duration>ms — <error|message|outcome>
54
+ ```
55
+
56
+ This is the whole request across the stage — every unit it touched, in order,
57
+ with per-event durations. The last event before the failure is where to look.
58
+
59
+ **3 — Is it one request or the whole stage?**
60
+
61
+ ```bash
62
+ pikku fabric metrics -b main # last 24h
63
+ pikku fabric metrics -b main --hours 2 --function createOrder
64
+ ```
65
+
66
+ Rows are `reqs= err= (rate%) avg= min= max=` per bucket. A single bad request
67
+ with a healthy error rate is a data problem; a climbing error rate is a
68
+ deployment or dependency problem. `--json` additionally returns a `wireTypes`
69
+ breakdown (requests per http/queue/scheduler/…) that the table output omits.
70
+
71
+ **4 — Wider context around the failure**
72
+
73
+ ```bash
74
+ pikku fabric logs -b main
75
+ pikku fabric logs -b main --level warn
76
+ pikku fabric logs -b main -f # follow
77
+ ```
78
+
79
+ `--branch` is **required** — `logs` throws `Specify --branch <branch-name>.`
80
+ without it, even though the flag reads as optional.
81
+
82
+ **5 — Is the running code the code you think it is?**
83
+
84
+ ```bash
85
+ pikku fabric status # active + in-flight deployment, per stage, with gitSha
86
+ ```
87
+
88
+ Check this *before* deep-diving. A stage still serving an older `gitSha`, or a
89
+ deploy stuck in flight, explains a whole class of "my fix did nothing".
90
+
91
+ ## Known gaps — do not misread these as bugs in your app
92
+
93
+ - **`pikku fabric logs --since` and `--deployment` are accepted and ignored.**
94
+ They are declared as options but the command never reads them, so
95
+ `--since 15m` silently returns the same default window as no flag at all. Do
96
+ not conclude "nothing happened in the last 15 minutes" from it. Narrow by
97
+ `--level`, or by `--function` via `errors`, instead.
98
+ - **`--follow` is a 2-second client-side poll, not a server stream.** It
99
+ dedups against what it already printed, so it behaves like `tail -f`, but new
100
+ entries can appear up to ~2s late and it holds the process open until killed.
101
+
102
+ ## What NOT to do
103
+
104
+ - **Do not SSH anywhere or query the telemetry backend directly.** These
105
+ commands are the supported surface; anything lower-level is Fabric-internal
106
+ and will not exist for your project.
107
+ - **Do not debug by redeploying with added `console.log`s.** Get the traceId,
108
+ read the trace. A deploy cycle per hypothesis is the slow path.
109
+ - **Do not read the truncated `errors` message as the full error.** Always
110
+ confirm against `trace` before changing code.
111
+ - **Do not treat an empty `errors` table as "the app is fine"** — a request that
112
+ returns a wrong 200 logs nothing. Check `metrics` for the outcome mix.
@@ -49,20 +49,36 @@ export const deleteBook = pikkuFunc({
49
49
 
50
50
  ### `pikkuAuth(fn)` — Session-Only Checks
51
51
 
52
- Use for checks that only need the session no request data required.
52
+ Use for checks that read the session but need no request data — and that assert
53
+ something **beyond** merely having a session (a flag, a tier, a claim).
53
54
 
54
55
  ```typescript
55
56
  import { pikkuAuth } from '#pikku'
56
57
 
57
- export const isAuthenticated = pikkuAuth(
58
- async (_services, session) => !!session
58
+ // Good: a real gate on the session's contents, not just its existence.
59
+ export const isVerified = pikkuAuth(
60
+ async (_services, session) => !!session?.emailVerified
59
61
  )
62
+ ```
60
63
 
61
- export const isAdmin = pikkuAuth(
62
- async (_services, session) => session?.role === 'admin'
63
- )
64
+ **Do NOT write an "is signed in" permission.** A checker that just returns
65
+ `!!session` is not authorization it re-checks authentication, which the
66
+ function already enforces. A function that needs a signed-in user sets
67
+ `auth: true` (the default for `pikkuFunc`); it does not also carry a
68
+ `permissions: { signedIn }`.
69
+
70
+ ```typescript
71
+ // WRONG — redundant with auth: true; adds a permission that gates nothing.
72
+ export const isSignedIn = pikkuAuth(async (_s, session) => !!session)
73
+ pikkuFunc({ auth: true, permissions: { signedIn: isSignedIn }, /* ... */ })
74
+
75
+ // RIGHT — auth: true already requires the session; permissions are for capability.
76
+ pikkuFunc({ auth: true, /* ... */ })
64
77
  ```
65
78
 
79
+ A permission answers "*may this user do this?*" (role, ownership, tier) — never
80
+ "*is there a session?*".
81
+
66
82
  ### `pikkuPermission(fn)` — Data-Aware Checks
67
83
 
68
84
  Use when authorization depends on the actual request data (e.g., resource ownership).
@@ -88,11 +104,11 @@ export const hasBookAccess = pikkuPermission(
88
104
 
89
105
  ```typescript
90
106
  permissions: {
91
- admin: isAdmin, // OR: admins can access
107
+ verified: isVerified, // OR: verified users can access
92
108
  owner: isBookOwner, // OR: owners can access
93
109
  reviewer: [isAuthenticated, hasBookAccess], // AND: both must pass
94
110
  }
95
- // Logic: admin OR owner OR (isAuthenticated AND hasBookAccess)
111
+ // Logic: verified OR owner OR (isAuthenticated AND hasBookAccess)
96
112
  ```
97
113
 
98
114
  Groups are OR'd. Entries within a group array are AND'd.
@@ -107,7 +123,7 @@ export const deleteBook = pikkuFunc({
107
123
  await db.deleteBook(bookId)
108
124
  },
109
125
  permissions: {
110
- admin: isAdmin,
126
+ verified: isVerified,
111
127
  owner: isBookOwner,
112
128
  },
113
129
  })
@@ -146,8 +162,8 @@ export const isAuthenticated = pikkuAuth(
146
162
  async (_services, session) => !!session
147
163
  )
148
164
 
149
- export const isAdmin = pikkuAuth(
150
- async (_services, session) => session?.role === 'admin'
165
+ export const isVerified = pikkuAuth(
166
+ async (_services, session) => !!session?.emailVerified
151
167
  )
152
168
 
153
169
  export const isOrgMember = pikkuPermission(
@@ -162,7 +178,7 @@ export const deleteOrg = pikkuFunc({
162
178
  await db.deleteOrg(orgId)
163
179
  },
164
180
  permissions: {
165
- admin: isAdmin,
181
+ verified: isVerified,
166
182
  owner: [isAuthenticated, isOrgMember],
167
183
  },
168
184
  })
@@ -36,15 +36,44 @@ import {
36
36
  Five exports. `usePikkuRealtime` is only valid when you wired a
37
37
  `PikkuRealtime` class via `createPikku` — see step 3 below.
38
38
 
39
+ ## Resolving the server URL
40
+
41
+ Every client (`createPikku`, realtime, the auth client) resolves its base
42
+ through one shared helper in `src/lib/env.ts`. Write this once:
43
+
44
+ ```ts
45
+ // Endpoints come from env, never hardcoded.
46
+ export function apiUrl(): string {
47
+ // SSR: the client hooks only run in the browser, so a placeholder is fine.
48
+ if (import.meta.env.SSR) {
49
+ return import.meta.env.VITE_API_URL ?? '/__api'
50
+ }
51
+ return import.meta.env.VITE_API_URL ?? `${window.location.origin}/api`
52
+ }
53
+ ```
54
+
55
+ **Never fall back to `http://localhost:3000`.** `import.meta.env.VITE_API_URL`
56
+ is substituted by Vite at *build* time, so any deploy that supplies the URL as
57
+ a *runtime* env var or platform binding leaves it `undefined` in the shipped
58
+ bundle — the fallback is then the only branch that ever runs in the browser. A
59
+ localhost fallback means every request from a deployed app goes to the user's
60
+ own machine. `origin + '/api'` is same-origin, needs no build-time knowledge of
61
+ the domain, and is correct wherever the app is served from.
62
+
63
+ For local dev, set `VITE_API_URL`, or proxy `/api` → your backend in
64
+ `vite.config.ts` under `server.proxy`. One `/api` entry also covers
65
+ `/api/auth/*`; only add more entries for root-level routes outside `/api`.
66
+
39
67
  ## Setup at the app root
40
68
 
41
69
  ```tsx
42
70
  import { createPikku, PikkuProvider } from '@pikku/react'
43
71
  import { PikkuFetch } from './pikku/pikku-fetch.gen'
44
72
  import { PikkuRPC } from './pikku/pikku-rpc.gen'
73
+ import { apiUrl } from './lib/env'
45
74
 
46
75
  const pikku = createPikku(PikkuFetch, PikkuRPC, {
47
- serverUrl: import.meta.env.VITE_API_URL ?? 'http://localhost:3000',
76
+ serverUrl: apiUrl(),
48
77
  })
49
78
 
50
79
  createRoot(document.getElementById('root')!).render(
@@ -62,7 +91,7 @@ the `PikkuRealtime` class as the third argument and the instance gets a
62
91
  import { PikkuRealtime } from './pikku/realtime.gen'
63
92
 
64
93
  const pikku = createPikku(PikkuFetch, PikkuRPC, PikkuRealtime, {
65
- serverUrl: import.meta.env.VITE_API_URL ?? 'http://localhost:3000',
94
+ serverUrl: apiUrl(),
66
95
  })
67
96
  // pikku.fetch / pikku.rpc / pikku.realtime — all share the same fetch
68
97
  // (server URL + auth configured once).
@@ -159,7 +188,7 @@ or set headers on the fetch instance after creation. Common pattern:
159
188
 
160
189
  ```tsx
161
190
  const pikku = createPikku(PikkuFetch, PikkuRPC, {
162
- serverUrl: '...',
191
+ serverUrl: apiUrl(),
163
192
  fetchOptions: {
164
193
  onRequest: (req) => {
165
194
  const token = localStorage.getItem('token')
@@ -59,9 +59,11 @@ import { PikkuProvider, createPikku } from '@pikku/react'
59
59
  import { PikkuFetch } from './pikku/pikku-fetch.gen'
60
60
  import { PikkuRPC } from './pikku/pikku-rpc.gen'
61
61
 
62
+ import { apiUrl } from './lib/env'
63
+
62
64
  const queryClient = new QueryClient()
63
65
  const pikku = createPikku(PikkuFetch, PikkuRPC, {
64
- serverUrl: import.meta.env.VITE_API_URL ?? 'http://localhost:3000',
66
+ serverUrl: apiUrl(),
65
67
  })
66
68
 
67
69
  <QueryClientProvider client={queryClient}>
@@ -75,6 +77,41 @@ The two generated files come from `pikku.config.json`'s
75
77
  `clientFiles.fetchFile` and `clientFiles.rpcWiringsFile`. Hooks live in
76
78
  the file at `clientFiles.reactQueryFile` (typically `api.gen.ts`).
77
79
 
80
+ `apiUrl()` is the shared server-URL helper — see **pikku-react**. Never
81
+ inline `?? 'http://localhost:3000'`: a deploy that supplies the URL as a
82
+ runtime binding leaves `import.meta.env.VITE_API_URL` undefined in the
83
+ bundle, so the fallback is the branch that actually runs.
84
+
85
+ ## TanStack Start (SSR)
86
+
87
+ Under Start the provider mounts in `routes/__root.tsx` rather than
88
+ `main.tsx`, and the same module is evaluated on the server. Three things
89
+ differ:
90
+
91
+ 1. **`apiUrl()` must have an SSR branch.** `window` is undefined during
92
+ render; return the build-time var or a placeholder (the client hooks
93
+ only fire in the browser).
94
+ 2. **Build auth clients lazily.** Better Auth validates its baseURL with
95
+ `new URL(...)` at construction, so a module-scope `createAuthClient`
96
+ crashes SSR on the placeholder. Memoize it behind a getter:
97
+
98
+ ```ts
99
+ let _authClient: ReturnType<typeof createAuthClient> | undefined
100
+ export const authClient = () =>
101
+ (_authClient ??= createAuthClient({ baseURL: `${apiUrl()}/auth` }))
102
+ ```
103
+
104
+ 3. **The auth baseURL needs the `/auth` suffix.** Better Auth only
105
+ appends its default `/api/auth` when the baseURL carries no path.
106
+ `apiUrl()` already ends in `/api`, so a bare `apiUrl()` leaves the
107
+ client calling `/api/get-session` and 404ing.
108
+
109
+ Server functions that need typed RPC access use the generated shim:
110
+
111
+ ```bash
112
+ pikku tanstack-start # emits the makeApi server-function shim
113
+ ```
114
+
78
115
  ## The hooks
79
116
 
80
117
  All hooks are imported from your generated `api.gen.ts`:
@@ -160,7 +160,7 @@ const pikku = createPikku(
160
160
  PikkuFetch,
161
161
  PikkuRPC,
162
162
  PikkuRealtime, // pass the realtime class as the third arg
163
- { serverUrl: import.meta.env.VITE_API_URL ?? 'http://localhost:3000' }
163
+ { serverUrl: apiUrl() } // shared env helper see pikku-react
164
164
  )
165
165
  // pikku.fetch / pikku.rpc / pikku.realtime — all share the same fetch.
166
166
 
@@ -36,7 +36,7 @@ const login = pikkuFunc({
36
36
  auth: false,
37
37
  func: async ({ jwt, db }, { email, password }, { setSession }) => {
38
38
  const user = await db.verifyCredentials(email, password)
39
- setSession({ userId: user.id, role: user.role })
39
+ setSession({ userId: user.id })
40
40
  return { token: jwt.sign({ userId: user.id }) }
41
41
  },
42
42
  })
@@ -80,7 +80,7 @@ addHTTPMiddleware('*', [authAPIKey({ source: 'all' })])
80
80
  import { pikkuAuth, pikkuPermission } from '#pikku'
81
81
 
82
82
  export const isAuthenticated = pikkuAuth(async (_services, session) => !!session)
83
- export const isAdmin = pikkuAuth(async (_services, session) => session?.role === 'admin')
83
+ export const isVerified = pikkuAuth(async (_services, session) => !!session?.emailVerified)
84
84
 
85
85
  // wirings/auth.wiring.ts
86
86
  import { authCookie } from '@pikku/core/middleware'
@@ -95,7 +95,7 @@ export const login = pikkuFunc({
95
95
  auth: false,
96
96
  func: async ({ jwt, db }, { email, password }, { setSession }) => {
97
97
  const user = await db.verifyCredentials(email, password)
98
- setSession({ userId: user.id, role: user.role })
98
+ setSession({ userId: user.id })
99
99
  return { token: jwt.sign({ userId: user.id }) }
100
100
  },
101
101
  })
@@ -2,6 +2,7 @@
2
2
  name: pikku-template-clone
3
3
  description: 'Standard cleanup to run right after a Pikku template is cloned or scaffolded into a new project. TRIGGER when: a Pikku template was just cloned/scaffolded (via `npm create pikku`, `git clone <template>`, or the user says "I cloned the kanban template / starter / template"), or the working tree still looks like an untouched template (template README, placeholder `@project/*` name in package.json). DO NOT TRIGGER when: working in an established project mid-feature, or editing the template repo itself.'
4
4
  allowed-tools: Bash(git status *), Bash(git add *), Bash(git commit *), Bash(git rm *), Bash(git mv *), Bash(git log *)
5
+ installGroups: [core]
5
6
  ---
6
7
 
7
8
  # Pikku Template Post-Clone Cleanup
package/cli.schema.json DELETED
@@ -1 +0,0 @@
1
- {"$schema":"http://json-schema.org/draft-07/schema#","type":"object","additionalProperties":false,"properties":{"configFile":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"wires":{"type":"array","items":{"type":"string"}},"excludeWires":{"type":"array","items":{"type":"string"}},"userSessionType":{"type":"string"},"singletonServicesFactoryType":{"type":"string"},"wireServicesFactoryType":{"type":"string"},"outDir":{"type":"string"},"schemaDirectory":{"type":"string"},"typesDeclarationFile":{"type":"string"},"functionsFile":{"type":"string"},"functionsMetaFile":{"type":"string"},"functionsMetaJsonFile":{"type":"string"},"functionTypesFile":{"type":"string"},"httpWiringsFile":{"type":"string"},"httpWiringMetaFile":{"type":"string"},"httpWiringMetaJsonFile":{"type":"string"},"httpContractsMetaJsonFile":{"type":"string"},"httpContractsMetaFile":{"type":"string"},"httpMapDeclarationFile":{"type":"string"},"httpTypesFile":{"type":"string"},"gatewaysWiringFile":{"type":"string"},"gatewaysWiringMetaFile":{"type":"string"},"gatewaysWiringMetaJsonFile":{"type":"string"},"channelsWiringFile":{"type":"string"},"channelsWiringMetaFile":{"type":"string"},"channelsWiringMetaJsonFile":{"type":"string"},"channelContractsMetaJsonFile":{"type":"string"},"channelContractsMetaFile":{"type":"string"},"channelsMapDeclarationFile":{"type":"string"},"channelsTypesFile":{"type":"string"},"rpcInternalWiringMetaFile":{"type":"string"},"rpcInternalWiringMetaJsonFile":{"type":"string"},"rpcInternalMapDeclarationFile":{"type":"string"},"rpcMapDeclarationFile":{"type":"string"},"rpcRemoteMapDeclarationFile":{"type":"string"},"remoteRpcWorkersFile":{"type":"string"},"graphWiringsFile":{"type":"string"},"webhookWorkersFile":{"type":"string"},"publicRpcFile":{"type":"string"},"publicAgentFile":{"type":"string"},"consoleFunctionsFile":{"type":"string"},"scenariosFunctionsFile":{"type":"string"},"workflowRoutesFile":{"type":"string"},"eventsChannelFile":{"type":"string"},"triggersTypesFile":{"type":"string"},"triggersWiringFile":{"type":"string"},"triggersWiringMetaFile":{"type":"string"},"triggersWiringMetaJsonFile":{"type":"string"},"triggerSourcesMetaFile":{"type":"string"},"triggerSourcesMetaJsonFile":{"type":"string"},"schedulersWiringFile":{"type":"string"},"schedulersWiringMetaFile":{"type":"string"},"schedulersWiringMetaJsonFile":{"type":"string"},"schedulersTypesFile":{"type":"string"},"queueWorkersWiringFile":{"type":"string"},"queueWorkersWiringMetaFile":{"type":"string"},"queueWorkersWiringMetaJsonFile":{"type":"string"},"queueMapDeclarationFile":{"type":"string"},"queueTypesFile":{"type":"string"},"workflowsWiringFile":{"type":"string"},"workflowsWiringMetaFile":{"type":"string"},"workflowsWorkersFile":{"type":"string"},"workflowMapDeclarationFile":{"type":"string"},"workflowTypesFile":{"type":"string"},"workflowMetaDir":{"type":"string"},"scenarioActorsFile":{"type":"string"},"mcpWiringsFile":{"type":"string"},"mcpWiringsMetaFile":{"type":"string"},"mcpWiringsMetaJsonFile":{"type":"string"},"mcpTypesFile":{"type":"string"},"mcpJsonFile":{"type":"string"},"agentWiringsFile":{"type":"string"},"agentWiringMetaFile":{"type":"string"},"agentWiringMetaJsonFile":{"type":"string"},"agentTypesFile":{"type":"string"},"agentMapDeclarationFile":{"type":"string"},"cliWiringsFile":{"type":"string"},"cliWiringMetaFile":{"type":"string"},"cliWiringMetaJsonFile":{"type":"string"},"cliContractsMetaJsonFile":{"type":"string"},"cliContractsMetaFile":{"type":"string"},"cliBootstrapFile":{"type":"string"},"cliTypesFile":{"type":"string"},"servicesFile":{"type":"string"},"middlewareFile":{"type":"string"},"middlewareGroupsMetaJsonFile":{"type":"string"},"permissionsFile":{"type":"string"},"permissionsGroupsMetaJsonFile":{"type":"string"},"bootstrapFile":{"type":"string"},"packageFile":{"type":"string"},"addonTypesFile":{"type":"string"},"nodeTypesFile":{"type":"string"},"secretTypesFile":{"type":"string"},"secretsFile":{"type":"string"},"secretsMetaJsonFile":{"type":"string"},"credentialsFile":{"type":"string"},"credentialsMetaJsonFile":{"type":"string"},"scopeTypesFile":{"type":"string"},"scopesFile":{"type":"string"},"scopesMetaJsonFile":{"type":"string"},"variableTypesFile":{"type":"string"},"variablesFile":{"type":"string"},"variablesMetaJsonFile":{"type":"string"},"$schema":{"type":"string"},"extends":{"type":"string"},"rootDir":{"type":"string"},"runtimeDir":{"type":"string","description":"Runtime artifacts directory (dev.db, content, tmp). Resolved relative to rootDir. Defaults to <rootDir>/.pikku-runtime."},"srcDirectories":{"type":"array","items":{"type":"string"}},"ignoreFiles":{"type":"array","items":{"type":"string"}},"packageMappings":{"type":"object","additionalProperties":{"type":"string"}},"addon":{"anyOf":[{"type":"boolean"},{"type":"object","properties":{"categories":{"type":"array","items":{"type":"string"}},"icon":{"type":"string"},"displayName":{"type":"string"},"description":{"type":"string"},"serverlessIncompatible":{"type":"array","items":{"type":"string"}},"openapi":{"type":"object","properties":{"version":{"type":"string"},"hash":{"type":"string"}},"required":["version","hash"],"additionalProperties":false}},"additionalProperties":false}]},"addonName":{"type":"string"},"configDir":{"type":"string"},"tsconfig":{"type":"string"},"clientFiles":{"type":"object","properties":{"fetchFile":{"type":"string"},"websocketFile":{"type":"string"},"rpcWiringsFile":{"type":"string"},"reactQueryFile":{"type":"string"},"realtimeFile":{"type":"string"},"realtimeEventHubTopicsImport":{"type":"string","description":"Optional import for the EventHubTopics type so the realtime client is fully typed. Format: `<path>#<TypeName>` resolved relative to `realtimeFile`. Example: `../types/eventhub-topics.js#EventHubTopics`. If unset, the generated client treats topics as `Record<string, unknown>`."},"queueWiringsFile":{"type":"string"},"mcpJsonFile":{"type":"string"},"nextBackendFile":{"type":"string"},"nextHTTPFile":{"type":"string"},"nextBackendTransport":{"type":"string","enum":["local","worker-rpc","http"],"description":"Transport used by the generated nextBackendFile wrapper.\n- `'local'` (default): function code is loaded in-process via bootstrap + createSingletonServices. Required for Node/dev runs.\n- `'worker-rpc'`: SSR dispatches every call through an injected `Fetcher` ({ fetch(req): Promise<Response> }). Function code is NOT bundled into the SSR worker. Pair with `nextBackendFetcherImport` to point at your resolver module.\n- `'http'`: SSR dispatches every call through the generated `PikkuFetch` client. Use this when your Next app should call a separately running local/server API instead of importing function code in-process."},"nextBackendFetcherImport":{"type":"string","description":"Module that exports a `fetcher: Fetcher` (or default export) used by the worker-RPC variant of the next backend wrapper. Resolved relative to `nextBackendFile`. Required when `nextBackendTransport === 'worker-rpc'`."},"startServerFnsFile":{"type":"string","description":"Emit a TanStack Start server-function shim into this file. The shim exports `makeApi(): PikkuRPC` — a typed caller over the generated RPC map for use in Start loaders, actions and components. It reads the API base URL from `import.meta.env.VITE_API_URL` (throws if unset). Requires `rpcWiringsFile` (where the `PikkuRPC` class is generated)."}},"additionalProperties":false},"emailTemplatesDir":{"type":"string","description":"Directory containing email templates, locales, partials, and theme.json."},"authFile":{"type":"string","description":"Path to write the generated Better Auth wiring file (auth.gen.ts). Must be within srcDirectories so wireSecret calls are picked up by the inspector. Example: \"src/auth.gen.ts\""},"authTypesFile":{"type":"string","description":"Path to write the generated typed `pikkuBetterAuth` re-export (auth.types.ts). Defaults to `{outDir}/auth/auth.types.ts`. Re-exported from `#pikku` so user code can `import { pikkuBetterAuth } from '#pikku'` with project-typed services."},"authMetaJsonFile":{"type":"string","description":"Path to write the generated Better Auth metadata (auth-meta.gen.json) — the enabled social providers and plugins the console SSO page reads via getAuthProviders. Defaults to `{outDir}/auth/pikku-auth-meta.gen.json`."},"openAPI":{"type":"object","properties":{"outputFile":{"type":"string"},"additionalInfo":{"$ref":"#/definitions/OpenAPISpecInfo"}},"required":["outputFile","additionalInfo"],"additionalProperties":false},"schema":{"type":"object","properties":{"additionalProperties":{"type":"boolean"},"supportsImportAttributes":{"type":"boolean"}},"additionalProperties":false},"db":{"type":"object","properties":{"engine":{"type":"string","enum":["sqlite","postgres"]},"pgVersion":{"type":"number"}},"additionalProperties":false},"cli":{"type":"object","properties":{"entrypoints":{"type":"object","additionalProperties":{"anyOf":[{"type":"string"},{"type":"object","properties":{"type":{"type":"string","const":"local"},"path":{"type":"string"}},"required":["type","path"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"channel"},"name":{"type":"string"},"route":{"type":"string"},"wirePath":{"type":"string"},"path":{"type":"string"}},"required":["type","wirePath"],"additionalProperties":false},{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"object","properties":{"type":{"type":"string","const":"local"},"path":{"type":"string"}},"required":["type","path"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"channel"},"name":{"type":"string"},"route":{"type":"string"},"wirePath":{"type":"string"},"path":{"type":"string"}},"required":["type","wirePath"],"additionalProperties":false}]}}]}}},"additionalProperties":false},"workflows":{"type":"object","properties":{"orchestratorQueue":{"type":"string"},"workerQueue":{"type":"string"}},"additionalProperties":false},"scenarios":{"type":"object","properties":{"actors":{"type":"object","additionalProperties":{"type":"object","properties":{"email":{"type":"string"},"name":{"type":"string"},"jobTitle":{"type":"string"},"personality":{"type":"string"}},"required":["email"],"additionalProperties":false},"description":"Global scenario actor registry — any scenario can impersonate any actor"},"environments":{"type":"object","additionalProperties":{"type":"object","properties":{"apiUrl":{"type":"string"},"signInPath":{"type":"string","description":"Actor sign-in path under apiUrl. Default: /auth/sign-in/actor"},"rpcPath":{"type":"string","description":"Exposed-RPC prefix under apiUrl. Default: /rpc"}},"required":["apiUrl"],"additionalProperties":false},"description":"Environments `pikku scenario run <environment>` can target; the actor secret comes from SCENARIO_ACTOR_SECRET, never config"}},"additionalProperties":false},"scaffold":{"type":"object","properties":{"addonDir":{"type":"string"},"functionDir":{"type":"string"},"wiringDir":{"type":"string"},"middlewareDir":{"type":"string"},"permissionDir":{"type":"string"},"pikkuDir":{"type":"string"},"graph":{"type":"boolean","description":"Wire the pikku addon-graph package so pikkuWorkflowGraph can reference its native transforms like graph:editFields."},"rpc":{"$ref":"#/definitions/PikkuScaffoldFeature"},"console":{"$ref":"#/definitions/PikkuScaffoldFeature"},"scenarios":{"$ref":"#/definitions/PikkuScaffoldFeature"},"agent":{"$ref":"#/definitions/PikkuScaffoldFeature"},"workflow":{"$ref":"#/definitions/PikkuScaffoldFeature"},"events":{"$ref":"#/definitions/PikkuScaffoldFeature"},"remoteRpc":{"$ref":"#/definitions/PikkuScaffoldFeature"},"webhook":{"type":"boolean","description":"The outgoing webhook delivery worker exposes no endpoint of its own, so it has no auth dimension — it is on or off."}},"additionalProperties":false},"addons":{"type":"object","properties":{"addonDir":{"type":"string"}},"additionalProperties":false,"description":"Community-registry addons installed via `pikku fabric addon add`. The source is copied into the project shadcn-style; each lands in `<addonDir>/<name>/` and the dir is registered as a yarn workspace so `wireAddon({ package })` resolves it by name. `addonDir` defaults to `addons` (top-level, outside the app's TS scan). Install provenance is tracked in pikku-addons.json."},"tests":{"type":"object","properties":{"outputDir":{"type":"string"}},"additionalProperties":false},"forceRequiredServices":{"type":"array","items":{"type":"string"}},"schemasFromTypes":{"type":"array","items":{"type":"string"}},"stateOutput":{"type":"string"},"stateInput":{"type":"string"},"verboseMeta":{"type":"boolean"},"security":{"type":"boolean","description":"Run the data-classification security lint (scans function return types for Private/Pii/Secret leaks). Off by default — it forces expensive return-type inference on every function and is not part of codegen. Enable here to always run it, or per-invocation via `pikku all --security`. Pair with `failOnError` to gate a build/CI on leaks."},"tsc":{"type":"boolean","description":"After codegen, run a real `tsc --noEmit` over the project's tsconfig and fail on type errors. `tsc` prints full diagnostics with code frames; `tscSummary` prints a compact one-line-per-error render (no code frames, capped) that's cheap for AI agents / CI logs. Off by default; enable per invocation via `pikku all --tsc` / `--tsc-summary`."},"tscSummary":{"type":"boolean"},"diff":{"type":"boolean","description":"After a successful codegen run, emit a structural diff of the generated `.pikku` meta (functions/wirings/workflows/emails added/removed/changed vs the state before this run) as a `PIKKU_DIFF <json>` line on stdout. Only emitted on exit 0. Off by default; enable per invocation via `pikku all --diff`. Consumed by the sandbox \"what changed\" build card."},"lint":{"type":"object","properties":{"servicesNotDestructured":{"type":"string","enum":["off","warn","error"]},"wiresNotDestructured":{"type":"string","enum":["off","warn","error"]},"functionDynamicImport":{"type":"string","enum":["off","warn","error"]}},"additionalProperties":false},"addonMetaJsonFile":{"type":"string"},"globalHTTPPrefix":{"type":"string"},"binary":{"type":"object","properties":{"entrypoint":{"type":"string"},"output":{"type":"string"},"targets":{"type":"array","items":{"type":"string"}}},"required":["entrypoint","output"],"additionalProperties":false},"deploy":{"type":"object","properties":{"providers":{"type":"object","additionalProperties":{"type":"string"}},"defaultProvider":{"type":"string"},"serverlessIncompatible":{"type":"array","items":{"type":"string"}},"defaultTarget":{"type":"string","enum":["serverless","server"],"description":"Default deploy target for functions that don't declare an explicit `deploy` flag and don't use a serverless-incompatible service. Defaults to 'serverless'."}},"required":["providers"],"additionalProperties":false},"namedFilters":{"type":"object","additionalProperties":{"$ref":"#/definitions/InspectorFilters"},"description":"Named filter presets keyed by name, used via CLI --filter <name>."},"filters":{"$ref":"#/definitions/InspectorFilters"}},"required":["addonTypesFile","agentMapDeclarationFile","agentTypesFile","agentWiringMetaFile","agentWiringMetaJsonFile","agentWiringsFile","bootstrapFile","channelContractsMetaFile","channelContractsMetaJsonFile","channelsMapDeclarationFile","channelsTypesFile","channelsWiringFile","channelsWiringMetaFile","channelsWiringMetaJsonFile","cliBootstrapFile","cliContractsMetaFile","cliContractsMetaJsonFile","cliTypesFile","cliWiringMetaFile","cliWiringMetaJsonFile","cliWiringsFile","configDir","consoleFunctionsFile","credentialsFile","credentialsMetaJsonFile","eventsChannelFile","filters","functionTypesFile","functionsFile","functionsMetaFile","functionsMetaJsonFile","gatewaysWiringFile","gatewaysWiringMetaFile","gatewaysWiringMetaJsonFile","httpContractsMetaFile","httpContractsMetaJsonFile","httpMapDeclarationFile","httpTypesFile","httpWiringMetaFile","httpWiringMetaJsonFile","httpWiringsFile","mcpJsonFile","mcpTypesFile","mcpWiringsFile","mcpWiringsMetaFile","mcpWiringsMetaJsonFile","middlewareFile","middlewareGroupsMetaJsonFile","nodeTypesFile","outDir","packageFile","packageMappings","permissionsFile","permissionsGroupsMetaJsonFile","publicAgentFile","publicRpcFile","queueMapDeclarationFile","queueTypesFile","queueWorkersWiringFile","queueWorkersWiringMetaFile","queueWorkersWiringMetaJsonFile","rootDir","rpcInternalMapDeclarationFile","rpcInternalWiringMetaFile","rpcInternalWiringMetaJsonFile","rpcMapDeclarationFile","rpcRemoteMapDeclarationFile","scenarioActorsFile","scenariosFunctionsFile","schedulersTypesFile","schedulersWiringFile","schedulersWiringMetaFile","schedulersWiringMetaJsonFile","schemaDirectory","scopeTypesFile","scopesFile","scopesMetaJsonFile","secretTypesFile","secretsFile","secretsMetaJsonFile","servicesFile","srcDirectories","triggerSourcesMetaFile","triggerSourcesMetaJsonFile","triggersTypesFile","triggersWiringFile","triggersWiringMetaFile","triggersWiringMetaJsonFile","tsconfig","typesDeclarationFile","variableTypesFile","variablesFile","variablesMetaJsonFile","workflowMapDeclarationFile","workflowMetaDir","workflowRoutesFile","workflowTypesFile","workflowsWiringFile","workflowsWiringMetaFile","workflowsWorkersFile"],"definitions":{"OpenAPISpecInfo":{"type":"object","properties":{"info":{"type":"object","properties":{"title":{"type":"string"},"version":{"type":"string"},"description":{"type":"string"},"termsOfService":{"type":"string"},"contact":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"},"email":{"type":"string"}},"additionalProperties":false},"license":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"}},"required":["name"],"additionalProperties":false}},"required":["title","version","description"],"additionalProperties":false},"servers":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"description":{"type":"string"}},"required":["url"],"additionalProperties":false}},"tags":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}},"required":["name"],"additionalProperties":false}},"externalDocs":{"type":"object","properties":{"description":{"type":"string"},"url":{"type":"string"}},"required":["url"],"additionalProperties":false},"securitySchemes":{"type":"object"},"security":{"type":"array","items":{"type":"object","additionalProperties":{"type":"array","items":{}}}}},"required":["info","servers"],"additionalProperties":false},"PikkuScaffoldFeature":{"type":["string","boolean"],"enum":["auth","no-auth",false]},"InspectorFilters":{"type":"object","properties":{"names":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"}},"wires":{"type":"array","items":{"type":"string"}},"directories":{"type":"array","items":{"type":"string"}},"httpRoutes":{"type":"array","items":{"type":"string"}},"httpMethods":{"type":"array","items":{"type":"string"}},"excludeNames":{"type":"array","items":{"type":"string"}},"excludeTags":{"type":"array","items":{"type":"string"}},"excludeWires":{"type":"array","items":{"type":"string"}},"excludeDirectories":{"type":"array","items":{"type":"string"}},"excludeHttpRoutes":{"type":"array","items":{"type":"string"}},"excludeHttpMethods":{"type":"array","items":{"type":"string"}},"target":{"type":"array","items":{"type":"string","enum":["serverless","server"]}},"excludeTarget":{"type":"array","items":{"type":"string","enum":["serverless","server"]}},"serverlessIncompatible":{"type":"array","items":{"type":"string"}},"defaultTarget":{"type":"string","enum":["serverless","server"]}},"additionalProperties":false}}}