@pikku/cli 0.12.32 → 0.12.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.schema.json +1 -1
- package/console-app/assets/index-DsW0T00Z.js +233 -0
- package/console-app/index.html +1 -1
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +1 -0
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +158 -158
- package/dist/.pikku/function/pikku-functions.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +2 -1
- package/dist/.pikku/pikku-services.gen.js +1 -0
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +8 -8
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +11 -11
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/bin/pikku-bin.mjs +2 -2
- package/dist/src/deploy/analyzer/analyzer.js +7 -4
- package/dist/src/fabric/fabric-commands.d.ts +3 -3
- package/dist/src/fabric/functions/llm-key.function.d.ts +4 -4
- package/dist/src/functions/db/db-codegen.js +7 -3
- package/dist/src/functions/db/zod-codegen.js +81 -6
- package/dist/src/functions/wirings/console/serialize-console-functions.js +6 -0
- package/dist/src/functions/wirings/mcp/pikku-command-mcp-json.js +1 -1
- package/dist/src/functions/wirings/rpc/serialize-rpc-wrapper.js +8 -0
- package/dist/src/functions/workflows/all.workflow.js +1 -1
- package/dist/src/scaffold/rpc-remote.gen.js +1 -1
- package/dist/src/utils/pikku-cli-config.js +3 -0
- package/package.json +5 -5
- package/skills/pikku-workflow/SKILL.md +31 -2
- package/console-app/assets/index-BkiCv5R3.js +0 -229
|
@@ -67,14 +67,67 @@ function emitZodModule(tables) {
|
|
|
67
67
|
}
|
|
68
68
|
return `${lines.join('\n')}\n`;
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Resolve a Kysely field type to a `{ schema, generated }` pair, where `schema`
|
|
72
|
+
* is the zod expression for the *row* (select) shape and `generated` is true
|
|
73
|
+
* when the column is optional on insert.
|
|
74
|
+
*
|
|
75
|
+
* `db-codegen` emits two field shapes depending on a column's classification:
|
|
76
|
+
* - public columns → `Generated<T>`, bare `T`, `T | null`, or a public
|
|
77
|
+
* bool/date column as `Generated<ColumnType<base, rw, rw>>`
|
|
78
|
+
* - private/pii/secret columns (the default is `private`) →
|
|
79
|
+
* `ColumnType<Brand<base> | null, insert | undefined, update>`
|
|
80
|
+
* where Brand is `Private` | `Pii` | `Secret`.
|
|
81
|
+
*
|
|
82
|
+
* For `ColumnType<Select, Insert, Update>` the row schema comes from `Select`
|
|
83
|
+
* and the column is insert-optional when `Insert` admits `undefined` (Kysely's
|
|
84
|
+
* encoding for default/auto/generated columns).
|
|
85
|
+
*/
|
|
70
86
|
function zodForType(tsType) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
let inner = tsType.trim();
|
|
88
|
+
let generated = false;
|
|
89
|
+
// Peel a single `Generated<…>` wrapper. For public bool/date columns this
|
|
90
|
+
// wraps a `ColumnType<…>`, so the unwrapped inner is handled below.
|
|
91
|
+
const generatedMatch = inner.match(/^Generated<(.+)>$/);
|
|
92
|
+
if (generatedMatch) {
|
|
93
|
+
generated = true;
|
|
94
|
+
inner = generatedMatch[1].trim();
|
|
95
|
+
}
|
|
96
|
+
// `ColumnType<Select, Insert, Update>` — classified columns and wrapped
|
|
97
|
+
// public bool/date columns. Row schema from Select; optional from Insert.
|
|
98
|
+
if (inner.startsWith('ColumnType<') && inner.endsWith('>')) {
|
|
99
|
+
const args = splitGenericArgs(inner.slice('ColumnType<'.length, -1));
|
|
100
|
+
const selectT = args[0]?.trim() ?? 'unknown';
|
|
101
|
+
const insertT = args[1]?.trim() ?? '';
|
|
102
|
+
if (unionIncludesUndefined(insertT)) {
|
|
103
|
+
generated = true;
|
|
104
|
+
}
|
|
105
|
+
return { schema: scalarSchema(selectT), generated };
|
|
106
|
+
}
|
|
107
|
+
return { schema: scalarSchema(inner), generated };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Resolve a scalar/select type expression to a zod expression. Handles a
|
|
111
|
+
* trailing `| null`, classification brands (`Private`/`Pii`/`Secret`), arrays,
|
|
112
|
+
* and the known scalar bases. Unknown bases fall back to `z.unknown()` so
|
|
113
|
+
* generation stays total.
|
|
114
|
+
*/
|
|
115
|
+
function scalarSchema(tsType) {
|
|
116
|
+
let inner = tsType.trim();
|
|
117
|
+
// Defensive: a Select arg may itself be `Generated<…>` in older schemas.
|
|
118
|
+
const generatedMatch = inner.match(/^Generated<(.+)>$/);
|
|
119
|
+
if (generatedMatch) {
|
|
120
|
+
inner = generatedMatch[1].trim();
|
|
121
|
+
}
|
|
74
122
|
const nullable = inner.endsWith(' | null');
|
|
75
123
|
if (nullable) {
|
|
76
124
|
inner = inner.slice(0, -' | null'.length).trim();
|
|
77
125
|
}
|
|
126
|
+
// Unwrap a classification brand: `Private<T>` / `Pii<T>` / `Secret<T>` → T.
|
|
127
|
+
const brandMatch = inner.match(/^(?:Private|Pii|Secret)<(.+)>$/);
|
|
128
|
+
if (brandMatch) {
|
|
129
|
+
inner = brandMatch[1].trim();
|
|
130
|
+
}
|
|
78
131
|
let schema;
|
|
79
132
|
switch (inner) {
|
|
80
133
|
case 'string':
|
|
@@ -94,8 +147,7 @@ function zodForType(tsType) {
|
|
|
94
147
|
break;
|
|
95
148
|
default:
|
|
96
149
|
if (inner.endsWith('[]')) {
|
|
97
|
-
|
|
98
|
-
schema = `z.array(${item})`;
|
|
150
|
+
schema = `z.array(${scalarSchema(inner.slice(0, -2).trim())})`;
|
|
99
151
|
}
|
|
100
152
|
else {
|
|
101
153
|
schema = 'z.unknown()';
|
|
@@ -105,5 +157,28 @@ function zodForType(tsType) {
|
|
|
105
157
|
if (nullable) {
|
|
106
158
|
schema += '.nullable()';
|
|
107
159
|
}
|
|
108
|
-
return
|
|
160
|
+
return schema;
|
|
161
|
+
}
|
|
162
|
+
/** Split the generic argument list of `Foo<a, b, c>` on top-level commas. */
|
|
163
|
+
function splitGenericArgs(args) {
|
|
164
|
+
const parts = [];
|
|
165
|
+
let depth = 0;
|
|
166
|
+
let start = 0;
|
|
167
|
+
for (let i = 0; i < args.length; i++) {
|
|
168
|
+
const char = args[i];
|
|
169
|
+
if (char === '<')
|
|
170
|
+
depth++;
|
|
171
|
+
else if (char === '>')
|
|
172
|
+
depth--;
|
|
173
|
+
else if (char === ',' && depth === 0) {
|
|
174
|
+
parts.push(args.slice(start, i));
|
|
175
|
+
start = i + 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
parts.push(args.slice(start));
|
|
179
|
+
return parts;
|
|
180
|
+
}
|
|
181
|
+
/** True when a union type expression contains `undefined` as a top-level member. */
|
|
182
|
+
function unionIncludesUndefined(union) {
|
|
183
|
+
return union.split('|').some((member) => member.trim() === 'undefined');
|
|
109
184
|
}
|
|
@@ -99,6 +99,12 @@ export const consoleRoutes = defineHTTPRoutes({
|
|
|
99
99
|
sse: true,
|
|
100
100
|
func: ref('console:streamWorkflowRun'),
|
|
101
101
|
},
|
|
102
|
+
functionTestsStream: {
|
|
103
|
+
route: '/function-tests/stream',
|
|
104
|
+
method: 'get',
|
|
105
|
+
sse: true,
|
|
106
|
+
func: ref('console:streamFunctionTests'),
|
|
107
|
+
},
|
|
102
108
|
},
|
|
103
109
|
})
|
|
104
110
|
|
|
@@ -5,7 +5,7 @@ import { serializeMCPJson } from '@pikku/inspector';
|
|
|
5
5
|
export const pikkuMCPJSON = pikkuSessionlessFunc({
|
|
6
6
|
func: async ({ logger, config, getInspectorState }) => {
|
|
7
7
|
const state = await getInspectorState();
|
|
8
|
-
const mcpJsonFile = config.clientFiles?.mcpJsonFile;
|
|
8
|
+
const mcpJsonFile = config.mcpJsonFile ?? config.clientFiles?.mcpJsonFile;
|
|
9
9
|
if (mcpJsonFile) {
|
|
10
10
|
const mcpJson = serializeMCPJson(logger, state);
|
|
11
11
|
await writeFileInDir(logger, mcpJsonFile, mcpJson, {
|
|
@@ -133,6 +133,14 @@ export class PikkuRPC {
|
|
|
133
133
|
return await this.pikkuFetch.post(\`${globalHTTPPrefix}/rpc/agent/\${String(agentName)}/approve\` as never, input) as any
|
|
134
134
|
},
|
|
135
135
|
}
|
|
136
|
+
|
|
137
|
+
subscribeToSSE<T = unknown>(
|
|
138
|
+
path: string,
|
|
139
|
+
handler: (event: T) => void,
|
|
140
|
+
onError?: (err: unknown) => void
|
|
141
|
+
): { close: () => void } {
|
|
142
|
+
return this.pikkuFetch.subscribeToSSE(path, handler, onError)
|
|
143
|
+
}
|
|
136
144
|
}
|
|
137
145
|
|
|
138
146
|
export const pikkuRPC = new PikkuRPC();
|
|
@@ -208,8 +208,8 @@ export const allWorkflow = pikkuWorkflowComplexFunc({
|
|
|
208
208
|
if (gateways) {
|
|
209
209
|
allImports.push(config.gatewaysWiringFile);
|
|
210
210
|
}
|
|
211
|
+
await workflow.do('MCP JSON', 'pikkuMCPJSON', null);
|
|
211
212
|
if (mcp) {
|
|
212
|
-
await workflow.do('MCP JSON', 'pikkuMCPJSON', null);
|
|
213
213
|
allImports.push(config.mcpWiringsMetaFile, config.mcpWiringsFile);
|
|
214
214
|
}
|
|
215
215
|
if (cli) {
|
|
@@ -309,6 +309,9 @@ const _getPikkuCLIConfig = async (logger, configFile = undefined, requiredFields
|
|
|
309
309
|
if (!result.mcpTypesFile) {
|
|
310
310
|
result.mcpTypesFile = join(mcpDir, 'pikku-mcp-types.gen.ts');
|
|
311
311
|
}
|
|
312
|
+
if (!result.mcpJsonFile) {
|
|
313
|
+
result.mcpJsonFile = join(mcpDir, 'mcp.gen.json');
|
|
314
|
+
}
|
|
312
315
|
// AI Agent
|
|
313
316
|
const agentDir = join(result.outDir, 'agent');
|
|
314
317
|
if (!result.agentWiringsFile) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.34",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"imports": {
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@openapi-contrib/json-schema-to-openapi-schema": "^4.3.1",
|
|
29
|
-
"@pikku/core": "^0.12.
|
|
29
|
+
"@pikku/core": "^0.12.31",
|
|
30
30
|
"@pikku/deploy-cloudflare": "^0.12.3",
|
|
31
|
-
"@pikku/fetch": "^0.12.
|
|
32
|
-
"@pikku/inspector": "^0.12.
|
|
31
|
+
"@pikku/fetch": "^0.12.3",
|
|
32
|
+
"@pikku/inspector": "^0.12.19",
|
|
33
33
|
"@pikku/kysely": "^0.12.14",
|
|
34
34
|
"@pikku/kysely-node-sqlite": "^0.12.1",
|
|
35
|
-
"@pikku/node-http-server": "^0.12.
|
|
35
|
+
"@pikku/node-http-server": "^0.12.2",
|
|
36
36
|
"@pikku/openapi-parser": "^0.12.10",
|
|
37
37
|
"@pikku/schedule": "^0.12.0",
|
|
38
38
|
"@pikku/ws": "^0.12.2",
|
|
@@ -47,7 +47,7 @@ const myWorkflow = pikkuWorkflowFunc<InputType, OutputType>(
|
|
|
47
47
|
### Workflow Step Types
|
|
48
48
|
|
|
49
49
|
```typescript
|
|
50
|
-
// RPC step —
|
|
50
|
+
// RPC step — run a named Pikku function as a step
|
|
51
51
|
// workflow.do(stepName, funcName, data, options?)
|
|
52
52
|
const result = await workflow.do(
|
|
53
53
|
'Create profile',
|
|
@@ -58,11 +58,40 @@ const result = await workflow.do(
|
|
|
58
58
|
{ retries: 3, retryDelay: '1s' }
|
|
59
59
|
)
|
|
60
60
|
|
|
61
|
-
// Inline step — immediate execution, cached for replay
|
|
61
|
+
// Inline closure step — immediate execution, cached for replay
|
|
62
62
|
// workflow.do(stepName, asyncFn)
|
|
63
63
|
const result = await workflow.do('Generate message', async () => {
|
|
64
64
|
return `Welcome, ${data.email}!`
|
|
65
65
|
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Step execution: inline vs queue dispatch
|
|
69
|
+
|
|
70
|
+
Whether a step runs **inline** (same process/session, no queue round-trip) or is **dispatched to the queue** is decided **purely by the step's function** — there is no workflow-level or per-call `inline` flag.
|
|
71
|
+
|
|
72
|
+
- **Steps default to inline.** Most steps don't need their own worker; running them inline avoids a queue round-trip per step, so a normally-started workflow executes its whole chain in one orchestrator pass.
|
|
73
|
+
- **`inline: false` opts a function out.** Set `inline: false` on the **function config** (`pikkuFunc` / `pikkuSessionlessFunc`, same level as `auth`/`expose`) to dispatch that step via the queue — for expensive/long-running steps that deserve their own worker, retry isolation, and concurrency limits. It is **not** a `workflow.do(...)` option (those are only `retries`/`retryDelay`/`description`).
|
|
74
|
+
|
|
75
|
+
The rule (`dispatchStep`):
|
|
76
|
+
|
|
77
|
+
| Function `inline` | `queueService` present? | Result |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| default / `true` | any | **inline** |
|
|
80
|
+
| `false` | yes | **queued** (own worker) |
|
|
81
|
+
| `false` | no | **inline + a `logger.warn`** (misconfiguration: can't dispatch) |
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// Push this one expensive step onto the queue; every other step stays inline:
|
|
85
|
+
export const renderLargeReport = pikkuSessionlessFunc({
|
|
86
|
+
inline: false, // dispatch via queue instead of running inline
|
|
87
|
+
input: ReportInput,
|
|
88
|
+
output: ReportOutput,
|
|
89
|
+
func: async (services, data) => { /* ... */ },
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- **Run-level `inline` is separate** and only controls whether the *whole run* executes in-process without queue infrastructure (it's set automatically when there is no `queueService`, or via `startWorkflow(..., { inline: true })`). It governs sleep handling, **not** per-step dispatch — step dispatch is always per-function.
|
|
94
|
+
- `inline: false` requires a `queueService`; without one the step still runs (so the workflow progresses) but emits a `logger.warn` so the misconfiguration is visible.
|
|
66
95
|
|
|
67
96
|
// Sleep — durable pause (duration: '5min', '1h', '30s', '1d')
|
|
68
97
|
await workflow.sleep('Wait 5 minutes', '5min')
|