@pikku/cli 0.12.91 → 0.12.93

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 (241) hide show
  1. package/LICENSE +106 -0
  2. package/console-app/assets/{index-D0HG8q0B.css → index-BYj7ZbUU.css} +1 -1
  3. package/console-app/assets/{index-CpkvmAGb.js → index-CmAplHjZ.js} +151 -149
  4. package/console-app/index.html +2 -2
  5. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  6. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  7. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  8. package/dist/.pikku/cli/pikku-cli-channel.js +16 -1
  9. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  10. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  11. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  12. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  13. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +15 -0
  14. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  15. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  16. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  17. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +48 -0
  18. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  19. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  20. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  21. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  22. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  23. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  24. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  25. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  26. package/dist/.pikku/function/pikku-functions-meta.gen.json +73 -40
  27. package/dist/.pikku/function/pikku-functions.gen.js +1 -3
  28. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  29. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  30. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  31. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  32. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +2 -1
  33. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +2 -1
  34. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  35. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  36. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  37. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  38. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  39. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  40. package/dist/.pikku/pikku-types.gen.js +1 -1
  41. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  42. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  43. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  44. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  45. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  46. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  47. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +3 -1
  48. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  49. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  50. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  51. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  52. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +4 -0
  53. package/dist/.pikku/scenarios/schemas/register.gen.js +4 -0
  54. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  55. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  56. package/dist/.pikku/schemas/register.gen.js +13 -3
  57. package/dist/.pikku/schemas/schemas/FabricSecretsListOutput.schema.json +1 -1
  58. package/dist/.pikku/schemas/schemas/FabricSecretsRotateInput.schema.json +1 -0
  59. package/dist/.pikku/schemas/schemas/FabricSecretsRotateOutput.schema.json +1 -0
  60. package/dist/.pikku/schemas/schemas/FabricSecretsSetOutput.schema.json +1 -1
  61. package/dist/.pikku/schemas/schemas/KnowledgeIndexInput.schema.json +1 -0
  62. package/dist/.pikku/schemas/schemas/KnowledgeIndexOutput.schema.json +1 -0
  63. package/dist/.pikku/schemas/schemas/KnowledgeValidateInput.schema.json +1 -0
  64. package/dist/.pikku/schemas/schemas/KnowledgeValidateOutput.schema.json +1 -0
  65. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  66. package/dist/.pikku/schemas/schemas/ScenarioRunInput.schema.json +1 -1
  67. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  68. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  69. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  70. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  71. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  72. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  73. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  74. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  75. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  76. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  77. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  78. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  79. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  80. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +2 -8
  81. package/dist/.pikku/workflow/pikku-scenario-actors.gen.d.ts +1 -1
  82. package/dist/.pikku/workflow/pikku-scenario-actors.gen.js +1 -1
  83. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  84. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  85. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  86. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  87. package/dist/bin/pikku-bin.mjs +2 -2
  88. package/dist/src/cli.wiring.js +29 -0
  89. package/dist/src/deploy/analyzer/analyzer.js +22 -5
  90. package/dist/src/deploy/build-pipeline.js +5 -1
  91. package/dist/src/fabric/fabric-commands.d.ts +37 -9
  92. package/dist/src/fabric/fabric-commands.js +12 -0
  93. package/dist/src/fabric/functions/domains-add.function.d.ts +4 -4
  94. package/dist/src/fabric/functions/secrets-list.function.d.ts +16 -4
  95. package/dist/src/fabric/functions/secrets-list.function.js +12 -10
  96. package/dist/src/fabric/functions/secrets-rotate.function.d.ts +24 -0
  97. package/dist/src/fabric/functions/secrets-rotate.function.js +36 -0
  98. package/dist/src/fabric/functions/secrets-set.function.d.ts +8 -4
  99. package/dist/src/fabric/functions/secrets-set.function.js +18 -7
  100. package/dist/src/fabric/functions/validate.function.js +16 -7
  101. package/dist/src/fabric/lib/http.d.ts +7 -1
  102. package/dist/src/fabric/lib/sealed-box.d.ts +16 -0
  103. package/dist/src/fabric/lib/sealed-box.js +72 -0
  104. package/dist/src/functions/commands/dev.js +9 -0
  105. package/dist/src/functions/commands/knowledge-index.d.ts +30 -0
  106. package/dist/src/functions/commands/knowledge-index.js +11 -0
  107. package/dist/src/functions/commands/knowledge-validate.d.ts +33 -0
  108. package/dist/src/functions/commands/knowledge-validate.js +11 -0
  109. package/dist/src/functions/commands/new-addon.d.ts +18 -0
  110. package/dist/src/functions/commands/new-addon.js +40 -24
  111. package/dist/src/functions/commands/pikku-command-bootstrap.js +1 -0
  112. package/dist/src/functions/commands/scenario-browser.d.ts +17 -2
  113. package/dist/src/functions/commands/scenario-browser.js +16 -8
  114. package/dist/src/functions/commands/scenario-environment.d.ts +32 -0
  115. package/dist/src/functions/commands/scenario-environment.js +61 -0
  116. package/dist/src/functions/commands/scenario.d.ts +6 -0
  117. package/dist/src/functions/commands/scenario.js +14 -9
  118. package/dist/src/functions/commands/skills.js +27 -61
  119. package/dist/src/functions/db/db-codegen.d.ts +6 -0
  120. package/dist/src/functions/db/db-codegen.js +9 -0
  121. package/dist/src/functions/db/db-migrator.js +14 -0
  122. package/dist/src/functions/db/local-db.d.ts +8 -0
  123. package/dist/src/functions/db/local-db.js +24 -2
  124. package/dist/src/functions/db/migration-identifiers.d.ts +58 -0
  125. package/dist/src/functions/db/migration-identifiers.js +262 -0
  126. package/dist/src/functions/db/migration-provenance.d.ts +35 -0
  127. package/dist/src/functions/db/migration-provenance.js +80 -0
  128. package/dist/src/functions/db/schema-sql.d.ts +43 -0
  129. package/dist/src/functions/db/schema-sql.js +135 -0
  130. package/dist/src/functions/db/sqlite/sqlite-kysely.js +22 -2
  131. package/dist/src/functions/knowledge/render.d.ts +3 -0
  132. package/dist/src/functions/knowledge/render.js +66 -0
  133. package/dist/src/functions/knowledge/schemas.d.ts +51 -0
  134. package/dist/src/functions/knowledge/schemas.js +25 -0
  135. package/dist/src/functions/wirings/credentials/serialize-credentials-types.js +8 -1
  136. package/dist/src/functions/wirings/functions/schemas.js +13 -1
  137. package/dist/src/functions/wirings/rpc/pikku-command-rpc.js +6 -1
  138. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.d.ts +10 -0
  139. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.js +102 -0
  140. package/dist/src/functions/wirings/scenarios/scenario-partition.d.ts +13 -0
  141. package/dist/src/functions/wirings/scenarios/scenario-partition.js +15 -2
  142. package/dist/src/functions/wirings/scenarios/scenario-schema-partition.d.ts +33 -0
  143. package/dist/src/functions/wirings/scenarios/scenario-schema-partition.js +49 -0
  144. package/dist/src/functions/wirings/secrets/serialize-secrets-types.js +10 -3
  145. package/dist/src/functions/wirings/variables/serialize-variables-types.js +8 -1
  146. package/dist/src/functions/workflows/all.workflow.js +2 -7
  147. package/dist/src/utils/file-writer.js +14 -2
  148. package/dist/src/utils/pikku-cli-config.d.ts +14 -0
  149. package/dist/src/utils/pikku-cli-config.js +34 -0
  150. package/dist/src/utils/remove-legacy-scaffold-file.d.ts +14 -0
  151. package/dist/src/utils/remove-legacy-scaffold-file.js +23 -0
  152. package/dist/src/utils/serialize-schemas.d.ts +3 -1
  153. package/dist/src/utils/serialize-schemas.js +39 -4
  154. package/dist/tsconfig.tsbuildinfo +1 -1
  155. package/package.json +10 -9
  156. package/dist/.pikku/schemas/schemas/PikkuScenarioFunctionsOutput.schema.json +0 -1
  157. package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.d.ts +0 -1
  158. package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.js +0 -31
  159. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.d.ts +0 -10
  160. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.js +0 -104
  161. package/skills/pikku-addon/SKILL.md +0 -243
  162. package/skills/pikku-addon/references/addon-package-manifest.md +0 -63
  163. package/skills/pikku-ai-agent/SKILL.md +0 -231
  164. package/skills/pikku-ai-vercel/SKILL.md +0 -82
  165. package/skills/pikku-ai-voice/SKILL.md +0 -88
  166. package/skills/pikku-audit/SKILL.md +0 -175
  167. package/skills/pikku-aws/SKILL.md +0 -111
  168. package/skills/pikku-backblaze/SKILL.md +0 -71
  169. package/skills/pikku-better-auth/SKILL.md +0 -298
  170. package/skills/pikku-cli/SKILL.md +0 -198
  171. package/skills/pikku-cli/references/complete-example.md +0 -82
  172. package/skills/pikku-concepts/SKILL.md +0 -250
  173. package/skills/pikku-concepts/references/concept-mapping.md +0 -556
  174. package/skills/pikku-concepts/references/packages.md +0 -29
  175. package/skills/pikku-config/SKILL.md +0 -212
  176. package/skills/pikku-cron/SKILL.md +0 -214
  177. package/skills/pikku-deploy-azure/SKILL.md +0 -71
  178. package/skills/pikku-deploy-cloudflare/SKILL.md +0 -82
  179. package/skills/pikku-deploy-express/SKILL.md +0 -86
  180. package/skills/pikku-deploy-fastify/SKILL.md +0 -73
  181. package/skills/pikku-deploy-lambda/SKILL.md +0 -110
  182. package/skills/pikku-deploy-nextjs/SKILL.md +0 -78
  183. package/skills/pikku-deploy-uws/SKILL.md +0 -88
  184. package/skills/pikku-deps/SKILL.md +0 -98
  185. package/skills/pikku-emails/SKILL.md +0 -163
  186. package/skills/pikku-fabric/SKILL.md +0 -318
  187. package/skills/pikku-fabric-debug/SKILL.md +0 -112
  188. package/skills/pikku-feature/SKILL.md +0 -258
  189. package/skills/pikku-gateway-slack/SKILL.md +0 -115
  190. package/skills/pikku-http/SKILL.md +0 -220
  191. package/skills/pikku-http/references/http-options.md +0 -55
  192. package/skills/pikku-i18n/SKILL.md +0 -137
  193. package/skills/pikku-info/SKILL.md +0 -100
  194. package/skills/pikku-jose/SKILL.md +0 -105
  195. package/skills/pikku-kysely/SKILL.md +0 -219
  196. package/skills/pikku-machine-auth/SKILL.md +0 -183
  197. package/skills/pikku-mcp/SKILL.md +0 -241
  198. package/skills/pikku-middleware/SKILL.md +0 -231
  199. package/skills/pikku-middleware/references/middleware-patterns.md +0 -61
  200. package/skills/pikku-mongodb/SKILL.md +0 -105
  201. package/skills/pikku-n8n-import/SKILL.md +0 -109
  202. package/skills/pikku-n8n-import/SPEC.md +0 -84
  203. package/skills/pikku-n8n-import/references/addon-mapping.md +0 -121
  204. package/skills/pikku-n8n-import/references/code-translation.md +0 -121
  205. package/skills/pikku-n8n-import/references/loops-and-control.md +0 -87
  206. package/skills/pikku-paraglide/SKILL.md +0 -117
  207. package/skills/pikku-permissions/SKILL.md +0 -192
  208. package/skills/pikku-pino/SKILL.md +0 -79
  209. package/skills/pikku-product-second-opinion/README.md +0 -43
  210. package/skills/pikku-product-second-opinion/SKILL.md +0 -158
  211. package/skills/pikku-product-second-opinion/example/sample-report.md +0 -90
  212. package/skills/pikku-product-second-opinion/references/report-template.md +0 -73
  213. package/skills/pikku-queue/SKILL.md +0 -240
  214. package/skills/pikku-react/SKILL.md +0 -212
  215. package/skills/pikku-react-query/SKILL.md +0 -242
  216. package/skills/pikku-realtime/SKILL.md +0 -236
  217. package/skills/pikku-realtime/references/other-routes.md +0 -23
  218. package/skills/pikku-redis/SKILL.md +0 -90
  219. package/skills/pikku-rpc/SKILL.md +0 -171
  220. package/skills/pikku-rtl/SKILL.md +0 -219
  221. package/skills/pikku-scenario/SKILL.md +0 -382
  222. package/skills/pikku-schedule/SKILL.md +0 -57
  223. package/skills/pikku-schema-ajv/SKILL.md +0 -62
  224. package/skills/pikku-schema-cfworker/SKILL.md +0 -63
  225. package/skills/pikku-security/SKILL.md +0 -108
  226. package/skills/pikku-services/SKILL.md +0 -248
  227. package/skills/pikku-services/references/audit-wire-service.md +0 -34
  228. package/skills/pikku-software-archaeology/README.md +0 -70
  229. package/skills/pikku-software-archaeology/SKILL.md +0 -186
  230. package/skills/pikku-software-archaeology/references/blueprint.schema.json +0 -625
  231. package/skills/pikku-software-archaeology/references/pikku-mapping.md +0 -49
  232. package/skills/pikku-software-archaeology/scripts/validate.mjs +0 -173
  233. package/skills/pikku-tag-middleware/SKILL.md +0 -13
  234. package/skills/pikku-template-clone/SKILL.md +0 -40
  235. package/skills/pikku-trigger/SKILL.md +0 -181
  236. package/skills/pikku-versioning/SKILL.md +0 -173
  237. package/skills/pikku-websocket/SKILL.md +0 -243
  238. package/skills/pikku-workflow/SKILL.md +0 -172
  239. package/skills/pikku-workflow/references/workflow-reference.md +0 -63
  240. package/skills/pikku-workflows-client/SKILL.md +0 -150
  241. package/skills/pikku-ws/SKILL.md +0 -47
