@pikku/skills 0.12.61 → 0.12.62
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/package.json
CHANGED
|
@@ -47,10 +47,12 @@ wireAddon({
|
|
|
47
47
|
name: string, // Namespace for addon functions (e.g. 'todos')
|
|
48
48
|
package: string, // NPM package name (e.g. '@pikku/addon-todos')
|
|
49
49
|
rpcEndpoint?: string, // Optional remote RPC endpoint for distributed execution
|
|
50
|
+
uses?: Record<string, string>, // alias the addon declared → name this app wired it under (only declared aliases; PKU355)
|
|
50
51
|
auth?: boolean, // Require a session for every function in the addon
|
|
51
52
|
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
53
|
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
54
|
tags?: string[], // Tags applied to all addon functions
|
|
55
|
+
ui?: boolean, // Mount the screens the addon declares with pikkuStudio
|
|
54
56
|
scopes?: string[], // Required of every function, on top of its own
|
|
55
57
|
secretOverrides?: Record<string, string>, // Remap secret names (and grant them)
|
|
56
58
|
variableOverrides?: Record<string, string>, // Remap variable names
|
|
@@ -159,6 +161,12 @@ export const createSingletonServices = pikkuAddonServices(
|
|
|
159
161
|
)
|
|
160
162
|
```
|
|
161
163
|
|
|
164
|
+
Export the call as it is. A cast around it (`pikkuAddonServices(...) as unknown
|
|
165
|
+
as CreateSingletonServices<...>`, `<T>`, `!`) hides the factory
|
|
166
|
+
from the inspector, which would leave it unregistered and the addon's workflows
|
|
167
|
+
failing at runtime; the build fails with `PKU564` instead. Fix the type the cast
|
|
168
|
+
was hiding. `satisfies` and parentheses are fine: the inspector reads through them.
|
|
169
|
+
|
|
162
170
|
`secrets` and `variables` arrive **typed against the addon's own declarations**,
|
|
163
171
|
and a secret is a `SecretValue` — `.reveal()` is the only way to the plaintext
|
|
164
172
|
(see `pikku-services`). `pikkuAddonConfig` is the matching factory for the addon's
|
|
@@ -318,6 +326,116 @@ version. The SDK carries only the remote RPCs, the `expose: true` workflows and
|
|
|
318
326
|
the types they reach. Changing a published `remote: true` contract needs a new
|
|
319
327
|
`version` on the function, which `pikku versions check` enforces.
|
|
320
328
|
|
|
329
|
+
### Calling another addon
|
|
330
|
+
|
|
331
|
+
Declare it in the addon's **package.json** (not pikku.config.json), by the
|
|
332
|
+
alias your code calls it by and a range:
|
|
333
|
+
|
|
334
|
+
```json
|
|
335
|
+
"pikku": { "uses": { "stripe": "@pikkustudio/addon-stripe@^0.12.0" } }
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
Then call it typed, with no cast and no hand-written wrapper:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
const charge = await rpc.invoke('stripe:charge', { amountCents })
|
|
342
|
+
await rpc.startWorkflow('stripe:refund', { chargeId: charge.chargeId })
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
`pikku all` in the addon types `stripe:*` from the dependency's SDK (its remote
|
|
346
|
+
surface). A package in the same workspace is read from its own
|
|
347
|
+
`<outDir>/sdk`, so run `pikku all` there first (PKU338 if missing, PKU354 if
|
|
348
|
+
its version is outside the range); anything else is fetched from the registry
|
|
349
|
+
and pinned in `pikku-addons.lock`. Never cast `rpc` or call by the package
|
|
350
|
+
name; the alias is the only name that works.
|
|
351
|
+
|
|
352
|
+
The app wiring your addon maps the alias with `uses`, local or remote alike:
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
wireRemoteAddon({
|
|
356
|
+
name: 'payments',
|
|
357
|
+
package: '@pikkustudio/addon-stripe@^0.12.0',
|
|
358
|
+
serverUrl,
|
|
359
|
+
})
|
|
360
|
+
wireAddon({
|
|
361
|
+
name: 'billing',
|
|
362
|
+
package: '@acme/addon-billing',
|
|
363
|
+
uses: { stripe: 'payments' },
|
|
364
|
+
})
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
| Code | The app's `pikku all` fails when |
|
|
368
|
+
| ------ | ------------------------------------------------------------------- |
|
|
369
|
+
| PKU348 | an alias the addon declared is not mapped |
|
|
370
|
+
| PKU347 | it is mapped to a name not wired, or wired from another package |
|
|
371
|
+
| PKU355 | `uses` maps a name the addon did not declare |
|
|
372
|
+
| PKU354 | the mapped instance's version is outside the range |
|
|
373
|
+
| PKU356 | the addon's `pikku.uses` entry is malformed (alias, type, no range) |
|
|
374
|
+
|
|
375
|
+
At runtime an unmapped alias, or the wired name called directly, throws
|
|
376
|
+
`AddonNotUsedError` (403) for invoke, workflows and agents.
|
|
377
|
+
|
|
378
|
+
### Studio screens
|
|
379
|
+
|
|
380
|
+
An addon declares the screens it adds to Pikku Studio with `pikkuStudio`, in
|
|
381
|
+
`src/screens.ts` (`pikku new addon-ui <name>` scaffolds one). It replaces
|
|
382
|
+
`defineScreens`, which no longer exists.
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
import { pikkuStudio } from '@pikku/core/addon'
|
|
386
|
+
|
|
387
|
+
export default pikkuStudio({
|
|
388
|
+
title: 'invoices__title', // message key: the addon's name in Studio
|
|
389
|
+
screens: [
|
|
390
|
+
{
|
|
391
|
+
path: '/invoices',
|
|
392
|
+
title: 'invoices_list__title', // message key, also the nav label
|
|
393
|
+
scope: 'project', // 'studio' → /<path>, 'team' → /<team>/<path>,
|
|
394
|
+
// 'project' → /<team>/<project>[/<target>]/<path>
|
|
395
|
+
nav: {
|
|
396
|
+
area: 'project',
|
|
397
|
+
icon: 'receipt',
|
|
398
|
+
order: 30,
|
|
399
|
+
badge: 'unpaidCount',
|
|
400
|
+
},
|
|
401
|
+
needsApp: true, // project screens only; default false
|
|
402
|
+
scopes: ['invoices:read'], // hides the screen; NOT access control
|
|
403
|
+
component: () => import('./screens/Invoices.js'),
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
path: '/invoices/print',
|
|
407
|
+
title: 'invoices_print__title',
|
|
408
|
+
scope: 'project',
|
|
409
|
+
layout: 'bare',
|
|
410
|
+
app: 'print',
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
})
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
- A screen is a lazy `component` or an `app`, never both. `nav` is optional:
|
|
417
|
+
without it the screen is routed but not listed. `nav.area` is `'rail'`,
|
|
418
|
+
`'team'`, `'project'` or `'account'`; `badge` names one of the addon's
|
|
419
|
+
functions that returns a count.
|
|
420
|
+
- `replaces: true` takes over the core screen at the same path (first-party
|
|
421
|
+
addons only; Studio enforces that). `layout: 'bare'` renders without the shell.
|
|
422
|
+
- Titles are message keys in the addon's `messages/{locale}.json`, found by
|
|
423
|
+
convention next to its `package.json`; there is no field for them.
|
|
424
|
+
- `scopes` only hide the screen. The addon's functions must enforce the same
|
|
425
|
+
scopes.
|
|
426
|
+
|
|
427
|
+
The inspector reads every field statically, so each must be a literal.
|
|
428
|
+
`pikku all` fails on:
|
|
429
|
+
|
|
430
|
+
| Code | When |
|
|
431
|
+
| ------ | ------------------------------------------------------------ |
|
|
432
|
+
| PKU345 | a field is not a literal (variable, call, spread, bad value) |
|
|
433
|
+
| PKU357 | two screens of the addon use the same path |
|
|
434
|
+
| PKU358 | a title key is missing from `messages/en.json` |
|
|
435
|
+
| PKU359 | `nav.badge` names no function of the addon |
|
|
436
|
+
|
|
437
|
+
A key missing from another locale's catalog is a warning.
|
|
438
|
+
|
|
321
439
|
### Database tables
|
|
322
440
|
|
|
323
441
|
An addon may **ship** tables. It must never **create** them: it runs inside the
|
|
@@ -81,6 +81,12 @@ package that declared them, and the extra segment is what stops a linked addon's
|
|
|
81
81
|
}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
**`pikku.uses` names the addons this addon calls**, as
|
|
85
|
+
`{ "<alias>": "<package>@<range>" }`. The addon calls `<alias>:<fn>`, typed from
|
|
86
|
+
that package's SDK, and the app wiring it maps each alias with `wireAddon`
|
|
87
|
+
`uses` (see "Calling another addon" in the skill). The older list form,
|
|
88
|
+
`["kysely"]`, names keys the app must map, with no SDK or version check.
|
|
89
|
+
|
|
84
90
|
**`imports` names `dist`, never the source tree.** `files: ["dist"]` is the whole
|
|
85
91
|
published package, and `pikku dist` copies `.pikku` and `types` into it — so a
|
|
86
92
|
`#pikku/*` target under `./.pikku/` resolves for the author and for nobody else.
|
|
@@ -74,6 +74,12 @@ service's paths and size limit; omitting it does not disable it.
|
|
|
74
74
|
- **Do not take `services` as a named parameter and cast in the body.** The
|
|
75
75
|
inspector reads the destructuring pattern to build the manifest; a cast makes
|
|
76
76
|
the service invisible to it, so tree-shaking drops what the function needs.
|
|
77
|
+
- **Do not cast a service factory.** `pikkuServices(...) as X` (or `as unknown
|
|
78
|
+
as`, `!`, `<X>`) hides the call from the inspector,
|
|
79
|
+
so the factory would not be registered; the build fails with `PKU564`
|
|
80
|
+
instead. Export the direct `pikkuServices(...)` / `pikkuWireServices(...)` /
|
|
81
|
+
`pikkuConfig(...)` call and fix the type mismatch the cast was hiding.
|
|
82
|
+
`satisfies` and parentheses are fine: the inspector reads through them.
|
|
77
83
|
- **Do not hand-roll an audit table.** `auditLog.write()` on an `audit: true`
|
|
78
84
|
function enriches the event with the function id, wire type, trace id and user
|
|
79
85
|
identity; an `insertInto('audit_log')` of your own gets none of that, and a
|
|
@@ -167,6 +167,36 @@ cron expression can decide which tasks fire. `pikku-deploy` has the comparison.
|
|
|
167
167
|
Enabling the RPC endpoint says the endpoint exists, not who may call it — each
|
|
168
168
|
`expose: true` function is still gated by its own `auth`, permissions and scopes.
|
|
169
169
|
|
|
170
|
+
## A function nothing uses: wire it or disable it
|
|
171
|
+
|
|
172
|
+
`pikku verify` fails on an app function that is not wired, exposed, or called
|
|
173
|
+
by name (`rpc.invoke`, a workflow step, an agent tool) — `[PKU563]`, naming the
|
|
174
|
+
function and its file. `pikku all` still builds; only verify fails. Addon
|
|
175
|
+
functions are never flagged, in the addon or in an app that uses it.
|
|
176
|
+
|
|
177
|
+
Workflows are checked too. A workflow is used when it is wired, exposed, or
|
|
178
|
+
started by a literal name: `rpc.startWorkflow('name')`,
|
|
179
|
+
`workflowService.startWorkflow` / `runToCompletion('name')`,
|
|
180
|
+
`rpc.invoke('name')`, another workflow's step, or a client helper. A workflow
|
|
181
|
+
started only through a computed name is flagged.
|
|
182
|
+
|
|
183
|
+
To keep a function in the source but out of the build, mark it `disabled`:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
export const oldReport = pikkuSessionlessFunc({
|
|
187
|
+
disabled: 'replaced by liveReport', // or `true`
|
|
188
|
+
func: async () => 'old',
|
|
189
|
+
})
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
A disabled function is left out of everything: meta, RPC maps, clients, MCP,
|
|
193
|
+
CLI, HTTP, addon SDKs. It cannot be registered at runtime either. Anything
|
|
194
|
+
that still references it — a wiring, an `rpc.invoke('oldReport')`, a workflow
|
|
195
|
+
step, an agent tool — fails the build with `[PKU562]`, naming both sides.
|
|
196
|
+
`pikku all` lists the disabled functions once, with file and reason.
|
|
197
|
+
|
|
198
|
+
The value must be `true` or a string literal; a computed value is refused.
|
|
199
|
+
|
|
170
200
|
## What NOT to do
|
|
171
201
|
|
|
172
202
|
- Do not import a wiring factory from `@pikku/core/*`. It compiles and silently
|