@beignet/cli 0.0.47 → 0.0.49
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/CHANGELOG.md +46 -0
- package/README.md +126 -42
- package/dist/analysis/contract-factories.d.ts +17 -0
- package/dist/analysis/contract-factories.d.ts.map +1 -0
- package/dist/analysis/contract-factories.js +176 -0
- package/dist/analysis/contract-factories.js.map +1 -0
- package/dist/analysis/layers.d.ts +5 -0
- package/dist/analysis/layers.d.ts.map +1 -0
- package/dist/analysis/layers.js +172 -0
- package/dist/analysis/layers.js.map +1 -0
- package/dist/analysis/port-wiring.d.ts +23 -0
- package/dist/analysis/port-wiring.d.ts.map +1 -0
- package/dist/analysis/port-wiring.js +379 -0
- package/dist/analysis/port-wiring.js.map +1 -0
- package/dist/analysis/source-index.d.ts +5 -0
- package/dist/analysis/source-index.d.ts.map +1 -1
- package/dist/analysis/source-index.js +171 -42
- package/dist/analysis/source-index.js.map +1 -1
- package/dist/analysis/workspace.d.ts +10 -4
- package/dist/analysis/workspace.d.ts.map +1 -1
- package/dist/analysis/workspace.js +66 -6
- package/dist/analysis/workspace.js.map +1 -1
- package/dist/app-map.d.ts.map +1 -1
- package/dist/app-map.js +311 -92
- package/dist/app-map.js.map +1 -1
- package/dist/check.d.ts +8 -1
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +55 -9
- package/dist/check.js.map +1 -1
- package/dist/db.d.ts +38 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +52 -0
- package/dist/db.js.map +1 -1
- package/dist/explain.js +4 -4
- package/dist/explain.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +73 -6
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +454 -170
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +2 -2
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -1
- package/dist/lib.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +309 -487
- package/dist/lint.js.map +1 -1
- package/dist/make/inbox.d.ts.map +1 -1
- package/dist/make/inbox.js +12 -5
- package/dist/make/inbox.js.map +1 -1
- package/dist/make/payments.js +3 -3
- package/dist/make/payments.js.map +1 -1
- package/dist/make/tenancy.js +1 -1
- package/dist/make/tenancy.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +144 -8
- package/dist/make.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +56 -19
- package/dist/mcp.js.map +1 -1
- package/dist/preflight.d.ts +11 -3
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +139 -16
- package/dist/preflight.js.map +1 -1
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +149 -9
- package/dist/provider-add.js.map +1 -1
- package/dist/provider-audit.d.ts +13 -0
- package/dist/provider-audit.d.ts.map +1 -1
- package/dist/provider-audit.js +122 -8
- package/dist/provider-audit.js.map +1 -1
- package/dist/registry-edits.d.ts +7 -0
- package/dist/registry-edits.d.ts.map +1 -1
- package/dist/registry-edits.js +237 -53
- package/dist/registry-edits.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +29 -7
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts +1 -0
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +20 -6
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/db/index.d.ts +1 -0
- package/dist/templates/db/index.d.ts.map +1 -1
- package/dist/templates/db/index.js.map +1 -1
- package/dist/templates/db/migration-status.d.ts +9 -0
- package/dist/templates/db/migration-status.d.ts.map +1 -0
- package/dist/templates/db/migration-status.js +117 -0
- package/dist/templates/db/migration-status.js.map +1 -0
- package/dist/templates/db/mysql.d.ts.map +1 -1
- package/dist/templates/db/mysql.js +23 -0
- package/dist/templates/db/mysql.js.map +1 -1
- package/dist/templates/db/postgres.d.ts.map +1 -1
- package/dist/templates/db/postgres.js +21 -0
- package/dist/templates/db/postgres.js.map +1 -1
- package/dist/templates/db/sqlite.d.ts.map +1 -1
- package/dist/templates/db/sqlite.js +53 -0
- package/dist/templates/db/sqlite.js.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +5 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +2 -0
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shared.d.ts +1 -0
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +4 -2
- package/dist/templates/shared.js.map +1 -1
- package/package.json +7 -6
- package/skills/app-structure/SKILL.md +56 -11
- package/src/analysis/contract-factories.ts +256 -0
- package/src/analysis/layers.ts +266 -0
- package/src/analysis/port-wiring.ts +506 -0
- package/src/analysis/source-index.ts +225 -57
- package/src/analysis/workspace.ts +115 -6
- package/src/app-map.ts +418 -109
- package/src/check.ts +86 -10
- package/src/db.ts +97 -0
- package/src/explain.ts +4 -4
- package/src/index.ts +108 -7
- package/src/inspect.ts +659 -216
- package/src/lib.ts +9 -1
- package/src/lint.ts +393 -643
- package/src/make/inbox.ts +12 -5
- package/src/make/payments.ts +3 -3
- package/src/make/tenancy.ts +1 -1
- package/src/make.ts +225 -8
- package/src/mcp.ts +73 -21
- package/src/preflight.ts +195 -16
- package/src/provider-add.ts +193 -9
- package/src/provider-audit.ts +210 -18
- package/src/registry-edits.ts +313 -63
- package/src/templates/agents.ts +29 -7
- package/src/templates/base.ts +21 -4
- package/src/templates/db/index.ts +1 -0
- package/src/templates/db/migration-status.ts +126 -0
- package/src/templates/db/mysql.ts +23 -0
- package/src/templates/db/postgres.ts +21 -0
- package/src/templates/db/sqlite.ts +53 -0
- package/src/templates/index.ts +5 -0
- package/src/templates/server.ts +2 -0
- package/src/templates/shared.ts +4 -2
package/src/make/inbox.ts
CHANGED
|
@@ -226,7 +226,7 @@ export async function makeInbox(
|
|
|
226
226
|
*/
|
|
227
227
|
export function makeInboxNextStepLines(): string[] {
|
|
228
228
|
return [
|
|
229
|
-
"Run beignet db generate and beignet db
|
|
229
|
+
"Run beignet db generate, beignet db migrate, and beignet db status so inbox_notifications exists and is verified.",
|
|
230
230
|
'Add an in-app channel to any feature notification: import { defineInboxNotificationChannel } from "@/features/inbox/channel" and add inApp: defineInboxNotificationChannel(({ payload }) => ({ userId: payload.userId, type: "your-type", title: "Something happened" })) to its channels.',
|
|
231
231
|
"Render the unread badge from features/inbox/components/unread-badge.tsx next to your Inbox nav link for a live unread count.",
|
|
232
232
|
"Run your app's test, lint, and typecheck commands, then beignet lint and beignet doctor.",
|
|
@@ -518,9 +518,9 @@ export const inboxNotificationSchema = z.object({
|
|
|
518
518
|
});
|
|
519
519
|
|
|
520
520
|
export const listInboxQuerySchema = z.object({
|
|
521
|
-
\tlimit: z.
|
|
521
|
+
\tlimit: z.number().int().min(1).max(50).default(20),
|
|
522
522
|
\tcursor: z.string().min(1).optional(),
|
|
523
|
-
\tunread: z.
|
|
523
|
+
\tunread: z.boolean().default(false),
|
|
524
524
|
});
|
|
525
525
|
|
|
526
526
|
export const listInboxOutputSchema = z.object({
|
|
@@ -659,7 +659,7 @@ export function inboxContractsFile(config: ResolvedBeignetConfig): string {
|
|
|
659
659
|
);
|
|
660
660
|
const errorsModule = aliasModule(resourceSharedErrorsPath(config));
|
|
661
661
|
|
|
662
|
-
return `import { defineContractGroup } from "@beignet/core/contracts";
|
|
662
|
+
return `import { defineContractGroup, defineQueryTransport, query } from "@beignet/core/contracts";
|
|
663
663
|
import {
|
|
664
664
|
\tinboxNotificationSchema,
|
|
665
665
|
\tlistInboxOutputSchema,
|
|
@@ -674,7 +674,14 @@ const inbox = defineContractGroup().namespace("inbox").prefix("/api/inbox");
|
|
|
674
674
|
|
|
675
675
|
export const listInbox = inbox
|
|
676
676
|
\t.get("/")
|
|
677
|
-
\t.query(
|
|
677
|
+
\t.query(
|
|
678
|
+
\t\tlistInboxQuerySchema,
|
|
679
|
+
\t\tdefineQueryTransport({
|
|
680
|
+
\t\t\tlimit: query.integer(),
|
|
681
|
+
\t\t\tcursor: query.string(),
|
|
682
|
+
\t\t\tunread: query.boolean(),
|
|
683
|
+
\t\t}),
|
|
684
|
+
\t)
|
|
678
685
|
\t.meta({ auth: "required" })
|
|
679
686
|
\t.errors({ Unauthorized: errors.Unauthorized })
|
|
680
687
|
\t.responses({
|
package/src/make/payments.ts
CHANGED
|
@@ -209,7 +209,7 @@ export function makePaymentsNextStepLines(
|
|
|
209
209
|
return [
|
|
210
210
|
"The free plan works out of the box; billing accounts without an active subscription resolve to it automatically.",
|
|
211
211
|
"Set BILLING_PRO_PRICE_ID in .env.local to the provider price ID for your pro plan.",
|
|
212
|
-
"Run beignet db generate and beignet db
|
|
212
|
+
"Run beignet db generate, beignet db migrate, and beignet db status so billing_accounts exists and is verified before handling webhooks.",
|
|
213
213
|
'Gate paid actions once usage passes FREE_PLAN_LIMITS: await requireEntitlement(ctx, { entitlement: "todos.create", subject: { type: "tenant", id: tenantId } });',
|
|
214
214
|
...(hasPlanUi
|
|
215
215
|
? [
|
|
@@ -862,7 +862,7 @@ export function isBillingAccountActive(
|
|
|
862
862
|
export function billingContractsFile(
|
|
863
863
|
idempotencyScope: "actor" | "actor-tenant" = "actor",
|
|
864
864
|
): string {
|
|
865
|
-
return `import { defineContractGroup } from "@beignet/core/contracts";
|
|
865
|
+
return `import { defineContractGroup, defineQueryTransport } from "@beignet/core/contracts";
|
|
866
866
|
import { z } from "zod";
|
|
867
867
|
import {
|
|
868
868
|
\tbillingStatusOutputSchema,
|
|
@@ -882,7 +882,7 @@ const checkoutBilling = billing.headers(checkoutHeadersSchema);
|
|
|
882
882
|
|
|
883
883
|
export const getBillingStatus = billing
|
|
884
884
|
\t.get("/")
|
|
885
|
-
\t.query(z.object({}))
|
|
885
|
+
\t.query(z.object({}), defineQueryTransport({}))
|
|
886
886
|
\t.meta({ auth: "required" })
|
|
887
887
|
\t.errors({ Unauthorized: errors.Unauthorized })
|
|
888
888
|
\t.responses({
|
package/src/make/tenancy.ts
CHANGED
|
@@ -249,7 +249,7 @@ export async function makeTenancy(
|
|
|
249
249
|
*/
|
|
250
250
|
export function makeTenancyNextStepLines(): string[] {
|
|
251
251
|
return [
|
|
252
|
-
"Run beignet db generate and beignet db
|
|
252
|
+
"Run beignet db generate, beignet db migrate, and beignet db status so the workspaces, workspace_members, and workspace_invites tables exist and are verified.",
|
|
253
253
|
"Requests resolve the active workspace from memberships and the beignet-workspace cookie; users without a workspace have no tenant, so point them at Settings > Workspace to create one.",
|
|
254
254
|
"Run the db:seed script for a demo workspace with a sign-in-able demo admin (demo-admin@example.com).",
|
|
255
255
|
"Invite emails send through the in-memory mailer in development; swap server/providers.ts to a real mail provider such as @beignet/provider-mail-resend for delivery.",
|
package/src/make.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { randomBytes } from "node:crypto";
|
|
|
2
2
|
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import ts from "typescript";
|
|
5
|
+
import { analyzePortWiringFiles } from "./analysis/port-wiring.js";
|
|
6
|
+
import { listProjectFiles } from "./analysis/workspace.js";
|
|
5
7
|
import type { MakeFeatureAddon, MakeFeatureRecipe } from "./choices.js";
|
|
6
8
|
import {
|
|
7
9
|
type BeignetConfig,
|
|
@@ -1084,6 +1086,10 @@ export async function makeJob(options: MakeJobOptions): Promise<MakeJobResult> {
|
|
|
1084
1086
|
await assertFeatureArtifactApp(targetDir, config, "job");
|
|
1085
1087
|
await assertServerRuntimeApp(targetDir, config, "job");
|
|
1086
1088
|
await updateOutboxPortWiring(targetDir, config, { dryRun: true });
|
|
1089
|
+
const jobDispatcherPlan = await planInlineJobDispatcherWiring(
|
|
1090
|
+
targetDir,
|
|
1091
|
+
config,
|
|
1092
|
+
);
|
|
1087
1093
|
const outboxDrainPlan = await planOutboxDrainWiring(targetDir, config, {
|
|
1088
1094
|
force: Boolean(options.force),
|
|
1089
1095
|
});
|
|
@@ -1114,6 +1120,9 @@ export async function makeJob(options: MakeJobOptions): Promise<MakeJobResult> {
|
|
|
1114
1120
|
await applyOutboxDrainWiring(result, targetDir, outboxDrainPlan, {
|
|
1115
1121
|
dryRun: Boolean(options.dryRun),
|
|
1116
1122
|
});
|
|
1123
|
+
await applyInlineJobDispatcherWiring(result, targetDir, jobDispatcherPlan, {
|
|
1124
|
+
dryRun: Boolean(options.dryRun),
|
|
1125
|
+
});
|
|
1117
1126
|
await applyOutboxPortWiring(result, targetDir, config, {
|
|
1118
1127
|
dryRun: Boolean(options.dryRun),
|
|
1119
1128
|
});
|
|
@@ -1124,6 +1133,201 @@ export async function makeJob(options: MakeJobOptions): Promise<MakeJobResult> {
|
|
|
1124
1133
|
return result;
|
|
1125
1134
|
}
|
|
1126
1135
|
|
|
1136
|
+
type InlineJobDispatcherWiringPlan = {
|
|
1137
|
+
changes: { path: string; before: string; after: string }[];
|
|
1138
|
+
};
|
|
1139
|
+
|
|
1140
|
+
async function planInlineJobDispatcherWiring(
|
|
1141
|
+
targetDir: string,
|
|
1142
|
+
config: ResolvedBeignetConfig,
|
|
1143
|
+
): Promise<InlineJobDispatcherWiringPlan> {
|
|
1144
|
+
const portsFile = config.paths.ports;
|
|
1145
|
+
const portWiringFile = config.paths.portWiring;
|
|
1146
|
+
const providersFile = providersFilePath(config);
|
|
1147
|
+
const ports = await readFile(path.join(targetDir, portsFile), "utf8");
|
|
1148
|
+
const portWiring = await readFile(
|
|
1149
|
+
path.join(targetDir, portWiringFile),
|
|
1150
|
+
"utf8",
|
|
1151
|
+
);
|
|
1152
|
+
const analysis = await analyzePortWiringFiles({
|
|
1153
|
+
targetDir,
|
|
1154
|
+
files: await listProjectFiles(targetDir),
|
|
1155
|
+
config,
|
|
1156
|
+
});
|
|
1157
|
+
const declaresJobs = analysis.declared.has("jobs");
|
|
1158
|
+
const wiresJobs = analysis.bound.has("jobs") || analysis.deferred.has("jobs");
|
|
1159
|
+
|
|
1160
|
+
if (declaresJobs && wiresJobs) {
|
|
1161
|
+
return { changes: [] };
|
|
1162
|
+
}
|
|
1163
|
+
if (!declaresJobs && analysis.declarationIndeterminate) {
|
|
1164
|
+
throw new Error(
|
|
1165
|
+
`Could not prove whether AppPorts in ${portsFile} declares the jobs port. Preserve the app's custom port type and add jobs: JobDispatcherPort manually before running make job.`,
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
if (!wiresJobs && analysis.indeterminate) {
|
|
1169
|
+
throw new Error(
|
|
1170
|
+
`Could not prove how the jobs port is wired in ${portWiringFile}. Preserve the app's custom dispatcher by binding or deferring "jobs" in a statically inspectable definePorts(...) object before running make job.`,
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
let nextPorts = ports;
|
|
1175
|
+
if (!declaresJobs && !analysis.appPortsInferredFromWiring) {
|
|
1176
|
+
nextPorts = addNamedTypeImport(
|
|
1177
|
+
nextPorts,
|
|
1178
|
+
"JobDispatcherPort",
|
|
1179
|
+
"@beignet/core/ports",
|
|
1180
|
+
);
|
|
1181
|
+
nextPorts = insertTypeProperty(
|
|
1182
|
+
nextPorts,
|
|
1183
|
+
"AppPorts",
|
|
1184
|
+
"jobs: JobDispatcherPort;",
|
|
1185
|
+
`Could not find AppPorts in ${portsFile}. Add jobs: JobDispatcherPort manually and wire a dispatcher, or restore the generated ports file before running make job.`,
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
if (wiresJobs) {
|
|
1190
|
+
return {
|
|
1191
|
+
changes:
|
|
1192
|
+
ports === nextPorts
|
|
1193
|
+
? []
|
|
1194
|
+
: [{ path: portsFile, before: ports, after: nextPorts }],
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
const nextPortWiring = appendDeferredPortKey(portWiring, "jobs");
|
|
1199
|
+
if (nextPortWiring === portWiring) {
|
|
1200
|
+
throw new Error(
|
|
1201
|
+
`Could not find the deferred port list in ${portWiringFile}. Add "jobs" manually and wire a dispatcher, or restore the generated port wiring file before running make job.`,
|
|
1202
|
+
);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
const providersPath = path.join(targetDir, providersFile);
|
|
1206
|
+
const providers = await readOptionalFile(providersPath);
|
|
1207
|
+
if (providers === undefined) {
|
|
1208
|
+
throw new Error(
|
|
1209
|
+
`Could not find ${providersFile}. Register an inline or provider-backed jobs dispatcher manually, or restore the generated providers file before running make job.`,
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
let nextProviders = addNamedImport(
|
|
1213
|
+
providers,
|
|
1214
|
+
"createInlineJobDispatcher",
|
|
1215
|
+
"@beignet/core/jobs",
|
|
1216
|
+
);
|
|
1217
|
+
nextProviders = addNamedImport(
|
|
1218
|
+
nextProviders,
|
|
1219
|
+
"createProvider",
|
|
1220
|
+
"@beignet/core/providers",
|
|
1221
|
+
);
|
|
1222
|
+
nextProviders = addNamedTypeImport(
|
|
1223
|
+
nextProviders,
|
|
1224
|
+
"AppContext",
|
|
1225
|
+
aliasModule(config.paths.appContext),
|
|
1226
|
+
);
|
|
1227
|
+
nextProviders = addNamedTypeImport(
|
|
1228
|
+
nextProviders,
|
|
1229
|
+
"AppPorts",
|
|
1230
|
+
aliasModule(config.paths.ports),
|
|
1231
|
+
);
|
|
1232
|
+
nextProviders = addNamedTypeImport(
|
|
1233
|
+
nextProviders,
|
|
1234
|
+
"AppServiceContextInput",
|
|
1235
|
+
aliasModule(path.join(path.dirname(config.paths.server), "context.ts")),
|
|
1236
|
+
);
|
|
1237
|
+
|
|
1238
|
+
const providerName = "inlineJobsProvider";
|
|
1239
|
+
const generatedProviderMarker = "// beignet:generated-inline-jobs-provider";
|
|
1240
|
+
const providersSourceFile = ts.createSourceFile(
|
|
1241
|
+
providersFile,
|
|
1242
|
+
nextProviders,
|
|
1243
|
+
ts.ScriptTarget.Latest,
|
|
1244
|
+
true,
|
|
1245
|
+
ts.ScriptKind.TS,
|
|
1246
|
+
);
|
|
1247
|
+
const providerDeclaration = providersSourceFile.statements
|
|
1248
|
+
.filter(ts.isVariableStatement)
|
|
1249
|
+
.flatMap((statement) => [...statement.declarationList.declarations])
|
|
1250
|
+
.find(
|
|
1251
|
+
(declaration) =>
|
|
1252
|
+
ts.isIdentifier(declaration.name) &&
|
|
1253
|
+
declaration.name.text === providerName,
|
|
1254
|
+
);
|
|
1255
|
+
const providerDeclarationExists = providerDeclaration !== undefined;
|
|
1256
|
+
const providerIsGenerated = providerDeclaration
|
|
1257
|
+
? nextProviders
|
|
1258
|
+
.slice(0, providerDeclaration.getStart(providersSourceFile))
|
|
1259
|
+
.trimEnd()
|
|
1260
|
+
.endsWith(generatedProviderMarker)
|
|
1261
|
+
: false;
|
|
1262
|
+
if (providerDeclarationExists && !providerIsGenerated) {
|
|
1263
|
+
throw new Error(
|
|
1264
|
+
`Could not install the generated inline jobs dispatcher because ${providerName} already exists in ${providersFile}. Rename the app-owned declaration or wire its jobs port explicitly before running make job.`,
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
if (!providerDeclarationExists) {
|
|
1268
|
+
const definition = `// beignet:generated-inline-jobs-provider
|
|
1269
|
+
const ${providerName} = createProvider<
|
|
1270
|
+
{},
|
|
1271
|
+
AppContext,
|
|
1272
|
+
AppServiceContextInput
|
|
1273
|
+
>()({
|
|
1274
|
+
name: "inline-jobs",
|
|
1275
|
+
setup({ createServiceContext }): { ports: Pick<AppPorts, "jobs"> } {
|
|
1276
|
+
return {
|
|
1277
|
+
ports: {
|
|
1278
|
+
jobs: createInlineJobDispatcher<AppContext>({
|
|
1279
|
+
ctx: () => createServiceContext(undefined),
|
|
1280
|
+
}),
|
|
1281
|
+
},
|
|
1282
|
+
};
|
|
1283
|
+
},
|
|
1284
|
+
});
|
|
1285
|
+
`;
|
|
1286
|
+
const inserted = nextProviders.replace(
|
|
1287
|
+
/\nexport const providers = \[/,
|
|
1288
|
+
`\n${definition}\nexport const providers = [`,
|
|
1289
|
+
);
|
|
1290
|
+
if (inserted === nextProviders) {
|
|
1291
|
+
throw new Error(
|
|
1292
|
+
`Could not find the exported providers array in ${providersFile}. Register an inline or provider-backed jobs dispatcher manually, or restore the generated providers file before running make job.`,
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1295
|
+
nextProviders = inserted;
|
|
1296
|
+
}
|
|
1297
|
+
const appended = appendToNamedArray(nextProviders, "providers", providerName);
|
|
1298
|
+
if (appended.kind === "missing") {
|
|
1299
|
+
throw new Error(
|
|
1300
|
+
`Could not find the exported providers array in ${providersFile}. Register ${providerName} manually, or restore the generated providers file before running make job.`,
|
|
1301
|
+
);
|
|
1302
|
+
}
|
|
1303
|
+
if (appended.kind === "updated") nextProviders = appended.source;
|
|
1304
|
+
|
|
1305
|
+
return {
|
|
1306
|
+
changes: [
|
|
1307
|
+
{ path: portsFile, before: ports, after: nextPorts },
|
|
1308
|
+
{ path: portWiringFile, before: portWiring, after: nextPortWiring },
|
|
1309
|
+
{ path: providersFile, before: providers, after: nextProviders },
|
|
1310
|
+
].filter((change) => change.before !== change.after),
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
async function applyInlineJobDispatcherWiring(
|
|
1315
|
+
result: MakeResult,
|
|
1316
|
+
targetDir: string,
|
|
1317
|
+
plan: InlineJobDispatcherWiringPlan,
|
|
1318
|
+
options: { dryRun: boolean },
|
|
1319
|
+
): Promise<void> {
|
|
1320
|
+
for (const change of plan.changes) {
|
|
1321
|
+
if (!options.dryRun) {
|
|
1322
|
+
await writeFile(path.join(targetDir, change.path), change.after);
|
|
1323
|
+
}
|
|
1324
|
+
if (!result.updatedFiles.includes(change.path)) {
|
|
1325
|
+
result.updatedFiles.push(change.path);
|
|
1326
|
+
}
|
|
1327
|
+
if (!result.files.includes(change.path)) result.files.push(change.path);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1127
1331
|
export async function makeTask(
|
|
1128
1332
|
options: MakeTaskOptions,
|
|
1129
1333
|
): Promise<MakeTaskResult> {
|
|
@@ -3983,8 +4187,8 @@ export function decode${names.singularPascal}Cursor(cursor: string): ${names.sin
|
|
|
3983
4187
|
|
|
3984
4188
|
export const List${names.pluralPascal}InputSchema = z
|
|
3985
4189
|
.object({
|
|
3986
|
-
limit: z.
|
|
3987
|
-
cursor: z.string().
|
|
4190
|
+
limit: z.number().int().min(1).max(100).default(20),
|
|
4191
|
+
cursor: z.string().optional().transform((value) => value ?? null),
|
|
3988
4192
|
name: z.string().trim().min(1).max(120).optional(),
|
|
3989
4193
|
sortBy: ${names.singularPascal}SortBySchema.default("createdAt"),
|
|
3990
4194
|
sortDirection: ${names.singularPascal}SortDirectionSchema.default("desc"),
|
|
@@ -4840,7 +5044,7 @@ function contractFile(
|
|
|
4840
5044
|
mode: ResourceGenerationMode,
|
|
4841
5045
|
options: ResourceGenerationOptions,
|
|
4842
5046
|
): string {
|
|
4843
|
-
return `import { defineContractGroup } from "@beignet/core/contracts";
|
|
5047
|
+
return `import { defineContractGroup, defineQueryTransport, query } from "@beignet/core/contracts";
|
|
4844
5048
|
import { z } from "zod";
|
|
4845
5049
|
${mode === "resource" ? `import { errors } from "${aliasModule(resourceSharedErrorsPath(config))}";\n` : ""}import {
|
|
4846
5050
|
Create${names.singularPascal}InputSchema,
|
|
@@ -4863,7 +5067,16 @@ const ${names.pluralCamel} = defineContractGroup()
|
|
|
4863
5067
|
|
|
4864
5068
|
export const list${names.pluralPascal} = ${names.pluralCamel}
|
|
4865
5069
|
.get("/api/${names.pluralKebab}")
|
|
4866
|
-
.query(
|
|
5070
|
+
.query(
|
|
5071
|
+
List${names.pluralPascal}InputSchema,
|
|
5072
|
+
defineQueryTransport({
|
|
5073
|
+
limit: query.integer(),
|
|
5074
|
+
cursor: query.string(),
|
|
5075
|
+
name: query.string(),
|
|
5076
|
+
sortBy: query.string(),
|
|
5077
|
+
sortDirection: query.string(),
|
|
5078
|
+
}),
|
|
5079
|
+
)
|
|
4867
5080
|
.responses({
|
|
4868
5081
|
200: List${names.pluralPascal}OutputSchema,
|
|
4869
5082
|
});
|
|
@@ -4908,7 +5121,7 @@ ${options.authorization ? `\t.meta({\n\t\tauth: "required",\n\t\tauthorization:
|
|
|
4908
5121
|
}
|
|
4909
5122
|
|
|
4910
5123
|
function standaloneContractFile(names: ResourceNames): string {
|
|
4911
|
-
return `import { defineContractGroup } from "@beignet/core/contracts";
|
|
5124
|
+
return `import { defineContractGroup, defineQueryTransport, query } from "@beignet/core/contracts";
|
|
4912
5125
|
import { z } from "zod";
|
|
4913
5126
|
|
|
4914
5127
|
const ErrorResponseSchema = z.object({
|
|
@@ -4932,8 +5145,12 @@ export const list${names.pluralPascal} = ${names.pluralCamel}
|
|
|
4932
5145
|
.get("/api/${names.pluralKebab}")
|
|
4933
5146
|
.query(
|
|
4934
5147
|
z.object({
|
|
4935
|
-
limit: z.
|
|
4936
|
-
offset: z.
|
|
5148
|
+
limit: z.number().int().min(1).max(100).default(20),
|
|
5149
|
+
offset: z.number().int().min(0).default(0),
|
|
5150
|
+
}),
|
|
5151
|
+
defineQueryTransport({
|
|
5152
|
+
limit: query.integer(),
|
|
5153
|
+
offset: query.integer(),
|
|
4937
5154
|
}),
|
|
4938
5155
|
)
|
|
4939
5156
|
.responses({
|
|
@@ -6038,7 +6255,7 @@ ${options.authorization ? `\t\t\t\tgate: initialPorts.gate,\n` : ""}
|
|
|
6038
6255
|
);
|
|
6039
6256
|
const secondPage = await tester.run(
|
|
6040
6257
|
list${names.pluralPascal}UseCase,
|
|
6041
|
-
{ limit: 2, cursor: firstPage.page.nextCursor },
|
|
6258
|
+
{ limit: 2, cursor: firstPage.page.nextCursor! },
|
|
6042
6259
|
{ ctx },
|
|
6043
6260
|
);
|
|
6044
6261
|
const nameAscendingPage = await tester.run(
|
package/src/mcp.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { open, realpath } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
-
import { z } from "zod";
|
|
3
|
+
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/server";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
|
|
5
|
+
import { z } from "zod/v4";
|
|
9
6
|
import { mapApp, projectAppMap } from "./app-map.js";
|
|
10
7
|
import { appMapNodeKinds } from "./app-map-schema.js";
|
|
11
8
|
import { checkApp } from "./check.js";
|
|
@@ -23,6 +20,7 @@ import {
|
|
|
23
20
|
defaultDatabaseCommandMaxOutputBytes,
|
|
24
21
|
defaultDatabaseCommandTimeoutMs,
|
|
25
22
|
runDatabaseCommand,
|
|
23
|
+
runDatabaseStatus,
|
|
26
24
|
syncDatabaseSchema,
|
|
27
25
|
} from "./db.js";
|
|
28
26
|
import { explainApp, explainTargetKinds } from "./explain.js";
|
|
@@ -639,7 +637,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
639
637
|
"check",
|
|
640
638
|
{
|
|
641
639
|
description:
|
|
642
|
-
"Run the full Beignet app validation loop: Beignet lint, strict doctor, and the app's lint, typecheck, and test scripts. Returns the same versioned JSON as beignet check --json, including every step, bounded failure output, durations, and overall ok status. Does not apply Beignet fixes, but app-owned scripts run with their normal side effects.",
|
|
640
|
+
"Run the full Beignet app validation loop: Beignet lint, strict doctor, optional disconnected or connected preflight, and the app's lint, typecheck, and test scripts. Returns the same versioned JSON as beignet check --json, including every step, bounded failure output, durations, and overall ok status. Does not apply Beignet fixes, but app-owned scripts run with their normal side effects.",
|
|
643
641
|
inputSchema: {
|
|
644
642
|
preflight: z
|
|
645
643
|
.boolean()
|
|
@@ -647,6 +645,21 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
647
645
|
.describe(
|
|
648
646
|
"Include the environment preflight check before app package scripts. Defaults to false.",
|
|
649
647
|
),
|
|
648
|
+
preflightConnect: z
|
|
649
|
+
.boolean()
|
|
650
|
+
.optional()
|
|
651
|
+
.describe(
|
|
652
|
+
"Include connected preflight with migration status and dependency health checks. Implies preflight and defaults to false.",
|
|
653
|
+
),
|
|
654
|
+
connectTimeoutMs: z
|
|
655
|
+
.number()
|
|
656
|
+
.int()
|
|
657
|
+
.min(1)
|
|
658
|
+
.max(60 * 60 * 1_000)
|
|
659
|
+
.optional()
|
|
660
|
+
.describe(
|
|
661
|
+
"Maximum duration for migration status and each dependency health check in milliseconds. Defaults to 5000.",
|
|
662
|
+
),
|
|
650
663
|
timeoutMs: z
|
|
651
664
|
.number()
|
|
652
665
|
.int()
|
|
@@ -664,14 +677,16 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
664
677
|
openWorldHint: true,
|
|
665
678
|
},
|
|
666
679
|
},
|
|
667
|
-
async (input,
|
|
680
|
+
async (input, ctx) =>
|
|
668
681
|
safeToolResult(async () =>
|
|
669
682
|
jsonResult(
|
|
670
683
|
await checkApp({
|
|
671
684
|
cwd,
|
|
672
685
|
preflight: input.preflight ?? false,
|
|
686
|
+
preflightConnect: input.preflightConnect ?? false,
|
|
687
|
+
connectTimeoutMs: input.connectTimeoutMs,
|
|
673
688
|
scriptTimeoutMs: input.timeoutMs ?? defaultMcpCheckTimeoutMs,
|
|
674
|
-
signal:
|
|
689
|
+
signal: ctx.mcpReq.signal,
|
|
675
690
|
}),
|
|
676
691
|
),
|
|
677
692
|
),
|
|
@@ -709,7 +724,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
709
724
|
openWorldHint: true,
|
|
710
725
|
},
|
|
711
726
|
},
|
|
712
|
-
async (input,
|
|
727
|
+
async (input, ctx) =>
|
|
713
728
|
safeToolResult(async () =>
|
|
714
729
|
jsonResult(
|
|
715
730
|
await runDatabaseCommand({
|
|
@@ -717,7 +732,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
717
732
|
cwd,
|
|
718
733
|
captureOutput: true,
|
|
719
734
|
maxOutputBytes: defaultDatabaseCommandMaxOutputBytes,
|
|
720
|
-
signal:
|
|
735
|
+
signal: ctx.mcpReq.signal,
|
|
721
736
|
timeoutMs: input.timeoutMs ?? defaultDatabaseCommandTimeoutMs,
|
|
722
737
|
dryRun: input.dryRun,
|
|
723
738
|
}),
|
|
@@ -725,6 +740,43 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
725
740
|
),
|
|
726
741
|
);
|
|
727
742
|
|
|
743
|
+
server.registerTool(
|
|
744
|
+
"db_status",
|
|
745
|
+
{
|
|
746
|
+
description:
|
|
747
|
+
"Inspect app-owned database migrations and return exactly the versioned beignet db status --json result. Exit state distinguishes current, pending, and failed inspection without mutating the database. Output is bounded, the complete child process tree is cancelled with the MCP request, and execution stops after the configured timeout.",
|
|
748
|
+
inputSchema: {
|
|
749
|
+
timeoutMs: z
|
|
750
|
+
.number()
|
|
751
|
+
.int()
|
|
752
|
+
.min(1_000)
|
|
753
|
+
.max(60 * 60 * 1_000)
|
|
754
|
+
.optional()
|
|
755
|
+
.describe(
|
|
756
|
+
"Maximum status duration in milliseconds. Defaults to 600000 (10 minutes).",
|
|
757
|
+
),
|
|
758
|
+
},
|
|
759
|
+
annotations: {
|
|
760
|
+
readOnlyHint: true,
|
|
761
|
+
destructiveHint: false,
|
|
762
|
+
idempotentHint: true,
|
|
763
|
+
openWorldHint: true,
|
|
764
|
+
},
|
|
765
|
+
},
|
|
766
|
+
async (input, ctx) =>
|
|
767
|
+
safeToolResult(async () =>
|
|
768
|
+
jsonResult(
|
|
769
|
+
await runDatabaseStatus({
|
|
770
|
+
cwd,
|
|
771
|
+
captureOutput: true,
|
|
772
|
+
maxOutputBytes: defaultDatabaseCommandMaxOutputBytes,
|
|
773
|
+
signal: ctx.mcpReq.signal,
|
|
774
|
+
timeoutMs: input.timeoutMs ?? defaultDatabaseCommandTimeoutMs,
|
|
775
|
+
}),
|
|
776
|
+
),
|
|
777
|
+
),
|
|
778
|
+
);
|
|
779
|
+
|
|
728
780
|
server.registerTool(
|
|
729
781
|
"db_schema_sync",
|
|
730
782
|
{
|
|
@@ -763,7 +815,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
763
815
|
openWorldHint: false,
|
|
764
816
|
},
|
|
765
817
|
},
|
|
766
|
-
async (input,
|
|
818
|
+
async (input, ctx) =>
|
|
767
819
|
safeToolResult(async () =>
|
|
768
820
|
jsonResult(
|
|
769
821
|
await syncDatabaseSchema({
|
|
@@ -772,7 +824,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
772
824
|
tables: input.tables,
|
|
773
825
|
output: input.output,
|
|
774
826
|
dryRun: input.dryRun,
|
|
775
|
-
signal:
|
|
827
|
+
signal: ctx.mcpReq.signal,
|
|
776
828
|
}),
|
|
777
829
|
),
|
|
778
830
|
),
|
|
@@ -791,7 +843,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
791
843
|
openWorldHint: true,
|
|
792
844
|
},
|
|
793
845
|
},
|
|
794
|
-
async (input,
|
|
846
|
+
async (input, ctx) =>
|
|
795
847
|
safeToolResult(async () =>
|
|
796
848
|
operationalJsonResult(
|
|
797
849
|
await runIsolatedOperationalCommand({
|
|
@@ -803,7 +855,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
803
855
|
tenant: input.tenant,
|
|
804
856
|
modulePath: input.module,
|
|
805
857
|
},
|
|
806
|
-
signal:
|
|
858
|
+
signal: ctx.mcpReq.signal,
|
|
807
859
|
timeoutMs: input.timeoutMs ?? defaultOperationalCommandTimeoutMs,
|
|
808
860
|
}),
|
|
809
861
|
),
|
|
@@ -823,7 +875,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
823
875
|
openWorldHint: true,
|
|
824
876
|
},
|
|
825
877
|
},
|
|
826
|
-
async (input,
|
|
878
|
+
async (input, ctx) =>
|
|
827
879
|
safeToolResult(async () =>
|
|
828
880
|
operationalJsonResult(
|
|
829
881
|
await runIsolatedOperationalCommand({
|
|
@@ -839,7 +891,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
839
891
|
source: input.source,
|
|
840
892
|
modulePath: input.module,
|
|
841
893
|
},
|
|
842
|
-
signal:
|
|
894
|
+
signal: ctx.mcpReq.signal,
|
|
843
895
|
timeoutMs: input.timeoutMs ?? defaultOperationalCommandTimeoutMs,
|
|
844
896
|
}),
|
|
845
897
|
),
|
|
@@ -859,7 +911,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
859
911
|
openWorldHint: true,
|
|
860
912
|
},
|
|
861
913
|
},
|
|
862
|
-
async (input,
|
|
914
|
+
async (input, ctx) =>
|
|
863
915
|
safeToolResult(async () =>
|
|
864
916
|
operationalJsonResult(
|
|
865
917
|
await runIsolatedOperationalCommand({
|
|
@@ -879,7 +931,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
879
931
|
id: input.id,
|
|
880
932
|
modulePath: input.module,
|
|
881
933
|
},
|
|
882
|
-
signal:
|
|
934
|
+
signal: ctx.mcpReq.signal,
|
|
883
935
|
timeoutMs: input.timeoutMs ?? defaultOperationalCommandTimeoutMs,
|
|
884
936
|
}),
|
|
885
937
|
),
|
|
@@ -899,7 +951,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
899
951
|
openWorldHint: true,
|
|
900
952
|
},
|
|
901
953
|
},
|
|
902
|
-
async (input,
|
|
954
|
+
async (input, ctx) =>
|
|
903
955
|
safeToolResult(async () => {
|
|
904
956
|
const request =
|
|
905
957
|
input.operation === "drain"
|
|
@@ -936,7 +988,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
936
988
|
await runIsolatedOperationalCommand({
|
|
937
989
|
cwd,
|
|
938
990
|
request,
|
|
939
|
-
signal:
|
|
991
|
+
signal: ctx.mcpReq.signal,
|
|
940
992
|
timeoutMs: input.timeoutMs ?? defaultOperationalCommandTimeoutMs,
|
|
941
993
|
}),
|
|
942
994
|
);
|