@@ -29,6 +29,8 @@ import { scopesAudit } from './functions/commands/scopes-audit.js';
29
29
  import { scopesPrune } from './functions/commands/scopes-prune.js';
30
30
  import { pikkuAudit } from './functions/commands/audit.js';
31
31
  import { workspaceValidate, renderWorkspaceValidate, } from './functions/commands/workspace-validate.js';
32
+ import { knowledgeValidate, renderKnowledgeValidate, } from './functions/commands/knowledge-validate.js';
33
+ import { knowledgeIndex, renderKnowledgeIndex, } from './functions/commands/knowledge-index.js';
32
34
  import { scenarioRun, scenarioList } from './functions/commands/scenario.js';
33
35
  import { pikkuVersionsInit } from './functions/commands/versions-init.js';
34
36
  import { pikkuEmailsInit } from './functions/commands/emails-init.js';
@@ -375,6 +377,27 @@ wireCLI({
375
377
  }),
376
378
  },
377
379
  },
380
+ knowledge: {
381
+ description: 'Inspect and maintain the knowledge base — what this app is, in the language its users use',
382
+ subcommands: {
383
+ validate: pikkuCLICommand({
384
+ func: knowledgeValidate,
385
+ render: renderKnowledgeValidate,
386
+ description: 'Check the knowledge base against the app-project profile, including every resource: reference',
387
+ }),
388
+ index: pikkuCLICommand({
389
+ func: knowledgeIndex,
390
+ render: renderKnowledgeIndex,
391
+ description: 'Refresh every index.md so each section lists the notes actually in it',
392
+ options: {
393
+ check: {
394
+ description: 'Report stale indexes without writing anything, for use as a CI gate',
395
+ default: false,
396
+ },
397
+ },
398
+ }),
399
+ },
400
+ },
378
401
  workspace: {
379
402
  description: 'Workspace-level validation and maintenance commands',
380
403
  subcommands: {
@@ -427,6 +450,12 @@ wireCLI({
427
450
  description: 'Keep every stack frame on a failure. Without it, only the project’s own frames are shown',
428
451
  default: false,
429
452
  },
453
+ apiUrl: {
454
+ description: "Override the environment's apiUrl for this run — for a target that only exists at run time, such as a freshly deployed sandbox",
455
+ },
456
+ appUrl: {
457
+ description: "Override the environment's appUrl for this run (the frontend browser steps navigate against)",
458
+ },
430
459
  },
431
460
  }),
