@beignet/cli 0.0.39 → 0.0.41
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 +18 -0
- package/README.md +110 -16
- package/dist/analysis/source-index.d.ts +38 -0
- package/dist/analysis/source-index.d.ts.map +1 -0
- package/dist/analysis/source-index.js +271 -0
- package/dist/analysis/source-index.js.map +1 -0
- package/dist/analysis/workspace.d.ts +16 -0
- package/dist/analysis/workspace.d.ts.map +1 -0
- package/dist/analysis/workspace.js +134 -0
- package/dist/analysis/workspace.js.map +1 -0
- package/dist/app-map-schema.d.ts +5 -0
- package/dist/app-map-schema.d.ts.map +1 -0
- package/dist/app-map-schema.js +26 -0
- package/dist/app-map-schema.js.map +1 -0
- package/dist/app-map.d.ts +96 -0
- package/dist/app-map.d.ts.map +1 -0
- package/dist/app-map.js +1141 -0
- package/dist/app-map.js.map +1 -0
- package/dist/check.d.ts +7 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +20 -5
- package/dist/check.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/db.d.ts +32 -7
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +182 -14
- package/dist/db.js.map +1 -1
- package/dist/explain.d.ts +98 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +512 -0
- package/dist/explain.js.map +1 -0
- package/dist/framework.d.ts +3 -0
- package/dist/framework.d.ts.map +1 -0
- package/dist/framework.js +6 -0
- package/dist/framework.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +79 -2
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +4 -2
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +173 -36
- package/dist/inspect.js.map +1 -1
- package/dist/lib.d.ts +4 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +2 -0
- package/dist/lib.js.map +1 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +2 -0
- package/dist/make/payments.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +157 -3
- 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 +69 -1
- package/dist/mcp.js.map +1 -1
- package/dist/preflight.d.ts.map +1 -1
- package/dist/preflight.js +10 -0
- package/dist/preflight.js.map +1 -1
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +167 -13
- package/dist/provider-add.js.map +1 -1
- package/dist/provider-audit.d.ts +6 -0
- package/dist/provider-audit.d.ts.map +1 -1
- package/dist/provider-audit.js +63 -15
- package/dist/provider-audit.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +17 -4
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +10 -4
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/index.d.ts +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +8 -1
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts +3 -0
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +55 -1
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shadcn.js +1 -1
- package/dist/templates/shared.js +1 -1
- package/package.json +2 -2
- package/skills/app-structure/SKILL.md +38 -5
- package/src/analysis/source-index.ts +395 -0
- package/src/analysis/workspace.ts +180 -0
- package/src/app-map-schema.ts +28 -0
- package/src/app-map.ts +1705 -0
- package/src/check.ts +27 -4
- package/src/config.ts +7 -3
- package/src/db.ts +232 -14
- package/src/explain.ts +786 -0
- package/src/framework.ts +13 -0
- package/src/index.ts +113 -2
- package/src/inspect.ts +259 -40
- package/src/lib.ts +36 -0
- package/src/make/payments.ts +7 -0
- package/src/make.ts +232 -2
- package/src/mcp.ts +107 -1
- package/src/preflight.ts +14 -0
- package/src/provider-add.ts +211 -12
- package/src/provider-audit.ts +127 -22
- package/src/templates/agents.ts +17 -4
- package/src/templates/base.ts +22 -4
- package/src/templates/index.ts +18 -1
- package/src/templates/server.ts +58 -1
- package/src/templates/shadcn.ts +1 -1
- package/src/templates/shared.ts +1 -1
- package/src/test-helpers/generated-app.ts +1 -1
package/src/lib.ts
CHANGED
|
@@ -6,9 +6,45 @@
|
|
|
6
6
|
* loads command modules lazily.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
export type {
|
|
10
|
+
AppMapEdge,
|
|
11
|
+
AppMapEdgeKind,
|
|
12
|
+
AppMapNode,
|
|
13
|
+
AppMapNodeKind,
|
|
14
|
+
AppMapNodeStatus,
|
|
15
|
+
AppMapOptions,
|
|
16
|
+
AppMapProjectionOptions,
|
|
17
|
+
AppMapResult,
|
|
18
|
+
AppMapSource,
|
|
19
|
+
AppMapSummary,
|
|
20
|
+
AppMapUnresolvedReference,
|
|
21
|
+
} from "./app-map.js";
|
|
22
|
+
export {
|
|
23
|
+
appMapNodeKinds,
|
|
24
|
+
formatAppMap,
|
|
25
|
+
mapApp,
|
|
26
|
+
projectAppMap,
|
|
27
|
+
} from "./app-map.js";
|
|
9
28
|
export type { CreateOptions } from "./create.js";
|
|
10
29
|
export { createProject } from "./create.js";
|
|
11
30
|
export { runDatabaseCommand } from "./db.js";
|
|
31
|
+
export type {
|
|
32
|
+
ExplainAppOptions,
|
|
33
|
+
ExplainAppResult,
|
|
34
|
+
ExplainEvidence,
|
|
35
|
+
ExplainFinding,
|
|
36
|
+
ExplainRelatedNode,
|
|
37
|
+
ExplainRelationship,
|
|
38
|
+
ExplainSuggestedAction,
|
|
39
|
+
ExplainSuggestedFile,
|
|
40
|
+
ExplainTarget,
|
|
41
|
+
ExplainTargetKind,
|
|
42
|
+
} from "./explain.js";
|
|
43
|
+
export {
|
|
44
|
+
explainApp,
|
|
45
|
+
explainTargetKinds,
|
|
46
|
+
formatExplain,
|
|
47
|
+
} from "./explain.js";
|
|
12
48
|
export { main } from "./index.js";
|
|
13
49
|
export {
|
|
14
50
|
applyDoctorFixes,
|
package/src/make/payments.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type ResolvedBeignetConfig,
|
|
7
7
|
resolveConfig,
|
|
8
8
|
} from "../config.js";
|
|
9
|
+
import { assertNextFramework } from "../framework.js";
|
|
9
10
|
import { appendToNamedArray, insertAfterImports } from "../registry-edits.js";
|
|
10
11
|
import { testSupportTemplateFiles } from "../templates/testing.js";
|
|
11
12
|
import {
|
|
@@ -71,6 +72,12 @@ export async function makePayments(
|
|
|
71
72
|
? resolveConfig(options.config)
|
|
72
73
|
: await loadBeignetConfig(targetDir);
|
|
73
74
|
|
|
75
|
+
assertNextFramework(
|
|
76
|
+
config,
|
|
77
|
+
"beignet make payments",
|
|
78
|
+
"Next.js App Router billing and webhook handlers",
|
|
79
|
+
);
|
|
80
|
+
|
|
74
81
|
await assertStandardApp(targetDir, config);
|
|
75
82
|
|
|
76
83
|
const persistence = await detectResourcePersistence(targetDir, config);
|
package/src/make.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type ResolvedBeignetConfig,
|
|
12
12
|
resolveConfig,
|
|
13
13
|
} from "./config.js";
|
|
14
|
+
import { assertNextFramework } from "./framework.js";
|
|
14
15
|
import {
|
|
15
16
|
adapterFilePath,
|
|
16
17
|
addNamedImport,
|
|
@@ -143,6 +144,7 @@ import {
|
|
|
143
144
|
} from "./make/shared.js";
|
|
144
145
|
import {
|
|
145
146
|
appendToArrayExpression,
|
|
147
|
+
appendToNamedArray,
|
|
146
148
|
appendToOutboxRegistryArray,
|
|
147
149
|
arrayInitializerInfo,
|
|
148
150
|
identifiersFromArrayExpression,
|
|
@@ -1088,6 +1090,7 @@ export async function makeJob(options: MakeJobOptions): Promise<MakeJobResult> {
|
|
|
1088
1090
|
await updateOutboxRegistry(targetDir, names, "jobs", config, {
|
|
1089
1091
|
dryRun: true,
|
|
1090
1092
|
});
|
|
1093
|
+
await updateInngestJobRegistry(targetDir, names, config, { dryRun: true });
|
|
1091
1094
|
|
|
1092
1095
|
const result = await makeFeatureArtifact({
|
|
1093
1096
|
targetDir,
|
|
@@ -1114,6 +1117,9 @@ export async function makeJob(options: MakeJobOptions): Promise<MakeJobResult> {
|
|
|
1114
1117
|
await applyOutboxPortWiring(result, targetDir, config, {
|
|
1115
1118
|
dryRun: Boolean(options.dryRun),
|
|
1116
1119
|
});
|
|
1120
|
+
await applyInngestJobRegistryUpdate(result, targetDir, names, config, {
|
|
1121
|
+
dryRun: Boolean(options.dryRun),
|
|
1122
|
+
});
|
|
1117
1123
|
|
|
1118
1124
|
return result;
|
|
1119
1125
|
}
|
|
@@ -1499,6 +1505,14 @@ export async function makeSchedule(
|
|
|
1499
1505
|
? resolveConfig(options.config)
|
|
1500
1506
|
: await loadBeignetConfig(targetDir);
|
|
1501
1507
|
|
|
1508
|
+
if (options.route) {
|
|
1509
|
+
assertNextFramework(
|
|
1510
|
+
config,
|
|
1511
|
+
"beignet make schedule --route",
|
|
1512
|
+
"a Next.js App Router cron handler",
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1502
1516
|
await assertFeatureArtifactApp(targetDir, config, "schedule", {
|
|
1503
1517
|
requireServer: Boolean(options.route),
|
|
1504
1518
|
});
|
|
@@ -1714,6 +1728,9 @@ async function updateOutboxPortWiring(
|
|
|
1714
1728
|
path.join(infrastructureDir(config), "db/provider.ts"),
|
|
1715
1729
|
);
|
|
1716
1730
|
}
|
|
1731
|
+
if (await updateOutboxDrainProvider(targetDir, config, options)) {
|
|
1732
|
+
changes.updatedFiles.push(providersFilePath(config));
|
|
1733
|
+
}
|
|
1717
1734
|
if (await updateOutboxRepositoriesReturnType(targetDir, config, options)) {
|
|
1718
1735
|
changes.updatedFiles.push(drizzleRepositoriesPath(config));
|
|
1719
1736
|
}
|
|
@@ -1825,6 +1842,31 @@ async function updateOutboxInfrastructurePorts(
|
|
|
1825
1842
|
return true;
|
|
1826
1843
|
}
|
|
1827
1844
|
|
|
1845
|
+
async function projectSupportsNextAfter(targetDir: string): Promise<boolean> {
|
|
1846
|
+
const packageJson = JSON.parse(
|
|
1847
|
+
await readFile(path.join(targetDir, "package.json"), "utf8"),
|
|
1848
|
+
) as PackageJsonLike;
|
|
1849
|
+
const version =
|
|
1850
|
+
packageJson.dependencies?.next ?? packageJson.devDependencies?.next;
|
|
1851
|
+
if (!version) return false;
|
|
1852
|
+
|
|
1853
|
+
return version.split("||").every((range) => {
|
|
1854
|
+
const lowerBound =
|
|
1855
|
+
/^\s*(\^|~|>=|>|=|<=|<)?\s*v?(\d+)(?:\.(\d+|x|\*))?(?:\.(\d+|x|\*))?([+-][0-9A-Za-z.-]+)?(?=\s|$)/.exec(
|
|
1856
|
+
range,
|
|
1857
|
+
);
|
|
1858
|
+
if (!lowerBound) return false;
|
|
1859
|
+
|
|
1860
|
+
const operator = lowerBound[1];
|
|
1861
|
+
if (operator === "<" || operator === "<=") return false;
|
|
1862
|
+
if (lowerBound[5]?.startsWith("-")) return false;
|
|
1863
|
+
|
|
1864
|
+
const major = Number(lowerBound[2]);
|
|
1865
|
+
const minor = /^\d+$/.test(lowerBound[3] ?? "") ? Number(lowerBound[3]) : 0;
|
|
1866
|
+
return major > 15 || (major === 15 && minor >= 1);
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1828
1870
|
async function updateOutboxDatabaseProvider(
|
|
1829
1871
|
targetDir: string,
|
|
1830
1872
|
config: ResolvedBeignetConfig,
|
|
@@ -1923,6 +1965,95 @@ async function updateOutboxDatabaseProvider(
|
|
|
1923
1965
|
return true;
|
|
1924
1966
|
}
|
|
1925
1967
|
|
|
1968
|
+
async function updateOutboxDrainProvider(
|
|
1969
|
+
targetDir: string,
|
|
1970
|
+
config: ResolvedBeignetConfig,
|
|
1971
|
+
options: { dryRun: boolean },
|
|
1972
|
+
): Promise<boolean> {
|
|
1973
|
+
if (!(await projectSupportsNextAfter(targetDir))) return false;
|
|
1974
|
+
|
|
1975
|
+
const file = providersFilePath(config);
|
|
1976
|
+
const filePath = path.join(targetDir, file);
|
|
1977
|
+
const original = await readOptionalFile(filePath);
|
|
1978
|
+
if (original === undefined) {
|
|
1979
|
+
throw new Error(
|
|
1980
|
+
`Could not find the generated providers file ${file}. Register the outbox drain trigger manually, or restore the generated providers file before running make outbox.`,
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
let next = addNamedImport(
|
|
1985
|
+
original,
|
|
1986
|
+
"createObservedUnitOfWork",
|
|
1987
|
+
"@beignet/core/ports",
|
|
1988
|
+
);
|
|
1989
|
+
next = addNamedImport(next, "createProvider", "@beignet/core/providers");
|
|
1990
|
+
next = addNamedImport(next, "createNextOutboxDrainTrigger", "@beignet/next");
|
|
1991
|
+
next = addNamedImport(next, "after", "next/server");
|
|
1992
|
+
next = addNamedTypeImport(
|
|
1993
|
+
next,
|
|
1994
|
+
"AppContext",
|
|
1995
|
+
aliasModule(config.paths.appContext),
|
|
1996
|
+
);
|
|
1997
|
+
next = addNamedTypeImport(next, "AppPorts", aliasModule(config.paths.ports));
|
|
1998
|
+
next = addNamedTypeImport(
|
|
1999
|
+
next,
|
|
2000
|
+
"AppServiceContextInput",
|
|
2001
|
+
aliasModule(path.join(path.dirname(config.paths.server), "context.ts")),
|
|
2002
|
+
);
|
|
2003
|
+
|
|
2004
|
+
const providerName = "outboxDrainProvider";
|
|
2005
|
+
if (!new RegExp(`\\bconst\\s+${providerName}\\b`).test(next)) {
|
|
2006
|
+
const outboxModule = aliasModule(config.paths.outbox);
|
|
2007
|
+
const providerDefinition = `const ${providerName} = createProvider<
|
|
2008
|
+
\tPick<AppPorts, "uow">,
|
|
2009
|
+
\tAppContext,
|
|
2010
|
+
\tAppServiceContextInput
|
|
2011
|
+
>()({
|
|
2012
|
+
\tname: "outbox-drain-trigger",
|
|
2013
|
+
\tsetup({ ports, createServiceContext }): { ports: Pick<AppPorts, "uow"> } {
|
|
2014
|
+
\t\tconst triggerOutboxDrain: () => void = createNextOutboxDrainTrigger({
|
|
2015
|
+
\t\t\tdefer: after,
|
|
2016
|
+
\t\t\tcreateContext: () => createServiceContext(undefined),
|
|
2017
|
+
\t\t\tregistry: async () => (await import("${outboxModule}")).outboxRegistry,
|
|
2018
|
+
\t\t\tbatchSize: 100,
|
|
2019
|
+
\t\t});
|
|
2020
|
+
|
|
2021
|
+
\t\treturn {
|
|
2022
|
+
\t\t\tports: {
|
|
2023
|
+
\t\t\t\tuow: createObservedUnitOfWork({
|
|
2024
|
+
\t\t\t\t\tunitOfWork: ports.uow,
|
|
2025
|
+
\t\t\t\t\tafterCommit: triggerOutboxDrain,
|
|
2026
|
+
\t\t\t\t}),
|
|
2027
|
+
\t\t\t},
|
|
2028
|
+
\t\t};
|
|
2029
|
+
\t},
|
|
2030
|
+
});
|
|
2031
|
+
`;
|
|
2032
|
+
const withProvider = next.replace(
|
|
2033
|
+
/\nexport const providers = \[/,
|
|
2034
|
+
`\n${providerDefinition}\nexport const providers = [`,
|
|
2035
|
+
);
|
|
2036
|
+
if (withProvider === next) {
|
|
2037
|
+
throw new Error(
|
|
2038
|
+
`Could not find the exported providers array in ${file}. Register outboxDrainProvider manually, or restore the generated providers file before running make outbox.`,
|
|
2039
|
+
);
|
|
2040
|
+
}
|
|
2041
|
+
next = withProvider;
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
const appended = appendToNamedArray(next, "providers", providerName);
|
|
2045
|
+
if (appended.kind === "missing") {
|
|
2046
|
+
throw new Error(
|
|
2047
|
+
`Could not find the exported providers array in ${file}. Register outboxDrainProvider manually, or restore the generated providers file before running make outbox.`,
|
|
2048
|
+
);
|
|
2049
|
+
}
|
|
2050
|
+
if (appended.kind === "updated") next = appended.source;
|
|
2051
|
+
|
|
2052
|
+
if (next === original) return false;
|
|
2053
|
+
if (!options.dryRun) await writeFile(filePath, next);
|
|
2054
|
+
return true;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
1926
2057
|
async function updateOutboxRepositoriesReturnType(
|
|
1927
2058
|
targetDir: string,
|
|
1928
2059
|
config: ResolvedBeignetConfig,
|
|
@@ -2068,6 +2199,12 @@ export async function makeUpload(
|
|
|
2068
2199
|
? resolveConfig(options.config)
|
|
2069
2200
|
: await loadBeignetConfig(targetDir);
|
|
2070
2201
|
|
|
2202
|
+
assertNextFramework(
|
|
2203
|
+
config,
|
|
2204
|
+
"beignet make upload",
|
|
2205
|
+
"a Next.js App Router upload handler",
|
|
2206
|
+
);
|
|
2207
|
+
|
|
2071
2208
|
await assertFeatureArtifactApp(targetDir, config, "upload");
|
|
2072
2209
|
if (options.ui) {
|
|
2073
2210
|
await assertUploadUiApp(targetDir, config);
|
|
@@ -2168,6 +2305,12 @@ export async function makeOutbox(
|
|
|
2168
2305
|
? resolveConfig(options.config)
|
|
2169
2306
|
: await loadBeignetConfig(targetDir);
|
|
2170
2307
|
|
|
2308
|
+
assertNextFramework(
|
|
2309
|
+
config,
|
|
2310
|
+
"beignet make outbox",
|
|
2311
|
+
"a Next.js App Router recovery drain handler",
|
|
2312
|
+
);
|
|
2313
|
+
|
|
2171
2314
|
await assertServerRuntimeApp(targetDir, config, "outbox");
|
|
2172
2315
|
await updateOutboxPortWiring(targetDir, config, { dryRun: true });
|
|
2173
2316
|
|
|
@@ -2219,6 +2362,9 @@ export async function makeOutbox(
|
|
|
2219
2362
|
config.paths.ports,
|
|
2220
2363
|
config.paths.portWiring,
|
|
2221
2364
|
path.join(infrastructureDir(config), "db/provider.ts"),
|
|
2365
|
+
...((await projectSupportsNextAfter(targetDir))
|
|
2366
|
+
? [providersFilePath(config)]
|
|
2367
|
+
: []),
|
|
2222
2368
|
drizzleRepositoriesPath(config),
|
|
2223
2369
|
outboxDrizzleSchemaFilePath(config),
|
|
2224
2370
|
drizzleSchemaIndexPath(config),
|
|
@@ -3672,6 +3818,92 @@ async function applyOutboxRegistryUpdate(
|
|
|
3672
3818
|
}
|
|
3673
3819
|
}
|
|
3674
3820
|
|
|
3821
|
+
function inngestRegistryPath(config: ResolvedBeignetConfig): string {
|
|
3822
|
+
return path.posix.join(path.posix.dirname(config.paths.server), "inngest.ts");
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
async function appUsesInngestJobs(targetDir: string): Promise<boolean> {
|
|
3826
|
+
const source = await readOptionalFile(path.join(targetDir, "package.json"));
|
|
3827
|
+
if (source === undefined) return false;
|
|
3828
|
+
|
|
3829
|
+
try {
|
|
3830
|
+
const packageJson = JSON.parse(source) as PackageJsonLike;
|
|
3831
|
+
return Boolean(
|
|
3832
|
+
packageJson.dependencies?.["@beignet/provider-jobs-inngest"] ??
|
|
3833
|
+
packageJson.devDependencies?.["@beignet/provider-jobs-inngest"],
|
|
3834
|
+
);
|
|
3835
|
+
} catch {
|
|
3836
|
+
return false;
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
async function updateInngestJobRegistry(
|
|
3841
|
+
targetDir: string,
|
|
3842
|
+
names: JobNames,
|
|
3843
|
+
config: ResolvedBeignetConfig,
|
|
3844
|
+
options: { dryRun: boolean },
|
|
3845
|
+
): Promise<WriteGeneratedFileResult | undefined> {
|
|
3846
|
+
if (!(await appUsesInngestJobs(targetDir))) return undefined;
|
|
3847
|
+
|
|
3848
|
+
const registryPath = inngestRegistryPath(config);
|
|
3849
|
+
const destination = path.join(targetDir, registryPath);
|
|
3850
|
+
const existing = await readOptionalFile(destination);
|
|
3851
|
+
if (existing === undefined) {
|
|
3852
|
+
throw new Error(
|
|
3853
|
+
`@beignet/provider-jobs-inngest is installed, but ${registryPath} is missing. Run \`beignet provider add jobs-inngest\` to create the execution registry before generating jobs.`,
|
|
3854
|
+
);
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
const featureIndexPath = path.join(
|
|
3858
|
+
featureArtifactDir(names, "jobs", config),
|
|
3859
|
+
"index.ts",
|
|
3860
|
+
);
|
|
3861
|
+
const registryName = `${names.featureSingularCamel}Jobs`;
|
|
3862
|
+
const importLine = `import { ${registryName} } from "${aliasModule(featureIndexPath)}";`;
|
|
3863
|
+
let next = existing;
|
|
3864
|
+
if (!next.includes(importLine)) next = insertAfterImports(next, importLine);
|
|
3865
|
+
|
|
3866
|
+
const registry = arrayInitializerInfo(next, "inngestJobs");
|
|
3867
|
+
if (!registry) {
|
|
3868
|
+
throw new Error(
|
|
3869
|
+
`Could not find \`export const inngestJobs = [...]\` in ${registryPath}. Restore the generated registry or register ${registryName} manually.`,
|
|
3870
|
+
);
|
|
3871
|
+
}
|
|
3872
|
+
if (!identifiersFromArrayExpression(registry.text).has(registryName)) {
|
|
3873
|
+
const updatedArray = appendToArrayExpression(registry.text, [
|
|
3874
|
+
`...${registryName}`,
|
|
3875
|
+
]);
|
|
3876
|
+
next = `${next.slice(0, registry.start)}${updatedArray}${next.slice(
|
|
3877
|
+
registry.end,
|
|
3878
|
+
)}`;
|
|
3879
|
+
}
|
|
3880
|
+
|
|
3881
|
+
if (next === existing) return "skipped";
|
|
3882
|
+
if (!options.dryRun) await writeFile(destination, next);
|
|
3883
|
+
return "updated";
|
|
3884
|
+
}
|
|
3885
|
+
|
|
3886
|
+
async function applyInngestJobRegistryUpdate(
|
|
3887
|
+
result: MakeJobResult,
|
|
3888
|
+
targetDir: string,
|
|
3889
|
+
names: JobNames,
|
|
3890
|
+
config: ResolvedBeignetConfig,
|
|
3891
|
+
options: { dryRun: boolean },
|
|
3892
|
+
): Promise<void> {
|
|
3893
|
+
const registryResult = await updateInngestJobRegistry(
|
|
3894
|
+
targetDir,
|
|
3895
|
+
names,
|
|
3896
|
+
config,
|
|
3897
|
+
options,
|
|
3898
|
+
);
|
|
3899
|
+
if (registryResult === undefined) return;
|
|
3900
|
+
|
|
3901
|
+
const registryPath = inngestRegistryPath(config);
|
|
3902
|
+
if (registryResult === "updated") result.updatedFiles.push(registryPath);
|
|
3903
|
+
if (registryResult === "skipped") result.skippedFiles.push(registryPath);
|
|
3904
|
+
if (!result.files.includes(registryPath)) result.files.push(registryPath);
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3675
3907
|
function schemasFile(
|
|
3676
3908
|
names: ResourceNames,
|
|
3677
3909
|
mode: ResourceGenerationMode,
|
|
@@ -5003,8 +5235,6 @@ export const ${names.jobExportName} = defineJob("${names.jobName}", {
|
|
|
5003
5235
|
\tpayload: ${names.payloadSchemaName},
|
|
5004
5236
|
\tretry: retry.exponential({
|
|
5005
5237
|
\t\tattempts: 3,
|
|
5006
|
-
\t\tinitialDelay: "1s",
|
|
5007
|
-
\t\tmaxDelay: "1m",
|
|
5008
5238
|
\t}),
|
|
5009
5239
|
\tasync handle({ payload, ctx }) {
|
|
5010
5240
|
\t\tctx.ports.logger.info("Job handled", {
|
package/src/mcp.ts
CHANGED
|
@@ -2,7 +2,11 @@ import path from "node:path";
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { mapApp, projectAppMap } from "./app-map.js";
|
|
6
|
+
import { appMapNodeKinds } from "./app-map-schema.js";
|
|
7
|
+
import { checkApp } from "./check.js";
|
|
5
8
|
import { type ProviderPresetName, providerPresetChoices } from "./choices.js";
|
|
9
|
+
import { explainApp, explainTargetKinds } from "./explain.js";
|
|
6
10
|
import { applyDoctorFixes, inspectApp } from "./inspect.js";
|
|
7
11
|
import { lintApp } from "./lint.js";
|
|
8
12
|
import {
|
|
@@ -68,6 +72,8 @@ const makeArtifactChoices = [
|
|
|
68
72
|
"use-case",
|
|
69
73
|
] as const;
|
|
70
74
|
|
|
75
|
+
const defaultMcpCheckTimeoutMs = 10 * 60 * 1_000;
|
|
76
|
+
|
|
71
77
|
type MakeArtifact = (typeof makeArtifactChoices)[number];
|
|
72
78
|
|
|
73
79
|
const makeInputSchema = {
|
|
@@ -325,7 +331,7 @@ async function runProviderAddTool(
|
|
|
325
331
|
}
|
|
326
332
|
|
|
327
333
|
/**
|
|
328
|
-
* Build the Beignet MCP server with
|
|
334
|
+
* Build the Beignet MCP server with app-map, validation, and generator tools.
|
|
329
335
|
* Every tool resolves against the app directory captured at launch.
|
|
330
336
|
*/
|
|
331
337
|
export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
@@ -335,6 +341,106 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
|
|
|
335
341
|
version: options.version,
|
|
336
342
|
});
|
|
337
343
|
|
|
344
|
+
server.registerTool(
|
|
345
|
+
"app_map",
|
|
346
|
+
{
|
|
347
|
+
description:
|
|
348
|
+
"Return a deterministic graph of this Beignet app: features, contracts, routes, use cases, authorization, workflows, ports, providers, OpenAPI, tests, dependency edges, and validation findings. Project by feature or node kind to keep agent context focused.",
|
|
349
|
+
inputSchema: {
|
|
350
|
+
feature: z
|
|
351
|
+
.string()
|
|
352
|
+
.optional()
|
|
353
|
+
.describe("Project one feature and its direct relationships."),
|
|
354
|
+
kinds: z
|
|
355
|
+
.array(z.enum(appMapNodeKinds))
|
|
356
|
+
.optional()
|
|
357
|
+
.describe("Include only these app-map node kinds."),
|
|
358
|
+
includeDiagnostics: z
|
|
359
|
+
.boolean()
|
|
360
|
+
.optional()
|
|
361
|
+
.describe("Include doctor and lint findings. Defaults to true."),
|
|
362
|
+
},
|
|
363
|
+
annotations: { readOnlyHint: true },
|
|
364
|
+
},
|
|
365
|
+
async (input) =>
|
|
366
|
+
safeToolResult(async () => {
|
|
367
|
+
const result = await mapApp({ cwd, strict: true });
|
|
368
|
+
return jsonResult(
|
|
369
|
+
projectAppMap(result, {
|
|
370
|
+
feature: input.feature,
|
|
371
|
+
kinds: input.kinds,
|
|
372
|
+
includeDiagnostics: input.includeDiagnostics,
|
|
373
|
+
}),
|
|
374
|
+
);
|
|
375
|
+
}),
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
server.registerTool(
|
|
379
|
+
"explain",
|
|
380
|
+
{
|
|
381
|
+
description:
|
|
382
|
+
"Explain one mapped Beignet feature, route, provider, or diagnostic using deterministic source evidence. Returns relationships, relevant conventions, findings, suggested files, and follow-up commands without changing the app.",
|
|
383
|
+
inputSchema: {
|
|
384
|
+
kind: z.enum(explainTargetKinds).describe("Mapped concept to explain."),
|
|
385
|
+
target: z
|
|
386
|
+
.string()
|
|
387
|
+
.min(1)
|
|
388
|
+
.describe(
|
|
389
|
+
"Feature name, METHOD /path route, provider package or port name, or diagnostic code.",
|
|
390
|
+
),
|
|
391
|
+
},
|
|
392
|
+
annotations: { readOnlyHint: true },
|
|
393
|
+
},
|
|
394
|
+
async (input) =>
|
|
395
|
+
safeToolResult(async () =>
|
|
396
|
+
jsonResult(
|
|
397
|
+
await explainApp({ cwd, kind: input.kind, target: input.target }),
|
|
398
|
+
),
|
|
399
|
+
),
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
server.registerTool(
|
|
403
|
+
"check",
|
|
404
|
+
{
|
|
405
|
+
description:
|
|
406
|
+
"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.",
|
|
407
|
+
inputSchema: {
|
|
408
|
+
preflight: z
|
|
409
|
+
.boolean()
|
|
410
|
+
.optional()
|
|
411
|
+
.describe(
|
|
412
|
+
"Include the environment preflight check before app package scripts. Defaults to false.",
|
|
413
|
+
),
|
|
414
|
+
timeoutMs: z
|
|
415
|
+
.number()
|
|
416
|
+
.int()
|
|
417
|
+
.min(1_000)
|
|
418
|
+
.max(60 * 60 * 1_000)
|
|
419
|
+
.optional()
|
|
420
|
+
.describe(
|
|
421
|
+
"Maximum duration for each app package script in milliseconds. Defaults to 600000 (10 minutes).",
|
|
422
|
+
),
|
|
423
|
+
},
|
|
424
|
+
annotations: {
|
|
425
|
+
readOnlyHint: false,
|
|
426
|
+
destructiveHint: true,
|
|
427
|
+
idempotentHint: false,
|
|
428
|
+
openWorldHint: true,
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
async (input, extra) =>
|
|
432
|
+
safeToolResult(async () =>
|
|
433
|
+
jsonResult(
|
|
434
|
+
await checkApp({
|
|
435
|
+
cwd,
|
|
436
|
+
preflight: input.preflight ?? false,
|
|
437
|
+
scriptTimeoutMs: input.timeoutMs ?? defaultMcpCheckTimeoutMs,
|
|
438
|
+
signal: extra.signal,
|
|
439
|
+
}),
|
|
440
|
+
),
|
|
441
|
+
),
|
|
442
|
+
);
|
|
443
|
+
|
|
338
444
|
server.registerTool(
|
|
339
445
|
"routes",
|
|
340
446
|
{
|
package/src/preflight.ts
CHANGED
|
@@ -245,6 +245,20 @@ export async function runPreflight(
|
|
|
245
245
|
}
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
+
const inngestDev = env.INNGEST_DEV?.trim().toLowerCase();
|
|
249
|
+
if (
|
|
250
|
+
env.NODE_ENV === "production" &&
|
|
251
|
+
inngestDev !== undefined &&
|
|
252
|
+
!["", "0", "false"].includes(inngestDev)
|
|
253
|
+
) {
|
|
254
|
+
findings.push({
|
|
255
|
+
severity: "error",
|
|
256
|
+
code: "BEIGNET_PREFLIGHT_INNGEST_DEV_IN_PRODUCTION",
|
|
257
|
+
message:
|
|
258
|
+
"INNGEST_DEV enables the local Inngest dev server and must be disabled in production.",
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
248
262
|
const exampleSource = await readFileIfExists(
|
|
249
263
|
path.join(options.cwd, ".env.example"),
|
|
250
264
|
);
|