@pikku/cli 0.12.85 → 0.12.88
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/console-app/assets/{index-CEv-kj3J.js → index-C3FPwdl7.js} +161 -147
- 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-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-contracts-meta.gen.json +4 -0
- 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 +4 -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 +41 -17
- package/dist/.pikku/function/pikku-functions.gen.js +3 -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/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 +3 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +3 -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.d.ts +1 -0
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +6 -0
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.json +6 -0
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +4 -0
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +5 -0
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -0
- 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 +3 -1
- package/dist/.pikku/schemas/schemas/FabricLoginInput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuUserAdminFunctionsOutput.schema.json +1 -0
- package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +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/meta/allWorkflow.gen.json +8 -2
- 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/bin/pikku.js +22 -0
- package/dist/src/fabric/fabric-commands.d.ts +6 -3
- package/dist/src/fabric/fabric-commands.js +4 -0
- package/dist/src/fabric/functions/deploy.function.js +6 -0
- package/dist/src/fabric/functions/init.function.js +2 -2
- package/dist/src/fabric/functions/llm-key.function.d.ts +4 -4
- package/dist/src/fabric/functions/login.function.d.ts +4 -0
- package/dist/src/fabric/functions/login.function.js +45 -4
- package/dist/src/fabric/functions/validate.function.js +12 -2
- package/dist/src/fabric/lib/config.d.ts +8 -0
- package/dist/src/fabric/lib/config.js +15 -1
- package/dist/src/fabric/lib/git.js +2 -2
- package/dist/src/functions/wirings/ai-agent/pikku-command-public-agent.js +6 -2
- package/dist/src/functions/wirings/ai-agent/serialize-public-agent.d.ts +5 -1
- package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +81 -48
- package/dist/src/functions/wirings/auth/pikku-command-auth.js +4 -0
- package/dist/src/functions/wirings/console/pikku-command-console-functions.js +6 -2
- package/dist/src/functions/wirings/console/serialize-console-functions.d.ts +5 -1
- package/dist/src/functions/wirings/console/serialize-console-functions.js +55 -23
- package/dist/src/functions/wirings/functions/schemas.js +10 -1
- package/dist/src/functions/wirings/graph/pikku-command-graph-wirings.js +2 -0
- package/dist/src/functions/wirings/queue/pikku-command-queue.js +10 -2
- package/dist/src/functions/wirings/realtime/pikku-command-events-scaffold.js +8 -2
- package/dist/src/functions/wirings/realtime/serialize-events-scaffold.d.ts +5 -4
- package/dist/src/functions/wirings/realtime/serialize-events-scaffold.js +17 -7
- package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.js +6 -2
- package/dist/src/functions/wirings/rpc/pikku-command-remote-rpc.js +6 -2
- package/dist/src/functions/wirings/rpc/serialize-public-rpc.d.ts +5 -1
- package/dist/src/functions/wirings/rpc/serialize-public-rpc.js +16 -5
- package/dist/src/functions/wirings/rpc/serialize-remote-rpc.d.ts +5 -1
- package/dist/src/functions/wirings/rpc/serialize-remote-rpc.js +16 -5
- package/dist/src/functions/wirings/scenarios/pikku-command-scenario-functions.js +8 -2
- package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.d.ts +5 -1
- package/dist/src/functions/wirings/scenarios/serialize-scenario-functions.js +22 -16
- package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.d.ts +1 -0
- package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.js +49 -0
- package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.d.ts +24 -0
- package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.js +283 -0
- package/dist/src/functions/wirings/webhook/pikku-command-webhook.js +6 -2
- package/dist/src/functions/wirings/webhook/serialize-webhook.d.ts +5 -8
- package/dist/src/functions/wirings/webhook/serialize-webhook.js +19 -12
- package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.js +8 -2
- package/dist/src/functions/wirings/workflow/serialize-workflow-routes.d.ts +5 -1
- package/dist/src/functions/wirings/workflow/serialize-workflow-routes.js +61 -31
- package/dist/src/functions/workflows/all.workflow.js +8 -0
- package/dist/src/utils/pikku-cli-config.js +40 -10
- package/dist/src/utils/remove-legacy-scaffold-file.d.ts +3 -0
- package/dist/src/utils/remove-legacy-scaffold-file.js +43 -0
- package/dist/src/utils/serialize-schemas.js +57 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/skills/pikku-addon/SKILL.md +1 -0
- package/skills/pikku-ai-agent/SKILL.md +2 -1
- package/skills/pikku-ai-vercel/SKILL.md +1 -0
- package/skills/pikku-better-auth/SKILL.md +57 -0
- package/skills/pikku-concepts/SKILL.md +17 -1
- package/skills/pikku-concepts/references/concept-mapping.md +11 -9
- package/skills/pikku-emails/SKILL.md +1 -0
- package/skills/pikku-fabric/SKILL.md +92 -19
- package/skills/pikku-fabric-debug/SKILL.md +112 -0
- package/skills/pikku-permissions/SKILL.md +28 -12
- package/skills/pikku-react/SKILL.md +32 -3
- package/skills/pikku-react-query/SKILL.md +38 -1
- package/skills/pikku-realtime/SKILL.md +1 -1
- package/skills/pikku-security/SKILL.md +3 -3
- package/skills/pikku-template-clone/SKILL.md +1 -0
- package/cli.schema.json +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
2
3
|
import { pikkuSessionlessFunc } from '../../../.pikku/pikku-types.gen.js';
|
|
3
4
|
import { readAuthFile, writeAuthFile, resolveApiContext, } from '../lib/config.js';
|
|
4
5
|
import { getFabricRPC } from '../lib/http.js';
|
|
@@ -8,6 +9,7 @@ export const FabricLoginInput = z.object({
|
|
|
8
9
|
token: z.string().optional(),
|
|
9
10
|
apiUrl: z.string().optional(),
|
|
10
11
|
consoleUrl: z.string().optional(),
|
|
12
|
+
browser: z.boolean().optional(),
|
|
11
13
|
});
|
|
12
14
|
export const FabricLoginOutput = z.object({
|
|
13
15
|
ok: z.boolean(),
|
|
@@ -15,6 +17,33 @@ export const FabricLoginOutput = z.object({
|
|
|
15
17
|
});
|
|
16
18
|
const POLL_INTERVAL_MS = 2000;
|
|
17
19
|
const POLL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
20
|
+
/**
|
|
21
|
+
* Best-effort browser launch. The URL is always printed first, so a failure
|
|
22
|
+
* here costs the user a click, not the login — hence a warning rather than a
|
|
23
|
+
* throw. Skipped over SSH and in CI, where opening a browser on the wrong
|
|
24
|
+
* machine is worse than not opening one.
|
|
25
|
+
*/
|
|
26
|
+
function openBrowser(url) {
|
|
27
|
+
if (process.env.SSH_TTY || process.env.CI)
|
|
28
|
+
return;
|
|
29
|
+
const cmd = process.platform === 'darwin'
|
|
30
|
+
? 'open'
|
|
31
|
+
: process.platform === 'win32'
|
|
32
|
+
? 'start'
|
|
33
|
+
: 'xdg-open';
|
|
34
|
+
try {
|
|
35
|
+
const child = spawn(cmd, [url], {
|
|
36
|
+
stdio: 'ignore',
|
|
37
|
+
detached: true,
|
|
38
|
+
shell: process.platform === 'win32',
|
|
39
|
+
});
|
|
40
|
+
child.on('error', (err) => console.warn(`[fabric] could not open a browser (${err.message}) — open the URL above.`));
|
|
41
|
+
child.unref();
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.warn(`[fabric] could not open a browser (${err.message}) — open the URL above.`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
18
47
|
/**
|
|
19
48
|
* Device-authorization-style CLI login. Server mints a short code, user types
|
|
20
49
|
* it on the console's /cli-auth page (logging in via Better Auth if not already
|
|
@@ -25,7 +54,7 @@ export const FabricLogin = pikkuSessionlessFunc({
|
|
|
25
54
|
description: 'Authenticate the CLI against fabric-api via a one-time code.',
|
|
26
55
|
input: FabricLoginInput,
|
|
27
56
|
output: FabricLoginOutput,
|
|
28
|
-
func: async (_services, { apiKey, token, apiUrl: apiUrlOverride, consoleUrl: consoleUrlOverride }) => {
|
|
57
|
+
func: async (_services, { apiKey, token, apiUrl: apiUrlOverride, consoleUrl: consoleUrlOverride, browser, }) => {
|
|
29
58
|
const ctx = await resolveApiContext({ apiUrlOverride });
|
|
30
59
|
const apiUrl = ctx.apiUrl;
|
|
31
60
|
// Static-token fast paths — useful for CI and "I already have a token".
|
|
@@ -40,14 +69,21 @@ export const FabricLogin = pikkuSessionlessFunc({
|
|
|
40
69
|
const consoleUrl = (consoleUrlOverride ?? deriveConsoleUrl(apiUrl)).replace(/\/$/, '');
|
|
41
70
|
const rpc = getFabricRPC({ apiUrl, token: null });
|
|
42
71
|
const { code, expiresAt } = await rpc.invoke('requestCliAuth');
|
|
72
|
+
// Deliberately no ?code= — the browser opens the page, but the code is
|
|
73
|
+
// typed. That hand-off is what proves the person authorizing the token is
|
|
74
|
+
// the person who ran this command; a link carrying its own code is a
|
|
75
|
+
// one-click grant for anyone who can put it in front of a signed-in user.
|
|
76
|
+
const authUrl = `${consoleUrl}/cli-auth`;
|
|
43
77
|
console.log('');
|
|
44
|
-
console.log(' Enter this code
|
|
45
|
-
console.log(` ${
|
|
78
|
+
console.log(' Enter this code to finish signing in:');
|
|
79
|
+
console.log(` ${code}`);
|
|
46
80
|
console.log('');
|
|
47
|
-
console.log(`
|
|
81
|
+
console.log(` at ${authUrl}`);
|
|
48
82
|
console.log(` (expires ${new Date(expiresAt).toLocaleTimeString()})`);
|
|
49
83
|
console.log('');
|
|
50
84
|
console.log(' Waiting for confirmation…');
|
|
85
|
+
if (browser !== false)
|
|
86
|
+
openBrowser(authUrl);
|
|
51
87
|
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
52
88
|
while (Date.now() < deadline) {
|
|
53
89
|
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
@@ -55,6 +91,11 @@ export const FabricLogin = pikkuSessionlessFunc({
|
|
|
55
91
|
if (result.status === 'expired') {
|
|
56
92
|
throw new Error('Code expired before confirmation. Run `pikku fabric login` again.');
|
|
57
93
|
}
|
|
94
|
+
// Distinct from expired: the user pressed Cancel, so stop immediately
|
|
95
|
+
// rather than sitting here until the TTL runs out.
|
|
96
|
+
if (result.status === 'rejected') {
|
|
97
|
+
throw new Error('Sign-in was cancelled in the browser. Run `pikku fabric login` again to retry.');
|
|
98
|
+
}
|
|
58
99
|
if (result.status === 'confirmed' && result.token) {
|
|
59
100
|
const auth = await readAuthFile();
|
|
60
101
|
auth.tokens[apiUrl] = result.token;
|
|
@@ -605,8 +605,12 @@ export async function runValidate(startDir = process.cwd()) {
|
|
|
605
605
|
const pikkuGenDir = join(fnDir, '.pikku');
|
|
606
606
|
const typesGenTs = join(pikkuGenDir, 'pikku-types.gen.ts');
|
|
607
607
|
const typesGenJs = join(pikkuGenDir, 'pikku-types.gen.js');
|
|
608
|
+
// A warning, not an error: the wrapper is gitignored and recreated by
|
|
609
|
+
// the sandbox entrypoint and `fabric smoke`, so a freshly cloned or
|
|
610
|
+
// freshly scaffolded project never has it. As an error, "validate must
|
|
611
|
+
// pass clean" was unachievable from a clean checkout.
|
|
608
612
|
if (existsSync(typesGenTs) && !existsSync(typesGenJs)) {
|
|
609
|
-
|
|
613
|
+
w('pikku-types-js-wrapper-missing', 'packages/functions/.pikku/pikku-types.gen.js is missing — the sandbox runs pikku dev via bare Node.js, which cannot load .ts files; without the .js wrapper it crashes with "Cannot find module \'#pikku/pikku-types.gen.js\'" on every restart', typesGenTs, lines('Create packages/functions/.pikku/pikku-types.gen.js:', " import './pikku-types.gen.ts';", " export * from './pikku-types.gen.ts';", 'Or run `pikku fabric smoke` to test the full boot locally (it creates the wrappers).', 'This file is gitignored — it must be recreated after every `pikku all` run.'));
|
|
610
614
|
}
|
|
611
615
|
}
|
|
612
616
|
const fnAllDeps = {
|
|
@@ -655,7 +659,13 @@ export async function runValidate(startDir = process.cwd()) {
|
|
|
655
659
|
const usesLibsql = servicesText.includes('@pikku/kysely-sqlite') ||
|
|
656
660
|
servicesText.includes('LibsqlWebDialect');
|
|
657
661
|
const usesProcessEnv = /\bprocess\.env\.[A-Z_]/.test(servicesText);
|
|
658
|
-
|
|
662
|
+
// Only a services.ts that BUILDS its own client can pick the wrong
|
|
663
|
+
// dialect. The starter template takes kysely from injection (pikku dev
|
|
664
|
+
// supplies node:sqlite, the Worker supplies libsql) and constructs
|
|
665
|
+
// nothing — flagging it told every fresh scaffold to import a dialect it
|
|
666
|
+
// must not use.
|
|
667
|
+
const constructsKysely = /new\s+Kysely\s*[<(]/.test(servicesText);
|
|
668
|
+
if (dbEngine !== 'postgres' && usesKysely && constructsKysely && !usesLibsql) {
|
|
659
669
|
e('services-wrong-db-adapter', 'services.ts uses Kysely but not LibsqlWebDialect — Fabric injects a Turso/libSQL DATABASE_URL at runtime, not a PostgreSQL URL', servicesPath, 'Import LibsqlWebDialect from @pikku/kysely-sqlite and replace the dialect: new Kysely({ dialect: new LibsqlWebDialect({ url: databaseUrl }) })');
|
|
660
670
|
}
|
|
661
671
|
if (usesProcessEnv) {
|
|
@@ -42,6 +42,14 @@ export declare function findProjectConfig(startDir?: string): Promise<{
|
|
|
42
42
|
path: string;
|
|
43
43
|
config: ProjectConfig;
|
|
44
44
|
} | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Templates ship `pikkufabric.config.json` with a `__PROJECT_ID__` placeholder
|
|
47
|
+
* so the file's shape is visible before the repo is linked. A placeholder is
|
|
48
|
+
* not a link: without this, `fabric init` on a fresh scaffold reports
|
|
49
|
+
* "Already linked: __PROJECT_ID__" and every other command sends the
|
|
50
|
+
* placeholder to the API as if it were a real id.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isLinkedProjectId(projectId?: string | null): boolean;
|
|
45
53
|
export declare function writeProjectConfig(cwd: string, config: ProjectConfig): Promise<string>;
|
|
46
54
|
export declare function readAuthFile(): Promise<AuthFile>;
|
|
47
55
|
export declare function writeAuthFile(file: AuthFile): Promise<void>;
|
|
@@ -19,6 +19,18 @@ export async function findProjectConfig(startDir = process.cwd()) {
|
|
|
19
19
|
dir = parent;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Templates ship `pikkufabric.config.json` with a `__PROJECT_ID__` placeholder
|
|
24
|
+
* so the file's shape is visible before the repo is linked. A placeholder is
|
|
25
|
+
* not a link: without this, `fabric init` on a fresh scaffold reports
|
|
26
|
+
* "Already linked: __PROJECT_ID__" and every other command sends the
|
|
27
|
+
* placeholder to the API as if it were a real id.
|
|
28
|
+
*/
|
|
29
|
+
export function isLinkedProjectId(projectId) {
|
|
30
|
+
if (!projectId)
|
|
31
|
+
return false;
|
|
32
|
+
return !/^__.*__$/.test(projectId);
|
|
33
|
+
}
|
|
22
34
|
export async function writeProjectConfig(cwd, config) {
|
|
23
35
|
const path = join(cwd, projectConfigName);
|
|
24
36
|
await mkdir(dirname(path), { recursive: true });
|
|
@@ -57,6 +69,8 @@ export async function resolveApiContext(opts = {}) {
|
|
|
57
69
|
return {
|
|
58
70
|
apiUrl,
|
|
59
71
|
token: auth.tokens[apiUrl] ?? null,
|
|
60
|
-
projectId: projectFile?.config.projectId
|
|
72
|
+
projectId: isLinkedProjectId(projectFile?.config.projectId)
|
|
73
|
+
? projectFile.config.projectId
|
|
74
|
+
: null,
|
|
61
75
|
};
|
|
62
76
|
}
|
|
@@ -136,8 +136,8 @@ export async function assertNamedBranchDeploySafety(branch, cwd) {
|
|
|
136
136
|
try {
|
|
137
137
|
head = await localBranchHeadSha(branch, cwd);
|
|
138
138
|
}
|
|
139
|
-
catch {
|
|
140
|
-
throw new Error(`Deployment blocked: local branch ${branch} does not exist.\nFetch or create it before deploying.`);
|
|
139
|
+
catch (error) {
|
|
140
|
+
throw new Error(`Deployment blocked: local branch ${branch} does not exist (${error.message}).\nFetch or create it before deploying.`);
|
|
141
141
|
}
|
|
142
142
|
const upstream = await upstreamForBranch(branch, cwd);
|
|
143
143
|
if (!upstream) {
|
|
@@ -2,12 +2,16 @@ 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 { serializePublicAgent } from './serialize-public-agent.js';
|
|
6
7
|
export const pikkuPublicAgent = pikkuSessionlessFunc({
|
|
7
8
|
func: async ({ logger, config }) => {
|
|
8
|
-
if (config.scaffold?.agent) {
|
|
9
|
+
if (config.scaffold?.agent && config.publicAgentSchemasFile) {
|
|
9
10
|
const pathToPikkuTypes = getFileImportRelativePath(config.publicAgentFile, config.typesDeclarationFile, config.packageMappings);
|
|
10
|
-
|
|
11
|
+
const { schemas, functions } = serializePublicAgent(pathToPikkuTypes, config.scaffold.agent === 'auth', config.globalHTTPPrefix || '');
|
|
12
|
+
await writeFileInDir(logger, config.publicAgentSchemasFile, schemas);
|
|
13
|
+
await writeFileInDir(logger, config.publicAgentFile, functions);
|
|
14
|
+
await removeLegacyScaffoldFile(config.publicAgentFile);
|
|
11
15
|
return true;
|
|
12
16
|
}
|
|
13
17
|
return false;
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface PublicAgentGenOutput {
|
|
2
|
+
schemas: string;
|
|
3
|
+
functions: string;
|
|
4
|
+
}
|
|
5
|
+
export declare const serializePublicAgent: (pathToPikkuTypes: string, requireAuth?: boolean, globalHTTPPrefix?: string) => PublicAgentGenOutput;
|
|
@@ -1,28 +1,69 @@
|
|
|
1
1
|
export const serializePublicAgent = (pathToPikkuTypes, requireAuth = true, globalHTTPPrefix = '') => {
|
|
2
2
|
const authFlag = requireAuth ? 'true' : 'false';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -10,8 +10,16 @@ export const pikkuCommandQueue = pikkuSessionlessFunc({
|
|
|
10
10
|
const visitState = await getInspectorState();
|
|
11
11
|
const { queueWorkersWiringFile, queueWorkersWiringMetaFile, queueWorkersWiringMetaJsonFile, packageMappings, schema, } = config;
|
|
12
12
|
const { queueWorkers } = visitState;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
// Gate on the meta alone, never on `files`. Workflows synthesise their own
|
|
14
|
+
// `wf-orchestrator-*` / `wf-step-*` queue meta during post-processing, and
|
|
15
|
+
// those entries have no declaring source file — so a project that uses
|
|
16
|
+
// workflows but hand-declares no `wireQueueWorker` has a full `meta` and an
|
|
17
|
+
// empty `files`. Bailing on `files.size === 0` there skipped writing the
|
|
18
|
+
// queue meta entirely, which left `pikkuState(queue,meta)` empty at runtime:
|
|
19
|
+
// `getOrchestratorQueueName()` then never found a per-workflow queue and
|
|
20
|
+
// EVERY workflow fell back to the single shared `pikku-workflow-orchestrator`,
|
|
21
|
+
// where one slow job head-of-line-blocks everything queued behind it.
|
|
22
|
+
if (Object.keys(queueWorkers.meta).length === 0) {
|
|
15
23
|
return undefined;
|
|
16
24
|
}
|
|
17
25
|
const fullMeta = serializeQueueMeta(queueWorkers.meta);
|