432
461
  list: pikkuCLICommand({
@@ -7,6 +7,7 @@
7
7
  * Workflow orchestrators dispatch to step units via queue or RPC (inline).
8
8
  */
9
9
  import { resolveDeployTarget, } from '@pikku/inspector';
10
+ import { isScenarioFunction, withoutScenarios, withoutScenarioWorkflows, } from '../../functions/wirings/scenarios/scenario-partition.js';
10
11
  export function analyzeDeployment(state, options) {
11
12
  const serverlessIncompatible = new Set(options.serverlessIncompatible ?? []);
12
13
  const defaultTarget = options.defaultTarget ?? 'serverless';
@@ -18,7 +19,13 @@ export function analyzeDeployment(state, options) {
18
19
  const agents = [];
19
20
  const mcpEndpoints = [];
20
21
  const workflows = [];
21
- const functionsMeta = state.functions.meta;
22
+ // Scenarios, features and steps are test artifacts that happen to be built out
23
+ // of functions and workflows, so without this they analyze like application
24
+ // code: every scenario became its own unit, its own `WorkflowDefinition` and —
25
+ // because a scenario IS a workflow — a `wf-orchestrator-<scenario>` queue that
26
+ // a provider would then create in production.
27
+ const functionsMeta = withoutScenarios(state.functions.meta);
28
+ const graphMeta = withoutScenarioWorkflows(state.workflows.graphMeta);
22
29
  const httpMeta = state.http.meta;
23
30
  // ── Step 1: Create function units ──────────────────────────────────
24
31
  // Each function gets one unit. Collect all its triggers.
@@ -190,9 +197,19 @@ export function analyzeDeployment(state, options) {
190
197
  });
191
198
  }
192
199
  // ── Step 3: MCP gateway ────────────────────────────────────────────
193
- const mcpToolIds = values(state.mcpEndpoints.toolsMeta).map((t) => t.pikkuFuncId);
194
- const mcpResourceIds = values(state.mcpEndpoints.resourcesMeta).map((r) => r.pikkuFuncId);
195
- const mcpPromptIds = values(state.mcpEndpoints.promptsMeta).map((p) => p.pikkuFuncId);
200
+ // The MCP metas are keyed by wiring, not by function, so they are the one
201
+ // place a scenario or step id can still arrive from raw state. A gateway that
202
+ // lists one would depend on a unit that was never emitted.
203
+ const deployableFuncId = (funcId) => !isScenarioFunction(state.functions.meta[funcId]);
204
+ const mcpToolIds = values(state.mcpEndpoints.toolsMeta)
205
+ .map((t) => t.pikkuFuncId)
206
+ .filter(deployableFuncId);
207
+ const mcpResourceIds = values(state.mcpEndpoints.resourcesMeta)
208
+ .map((r) => r.pikkuFuncId)
209
+ .filter(deployableFuncId);
210
+ const mcpPromptIds = values(state.mcpEndpoints.promptsMeta)
211
+ .map((p) => p.pikkuFuncId)
212
+ .filter(deployableFuncId);
196
213
  // Include functions explicitly marked mcp: true
197
214
  for (const [funcId, funcMeta] of entries(functionsMeta)) {
198
215
  if (funcMeta.mcp &&
@@ -248,7 +265,7 @@ export function analyzeDeployment(state, options) {
248
265
  });
249
266
  }
250
267
  // ── Step 5: Workflows ──────────────────────────────────────────────
251
- buildWorkflows(state.workflows.graphMeta, functionsMeta, { ...state.rpc?.exposedMeta, ...state.rpc?.internalMeta }, httpMeta, units, workflows, queues, workflowQueues);
268
+ buildWorkflows(graphMeta, functionsMeta, { ...state.rpc?.exposedMeta, ...state.rpc?.internalMeta }, httpMeta, units, workflows, queues, workflowQueues);
252
269
  // ── Step 6: Ensure function units exist for gateway dependencies ───
253
270
  // Gateways depend on function units. If a function is only used via
254
271
  // a gateway (not directly wired to HTTP/queue/cron), it still needs
@@ -8,6 +8,7 @@ import { join } from 'node:path';
8
8
  import { existsSync } from 'node:fs';
9
9
  import { mkdir, writeFile, copyFile } from 'node:fs/promises';
10
10
  import { analyzeDeployment } from './analyzer/index.js';
11
+ import { withoutScenarios } from '../functions/wirings/scenarios/scenario-partition.js';
11
12
  import { generatePerUnitCodegen } from './codegen/per-unit-codegen.js';
12
13
  import { generateServerEntrySource, SERVER_DOCKERFILE, SERVER_DOCKERIGNORE, } from './server-entry.js';
13
14
  const MERGED_SERVER_UNIT_NAME = 'pikku-server-container';
@@ -63,7 +64,10 @@ export async function runBuildPipeline(options) {
63
64
  name: unitName,
64
65
  role: 'function',
65
66
  target: 'serverless',
66
- functionIds: Object.keys(inspectorState.functions.meta),
67
+ // Scenarios and steps are excluded for the same reason the analyzer drops
68
+ // them: a single-unit bundle is still a deployment, and a test harness has
69
+ // no business inside one.
70
+ functionIds: Object.keys(withoutScenarios(inspectorState.functions.meta)),
67
71
  services: [],
68
72
  dependsOn: [],
69
73
  handlers: [],
@@ -451,37 +451,65 @@ export declare const fabricCommands: {
451
451
  value?: string | undefined;
452
452
  force?: boolean | undefined;
453
453
  }, {
454
- ok: boolean;
454
+ name: string;
455
+ keyId: string;
455
456
  }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
456
457
  name: string;
457
458
  branch: string;
458
459
  value?: string | undefined;
459
460
  force?: boolean | undefined;
460
461
  }, {
461
- ok: boolean;
462
+ name: string;
463
+ keyId: string;
462
464
  }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").WiredServices> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
463
465
  name: string;
464
466
  branch: string;
465
467
  value?: string | undefined;
466
468
  force?: boolean | undefined;
467
469
  }, {
468
- ok: boolean;
470
+ name: string;
471
+ keyId: string;
469
472
  }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").WiredServices>, undefined, undefined>, import("../../.pikku/pikku-types.gen.js").PikkuMiddleware, (services: import("../../types/application-types.js").SingletonServices, data: any, session?: import("../../types/application-types.js").UserSession | undefined) => void | Promise<void>, string>;
470
473
  list: import("@pikku/core/cli").CoreCLICommandConfig<import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
471
474
  branch: string;
472
475
  json?: boolean | undefined;
473
476
  }, {
474
- names: string[];
477
+ secrets: {
478
+ name: string;
479
+ updatedAt: string;
480
+ }[];
475
481
  }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
