@pikku/skills 0.12.59 → 0.12.61
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 +1 -1
- package/dist/skills.gen.js +1 -1
- package/package.json +1 -1
- package/skills/pikku-addon/SKILL.md +51 -4
- package/skills/pikku-addon/references/addon-package-manifest.md +53 -0
- package/skills/pikku-build/references/phase-4-build.md +6 -2
- package/skills/pikku-build/references/platform.md +1 -1
- package/skills/pikku-build/references/ship.md +8 -1
- package/skills/pikku-concepts/SKILL.md +7 -0
- package/skills/pikku-wiring/references/rpc.md +2 -2
package/package.json
CHANGED
|
@@ -49,7 +49,7 @@ wireAddon({
|
|
|
49
49
|
rpcEndpoint?: string, // Optional remote RPC endpoint for distributed execution
|
|
50
50
|
auth?: boolean, // Require a session for every function in the addon
|
|
51
51
|
mcp?: boolean | string[], // true: every function the addon declared mcp: true; a list: the tools this app offers, typed against the addon's function names
|
|
52
|
-
expose?: boolean | string[], // what rpc.exposed / POST /rpc may reach
|
|
52
|
+
expose?: boolean | string[], // opt-in: what rpc.exposed / POST /rpc may reach. true = every function the addon declared expose: true, a list = those of them named (typed; PKU343 on unknown or undeclared names), unset/false = none
|
|
53
53
|
tags?: string[], // Tags applied to all addon functions
|
|
54
54
|
scopes?: string[], // Required of every function, on top of its own
|
|
55
55
|
secretOverrides?: Record<string, string>, // Remap secret names (and grant them)
|
|
@@ -279,6 +279,45 @@ everything else. Leave it out and the addon installs fine and fails to typecheck
|
|
|
279
279
|
in every app that depends on it, which is what `pikku validate` is there to
|
|
280
280
|
catch before you publish.
|
|
281
281
|
|
|
282
|
+
### Remote functions and the SDK
|
|
283
|
+
|
|
284
|
+
A `remote: true` function is called over the wire by apps that use
|
|
285
|
+
`wireRemoteAddon`. Those apps build against the addon's SDK, not its source, so
|
|
286
|
+
**a remote function's input and output must be exported zod schemas**. If it
|
|
287
|
+
uses generics (`pikkuSessionlessFunc<In, Out>`) or returns a type declared in
|
|
288
|
+
`src/`, codegen fails with PKU922. Functions that are not remote can keep their
|
|
289
|
+
generics.
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// src/functions/listNotes.schemas.ts
|
|
293
|
+
import { z } from 'zod'
|
|
294
|
+
|
|
295
|
+
export const NoteRows = z.array(z.object({ id: z.string(), body: z.string() }))
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
// src/functions/listNotes.function.ts
|
|
300
|
+
import { pikkuSessionlessFunc } from '#pikku/addon/function'
|
|
301
|
+
import { NoteRows } from './listNotes.schemas.js'
|
|
302
|
+
|
|
303
|
+
export const listNotes = pikkuSessionlessFunc({
|
|
304
|
+
output: NoteRows,
|
|
305
|
+
func: async ({ kysely }) =>
|
|
306
|
+
kysely.selectFrom('notes').select(['id', 'body']).execute(),
|
|
307
|
+
remote: true,
|
|
308
|
+
})
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
yarn pikku all # also writes .pikku/sdk/ + <scope>-<name>-<version>.sdk.tgz
|
|
313
|
+
yarn pikku versions check # fails on a remote contract changed without a new version
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
In an addon, `pikku all` packs the SDK on every run, at the package.json
|
|
317
|
+
version. The SDK carries only the remote RPCs, the `expose: true` workflows and
|
|
318
|
+
the types they reach. Changing a published `remote: true` contract needs a new
|
|
319
|
+
`version` on the function, which `pikku versions check` enforces.
|
|
320
|
+
|
|
282
321
|
### Database tables
|
|
283
322
|
|
|
284
323
|
An addon may **ship** tables. It must never **create** them: it runs inside the
|
|
@@ -361,6 +400,13 @@ covered, and writes only the delta after an addon upgrade. An addon wired with
|
|
|
361
400
|
`wireRemoteAddon` contributes no schema at all: its tables belong to the
|
|
362
401
|
deployment that runs its functions.
|
|
363
402
|
|
|
403
|
+
A `wireRemoteAddon` whose `package` carries a range (`'@scope/name@^1.4.0'`)
|
|
404
|
+
is not installed at all: `pikku all` downloads its SDK into
|
|
405
|
+
`.pikku/remote` and pins it in `pikku-addons.lock` (commit the lock). If
|
|
406
|
+
codegen reports PKU338 for one, run `pikku all`, not an install; to move
|
|
407
|
+
to a newer SDK, edit the range by hand (`pikku update` prints the file:line
|
|
408
|
+
and the new literal; it never rewrites source), then run `pikku all`.
|
|
409
|
+
|
|
364
410
|
### Call via RPC
|
|
365
411
|
|
|
366
412
|
```typescript
|
|
@@ -381,9 +427,10 @@ and the frontend calls `rpc.invoke('todos:listTodos', …)` (over HTTP,
|
|
|
381
427
|
Use `ref('todos:listTodos')` on an HTTP or MCP wiring only when the addon needs
|
|
382
428
|
a route of its own. Don't write an app function that calls `rpc.invoke` and
|
|
383
429
|
returns the result unchanged: it's a second name and a second schema for the
|
|
384
|
-
same thing, and it drifts.
|
|
385
|
-
|
|
386
|
-
|
|
430
|
+
same thing, and it drifts. Nothing in an addon is reachable over `/rpc` until
|
|
431
|
+
the wiring opts in, and it can only open what the addon itself declared
|
|
432
|
+
`expose: true` — an OpenAPI-generated addon declares every operation, so list
|
|
433
|
+
the ones the frontend needs.
|
|
387
434
|
|
|
388
435
|
Write your own function when the app needs the data narrowed, typed, or
|
|
389
436
|
combined (a flag the upstream sends as `"0"`, a total summed from several
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
- Package Structure
|
|
6
6
|
- pikku.config.json
|
|
7
7
|
- package.json (key fields)
|
|
8
|
+
- Platform service contributors (`pikku.contributors`)
|
|
8
9
|
- tsconfig.json (key fields)
|
|
9
10
|
|
|
10
11
|
`npx pikku new addon` scaffolds these files. You rarely hand-edit them — consult this when wiring exports or config by hand.
|
|
@@ -98,6 +99,58 @@ live in a file that never imports `#pikku` — see "Functions" in the skill.
|
|
|
98
99
|
writes `@my-org/addon-todos/.pikku/rpc/...`, exactly as it would in an
|
|
99
100
|
application, and the leaf stays the package's own business.
|
|
100
101
|
|
|
102
|
+
## Platform service contributors (`pikku.contributors`)
|
|
103
|
+
|
|
104
|
+
An addon can ship code that runs in the **host's** generated
|
|
105
|
+
`createPlatformServices` — a log source over the host's database, a client
|
|
106
|
+
built from a host secret. `wireAddon` has no field for it; the package declares
|
|
107
|
+
it in its own `package.json`, the same shape as the host's `dev.contributors`:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"exports": {
|
|
112
|
+
"./contributors": {
|
|
113
|
+
"types": "./dist/src/contributors.d.ts",
|
|
114
|
+
"import": "./dist/src/contributors.js"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"pikku": {
|
|
118
|
+
"contributors": [
|
|
119
|
+
{ "module": "@my-org/addon-logs/contributors", "export": "logSources" }
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`module` resolves from the addon's package directory. Every app that wires the
|
|
126
|
+
addon (not `wireRemoteAddon`) picks the contributors up: `pikku all` emits them
|
|
127
|
+
into `.pikku/pikku-dev-platform-services.gen.ts` for `pikku dev`, and
|
|
128
|
+
`pikku deploy plan` / `apply` hands them to the provider adapter, after the
|
|
129
|
+
app's own `deploy.contributors`.
|
|
130
|
+
|
|
131
|
+
Declare what a contributor reads and sets, rather than relying on list order:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
export const logSources: PlatformServiceContributor = {
|
|
135
|
+
name: 'log-sources',
|
|
136
|
+
needs: ['kysely'], // runtime.kysely, or a contributor's services.kysely
|
|
137
|
+
provides: ['logSources', 'logSources.workspace'],
|
|
138
|
+
imports: [`import { kyselyLogSource } from '@my-org/addon-logs/log-source'`],
|
|
139
|
+
emit: () => [
|
|
140
|
+
` services.logSources = { workspace: kyselyLogSource(runtime.kysely) }`,
|
|
141
|
+
],
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Contributors are sorted so each runs after every contributor providing one of
|
|
146
|
+
its `needs`; a dotted need (`logSources.stage`) is met by a provider of the key
|
|
147
|
+
or of its prefix. Emitted lines read services the entry built before them as
|
|
148
|
+
`runtime.<name>` — `pikku dev` has logger, variables, secrets, schema and (with
|
|
149
|
+
`db`) kysely; the standalone entry has logger, kysely (with a database) and its
|
|
150
|
+
scheduler/queue/workflow/trigger services and event hub. Codegen **fails** on a
|
|
151
|
+
need nothing meets, on two contributors providing the same key, and on a cycle,
|
|
152
|
+
naming the contributors involved.
|
|
153
|
+
|
|
101
154
|
## tsconfig.json (key fields)
|
|
102
155
|
|
|
103
156
|
```json
|
|
@@ -179,8 +179,12 @@ in the repo, green or red on every future run.
|
|
|
179
179
|
}
|
|
180
180
|
```
|
|
181
181
|
|
|
182
|
-
The module exports a `PlatformServiceContributor` (`{ name, imports?, emit }`, the
|
|
183
|
-
type `@pikku/deploy` takes).
|
|
182
|
+
The module exports a `PlatformServiceContributor` (`{ name, needs?, provides?, imports?, emit }`, the
|
|
183
|
+
type `@pikku/deploy` takes). Addons the project wires add their own, declared in their
|
|
184
|
+
package.json as `pikku.contributors` (see the pikku-addon skill's package manifest
|
|
185
|
+
reference). Contributors run in the order their `needs`/`provides` require, and the
|
|
186
|
+
generated `createPlatformServices(env, runtime)` gets the local services as `runtime`;
|
|
187
|
+
an unmet need, a key provided twice or a cycle fails `pikku all`. `pikku all` writes `.pikku/pikku-dev-platform-services.gen.ts`
|
|
184
188
|
from them, and `pikku dev` merges what it returns over the local services before
|
|
185
189
|
`createSingletonServices` runs, so a contributed `emailService` beats
|
|
186
190
|
`LocalEmailService` (and the test-mode spy). Nothing is written without
|
|
@@ -60,7 +60,7 @@ Most surfaces are switched on by the CLI rather than hand-wired:
|
|
|
60
60
|
bunx --bun pikku enable workflow # workflow workers
|
|
61
61
|
bunx --bun pikku enable agent # public agent endpoints
|
|
62
62
|
bunx --bun pikku enable events # realtime events channel + SSE stream
|
|
63
|
-
bunx --bun pikku enable
|
|
63
|
+
bunx --bun pikku enable mesh-rpc # mesh RPC queue worker + HTTP endpoint
|
|
64
64
|
bunx --bun pikku enable webhook # outgoing webhook delivery queue worker
|
|
65
65
|
bunx --bun pikku enable scenarios # scenario instrumentation
|
|
66
66
|
bunx --bun pikku enable rpc # public RPC endpoint
|
|
@@ -25,6 +25,13 @@ with Node, or a self-contained executable compiled with `bun build --compile`.
|
|
|
25
25
|
factories the deploy codegen expects, and hand-rolling an `ExportedHandler` is
|
|
26
26
|
how a worker deploy fails at runtime instead of at build.
|
|
27
27
|
|
|
28
|
+
Platform services a host injects into the bundle come from contributors:
|
|
29
|
+
`deploy.contributors` in `pikku.config.json` (`[{ "module", "export"? }]`, the
|
|
30
|
+
same shape as `dev.contributors`) plus the `pikku.contributors` every wired addon
|
|
31
|
+
declares in its package.json. `plan` and `apply` pass them all to the adapter,
|
|
32
|
+
which orders them by `needs`/`provides` and, on standalone, runs them after the
|
|
33
|
+
database is open, with `runtime.logger` and `runtime.kysely` in reach.
|
|
34
|
+
|
|
28
35
|
Always run `plan` before `apply`, and read it. It names what will be created,
|
|
29
36
|
updated and deleted — the deletions are the reason to look.
|
|
30
37
|
|
|
@@ -67,7 +74,7 @@ builds anyway (`config`, `logger`, `variables`, `schema`, `secrets`, and the
|
|
|
67
74
|
per-request `rpc`/`mcp`/`channel`/`userSession`). Units are named for that set —
|
|
68
75
|
`svc-todo-store`, `svc-event-hub-todo-store`, `svc-base` for functions needing
|
|
69
76
|
nothing else — and each records it as `servicesKey` in the manifest. On the
|
|
70
|
-
`
|
|
77
|
+
`recipes/functions` app it turns 44 units into 10.
|
|
71
78
|
|
|
72
79
|
The deploy target is part of the key, so a `server` unit is named `-server` and
|
|
73
80
|
can never merge with its serverless twin. That is what keeps `services` from
|
|
@@ -124,6 +124,13 @@ Config lives in `pikku.config.json`:
|
|
|
124
124
|
}
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
A config can `extends` another (`"extends": "./pikku.config.json"`) and state only
|
|
128
|
+
what differs. The child merges into the parent: objects merge key by key,
|
|
129
|
+
`srcDirectories` is the parent's list plus the child's new entries, every other
|
|
130
|
+
array and value the child sets replaces the parent's, and `null` removes a
|
|
131
|
+
parent value. Each file's paths are relative to that file, and `rootDir` is
|
|
132
|
+
inherited unless the child sets its own.
|
|
133
|
+
|
|
127
134
|
## Project Structure Convention
|
|
128
135
|
|
|
129
136
|
```text
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
| Method | Purpose |
|
|
14
14
|
| -------------------------------- | ----------------------------------------- |
|
|
15
15
|
| `rpc.invoke(name, data)` | Internal call to any wired function |
|
|
16
|
-
| `rpc.
|
|
16
|
+
| `rpc.mesh(name, data)` | Mesh call via DeploymentService |
|
|
17
17
|
| `rpc.exposed(name, data)` | Call functions marked with `expose: true` |
|
|
18
18
|
| `rpc.startWorkflow(name, input)` | Start a workflow (see `pikku-workflow`) |
|
|
19
19
|
| `rpc.agent.run/stream(...)` | Run an AI agent (see `pikku-agent`) |
|
|
20
20
|
| `rpc.agent.resume/approve(...)` | Answer a tool-approval interrupt |
|
|
21
21
|
| `rpc.agent.interrupt(runId)` | Stop an in-flight run |
|
|
22
22
|
|
|
23
|
-
`rpc.invoke`, `rpc.
|
|
23
|
+
`rpc.invoke`, `rpc.mesh` and `rpc.startWorkflow` are typed off the generated
|
|
24
24
|
RPC map, so the name and the payload are checked. `rpc.exposed` is deliberately
|
|
25
25
|
`(name: string, data: any) => Promise<any>` — it exists to dispatch a name that
|
|
26
26
|
arrived from outside, which by definition cannot be checked at compile time.
|