@pikku/cli 0.12.48 → 0.12.50
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/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-client.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.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.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 +165 -165
- 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 +1 -1
- 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 +2 -2
- 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/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/server-entry.d.ts +1 -1
- package/dist/src/deploy/server-entry.js +3 -1
- package/dist/src/fabric/functions/validate.function.js +102 -0
- package/dist/src/fabric/lib/config.js +1 -1
- package/dist/src/functions/commands/dev-ai-runner.d.ts +19 -0
- package/dist/src/functions/commands/dev-ai-runner.js +70 -0
- package/dist/src/functions/commands/dev.js +16 -0
- package/dist/src/functions/commands/tests-coverage.js +6 -0
- package/dist/src/functions/db/db-codegen.d.ts +9 -6
- package/dist/src/functions/db/db-codegen.js +62 -5
- package/dist/src/functions/db/db-introspector.d.ts +6 -0
- package/dist/src/functions/db/local-db.d.ts +1 -0
- package/dist/src/functions/db/local-db.js +3 -0
- package/dist/src/functions/db/sqlite/sqlite-introspector.d.ts +8 -0
- package/dist/src/functions/db/sqlite/sqlite-introspector.js +27 -0
- package/dist/src/scaffold/rpc-remote.gen.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/skills/pikku-paraglide/SKILL.md +117 -0
- package/dist/src/fabric/functions/validate-core.d.ts +0 -20
- package/dist/src/fabric/functions/validate-core.js +0 -287
|
@@ -109,15 +109,15 @@
|
|
|
109
109
|
"pikkuCommandChannels": "pikkuCommandChannels",
|
|
110
110
|
"pikkuCLIEntry": "pikkuCLIEntry",
|
|
111
111
|
"pikkuCLI": "pikkuCLI",
|
|
112
|
+
"pikkuCredentials": "pikkuCredentials",
|
|
112
113
|
"pikkuConsoleFunctions": "pikkuConsoleFunctions",
|
|
113
114
|
"pikkuNodeTypes": "pikkuNodeTypes",
|
|
114
115
|
"pikkuNodesMeta": "pikkuNodesMeta",
|
|
115
|
-
"
|
|
116
|
+
"pikkuGateway": "pikkuGateway",
|
|
116
117
|
"pikkuFunctionTypesSplit": "pikkuFunctionTypesSplit",
|
|
117
118
|
"pikkuFunctionTypes": "pikkuFunctionTypes",
|
|
118
119
|
"pikkuFunctions": "pikkuFunctions",
|
|
119
120
|
"pikkuServices": "pikkuServices",
|
|
120
|
-
"pikkuGateway": "pikkuGateway",
|
|
121
121
|
"pikkuHTTPMap": "pikkuHTTPMap",
|
|
122
122
|
"pikkuCommandHTTP": "pikkuCommandHTTP",
|
|
123
123
|
"pikkuHTTPTypes": "pikkuHTTPTypes",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* This file was generated by @pikku/cli@0.12.
|
|
2
|
+
* This file was generated by @pikku/cli@0.12.50
|
|
3
3
|
*/
|
|
4
4
|
import { addSchema } from '@pikku/core/schema';
|
|
5
5
|
import * as PikkuSchemasOutput from './schemas/PikkuSchemasOutput.schema.json' with { type: 'json' };
|
|
@@ -202,14 +202,14 @@ import * as PikkuPublicAgentOutput from './schemas/PikkuPublicAgentOutput.schema
|
|
|
202
202
|
addSchema('PikkuPublicAgentOutput', PikkuPublicAgentOutput);
|
|
203
203
|
import * as PikkuCommandChannelsOutput from './schemas/PikkuCommandChannelsOutput.schema.json' with { type: 'json' };
|
|
204
204
|
addSchema('PikkuCommandChannelsOutput', PikkuCommandChannelsOutput);
|
|
205
|
-
import * as PikkuCLIEntryOutput from './schemas/PikkuCLIEntryOutput.schema.json' with { type: 'json' };
|
|
206
|
-
addSchema('PikkuCLIEntryOutput', PikkuCLIEntryOutput);
|
|
207
|
-
import * as PikkuCLIOutput from './schemas/PikkuCLIOutput.schema.json' with { type: 'json' };
|
|
208
|
-
addSchema('PikkuCLIOutput', PikkuCLIOutput);
|
|
209
205
|
import * as PikkuConsoleFunctionsOutput from './schemas/PikkuConsoleFunctionsOutput.schema.json' with { type: 'json' };
|
|
210
206
|
addSchema('PikkuConsoleFunctionsOutput', PikkuConsoleFunctionsOutput);
|
|
211
207
|
import * as PikkuNodesMetaOutput from './schemas/PikkuNodesMetaOutput.schema.json' with { type: 'json' };
|
|
212
208
|
addSchema('PikkuNodesMetaOutput', PikkuNodesMetaOutput);
|
|
209
|
+
import * as PikkuCLIEntryOutput from './schemas/PikkuCLIEntryOutput.schema.json' with { type: 'json' };
|
|
210
|
+
addSchema('PikkuCLIEntryOutput', PikkuCLIEntryOutput);
|
|
211
|
+
import * as PikkuCLIOutput from './schemas/PikkuCLIOutput.schema.json' with { type: 'json' };
|
|
212
|
+
addSchema('PikkuCLIOutput', PikkuCLIOutput);
|
|
213
213
|
import * as PikkuFunctionTypesSplitInput from './schemas/PikkuFunctionTypesSplitInput.schema.json' with { type: 'json' };
|
|
214
214
|
addSchema('PikkuFunctionTypesSplitInput', PikkuFunctionTypesSplitInput);
|
|
215
215
|
import * as PikkuFunctionTypesInput from './schemas/PikkuFunctionTypesInput.schema.json' with { type: 'json' };
|
|
@@ -236,12 +236,6 @@ import * as PikkuQueueOutput from './schemas/PikkuQueueOutput.schema.json' with
|
|
|
236
236
|
addSchema('PikkuQueueOutput', PikkuQueueOutput);
|
|
237
237
|
import * as PikkuEventsScaffoldOutput from './schemas/PikkuEventsScaffoldOutput.schema.json' with { type: 'json' };
|
|
238
238
|
addSchema('PikkuEventsScaffoldOutput', PikkuEventsScaffoldOutput);
|
|
239
|
-
import * as PikkuPublicRPCOutput from './schemas/PikkuPublicRPCOutput.schema.json' with { type: 'json' };
|
|
240
|
-
addSchema('PikkuPublicRPCOutput', PikkuPublicRPCOutput);
|
|
241
|
-
import * as PikkuRemoteRPCOutput from './schemas/PikkuRemoteRPCOutput.schema.json' with { type: 'json' };
|
|
242
|
-
addSchema('PikkuRemoteRPCOutput', PikkuRemoteRPCOutput);
|
|
243
|
-
import * as PikkuRPCOutput from './schemas/PikkuRPCOutput.schema.json' with { type: 'json' };
|
|
244
|
-
addSchema('PikkuRPCOutput', PikkuRPCOutput);
|
|
245
239
|
import * as PikkuSchedulerOutput from './schemas/PikkuSchedulerOutput.schema.json' with { type: 'json' };
|
|
246
240
|
addSchema('PikkuSchedulerOutput', PikkuSchedulerOutput);
|
|
247
241
|
import * as PikkuTriggerTypesInput from './schemas/PikkuTriggerTypesInput.schema.json' with { type: 'json' };
|
|
@@ -250,5 +244,11 @@ import * as PikkuTriggerOutput from './schemas/PikkuTriggerOutput.schema.json' w
|
|
|
250
244
|
addSchema('PikkuTriggerOutput', PikkuTriggerOutput);
|
|
251
245
|
import * as PikkuWorkflowRoutesOutput from './schemas/PikkuWorkflowRoutesOutput.schema.json' with { type: 'json' };
|
|
252
246
|
addSchema('PikkuWorkflowRoutesOutput', PikkuWorkflowRoutesOutput);
|
|
247
|
+
import * as PikkuPublicRPCOutput from './schemas/PikkuPublicRPCOutput.schema.json' with { type: 'json' };
|
|
248
|
+
addSchema('PikkuPublicRPCOutput', PikkuPublicRPCOutput);
|
|
249
|
+
import * as PikkuRemoteRPCOutput from './schemas/PikkuRemoteRPCOutput.schema.json' with { type: 'json' };
|
|
250
|
+
addSchema('PikkuRemoteRPCOutput', PikkuRemoteRPCOutput);
|
|
251
|
+
import * as PikkuRPCOutput from './schemas/PikkuRPCOutput.schema.json' with { type: 'json' };
|
|
252
|
+
addSchema('PikkuRPCOutput', PikkuRPCOutput);
|
|
253
253
|
import * as PikkuCLIConfig from './schemas/PikkuCLIConfig.schema.json' with { type: 'json' };
|
|
254
254
|
addSchema('PikkuCLIConfig', PikkuCLIConfig);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* This file was generated by @pikku/cli@0.12.
|
|
2
|
+
* This file was generated by @pikku/cli@0.12.50
|
|
3
3
|
*/
|
|
4
4
|
export { wireVariable } from '@pikku/core/variable';
|
|
5
5
|
export type { CoreVariable, VariableDefinitionMeta, VariableDefinitionsMeta } from '@pikku/core/variable';
|
package/dist/bin/pikku-bin.mjs
CHANGED
|
@@ -11,8 +11,8 @@ async function checkForUpdate() {
|
|
|
11
11
|
})
|
|
12
12
|
if (!res.ok) return
|
|
13
13
|
const { version: latest } = await res.json()
|
|
14
|
-
if (latest !== '0.12.
|
|
15
|
-
process.stderr.write(`\n Update available 0.12.
|
|
14
|
+
if (latest !== '0.12.50') {
|
|
15
|
+
process.stderr.write(`\n Update available 0.12.50 → ${latest}\n brew upgrade pikku or npm install -g @pikku/cli\n\n`)
|
|
16
16
|
}
|
|
17
17
|
} catch {}
|
|
18
18
|
}
|
|
@@ -12,5 +12,5 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { EntryGenerationContext } from './provider-adapter.js';
|
|
14
14
|
export declare function generateServerEntrySource(ctx: EntryGenerationContext): string;
|
|
15
|
-
export declare const SERVER_DOCKERFILE = "# Generated by @pikku/cli \u2014 do not edit\nFROM node:
|
|
15
|
+
export declare const SERVER_DOCKERFILE = "# Generated by @pikku/cli \u2014 do not edit\n# Full image (not -slim): carries the build toolchain so externalised deps with\n# native addons can compile from source at `npm install` time.\nFROM node:26\n\nRUN apt-get update \\\n && apt-get install -y --no-install-recommends tini ca-certificates \\\n && rm -rf /var/lib/apt/lists/*\n\nRUN groupadd -r pikku && useradd -r -g pikku -u 1001 pikku\n\nWORKDIR /app\n\n# Pikku bundles user code + first-party deps inline; only externalised deps\n# (typically empty for pure-JS apps) are listed in package.json. We ship no\n# lockfile because the bundle has already pinned everything via the\n# extracted exact-dependencies \u2014 `npm install --omit=dev` resolves the\n# leftover externals (if any) at build time.\nCOPY --chown=pikku:pikku package.json ./\nRUN npm install --omit=dev --no-audit --no-fund\n\nCOPY --chown=pikku:pikku bundle.js ./\n\nUSER pikku\nENV NODE_ENV=production\nENV PORT=8080\nEXPOSE 8080\n\nENTRYPOINT [\"/usr/bin/tini\", \"--\"]\nCMD [\"node\", \"bundle.js\"]\n";
|
|
16
16
|
export declare const SERVER_DOCKERIGNORE = "node_modules\n.git\n*.log\nmetafile.json\nexact-dependencies.json\n";
|
|
@@ -45,7 +45,9 @@ export function generateServerEntrySource(ctx) {
|
|
|
45
45
|
return lines.join('\n');
|
|
46
46
|
}
|
|
47
47
|
export const SERVER_DOCKERFILE = `# Generated by @pikku/cli — do not edit
|
|
48
|
-
|
|
48
|
+
# Full image (not -slim): carries the build toolchain so externalised deps with
|
|
49
|
+
# native addons can compile from source at \`npm install\` time.
|
|
50
|
+
FROM node:26
|
|
49
51
|
|
|
50
52
|
RUN apt-get update \\
|
|
51
53
|
&& apt-get install -y --no-install-recommends tini ca-certificates \\
|
|
@@ -180,8 +180,84 @@ export async function runValidate(startDir = process.cwd()) {
|
|
|
180
180
|
if (!pikkuConfig.clientFiles) {
|
|
181
181
|
info('pikku-config-no-client-files', 'pikku.config.json missing "clientFiles" — generated RPC client files and React Query hooks will not be written', pikkuConfigPath, lines('Add a `clientFiles` block to `pikku.config.json`.', 'Recommended values:', '"clientFiles": {', ' "rpcMapDeclarationFile": "packages/functions-sdk/src/pikku/rpc-map.gen.d.ts",', ' "reactQueryFile": "packages/functions-sdk/src/pikku/api.gen.ts"', '}', 'Those files should live in `packages/functions-sdk/src/pikku/` and are generated by Pikku.'));
|
|
182
182
|
}
|
|
183
|
+
// Each scaffold key gates generation of a public surface the Fabric console
|
|
184
|
+
// calls. Missing one means those endpoints are never generated, so the
|
|
185
|
+
// console 404s even though the functions/agents/workflows are wired. The
|
|
186
|
+
// generated file paths auto-derive from the flag (see pikku-cli-config), so
|
|
187
|
+
// setting the flag is sufficient. console/rpc/agent/workflow gate HTTP/RPC
|
|
188
|
+
// endpoints the console hits directly → error; events gates the realtime
|
|
189
|
+
// channel (feature-dependent, no template ships it yet) → warn.
|
|
190
|
+
const REQUIRED_SCAFFOLD = [
|
|
191
|
+
{
|
|
192
|
+
key: 'console',
|
|
193
|
+
severity: 'error',
|
|
194
|
+
surface: 'app introspection (console:getFunctionsMeta and friends) — the sandbox builder shows no functions',
|
|
195
|
+
value: '"no-auth"',
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
key: 'rpc',
|
|
199
|
+
severity: 'error',
|
|
200
|
+
surface: 'the generic /rpc/:name endpoint',
|
|
201
|
+
value: 'true',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
key: 'agent',
|
|
205
|
+
severity: 'error',
|
|
206
|
+
surface: 'the agent endpoints (/rpc/agent/:agentName) — the agent playground 404s',
|
|
207
|
+
value: '"no-auth"',
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
key: 'workflow',
|
|
211
|
+
severity: 'error',
|
|
212
|
+
surface: 'the workflow endpoints (/workflow/:workflowName/start) — triggering a workflow 404s',
|
|
213
|
+
value: '"no-auth"',
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
key: 'events',
|
|
217
|
+
severity: 'warn',
|
|
218
|
+
surface: 'the realtime events channel',
|
|
219
|
+
value: 'true',
|
|
220
|
+
},
|
|
221
|
+
];
|
|
222
|
+
for (const s of REQUIRED_SCAFFOLD) {
|
|
223
|
+
if (pikkuConfig.scaffold?.[s.key])
|
|
224
|
+
continue;
|
|
225
|
+
const report = s.severity === 'error' ? e : w;
|
|
226
|
+
report(`pikku-config-no-scaffold-${s.key}`, `pikku.config.json scaffold is missing "${s.key}" — ${s.surface} is never generated, so the Fabric console 404s for it`, pikkuConfigPath, lines(`Add "${s.key}" to the scaffold block in pikku.config.json:`, '"scaffold": {', ' "pikkuDir": "packages/functions/src/scaffold",', ` "${s.key}": ${s.value}`, '}', 'Then re-run codegen (`pikku all`) and restart the dev server.'));
|
|
227
|
+
}
|
|
183
228
|
}
|
|
184
229
|
const dbEngine = pikkuConfig?.db?.engine ?? 'sqlite';
|
|
230
|
+
// ── .gitignore must ignore generated/runtime artifacts ─────────────────
|
|
231
|
+
// These are regenerated on every dev boot / scaffold / codegen. Committing
|
|
232
|
+
// them lets a stale copy shadow the freshly generated one — a committed
|
|
233
|
+
// __fabric_scaffold.vite.config.mjs or .pikku-runtime breaks the sandbox dev
|
|
234
|
+
// server — and pollutes diffs. Tolerate trailing/leading slashes.
|
|
235
|
+
{
|
|
236
|
+
const requiredIgnores = [
|
|
237
|
+
'.opencode',
|
|
238
|
+
'.pikku',
|
|
239
|
+
'.pikku-runtime',
|
|
240
|
+
'.reports',
|
|
241
|
+
'__fabric_scaffold.vite.config.mjs',
|
|
242
|
+
];
|
|
243
|
+
const gitignorePath = join(root, '.gitignore');
|
|
244
|
+
const gitignoreText = await readTextSafe(gitignorePath);
|
|
245
|
+
const norm = (s) => s.replace(/^\//, '').replace(/\/$/, '');
|
|
246
|
+
const ignored = new Set((gitignoreText ?? '')
|
|
247
|
+
.split('\n')
|
|
248
|
+
.map((l) => norm(l.trim()))
|
|
249
|
+
.filter(Boolean));
|
|
250
|
+
const missing = requiredIgnores.filter((entry) => !ignored.has(norm(entry)));
|
|
251
|
+
// Generated files: accept a single `*.gen.*` glob or the explicit
|
|
252
|
+
// `*.gen.ts` + `*.gen.js` pair (the canonical scaffold uses the pair).
|
|
253
|
+
const genIgnored = ignored.has('*.gen.*') ||
|
|
254
|
+
(ignored.has('*.gen.ts') && ignored.has('*.gen.js'));
|
|
255
|
+
if (!genIgnored)
|
|
256
|
+
missing.push('*.gen.*');
|
|
257
|
+
if (missing.length > 0) {
|
|
258
|
+
w('gitignore-missing-generated', `.gitignore does not ignore Fabric generated/runtime artifacts: ${missing.join(', ')} — committing them lets a stale copy shadow the freshly generated one (e.g. a committed __fabric_scaffold.vite.config.mjs or .pikku-runtime breaks the sandbox dev server)`, gitignorePath, lines('Add these entries to .gitignore:', ...missing.map((entry) => ` ${entry}`), 'They are regenerated on every dev boot / scaffold / codegen and must never be committed.'));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
185
261
|
const rootPkgPath = join(root, 'package.json');
|
|
186
262
|
const rootPkg = await readJsonSafe(rootPkgPath);
|
|
187
263
|
if (!rootPkg) {
|
|
@@ -291,6 +367,32 @@ export async function runValidate(startDir = process.cwd()) {
|
|
|
291
367
|
if (dbEngine !== 'postgres' && fnAllDeps['@pikku/kysely-postgres']) {
|
|
292
368
|
e('fn-pkg-postgres-dep', '@pikku/kysely-postgres is in packages/functions dependencies — Fabric uses SQLite/libSQL (Turso), not PostgreSQL', fnPkgPath, 'Remove @pikku/kysely-postgres and use @pikku/kysely-sqlite with LibsqlWebDialect instead');
|
|
293
369
|
}
|
|
370
|
+
// CF worker runtime deps — must be in dependencies (not dev), every
|
|
371
|
+
// worker entry resolves them at deploy time.
|
|
372
|
+
if (!fnPkg.dependencies?.['@pikku/schema-cfworker']) {
|
|
373
|
+
e('missing-schema-cfworker', '@pikku/schema-cfworker is not in packages/functions dependencies — every Cloudflare worker entry requires it', fnPkgPath, 'Run `yarn add @pikku/schema-cfworker` in packages/functions — must be in dependencies, not devDependencies');
|
|
374
|
+
}
|
|
375
|
+
if (!fnPkg.dependencies?.['@pikku/kysely']) {
|
|
376
|
+
e('missing-pikku-kysely', '@pikku/kysely is not in packages/functions dependencies — every Cloudflare worker entry requires it (KyselySecretService)', fnPkgPath, 'Run `yarn add @pikku/kysely` in packages/functions — must be in dependencies, not devDependencies');
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
// Agent units require the AI SDK deps explicitly (not CI-injected). Gate on
|
|
380
|
+
// the generated agent meta so non-agent projects aren't flagged.
|
|
381
|
+
const agentMeta = await readJsonSafe(join(fnDir, '.pikku', 'agent', 'pikku-agent-wirings-meta.gen.json'));
|
|
382
|
+
if (agentMeta && Object.keys(agentMeta.agentsMeta ?? {}).length > 0) {
|
|
383
|
+
const fnPkgPath = join(fnDir, 'package.json');
|
|
384
|
+
if (!fnPkg?.dependencies?.['@pikku/ai-vercel']) {
|
|
385
|
+
e('missing-ai-vercel', 'Project declares agent units but @pikku/ai-vercel is not in packages/functions dependencies', fnPkgPath, 'Run `yarn add @pikku/ai-vercel` in packages/functions — must be in dependencies, not devDependencies');
|
|
386
|
+
}
|
|
387
|
+
if (!fnPkg?.dependencies?.['@ai-sdk/openai-compatible']) {
|
|
388
|
+
e('missing-ai-sdk-openai-compatible', 'Project declares agent units but @ai-sdk/openai-compatible is not in packages/functions dependencies', fnPkgPath, 'Run `yarn add @ai-sdk/openai-compatible` in packages/functions — must be in dependencies, not devDependencies');
|
|
389
|
+
}
|
|
390
|
+
// `ai` is a peer dep of @pikku/ai-vercel — not auto-installed. Without it
|
|
391
|
+
// `pikku dev` can't construct the agent runner and agents 503 with
|
|
392
|
+
// AIProviderNotConfiguredError.
|
|
393
|
+
if (!fnPkg?.dependencies?.['ai']) {
|
|
394
|
+
e('missing-ai-sdk-core', 'Project declares agent units but `ai` (the Vercel AI SDK) is not in packages/functions dependencies — it is a peer dependency of @pikku/ai-vercel and is not installed automatically', fnPkgPath, 'Run `yarn add ai` in packages/functions — must be in dependencies, not devDependencies');
|
|
395
|
+
}
|
|
294
396
|
}
|
|
295
397
|
// services.ts
|
|
296
398
|
const servicesPath = join(fnDir, 'src', 'services.ts');
|
|
@@ -2,7 +2,7 @@ import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
|
-
const DEFAULT_API_URL = '
|
|
5
|
+
const DEFAULT_API_URL = 'https://api.pikkufabric.com';
|
|
6
6
|
const projectConfigName = 'pikkufabric.config.json';
|
|
7
7
|
const authFilePath = join(homedir(), '.fabric', 'auth.json');
|
|
8
8
|
export async function findProjectConfig(startDir = process.cwd()) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Logger, VariablesService } from '@pikku/core/services';
|
|
2
|
+
import type { AIAgentRunnerService } from '@pikku/core/services';
|
|
3
|
+
/**
|
|
4
|
+
* Build the AI agent runner for `pikku dev` from env.
|
|
5
|
+
*
|
|
6
|
+
* Deployed agent units get their runner wired by the bundler; the dev server
|
|
7
|
+
* has no equivalent, so agents 503 with AIProviderNotConfiguredError unless we
|
|
8
|
+
* construct one here. When an OpenAI-compatible base URL + key are present
|
|
9
|
+
* (fabric injects LITELLM_PROXY_URL/LITELLM_API_KEY; the standard OPENAI_*
|
|
10
|
+
* vars are also honored) we point a single openai-compatible provider at it and
|
|
11
|
+
* register it under every common provider prefix. Returns undefined when no AI
|
|
12
|
+
* env is configured (agents stay disabled, with the clear downstream error) or
|
|
13
|
+
* when the AI SDK packages aren't installed in the project.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createDevAIAgentRunner({ logger, projectRoot, variables, }: {
|
|
16
|
+
logger: Logger;
|
|
17
|
+
projectRoot: string;
|
|
18
|
+
variables: VariablesService;
|
|
19
|
+
}): Promise<AIAgentRunnerService | undefined>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
// Provider prefixes a fabric/proxy baseURL fronts. Models are written as
|
|
5
|
+
// `openai/gpt-4o-mini`, `openai/deepseek-v4-flash`, etc. — the runner splits on
|
|
6
|
+
// the first `/`, so the prefix only selects the provider entry and the bare
|
|
7
|
+
// model name is forwarded to the OpenAI-compatible proxy, which routes it.
|
|
8
|
+
const PROXY_PROVIDER_NAMES = [
|
|
9
|
+
'openai',
|
|
10
|
+
'anthropic',
|
|
11
|
+
'google',
|
|
12
|
+
'gemini',
|
|
13
|
+
'deepseek',
|
|
14
|
+
'xai',
|
|
15
|
+
'litellm',
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* Build the AI agent runner for `pikku dev` from env.
|
|
19
|
+
*
|
|
20
|
+
* Deployed agent units get their runner wired by the bundler; the dev server
|
|
21
|
+
* has no equivalent, so agents 503 with AIProviderNotConfiguredError unless we
|
|
22
|
+
* construct one here. When an OpenAI-compatible base URL + key are present
|
|
23
|
+
* (fabric injects LITELLM_PROXY_URL/LITELLM_API_KEY; the standard OPENAI_*
|
|
24
|
+
* vars are also honored) we point a single openai-compatible provider at it and
|
|
25
|
+
* register it under every common provider prefix. Returns undefined when no AI
|
|
26
|
+
* env is configured (agents stay disabled, with the clear downstream error) or
|
|
27
|
+
* when the AI SDK packages aren't installed in the project.
|
|
28
|
+
*/
|
|
29
|
+
export async function createDevAIAgentRunner({ logger, projectRoot, variables, }) {
|
|
30
|
+
// Pair the URL with its matching key — coalescing each var independently could
|
|
31
|
+
// combine an OPENAI_BASE_URL with a LITELLM_API_KEY (or vice versa) and
|
|
32
|
+
// misroute or 401 every call. Take a complete OpenAI pair first, else LiteLLM.
|
|
33
|
+
const openAIBaseURL = await variables.get('OPENAI_BASE_URL');
|
|
34
|
+
const openAIApiKey = await variables.get('OPENAI_API_KEY');
|
|
35
|
+
const liteLLMBaseURL = await variables.get('LITELLM_PROXY_URL');
|
|
36
|
+
const liteLLMApiKey = await variables.get('LITELLM_API_KEY');
|
|
37
|
+
const [baseURL, apiKey] = openAIBaseURL && openAIApiKey
|
|
38
|
+
? [openAIBaseURL, openAIApiKey]
|
|
39
|
+
: liteLLMBaseURL && liteLLMApiKey
|
|
40
|
+
? [liteLLMBaseURL, liteLLMApiKey]
|
|
41
|
+
: [undefined, undefined];
|
|
42
|
+
if (!baseURL || !apiKey) {
|
|
43
|
+
logger.debug('pikku dev: no AI provider env (OPENAI_BASE_URL/OPENAI_API_KEY or LITELLM_PROXY_URL/LITELLM_API_KEY) — AI agents disabled');
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
// Resolve from the project's node_modules — the AI SDK packages are the
|
|
47
|
+
// project's deps, not the CLI's, so they share the project's `ai` version.
|
|
48
|
+
const require = createRequire(pathToFileURL(join(projectRoot, 'package.json')).href);
|
|
49
|
+
let VercelAIAgentRunner;
|
|
50
|
+
let createOpenAICompatible;
|
|
51
|
+
try {
|
|
52
|
+
;
|
|
53
|
+
({ VercelAIAgentRunner } = await import(pathToFileURL(require.resolve('@pikku/ai-vercel')).href));
|
|
54
|
+
({ createOpenAICompatible } = await import(pathToFileURL(require.resolve('@ai-sdk/openai-compatible')).href));
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
logger.warn(`pikku dev: AI provider env is set but the AI SDK packages could not be loaded (install @pikku/ai-vercel, @ai-sdk/openai-compatible, and ai) — AI agents disabled: ${error instanceof Error ? error.message : String(error)}`);
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const buildProviders = (key) => {
|
|
61
|
+
const provider = createOpenAICompatible({
|
|
62
|
+
name: 'pikku-dev',
|
|
63
|
+
baseURL,
|
|
64
|
+
apiKey: key,
|
|
65
|
+
});
|
|
66
|
+
return Object.fromEntries(PROXY_PROVIDER_NAMES.map((name) => [name, provider]));
|
|
67
|
+
};
|
|
68
|
+
logger.info(`pikku dev: AI agent runner wired to ${baseURL}`);
|
|
69
|
+
return new VercelAIAgentRunner(buildProviders(apiKey), buildProviders);
|
|
70
|
+
}
|
|
@@ -15,6 +15,7 @@ import { WebSocketServer } from 'ws';
|
|
|
15
15
|
import { InMemorySchedulerService } from '@pikku/schedule';
|
|
16
16
|
import { resolveDb, createKysely, parseDatabaseUrl, } from '../db/local-db.js';
|
|
17
17
|
import { loadUserBootstrap, loadUserModule } from './load-user-project.js';
|
|
18
|
+
import { createDevAIAgentRunner } from './dev-ai-runner.js';
|
|
18
19
|
export const dev = pikkuSessionlessFunc({
|
|
19
20
|
remote: true,
|
|
20
21
|
func: async ({ logger, config, getInspectorState, variables }, { port, watch, hmr }, { rpc }) => {
|
|
@@ -153,8 +154,23 @@ export const dev = pikkuSessionlessFunc({
|
|
|
153
154
|
// can read runs in dev without projects having to wire their own backing
|
|
154
155
|
// store.
|
|
155
156
|
const devLogger = new ConsoleLogger();
|
|
157
|
+
// Deployed agent units get their runner from the bundler; the dev server
|
|
158
|
+
// has no equivalent, so construct one from env or agents 503 with
|
|
159
|
+
// AIProviderNotConfiguredError. The template forwards injected services
|
|
160
|
+
// (`...existingServices`) so this reaches getSingletonServices().
|
|
161
|
+
// Only when the project declares agents — otherwise the runner's
|
|
162
|
+
// missing-SDK warning fires spuriously for projects with global AI env.
|
|
163
|
+
const hasAgents = Object.keys(inspectorState.agents.agentsMeta).length > 0;
|
|
164
|
+
const aiAgentRunner = hasAgents
|
|
165
|
+
? await createDevAIAgentRunner({
|
|
166
|
+
logger,
|
|
167
|
+
projectRoot: config.rootDir,
|
|
168
|
+
variables,
|
|
169
|
+
})
|
|
170
|
+
: undefined;
|
|
156
171
|
const inMemoryServices = {
|
|
157
172
|
logger: devLogger,
|
|
173
|
+
...(aiAgentRunner ? { aiAgentRunner } : {}),
|
|
158
174
|
emailService: new LocalEmailService(),
|
|
159
175
|
metaService: new LocalMetaService(pikkuDir),
|
|
160
176
|
schedulerService,
|
|
@@ -165,6 +165,12 @@ export const pikkuTestsCoverage = pikkuSessionlessFunc({
|
|
|
165
165
|
'--require',
|
|
166
166
|
'tests/tests/support/**/*.ts',
|
|
167
167
|
'tests/tests/features/**/*.feature',
|
|
168
|
+
'--format',
|
|
169
|
+
'progress',
|
|
170
|
+
// Persist the HTML report so the console can parse scenarios back out
|
|
171
|
+
// of it (readAllMeta attaches them per function).
|
|
172
|
+
'--format',
|
|
173
|
+
'html:tests/tests/reports/cucumber-report.html',
|
|
168
174
|
], { cwd: functionsDir, stdio: 'inherit', env: spawnEnv });
|
|
169
175
|
if (res.status !== 0) {
|
|
170
176
|
logger.error(`Test run failed (exit ${res.status})`);
|