476
482
  branch: string;
477
483
  json?: boolean | undefined;
478
484
  }, {
479
- names: string[];
485
+ secrets: {
486
+ name: string;
487
+ updatedAt: string;
488
+ }[];
480
489
  }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").WiredServices> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
481
490
  branch: string;
482
491
  json?: boolean | undefined;
483
492
  }, {
484
- names: string[];
493
+ secrets: {
494
+ name: string;
495
+ updatedAt: string;
496
+ }[];
497
+ }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").WiredServices>, undefined, undefined>, import("../../.pikku/pikku-types.gen.js").PikkuMiddleware, (services: import("../../types/application-types.js").SingletonServices, data: any, session?: import("../../types/application-types.js").UserSession | undefined) => void | Promise<void>, string>;
498
+ rotate: import("@pikku/core/cli").CoreCLICommandConfig<import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
499
+ branch: string;
500
+ force?: boolean | undefined;
501
+ }, {
502
+ retiredKeyId: string;
503
+ }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
504
+ branch: string;
505
+ force?: boolean | undefined;
506
+ }, {
507
+ retiredKeyId: string;
508
+ }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").WiredServices> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
509
+ branch: string;
510
+ force?: boolean | undefined;
511
+ }, {
512
+ retiredKeyId: string;
485
513
  }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").WiredServices>, undefined, undefined>, import("../../.pikku/pikku-types.gen.js").PikkuMiddleware, (services: import("../../types/application-types.js").SingletonServices, data: any, session?: import("../../types/application-types.js").UserSession | undefined) => void | Promise<void>, string>;
