@pikku/cli 0.12.85 → 0.12.86

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 (124) hide show
  1. package/console-app/assets/{index-CEv-kj3J.js → index-Cr3_cO3Z.js} +161 -147
  2. package/console-app/index.html +1 -1
  3. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  4. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  5. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  6. package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
  7. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  8. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  9. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  10. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  11. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +4 -0
  12. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  13. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  14. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  15. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +4 -0
  16. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  17. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  18. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  19. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  20. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  21. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  22. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  23. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  24. package/dist/.pikku/function/pikku-functions-meta.gen.json +39 -15
  25. package/dist/.pikku/function/pikku-functions.gen.js +3 -1
  26. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  27. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  28. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  29. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  30. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  31. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  32. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  33. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  34. package/dist/.pikku/pikku-services.gen.d.ts +2 -1
  35. package/dist/.pikku/pikku-services.gen.js +1 -0
  36. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  37. package/dist/.pikku/pikku-types.gen.js +1 -1
  38. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  39. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  40. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  41. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -0
  42. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  43. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  44. package/dist/.pikku/schemas/register.gen.js +3 -1
  45. package/dist/.pikku/schemas/schemas/FabricLoginInput.schema.json +1 -1
  46. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  47. package/dist/.pikku/schemas/schemas/PikkuUserAdminFunctionsOutput.schema.json +1 -0
  48. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  49. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  50. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  51. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  52. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  53. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  54. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  55. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  56. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  57. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  58. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  59. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  60. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  61. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +8 -2
  62. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  63. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  64. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  65. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  66. package/dist/bin/pikku-bin.mjs +2 -2
  67. package/dist/src/fabric/fabric-commands.d.ts +6 -3
  68. package/dist/src/fabric/fabric-commands.js +4 -0
  69. package/dist/src/fabric/functions/llm-key.function.d.ts +4 -4
  70. package/dist/src/fabric/functions/login.function.d.ts +4 -0
  71. package/dist/src/fabric/functions/login.function.js +45 -4
  72. package/dist/src/functions/wirings/ai-agent/pikku-command-public-agent.js +6 -2
  73. package/dist/src/functions/wirings/ai-agent/serialize-public-agent.d.ts +5 -1
  74. package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +81 -48
  75. package/dist/src/functions/wirings/auth/pikku-command-auth.js +4 -0
  76. package/dist/src/functions/wirings/console/pikku-command-console-functions.js +6 -2
  77. package/dist/src/functions/wirings/console/serialize-console-functions.d.ts +5 -1
  78. package/dist/src/functions/wirings/console/serialize-console-functions.js +55 -23
  79. package/dist/src/functions/wirings/functions/schemas.js +10 -1
  80. package/dist/src/functions/wirings/graph/pikku-command-graph-wirings.js +2 -0
  81. package/dist/src/functions/wirings/realtime/pikku-command-events-scaffold.js +8 -2
  82. package/dist/src/functions/wirings/realtime/serialize-events-scaffold.d.ts +5 -4
  83. package/dist/src/functions/wirings/realtime/serialize-events-scaffold.js +17 -7
  84. package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.js +6 -2
  85. package/dist/src/functions/wirings/rpc/pikku-command-remote-rpc.js +6 -2
  86. package/dist/src/functions/wirings/rpc/serialize-public-rpc.d.ts +5 -1
  87. package/dist/src/functions/wirings/rpc/serialize-public-rpc.js +16 -5
  88. package/dist/src/functions/wirings/rpc/serialize-remote-rpc.d.ts +5 -1
  89. package/dist/src/functions/wirings/rpc/serialize-remote-rpc.js +16 -5
  90. package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.js +8 -2
  91. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.d.ts +5 -1
  92. package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.js +22 -16
  93. package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.d.ts +1 -0
  94. package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.js +49 -0
  95. package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.d.ts +24 -0
  96. package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.js +283 -0
  97. package/dist/src/functions/wirings/webhook/pikku-command-webhook.js +6 -2
  98. package/dist/src/functions/wirings/webhook/serialize-webhook.d.ts +5 -8
  99. package/dist/src/functions/wirings/webhook/serialize-webhook.js +19 -12
  100. package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.js +8 -2
  101. package/dist/src/functions/wirings/workflow/serialize-workflow-routes.d.ts +5 -1
  102. package/dist/src/functions/wirings/workflow/serialize-workflow-routes.js +61 -31
  103. package/dist/src/functions/workflows/all.workflow.js +8 -0
  104. package/dist/src/utils/pikku-cli-config.js +40 -10
  105. package/dist/src/utils/remove-legacy-scaffold-file.d.ts +3 -0
  106. package/dist/src/utils/remove-legacy-scaffold-file.js +43 -0
  107. package/dist/src/utils/serialize-schemas.js +57 -2
  108. package/dist/tsconfig.tsbuildinfo +1 -1
  109. package/package.json +5 -5
  110. package/skills/pikku-addon/SKILL.md +1 -0
  111. package/skills/pikku-ai-agent/SKILL.md +2 -1
  112. package/skills/pikku-ai-vercel/SKILL.md +1 -0
  113. package/skills/pikku-better-auth/SKILL.md +57 -0
  114. package/skills/pikku-concepts/SKILL.md +1 -1
  115. package/skills/pikku-concepts/references/concept-mapping.md +11 -9
  116. package/skills/pikku-emails/SKILL.md +1 -0
  117. package/skills/pikku-fabric-debug/SKILL.md +112 -0
  118. package/skills/pikku-permissions/SKILL.md +8 -8
  119. package/skills/pikku-react/SKILL.md +32 -3
  120. package/skills/pikku-react-query/SKILL.md +38 -1
  121. package/skills/pikku-realtime/SKILL.md +1 -1
  122. package/skills/pikku-security/SKILL.md +3 -3
  123. package/skills/pikku-template-clone/SKILL.md +1 -0
  124. package/cli.schema.json +0 -1
