@pikku/cli 0.12.15 → 0.12.16

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 (160) hide show
  1. package/cli.schema.json +1 -1
  2. package/console-app/assets/index-CzMWJFqj.js +700 -0
  3. package/console-app/index.html +1 -1
  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.js +16 -1
  8. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  9. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  10. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  11. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  12. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  13. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +68 -5
  14. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  15. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  16. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  17. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  18. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  19. package/dist/.pikku/function/pikku-function-types.gen.d.ts +16 -19
  20. package/dist/.pikku/function/pikku-function-types.gen.js +15 -19
  21. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  22. package/dist/.pikku/function/pikku-functions-meta.gen.json +190 -107
  23. package/dist/.pikku/function/pikku-functions.gen.js +6 -2
  24. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  25. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  26. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  27. package/dist/.pikku/http/pikku-http-wirings-meta.gen.json +18 -1
  28. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +2 -2
  29. package/dist/.pikku/http/pikku-http-wirings.gen.js +3 -3
  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.gen.d.ts +3 -10
  33. package/dist/.pikku/pikku-bootstrap.gen.js +1 -18
  34. package/dist/.pikku/pikku-meta-service.gen.d.ts +7 -0
  35. package/dist/.pikku/pikku-meta-service.gen.js +9 -0
  36. package/dist/.pikku/pikku-services.gen.d.ts +2 -1
  37. package/dist/.pikku/pikku-services.gen.js +1 -0
  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/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  43. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +5 -1
  44. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  45. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  46. package/dist/.pikku/schemas/register.gen.js +15 -7
  47. package/dist/.pikku/schemas/schemas/DeployApplyInput.schema.json +1 -0
  48. package/dist/.pikku/schemas/schemas/DeployPlanInput.schema.json +1 -0
  49. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  50. package/dist/.pikku/schemas/schemas/PikkuNewAddonInput.schema.json +1 -1
  51. package/dist/.pikku/schemas/schemas/PikkuWorkflowRoutesOutput.schema.json +1 -0
  52. package/dist/.pikku/schemas/schemas/RemoteRPCHandlerInput.schema.json +1 -0
  53. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  54. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  55. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  56. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  57. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  58. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  59. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  60. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  61. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  62. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  63. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +3 -24
  64. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -14
  65. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  66. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.d.ts +1 -1
  67. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  68. package/dist/src/cli.wiring.js +63 -4
  69. package/dist/src/deploy/analyzer/analyzer.d.ts +16 -0
  70. package/dist/src/deploy/analyzer/analyzer.js +557 -0
  71. package/dist/src/deploy/analyzer/index.d.ts +3 -0
  72. package/dist/src/deploy/analyzer/index.js +1 -0
  73. package/dist/src/deploy/analyzer/manifest.d.ts +112 -0
  74. package/dist/src/deploy/analyzer/manifest.js +8 -0
  75. package/dist/src/deploy/build-pipeline.d.ts +39 -0
  76. package/dist/src/deploy/build-pipeline.js +209 -0
  77. package/dist/src/deploy/bundler/bundler.d.ts +30 -0
  78. package/dist/src/deploy/bundler/bundler.js +196 -0
  79. package/dist/src/deploy/bundler/dep-extractor.d.ts +35 -0
  80. package/dist/src/deploy/bundler/dep-extractor.js +213 -0
  81. package/dist/src/deploy/bundler/index.d.ts +3 -0
  82. package/dist/src/deploy/bundler/index.js +2 -0
  83. package/dist/src/deploy/bundler/types.d.ts +21 -0
  84. package/dist/src/deploy/bundler/types.js +5 -0
  85. package/dist/src/deploy/codegen/index.d.ts +2 -0
  86. package/dist/src/deploy/codegen/index.js +1 -0
  87. package/dist/src/deploy/codegen/per-unit-codegen.d.ts +44 -0
  88. package/dist/src/deploy/codegen/per-unit-codegen.js +216 -0
  89. package/dist/src/deploy/plan/executor.d.ts +9 -0
  90. package/dist/src/deploy/plan/executor.js +49 -0
  91. package/dist/src/deploy/plan/formatter.d.ts +4 -0
  92. package/dist/src/deploy/plan/formatter.js +114 -0
  93. package/dist/src/deploy/plan/index.d.ts +5 -0
  94. package/dist/src/deploy/plan/index.js +3 -0
  95. package/dist/src/deploy/plan/planner.d.ts +4 -0
  96. package/dist/src/deploy/plan/planner.js +220 -0
  97. package/dist/src/deploy/plan/provider.d.ts +30 -0
  98. package/dist/src/deploy/plan/provider.js +1 -0
  99. package/dist/src/deploy/plan/types.d.ts +29 -0
  100. package/dist/src/deploy/plan/types.js +1 -0
  101. package/dist/src/deploy/provider-adapter.d.ts +111 -0
  102. package/dist/src/deploy/provider-adapter.js +10 -0
  103. package/dist/src/functions/commands/all.js +6 -2
  104. package/dist/src/functions/commands/deploy-apply.d.ts +22 -0
  105. package/dist/src/functions/commands/deploy-apply.js +206 -0
  106. package/dist/src/functions/commands/deploy-info.d.ts +1 -0
  107. package/dist/src/functions/commands/deploy-info.js +122 -0
  108. package/dist/src/functions/commands/deploy-plan.d.ts +10 -0
  109. package/dist/src/functions/commands/deploy-plan.js +96 -0
  110. package/dist/src/functions/commands/enable.js +1 -1
  111. package/dist/src/functions/commands/new-addon.d.ts +3 -0
  112. package/dist/src/functions/commands/new-addon.js +68 -2
  113. package/dist/src/functions/commands/pikku-command-bootstrap.js +30 -20
  114. package/dist/src/functions/runtimes/nextjs/pikku-command-nextjs.js +7 -3
  115. package/dist/src/functions/wirings/ai-agent/pikku-command-ai-agent.js +7 -5
  116. package/dist/src/functions/wirings/channels/pikku-channels.js +3 -0
  117. package/dist/src/functions/wirings/channels/pikku-command-channels.js +3 -0
  118. package/dist/src/functions/wirings/cli/pikku-command-cli.js +3 -0
  119. package/dist/src/functions/wirings/cli/serialize-channel-cli.js +2 -2
  120. package/dist/src/functions/wirings/console/serialize-console-functions.js +2 -2
  121. package/dist/src/functions/wirings/functions/pikku-command-services.d.ts +1 -1
  122. package/dist/src/functions/wirings/functions/pikku-command-services.js +9 -2
  123. package/dist/src/functions/wirings/functions/serialize-function-imports.js +5 -3
  124. package/dist/src/functions/wirings/functions/serialize-function-types.js +17 -19
  125. package/dist/src/functions/wirings/http/pikku-command-http-routes.js +3 -0
  126. package/dist/src/functions/wirings/http/pikku-http-routes.js +3 -0
  127. package/dist/src/functions/wirings/mcp/pikku-command-mcp.js +6 -0
  128. package/dist/src/functions/wirings/package/pikku-command-package.js +1 -1
  129. package/dist/src/functions/wirings/package/serialize-package.d.ts +1 -1
  130. package/dist/src/functions/wirings/package/serialize-package.js +5 -2
  131. package/dist/src/functions/wirings/queue/pikku-command-queue.js +4 -0
  132. package/dist/src/functions/wirings/queue/pikku-queue.js +4 -0
  133. package/dist/src/functions/wirings/queue/serialize-queue-map.js +4 -1
  134. package/dist/src/functions/wirings/rpc/pikku-command-rpc.js +10 -3
  135. package/dist/src/functions/wirings/rpc/serialize-public-rpc.js +5 -27
  136. package/dist/src/functions/wirings/rpc/serialize-remote-rpc.js +11 -14
  137. package/dist/src/functions/wirings/rpc/serialize-rpc-wrapper.js +28 -3
  138. package/dist/src/functions/wirings/rpc/serialize-typed-rpc-map.js +15 -5
  139. package/dist/src/functions/wirings/scheduler/pikku-command-scheduler.js +4 -0
  140. package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.d.ts +1 -0
  141. package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.js +21 -0
  142. package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +10 -9
  143. package/dist/src/functions/wirings/workflow/serialize-workflow-map.d.ts +6 -1
  144. package/dist/src/functions/wirings/workflow/serialize-workflow-map.js +42 -5
  145. package/dist/src/functions/wirings/workflow/serialize-workflow-registration.d.ts +1 -1
  146. package/dist/src/functions/wirings/workflow/serialize-workflow-registration.js +3 -2
  147. package/dist/src/functions/wirings/workflow/serialize-workflow-routes.d.ts +4 -0
  148. package/dist/src/functions/wirings/workflow/serialize-workflow-routes.js +139 -0
  149. package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +4 -51
  150. package/dist/src/scaffold/rpc-remote.gen.d.ts +10 -0
  151. package/dist/src/scaffold/rpc-remote.gen.js +22 -0
  152. package/dist/src/services.js +12 -7
  153. package/dist/src/utils/pikku-cli-config.d.ts +1 -1
  154. package/dist/src/utils/pikku-cli-config.js +30 -28
  155. package/dist/src/utils/strip-verbose-meta.js +2 -0
  156. package/dist/tsconfig.tsbuildinfo +1 -1
  157. package/package.json +8 -4
  158. package/console-app/assets/index-robZPL3O.js +0 -672
  159. package/dist/src/functions/wirings/workflow/serialize-workflow-workers.d.ts +0 -4
  160. package/dist/src/functions/wirings/workflow/serialize-workflow-workers.js +0 -47