486
514
  };
487
515
  };
@@ -622,21 +650,21 @@ export declare const fabricCommands: {
622
650
  }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").WiredServices>, undefined, undefined>, import("../../.pikku/pikku-types.gen.js").PikkuMiddleware, (services: import("../../types/application-types.js").SingletonServices, data: any, session?: import("../../types/application-types.js").UserSession | undefined) => void | Promise<void>, string>;
623
651
  add: import("@pikku/core/cli").CoreCLICommandConfig<import("../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
624
652
  hostname: string;
625
- target?: "api" | "app" | undefined;
653
+ target?: "app" | "api" | undefined;
626
654
  apiUrl?: string | undefined;
627
655
  }, {
628
656
  hostname: string;
629
657
  cnameTarget: string;
630
658
  }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
631
659
  hostname: string;
632
- target?: "api" | "app" | undefined;
660
+ target?: "app" | "api" | undefined;
633
661
  apiUrl?: string | undefined;
634
662
  }, {
635
663
  hostname: string;
636
664
  cnameTarget: string;
637
665
  }, "rpc" | "session", import("../../.pikku/pikku-types.gen.js").WiredServices> | import("../../.pikku/pikku-types.gen.js").PikkuFunction<{
638
666
  hostname: string;
639
- target?: "api" | "app" | undefined;
667
+ target?: "app" | "api" | undefined;
640
668
  apiUrl?: string | undefined;
641
669
  }, {
642
670
  hostname: string;
@@ -31,6 +31,7 @@ import { FabricDbSchema, renderDbSchema, } from './functions/db-schema.function.
31
31
  import { FabricRollback } from './functions/rollback.function.js';
32
32
  import { FabricSecretsSet } from './functions/secrets-set.function.js';
33
33
  import { FabricSecretsList } from './functions/secrets-list.function.js';
34
+ import { FabricSecretsRotate } from './functions/secrets-rotate.function.js';
34
35
  import { FabricLogs } from './functions/logs.function.js';
35
36
  import { FabricMetrics } from './functions/metrics.function.js';
36
37
  import { FabricTrace } from './functions/trace.function.js';
@@ -241,6 +242,17 @@ export const fabricCommands = defineCLICommands({
241
242
  json: { description: 'Machine-readable output', default: false },
242
243
  },
243
244
  }),
245
+ rotate: pikkuCLICommand({
246
+ func: FabricSecretsRotate,
247
+ description: "Retire a stage's sealing key (secrets must be set again)",
248
+ options: {
249
+ branch: { description: 'Target branch', short: 'b' },
250
+ force: {
251
+ description: 'Confirm that existing secrets become unreadable',
252
+ default: false,
253
+ },
254
+ },
255
+ }),
244
256
  },
245
257
  },
