@pikku/cli 0.12.92 → 0.12.96

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 (196) hide show
  1. package/console-app/assets/{index-DDpIMCpy.js → index-CeQodYfg.js} +151 -149
  2. package/console-app/assets/{index-D0HG8q0B.css → index-CyYHTGWZ.css} +1 -1
  3. package/console-app/index.html +2 -2
  4. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  5. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  6. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  7. package/dist/.pikku/cli/pikku-cli-channel.d.ts +13 -15
  8. package/dist/.pikku/cli/pikku-cli-channel.js +72 -3
  9. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +14 -4
  10. package/dist/.pikku/cli/pikku-cli-client.gen.js +20 -10
  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-types.gen.d.ts +1 -1
  14. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  15. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  16. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +156 -2
  17. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  18. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  19. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  20. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  21. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  22. package/dist/.pikku/function/pikku-function-types.gen.d.ts +55 -20
  23. package/dist/.pikku/function/pikku-function-types.gen.js +27 -1
  24. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  25. package/dist/.pikku/function/pikku-functions-meta.gen.json +176 -13
  26. package/dist/.pikku/function/pikku-functions.gen.js +11 -1
  27. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  28. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  29. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  30. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  31. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  32. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  33. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  34. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  35. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  36. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  37. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  38. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  39. package/dist/.pikku/pikku-types.gen.js +1 -1
  40. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  41. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  42. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  43. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  44. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  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 +9 -0
  47. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  48. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  49. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  50. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  51. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  52. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  53. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  54. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  55. package/dist/.pikku/schemas/register.gen.js +21 -3
  56. package/dist/.pikku/schemas/schemas/KnowledgeIndexInput.schema.json +1 -0
  57. package/dist/.pikku/schemas/schemas/KnowledgeIndexOutput.schema.json +1 -0
  58. package/dist/.pikku/schemas/schemas/KnowledgeValidateInput.schema.json +1 -0
  59. package/dist/.pikku/schemas/schemas/KnowledgeValidateOutput.schema.json +1 -0
  60. package/dist/.pikku/schemas/schemas/PersonaRunInput.schema.json +1 -0
  61. package/dist/.pikku/schemas/schemas/PersonaSyncInput.schema.json +1 -0
  62. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  63. package/dist/.pikku/schemas/schemas/PikkuPersonasInput.schema.json +1 -0
  64. package/dist/.pikku/schemas/schemas/PikkuRolesInput.schema.json +1 -0
  65. package/dist/.pikku/schemas/schemas/RolesAuditInput.schema.json +1 -0
  66. package/dist/.pikku/schemas/schemas/RolesPruneInput.schema.json +1 -0
  67. package/dist/.pikku/scopes/pikku-personas-meta.gen.json +1 -0
  68. package/dist/.pikku/scopes/pikku-personas.gen.d.ts +36 -0
  69. package/dist/.pikku/scopes/pikku-personas.gen.js +17 -0
  70. package/dist/.pikku/scopes/pikku-roles-meta.gen.json +1 -0
  71. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +30 -0
  72. package/dist/.pikku/scopes/pikku-roles.gen.js +18 -0
  73. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +4 -2
  74. package/dist/.pikku/scopes/pikku-scope-types.gen.js +3 -2
  75. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  76. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +2 -2
  77. package/dist/.pikku/secrets/pikku-secret-types.gen.js +2 -2
  78. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  79. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  80. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  81. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  82. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +2 -2
  83. package/dist/.pikku/variables/pikku-variable-types.gen.js +2 -2
  84. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  85. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  86. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +27 -3
  87. package/dist/.pikku/workflow/pikku-personas.gen.d.ts +15 -0
  88. package/dist/.pikku/workflow/pikku-personas.gen.js +17 -0
  89. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +96 -19
  90. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +22 -2
  91. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  92. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  93. package/dist/bin/pikku-bin.mjs +2 -2
  94. package/dist/bin/pikku.js +0 -0
  95. package/dist/src/cli.wiring.js +124 -2
  96. package/dist/src/deploy/analyzer/analyzer.js +14 -0
  97. package/dist/src/deploy/analyzer/manifest.d.ts +11 -0
  98. package/dist/src/fabric/functions/validate.function.js +20 -11
  99. package/dist/src/functions/commands/bootstrap.js +3 -0
  100. package/dist/src/functions/commands/dev-ai-runner.d.ts +16 -4
  101. package/dist/src/functions/commands/dev-ai-runner.js +63 -29
  102. package/dist/src/functions/commands/dev.js +9 -4
  103. package/dist/src/functions/commands/enable.js +15 -19
  104. package/dist/src/functions/commands/{scenario-environment.d.ts → environment.d.ts} +9 -6
  105. package/dist/src/functions/commands/{scenario-environment.js → environment.js} +5 -4
  106. package/dist/src/functions/commands/knowledge-index.d.ts +30 -0
  107. package/dist/src/functions/commands/knowledge-index.js +11 -0
  108. package/dist/src/functions/commands/knowledge-validate.d.ts +33 -0
  109. package/dist/src/functions/commands/knowledge-validate.js +11 -0
  110. package/dist/src/functions/commands/new-addon.d.ts +18 -0
  111. package/dist/src/functions/commands/new-addon.js +52 -36
  112. package/dist/src/functions/commands/persona-sync.d.ts +33 -0
  113. package/dist/src/functions/commands/persona-sync.js +151 -0
  114. package/dist/src/functions/commands/persona.d.ts +58 -0
  115. package/dist/src/functions/commands/persona.js +217 -0
  116. package/dist/src/functions/commands/roles-audit.d.ts +1 -0
  117. package/dist/src/functions/commands/roles-audit.js +37 -0
  118. package/dist/src/functions/commands/roles-prune.d.ts +16 -0
  119. package/dist/src/functions/commands/roles-prune.js +52 -0
  120. package/dist/src/functions/commands/roles-shared.d.ts +40 -0
  121. package/dist/src/functions/commands/roles-shared.js +77 -0
  122. package/dist/src/functions/commands/scenario-browser.d.ts +3 -3
  123. package/dist/src/functions/commands/scenario-browser.js +1 -1
  124. package/dist/src/functions/commands/scenario-ladder.d.ts +44 -2
  125. package/dist/src/functions/commands/scenario-ladder.js +51 -2
  126. package/dist/src/functions/commands/scenario.d.ts +7 -0
  127. package/dist/src/functions/commands/scenario.js +79 -17
  128. package/dist/src/functions/commands/scopes-shared.d.ts +8 -0
  129. package/dist/src/functions/commands/scopes-shared.js +1 -1
  130. package/dist/src/functions/commands/serve.js +7 -3
  131. package/dist/src/functions/commands/virtual-user-formatter.d.ts +30 -0
  132. package/dist/src/functions/commands/virtual-user-formatter.js +65 -0
  133. package/dist/src/functions/db/local-db.js +38 -1
  134. package/dist/src/functions/knowledge/render.d.ts +3 -0
  135. package/dist/src/functions/knowledge/render.js +66 -0
  136. package/dist/src/functions/knowledge/schemas.d.ts +51 -0
  137. package/dist/src/functions/knowledge/schemas.js +25 -0
  138. package/dist/src/functions/validate/workspace-validate.js +1 -1
  139. package/dist/src/functions/wirings/ai-agent/pikku-command-public-agent.js +2 -1
  140. package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +1 -0
  141. package/dist/src/functions/wirings/auth/serialize-auth-gen.d.ts +2 -2
  142. package/dist/src/functions/wirings/auth/serialize-auth-gen.js +8 -8
  143. package/dist/src/functions/wirings/cli/serialize-channel-cli-client.d.ts +7 -3
  144. package/dist/src/functions/wirings/cli/serialize-channel-cli-client.js +32 -18
  145. package/dist/src/functions/wirings/cli/serialize-channel-cli.js +63 -6
  146. package/dist/src/functions/wirings/console/serialize-console-functions.js +4 -7
  147. package/dist/src/functions/wirings/credentials/serialize-credentials-types.js +8 -1
  148. package/dist/src/functions/wirings/functions/serialize-function-types.js +88 -28
  149. package/dist/src/functions/wirings/middleware/serialize-middleware-imports.js +25 -9
  150. package/dist/src/functions/wirings/package/serialize-package-types.js +5 -3
  151. package/dist/src/functions/wirings/personas/pikku-command-personas.d.ts +7 -0
  152. package/dist/src/functions/wirings/personas/pikku-command-personas.js +43 -0
  153. package/dist/src/functions/wirings/personas/serialize-personas-types.d.ts +18 -0
  154. package/dist/src/functions/wirings/personas/serialize-personas-types.js +76 -0
  155. package/dist/src/functions/wirings/realtime/pikku-command-events-scaffold.js +2 -1
  156. package/dist/src/functions/wirings/roles/pikku-command-roles.d.ts +7 -0
  157. package/dist/src/functions/wirings/roles/pikku-command-roles.js +33 -0
  158. package/dist/src/functions/wirings/roles/serialize-roles-types.d.ts +14 -0
  159. package/dist/src/functions/wirings/roles/serialize-roles-types.js +55 -0
  160. package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.js +2 -1
  161. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.d.ts +3 -3
  162. package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.js +3 -3
  163. package/dist/src/functions/wirings/scopes/serialize-scopes-types.d.ts +1 -1
  164. package/dist/src/functions/wirings/scopes/serialize-scopes-types.js +1 -1
  165. package/dist/src/functions/wirings/secrets/serialize-secrets-types.js +10 -3
  166. package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.js +2 -1
  167. package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.js +2 -2
  168. package/dist/src/functions/wirings/variables/serialize-variables-types.js +8 -1
  169. package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.js +2 -1
  170. package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +13 -13
  171. package/dist/src/functions/wirings/workflow/serialize-personas.d.ts +10 -0
  172. package/dist/src/functions/wirings/workflow/serialize-personas.js +46 -0
  173. package/dist/src/functions/wirings/workflow/serialize-scenario-step-map.js +11 -2
  174. package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +165 -29
  175. package/dist/src/functions/workflows/all.workflow.js +7 -1
  176. package/dist/src/server/content-signing-jwt.d.ts +13 -0
  177. package/dist/src/server/content-signing-jwt.js +51 -0
  178. package/dist/src/server/dev-server-runner.interface.d.ts +10 -2
  179. package/dist/src/server/node-server-runner.d.ts +2 -2
  180. package/dist/src/server/node-server-runner.js +2 -1
  181. package/dist/src/services.d.ts +3 -1
  182. package/dist/src/services.js +2 -1
  183. package/dist/src/utils/pikku-cli-config.js +45 -2
  184. package/dist/src/utils/resolve-personas.d.ts +32 -0
  185. package/dist/src/utils/resolve-personas.js +46 -0
  186. package/dist/src/utils/resolve-scaffold-feature.d.ts +19 -0
  187. package/dist/src/utils/resolve-scaffold-feature.js +49 -0
  188. package/dist/tsconfig.tsbuildinfo +1 -1
  189. package/package.json +17 -13
  190. package/dist/.pikku/schemas/schemas/ScenarioRunInput.schema.json +0 -1
  191. package/dist/.pikku/workflow/pikku-scenario-actors.gen.d.ts +0 -19
  192. package/dist/.pikku/workflow/pikku-scenario-actors.gen.js +0 -17
  193. package/dist/src/functions/wirings/workflow/serialize-scenario-actors.d.ts +0 -2
  194. package/dist/src/functions/wirings/workflow/serialize-scenario-actors.js +0 -40
  195. package/dist/src/utils/resolve-scenario-actors.d.ts +0 -23
  196. package/dist/src/utils/resolve-scenario-actors.js +0 -74
