@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
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate the user-management functions into the project scaffold: the
|
|
3
|
+
* directory read plus the writes that wrap better-auth's `admin()` endpoints.
|
|
4
|
+
*
|
|
5
|
+
* Scaffolded rather than shipped in an addon because managing users is ordinary
|
|
6
|
+
* application behaviour: an app should not have to install the console — which
|
|
7
|
+
* exists to be served alongside a running dev server — just to list or ban its
|
|
8
|
+
* own users. The console UI calls these same functions when it is present.
|
|
9
|
+
*
|
|
10
|
+
* Each function is gated on its own `admin:users:*` scope. The writes are only
|
|
11
|
+
* meaningful where `admin()` is wired, which is why this file is generated only
|
|
12
|
+
* in that case.
|
|
13
|
+
*
|
|
14
|
+
* Emitted as two files. The schemas are zod, and the inspector reads a zod
|
|
15
|
+
* schema by importing the module that declares it — which it cannot do for the
|
|
16
|
+
* functions file, whose relative pikku-types import per-unit deploy codegen
|
|
17
|
+
* rewrites. Keeping the schemas in a sibling module that imports nothing but
|
|
18
|
+
* zod sidesteps that entirely.
|
|
19
|
+
*/
|
|
20
|
+
export const serializeUserAdminFunctions = (pathToPikkuTypes, requireAuth = true) => {
|
|
21
|
+
const authFlag = requireAuth ? 'true' : 'false';
|
|
22
|
+
const schemas = `/**
|
|
23
|
+
* Auto-generated user management schemas
|
|
24
|
+
* Do not edit manually - regenerate with 'npx pikku'
|
|
25
|
+
*/
|
|
26
|
+
import { z } from 'zod'
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A user, as the directory sees one. Ban state is optional because those
|
|
30
|
+
* columns belong to better-auth's \`admin()\` plugin: a host without it reports
|
|
31
|
+
* no ban state at all, which a client can render as "unknown" rather than as a
|
|
32
|
+
* misleading "not banned".
|
|
33
|
+
*/
|
|
34
|
+
export const User = z.object({
|
|
35
|
+
id: z.string(),
|
|
36
|
+
email: z.string(),
|
|
37
|
+
name: z.string().optional(),
|
|
38
|
+
image: z.string().optional(),
|
|
39
|
+
createdAt: z.string().optional(),
|
|
40
|
+
banned: z.boolean().optional(),
|
|
41
|
+
banReason: z.string().optional(),
|
|
42
|
+
banExpires: z.string().optional(),
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
export const ListUsersInput = z.object({
|
|
46
|
+
search: z.string().optional(),
|
|
47
|
+
limit: z.number().int().positive().optional(),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
export const ListUsersOutput = z.object({
|
|
51
|
+
users: z.array(User),
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
export const CreateUserInput = z.object({
|
|
55
|
+
email: z.string(),
|
|
56
|
+
password: z.string(),
|
|
57
|
+
name: z.string().optional(),
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
export const CreateUserOutput = z.object({
|
|
61
|
+
userId: z.string(),
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
/** Every write targets one user by id. */
|
|
65
|
+
export const UserRef = z.object({
|
|
66
|
+
userId: z.string(),
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
export const SetUserBannedInput = z.object({
|
|
70
|
+
userId: z.string(),
|
|
71
|
+
banned: z.boolean(),
|
|
72
|
+
reason: z.string().optional(),
|
|
73
|
+
expiresInSeconds: z.number().int().positive().optional(),
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
export const SetUserPasswordInput = z.object({
|
|
77
|
+
userId: z.string(),
|
|
78
|
+
newPassword: z.string(),
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
export const Success = z.object({
|
|
82
|
+
success: z.boolean(),
|
|
83
|
+
})
|
|
84
|
+
`;
|
|
85
|
+
const functions = `/**
|
|
86
|
+
* Auto-generated user management functions
|
|
87
|
+
* Do not edit manually - regenerate with 'npx pikku'
|
|
88
|
+
*/
|
|
89
|
+
import { pikkuFunc, wireScope } from '${pathToPikkuTypes}'
|
|
90
|
+
import { callAdminApi } from '@pikku/better-auth'
|
|
91
|
+
import {
|
|
92
|
+
CreateUserInput,
|
|
93
|
+
CreateUserOutput,
|
|
94
|
+
ListUsersInput,
|
|
95
|
+
ListUsersOutput,
|
|
96
|
+
SetUserBannedInput,
|
|
97
|
+
SetUserPasswordInput,
|
|
98
|
+
Success,
|
|
99
|
+
UserRef,
|
|
100
|
+
} from './user-admin.schemas.gen.js'
|
|
101
|
+
|
|
102
|
+
// pikku requires every declaration of a shared scope root to be identical, so
|
|
103
|
+
// this is the whole \`admin\` tree — not just the leaves gated below. It must stay
|
|
104
|
+
// byte-identical to ADMIN_SCOPE_TREE in @pikku/better-auth and to the copy in
|
|
105
|
+
// @pikku/addon-console, or codegen fails with conflicting declarations.
|
|
106
|
+
wireScope({
|
|
107
|
+
admin: {
|
|
108
|
+
displayName: 'Administration',
|
|
109
|
+
description: 'Capabilities that act on the application as a whole',
|
|
110
|
+
scopes: {
|
|
111
|
+
impersonate: { description: 'Act as another user' },
|
|
112
|
+
credentials: {
|
|
113
|
+
description: 'Application-wide credentials',
|
|
114
|
+
scopes: {
|
|
115
|
+
link: { description: 'Bind a shared credential for every user' },
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
users: {
|
|
119
|
+
description: 'The user directory',
|
|
120
|
+
scopes: {
|
|
121
|
+
list: { description: 'List and search users' },
|
|
122
|
+
create: { description: 'Create users out of band' },
|
|
123
|
+
ban: { description: 'Ban and unban users' },
|
|
124
|
+
remove: { description: 'Delete users and all their data' },
|
|
125
|
+
sessions: { description: "Revoke a user's sessions" },
|
|
126
|
+
password: { description: "Set a user's password" },
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Synthetic principals — the platform credential owner, fabric service users
|
|
135
|
+
* and agent actors — are not people, so they never belong in a directory a
|
|
136
|
+
* human picks from.
|
|
137
|
+
*/
|
|
138
|
+
const isPerson = (row: any) =>
|
|
139
|
+
row.fabric !== true && row.actor !== true && row.id !== 'pikku-platform'
|
|
140
|
+
|
|
141
|
+
export const pikkuAdminListUsers = pikkuFunc({
|
|
142
|
+
tags: ['pikku'],
|
|
143
|
+
title: 'List Users',
|
|
144
|
+
description:
|
|
145
|
+
'Lists and searches the user directory, read through the auth adapter rather than an admin endpoint so it works on any database better-auth supports.',
|
|
146
|
+
expose: true,
|
|
147
|
+
auth: ${authFlag},
|
|
148
|
+
scopes: ['admin:users:list'],
|
|
149
|
+
input: ListUsersInput,
|
|
150
|
+
output: ListUsersOutput,
|
|
151
|
+
func: async ({ auth }, { search, limit }) => {
|
|
152
|
+
const ctx = await (await auth()).$context
|
|
153
|
+
const rows = (await ctx.adapter.findMany({
|
|
154
|
+
model: 'user',
|
|
155
|
+
limit: limit ?? 200,
|
|
156
|
+
where: search
|
|
157
|
+
? [{ field: 'email', operator: 'contains', value: search }]
|
|
158
|
+
: undefined,
|
|
159
|
+
})) as any[]
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
users: rows.filter(isPerson).map((row) => ({
|
|
163
|
+
id: row.id,
|
|
164
|
+
email: row.email,
|
|
165
|
+
name: row.name ?? undefined,
|
|
166
|
+
image: row.image ?? undefined,
|
|
167
|
+
createdAt: row.createdAt
|
|
168
|
+
? new Date(row.createdAt).toISOString()
|
|
169
|
+
: undefined,
|
|
170
|
+
banned: typeof row.banned === 'boolean' ? row.banned : undefined,
|
|
171
|
+
banReason: row.banReason ?? undefined,
|
|
172
|
+
banExpires: row.banExpires
|
|
173
|
+
? new Date(row.banExpires).toISOString()
|
|
174
|
+
: undefined,
|
|
175
|
+
})),
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
export const pikkuAdminCreateUser = pikkuFunc({
|
|
181
|
+
tags: ['pikku'],
|
|
182
|
+
title: 'Create User',
|
|
183
|
+
description:
|
|
184
|
+
'Creates a user directly, for provisioning an account out of band rather than through your sign-up flow. better-auth enforces the configured password bounds and rejects a duplicate email.',
|
|
185
|
+
expose: true,
|
|
186
|
+
auth: ${authFlag},
|
|
187
|
+
scopes: ['admin:users:create'],
|
|
188
|
+
input: CreateUserInput,
|
|
189
|
+
output: CreateUserOutput,
|
|
190
|
+
func: async ({ auth }, { email, password, name }, { http }) => {
|
|
191
|
+
const created: any = await callAdminApi(auth, http, (api, headers) =>
|
|
192
|
+
api.createUser!({
|
|
193
|
+
body: { email, password, name: name ?? email },
|
|
194
|
+
headers,
|
|
195
|
+
})
|
|
196
|
+
)
|
|
197
|
+
return { userId: created?.user?.id ?? created?.id }
|
|
198
|
+
},
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
export const pikkuAdminSetUserBanned = pikkuFunc({
|
|
202
|
+
tags: ['pikku'],
|
|
203
|
+
title: 'Ban or Unban User',
|
|
204
|
+
description:
|
|
205
|
+
'Bans a user — revoking their sessions and blocking sign-in — or lifts an existing ban. An expiry lets the ban lapse on its own; without one it holds until it is lifted.',
|
|
206
|
+
expose: true,
|
|
207
|
+
auth: ${authFlag},
|
|
208
|
+
scopes: ['admin:users:ban'],
|
|
209
|
+
input: SetUserBannedInput,
|
|
210
|
+
output: Success,
|
|
211
|
+
func: async (
|
|
212
|
+
{ auth },
|
|
213
|
+
{ userId, banned, reason, expiresInSeconds },
|
|
214
|
+
{ http }
|
|
215
|
+
) => {
|
|
216
|
+
await callAdminApi(auth, http, (api, headers) =>
|
|
217
|
+
banned
|
|
218
|
+
? api.banUser!({
|
|
219
|
+
body: { userId, banReason: reason, banExpiresIn: expiresInSeconds },
|
|
220
|
+
headers,
|
|
221
|
+
})
|
|
222
|
+
: api.unbanUser!({ body: { userId }, headers })
|
|
223
|
+
)
|
|
224
|
+
return { success: true }
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
export const pikkuAdminRemoveUser = pikkuFunc({
|
|
229
|
+
tags: ['pikku'],
|
|
230
|
+
title: 'Remove User',
|
|
231
|
+
description:
|
|
232
|
+
'Permanently deletes a user along with their sessions and linked accounts. Cannot be undone.',
|
|
233
|
+
expose: true,
|
|
234
|
+
auth: ${authFlag},
|
|
235
|
+
scopes: ['admin:users:remove'],
|
|
236
|
+
input: UserRef,
|
|
237
|
+
output: Success,
|
|
238
|
+
func: async ({ auth }, { userId }, { http }) => {
|
|
239
|
+
await callAdminApi(auth, http, (api, headers) =>
|
|
240
|
+
api.removeUser!({ body: { userId }, headers })
|
|
241
|
+
)
|
|
242
|
+
return { success: true }
|
|
243
|
+
},
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
export const pikkuAdminRevokeUserSessions = pikkuFunc({
|
|
247
|
+
tags: ['pikku'],
|
|
248
|
+
title: 'Revoke User Sessions',
|
|
249
|
+
description:
|
|
250
|
+
'Signs a user out of every device by deleting all of their sessions. They keep their account and can sign in again.',
|
|
251
|
+
expose: true,
|
|
252
|
+
auth: ${authFlag},
|
|
253
|
+
scopes: ['admin:users:sessions'],
|
|
254
|
+
input: UserRef,
|
|
255
|
+
output: Success,
|
|
256
|
+
func: async ({ auth }, { userId }, { http }) => {
|
|
257
|
+
await callAdminApi(auth, http, (api, headers) =>
|
|
258
|
+
api.revokeUserSessions!({ body: { userId }, headers })
|
|
259
|
+
)
|
|
260
|
+
return { success: true }
|
|
261
|
+
},
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
export const pikkuAdminSetUserPassword = pikkuFunc({
|
|
265
|
+
tags: ['pikku'],
|
|
266
|
+
title: "Set User's Password",
|
|
267
|
+
description:
|
|
268
|
+
'Sets a user password out of band, for when they cannot complete a reset themselves. better-auth enforces the configured length bounds.',
|
|
269
|
+
expose: true,
|
|
270
|
+
auth: ${authFlag},
|
|
271
|
+
scopes: ['admin:users:password'],
|
|
272
|
+
input: SetUserPasswordInput,
|
|
273
|
+
output: Success,
|
|
274
|
+
func: async ({ auth }, { userId, newPassword }, { http }) => {
|
|
275
|
+
await callAdminApi(auth, http, (api, headers) =>
|
|
276
|
+
api.setUserPassword!({ body: { userId, newPassword }, headers })
|
|
277
|
+
)
|
|
278
|
+
return { success: true }
|
|
279
|
+
},
|
|
280
|
+
})
|
|
281
|
+
`;
|
|
282
|
+
return { schemas, functions };
|
|
283
|
+
};
|
|
@@ -2,6 +2,7 @@ import { pikkuSessionlessFunc } from '#pikku';
|
|
|
2
2
|
import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
|
|
3
3
|
import { writeFileInDir } from '../../../utils/file-writer.js';
|
|
4
4
|
import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
|
|
5
|
+
import { removeLegacyScaffoldFile } from '../../../utils/remove-legacy-scaffold-file.js';
|
|
5
6
|
import { serializeWebhook } from './serialize-webhook.js';
|
|
6
7
|
export const pikkuWebhook = pikkuSessionlessFunc({
|
|
7
8
|
func: async ({ logger, config, variables }) => {
|
|
@@ -9,9 +10,12 @@ export const pikkuWebhook = pikkuSessionlessFunc({
|
|
|
9
10
|
if (deployCodegenFlag === '1') {
|
|
10
11
|
return false;
|
|
11
12
|
}
|
|
12
|
-
if (config.webhookWorkersFile) {
|
|
13
|
+
if (config.webhookWorkersFile && config.webhookSchemasFile) {
|
|
13
14
|
const pathToPikkuTypes = getFileImportRelativePath(config.webhookWorkersFile, config.typesDeclarationFile, config.packageMappings);
|
|
14
|
-
|
|
15
|
+
const { schemas, functions } = serializeWebhook(pathToPikkuTypes);
|
|
16
|
+
await writeFileInDir(logger, config.webhookSchemasFile, schemas);
|
|
17
|
+
await writeFileInDir(logger, config.webhookWorkersFile, functions);
|
|
18
|
+
await removeLegacyScaffoldFile(config.webhookWorkersFile);
|
|
15
19
|
return true;
|
|
16
20
|
}
|
|
17
21
|
return false;
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
+
export interface WebhookGenOutput {
|
|
2
|
+
schemas: string;
|
|
3
|
+
functions: string;
|
|
4
|
+
}
|
|
1
5
|
/**
|
|
2
6
|
* Generate the outgoing webhook delivery queue worker.
|
|
3
7
|
*
|
|
4
|
-
* The payload type is an inline TS literal (not zod): the inspector imports a
|
|
5
|
-
* zod schema's declaring module at codegen time, and the wiring file's
|
|
6
|
-
* pikku-types import can go stale between deploy per-unit codegen runs, which
|
|
7
|
-
* would fail schema generation before the wiring gets rewritten. TS-based
|
|
8
|
-
* schema generation goes through the type checker instead, so it never
|
|
9
|
-
* imports this file.
|
|
10
|
-
*
|
|
11
8
|
* The handler is inlined: it is wired exactly once, so a named export would
|
|
12
9
|
* only be indirection.
|
|
13
10
|
*/
|
|
14
|
-
export declare const serializeWebhook: (pathToPikkuTypes: string) =>
|
|
11
|
+
export declare const serializeWebhook: (pathToPikkuTypes: string) => WebhookGenOutput;
|
|
@@ -1,34 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generate the outgoing webhook delivery queue worker.
|
|
3
3
|
*
|
|
4
|
-
* The payload type is an inline TS literal (not zod): the inspector imports a
|
|
5
|
-
* zod schema's declaring module at codegen time, and the wiring file's
|
|
6
|
-
* pikku-types import can go stale between deploy per-unit codegen runs, which
|
|
7
|
-
* would fail schema generation before the wiring gets rewritten. TS-based
|
|
8
|
-
* schema generation goes through the type checker instead, so it never
|
|
9
|
-
* imports this file.
|
|
10
|
-
*
|
|
11
4
|
* The handler is inlined: it is wired exactly once, so a named export would
|
|
12
5
|
* only be indirection.
|
|
13
6
|
*/
|
|
14
7
|
export const serializeWebhook = (pathToPikkuTypes) => {
|
|
15
|
-
|
|
8
|
+
const schemas = `/**
|
|
9
|
+
* Auto-generated outgoing webhook delivery schemas
|
|
10
|
+
* Do not edit manually - regenerate with 'npx pikku'
|
|
11
|
+
*/
|
|
12
|
+
import { z } from 'zod'
|
|
13
|
+
|
|
14
|
+
export const WebhookDelivery = z.object({
|
|
15
|
+
url: z.string(),
|
|
16
|
+
event: z.string().optional(),
|
|
17
|
+
body: z.string(),
|
|
18
|
+
headers: z.record(z.string(), z.string()),
|
|
19
|
+
deliveryId: z.string().optional(),
|
|
20
|
+
})
|
|
21
|
+
`;
|
|
22
|
+
const functions = `/**
|
|
16
23
|
* Auto-generated outgoing webhook delivery queue worker
|
|
17
24
|
* Do not edit manually - regenerate with 'npx pikku'
|
|
18
25
|
*/
|
|
19
26
|
import { pikkuSessionlessFunc, wireQueueWorker } from '${pathToPikkuTypes}'
|
|
20
27
|
import { pikkuWebhookWorkerFunc } from '@pikku/core/services'
|
|
28
|
+
import { WebhookDelivery } from './webhook.schemas.gen.js'
|
|
21
29
|
|
|
22
30
|
wireQueueWorker({
|
|
23
31
|
name: 'pikku-outgoing-webhooks',
|
|
24
32
|
tags: ['pikku'],
|
|
25
|
-
func: pikkuSessionlessFunc
|
|
26
|
-
{ url: string, event?: string, body: string, headers: Record<string, string>, deliveryId?: string },
|
|
27
|
-
void
|
|
28
|
-
>({
|
|
33
|
+
func: pikkuSessionlessFunc({
|
|
29
34
|
tags: ['pikku'],
|
|
35
|
+
input: WebhookDelivery,
|
|
30
36
|
func: async (services, data) => pikkuWebhookWorkerFunc(services, data),
|
|
31
37
|
}),
|
|
32
38
|
})
|
|
33
39
|
`;
|
|
40
|
+
return { schemas, functions };
|
|
34
41
|
};
|
|
@@ -2,12 +2,18 @@ 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 { serializeWorkflowRoutes } from './serialize-workflow-routes.js';
|
|
6
7
|
export const pikkuWorkflowRoutes = pikkuSessionlessFunc({
|
|
7
8
|
func: async ({ logger, config }) => {
|
|
8
|
-
if (config.scaffold?.workflow &&
|
|
9
|
+
if (config.scaffold?.workflow &&
|
|
10
|
+
config.workflowRoutesFile &&
|
|
11
|
+
config.workflowRoutesSchemasFile) {
|
|
9
12
|
const pathToPikkuTypes = getFileImportRelativePath(config.workflowRoutesFile, config.typesDeclarationFile, config.packageMappings);
|
|
10
|
-
|
|
13
|
+
const { schemas, functions } = serializeWorkflowRoutes(pathToPikkuTypes, config.scaffold?.workflow === 'auth');
|
|
14
|
+
await writeFileInDir(logger, config.workflowRoutesSchemasFile, schemas);
|
|
15
|
+
await writeFileInDir(logger, config.workflowRoutesFile, functions);
|
|
16
|
+
await removeLegacyScaffoldFile(config.workflowRoutesFile);
|
|
11
17
|
return true;
|
|
12
18
|
}
|
|
13
19
|
return false;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
export interface WorkflowRoutesGenOutput {
|
|
2
|
+
schemas: string;
|
|
3
|
+
functions: string;
|
|
4
|
+
}
|
|
1
5
|
/**
|
|
2
6
|
* Generate catch-all HTTP routes for workflow operations
|
|
3
7
|
*/
|
|
4
|
-
export declare const serializeWorkflowRoutes: (pathToPikkuTypes: string, requireAuth?: boolean) =>
|
|
8
|
+
export declare const serializeWorkflowRoutes: (pathToPikkuTypes: string, requireAuth?: boolean) => WorkflowRoutesGenOutput;
|
|
@@ -3,13 +3,53 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const serializeWorkflowRoutes = (pathToPikkuTypes, requireAuth = true) => {
|
|
5
5
|
const authFlag = requireAuth ? 'true' : 'false';
|
|
6
|
-
|
|
6
|
+
const schemas = `/**
|
|
7
|
+
* Auto-generated workflow route schemas
|
|
8
|
+
* Do not edit manually - regenerate with 'npx pikku'
|
|
9
|
+
*/
|
|
10
|
+
import { z } from 'zod'
|
|
11
|
+
|
|
12
|
+
export const WorkflowStart = z.object({
|
|
13
|
+
workflowName: z.string(),
|
|
14
|
+
data: z.unknown().optional(),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export const GraphStart = z.object({
|
|
18
|
+
workflowName: z.string(),
|
|
19
|
+
nodeId: z.string(),
|
|
20
|
+
data: z.unknown().optional(),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export const WorkflowRunRef = z.object({
|
|
24
|
+
workflowName: z.string(),
|
|
25
|
+
runId: z.string(),
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
export const WorkflowRunId = z.object({ runId: z.string() })
|
|
29
|
+
|
|
30
|
+
export const WorkflowApproval = z.object({
|
|
31
|
+
workflowName: z.string(),
|
|
32
|
+
runId: z.string(),
|
|
33
|
+
reason: z.string(),
|
|
34
|
+
decision: z.unknown(),
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
export const Acknowledged = z.object({ ok: z.literal(true) })
|
|
38
|
+
`;
|
|
39
|
+
const functions = `/**
|
|
7
40
|
* Workflow HTTP catch-all routes
|
|
8
41
|
* Do not edit manually - regenerate with 'npx pikku'
|
|
9
42
|
*/
|
|
10
43
|
import { pikkuSessionlessFunc, wireHTTPRoutes } from '${pathToPikkuTypes}'
|
|
11
44
|
import { MissingServiceError } from '@pikku/core/errors'
|
|
12
|
-
import
|
|
45
|
+
import {
|
|
46
|
+
WorkflowStart,
|
|
47
|
+
GraphStart,
|
|
48
|
+
WorkflowRunRef,
|
|
49
|
+
WorkflowRunId,
|
|
50
|
+
WorkflowApproval,
|
|
51
|
+
Acknowledged,
|
|
52
|
+
} from './workflow-routes.schemas.gen.js'
|
|
13
53
|
|
|
14
54
|
function assertWorkflowService(workflowService: unknown): asserts workflowService {
|
|
15
55
|
if (!workflowService) throw new MissingServiceError('workflowService is required')
|
|
@@ -19,12 +59,11 @@ function assertWorkflowRunService(workflowRunService: unknown): asserts workflow
|
|
|
19
59
|
if (!workflowRunService) throw new MissingServiceError('workflowRunService is required')
|
|
20
60
|
}
|
|
21
61
|
|
|
22
|
-
export const workflowStarter = pikkuSessionlessFunc
|
|
23
|
-
{ workflowName: string; data?: unknown },
|
|
24
|
-
{ runId: string }
|
|
25
|
-
>({
|
|
62
|
+
export const workflowStarter = pikkuSessionlessFunc({
|
|
26
63
|
tags: ['pikku'],
|
|
27
64
|
auth: ${authFlag},
|
|
65
|
+
input: WorkflowStart,
|
|
66
|
+
output: WorkflowRunId,
|
|
28
67
|
// workflowService is destructured (even though we delegate via rpc) so the
|
|
29
68
|
// analyzer assigns workflow-state capability to this unit — without it,
|
|
30
69
|
// rpc.startWorkflow() runs against a container missing workflowService.
|
|
@@ -34,24 +73,20 @@ export const workflowStarter = pikkuSessionlessFunc<
|
|
|
34
73
|
},
|
|
35
74
|
})
|
|
36
75
|
|
|
37
|
-
export const workflowRunner = pikkuSessionlessFunc
|
|
38
|
-
{ workflowName: string; data?: unknown },
|
|
39
|
-
unknown
|
|
40
|
-
>({
|
|
76
|
+
export const workflowRunner = pikkuSessionlessFunc({
|
|
41
77
|
tags: ['pikku'],
|
|
42
78
|
auth: ${authFlag},
|
|
79
|
+
input: WorkflowStart,
|
|
43
80
|
func: async ({ workflowService }, { workflowName, data }, { rpc }) => {
|
|
44
81
|
assertWorkflowService(workflowService)
|
|
45
82
|
return await workflowService.runToCompletion(workflowName, data ?? {}, rpc)
|
|
46
83
|
},
|
|
47
84
|
})
|
|
48
85
|
|
|
49
|
-
export const workflowStatusChecker = pikkuSessionlessFunc
|
|
50
|
-
{ workflowName: string; runId: string },
|
|
51
|
-
WorkflowRunStatus
|
|
52
|
-
>({
|
|
86
|
+
export const workflowStatusChecker = pikkuSessionlessFunc({
|
|
53
87
|
tags: ['pikku'],
|
|
54
88
|
auth: ${authFlag},
|
|
89
|
+
input: WorkflowRunRef,
|
|
55
90
|
func: async ({ workflowService }, { runId }) => {
|
|
56
91
|
assertWorkflowService(workflowService)
|
|
57
92
|
const status = await workflowService.getRunStatus(runId)
|
|
@@ -64,12 +99,10 @@ export const workflowStatusChecker = pikkuSessionlessFunc<
|
|
|
64
99
|
* Minimal workflow status stream — sends step names and statuses only.
|
|
65
100
|
* Use this for user-facing frontends where internal details should not be exposed.
|
|
66
101
|
*/
|
|
67
|
-
export const workflowStatusStream = pikkuSessionlessFunc
|
|
68
|
-
{ workflowName: string; runId: string },
|
|
69
|
-
unknown
|
|
70
|
-
>({
|
|
102
|
+
export const workflowStatusStream = pikkuSessionlessFunc({
|
|
71
103
|
tags: ['pikku'],
|
|
72
104
|
auth: ${authFlag},
|
|
105
|
+
input: WorkflowRunRef,
|
|
73
106
|
func: async ({ workflowRunService }, { runId }, { channel }) => {
|
|
74
107
|
assertWorkflowRunService(workflowRunService)
|
|
75
108
|
if (!channel) return
|
|
@@ -151,12 +184,10 @@ export const workflowStatusStream = pikkuSessionlessFunc<
|
|
|
151
184
|
* Full workflow status stream — includes output, error, and child run IDs.
|
|
152
185
|
* Use this for admin consoles and internal tooling.
|
|
153
186
|
*/
|
|
154
|
-
export const workflowStatusStreamFull = pikkuSessionlessFunc
|
|
155
|
-
{ workflowName: string; runId: string },
|
|
156
|
-
unknown
|
|
157
|
-
>({
|
|
187
|
+
export const workflowStatusStreamFull = pikkuSessionlessFunc({
|
|
158
188
|
tags: ['pikku'],
|
|
159
189
|
auth: ${authFlag},
|
|
190
|
+
input: WorkflowRunRef,
|
|
160
191
|
func: async ({ workflowRunService }, { runId }, { channel }) => {
|
|
161
192
|
assertWorkflowRunService(workflowRunService)
|
|
162
193
|
if (!channel) return
|
|
@@ -238,12 +269,11 @@ export const workflowStatusStreamFull = pikkuSessionlessFunc<
|
|
|
238
269
|
},
|
|
239
270
|
})
|
|
240
271
|
|
|
241
|
-
export const graphStarter = pikkuSessionlessFunc
|
|
242
|
-
{ workflowName: string; nodeId: string; data?: unknown },
|
|
243
|
-
{ runId: string }
|
|
244
|
-
>({
|
|
272
|
+
export const graphStarter = pikkuSessionlessFunc({
|
|
245
273
|
tags: ['pikku'],
|
|
246
274
|
auth: ${authFlag},
|
|
275
|
+
input: GraphStart,
|
|
276
|
+
output: WorkflowRunId,
|
|
247
277
|
// See workflowStarter — destructure workflowService so the analyzer
|
|
248
278
|
// assigns workflow-state capability to this unit.
|
|
249
279
|
func: async ({ workflowService }, { workflowName, nodeId, data }, { rpc }) => {
|
|
@@ -261,18 +291,17 @@ export const graphStarter = pikkuSessionlessFunc<
|
|
|
261
291
|
* run. Gate this route with your own auth/permissions to control WHO may
|
|
262
292
|
* approve: the framework does not model approver identity.
|
|
263
293
|
*/
|
|
264
|
-
export const workflowApprover = pikkuSessionlessFunc
|
|
265
|
-
{ workflowName: string; runId: string; reason: string; decision: unknown },
|
|
266
|
-
{ ok: true }
|
|
267
|
-
>({
|
|
294
|
+
export const workflowApprover = pikkuSessionlessFunc({
|
|
268
295
|
tags: ['pikku'],
|
|
269
296
|
auth: ${authFlag},
|
|
297
|
+
input: WorkflowApproval,
|
|
298
|
+
output: Acknowledged,
|
|
270
299
|
// See workflowStarter — destructure workflowService so the analyzer
|
|
271
300
|
// assigns workflow-state capability to this unit.
|
|
272
301
|
func: async ({ workflowService }, { runId, reason, decision }) => {
|
|
273
302
|
assertWorkflowService(workflowService)
|
|
274
303
|
await workflowService.approveStep(runId, reason, decision)
|
|
275
|
-
return { ok: true }
|
|
304
|
+
return { ok: true as const }
|
|
276
305
|
},
|
|
277
306
|
})
|
|
278
307
|
|
|
@@ -320,4 +349,5 @@ wireHTTPRoutes({
|
|
|
320
349
|
},
|
|
321
350
|
})
|
|
322
351
|
`;
|
|
352
|
+
return { schemas, functions };
|
|
323
353
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
2
|
import { pikkuWorkflowComplexFunc } from '#pikku/workflow/pikku-workflow-types.gen.js';
|
|
3
3
|
import { assertSingleCoreVersion } from '../../utils/assert-single-core-version.js';
|
|
4
|
+
import { pruneLegacyScaffoldFiles } from '../../utils/remove-legacy-scaffold-file.js';
|
|
4
5
|
import { PikkuTypecheckFailedError, renderTscFull, renderTscSummary, runProjectTypecheck, } from '../../utils/tsc-check.js';
|
|
5
6
|
const scaffoldFiles = (config) => {
|
|
6
7
|
const files = [];
|
|
@@ -21,6 +22,11 @@ const scaffoldFiles = (config) => {
|
|
|
21
22
|
file: config.scenariosFunctionsFile,
|
|
22
23
|
generator: 'pikkuScenarioFunctions',
|
|
23
24
|
});
|
|
25
|
+
if (config.scaffold?.userAdmin && config.userAdminFunctionsFile)
|
|
26
|
+
files.push({
|
|
27
|
+
file: config.userAdminFunctionsFile,
|
|
28
|
+
generator: 'pikkuUserAdminFunctions',
|
|
29
|
+
});
|
|
24
30
|
if (config.scaffold?.agent && config.publicAgentFile)
|
|
25
31
|
files.push({
|
|
26
32
|
file: config.publicAgentFile,
|
|
@@ -39,6 +45,7 @@ export const allWorkflow = pikkuWorkflowComplexFunc({
|
|
|
39
45
|
// Preflight: a split @pikku/core install silently breaks wiring registration
|
|
40
46
|
// (workflows/RPCs "disappear"), so refuse to codegen against it.
|
|
41
47
|
await assertSingleCoreVersion(config.rootDir, logger);
|
|
48
|
+
await pruneLegacyScaffoldFiles(config);
|
|
42
49
|
const allImports = [];
|
|
43
50
|
let typesDeclarationFileExists = true;
|
|
44
51
|
if (!existsSync(config.outDir)) {
|
|
@@ -144,6 +151,7 @@ export const allWorkflow = pikkuWorkflowComplexFunc({
|
|
|
144
151
|
workflow.do('Public RPC', 'pikkuPublicRPC', null),
|
|
145
152
|
workflow.do('Console functions', 'pikkuConsoleFunctions', null),
|
|
146
153
|
workflow.do('Scenario functions', 'pikkuScenarioFunctions', null),
|
|
154
|
+
workflow.do('User admin functions', 'pikkuUserAdminFunctions', null),
|
|
147
155
|
workflow.do('Events scaffold', 'pikkuEventsScaffold', null),
|
|
148
156
|
workflow.do('Emails', 'pikkuEmails', null),
|
|
149
157
|
workflow.do('Secret definition types', 'pikkuSecretDefinitionTypes', null),
|