@@ -1,28 +1,69 @@
1
1
  export const serializePublicAgent = (pathToPikkuTypes, requireAuth = true, globalHTTPPrefix = '') => {
2
2
  const authFlag = requireAuth ? 'true' : 'false';
3
- // Emitted inline at each call site rather than as a shared named alias: the
4
- // schema extractor only reads type literals in the generic position, and
5
- // synthesises the schema name from the function name. Behind a named alias it
6
- // records an `inputSchemaName` with no schema behind it, and every agent call
7
- // then fails at runtime with MissingSchemaError.
8
- const callerInput = `{
9
- agentName: string
10
- message: string
11
- threadId: string
12
- resourceId: string
13
- attachments?: {
14
- type: 'image' | 'file'
15
- data?: string
16
- url?: string
17
- mediaType?: string
18
- filename?: string
19
- }[]
20
- model?: string
21
- temperature?: number
22
- context?: string
23
- }`;
24
- return `import { canAccessThread, threadOwnerConstraint } from '@pikku/core/ai-agent'
3
+ const schemas = `/**
4
+ * Auto-generated public agent schemas
5
+ * Do not edit manually - regenerate with 'npx pikku'
6
+ */
7
+ import { z } from 'zod'
8
+
9
+ export const Attachment = z.object({
10
+ type: z.enum(['image', 'file']),
11
+ data: z.string().optional(),
12
+ url: z.string().optional(),
13
+ mediaType: z.string().optional(),
14
+ filename: z.string().optional(),
15
+ })
16
+
17
+ export const AgentCall = z.object({
18
+ agentName: z.string(),
19
+ message: z.string(),
20
+ threadId: z.string(),
21
+ resourceId: z.string(),
22
+ attachments: z.array(Attachment).optional(),
23
+ model: z.string().optional(),
24
+ temperature: z.number().optional(),
25
+ context: z.string().optional(),
26
+ })
27
+
28
+ export const AgentApproval = z.object({
29
+ agentName: z.string(),
30
+ runId: z.string(),
31
+ approvals: z.array(
32
+ z.object({ toolCallId: z.string(), approved: z.boolean() })
33
+ ),
34
+ })
35
+
36
+ export const AgentResume = z.object({
37
+ agentName: z.string(),
38
+ runId: z.string(),
39
+ toolCallId: z.string(),
40
+ approved: z.boolean(),
41
+ })
42
+
43
+ export const ThreadsQuery = z.object({
44
+ agentName: z.string().optional(),
45
+ resourceId: z.string().optional(),
46
+ limit: z.number().optional(),
47
+ offset: z.number().optional(),
48
+ })
49
+
50
+ export const ThreadRef = z.object({
51
+ threadId: z.string(),
52
+ resourceId: z.string().optional(),
53
+ })
54
+
55
+ export const ThreadDeleted = z.object({ deleted: z.boolean() })
56
+ `;
57
+ const functions = `import { canAccessThread, threadOwnerConstraint } from '@pikku/core/ai-agent'
25
58
  import { pikkuSessionlessFunc, pikkuPermission, defineHTTPRoutes, wireHTTPRoutes } from '${pathToPikkuTypes}'
59
+ import {
60
+ AgentCall,
61
+ AgentApproval,
62
+ AgentResume,
63
+ ThreadsQuery,
64
+ ThreadRef,
65
+ ThreadDeleted,
66
+ } from './agent.schemas.gen.js'
26
67
 
27
68
  /**
28
69
  * Thread reads are keyed by a caller-supplied \`threadId\`, so ownership cannot
@@ -39,9 +80,10 @@ export const isThreadOwner = pikkuPermission<{ threadId: string }>(
39
80
  }
40
81
  )
41
82
 
42
- export const agentCaller = pikkuSessionlessFunc<${callerInput}, unknown>({
83
+ export const agentCaller = pikkuSessionlessFunc({
43
84
  tags: ['pikku'],
44
85
  auth: ${authFlag},
86
+ input: AgentCall,
45
87
  func: async (_services, data, { rpc }) => {
46
88
  return await rpc.agent.run(data.agentName as any, {
47
89
  message: data.message,
@@ -55,9 +97,10 @@ export const agentCaller = pikkuSessionlessFunc<${callerInput}, unknown>({
55
97
  },
56
98
  })
57
99
 
58
- export const agentStreamCaller = pikkuSessionlessFunc<${callerInput}, void>({
100
+ export const agentStreamCaller = pikkuSessionlessFunc({
59
101
  tags: ['pikku'],
60
102
  auth: ${authFlag},
103
+ input: AgentCall,
61
104
  func: async (_services, data, { rpc }) => {
62
105
  await rpc.agent.stream(data.agentName as any, {
63
106
  message: data.message,
@@ -71,23 +114,19 @@ export const agentStreamCaller = pikkuSessionlessFunc<${callerInput}, void>({
71
114
  },
72
115
  })
73
116
 
74
- export const agentApproveCaller = pikkuSessionlessFunc<
75
- { agentName: string; runId: string; approvals: { toolCallId: string; approved: boolean }[] },
76
- unknown
77
- >({
117
+ export const agentApproveCaller = pikkuSessionlessFunc({
78
118
  tags: ['pikku'],
79
119
  auth: ${authFlag},
120
+ input: AgentApproval,
80
121
  func: async (_services, { runId, approvals, agentName }, { rpc }) => {
81
122
  return await rpc.agent.approve(runId, approvals, agentName)
82
123
  },
83
124
  })
84
125
 
85
- export const agentResumeCaller = pikkuSessionlessFunc<
86
- { agentName: string; runId: string; toolCallId: string; approved: boolean },
87
- void
88
- >({
126
+ export const agentResumeCaller = pikkuSessionlessFunc({
89
127
  tags: ['pikku'],
90
128
  auth: ${authFlag},
129
+ input: AgentResume,
91
130
  func: async (_services, data, { rpc }) => {
92
131
  await rpc.agent.resume(data.runId, {
93
132
  toolCallId: data.toolCallId,
@@ -96,11 +135,9 @@ export const agentResumeCaller = pikkuSessionlessFunc<
96
135
  },
97
136
  })
98
137
 
99
- export const getAgentThreads = pikkuSessionlessFunc<
100
- { agentName?: string; resourceId?: string; limit?: number; offset?: number },
101
- any[]
102
- >({
138
+ export const getAgentThreads = pikkuSessionlessFunc({
103
139
  tags: ['pikku', 'pikku:agent'],
140
+ input: ThreadsQuery,
104
141
  title: 'Get Agent Threads',
105
142
  description:
106
143
  'Returns the caller\\'s AI agent threads from storage. Accepts optional filters: agentName, resourceId, limit, and offset for pagination.',
@@ -119,11 +156,9 @@ export const getAgentThreads = pikkuSessionlessFunc<
119
156
  },
120
157
  })
121
158
 
122
- export const getAgentThreadMessages = pikkuSessionlessFunc<
123
- { threadId: string; resourceId?: string },
124
- any[]
125
- >({
159
+ export const getAgentThreadMessages = pikkuSessionlessFunc({
126
160
  tags: ['pikku', 'pikku:agent'],
161
+ input: ThreadRef,
127
162
  title: 'Get Agent Thread Messages',
128
163
  description:
129
164
  'Returns all messages for a given AI agent thread, ordered by creation time.',
@@ -135,11 +170,9 @@ export const getAgentThreadMessages = pikkuSessionlessFunc<
135
170
  },
136
171
  })
137
172
 
138
- export const getAgentThreadRuns = pikkuSessionlessFunc<
139
- { threadId: string; resourceId?: string },
140
- any[]
141
- >({
173
+ export const getAgentThreadRuns = pikkuSessionlessFunc({
142
174
  tags: ['pikku', 'pikku:agent'],
175
+ input: ThreadRef,
143
176
  title: 'Get Agent Thread Runs',
144
177
  description:
145
178
  'Returns the run history for a given AI agent thread, ordered by creation time.',
@@ -151,11 +184,10 @@ export const getAgentThreadRuns = pikkuSessionlessFunc<
151
184
  },
152
185
  })
153
186
 
154
- export const deleteAgentThread = pikkuSessionlessFunc<
155
- { threadId: string; resourceId?: string },
156
- { deleted: boolean }
157
- >({
187
+ export const deleteAgentThread = pikkuSessionlessFunc({
158
188
  tags: ['pikku', 'pikku:agent'],
189
+ input: ThreadRef,
190
+ output: ThreadDeleted,
159
191
  title: 'Delete Agent Thread',
160
192
  description:
161
193
  'Deletes an AI agent thread and all of its persisted state.',
@@ -199,4 +231,5 @@ export const agentRoutes = defineHTTPRoutes({
199
231
 
200
232
  wireHTTPRoutes({ routes: { agent: agentRoutes } })
201
233
  `;
234
+ return { schemas, functions };
202
235
  };
@@ -4,6 +4,7 @@ import { rm } from 'node:fs/promises';
4
4
  import { pikkuSessionlessFunc } from '#pikku';
5
5
  import { writeFileInDir } from '../../../utils/file-writer.js';
6
6
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
7
+ import { removeLegacyScaffoldFile } from '../../../utils/remove-legacy-scaffold-file.js';
7
8
  import { serializeAuthGen } from './serialize-auth-gen.js';
8
9
  import { serializeAuthTypes, serializeAuthTypesBootstrap, } from './serialize-auth-types.js';
9
10
  import { serializeAuthMeta } from './serialize-auth-meta.js';
@@ -49,6 +50,9 @@ export const pikkuAuth = pikkuSessionlessFunc({
49
50
  else if (existsSync(middlewareFile)) {
50
51
  await rm(middlewareFile, { force: true });
51
52
  }
53
+ for (const file of [authFile, secretsFile, middlewareFile]) {
54
+ await removeLegacyScaffoldFile(file);
55
+ }
52
56
  // Static metadata of the enabled providers/plugins for the console SSO page,
53
57
  // following the `*-meta.gen.json` convention. Read at runtime by the console
54
58
  // getAuthProviders function instead of a runtime registry.
@@ -3,6 +3,7 @@ import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
3
3
  import { writeFileInDir } from '../../../utils/file-writer.js';
4
4
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
5
5
  import { projectDeclaresBetterAuth } from '../../../utils/detect-better-auth.js';
6
+ import { removeLegacyScaffoldFile } from '../../../utils/remove-legacy-scaffold-file.js';
6
7
  import { serializeConsoleFunctions } from './serialize-console-functions.js';
7
8
  export const pikkuConsoleFunctions = pikkuSessionlessFunc({
8
9
  func: async ({ logger, config, variables }) => {
@@ -10,7 +11,7 @@ export const pikkuConsoleFunctions = pikkuSessionlessFunc({
10
11
  if (deployCodegenFlag === '1') {
11
12
  return false;
12
13
  }
13
- if (config.scaffold?.console) {
14
+ if (config.scaffold?.console && config.consoleSchemasFile) {
14
15
  // Every console RPC now requires an authenticated session (the console is
15
16
  // an admin surface), so scaffolding it without an auth strategy produces a
16
17
  // console that 403s on every call. Better Auth is the supported strategy —
@@ -25,7 +26,10 @@ export const pikkuConsoleFunctions = pikkuSessionlessFunc({
25
26
  }
26
27
  const pathToPikkuTypes = getFileImportRelativePath(config.consoleFunctionsFile, config.typesDeclarationFile, config.packageMappings);
27
28
  const pathToAgentTypes = getFileImportRelativePath(config.consoleFunctionsFile, config.agentTypesFile, config.packageMappings);
28
- await writeFileInDir(logger, config.consoleFunctionsFile, serializeConsoleFunctions(pathToPikkuTypes, pathToAgentTypes, config.globalHTTPPrefix || ''));
29
+ const { schemas, functions } = serializeConsoleFunctions(pathToPikkuTypes, pathToAgentTypes, config.globalHTTPPrefix || '');
30
+ await writeFileInDir(logger, config.consoleSchemasFile, schemas);
31
+ await writeFileInDir(logger, config.consoleFunctionsFile, functions);
32
+ await removeLegacyScaffoldFile(config.consoleFunctionsFile);
29
33
  return true;
30
34
  }
31
35
  return false;
@@ -1 +1,5 @@
1
- export declare const serializeConsoleFunctions: (pathToPikkuTypes: string, _pathToAgentTypes: string, globalHTTPPrefix?: string) => string;
1
+ export interface ConsoleGenOutput {
2
+ schemas: string;
3
+ functions: string;
4
+ }
5
+ export declare const serializeConsoleFunctions: (pathToPikkuTypes: string, _pathToAgentTypes: string, globalHTTPPrefix?: string) => ConsoleGenOutput;
@@ -1,28 +1,62 @@
1
1
  export const serializeConsoleFunctions = (pathToPikkuTypes, _pathToAgentTypes, globalHTTPPrefix = '') => {
2
- return `import { pikkuFunc, defineHTTPRoutes, wireHTTPRoutes, ref, wireAddon } from '${pathToPikkuTypes}'
2
+ const schemas = `/**
3
+ * Auto-generated console function schemas
4
+ * Do not edit manually - regenerate with 'npx pikku'
5
+ */
6
+ import { z } from 'zod'
3
7
 
4
- export const pikkuConsoleSetSecret = pikkuFunc<{
5
- secretId: string
6
- value: unknown
7
- }, {
8
- success: boolean
9
- }>({
8
+ export const SecretRef = z.object({ secretId: z.string() })
9
+
10
+ export const SetSecret = z.object({
11
+ secretId: z.string(),
12
+ value: z.unknown(),
13
+ })
14
+
15
+ export const VariableRef = z.object({ variableId: z.string() })
16
+
17
+ export const SetVariable = z.object({
18
+ variableId: z.string(),
19
+ value: z.unknown(),
20
+ })
21
+
22
+ export const ValueResult = z.object({
23
+ exists: z.boolean(),
24
+ value: z.unknown().nullable(),
25
+ })
26
+
27
+ export const Exists = z.object({ exists: z.boolean() })
28
+
29
+ export const Success = z.object({ success: z.boolean() })
30
+ `;
31
+ const functions = `import { pikkuFunc, defineHTTPRoutes, wireHTTPRoutes, ref, wireAddon } from '${pathToPikkuTypes}'
32
+ import {
33
+ SecretRef,
34
+ SetSecret,
35
+ VariableRef,
36
+ SetVariable,
37
+ ValueResult,
38
+ Exists,
39
+ Success,
40
+ } from './console.schemas.gen.js'
41
+
42
+ export const pikkuConsoleSetSecret = pikkuFunc({
10
43
  tags: ['pikku'],
11
44
  description: 'Set the value of a secret',
12
45
  expose: true,
46
+ input: SetSecret,
47
+ output: Success,
13
48
  func: async ({ secrets }, { secretId, value }) => {
14
49
  await secrets.setSecret(secretId, value)
15
50
  return { success: true }
16
51
  },
17
52
  })
18
53
 
19
- export const pikkuConsoleGetVariable = pikkuFunc<
20
- { variableId: string },
21
- { exists: boolean; value: unknown | null }
22
- >({
54
+ export const pikkuConsoleGetVariable = pikkuFunc({
23
55
  tags: ['pikku'],
24
56
  description: 'Get the current value of a variable',
25
57
  expose: true,
58
+ input: VariableRef,
59
+ output: ValueResult,
26
60
  func: async ({ variables }, { variableId }) => {
27
61
  const exists = await variables.has(variableId)
28
62
  if (!exists) {
@@ -38,13 +72,12 @@ export const pikkuConsoleGetVariable = pikkuFunc<
38
72
  },
39
73
  })
40
74
 
41
- export const pikkuConsoleSetVariable = pikkuFunc<
42
- { variableId: string; value: unknown },
43
- { success: boolean }
44
- >({
75
+ export const pikkuConsoleSetVariable = pikkuFunc({
45
76
  tags: ['pikku'],
46
77
  description: 'Set the value of a variable',
47
78
  expose: true,
79
+ input: SetVariable,
80
+ output: Success,
48
81
  func: async ({ variables }, { variableId, value }) => {
49
82
  if (typeof value === 'string') {
50
83
  await variables.set(variableId, value)
@@ -55,26 +88,24 @@ export const pikkuConsoleSetVariable = pikkuFunc<
55
88
  },
56
89
  })
57
90
 
58
- export const pikkuConsoleHasSecret = pikkuFunc<
59
- { secretId: string },
60
- { exists: boolean }
61
- >({
91
+ export const pikkuConsoleHasSecret = pikkuFunc({
62
92
  tags: ['pikku'],
63
93
  description: 'Check if a secret exists without reading its value',
64
94
  expose: true,
95
+ input: SecretRef,
96
+ output: Exists,
65
97
  func: async ({ secrets }, { secretId }) => {
66
98
  const exists = await secrets.hasSecret(secretId)
67
99
  return { exists }
68
100
  },
69
101
  })
70
102
 
71
- export const pikkuConsoleGetSecret = pikkuFunc<
72
- { secretId: string },
73
- { exists: boolean; value: unknown | null }
74
- >({
103
+ export const pikkuConsoleGetSecret = pikkuFunc({
75
104
  tags: ['pikku'],
76
105
  description: 'Get the current value of a secret',
77
106
  expose: true,
107
+ input: SecretRef,
108
+ output: ValueResult,
78
109
  func: async ({ secrets }, { secretId }) => {
79
110
  const exists = await secrets.hasSecret(secretId)
80
111
  if (!exists) {
@@ -107,4 +138,5 @@ export const consoleRoutes = defineHTTPRoutes({
107
138
  wireAddon({ name: 'console', package: '@pikku/addon-console' })
108
139
  wireHTTPRoutes({ basePath: '${globalHTTPPrefix}', routes: { console: consoleRoutes } })
109
140
  `;
141
+ return { schemas, functions };
110
142
  };
@@ -4,7 +4,16 @@ import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-
4
4
  export const pikkuSchemas = pikkuSessionlessFunc({
5
5
  func: async ({ logger, config, getInspectorState }) => {
6
6
  const visitState = await getInspectorState();
7
- if (Object.keys(visitState.schemas).length === 0) {
7
+ // Bodies missing while functions still DECLARE schemas means the inspection pass
8
+ // was partial. Writing from it would empty register.gen.ts and delete the files
9
+ // behind schemas that are still required — unregistering the whole app off a bad
10
+ // read. Leave the previous output alone.
11
+ //
12
+ // Zero REQUIRED schemas is a different thing entirely and must still be written:
13
+ // that is a project whose last schema was removed, and it is the case where the
14
+ // files left by earlier runs have to be cleared.
15
+ if (visitState.requiredSchemas.size > 0 &&
16
+ Object.keys(visitState.schemas).length === 0) {
8
17
  return undefined;
9
18
  }
10
19
  await saveSchemas(logger, config.schemaDirectory, visitState.schemas, visitState.requiredSchemas, config.schema?.supportsImportAttributes ?? true, config.addonName || null);
@@ -2,12 +2,14 @@ import { pikkuSessionlessFunc } from '#pikku';
2
2
  import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
3
3
  import { writeFileInDir } from '../../../utils/file-writer.js';
4
4
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
5
+ import { removeLegacyScaffoldFile } from '../../../utils/remove-legacy-scaffold-file.js';
5
6
  import { serializeGraphWirings } from './serialize-graph-wirings.js';
6
7
  export const pikkuGraphWirings = pikkuSessionlessFunc({
7
8
  func: async ({ logger, config }) => {
8
9
  if (config.scaffold?.graph && config.graphWiringsFile) {
9
10
  const pathToPikkuTypes = getFileImportRelativePath(config.graphWiringsFile, config.typesDeclarationFile, config.packageMappings);
10
11
  await writeFileInDir(logger, config.graphWiringsFile, serializeGraphWirings(pathToPikkuTypes));
12
+ await removeLegacyScaffoldFile(config.graphWiringsFile);
11
13
  return true;
12
14
  }
13
15
  return false;
@@ -2,10 +2,13 @@ import { pikkuSessionlessFunc } from '#pikku';
2
2
  import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
3
3
  import { writeFileInDir } from '../../../utils/file-writer.js';
4
4
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
5
+ import { removeLegacyScaffoldFile } from '../../../utils/remove-legacy-scaffold-file.js';
5
6
  import { serializeEventsScaffold } from './serialize-events-scaffold.js';
6
7
  export const pikkuEventsScaffold = pikkuSessionlessFunc({
7
8
  func: async ({ logger, config }) => {
8
- if (!config.scaffold?.events || !config.eventsChannelFile) {
9
+ if (!config.scaffold?.events ||
10
+ !config.eventsChannelFile ||
11
+ !config.eventsSchemasFile) {
9
12
  logger.debug({
10
13
  message: 'Skipping events scaffold (set scaffold.events in pikku.config.json to enable).',
11
14
  type: 'skip',
@@ -14,7 +17,10 @@ export const pikkuEventsScaffold = pikkuSessionlessFunc({
14
17
  }
15
18
  const authRequired = config.scaffold.events === 'auth';
16
19
  const pikkuTypesImportPath = getFileImportRelativePath(config.eventsChannelFile, config.typesDeclarationFile, config.packageMappings);
17
- await writeFileInDir(logger, config.eventsChannelFile, serializeEventsScaffold(authRequired, pikkuTypesImportPath));
20
+ const { schemas, functions } = serializeEventsScaffold(authRequired, pikkuTypesImportPath);
21
+ await writeFileInDir(logger, config.eventsSchemasFile, schemas);
22
+ await writeFileInDir(logger, config.eventsChannelFile, functions);
23
+ await removeLegacyScaffoldFile(config.eventsChannelFile);
18
24
  return true;
19
25
  },
20
26
  middleware: [
@@ -1,3 +1,7 @@
1
+ export interface EventsGenOutput {
2
+ schemas: string;
3
+ functions: string;
4
+ }
1
5
  /**
2
6
  * Generates the server-side `events.gen.ts` scaffold.
3
7
  *
@@ -16,8 +20,5 @@
16
20
  * Both routes call `eventHub.subscribe`/`unsubscribe` with the channel id;
17
21
  * publish-side ergonomics (envelope shape) are documented in the
18
22
  * pikku-realtime skill.
19
- *
20
- * Uses TypeScript generics (not named Zod schema constants) so schemas and
21
- * wiring calls can safely coexist in a single generated file.
22
23
  */
23
- export declare const serializeEventsScaffold: (authRequired: boolean, pikkuTypesImportPath: string) => string;
24
+ export declare const serializeEventsScaffold: (authRequired: boolean, pikkuTypesImportPath: string) => EventsGenOutput;
@@ -16,19 +16,25 @@
16
16
  * Both routes call `eventHub.subscribe`/`unsubscribe` with the channel id;
17
17
  * publish-side ergonomics (envelope shape) are documented in the
18
18
  * pikku-realtime skill.
19
- *
20
- * Uses TypeScript generics (not named Zod schema constants) so schemas and
21
- * wiring calls can safely coexist in a single generated file.
22
19
  */
23
20
  export const serializeEventsScaffold = (authRequired, pikkuTypesImportPath) => {
24
21
  const auth = authRequired ? 'true' : 'false';
25
- return `import {
22
+ const schemas = `/**
23
+ * Auto-generated realtime event schemas
24
+ * Do not edit manually - regenerate with 'npx pikku'
25
+ */
26
+ import { z } from 'zod'
27
+
28
+ export const TopicRef = z.object({ topic: z.string() })
29
+ `;
30
+ const functions = `import {
26
31
  pikkuChannelFunc,
27
32
  pikkuSessionlessFunc,
28
33
  wireChannel,
29
34
  wireHTTP,
30
35
  defineChannelRoutes,
31
36
  } from '${pikkuTypesImportPath}'
37
+ import { TopicRef } from './events.schemas.gen.js'
32
38
 
33
39
  /**
34
40
  * Topic envelope clients receive: \`{ topic, data }\`. Functions that publish
@@ -40,13 +46,15 @@ export const serializeEventsScaffold = (authRequired, pikkuTypesImportPath) => {
40
46
  * the duplication; pick whichever style your project prefers.
41
47
  */
42
48
 
43
- export const realtimeSubscribe = pikkuChannelFunc<{ topic: string }>({
49
+ export const realtimeSubscribe = pikkuChannelFunc({
50
+ input: TopicRef,
44
51
  func: async ({ eventHub }, { topic }, { channel }) => {
45
52
  await eventHub?.subscribe(topic, channel.channelId)
46
53
  },
47
54
  })
48
55
 
49
- export const realtimeUnsubscribe = pikkuChannelFunc<{ topic: string }>({
56
+ export const realtimeUnsubscribe = pikkuChannelFunc({
57
+ input: TopicRef,
50
58
  func: async ({ eventHub }, { topic }, { channel }) => {
51
59
  await eventHub?.unsubscribe(topic, channel.channelId)
52
60
  },
@@ -74,7 +82,8 @@ wireChannel({
74
82
  * SSE per-topic stream. One connection = one subscription. The eventHub
75
83
  * cleans up automatically when the channel closes (onChannelClosed).
76
84
  */
77
- export const realtimeEventStream = pikkuSessionlessFunc<{ topic: string }, void>({
85
+ export const realtimeEventStream = pikkuSessionlessFunc({
86
+ input: TopicRef,
78
87
  description: 'Auto-generated SSE stream for a single event-hub topic',
79
88
  func: async ({ eventHub }, { topic }, { channel }) => {
80
89
  if (!channel) {
@@ -95,4 +104,5 @@ wireHTTP({
95
104
  tags: ['pikku:realtime', 'sse'],
96
105
  })
97
106
  `;
107
+ return { schemas, functions };
98
108
  };
@@ -2,6 +2,7 @@ import { pikkuSessionlessFunc } from '#pikku';
2
2
  import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
3
3
  import { writeFileInDir } from '../../../utils/file-writer.js';
4
4
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
5
+ import { removeLegacyScaffoldFile } from '../../../utils/remove-legacy-scaffold-file.js';
5
6
  import { serializePublicRPC } from './serialize-public-rpc.js';
6
7
  export const pikkuPublicRPC = pikkuSessionlessFunc({
7
8
  func: async ({ logger, config, variables }) => {
@@ -9,9 +10,12 @@ export const pikkuPublicRPC = pikkuSessionlessFunc({
9
10
  if (deployCodegenFlag === '1') {
10
11
  return false;
11
12
  }
12
- if (config.scaffold?.rpc) {
13
+ if (config.scaffold?.rpc && config.publicRpcSchemasFile) {
13
14
  const pathToPikkuTypes = getFileImportRelativePath(config.publicRpcFile, config.typesDeclarationFile, config.packageMappings);
14
- await writeFileInDir(logger, config.publicRpcFile, serializePublicRPC(pathToPikkuTypes, config.scaffold.rpc === 'auth', config.globalHTTPPrefix || ''));
15
+ const { schemas, functions } = serializePublicRPC(pathToPikkuTypes, config.scaffold.rpc === 'auth', config.globalHTTPPrefix || '');
16
+ await writeFileInDir(logger, config.publicRpcSchemasFile, schemas);
17
+ await writeFileInDir(logger, config.publicRpcFile, functions);
18
+ await removeLegacyScaffoldFile(config.publicRpcFile);
15
19
  return true;
16
20
  }
17
21
  return false;
@@ -2,6 +2,7 @@ import { pikkuSessionlessFunc } from '#pikku';
2
2
  import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
3
3
  import { writeFileInDir } from '../../../utils/file-writer.js';
4
4
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
5
+ import { removeLegacyScaffoldFile } from '../../../utils/remove-legacy-scaffold-file.js';
5
6
  import { serializeRemoteRPC } from './serialize-remote-rpc.js';
6
7
  export const pikkuRemoteRPC = pikkuSessionlessFunc({
7
8
  func: async ({ logger, config, variables }) => {
@@ -9,9 +10,12 @@ export const pikkuRemoteRPC = pikkuSessionlessFunc({
9
10
  if (deployCodegenFlag === '1') {
10
11
  return false;
11
12
  }
12
- if (config.remoteRpcWorkersFile) {
13
+ if (config.remoteRpcWorkersFile && config.remoteRpcSchemasFile) {
13
14
  const pathToPikkuTypes = getFileImportRelativePath(config.remoteRpcWorkersFile, config.typesDeclarationFile, config.packageMappings);
14
- await writeFileInDir(logger, config.remoteRpcWorkersFile, serializeRemoteRPC(pathToPikkuTypes));
15
+ const { schemas, functions } = serializeRemoteRPC(pathToPikkuTypes);
16
+ await writeFileInDir(logger, config.remoteRpcSchemasFile, schemas);
17
+ await writeFileInDir(logger, config.remoteRpcWorkersFile, functions);
18
+ await removeLegacyScaffoldFile(config.remoteRpcWorkersFile);
15
19
  return true;
16
20
  }
17
21
  return false;
@@ -1,4 +1,8 @@
1
+ export interface PublicRPCGenOutput {
2
+ schemas: string;
3
+ functions: string;
4
+ }
1
5
  /**
2
6
  * Generate public RPC HTTP endpoint
3
7
  */
4
- export declare const serializePublicRPC: (pathToPikkuTypes: string, requireAuth?: boolean, globalHTTPPrefix?: string) => string;
8
+ export declare const serializePublicRPC: (pathToPikkuTypes: string, requireAuth?: boolean, globalHTTPPrefix?: string) => PublicRPCGenOutput;
@@ -3,18 +3,28 @@
3
3
  */
4
4
  export const serializePublicRPC = (pathToPikkuTypes, requireAuth = true, globalHTTPPrefix = '') => {
5
5
  const authFlag = requireAuth ? 'true' : 'false';
6
- return `/**
6
+ const schemas = `/**
7
+ * Auto-generated public RPC endpoint schemas
8
+ * Do not edit manually - regenerate with 'npx pikku'
9
+ */
10
+ import { z } from 'zod'
11
+
12
+ export const RPCCall = z.object({
13
+ rpcName: z.string(),
14
+ data: z.unknown().optional(),
15
+ })
16
+ `;
17
+ const functions = `/**
7
18
  * Auto-generated public RPC HTTP endpoint
8
19
  * Do not edit manually - regenerate with 'npx pikku'
9
20
  */
10
21
  import { pikkuSessionlessFunc, wireHTTP } from '${pathToPikkuTypes}'
22
+ import { RPCCall } from './rpc-public.schemas.gen.js'
11
23
 
12
- export const rpcCaller = pikkuSessionlessFunc<
13
- { rpcName: string; data?: unknown },
14
- unknown
15
- >({
24
+ export const rpcCaller = pikkuSessionlessFunc({
16
25
  tags: ['pikku'],
17
26
  auth: ${authFlag},
27
+ input: RPCCall,
18
28
  func: async (_services, { rpcName, data }, { rpc }) => {
19
29
  return await rpc.exposed(rpcName, data)
20
30
  },
@@ -28,4 +38,5 @@ wireHTTP({
28
38
  func: rpcCaller,
29
39
  })
30
40
  `;
41
+ return { schemas, functions };
31
42
  };
@@ -1,4 +1,8 @@
1
+ export interface RemoteRPCGenOutput {
2
+ schemas: string;
3
+ functions: string;
4
+ }
1
5
  /**
2
6
  * Generate remote internal RPC queue worker and HTTP endpoint
3
7
  */
4
- export declare const serializeRemoteRPC: (pathToPikkuTypes: string) => string;
8
+ export declare const serializeRemoteRPC: (pathToPikkuTypes: string) => RemoteRPCGenOutput;