246
258
  logs: pikkuCLICommand({
@@ -2,8 +2,8 @@ import { z } from 'zod';
2
2
  export declare const FabricDomainsAddInput: z.ZodObject<{
3
3
  hostname: z.ZodString;
4
4
  target: z.ZodOptional<z.ZodEnum<{
5
- api: "api";
6
5
  app: "app";
6
+ api: "api";
7
7
  }>>;
8
8
  apiUrl: z.ZodOptional<z.ZodString>;
9
9
  }, z.core.$strip>;
@@ -13,21 +13,21 @@ export declare const FabricDomainsAddOutput: z.ZodObject<{
13
13
  }, z.core.$strip>;
14
14
  export declare const FabricDomainsAdd: import("../../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
15
15
  hostname: string;
16
- target?: "api" | "app" | undefined;
16
+ target?: "app" | "api" | undefined;
17
17
  apiUrl?: string | undefined;
18
18
  }, {
19
19
  hostname: string;
20
20
  cnameTarget: string;
21
21
  }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
22
22
  hostname: string;
23
- target?: "api" | "app" | undefined;
23
+ target?: "app" | "api" | undefined;
24
24
  apiUrl?: string | undefined;
25
25
  }, {
26
26
  hostname: string;
27
27
  cnameTarget: string;
28
28
  }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").WiredServices> | import("../../../.pikku/pikku-types.gen.js").PikkuFunction<{
29
29
  hostname: string;
30
- target?: "api" | "app" | undefined;
30
+ target?: "app" | "api" | undefined;
31
31
  apiUrl?: string | undefined;
32
32
  }, {
33
33
  hostname: string;
@@ -4,21 +4,33 @@ export declare const FabricSecretsListInput: z.ZodObject<{
4
4
  json: z.ZodOptional<z.ZodBoolean>;
5
5
  }, z.core.$strip>;
6
6
  export declare const FabricSecretsListOutput: z.ZodObject<{
7
- names: z.ZodArray<z.ZodString>;
7
+ secrets: z.ZodArray<z.ZodObject<{
8
+ name: z.ZodString;
9
+ updatedAt: z.ZodString;
10
+ }, z.core.$strip>>;
8
11
  }, z.core.$strip>;
9
12
  export declare const FabricSecretsList: import("../../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
10
13
  branch: string;
11
14
  json?: boolean | undefined;
12
15
  }, {
13
- names: string[];
16
+ secrets: {
17
+ name: string;
18
+ updatedAt: string;
19
+ }[];
14
20
  }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
15
21
  branch: string;
16
22
  json?: boolean | undefined;
17
23
  }, {
18
- names: string[];
24
+ secrets: {
25
+ name: string;
26
+ updatedAt: string;
27
+ }[];
19
28
  }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").WiredServices> | import("../../../.pikku/pikku-types.gen.js").PikkuFunction<{
20
29
  branch: string;
21
30
  json?: boolean | undefined;
22
31
  }, {
23
- names: string[];
32
+ secrets: {
33
+ name: string;
34
+ updatedAt: string;
35
+ }[];
24
36
  }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").WiredServices>, undefined, undefined>;
@@ -2,13 +2,16 @@ import { z } from 'zod';
2
2
  import { pikkuSessionlessFunc } from '../../../.pikku/pikku-types.gen.js';
3
3
  import { resolveApiContext } from '../lib/config.js';
4
4
  import { getFabricRPC } from '../lib/http.js';
5
+ import { resolveStageId } from '../lib/stage.js';
5
6
  export const FabricSecretsListInput = z.object({
6
7
  branch: z.string(),
7
8
  json: z.boolean().optional(),
8
9
  });