@@ -0,0 +1,557 @@
1
+ /**
2
+ * Provider-agnostic deployment analyzer.
3
+ *
4
+ * Core principle: one function = one deployment unit.
5
+ * Each function gets its own unit with all its triggers (HTTP, queue, cron).
6
+ * Gateways (MCP, agents, channels) dispatch to function units via RPC.
7
+ * Workflow orchestrators dispatch to step units via queue or RPC (inline).
8
+ */
9
+ /**
10
+ * Determine deploy target for a function based on its explicit flag
11
+ * and service compatibility.
12
+ */
13
+ function resolveDeployTarget(funcMeta, serverlessIncompatible) {
14
+ // Explicit flag takes priority
15
+ if (funcMeta.deploy === 'serverless')
16
+ return 'serverless';
17
+ if (funcMeta.deploy === 'server')
18
+ return 'server';
19
+ // Auto: check if any service is serverless-incompatible
20
+ if (funcMeta.services?.services) {
21
+ for (const svc of funcMeta.services.services) {
22
+ if (serverlessIncompatible.has(svc))
23
+ return 'server';
24
+ }
25
+ }
26
+ return 'serverless';
27
+ }
28
+ export function analyzeDeployment(state, options) {
29
+ const serverlessIncompatible = new Set(options.serverlessIncompatible ?? []);
30
+ const units = [];
31
+ const queues = [];
32
+ const scheduledTasks = [];
33
+ const channels = [];
34
+ const agents = [];
35
+ const mcpEndpoints = [];
36
+ const workflows = [];
37
+ const functionsMeta = state.functions.meta;
38
+ const httpMeta = state.http.meta;
39
+ // ── Step 1: Create function units ──────────────────────────────────
40
+ // Each function gets one unit. Collect all its triggers.
41
+ for (const [funcId, funcMeta] of entries(functionsMeta)) {
42
+ // Skip platform functions — their routes/queues are handled by gateway units
43
+ if (funcId.startsWith('agentRun:') ||
44
+ funcId.startsWith('agentStream:') ||
45
+ funcId.startsWith('agentApprove:') ||
46
+ funcId.startsWith('agentResume:') ||
47
+ funcId.startsWith('workflowStart:') ||
48
+ funcId.startsWith('workflow:') ||
49
+ funcId.startsWith('workflowStatus:') ||
50
+ funcId.startsWith('pikkuWorkflowWorker:') ||
51
+ funcId.startsWith('pikkuWorkflowOrchestrator:')) {
52
+ continue;
53
+ }
54
+ // Skip scaffold catch-all functions — they're bundled into units that need them
55
+ if (funcId.startsWith('http:') ||
56
+ funcId === 'agentCaller' ||
57
+ funcId === 'agentStreamCaller' ||
58
+ funcId === 'agentApproveCaller' ||
59
+ funcId === 'agentResumeCaller') {
60
+ continue;
61
+ }
62
+ const handlers = [];
63
+ // HTTP routes for this function
64
+ const routes = collectHttpRoutes(httpMeta, funcId);
65
+ if (routes.length > 0) {
66
+ handlers.push({ type: 'fetch', routes });
67
+ }
68
+ // Queue consumer for this function
69
+ for (const [queueName, queueMeta] of entries(state.queueWorkers.meta)) {
70
+ if (queueMeta.pikkuFuncId === funcId) {
71
+ handlers.push({ type: 'queue', queueName: queueMeta.name ?? queueName });
72
+ queues.push({
73
+ name: queueMeta.name ?? queueName,
74
+ consumerUnit: toKebab(funcId),
75
+ consumerFunctionId: funcId,
76
+ });
77
+ }
78
+ }
79
+ // Scheduled task for this function
80
+ for (const [_schedName, schedMeta] of entries(state.scheduledTasks.meta)) {
81
+ if (schedMeta.pikkuFuncId === funcId) {
82
+ handlers.push({
83
+ type: 'scheduled',
84
+ schedule: schedMeta.schedule,
85
+ taskName: schedMeta.name,
86
+ });
87
+ scheduledTasks.push({
88
+ name: schedMeta.name,
89
+ schedule: schedMeta.schedule,
90
+ unitName: toKebab(funcId),
91
+ functionId: funcId,
92
+ });
93
+ }
94
+ }
95
+ // Exposed/remote functions get concrete routes via the catch-all
96
+ if (funcMeta.expose) {
97
+ const funcName = funcMeta.name ?? funcId;
98
+ const rpcRoute = {
99
+ method: 'post',
100
+ route: `/rpc/${funcName}`,
101
+ pikkuFuncId: funcId,
102
+ };
103
+ const fetchHandler = handlers.find((h) => h.type === 'fetch');
104
+ if (fetchHandler) {
105
+ fetchHandler.routes.push(rpcRoute);
106
+ }
107
+ else {
108
+ handlers.push({ type: 'fetch', routes: [rpcRoute] });
109
+ }
110
+ }
111
+ if (funcMeta.remote) {
112
+ const funcName = funcMeta.name ?? funcId;
113
+ const remoteRoute = {
114
+ method: 'post',
115
+ route: `/remote/rpc/${funcName}`,
116
+ pikkuFuncId: funcId,
117
+ };
118
+ const fetchHandler = handlers.find((h) => h.type === 'fetch');
119
+ if (fetchHandler) {
120
+ fetchHandler.routes.push(remoteRoute);
121
+ }
122
+ else {
123
+ handlers.push({ type: 'fetch', routes: [remoteRoute] });
124
+ }
125
+ }
126
+ // Skip functions with no triggers (they'll be accessed via gateways)
127
+ // unless they're explicitly exposed/remote
128
+ if (handlers.length === 0) {
129
+ continue;
130
+ }
131
+ units.push({
132
+ name: toKebab(funcId),
133
+ role: 'function',
134
+ target: resolveDeployTarget(funcMeta, serverlessIncompatible),
135
+ functionIds: [funcId],
136
+ services: collectServicesForFunction(funcMeta),
137
+ dependsOn: [],
138
+ handlers,
139
+ tags: funcMeta.tags ?? [],
140
+ });
141
+ }
142
+ // ── Step 2: Agent gateways ─────────────────────────────────────────
143
+ for (const [agentName, agentMeta] of entries(state.agents.agentsMeta)) {
144
+ const toolIds = agentMeta.tools ?? [];
145
+ const subAgentNames = agentMeta.agents ?? [];
146
+ const unitName = `agent-${toKebab(agentName)}`;
147
+ // Agent gateway depends on its tool function units
148
+ const toolUnitNames = toolIds.map((id) => toKebab(id));
149
+ const subAgentUnitNames = subAgentNames.map((sa) => `agent-${toKebab(sa)}`);
150
+ // Agent needs AI services
151
+ const agentServices = [
152
+ { capability: 'ai-model', sourceServiceName: 'aiAgentRunner' },
153
+ { capability: 'ai-storage', sourceServiceName: 'aiStorage' },
154
+ ];
155
+ // Concrete routes for this agent via catch-all
156
+ const agentRoutes = [
157
+ {
158
+ method: 'post',
159
+ route: `/rpc/agent/${agentName}`,
160
+ pikkuFuncId: `agentRun:${agentName}`,
161
+ },
162
+ {
163
+ method: 'post',
164
+ route: `/rpc/agent/${agentName}/stream`,
165
+ pikkuFuncId: `agentStream:${agentName}`,
166
+ },
167
+ {
168
+ method: 'post',
169
+ route: `/rpc/agent/${agentName}/approve`,
170
+ pikkuFuncId: `agentApprove:${agentName}`,
171
+ },
172
+ {
173
+ method: 'post',
174
+ route: `/rpc/agent/${agentName}/resume`,
175
+ pikkuFuncId: `agentResume:${agentName}`,
176
+ },
177
+ ];
178
+ units.push({
179
+ name: unitName,
180
+ role: 'agent',
181
+ target: 'serverless',
182
+ functionIds: [],
183
+ services: agentServices,
184
+ dependsOn: [...toolUnitNames, ...subAgentUnitNames],
185
+ handlers: [{ type: 'fetch', routes: agentRoutes }],
186
+ tags: agentMeta.tags ?? [],
187
+ });
188
+ agents.push({
189
+ name: agentMeta.name,
190
+ unitName,
191
+ toolFunctionIds: toolIds,
192
+ subAgentNames,
193
+ model: agentMeta.model,
194
+ });
195
+ }
196
+ // ── Step 3: MCP gateway ────────────────────────────────────────────
197
+ const mcpToolIds = values(state.mcpEndpoints.toolsMeta).map((t) => t.pikkuFuncId);
198
+ const mcpResourceIds = values(state.mcpEndpoints.resourcesMeta).map((r) => r.pikkuFuncId);
199
+ const mcpPromptIds = values(state.mcpEndpoints.promptsMeta).map((p) => p.pikkuFuncId);
200
+ // Include functions explicitly marked mcp: true
201
+ for (const [funcId, funcMeta] of entries(functionsMeta)) {
202
+ if (funcMeta.mcp &&
203
+ !mcpToolIds.includes(funcId) &&
204
+ !mcpResourceIds.includes(funcId) &&
205
+ !mcpPromptIds.includes(funcId)) {
206
+ mcpToolIds.push(funcId);
207
+ }
208
+ }
209
+ const allMcpIds = [...mcpToolIds, ...mcpResourceIds, ...mcpPromptIds];
210
+ if (allMcpIds.length > 0) {
211
+ const unitName = 'mcp-server';
212
+ const mcpFuncUnitNames = allMcpIds.map((id) => toKebab(id));
213
+ units.push({
214
+ name: unitName,
215
+ role: 'mcp',
216
+ target: 'serverless',
217
+ functionIds: [], // No function code bundled
218
+ services: [],
219
+ dependsOn: mcpFuncUnitNames,
220
+ handlers: [{ type: 'fetch', routes: [] }],
221
+ tags: collectTags(allMcpIds, functionsMeta),
222
+ });
223
+ mcpEndpoints.push({
224
+ unitName,
225
+ toolFunctionIds: mcpToolIds,
226
+ resourceFunctionIds: mcpResourceIds,
227
+ promptFunctionIds: mcpPromptIds,
228
+ });
229
+ }
230
+ // ── Step 4: Channel gateways ───────────────────────────────────────
231
+ for (const [channelName, channelMeta] of entries(state.channels.meta)) {
232
+ const funcIds = collectChannelFunctionIds(channelMeta);
233
+ if (funcIds.length === 0)
234
+ continue;
235
+ const unitName = `channel-${toKebab(channelName)}`;
236
+ const funcUnitNames = funcIds.map((id) => toKebab(id));
237
+ units.push({
238
+ name: unitName,
239
+ role: 'channel',
240
+ target: 'serverless',
241
+ functionIds: [], // No function code bundled
242
+ services: [],
243
+ dependsOn: funcUnitNames,
244
+ handlers: [{ type: 'fetch', routes: [] }],
245
+ tags: channelMeta.tags ?? [],
246
+ });
247
+ channels.push({
248
+ name: channelMeta.name,
249
+ route: channelMeta.route,
250
+ unitName,
251
+ functionIds: funcIds,
252
+ });
253
+ }
254
+ // ── Step 5: Workflows ──────────────────────────────────────────────
255
+ buildWorkflows(state.workflows.graphMeta, functionsMeta, httpMeta, units, workflows, queues);
256
+ // ── Step 6: Ensure function units exist for gateway dependencies ───
257
+ // Gateways depend on function units. If a function is only used via
258
+ // a gateway (not directly wired to HTTP/queue/cron), it still needs
259
+ // a unit with a fetch handler for RPC access.
260
+ const existingUnitNames = new Set(units.map((u) => u.name));
261
+ const unitsSnapshot = Array.from(units);
262
+ for (const unit of unitsSnapshot) {
263
+ for (const dep of unit.dependsOn) {
264
+ if (!existingUnitNames.has(dep)) {
265
+ // Find the function ID for this dependency
266
+ const funcId = fromKebab(dep);
267
+ const funcMeta = functionsMeta[funcId];
268
+ if (funcMeta) {
269
+ units.push({
270
+ name: dep,
271
+ role: 'function',
272
+ target: resolveDeployTarget(funcMeta, serverlessIncompatible),
273
+ functionIds: [funcId],
274
+ services: collectServicesForFunction(funcMeta),
275
+ dependsOn: [],
276
+ handlers: [{ type: 'fetch', routes: [] }],
277
+ tags: funcMeta.tags ?? [],
278
+ });
279
+ existingUnitNames.add(dep);
280
+ }
281
+ }
282
+ }
283
+ }
284
+ // ── Step 7: Wire queues to their consumer units ──
285
+ // All queues (user-defined + workflow-generated) now have consumerUnit set.
286
+ // Wire the queue handler onto the unit and add workflow services if needed.
287
+ for (const queue of queues) {
288
+ const unit = units.find((u) => u.name === queue.consumerUnit);
289
+ if (!unit)
290
+ continue;
291
+ // Add queue handler if not already present
292
+ const hasQueueHandler = unit.handlers.some((h) => h.type === 'queue' && h.queueName === queue.name);
293
+ if (!hasQueueHandler) {
294
+ unit.handlers.push({ type: 'queue', queueName: queue.name });
295
+ }
296
+ // Workflow step workers need D1 for step state + queue for re-queuing orchestrator
297
+ if (queue.consumerFunctionId.startsWith('pikkuWorkflowWorker:')) {
298
+ if (!unit.services.some((s) => s.capability === 'workflow-state')) {
299
+ unit.services.push({
300
+ capability: 'workflow-state',
301
+ sourceServiceName: 'workflowService',
302
+ });
303
+ }
304
+ if (!unit.services.some((s) => s.capability === 'queue')) {
305
+ unit.services.push({
306
+ capability: 'queue',
307
+ sourceServiceName: 'queueService',
308
+ });
309
+ }
310
+ }
311
+ }
312
+ // ── Secrets & Variables ────────────────────────────────────────────
313
+ const secrets = state.secrets.definitions.map((s) => ({
314
+ secretId: s.secretId,
315
+ displayName: s.displayName,
316
+ description: s.description,
317
+ }));
318
+ const variables = state.variables.definitions.map((v) => ({
319
+ variableId: v.variableId,
320
+ displayName: v.displayName,
321
+ description: v.description,
322
+ }));
323
+ return {
324
+ projectId: options.projectId,
325
+ manifestVersion: 1,
326
+ units,
327
+ queues,
328
+ scheduledTasks,
329
+ channels,
330
+ agents,
331
+ mcpEndpoints,
332
+ workflows,
333
+ secrets,
334
+ variables,
335
+ };
336
+ }
337
+ // ---------------------------------------------------------------------------
338
+ // Workflow builder
339
+ // ---------------------------------------------------------------------------
340
+ function buildWorkflows(graphMeta, _functionsMeta, _httpMeta, units, workflows, queues) {
341
+ for (const [_wfName, graph] of entries(graphMeta)) {
342
+ const steps = [];
343
+ const stepUnitNames = [];
344
+ for (const [nodeId, node] of Object.entries(graph.nodes)) {
345
+ if ('flow' in node)
346
+ continue;
347
+ if (!('rpcName' in node))
348
+ continue;
349
+ const stepUnitName = toKebab(node.rpcName);
350
+ const isAsync = node.options?.async === true;
351
+ const isInline = !isAsync && graph.inline === true;
352
+ steps.push({
353
+ name: node.stepName ?? nodeId,
354
+ inline: isInline,
355
+ functionId: node.rpcName,
356
+ unitName: stepUnitName,
357
+ });
358
+ stepUnitNames.push(stepUnitName);
359
+ }
360
+ // Build orchestrator unit — no function code, just orchestration
361
+ const orchUnitName = `wf-${toKebab(graph.name)}`;
362
+ const orchServices = [
363
+ { capability: 'workflow-state', sourceServiceName: 'workflowService' },
364
+ { capability: 'queue', sourceServiceName: 'queueService' },
365
+ ];
366
+ // Concrete routes for this workflow via catch-all
367
+ const wfRoutes = [
368
+ {
369
+ method: 'post',
370
+ route: `/workflow/${graph.name}/start`,
371
+ pikkuFuncId: `workflowStart:${graph.name}`,
372
+ },
373
+ {
374
+ method: 'post',
375
+ route: `/workflow/${graph.name}/run`,
376
+ pikkuFuncId: `workflow:${graph.name}`,
377
+ },
378
+ {
379
+ method: 'get',
380
+ route: `/workflow/${graph.name}/status/:runId`,
381
+ pikkuFuncId: `workflowStatus:${graph.name}`,
382
+ },
383
+ {
384
+ method: 'post',
385
+ route: `/workflow/${graph.name}/graph/:nodeId`,
386
+ pikkuFuncId: `graphStart:${graph.name}`,
387
+ },
388
+ ];
389
+ // Orchestrator queue — the orchestrator consumes from this
390
+ const orchQueueName = `wf-orchestrator-${toKebab(graph.name)}`;
391
+ const orchHandlers = [
392
+ { type: 'fetch', routes: wfRoutes },
393
+ { type: 'queue', queueName: orchQueueName },
394
+ ];
395
+ units.push({
396
+ name: orchUnitName,
397
+ role: 'workflow',
398
+ target: 'serverless',
399
+ functionIds: [],
400
+ services: orchServices,
401
+ dependsOn: stepUnitNames,
402
+ handlers: orchHandlers,
403
+ tags: [],
404
+ });
405
+ queues.push({
406
+ name: orchQueueName,
407
+ consumerUnit: orchUnitName,
408
+ consumerFunctionId: `pikkuWorkflowOrchestrator:${graph.name}`,
409
+ });
410
+ // Per-step queues — each step function worker consumes its own queue.
411
+ // The step units may not exist yet (created in step 6), so we just
412
+ // create queue definitions here. Step 7 wires them to units.
413
+ for (const step of steps) {
414
+ if (step.inline || !step.functionId)
415
+ continue;
416
+ const stepQueueName = `wf-step-${toKebab(step.functionId)}`;
417
+ queues.push({
418
+ name: stepQueueName,
419
+ consumerUnit: toKebab(step.functionId),
420
+ consumerFunctionId: `pikkuWorkflowWorker:${step.functionId}`,
421
+ });
422
+ }
423
+ workflows.push({
424
+ name: graph.name,
425
+ pikkuFuncId: graph.pikkuFuncId,
426
+ orchestratorUnit: orchUnitName,
427
+ steps,
428
+ });
429
+ }
430
+ }
431
+ // ---------------------------------------------------------------------------
432
+ // Service mapping
433
+ // ---------------------------------------------------------------------------
434
+ const SERVICE_CAPABILITY_MAP = {
435
+ kysely: 'database',
436
+ database: 'database',
437
+ db: 'database',
438
+ contentService: 'object-storage',
439
+ content: 'object-storage',
440
+ storage: 'object-storage',
441
+ queueService: 'queue',
442
+ aiAgentRunner: 'ai-model',
443
+ ai: 'ai-model',
444
+ aiStorage: 'ai-storage',
445
+ workflowService: 'workflow-state',
446
+ workflow: 'workflow-state',
447
+ credentialService: 'credential-store',
448
+ credentials: 'credential-store',
449
+ schedulerService: 'scheduler',
450
+ };
451
+ function mapServiceToRequirement(serviceName) {
452
+ const capability = SERVICE_CAPABILITY_MAP[serviceName];
453
+ if (!capability)
454
+ return null;
455
+ return { capability, sourceServiceName: serviceName };
456
+ }
457
+ function collectServicesForFunction(funcMeta) {
458
+ if (!funcMeta?.services?.services)
459
+ return [];
460
+ const requirements = [];
461
+ const seen = new Set();
462
+ for (const svc of funcMeta.services.services) {
463
+ const req = mapServiceToRequirement(svc);
464
+ if (req && !seen.has(req.capability)) {
465
+ requirements.push(req);
466
+ seen.add(req.capability);
467
+ }
468
+ }
469
+ return requirements;
470
+ }
471
+ // ---------------------------------------------------------------------------
472
+ // HTTP route helpers
473
+ // ---------------------------------------------------------------------------
474
+ const HTTP_METHODS = [
475
+ 'get',
476
+ 'post',
477
+ 'put',
478
+ 'delete',
479
+ 'head',
480
+ 'patch',
481
+ 'options',
482
+ ];
483
+ function collectHttpRoutes(httpMeta, funcId) {
484
+ const routes = [];
485
+ for (const method of HTTP_METHODS) {
486
+ const methodRoutes = httpMeta[method];
487
+ if (!methodRoutes)
488
+ continue;
489
+ for (const routeMeta of values(methodRoutes)) {
490
+ if (routeMeta.pikkuFuncId === funcId) {
491
+ routes.push({
492
+ method: method.toUpperCase(),
493
+ route: routeMeta.route,
494
+ pikkuFuncId: funcId,
495
+ });
496
+ }
497
+ }
498
+ }
499
+ return routes;
500
+ }
501
+ // ---------------------------------------------------------------------------
502
+ // Channel helpers
503
+ // ---------------------------------------------------------------------------
504
+ function collectChannelFunctionIds(channelMeta) {
505
+ const ids = [];
506
+ if (channelMeta.connect?.pikkuFuncId)
507
+ ids.push(channelMeta.connect.pikkuFuncId);
508
+ if (channelMeta.disconnect?.pikkuFuncId)
509
+ ids.push(channelMeta.disconnect.pikkuFuncId);
510
+ if (channelMeta.message?.pikkuFuncId)
511
+ ids.push(channelMeta.message.pikkuFuncId);
512
+ if (channelMeta.messageWirings) {
513
+ for (const commandGroup of values(channelMeta.messageWirings)) {
514
+ for (const wiring of values(commandGroup)) {
515
+ if (wiring.pikkuFuncId) {
516
+ ids.push(wiring.pikkuFuncId);
517
+ }
518
+ }
519
+ }
520
+ }
521
+ return [...new Set(ids)];
522
+ }
523
+ // ---------------------------------------------------------------------------
524
+ // Naming helpers
525
+ // ---------------------------------------------------------------------------
526
+ function toKebab(str) {
527
+ return str
528
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
529
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
530
+ .toLowerCase();
531
+ }
532
+ function fromKebab(str) {
533
+ return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
534
+ }
535
+ // ---------------------------------------------------------------------------
536
+ // Tag helpers
537
+ // ---------------------------------------------------------------------------
538
+ function collectTags(funcIds, functionsMeta) {
539
+ const tags = new Set();
540
+ for (const id of funcIds) {
541
+ const meta = functionsMeta[id];
542
+ if (meta?.tags) {
543
+ for (const tag of meta.tags)
544
+ tags.add(tag);
545
+ }
546
+ }
547
+ return [...tags];
548
+ }
549
+ // ---------------------------------------------------------------------------
550
+ // Typed iteration helpers
551
+ // ---------------------------------------------------------------------------
552
+ function entries(record) {
553
+ return Object.entries(record);
554
+ }
555
+ function values(record) {
556
+ return Object.values(record);
557
+ }
@@ -0,0 +1,3 @@
1
+ export { analyzeDeployment } from './analyzer.js';
2
+ export type { AnalyzerOptions } from './analyzer.js';
3
+ export type { DeploymentManifest, DeploymentUnit, DeploymentUnitRole, ServiceCapability, ServiceRequirement, HttpRouteInfo, QueueDefinition, ScheduledTaskDefinition, ChannelDefinition, AgentDefinition, MCPEndpointDefinition, WorkflowStepDefinition, WorkflowDefinition, SecretDeclaration, VariableDeclaration, } from './manifest.js';
@@ -0,0 +1 @@
1
+ export { analyzeDeployment } from './analyzer.js';
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Provider-agnostic deployment manifest types.
3
+ *
4
+ * Core principle: one function = one deployment unit.
5
+ * Gateways (MCP, agents, channels, workflow orchestrators)
6
+ * don't bundle function code — they dispatch via RPC.
7
+ */
8
+ /** What kind of deployment entry */
9
+ export type DeploymentUnitRole = 'function' | 'mcp' | 'agent' | 'channel' | 'workflow' | 'workflow-step';
10
+ /** What handlers a unit needs to export */
11
+ export type DeploymentHandler = {
12
+ type: 'fetch';
13
+ routes: HttpRouteInfo[];
14
+ } | {
15
+ type: 'queue';
16
+ queueName: string;
17
+ } | {
18
+ type: 'scheduled';
19
+ schedule: string;
20
+ taskName: string;
21
+ };
22
+ /** Abstract infrastructure capability */
23
+ export type ServiceCapability = 'database' | 'object-storage' | 'queue' | 'kv' | 'ai-model' | 'ai-storage' | 'scheduler' | 'workflow-state' | 'credential-store';
24
+ export interface ServiceRequirement {
25
+ capability: ServiceCapability;
26
+ /** Original service name from code (e.g. 'kysely', 'contentService') */
27
+ sourceServiceName: string;
28
+ }
29
+ export interface HttpRouteInfo {
30
+ method: string;
31
+ route: string;
32
+ pikkuFuncId: string;
33
+ }
34
+ export interface DeploymentUnit {
35
+ name: string;
36
+ role: DeploymentUnitRole;
37
+ /** Deploy target: serverless (CF Worker / Lambda) or server (container) */
38
+ target: 'serverless' | 'server';
39
+ /** Functions bundled in this unit (for function/workflow-step units) */
40
+ functionIds: string[];
41
+ services: ServiceRequirement[];
42
+ /** Other unit names this unit calls via RPC / service bindings */
43
+ dependsOn: string[];
44
+ /** What runtime handlers this unit needs to export */
45
+ handlers: DeploymentHandler[];
46
+ tags: string[];
47
+ }
48
+ export interface QueueDefinition {
49
+ name: string;
50
+ consumerUnit: string;
51
+ consumerFunctionId: string;
52
+ }
53
+ export interface ScheduledTaskDefinition {
54
+ name: string;
55
+ schedule: string;
56
+ unitName: string;
57
+ functionId: string;
58
+ }
59
+ export interface ChannelDefinition {
60
+ name: string;
61
+ route: string;
62
+ unitName: string;
63
+ functionIds: string[];
64
+ }
65
+ export interface AgentDefinition {
66
+ name: string;
67
+ unitName: string;
68
+ toolFunctionIds: string[];
69
+ subAgentNames: string[];
70
+ model: string;
71
+ }
72
+ export interface MCPEndpointDefinition {
73
+ unitName: string;
74
+ toolFunctionIds: string[];
75
+ resourceFunctionIds: string[];
76
+ promptFunctionIds: string[];
77
+ }
78
+ export interface WorkflowStepDefinition {
79
+ name: string;
80
+ inline: boolean;
81
+ functionId?: string;
82
+ unitName?: string;
83
+ }
84
+ export interface WorkflowDefinition {
85
+ name: string;
86
+ pikkuFuncId: string;
87
+ orchestratorUnit: string;
88
+ steps: WorkflowStepDefinition[];
89
+ }
90
+ export interface SecretDeclaration {
91
+ secretId: string;
92
+ displayName: string;
93
+ description?: string;
94
+ }
95
+ export interface VariableDeclaration {
96
+ variableId: string;
97
+ displayName: string;
98
+ description?: string;
99
+ }
100
+ export interface DeploymentManifest {
101
+ projectId: string;
102
+ manifestVersion: 1;
103
+ units: DeploymentUnit[];
104
+ queues: QueueDefinition[];
105
+ scheduledTasks: ScheduledTaskDefinition[];
106
+ channels: ChannelDefinition[];
107
+ agents: AgentDefinition[];
108
+ mcpEndpoints: MCPEndpointDefinition[];
109
+ workflows: WorkflowDefinition[];
110
+ secrets: SecretDeclaration[];
111
+ variables: VariableDeclaration[];
112
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Provider-agnostic deployment manifest types.
3
+ *
4
+ * Core principle: one function = one deployment unit.
5
+ * Gateways (MCP, agents, channels, workflow orchestrators)
6
+ * don't bundle function code — they dispatch via RPC.
7
+ */
8
+ export {};