@@ -110,7 +110,23 @@ function sanitizeAddonName(raw) {
110
110
  name = `x${name}`;
111
111
  return name || raw;
112
112
  }
113
- function getAddonFiles(vars, flags) {
113
+ /**
114
+ * `--display-name` and `--description` are free-form prose, so an apostrophe in
115
+ * them is ordinary — "Bob's CRM". `JSON.stringify` emits a valid, escaped
116
+ * string literal; interpolating the raw text into a quoted one terminates the
117
+ * string and the scaffolded file no longer parses. Prose composed with other
118
+ * words is stringified as a whole, not swapped in place.
119
+ */
120
+ const literal = (value) => JSON.stringify(value);
121
+ /**
122
+ * The same prose inside a generated template literal, where a backtick or a
123
+ * `${` opens an interpolation rather than ending the string — so the emitted
124
+ * message keeps its own `\${response.status}` holes.
125
+ */
126
+ const inTemplate = (value) => value.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
127
+ /** The same prose on a `//` comment line, where a newline ends the comment. */
128
+ const inComment = (value) => value.replace(/\s+/g, ' ').trim();
129
+ export function getAddonFiles(vars, flags) {
114
130
  const { name, camelName, pascalName, screamingName, displayName, description, category, } = vars;
115
131
  const files = {};
116
132
  // package.json
@@ -205,7 +221,7 @@ ${description}
205
221
  6. Build: \`yarn install && yarn pikku && yarn build\`
206
222
  `;
207
223
  // src/index.ts
208
- files['src/index.ts'] = `// ${displayName} functions
224
+ files['src/index.ts'] = `// ${inComment(displayName)} functions
209
225
  // export { ${camelName}Operation } from './functions/operation.function.js'
210
226
  `;
211
227
  // src/services.ts
@@ -224,10 +240,10 @@ export const createWireServices = pikkuAddonWireServices(
224
240
  }
225
241
  const cred = await wire.getCredential<{ token: string; expiresAt?: number }>('${camelName}')
226
242
  if (!cred?.token) {
227
- throw new UnauthorizedError('No ${displayName} session — sign in again')
243
+ throw new UnauthorizedError(${literal(`No ${displayName} session — sign in again`)})
228
244
  }
229
245
  if (cred.expiresAt && cred.expiresAt * 1000 < Date.now()) {
230
- throw new UnauthorizedError('${displayName} session expired — sign in again')
246
+ throw new UnauthorizedError(${literal(`${displayName} session expired — sign in again`)})
231
247
  }
232
248
  const ${camelName} = new ${pascalName}Service(cred, variables)
233
249
 
@@ -275,7 +291,7 @@ export const createWireServices = pikkuAddonWireServices(
275
291
  }
276
292
  const cred = await wire.getCredential<{ accessToken: string }>('${camelName}')
277
293
  if (!cred?.accessToken) {
278
- throw new UnauthorizedError('No ${displayName} connection — connect ${displayName} first')
294
+ throw new UnauthorizedError(${literal(`No ${displayName} connection — connect ${displayName} first`)})
279
295
  }
280
296
  const ${camelName} = new ${pascalName}Service(cred, variables)
281
297
 
@@ -363,7 +379,7 @@ export class ${pascalName}Service {
363
379
 
364
380
  if (!response.ok) {
365
381
  const errorText = await response.text()
366
- throw new Error(\`${displayName} API error (\${response.status}): \${errorText}\`)
382
+ throw new Error(\`${inTemplate(displayName)} API error (\${response.status}): \${errorText}\`)
367
383
  }
368
384
 
369
385
  return response.json() as Promise<T>
@@ -416,7 +432,7 @@ export class ${pascalName}Service {
416
432
 
417
433
  if (!response.ok) {
418
434
  const errorText = await response.text()
419
- throw new Error(\`${displayName} API error (\${response.status}): \${errorText}\`)
435
+ throw new Error(\`${inTemplate(displayName)} API error (\${response.status}): \${errorText}\`)
420
436
  }
421
437
 
422
438
  return response.json() as Promise<T>
@@ -464,7 +480,7 @@ export class ${pascalName}Service {
464
480
 
465
481
  if (!response.ok) {
466
482
  const errorText = await response.text()
467
- throw new Error(\`${displayName} API error (\${response.status}): \${errorText}\`)
483
+ throw new Error(\`${inTemplate(displayName)} API error (\${response.status}): \${errorText}\`)
468
484
  }
469
485
 
470
486
  return response.json() as Promise<T>
@@ -507,7 +523,7 @@ export class ${pascalName}Service {
507
523
 
508
524
  if (!response.ok) {
509
525
  const errorText = await response.text()
510
- throw new Error(\`${displayName} API error (\${response.status}): \${errorText}\`)
526
+ throw new Error(\`${inTemplate(displayName)} API error (\${response.status}): \${errorText}\`)
511
527
  }
512
528
 
513
529
  return response.json() as Promise<T>
@@ -550,16 +566,16 @@ export interface Services extends CoreServices<SingletonServices> {}
550
566
  // Conditional: credential / secret file
551
567
  if (flags.credential === 'apikey') {
552
568
  files[`src/${name}.credential.ts`] = `import { z } from 'zod'
553
- import { wireCredential } from '@pikku/core/credential'
569
+ import { defineCredential } from '@pikku/core/credential'
554
570
 
555
571
  export const ${camelName}CredentialSchema = z.object({
556
- apiKey: z.string().describe('${displayName} API key'),
572
+ apiKey: z.string().describe(${literal(`${displayName} API key`)}),
557
573
  })
558
574
 
559
- wireCredential({
575
+ defineCredential({
560
576
  name: '${camelName}',
561
- displayName: '${displayName}',
562
- description: '${description}',
577
+ displayName: ${literal(displayName)},
578
+ description: ${literal(description)},
563
579
  type: 'wire',
564
580
  schema: ${camelName}CredentialSchema,
565
581
  })
@@ -567,21 +583,21 @@ wireCredential({
567
583
  }
568
584
  else if (flags.credential === 'bearer') {
569
585
  const credentialFields = flags.delegated
570
- ? ` token: z.string().describe('${displayName} session token captured at delegated sign-in'),
586
+ ? ` token: z.string().describe(${literal(`${displayName} session token captured at delegated sign-in`)}),
571
587
  expiresAt: z.number().optional().describe('Token expiry (epoch seconds)'),
572
588
  tenantId: z.string().optional().describe('Upstream tenant id'),`
573
- : ` token: z.string().describe('${displayName} bearer token'),`;
589
+ : ` token: z.string().describe(${literal(`${displayName} bearer token`)}),`;
574
590
  files[`src/${name}.credential.ts`] = `import { z } from 'zod'
575
- import { wireCredential } from '@pikku/core/credential'
591
+ import { defineCredential } from '@pikku/core/credential'
576
592
 
577
593
  export const ${camelName}CredentialSchema = z.object({
578
594
  ${credentialFields}
579
595
  })
580
596
 
581
- wireCredential({
597
+ defineCredential({
582
598
  name: '${camelName}',
583
- displayName: '${displayName}',
584
- description: '${description}',
599
+ displayName: ${literal(displayName)},
600
+ description: ${literal(description)},
585
601
  type: 'wire',
586
602
  schema: ${camelName}CredentialSchema,
587
603
  })
@@ -589,18 +605,18 @@ wireCredential({
589
605
  }
590
606
  else if (flags.oauth || flags.credential === 'oauth2') {
591
607
  files[`src/${name}.credential.ts`] = `import { z } from 'zod'
592
- import { wireCredential } from '@pikku/core/credential'
593
- import { wireSecret } from '@pikku/core/secret'
608
+ import { defineCredential } from '@pikku/core/credential'
609
+ import { defineSecret } from '@pikku/core/secret'
594
610
 
595
611
  export const ${camelName}TokenSchema = z.object({
596
612
  accessToken: z.string(),
597
613
  refreshToken: z.string().optional(),
598
614
  })
599
615
 
600
- wireCredential({
616
+ defineCredential({
601
617
  name: '${camelName}',
602
- displayName: '${displayName}',
603
- description: '${description}',
618
+ displayName: ${literal(displayName)},
619
+ description: ${literal(description)},
604
620
  type: 'wire',
605
621
  schema: ${camelName}TokenSchema,
606
622
  oauth2: {
@@ -612,29 +628,29 @@ wireCredential({
612
628
  },
613
629
  })
614
630
 
615
- wireSecret({
631
+ defineSecret({
616
632
  name: '${camelName}OAuthApp',
617
- displayName: '${displayName} OAuth App',
618
- description: 'OAuth2 app credentials for ${displayName}',
633
+ displayName: ${literal(`${displayName} OAuth App`)},
634
+ description: ${literal(`OAuth2 app credentials for ${displayName}`)},
619
635
  secretId: '${screamingName}_OAUTH_APP',
620
636
  })
621
637
  `;
622
638
  }
623
639
  else if (flags.secret) {
624
640
  files[`src/${name}.secret.ts`] = `import { z } from 'zod'
625
- import { wireSecret } from '@pikku/core/secret'
641
+ import { defineSecret } from '@pikku/core/secret'
626
642
 
627
643
  export const ${camelName}SecretsSchema = z.object({
628
- apiKey: z.string().describe('${displayName} API key'),
644
+ apiKey: z.string().describe(${literal(`${displayName} API key`)}),
629
645
  // Add other secret fields as needed
630
646
  })
631
647
 
632
648
  export type ${pascalName}Secrets = z.infer<typeof ${camelName}SecretsSchema>
633
649
 
634
- wireSecret({
650
+ defineSecret({
635
651
  name: '${camelName}',
636
- displayName: '${displayName} API',
637
- description: '${description}',
652
+ displayName: ${literal(`${displayName} API`)},
653
+ description: ${literal(description)},
638
654
  secretId: '${screamingName}_CREDENTIALS',
639
655
  schema: ${camelName}SecretsSchema,
640
656
  })
@@ -643,13 +659,13 @@ wireSecret({
643
659
  // Conditional: variable file
644
660
  if (flags.variable) {
645
661
  files[`src/${name}.variable.ts`] = `import { z } from 'zod'
646
- import { wireVariable } from '@pikku/core/variable'
662
+ import { defineVariable } from '@pikku/core/variable'
647
663
 
648
664
  export const ${camelName}VariableSchema = z.string().optional().describe('TODO: describe this variable')
649
665
 
650
- wireVariable({
666
+ defineVariable({
651
667
  name: '${camelName}_variable',
652
- displayName: '${displayName} Variable',
668
+ displayName: ${literal(`${displayName} Variable`)},
653
669
  description: 'TODO: describe this variable',
654
670
  variableId: '${screamingName}_VARIABLE',
655
671
  schema: ${camelName}VariableSchema,
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Provision the declared personas into an environment: their accounts, and the
3
+ * roles they declare.
4
+ *
5
+ * This is deployment, not seeding. `pikku db seed` applies a project's test
6
+ * fixtures and does not run in production; an accountable persona doing a real
7
+ * job in production still needs an account and its grants, and this is where
8
+ * they come from.
9
+ *
10
+ * Additive in the same way `syncSystemRoles` is: it creates and grants, and
11
+ * never deletes or revokes. Removing a persona from the code leaves their
12
+ * account and their grants standing until somebody decides otherwise — which is
13
+ * the point, because the alternative is a rolling deploy quietly locking out the
14
+ * teammate that the older replica is still running.
15
+ *
16
+ * It needs both halves of an environment: its API, to sign the persona in, and
17
+ * its database, to write the grants. They must be the same deployment — the
18
+ * command has no way to check that, so pointing it at a stage's API and a local
19
+ * database provisions nobody usefully.
20
+ */
21
+ export declare const personaSync: import("#pikku").PikkuFunctionConfig<{
22
+ environment: string;
23
+ apiUrl?: string;
24
+ dryRun?: boolean;
25
+ }, void, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<{
26
+ environment: string;
27
+ apiUrl?: string;
28
+ dryRun?: boolean;
29
+ }, void, "rpc" | "session", import("#pikku").WiredServices> | import("#pikku").PikkuFunction<{
30
+ environment: string;
31
+ apiUrl?: string;
32
+ dryRun?: boolean;
33
+ }, void, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;
@@ -0,0 +1,151 @@
1
+ import { pikkuSessionlessFunc } from '#pikku';
2
+ import { createHttpPersonas } from '@pikku/core/persona';
3
+ import { personaEnvironmentRefusal } from '@pikku/core/persona';
4
+ import { resolvePersonas } from '../../utils/resolve-personas.js';
5
+ import { resolveEnvironment } from './environment.js';
6
+ import { loadDeclaredRoles, openScopeServiceForRoles } from './roles-shared.js';
7
+ /**
8
+ * The user id behind a persona's address.
9
+ *
10
+ * Read straight off better-auth's own `user` table rather than through the
11
+ * ScopeService, because the ScopeService has no notion of an address — its
12
+ * grants are keyed by user id, and `pikku_user_role.user_id` already FKs into
13
+ * this exact table. Looking it up anywhere else would mean inventing a second
14
+ * account concept for personas, which is the one thing this command exists to
15
+ * avoid: a persona is a user, provisioned the way users are.
16
+ */
17
+ const userIdByEmail = async (db, email) => {
18
+ const row = await db
19
+ .selectFrom('user')
20
+ .select('id')
21
+ .where('email', '=', email)
22
+ .executeTakeFirst();
23
+ return row ? String(row.id) : null;
24
+ };
25
+ /**
26
+ * Provision the declared personas into an environment: their accounts, and the
27
+ * roles they declare.
28
+ *
29
+ * This is deployment, not seeding. `pikku db seed` applies a project's test
30
+ * fixtures and does not run in production; an accountable persona doing a real
31
+ * job in production still needs an account and its grants, and this is where
32
+ * they come from.
33
+ *
34
+ * Additive in the same way `syncSystemRoles` is: it creates and grants, and
35
+ * never deletes or revokes. Removing a persona from the code leaves their
36
+ * account and their grants standing until somebody decides otherwise — which is
37
+ * the point, because the alternative is a rolling deploy quietly locking out the
38
+ * teammate that the older replica is still running.
39
+ *
40
+ * It needs both halves of an environment: its API, to sign the persona in, and
41
+ * its database, to write the grants. They must be the same deployment — the
42
+ * command has no way to check that, so pointing it at a stage's API and a local
43
+ * database provisions nobody usefully.
44
+ */
45
+ export const personaSync = pikkuSessionlessFunc({
46
+ remote: true,
47
+ func: async ({ logger, config, getInspectorState, variables }, { environment, apiUrl, dryRun }) => {
48
+ const state = await getInspectorState(true, false, false, true);
49
+ const personas = resolvePersonas(state.personas?.definitions ?? [], config.scenarios?.emailDomain);
50
+ if (Object.keys(personas).length === 0) {
51
+ logger.info('persona sync: no personas are declared — add a definePersonas({ … }) call.');
52
+ return;
53
+ }
54
+ const environments = config.environments ?? {};
55
+ const env = resolveEnvironment({ environment, environments, apiUrl });
56
+ // The same rule that decides who may *run* against an environment decides
57
+ // who may be provisioned into it. Two rules would drift, and the one that
58
+ // drifted would leave an account standing in production for a persona the
59
+ // engine then refuses to sign in.
60
+ //
61
+ // A `runnable: false` persona is still provisioned: their account is the
62
+ // whole reason they were declared, because other people act on it.
63
+ const eligible = [];
64
+ const skipped = [];
65
+ for (const [id, persona] of Object.entries(personas)) {
66
+ const refusal = personaEnvironmentRefusal(id, persona, environment, environments);
67
+ if (refusal) {
68
+ skipped.push(refusal);
69
+ continue;
70
+ }
71
+ eligible.push([id, persona]);
72
+ }
73
+ if (eligible.length === 0) {
74
+ logger.info(`persona sync: no persona may act in '${environment}' — ${skipped.length} declared persona(s) were all skipped.`);
75
+ logger.info(' Run with --dry-run for the reasons, or give the persona this environment and disposition `accountable` if it is production.');
76
+ return;
77
+ }
78
+ if (dryRun) {
79
+ logger.info(`persona sync (dry run): ${eligible.length} persona(s) would be provisioned into '${environment}' at ${env.apiUrl}:`);
80
+ for (const [id, persona] of eligible) {
81
+ logger.info(` ${id.padEnd(20)} ${persona.email}${persona.roles.length
82
+ ? ` -> ${persona.roles.join(' + ')}`
83
+ : ' (no roles)'}`);
84
+ }
85
+ for (const refusal of skipped) {
86
+ logger.info(` skipped: ${refusal}`);
87
+ }
88
+ logger.info('Re-run without --dry-run to apply.');
89
+ return;
90
+ }
91
+ const secret = await variables.get('SCENARIO_ACTOR_SECRET');
92
+ if (!secret) {
93
+ throw new Error('SCENARIO_ACTOR_SECRET is not set — a persona account cannot be created. ' +
94
+ 'Export it in the environment running this command (never put it in pikku.config.json).');
95
+ }
96
+ const declaredRoles = await loadDeclaredRoles(config.rolesMetaJsonFile, logger);
97
+ if (!declaredRoles) {
98
+ throw new Error('role metadata not found');
99
+ }
100
+ const signedIn = createHttpPersonas({
101
+ apiUrl: env.apiUrl,
102
+ secret,
103
+ personas: Object.fromEntries(eligible),
104
+ signInPath: env.signInPath,
105
+ rpcPath: env.rpcPath,
106
+ });
107
+ // Roles are synced on the way in, so a grant can name a role this deploy
108
+ // introduced. Both that sync and everything below are additive.
109
+ const opened = await openScopeServiceForRoles({ config, logger }, declaredRoles, 'pikku persona sync');
110
+ if (!opened) {
111
+ return;
112
+ }
113
+ try {
114
+ let accounts = 0;
115
+ let granted = 0;
116
+ let held = 0;
117
+ for (const [id, persona] of eligible) {
118
+ // Signing in is what creates the account: the actor plugin upserts an
119
+ // `actor: true` user row on first sign-in, and nothing else does.
120
+ // `sessionRoles()` is the public call that forces it — its answer is a
121
+ // by-product here, not the reason for the call.
122
+ await signedIn[id].sessionRoles();
123
+ accounts++;
124
+ if (persona.roles.length === 0) {
125
+ logger.info(` ${id.padEnd(20)} ${persona.email} (no roles)`);
126
+ continue;
127
+ }
128
+ const userId = await userIdByEmail(opened.db, persona.email);
129
+ if (!userId) {
130
+ throw new Error(`persona sync: signed '${id}' in at ${env.apiUrl}, but no user row for ${persona.email} exists in the database this command opened. ` +
131
+ `That database is not the one '${environment}' runs on, so its grants would land nowhere.`);
132
+ }
133
+ const already = new Set(await opened.service.listUserRoles(userId));
134
+ const missing = persona.roles.filter((role) => !already.has(role));
135
+ for (const role of missing) {
136
+ await opened.service.addUserToRole(userId, role);
137
+ }
138
+ granted += missing.length;
139
+ held += persona.roles.length - missing.length;
140
+ logger.info(` ${id.padEnd(20)} ${persona.email} -> ${persona.roles.join(' + ')}${missing.length === 0 ? ' (already held)' : ''}`);
141
+ }
142
+ logger.info(`persona sync: ${accounts} account(s) in '${environment}', ${granted} role grant(s) applied, ${held} already held`);
143
+ if (skipped.length > 0) {
144
+ logger.info(` ${skipped.length} persona(s) skipped — they do not act in '${environment}'. Run with --dry-run for the reasons.`);
145
+ }
146
+ }
147
+ finally {
148
+ await opened.destroy();
149
+ }
150
+ },
151
+ });
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Run one declared persona against a real stage as a virtual user.
3
+ *
4
+ * Everything it needs is already in the project: the catalogue is the function
5
+ * meta, the intents are the scenarios' own prose, the identity is the persona
6
+ * signing in over the app's real auth, and the scopes come from the roles they
7
+ * declared. The only new input is which person to be — everything else is a
8
+ * flag for narrowing or replaying a run.
9
+ *
10
+ * This is deliberately not a test runner. It asserts nothing, and a green run
11
+ * proves nothing — what it produces is findings, and the absence of them is
12
+ * only ever "not this time, not with this seed".
13
+ */
14
+ export declare const personaRun: import("#pikku").PikkuFunctionConfig<{
15
+ environment: string;
16
+ persona: string;
17
+ disposition?: string;
18
+ goals?: string;
19
+ steps?: string;
20
+ mutations?: string;
21
+ duration?: string;
22
+ seed?: string;
23
+ model?: string;
24
+ apiUrl?: string;
25
+ allowApproval?: boolean;
26
+ skipRoleCheck?: boolean;
27
+ out?: string;
28
+ }, void, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<{
29
+ environment: string;
30
+ persona: string;
31
+ disposition?: string;
32
+ goals?: string;
33
+ steps?: string;
34
+ mutations?: string;
35
+ duration?: string;
36
+ seed?: string;
37
+ model?: string;
38
+ apiUrl?: string;
39
+ allowApproval?: boolean;
40
+ skipRoleCheck?: boolean;
41
+ out?: string;
42
+ }, void, "rpc" | "session", import("#pikku").WiredServices> | import("#pikku").PikkuFunction<{
43
+ environment: string;
44
+ persona: string;
45
+ disposition?: string;
46
+ goals?: string;
47
+ steps?: string;
48
+ mutations?: string;
49
+ duration?: string;
50
+ seed?: string;
51
+ model?: string;
52
+ apiUrl?: string;
53
+ allowApproval?: boolean;
54
+ skipRoleCheck?: boolean;
55
+ out?: string;
56
+ }, void, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;
57
+ /** Who this app is for, and what each of them wants, without running anything. */
58
+ export declare const personaList: import("#pikku").PikkuFunctionConfig<void, void, "rpc" | "session", import("#pikku").PikkuFunctionSessionless<void, void, "rpc" | "session", import("#pikku").WiredServices> | import("#pikku").PikkuFunction<void, void, "rpc" | "session", import("#pikku").WiredServices>, undefined, undefined>;