9
- export const FabricSecretsListOutput = z.object({ names: z.array(z.string()) });
10
+ export const FabricSecretsListOutput = z.object({
11
+ secrets: z.array(z.object({ name: z.string(), updatedAt: z.string() })),
12
+ });
10
13
  export const FabricSecretsList = pikkuSessionlessFunc({
11
- description: 'List secret names visible to a stage (values never leave the server).',
14
+ description: 'List the secrets set on a stage, by name. Values are sealed to the stage and never leave it.',
12
15
  input: FabricSecretsListInput,
13
16
  output: FabricSecretsListOutput,
14
17
  func: async (_services, { branch, json }) => {
@@ -18,19 +21,18 @@ export const FabricSecretsList = pikkuSessionlessFunc({
18
21
  if (!ctx.projectId)
19
22
  throw new Error('No fabric project linked. Run `pikku fabric link` first.');
20
23
  const rpc = getFabricRPC({ apiUrl: ctx.apiUrl, token: ctx.token });
21
- const result = await rpc.invoke('listStageSecrets', {
22
- projectId: ctx.projectId,
23
- branch,
24
- });
24
+ const stageId = await resolveStageId(rpc, ctx.projectId, branch);
25
+ const result = await rpc.invoke('listStageSecretNames', { stageId });
25
26
  if (json) {
26
- console.log(JSON.stringify({ branch, names: result.names }, null, 2));
27
+ console.log(JSON.stringify({ branch, secrets: result.secrets }, null, 2));
27
28
  }
28
- else if (result.names.length === 0) {
29
+ else if (result.secrets.length === 0) {
29
30
  console.log(`[fabric] no secrets set on ${branch}`);
30
31
  }
31
32
  else {
32
- for (const name of result.names)
33
- console.log(name);
33
+ for (const { name, updatedAt } of result.secrets) {
34
+ console.log(`${name}\t${updatedAt}`);
35
+ }
34
36
  }
35
37
  return result;
36
38
  },
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ export declare const FabricSecretsRotateInput: z.ZodObject<{
3
+ branch: z.ZodString;
4
+ force: z.ZodOptional<z.ZodBoolean>;
5
+ }, z.core.$strip>;
6
+ export declare const FabricSecretsRotateOutput: z.ZodObject<{
7
+ retiredKeyId: z.ZodString;
8
+ }, z.core.$strip>;
9
+ export declare const FabricSecretsRotate: import("../../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
10
+ branch: string;
11
+ force?: boolean | undefined;
12
+ }, {
13
+ retiredKeyId: string;
14
+ }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
15
+ branch: string;
16
+ force?: boolean | undefined;
17
+ }, {
18
+ retiredKeyId: string;
19
+ }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").WiredServices> | import("../../../.pikku/pikku-types.gen.js").PikkuFunction<{
20
+ branch: string;
21
+ force?: boolean | undefined;
22
+ }, {
23
+ retiredKeyId: string;
24
+ }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").WiredServices>, undefined, undefined>;
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod';
2
+ import { pikkuSessionlessFunc } from '../../../.pikku/pikku-types.gen.js';
3
+ import { resolveApiContext } from '../lib/config.js';
4
+ import { getFabricRPC } from '../lib/http.js';
5
+ import { resolveStageId } from '../lib/stage.js';
6
+ export const FabricSecretsRotateInput = z.object({
7
+ branch: z.string(),
8
+ force: z.boolean().optional(),
9
+ });
10
+ export const FabricSecretsRotateOutput = z.object({
11
+ retiredKeyId: z.string(),
12
+ });
13
+ export const FabricSecretsRotate = pikkuSessionlessFunc({
14
+ description: "Retire a stage's sealing key so the next deploy issues a new one. Secrets sealed to the old key must be set again.",
15
+ input: FabricSecretsRotateInput,
16
+ output: FabricSecretsRotateOutput,
17
+ func: async (_services, { branch, force }) => {
18
+ const ctx = await resolveApiContext();
19
+ if (!ctx.token)
20
+ throw new Error('Not logged in. Run `pikku fabric login` first.');
21
+ if (!ctx.projectId)
22
+ throw new Error('No fabric project linked. Run `pikku fabric link` first.');
23
+ if (!force) {
24
+ // Not a confirmation for politeness — this is the one operation here
25
+ // that destroys access to data. Fabric cannot read the sealed values, so
26
+ // it cannot carry them over, and nobody can undo it afterwards.
27
+ throw new Error(`Rotating the sealing key on ${branch} makes every secret already set on it unreadable — fabric cannot re-seal values it cannot open, so each one must be set again after the next deploy. Re-run with --force if that is what you want.`);
28
+ }
29
+ const rpc = getFabricRPC({ apiUrl: ctx.apiUrl, token: ctx.token });
30
+ const stageId = await resolveStageId(rpc, ctx.projectId, branch);
31
+ const result = await rpc.invoke('rotateStageSealingKey', { stageId });
32
+ console.log(`[fabric] retired sealing key ${result.retiredKeyId} on ${branch}`);
33
+ console.log('[fabric] deploy the stage to issue a new key, then set its secrets again');
34
+ return result;
35
+ },
36
+ });
@@ -6,7 +6,8 @@ export declare const FabricSecretsSetInput: z.ZodObject<{
6
6
  force: z.ZodOptional<z.ZodBoolean>;
7
7
  }, z.core.$strip>;
8
8
  export declare const FabricSecretsSetOutput: z.ZodObject<{
9
- ok: z.ZodBoolean;
9
+ name: z.ZodString;
10
+ keyId: z.ZodString;
10
11
  }, z.core.$strip>;
11
12
  export declare const FabricSecretsSet: import("../../../.pikku/pikku-types.gen.js").PikkuFunctionConfig<{
12
13
  name: string;
@@ -14,19 +15,22 @@ export declare const FabricSecretsSet: import("../../../.pikku/pikku-types.gen.j
14
15
  value?: string | undefined;
15
16
  force?: boolean | undefined;
16
17
  }, {
17
- ok: boolean;
18
+ name: string;
19
+ keyId: string;
18
20
  }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").PikkuFunctionSessionless<{
19
21
  name: string;
20
22
  branch: string;
21
23
  value?: string | undefined;
22
24
  force?: boolean | undefined;
23
25
  }, {
24
- ok: boolean;
26
+ name: string;
27
+ keyId: string;
25
28
  }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").WiredServices> | import("../../../.pikku/pikku-types.gen.js").PikkuFunction<{
26
29
  name: string;
27
30
  branch: string;
28
31
  value?: string | undefined;
29
32
  force?: boolean | undefined;
30
33
  }, {
31
- ok: boolean;
34
+ name: string;
35
+ keyId: string;
32
36
  }, "rpc" | "session", import("../../../.pikku/pikku-types.gen.js").WiredServices>, undefined, undefined>;
@@ -3,15 +3,20 @@ import { pikkuSessionlessFunc } from '../../../.pikku/pikku-types.gen.js';
3
3
  import { resolveApiContext } from '../lib/config.js';
4
4
  import { getFabricRPC } from '../lib/http.js';
5
5
  import { promptSecret } from '../lib/prompt.js';
6
+ import { resolveStageId } from '../lib/stage.js';
7
+ import { seal, serializeSealedValue } from '../lib/sealed-box.js';
6
8
  export const FabricSecretsSetInput = z.object({
7
9
  name: z.string(),
8
10
  branch: z.string(),
9
11
  value: z.string().optional(),
10
12
  force: z.boolean().optional(),
11
13
  });
12
- export const FabricSecretsSetOutput = z.object({ ok: z.boolean() });
14
+ export const FabricSecretsSetOutput = z.object({
15
+ name: z.string(),
16
+ keyId: z.string(),
17
+ });
13
18
  export const FabricSecretsSet = pikkuSessionlessFunc({
14
- description: 'Set a stage-scoped secret. Encrypted with the stage KEK and stored in the project D1.',
19
+ description: 'Set a stage-scoped secret. Sealed here to the stage public key the plaintext never leaves this machine.',
15
20
  input: FabricSecretsSetInput,
16
21
  output: FabricSecretsSetOutput,
17
22
  func: async (_services, { name, branch, value }) => {
@@ -24,13 +29,19 @@ export const FabricSecretsSet = pikkuSessionlessFunc({
24
29
  if (!plaintext)
25
30
  throw new Error('Empty secret value — aborting.');
26
31
  const rpc = getFabricRPC({ apiUrl: ctx.apiUrl, token: ctx.token });
27
- const result = await rpc.invoke('setStageSecret', {
28
- projectId: ctx.projectId,
29
- branch,
32
+ const stageId = await resolveStageId(rpc, ctx.projectId, branch);
33
+ // Fabric holds only the public half of this keypair. The private half went
34
+ // to the stage's worker at deploy time and was dropped there, so the value
35
+ // sealed below can be opened by that worker and by nothing else — including
36
+ // by the fabric process that stores it.
37
+ const key = await rpc.invoke('getStageSealingKey', { stageId });
38
+ const sealed = seal(key.publicKey, key.keyId, plaintext);
39
+ const result = await rpc.invoke('setStageSealedSecret', {
40
+ stageId,
30
41
  name,
31
- value: plaintext,
42
+ sealedValue: serializeSealedValue(sealed),
32
43
  });
33
- console.log(`[fabric] ${name} set on ${branch}.`);
44
+ console.log(`[fabric] ${name} sealed to ${result.keyId} and set on ${branch}.`);
34
45
  return result;
35
46
  },
36
47
  });
@@ -6,6 +6,7 @@ import { createRequire } from 'node:module';
6
6
  import { pikkuSessionlessFunc } from '../../../.pikku/pikku-types.gen.js';
7
7
  import { added, changed, removed, dim } from '../lib/output.js';
8
8
  import { typeCheckFrontends } from '../lib/frontend-typecheck.js';
9
+ import { runKnowledgeValidate } from '@pikku/knowledge';
9
10
  const FindingSchema = z.object({
10
11
  id: z.string(),
11
12
  severity: z.enum(['error', 'warn', 'info']),
@@ -312,16 +313,21 @@ export async function runValidate(startDir = process.cwd(), opts = {}) {
312
313
  // ── required project files ────────────────────────────────────────────
313
314
  // These files must exist and be committed — they are seeded from the sandbox
314
315
  // but belong to the project so the AI agent can read and update them.
315
- for (const relPath of [
316
- 'db/annotations.ts',
317
- 'knowledge/design-language.md',
318
- 'knowledge/security.md',
319
- 'knowledge/technology.md',
320
- ]) {
316
+ for (const relPath of ['db/annotations.ts']) {
321
317
  if (!existsSync(join(root, relPath))) {
322
318
  w(`missing-required-file-${relPath.replace(/[^a-z0-9]/gi, '-')}`, `${relPath} is missing — this file must be committed to the project`, join(root, relPath), lines(`Create ${relPath} and commit it.`, 'The starter-template ships a stub you can copy as a starting point.'));
323
319
  }
324
320
  }
321
+ // ── knowledge base ─────────────────────────────────────────────────────
322
+ // Delegated to the shared checker rather than asserting a list of filenames.
323
+ // The three flat files this used to require (design-language.md, security.md,
324
+ // technology.md) are not a knowledge base — they are three documents nothing
325
+ // can link into, and the profile now organises the same content into sections
326
+ // of notes. Fabric adds decisions/design/ on top of what is checked here.
327
+ const knowledge = await runKnowledgeValidate(root, join(root, typeof pikkuConfig?.outDir === 'string' ? pikkuConfig.outDir : '.pikku'));
328
+ for (const finding of knowledge.findings) {
329
+ findings.push({ ...finding, path: join(root, finding.path) });
330
+ }
325
331
  const rootPkgPath = join(root, 'package.json');
326
332
  const rootPkg = await readJsonSafe(rootPkgPath);
327
333
  if (!rootPkg) {
@@ -671,7 +677,10 @@ export async function runValidate(startDir = process.cwd(), opts = {}) {
671
677
  // nothing — flagging it told every fresh scaffold to import a dialect it
672
678
  // must not use.
673
679
  const constructsKysely = /new\s+Kysely\s*[<(]/.test(servicesText);
674
- if (dbEngine !== 'postgres' && usesKysely && constructsKysely && !usesLibsql) {
680
+ if (dbEngine !== 'postgres' &&
681
+ usesKysely &&
682
+ constructsKysely &&
683
+ !usesLibsql) {
675
684
  e('services-wrong-db-adapter', 'services.ts uses Kysely but not LibsqlWebDialect — Fabric injects a Turso/libSQL DATABASE_URL at runtime, not a PostgreSQL URL', servicesPath, 'Import LibsqlWebDialect from @pikku/kysely-sqlite and replace the dialect: new Kysely({ dialect: new LibsqlWebDialect({ url: databaseUrl }) })');
676
685
  }
677
686
  if (usesProcessEnv) {
@@ -1,4 +1,10 @@
1
+ /**
2
+ * Type-safe RPC client backed by the snapshot SDK in `../sdk/`. The map is
3
+ * a copy of fabric's generated rpc-map at the time of the addon's last
4
+ * release — versioning bumps are taken by republishing this package.
5
+ */
6
+ import { PikkuRPC } from '../sdk/pikku-rpc.gen.js';
1
7
  export declare function getFabricRPC(opts: {
2
8
  apiUrl: string;
3
9
  token: string | null;
4
- }): any;
10
+ }): PikkuRPC;
@@ -0,0 +1,16 @@
1
+ /** Algorithm tag carried by every sealed value. Must match fabric's. */
2
+ export declare const SEALED_BOX_ALGORITHM = "sealed-x25519-v1";
3
+ export interface SealedValue {
4
+ alg: typeof SEALED_BOX_ALGORITHM;
5
+ /** The `encryption_key` row whose public key sealed this. */
6
+ kid: string;
7
+ /** Ephemeral public key, SPKI DER as base64. */
8
+ epk: string;
9
+ iv: string;
10
+ ct: string;
11
+ tag: string;
12
+ }
13
+ /** Seal a value to a stage's public key. */
14
+ export declare function seal(publicKey: string, keyId: string, plaintext: string): SealedValue;
15
+ /** Wire form. Fabric stores exactly this string. */
16
+ export declare function serializeSealedValue(sealed: SealedValue): string;