@beignet/cli 0.0.48 → 0.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/README.md +117 -22
- 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 +11 -4
- package/dist/analysis/workspace.d.ts.map +1 -1
- package/dist/analysis/workspace.js +78 -8
- package/dist/analysis/workspace.js.map +1 -1
- package/dist/app-map-changes.d.ts +103 -0
- package/dist/app-map-changes.d.ts.map +1 -0
- package/dist/app-map-changes.js +949 -0
- package/dist/app-map-changes.js.map +1 -0
- 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.js +37 -3
- package/dist/check.js.map +1 -1
- package/dist/explain.js +4 -4
- package/dist/explain.js.map +1 -1
- package/dist/git-changes.d.ts +30 -0
- package/dist/git-changes.d.ts.map +1 -0
- package/dist/git-changes.js +367 -0
- package/dist/git-changes.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +706 -186
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +3 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -0
- 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 +11 -4
- package/dist/make/inbox.js.map +1 -1
- package/dist/make/payments.js +2 -2
- package/dist/make/shared.d.ts.map +1 -1
- package/dist/make/shared.js +31 -27
- package/dist/make/shared.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 +43 -21
- package/dist/mcp.js.map +1 -1
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +142 -7
- package/dist/provider-add.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 +37 -1
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +13 -2
- package/dist/templates/base.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 +3 -2
- package/dist/templates/shared.js.map +1 -1
- package/package.json +7 -6
- package/skills/app-structure/SKILL.md +71 -7
- 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 +134 -9
- package/src/app-map-changes.ts +1462 -0
- package/src/app-map.ts +418 -109
- package/src/check.ts +51 -3
- package/src/explain.ts +4 -4
- package/src/git-changes.ts +511 -0
- package/src/index.ts +39 -1
- package/src/inspect.ts +1005 -238
- package/src/lib.ts +21 -0
- package/src/lint.ts +393 -643
- package/src/make/inbox.ts +11 -4
- package/src/make/payments.ts +2 -2
- package/src/make/shared.ts +31 -27
- package/src/make.ts +225 -8
- package/src/mcp.ts +51 -24
- package/src/provider-add.ts +186 -7
- package/src/registry-edits.ts +313 -63
- package/src/templates/agents.ts +37 -1
- package/src/templates/base.ts +13 -2
- package/src/templates/server.ts +2 -0
- package/src/templates/shared.ts +3 -2
package/src/make/inbox.ts
CHANGED
|
@@ -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
|
@@ -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/shared.ts
CHANGED
|
@@ -453,36 +453,40 @@ export function wireListenersProviderSource(
|
|
|
453
453
|
>()({
|
|
454
454
|
\tname: "app-listeners",
|
|
455
455
|
\tsetup({ ports, createServiceContext }) {
|
|
456
|
-
\t\
|
|
457
|
-
|
|
458
|
-
\t\
|
|
459
|
-
\t\t\
|
|
460
|
-
\t\t\t\
|
|
461
|
-
\t\t\
|
|
462
|
-
\t\t\t\
|
|
463
|
-
\t\t\t\t\
|
|
464
|
-
\t\t\t\t\
|
|
465
|
-
\t\t\t\t
|
|
466
|
-
\t\t\t\
|
|
467
|
-
\t\t\t\t\
|
|
468
|
-
\t\t\t\t\
|
|
469
|
-
\t\t\t\t\
|
|
470
|
-
\t\t\t\t\t\
|
|
471
|
-
\t\t\t\t\t\
|
|
472
|
-
\t\t\t\t\t\
|
|
473
|
-
\t\t\t\t\t\
|
|
474
|
-
\t\t\t\t\t\t\t
|
|
475
|
-
\t\t\t\t\t\t\t"beignet.listener"
|
|
476
|
-
\t\t\t\t\t\t
|
|
477
|
-
\t\t\t\t\t\
|
|
456
|
+
\t\tlet registration: ReturnType<typeof registerListeners> | undefined;
|
|
457
|
+
|
|
458
|
+
\t\treturn {
|
|
459
|
+
\t\t\tasync start() {
|
|
460
|
+
\t\t\t\tregistration = registerListeners(ports.eventBus, listeners, {
|
|
461
|
+
\t\t\t\t\tctx: () =>
|
|
462
|
+
\t\t\t\t\t\tcreateServiceContext({
|
|
463
|
+
\t\t\t\t\t\t\tactor: createServiceActor("beignet-listener"),
|
|
464
|
+
\t\t\t\t\t\t}),
|
|
465
|
+
\t\t\t\t\tonError(error, listener) {
|
|
466
|
+
\t\t\t\t\t\tports.logger.error("Event listener failed", {
|
|
467
|
+
\t\t\t\t\t\t\terror,
|
|
468
|
+
\t\t\t\t\t\t\tlistenerName: listener.name,
|
|
469
|
+
\t\t\t\t\t\t});
|
|
470
|
+
\t\t\t\t\t\tvoid tryReportException({
|
|
471
|
+
\t\t\t\t\t\t\treporter: ports.errorReporter,
|
|
472
|
+
\t\t\t\t\t\t\terror,
|
|
473
|
+
\t\t\t\t\t\t\treportOptions: {
|
|
474
|
+
\t\t\t\t\t\t\t\tlevel: "error",
|
|
475
|
+
\t\t\t\t\t\t\t\tmechanism: "beignet.listener",
|
|
476
|
+
\t\t\t\t\t\t\t\thandled: false,
|
|
477
|
+
\t\t\t\t\t\t\t\ttags: {
|
|
478
|
+
\t\t\t\t\t\t\t\t\t"beignet.kind": "listener",
|
|
479
|
+
\t\t\t\t\t\t\t\t\t"beignet.listener": listener.name,
|
|
480
|
+
\t\t\t\t\t\t\t\t},
|
|
481
|
+
\t\t\t\t\t\t\t\tcontexts: { listener: { name: listener.name } },
|
|
482
|
+
\t\t\t\t\t\t\t},
|
|
483
|
+
\t\t\t\t\t\t});
|
|
478
484
|
\t\t\t\t\t},
|
|
479
485
|
\t\t\t\t});
|
|
486
|
+
\t\t\t\tawait registration.ready;
|
|
480
487
|
\t\t\t},
|
|
481
|
-
\t\t
|
|
482
|
-
|
|
483
|
-
\t\treturn {
|
|
484
|
-
\t\t\tstop() {
|
|
485
|
-
\t\t\t\tunregister();
|
|
488
|
+
\t\t\tasync stop() {
|
|
489
|
+
\t\t\t\tawait registration?.unsubscribe();
|
|
486
490
|
\t\t\t},
|
|
487
491
|
\t\t};
|
|
488
492
|
\t},
|
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,12 +1,10 @@
|
|
|
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";
|
|
7
|
+
import { mapAppChanges } from "./app-map-changes.js";
|
|
10
8
|
import { appMapNodeKinds } from "./app-map-schema.js";
|
|
11
9
|
import { checkApp } from "./check.js";
|
|
12
10
|
import {
|
|
@@ -582,8 +580,21 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
582
580
|
"app_map",
|
|
583
581
|
{
|
|
584
582
|
description:
|
|
585
|
-
"Return a deterministic graph of this Beignet app
|
|
583
|
+
"Return a deterministic graph of this Beignet app, or map the current Git change set to bounded, source-backed concepts with changed=true. Changed mode is report-only and exposes unresolved evidence without claiming verification.",
|
|
586
584
|
inputSchema: {
|
|
585
|
+
changed: z
|
|
586
|
+
.boolean()
|
|
587
|
+
.optional()
|
|
588
|
+
.describe(
|
|
589
|
+
"Map the current Git change set instead of returning the complete app graph.",
|
|
590
|
+
),
|
|
591
|
+
base: z
|
|
592
|
+
.string()
|
|
593
|
+
.min(1)
|
|
594
|
+
.optional()
|
|
595
|
+
.describe(
|
|
596
|
+
"Include committed changes since the merge base of this already-local Git ref. Requires changed=true; Beignet never fetches it.",
|
|
597
|
+
),
|
|
587
598
|
feature: z
|
|
588
599
|
.string()
|
|
589
600
|
.optional()
|
|
@@ -601,6 +612,22 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
601
612
|
},
|
|
602
613
|
async (input) =>
|
|
603
614
|
safeToolResult(async () => {
|
|
615
|
+
if (input.base !== undefined && !input.changed) {
|
|
616
|
+
throw new Error("app_map base requires changed=true.");
|
|
617
|
+
}
|
|
618
|
+
if (
|
|
619
|
+
input.changed &&
|
|
620
|
+
(input.feature !== undefined ||
|
|
621
|
+
input.kinds !== undefined ||
|
|
622
|
+
input.includeDiagnostics !== undefined)
|
|
623
|
+
) {
|
|
624
|
+
throw new Error(
|
|
625
|
+
"app_map changed=true cannot be combined with feature, kinds, or includeDiagnostics.",
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
if (input.changed) {
|
|
629
|
+
return jsonResult(await mapAppChanges({ cwd, base: input.base }));
|
|
630
|
+
}
|
|
604
631
|
const result = await mapApp({ cwd, strict: true });
|
|
605
632
|
return jsonResult(
|
|
606
633
|
projectAppMap(result, {
|
|
@@ -680,7 +707,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
680
707
|
openWorldHint: true,
|
|
681
708
|
},
|
|
682
709
|
},
|
|
683
|
-
async (input,
|
|
710
|
+
async (input, ctx) =>
|
|
684
711
|
safeToolResult(async () =>
|
|
685
712
|
jsonResult(
|
|
686
713
|
await checkApp({
|
|
@@ -689,7 +716,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
689
716
|
preflightConnect: input.preflightConnect ?? false,
|
|
690
717
|
connectTimeoutMs: input.connectTimeoutMs,
|
|
691
718
|
scriptTimeoutMs: input.timeoutMs ?? defaultMcpCheckTimeoutMs,
|
|
692
|
-
signal:
|
|
719
|
+
signal: ctx.mcpReq.signal,
|
|
693
720
|
}),
|
|
694
721
|
),
|
|
695
722
|
),
|
|
@@ -727,7 +754,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
727
754
|
openWorldHint: true,
|
|
728
755
|
},
|
|
729
756
|
},
|
|
730
|
-
async (input,
|
|
757
|
+
async (input, ctx) =>
|
|
731
758
|
safeToolResult(async () =>
|
|
732
759
|
jsonResult(
|
|
733
760
|
await runDatabaseCommand({
|
|
@@ -735,7 +762,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
735
762
|
cwd,
|
|
736
763
|
captureOutput: true,
|
|
737
764
|
maxOutputBytes: defaultDatabaseCommandMaxOutputBytes,
|
|
738
|
-
signal:
|
|
765
|
+
signal: ctx.mcpReq.signal,
|
|
739
766
|
timeoutMs: input.timeoutMs ?? defaultDatabaseCommandTimeoutMs,
|
|
740
767
|
dryRun: input.dryRun,
|
|
741
768
|
}),
|
|
@@ -766,14 +793,14 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
766
793
|
openWorldHint: true,
|
|
767
794
|
},
|
|
768
795
|
},
|
|
769
|
-
async (input,
|
|
796
|
+
async (input, ctx) =>
|
|
770
797
|
safeToolResult(async () =>
|
|
771
798
|
jsonResult(
|
|
772
799
|
await runDatabaseStatus({
|
|
773
800
|
cwd,
|
|
774
801
|
captureOutput: true,
|
|
775
802
|
maxOutputBytes: defaultDatabaseCommandMaxOutputBytes,
|
|
776
|
-
signal:
|
|
803
|
+
signal: ctx.mcpReq.signal,
|
|
777
804
|
timeoutMs: input.timeoutMs ?? defaultDatabaseCommandTimeoutMs,
|
|
778
805
|
}),
|
|
779
806
|
),
|
|
@@ -818,7 +845,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
818
845
|
openWorldHint: false,
|
|
819
846
|
},
|
|
820
847
|
},
|
|
821
|
-
async (input,
|
|
848
|
+
async (input, ctx) =>
|
|
822
849
|
safeToolResult(async () =>
|
|
823
850
|
jsonResult(
|
|
824
851
|
await syncDatabaseSchema({
|
|
@@ -827,7 +854,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
827
854
|
tables: input.tables,
|
|
828
855
|
output: input.output,
|
|
829
856
|
dryRun: input.dryRun,
|
|
830
|
-
signal:
|
|
857
|
+
signal: ctx.mcpReq.signal,
|
|
831
858
|
}),
|
|
832
859
|
),
|
|
833
860
|
),
|
|
@@ -846,7 +873,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
846
873
|
openWorldHint: true,
|
|
847
874
|
},
|
|
848
875
|
},
|
|
849
|
-
async (input,
|
|
876
|
+
async (input, ctx) =>
|
|
850
877
|
safeToolResult(async () =>
|
|
851
878
|
operationalJsonResult(
|
|
852
879
|
await runIsolatedOperationalCommand({
|
|
@@ -858,7 +885,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
858
885
|
tenant: input.tenant,
|
|
859
886
|
modulePath: input.module,
|
|
860
887
|
},
|
|
861
|
-
signal:
|
|
888
|
+
signal: ctx.mcpReq.signal,
|
|
862
889
|
timeoutMs: input.timeoutMs ?? defaultOperationalCommandTimeoutMs,
|
|
863
890
|
}),
|
|
864
891
|
),
|
|
@@ -878,7 +905,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
878
905
|
openWorldHint: true,
|
|
879
906
|
},
|
|
880
907
|
},
|
|
881
|
-
async (input,
|
|
908
|
+
async (input, ctx) =>
|
|
882
909
|
safeToolResult(async () =>
|
|
883
910
|
operationalJsonResult(
|
|
884
911
|
await runIsolatedOperationalCommand({
|
|
@@ -894,7 +921,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
894
921
|
source: input.source,
|
|
895
922
|
modulePath: input.module,
|
|
896
923
|
},
|
|
897
|
-
signal:
|
|
924
|
+
signal: ctx.mcpReq.signal,
|
|
898
925
|
timeoutMs: input.timeoutMs ?? defaultOperationalCommandTimeoutMs,
|
|
899
926
|
}),
|
|
900
927
|
),
|
|
@@ -914,7 +941,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
914
941
|
openWorldHint: true,
|
|
915
942
|
},
|
|
916
943
|
},
|
|
917
|
-
async (input,
|
|
944
|
+
async (input, ctx) =>
|
|
918
945
|
safeToolResult(async () =>
|
|
919
946
|
operationalJsonResult(
|
|
920
947
|
await runIsolatedOperationalCommand({
|
|
@@ -934,7 +961,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
934
961
|
id: input.id,
|
|
935
962
|
modulePath: input.module,
|
|
936
963
|
},
|
|
937
|
-
signal:
|
|
964
|
+
signal: ctx.mcpReq.signal,
|
|
938
965
|
timeoutMs: input.timeoutMs ?? defaultOperationalCommandTimeoutMs,
|
|
939
966
|
}),
|
|
940
967
|
),
|
|
@@ -954,7 +981,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
954
981
|
openWorldHint: true,
|
|
955
982
|
},
|
|
956
983
|
},
|
|
957
|
-
async (input,
|
|
984
|
+
async (input, ctx) =>
|
|
958
985
|
safeToolResult(async () => {
|
|
959
986
|
const request =
|
|
960
987
|
input.operation === "drain"
|
|
@@ -991,7 +1018,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
991
1018
|
await runIsolatedOperationalCommand({
|
|
992
1019
|
cwd,
|
|
993
1020
|
request,
|
|
994
|
-
signal:
|
|
1021
|
+
signal: ctx.mcpReq.signal,
|
|
995
1022
|
timeoutMs: input.timeoutMs ?? defaultOperationalCommandTimeoutMs,
|
|
996
1023
|
}),
|
|
997
1024
|
);
|
|
@@ -1169,7 +1196,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
1169
1196
|
async function readAppGuidance(cwd: string): Promise<string> {
|
|
1170
1197
|
const guidancePath = await resolveAppGuidancePath(cwd);
|
|
1171
1198
|
if (!guidancePath) {
|
|
1172
|
-
return "# Beignet app guidance\n\nNo app-local AGENTS.md was found. Use
|
|
1199
|
+
return "# Beignet app guidance\n\nNo app-local AGENTS.md was found. Use app_map and explain before editing, prefer Beignet generators, use app_map with changed=true to inspect potential consumers after editing in Git, and then run check.\n";
|
|
1173
1200
|
}
|
|
1174
1201
|
|
|
1175
1202
|
let file: Awaited<ReturnType<typeof open>>;
|