@restforgejs/mcp-server 1.2.1 → 1.2.3
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/dist/server.js +69 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/codegen/dbschema-apply.js +153 -153
- package/dist/tools/codegen/dbschema-diff.js +135 -135
- package/dist/tools/codegen/dbschema-template.js +111 -111
- package/dist/tools/codegen/index.js +2 -0
- package/dist/tools/codegen/index.js.map +1 -1
- package/dist/tools/codegen/migrate-payload.d.ts +2 -0
- package/dist/tools/codegen/migrate-payload.js +207 -0
- package/dist/tools/codegen/migrate-payload.js.map +1 -0
- package/dist/tools/designer/generate.d.ts +2 -0
- package/dist/tools/designer/generate.js +217 -0
- package/dist/tools/designer/generate.js.map +1 -0
- package/dist/tools/designer/get-udf-catalog.d.ts +2 -0
- package/dist/tools/designer/get-udf-catalog.js +225 -0
- package/dist/tools/designer/get-udf-catalog.js.map +1 -0
- package/dist/tools/designer/index.d.ts +2 -0
- package/dist/tools/designer/index.js +19 -0
- package/dist/tools/designer/index.js.map +1 -0
- package/dist/tools/designer/init-project.d.ts +2 -0
- package/dist/tools/designer/init-project.js +235 -0
- package/dist/tools/designer/init-project.js.map +1 -0
- package/dist/tools/designer/inspect-plugin.d.ts +2 -0
- package/dist/tools/designer/inspect-plugin.js +153 -0
- package/dist/tools/designer/inspect-plugin.js.map +1 -0
- package/dist/tools/designer/list-plugins.d.ts +2 -0
- package/dist/tools/designer/list-plugins.js +141 -0
- package/dist/tools/designer/list-plugins.js.map +1 -0
- package/dist/tools/designer/preview-files.d.ts +2 -0
- package/dist/tools/designer/preview-files.js +155 -0
- package/dist/tools/designer/preview-files.js.map +1 -0
- package/dist/tools/designer/scaffold-plugin.d.ts +2 -0
- package/dist/tools/designer/scaffold-plugin.js +162 -0
- package/dist/tools/designer/scaffold-plugin.js.map +1 -0
- package/dist/tools/designer/validate-payload.d.ts +2 -0
- package/dist/tools/designer/validate-payload.js +163 -0
- package/dist/tools/designer/validate-payload.js.map +1 -0
- package/dist/tools/runtime/generate-launcher.js +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { access } from 'node:fs/promises';
|
|
3
|
+
import { resolve, join } from 'node:path';
|
|
4
|
+
import { execProcess } from '../../lib/exec.js';
|
|
5
|
+
export function registerCodegenMigratePayload(server) {
|
|
6
|
+
server.registerTool('codegen_migrate_payload', {
|
|
7
|
+
title: 'Migrate Payload (RDF backend -> UDF frontend)',
|
|
8
|
+
description: `Convert an existing backend payload file (RDF) into a frontend payload (UDF) for RESTForge Designer, by wrapping restforge payload migrate. The output is always a SPLIT multi-file set written into an output directory (app-config.json, one file per page under pages/, and an aggregator <appCode>.json), not a single UDF file. The migrator also auto-discovers JOINed tables, so one JOINed RDF can produce several pages at once.
|
|
9
|
+
|
|
10
|
+
USE WHEN:
|
|
11
|
+
- The user wants to build a frontend UDF from an existing backend payload, e.g. "buat UDF dari payload backend", "konversi RDF ke UDF", "migrate payload ke frontend", "bikin payload designer dari backend existing"
|
|
12
|
+
- The user has a working backend payload and now wants to start authoring the matching frontend in RESTForge Designer (on-ramp from backend to frontend)
|
|
13
|
+
- The user mentions migrating, converting, or porting a backend payload over to the designer / frontend side
|
|
14
|
+
|
|
15
|
+
DO NOT USE FOR:
|
|
16
|
+
- Generating the actual frontend web application from a UDF that already exists -> use the designer generate action (describe it by what it does, do not name the tool)
|
|
17
|
+
- Validating a backend payload against the database schema -> use 'codegen_validate_payload'
|
|
18
|
+
- Generating a backend payload from a database table that has no payload yet -> use 'codegen_generate_payload'
|
|
19
|
+
|
|
20
|
+
This tool runs: npx restforge payload migrate --name=<name> --project=<project> [--output] [--config] [--app-name] [--app-code] [--plugin] [--port] [--overwrite] in the given cwd.
|
|
21
|
+
The CLI reads the backend RDF (resolved relative to cwd or cwd/payload/), reads SERVER_ADDRESS/SERVER_PORT from the DB config (or the default config) to build apiBaseUrl, and writes the split UDF files into the output directory (default frontend/payload/).
|
|
22
|
+
|
|
23
|
+
Cross-reference (downstream UDF flow):
|
|
24
|
+
- This tool is the on-ramp that creates a UDF from an existing backend RDF. Its split UDF output is consumed by the designer tools: validate it with 'designer_validate_payload', dry-run it with 'designer_preview_files', and generate the frontend with 'designer_generate' — always point those at the aggregator file (<appCode>.json), not the individual page fragments.
|
|
25
|
+
- To see the target UDF structure and rules (valid field types, required appConfig fields, enums, limits), use 'designer_get_udf_catalog'.
|
|
26
|
+
- Canonical UDF flow: codegen_migrate_payload -> designer_get_udf_catalog -> designer_validate_payload -> designer_preview_files -> designer_generate.
|
|
27
|
+
|
|
28
|
+
Preconditions:
|
|
29
|
+
- The project must have @restforgejs/platform installed in node_modules.
|
|
30
|
+
- The named RDF payload file must exist (resolved relative to cwd or cwd/payload/).
|
|
31
|
+
- Without --overwrite, the command fails if any split output file already exists in the output directory.
|
|
32
|
+
This tool does not pre-check those — if the CLI fails, the failure response will surface the cause.
|
|
33
|
+
|
|
34
|
+
PRESENTATION GUIDANCE:
|
|
35
|
+
- Match the user's language. If the user writes in Indonesian, respond in Indonesian.
|
|
36
|
+
- Never mention internal tool names in the reply to the user. Describe actions by what they do (e.g. "convert the backend payload into a frontend payload", "validate the frontend payload", "generate the frontend application").
|
|
37
|
+
- Speak in plain language. Confirm the migration, mention the output directory and that the result is a split multi-file UDF; do not paste the raw CLI output unless the user explicitly asks.
|
|
38
|
+
- After a successful migration, the usual next steps are to validate the resulting UDF, preview it, or generate the frontend application from it. Describe those steps by what they do; do not name internal tools.
|
|
39
|
+
- When a precondition is not met, frame it as a question or next-step suggestion rather than an error.`,
|
|
40
|
+
inputSchema: {
|
|
41
|
+
cwd: z
|
|
42
|
+
.string()
|
|
43
|
+
.min(1)
|
|
44
|
+
.describe('Absolute path of the backend project folder (must contain node_modules/@restforgejs/platform; the RDF payload is read from here)'),
|
|
45
|
+
name: z
|
|
46
|
+
.string()
|
|
47
|
+
.min(1)
|
|
48
|
+
.describe('Backend RDF payload file name (e.g. visitors.json). Resolved relative to cwd or cwd/payload/. REQUIRED.'),
|
|
49
|
+
project: z
|
|
50
|
+
.string()
|
|
51
|
+
.min(1)
|
|
52
|
+
.describe('Project name in kebab-case, used as the path segment in apiBaseUrl (http://{host}:{port}/api/{project}). REQUIRED.'),
|
|
53
|
+
output: z
|
|
54
|
+
.string()
|
|
55
|
+
.min(1)
|
|
56
|
+
.optional()
|
|
57
|
+
.describe('Output directory for the split UDF files, relative to cwd. When omitted, the CLI uses its default (frontend/payload/). A value ending in .json is reduced to its parent directory.'),
|
|
58
|
+
config: z
|
|
59
|
+
.string()
|
|
60
|
+
.min(1)
|
|
61
|
+
.optional()
|
|
62
|
+
.describe('Database config file (.env) read for SERVER_ADDRESS/SERVER_PORT (backend host/port). When omitted, the CLI falls back to the default config.'),
|
|
63
|
+
appName: z
|
|
64
|
+
.string()
|
|
65
|
+
.min(1)
|
|
66
|
+
.optional()
|
|
67
|
+
.describe('Value for appConfig.appName in the UDF output. When omitted, derived from project in Title Case (e.g. visitors-app -> "Visitors App").'),
|
|
68
|
+
appCode: z
|
|
69
|
+
.string()
|
|
70
|
+
.min(1)
|
|
71
|
+
.optional()
|
|
72
|
+
.describe('Value for appConfig.appCode in the UDF output (kebab-case). Also used as the aggregator file name (<appCode>.json). When omitted, follows project.'),
|
|
73
|
+
plugin: z
|
|
74
|
+
.string()
|
|
75
|
+
.min(1)
|
|
76
|
+
.optional()
|
|
77
|
+
.describe('Designer plugin ID written to appConfig.plugin. When omitted, the CLI uses its default (vanilla-js-basic).'),
|
|
78
|
+
port: z
|
|
79
|
+
.number()
|
|
80
|
+
.int()
|
|
81
|
+
.min(1)
|
|
82
|
+
.max(65535)
|
|
83
|
+
.optional()
|
|
84
|
+
.describe('Frontend application port written to appConfig.port. Independent of the backend port used in apiBaseUrl. When omitted, the CLI uses its default (8000).'),
|
|
85
|
+
overwrite: z
|
|
86
|
+
.boolean()
|
|
87
|
+
.optional()
|
|
88
|
+
.describe('Overwrite existing split output files. Without it, the command fails if any split file already exists in the output directory.'),
|
|
89
|
+
},
|
|
90
|
+
annotations: {
|
|
91
|
+
title: 'Migrate Payload (RDF backend -> UDF frontend)',
|
|
92
|
+
readOnlyHint: false,
|
|
93
|
+
idempotentHint: false, // re-running can fail or overwrite depending on --overwrite
|
|
94
|
+
destructiveHint: false,
|
|
95
|
+
},
|
|
96
|
+
}, async ({ cwd, name, project, output, config, appName, appCode, plugin, port, overwrite }) => {
|
|
97
|
+
const projectCwd = resolve(cwd);
|
|
98
|
+
// Precondition check: @restforgejs/platform must be present in node_modules.
|
|
99
|
+
// Treated as a non-error precondition per the authoring guide §3.4.
|
|
100
|
+
try {
|
|
101
|
+
await access(join(projectCwd, 'node_modules', '@restforgejs', 'platform'));
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: 'text',
|
|
108
|
+
text: `Precondition not met: the RESTForge package is not installed in this project.
|
|
109
|
+
|
|
110
|
+
Project path: ${projectCwd}
|
|
111
|
+
Expected location: node_modules/@restforgejs/platform
|
|
112
|
+
Requested RDF: ${name}
|
|
113
|
+
Requested project: ${project}
|
|
114
|
+
|
|
115
|
+
For the assistant:
|
|
116
|
+
- The user needs to install the RESTForge package before a backend payload can be migrated into a frontend payload.
|
|
117
|
+
- Suggest installing the package first, then retry the migration.
|
|
118
|
+
- When explaining to the user, say something like "the RESTForge package isn't installed yet — should I install it first?". Do not mention internal tool names.`,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
isError: false, // per §3.4
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// Forward only the arguments the user supplied. CLI defaults
|
|
125
|
+
// (frontend/payload/ output, vanilla-js-basic plugin, port 8000, etc.)
|
|
126
|
+
// stay in effect when the user does not specify them. per §3.5
|
|
127
|
+
const args = ['restforge', 'payload', 'migrate', `--name=${name}`, `--project=${project}`];
|
|
128
|
+
if (output)
|
|
129
|
+
args.push(`--output=${output}`);
|
|
130
|
+
if (config)
|
|
131
|
+
args.push(`--config=${config}`);
|
|
132
|
+
if (appName)
|
|
133
|
+
args.push(`--app-name=${appName}`);
|
|
134
|
+
if (appCode)
|
|
135
|
+
args.push(`--app-code=${appCode}`);
|
|
136
|
+
if (plugin)
|
|
137
|
+
args.push(`--plugin=${plugin}`);
|
|
138
|
+
if (port !== undefined)
|
|
139
|
+
args.push(`--port=${port}`);
|
|
140
|
+
if (overwrite)
|
|
141
|
+
args.push('--overwrite');
|
|
142
|
+
const result = await execProcess('npx', args, { cwd: projectCwd, timeout: 60_000 });
|
|
143
|
+
// Branch C: CLI failure — real error per §3.4. A failed or partial migration
|
|
144
|
+
// needs recovery, so this is isError: true. Structured per §3.5.
|
|
145
|
+
if (!result.success) {
|
|
146
|
+
return {
|
|
147
|
+
content: [
|
|
148
|
+
{
|
|
149
|
+
type: 'text',
|
|
150
|
+
text: `Failed to migrate the backend payload into a frontend payload.
|
|
151
|
+
|
|
152
|
+
Project path: ${projectCwd}
|
|
153
|
+
RDF name: ${name}
|
|
154
|
+
Project: ${project}
|
|
155
|
+
Output: ${output ?? 'default (frontend/payload/)'}
|
|
156
|
+
Config: ${config ?? 'default config'}
|
|
157
|
+
Command: ${result.command}
|
|
158
|
+
Exit code: ${result.exitCode}
|
|
159
|
+
|
|
160
|
+
--- CLI output ---
|
|
161
|
+
stdout:
|
|
162
|
+
${result.stdout}
|
|
163
|
+
|
|
164
|
+
stderr:
|
|
165
|
+
${result.stderr}
|
|
166
|
+
--- end CLI output ---
|
|
167
|
+
|
|
168
|
+
For the assistant:
|
|
169
|
+
- Tell the user that the payload migration did not complete successfully, and that any partially written output may need cleanup.
|
|
170
|
+
- Summarise the most likely cause from the CLI output in plain language. Common causes:
|
|
171
|
+
* The named RDF payload was not found — it is resolved relative to cwd or cwd/payload/. Suggest checking the file name and location.
|
|
172
|
+
* The database config could not be read (missing or incomplete SERVER_ADDRESS/SERVER_PORT), or no default config is set. Suggest pointing at a valid config file.
|
|
173
|
+
* Output files already exist and --overwrite was not set — suggest re-running with overwrite enabled or choosing a different output directory.
|
|
174
|
+
- Do not paste the raw stdout/stderr unless the user explicitly asks. Do not mention internal tool names.`,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
isError: true, // per §3.4
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// Branch B: success — labeled facts + fenced raw output per §3.5.
|
|
181
|
+
return {
|
|
182
|
+
content: [
|
|
183
|
+
{
|
|
184
|
+
type: 'text',
|
|
185
|
+
text: `Payload migration completed (RDF backend -> UDF frontend).
|
|
186
|
+
|
|
187
|
+
Project path: ${projectCwd}
|
|
188
|
+
RDF name: ${name}
|
|
189
|
+
Project: ${project}
|
|
190
|
+
Output dir: ${output ?? 'default (frontend/payload/)'}
|
|
191
|
+
Command: ${result.command}
|
|
192
|
+
|
|
193
|
+
--- CLI output ---
|
|
194
|
+
${result.stdout}
|
|
195
|
+
--- end CLI output ---
|
|
196
|
+
|
|
197
|
+
For the assistant:
|
|
198
|
+
- Confirm to the user that the backend payload was converted into a frontend payload. Mention that the output is a split multi-file set (a shared app-config file, one file per page under pages/, and an aggregator file) written into the output directory above.
|
|
199
|
+
- Read the CLI output for the number of pages and the file list; mention how many pages were produced (auto-discovered JOINs can produce more than one page from a single RDF).
|
|
200
|
+
- Suggest the usual next steps in plain language: validate the resulting frontend payload, preview it, or generate the frontend application from it (always pointing at the aggregator file, not the individual page fragments). Do not name internal tools.
|
|
201
|
+
- Keep the reply concise. Do not paste the raw CLI output unless the user explicitly asks. Do not mention internal tool names.`,
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=migrate-payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate-payload.js","sourceRoot":"","sources":["../../../src/tools/codegen/migrate-payload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { execProcess } from '../../lib/exec.js';
|
|
4
|
+
export function registerDesignerGenerate(server) {
|
|
5
|
+
server.registerTool('designer_generate', {
|
|
6
|
+
title: 'Generate Designer Frontend',
|
|
7
|
+
description: `Generate frontend code from a UI Definition File (UDF) payload, by running restforge-designer generate. This WRITES the generated frontend files to disk.
|
|
8
|
+
|
|
9
|
+
USE WHEN:
|
|
10
|
+
- The user asks to generate / build the frontend application from a UDF payload
|
|
11
|
+
- The user asks things like "generate aplikasi frontend dari UDF", "build frontend dari payload", "generate satu page/form" (scope=form), "generate the frontend code", "build the frontend from this UI definition", "generate frontend designer"
|
|
12
|
+
- The user has a valid UDF payload and wants the real frontend files produced (not just a preview)
|
|
13
|
+
- The user wants to generate a single page/form rather than the whole app (use scope=form with the page id)
|
|
14
|
+
|
|
15
|
+
DO NOT USE FOR:
|
|
16
|
+
- Previewing which files would be generated WITHOUT writing them -> use 'designer_preview_files'
|
|
17
|
+
- Validating that the UDF payload is structurally correct -> use 'designer_validate_payload'
|
|
18
|
+
- Initialising a new project from a plugin -> use 'designer_init_project'
|
|
19
|
+
- Creating a new custom plugin from a template -> use 'designer_scaffold_plugin'
|
|
20
|
+
|
|
21
|
+
This tool wraps the RESTForge Designer CLI command: restforge-designer generate --payload=<payload> --output=<output> [optional flags], run in the given cwd.
|
|
22
|
+
The --payload and --output flags are always sent so the binary never drops into interactive prompt mode. Optional flags (plugin override, overwrite, scope, page, skip shared, plugins dir, license) are forwarded only when supplied; the binary defaults to scope 'app' when --scope is omitted.
|
|
23
|
+
This operation is license-gated: the RESTForge Designer license must be active for generation to succeed.
|
|
24
|
+
The recommended flow is read-before-write (§5.3): validate the payload, then preview the files, then generate.
|
|
25
|
+
|
|
26
|
+
Cross-reference (grounding & on-ramp):
|
|
27
|
+
- Before authoring or generating from a UDF, ground its shape (valid field types, required appConfig fields, enums, limits) against the designer's own rules via 'designer_get_udf_catalog' — the authoritative source of UDF structure.
|
|
28
|
+
- If no UDF payload exists yet but the user has a backend RDF, the on-ramp is 'codegen_migrate_payload' (RDF -> split UDF set); validate and preview it before generating.
|
|
29
|
+
- Canonical UDF flow: codegen_migrate_payload -> designer_get_udf_catalog -> designer_validate_payload -> designer_preview_files -> designer_generate.
|
|
30
|
+
|
|
31
|
+
Preconditions:
|
|
32
|
+
- The 'restforge-designer' binary must be installed and reachable on PATH. This tool pre-checks that by running
|
|
33
|
+
'restforge-designer --version'; if the binary is missing, the response will surface that as a non-error precondition.
|
|
34
|
+
- A valid, active RESTForge Designer license is required (the operation is license-gated).
|
|
35
|
+
|
|
36
|
+
PRESENTATION GUIDANCE:
|
|
37
|
+
- Match the user's language. If the user writes in Indonesian, respond in Indonesian.
|
|
38
|
+
- Never mention internal tool names in the reply to the user. Describe actions by what they do (e.g. "generate the frontend code", "validate the UI definition", "preview the generated files").
|
|
39
|
+
- Speak in plain language. Confirm what was generated and where; do not paste raw CLI output unless the user explicitly asks.
|
|
40
|
+
- When a precondition is not met, frame it as a question or next-step suggestion rather than an error.`,
|
|
41
|
+
inputSchema: {
|
|
42
|
+
cwd: z
|
|
43
|
+
.string()
|
|
44
|
+
.min(1)
|
|
45
|
+
.describe('Absolute path of the working directory where the binary is run'),
|
|
46
|
+
payload: z
|
|
47
|
+
.string()
|
|
48
|
+
.min(1)
|
|
49
|
+
.describe('Path to the UDF payload JSON file (relative to cwd or absolute). Required.'),
|
|
50
|
+
output: z
|
|
51
|
+
.string()
|
|
52
|
+
.min(1)
|
|
53
|
+
.describe('Path to the output folder where the frontend files will be written (relative to cwd or absolute). Required.'),
|
|
54
|
+
plugin: z
|
|
55
|
+
.string()
|
|
56
|
+
.min(1)
|
|
57
|
+
.optional()
|
|
58
|
+
.describe('Override the plugin id taken from the payload.'),
|
|
59
|
+
overwrite: z
|
|
60
|
+
.boolean()
|
|
61
|
+
.optional()
|
|
62
|
+
.describe('When true, overwrite existing files in the output directory.'),
|
|
63
|
+
scope: z
|
|
64
|
+
.enum(['app', 'form'])
|
|
65
|
+
.optional()
|
|
66
|
+
.describe('What to generate: the whole app or a single form/page. When omitted, the binary defaults to "app".'),
|
|
67
|
+
page: z
|
|
68
|
+
.string()
|
|
69
|
+
.min(1)
|
|
70
|
+
.optional()
|
|
71
|
+
.describe('The page/form id to generate, used with scope "form".'),
|
|
72
|
+
skipShared: z
|
|
73
|
+
.boolean()
|
|
74
|
+
.optional()
|
|
75
|
+
.describe('When true, skip generating the shared/common files.'),
|
|
76
|
+
pluginsDir: z
|
|
77
|
+
.string()
|
|
78
|
+
.min(1)
|
|
79
|
+
.optional()
|
|
80
|
+
.describe('Override path to the plugins folder. When omitted, the binary auto-detects the plugins directory.'),
|
|
81
|
+
license: z
|
|
82
|
+
.string()
|
|
83
|
+
.min(1)
|
|
84
|
+
.optional()
|
|
85
|
+
.describe('License key override for this license-gated operation. When omitted, the binary uses the activated license.'),
|
|
86
|
+
},
|
|
87
|
+
annotations: {
|
|
88
|
+
title: 'Generate Designer Frontend',
|
|
89
|
+
destructiveHint: false, // writes generated files; overwrites only when overwrite=true is supplied
|
|
90
|
+
idempotentHint: false, // each call regenerates files
|
|
91
|
+
},
|
|
92
|
+
}, async ({ cwd, payload, output, plugin, overwrite, scope, page, skipShared, pluginsDir, license, }) => {
|
|
93
|
+
const projectCwd = resolve(cwd);
|
|
94
|
+
// Precondition check: the restforge-designer binary must be reachable on PATH.
|
|
95
|
+
// Treated as a non-error precondition per the authoring guide §3.4.
|
|
96
|
+
const probe = await execProcess('restforge-designer', ['--version'], {
|
|
97
|
+
cwd: projectCwd,
|
|
98
|
+
timeout: 10_000,
|
|
99
|
+
});
|
|
100
|
+
if (!probe.success) {
|
|
101
|
+
return {
|
|
102
|
+
content: [
|
|
103
|
+
{
|
|
104
|
+
type: 'text',
|
|
105
|
+
text: `Precondition not met: the RESTForge Designer command-line tool is not installed or not on PATH.
|
|
106
|
+
|
|
107
|
+
Working directory: ${projectCwd}
|
|
108
|
+
Payload: ${payload}
|
|
109
|
+
Output: ${output}
|
|
110
|
+
Plugins dir: ${pluginsDir ?? 'auto-detect'}
|
|
111
|
+
Probe command: ${probe.command}
|
|
112
|
+
Exit code: ${probe.exitCode}
|
|
113
|
+
|
|
114
|
+
For the assistant:
|
|
115
|
+
- The user needs to install RESTForge Designer (and ensure it is on the system PATH) before frontend code can be generated.
|
|
116
|
+
- When explaining to the user, say something like "the RESTForge Designer tool isn't installed or isn't on your PATH yet — please install it and try again". Do not mention internal tool names.
|
|
117
|
+
- Once it is installed, retry generating the frontend code.`,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
isError: false, // per §3.4
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// Always send the required flags (--payload, --output) so the binary never drops
|
|
124
|
+
// into interactive prompt mode. Forward optional flags only when supplied; --scope
|
|
125
|
+
// is omitted to keep the binary's "app" default. per §3.5 / D6
|
|
126
|
+
const args = ['generate', `--payload=${payload}`, `--output=${output}`];
|
|
127
|
+
if (plugin)
|
|
128
|
+
args.push(`--plugin=${plugin}`);
|
|
129
|
+
if (overwrite)
|
|
130
|
+
args.push('--overwrite');
|
|
131
|
+
if (scope)
|
|
132
|
+
args.push(`--scope=${scope}`);
|
|
133
|
+
if (page)
|
|
134
|
+
args.push(`--page=${page}`);
|
|
135
|
+
if (skipShared)
|
|
136
|
+
args.push('--skip-shared');
|
|
137
|
+
if (pluginsDir)
|
|
138
|
+
args.push(`--plugins-dir=${pluginsDir}`);
|
|
139
|
+
if (license)
|
|
140
|
+
args.push(`--license=${license}`);
|
|
141
|
+
const result = await execProcess('restforge-designer', args, {
|
|
142
|
+
cwd: projectCwd,
|
|
143
|
+
timeout: 120_000,
|
|
144
|
+
});
|
|
145
|
+
// D9: write tool. A non-zero exit (incl. -1 crash/timeout) means generation did not
|
|
146
|
+
// complete cleanly — files may be missing or partial, so the model needs to recover
|
|
147
|
+
// -> isError: true.
|
|
148
|
+
if (result.exitCode !== 0) {
|
|
149
|
+
const stderrBlock = result.stderr
|
|
150
|
+
? `\n--- stderr ---\n${result.stderr}\n--- end stderr ---\n`
|
|
151
|
+
: '';
|
|
152
|
+
return {
|
|
153
|
+
content: [
|
|
154
|
+
{
|
|
155
|
+
type: 'text',
|
|
156
|
+
text: `Generating the frontend code did not complete.
|
|
157
|
+
|
|
158
|
+
Working directory: ${projectCwd}
|
|
159
|
+
Payload: ${payload}
|
|
160
|
+
Output: ${output}
|
|
161
|
+
Scope: ${scope ?? 'app (default)'}
|
|
162
|
+
Plugins dir: ${pluginsDir ?? 'auto-detect'}
|
|
163
|
+
Command: ${result.command}
|
|
164
|
+
Exit code: ${result.exitCode}
|
|
165
|
+
|
|
166
|
+
--- stdout ---
|
|
167
|
+
${result.stdout}
|
|
168
|
+
--- end stdout ---
|
|
169
|
+
${stderrBlock}
|
|
170
|
+
For the assistant:
|
|
171
|
+
- The generation did not finish successfully, so the output files may be missing or partial. Read the CLI output above and explain the most likely cause to the user in plain language. Common causes:
|
|
172
|
+
* The RESTForge Designer license is not active — this operation is license-gated. Suggest the user activate their RESTForge Designer license through the Designer desktop application or its own CLI, then retry. Do not name any internal tool.
|
|
173
|
+
* The UDF payload is invalid — suggest validating the payload first to pinpoint the structural errors, then retry.
|
|
174
|
+
* The payload relies on 'extends' / 'include' references — soft note: if the same payload passes validation but generation fails with an appConfig/plugin error (e.g. the plugin or app config "not found"), the payload may use 'extends' / 'include' that generate does not appear to merge the way validation does. As a possible workaround, suggest a self-contained payload (inline appConfig and pages) or an already-merged file. This is a tentative hint, not a guaranteed rule — do not state it as certain or tie it to a specific version.
|
|
175
|
+
* The payload file path is wrong / the file was not found — suggest checking the path.
|
|
176
|
+
* The plugin id in the payload (or the plugins directory) could not be resolved — suggest listing the available plugins.
|
|
177
|
+
* The output directory already exists and overwrite was not enabled — suggest retrying with overwrite, or choosing a different output path.
|
|
178
|
+
* Exit code -1 — the command crashed or timed out; offer to retry.
|
|
179
|
+
- Do not paste the raw CLI output unless the user explicitly asks. Do not mention internal tool names. Match the user's language.`,
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
isError: true, // per §3.4 / D9 — write did not complete
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
// D9: success — exit 0. Labeled facts + fenced raw output per §3.5.
|
|
186
|
+
const stderrBlock = result.stderr
|
|
187
|
+
? `\n--- stderr ---\n${result.stderr}\n--- end stderr ---\n`
|
|
188
|
+
: '';
|
|
189
|
+
return {
|
|
190
|
+
content: [
|
|
191
|
+
{
|
|
192
|
+
type: 'text',
|
|
193
|
+
text: `Frontend code generated successfully.
|
|
194
|
+
|
|
195
|
+
Working directory: ${projectCwd}
|
|
196
|
+
Payload: ${payload}
|
|
197
|
+
Output: ${output}
|
|
198
|
+
Scope: ${scope ?? 'app (default)'}
|
|
199
|
+
Plugins dir: ${pluginsDir ?? 'auto-detect'}
|
|
200
|
+
Command: ${result.command}
|
|
201
|
+
Exit code: ${result.exitCode}
|
|
202
|
+
|
|
203
|
+
--- stdout ---
|
|
204
|
+
${result.stdout}
|
|
205
|
+
--- end stdout ---
|
|
206
|
+
${stderrBlock}
|
|
207
|
+
For the assistant:
|
|
208
|
+
- Confirm to the user that the frontend code was generated. Mention the output location and the scope (whole app, or a single page/form) in plain language.
|
|
209
|
+
- Summarise what was produced from the CLI output (how many files, what kind) rather than pasting it.
|
|
210
|
+
- Suggest a sensible next step, e.g. running or inspecting the generated frontend.
|
|
211
|
+
- Do not paste the raw CLI output unless the user explicitly asks. Do not mention internal tool names. Match the user's language.`,
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/tools/designer/generate.ts"],"names":[],"mappings":"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"}
|