@restforgejs/mcp-server 1.2.0
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/LICENSE.md +21 -0
- package/README.md +149 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/env-parser.d.ts +30 -0
- package/dist/lib/env-parser.js +150 -0
- package/dist/lib/env-parser.js.map +1 -0
- package/dist/lib/exec.d.ts +29 -0
- package/dist/lib/exec.js +38 -0
- package/dist/lib/exec.js.map +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +220 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/codegen/create-dashboard.d.ts +2 -0
- package/dist/tools/codegen/create-dashboard.js +256 -0
- package/dist/tools/codegen/create-dashboard.js.map +1 -0
- package/dist/tools/codegen/create-endpoint.d.ts +2 -0
- package/dist/tools/codegen/create-endpoint.js +263 -0
- package/dist/tools/codegen/create-endpoint.js.map +1 -0
- package/dist/tools/codegen/dbschema-generate-ddl.d.ts +2 -0
- package/dist/tools/codegen/dbschema-generate-ddl.js +187 -0
- package/dist/tools/codegen/dbschema-generate-ddl.js.map +1 -0
- package/dist/tools/codegen/dbschema-init.d.ts +2 -0
- package/dist/tools/codegen/dbschema-init.js +158 -0
- package/dist/tools/codegen/dbschema-init.js.map +1 -0
- package/dist/tools/codegen/dbschema-introspect.d.ts +2 -0
- package/dist/tools/codegen/dbschema-introspect.js +241 -0
- package/dist/tools/codegen/dbschema-introspect.js.map +1 -0
- package/dist/tools/codegen/dbschema-migrate.d.ts +2 -0
- package/dist/tools/codegen/dbschema-migrate.js +219 -0
- package/dist/tools/codegen/dbschema-migrate.js.map +1 -0
- package/dist/tools/codegen/dbschema-models.d.ts +2 -0
- package/dist/tools/codegen/dbschema-models.js +146 -0
- package/dist/tools/codegen/dbschema-models.js.map +1 -0
- package/dist/tools/codegen/dbschema-validate.d.ts +2 -0
- package/dist/tools/codegen/dbschema-validate.js +153 -0
- package/dist/tools/codegen/dbschema-validate.js.map +1 -0
- package/dist/tools/codegen/describe-table.d.ts +2 -0
- package/dist/tools/codegen/describe-table.js +259 -0
- package/dist/tools/codegen/describe-table.js.map +1 -0
- package/dist/tools/codegen/diff-payload.d.ts +2 -0
- package/dist/tools/codegen/diff-payload.js +165 -0
- package/dist/tools/codegen/diff-payload.js.map +1 -0
- package/dist/tools/codegen/generate-payload.d.ts +2 -0
- package/dist/tools/codegen/generate-payload.js +145 -0
- package/dist/tools/codegen/generate-payload.js.map +1 -0
- package/dist/tools/codegen/get-dashboard-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-dashboard-catalog.js +213 -0
- package/dist/tools/codegen/get-dashboard-catalog.js.map +1 -0
- package/dist/tools/codegen/get-dbschema-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-dbschema-catalog.js +244 -0
- package/dist/tools/codegen/get-dbschema-catalog.js.map +1 -0
- package/dist/tools/codegen/get-field-validation-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-field-validation-catalog.js +186 -0
- package/dist/tools/codegen/get-field-validation-catalog.js.map +1 -0
- package/dist/tools/codegen/get-query-declarative-catalog.d.ts +2 -0
- package/dist/tools/codegen/get-query-declarative-catalog.js +200 -0
- package/dist/tools/codegen/get-query-declarative-catalog.js.map +1 -0
- package/dist/tools/codegen/index.d.ts +2 -0
- package/dist/tools/codegen/index.js +43 -0
- package/dist/tools/codegen/index.js.map +1 -0
- package/dist/tools/codegen/list-tables.d.ts +2 -0
- package/dist/tools/codegen/list-tables.js +220 -0
- package/dist/tools/codegen/list-tables.js.map +1 -0
- package/dist/tools/codegen/sync-payload.d.ts +2 -0
- package/dist/tools/codegen/sync-payload.js +177 -0
- package/dist/tools/codegen/sync-payload.js.map +1 -0
- package/dist/tools/codegen/validate-dashboard-payload.d.ts +2 -0
- package/dist/tools/codegen/validate-dashboard-payload.js +239 -0
- package/dist/tools/codegen/validate-dashboard-payload.js.map +1 -0
- package/dist/tools/codegen/validate-payload.d.ts +2 -0
- package/dist/tools/codegen/validate-payload.js +166 -0
- package/dist/tools/codegen/validate-payload.js.map +1 -0
- package/dist/tools/codegen/validate-sql.d.ts +2 -0
- package/dist/tools/codegen/validate-sql.js +270 -0
- package/dist/tools/codegen/validate-sql.js.map +1 -0
- package/dist/tools/health/index.d.ts +2 -0
- package/dist/tools/health/index.js +5 -0
- package/dist/tools/health/index.js.map +1 -0
- package/dist/tools/health/ping.d.ts +2 -0
- package/dist/tools/health/ping.js +68 -0
- package/dist/tools/health/ping.js.map +1 -0
- package/dist/tools/runtime/check-launcher-exists.d.ts +2 -0
- package/dist/tools/runtime/check-launcher-exists.js +111 -0
- package/dist/tools/runtime/check-launcher-exists.js.map +1 -0
- package/dist/tools/runtime/check-status.d.ts +2 -0
- package/dist/tools/runtime/check-status.js +417 -0
- package/dist/tools/runtime/check-status.js.map +1 -0
- package/dist/tools/runtime/detect-config.d.ts +2 -0
- package/dist/tools/runtime/detect-config.js +130 -0
- package/dist/tools/runtime/detect-config.js.map +1 -0
- package/dist/tools/runtime/detect-project.d.ts +2 -0
- package/dist/tools/runtime/detect-project.js +132 -0
- package/dist/tools/runtime/detect-project.js.map +1 -0
- package/dist/tools/runtime/generate-launcher.d.ts +2 -0
- package/dist/tools/runtime/generate-launcher.js +438 -0
- package/dist/tools/runtime/generate-launcher.js.map +1 -0
- package/dist/tools/runtime/index.d.ts +2 -0
- package/dist/tools/runtime/index.js +15 -0
- package/dist/tools/runtime/index.js.map +1 -0
- package/dist/tools/runtime/validate-preflight.d.ts +2 -0
- package/dist/tools/runtime/validate-preflight.js +209 -0
- package/dist/tools/runtime/validate-preflight.js.map +1 -0
- package/dist/tools/setup/create-folder.d.ts +2 -0
- package/dist/tools/setup/create-folder.js +138 -0
- package/dist/tools/setup/create-folder.js.map +1 -0
- package/dist/tools/setup/get-config-schema.d.ts +2 -0
- package/dist/tools/setup/get-config-schema.js +158 -0
- package/dist/tools/setup/get-config-schema.js.map +1 -0
- package/dist/tools/setup/get-init-template.d.ts +2 -0
- package/dist/tools/setup/get-init-template.js +130 -0
- package/dist/tools/setup/get-init-template.js.map +1 -0
- package/dist/tools/setup/index.d.ts +2 -0
- package/dist/tools/setup/index.js +21 -0
- package/dist/tools/setup/index.js.map +1 -0
- package/dist/tools/setup/init-config.d.ts +2 -0
- package/dist/tools/setup/init-config.js +120 -0
- package/dist/tools/setup/init-config.js.map +1 -0
- package/dist/tools/setup/install-package.d.ts +2 -0
- package/dist/tools/setup/install-package.js +133 -0
- package/dist/tools/setup/install-package.js.map +1 -0
- package/dist/tools/setup/read-env.d.ts +2 -0
- package/dist/tools/setup/read-env.js +138 -0
- package/dist/tools/setup/read-env.js.map +1 -0
- package/dist/tools/setup/update-env.d.ts +2 -0
- package/dist/tools/setup/update-env.js +176 -0
- package/dist/tools/setup/update-env.js.map +1 -0
- package/dist/tools/setup/validate-config.d.ts +2 -0
- package/dist/tools/setup/validate-config.js +138 -0
- package/dist/tools/setup/validate-config.js.map +1 -0
- package/dist/tools/setup/write-env.d.ts +2 -0
- package/dist/tools/setup/write-env.js +168 -0
- package/dist/tools/setup/write-env.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,220 @@
|
|
|
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 registerCodegenListTables(server) {
|
|
6
|
+
server.registerTool('codegen_list_tables', {
|
|
7
|
+
title: 'List Database Tables',
|
|
8
|
+
description: `List all tables (and views) in the project's configured database, by wrapping restforge schema list. Live introspection — the CLI connects to the database and queries the catalog (information_schema in Postgres/MySQL, all_tables in Oracle).
|
|
9
|
+
|
|
10
|
+
USE WHEN:
|
|
11
|
+
- The user asks "what tables exist in the database?" or any equivalent question
|
|
12
|
+
- Pertanyaan dalam bentuk seperti "tabel apa saja yang ada di database", "list table di database project", "show me the tables", "ada tabel apa aja"
|
|
13
|
+
- Before authoring any SQL query (dashboard widget query, ad-hoc query, CRUD payload generation) and the table catalog is unknown — ground the query in the live database state instead of guessing
|
|
14
|
+
- The user mentions a specific table name and the AI is unsure whether it actually exists in the project's database
|
|
15
|
+
- The user is exploring an unfamiliar database before deciding what to build (e.g. picking a target table for a new endpoint or dashboard)
|
|
16
|
+
- The user asks to filter by schema or namespace (e.g. "list tables in core schema only", "tabel di schema public saja")
|
|
17
|
+
- The user asks for a read-only inspection without modifying anything
|
|
18
|
+
- Before invoking 'codegen_describe_table' — to discover candidate table names first
|
|
19
|
+
|
|
20
|
+
DO NOT USE FOR:
|
|
21
|
+
- Detailed column / primary key / foreign key / index information for a specific table -> use 'codegen_describe_table'
|
|
22
|
+
- Listing payload spec files on the filesystem (the payload/ folder) -> use generic Read or filesystem tools
|
|
23
|
+
- Validating whether a payload file is in sync with the database -> use 'codegen_validate_payload'
|
|
24
|
+
- Querying the actual row data inside tables -> out of scope; this tool returns only the table catalog (names + type), not row content
|
|
25
|
+
- Modifying the database schema (CREATE/ALTER/DROP TABLE) -> out of scope
|
|
26
|
+
- Listing schemas or databases themselves -> out of scope; this tool returns the tables WITHIN a schema, not the schemas themselves
|
|
27
|
+
|
|
28
|
+
This tool runs: npx restforge schema list --config=<config> [--schema=<schema>] [--include-system=<bool>] in the given cwd.
|
|
29
|
+
The CLI connects to the database described in the config file, queries the catalog, and emits a JSON envelope with summary counts and a tables array.
|
|
30
|
+
|
|
31
|
+
Preconditions:
|
|
32
|
+
- The project must have @restforgejs/platform installed in node_modules.
|
|
33
|
+
- The config file (default 'db-connection.env') must exist in the project and contain valid database credentials. This tool does not pre-check that — if the CLI fails, the failure response will surface the underlying cause.
|
|
34
|
+
|
|
35
|
+
PRESENTATION GUIDANCE:
|
|
36
|
+
- Match the user's language. If the user writes in Indonesian, respond in Indonesian.
|
|
37
|
+
- Never mention internal tool names in the reply to the user. Describe actions by what they do (e.g. "list the database tables", "describe a specific table", "install the package").
|
|
38
|
+
- Speak in plain language. Summarise the result (database type and total table count); do not paste the raw JSON unless the user explicitly asks.
|
|
39
|
+
- This is a live introspection: the tool actively queries the database catalog (information_schema in Postgres/MySQL, all_tables in Oracle). The result reflects the database state at query time.
|
|
40
|
+
- Database type is auto-detected from the config file. The schema filter is dialect-aware (Postgres schema vs MySQL database vs Oracle owner) and uppercase is required for Oracle owners.
|
|
41
|
+
- When a precondition is not met (e.g. the package is not installed), frame it as a question or next-step suggestion rather than an error.`,
|
|
42
|
+
inputSchema: {
|
|
43
|
+
cwd: z
|
|
44
|
+
.string()
|
|
45
|
+
.min(1)
|
|
46
|
+
.describe('Absolute path of the project folder (must contain node_modules/@restforgejs/platform and the config file)'),
|
|
47
|
+
config: z
|
|
48
|
+
.string()
|
|
49
|
+
.min(1)
|
|
50
|
+
.default('db-connection.env')
|
|
51
|
+
.describe('Config file name relative to the project, used by the CLI to connect to the database'),
|
|
52
|
+
schema: z
|
|
53
|
+
.string()
|
|
54
|
+
.min(1)
|
|
55
|
+
.optional()
|
|
56
|
+
.describe('Filter to a specific database schema/owner. Postgres: schema name (e.g. public); MySQL: database name; Oracle: owner (uppercase). When omitted, lists tables from all user-owned schemas.'),
|
|
57
|
+
includeSystem: z
|
|
58
|
+
.boolean()
|
|
59
|
+
.optional()
|
|
60
|
+
.describe("Default false. When true, include system tables (Postgres: pg_catalog, information_schema; MySQL: mysql, performance_schema, sys; Oracle: SYS, SYSTEM, etc). Most users do NOT want this."),
|
|
61
|
+
},
|
|
62
|
+
annotations: {
|
|
63
|
+
title: 'List Database Tables',
|
|
64
|
+
readOnlyHint: true,
|
|
65
|
+
idempotentHint: true,
|
|
66
|
+
},
|
|
67
|
+
}, async ({ cwd, config, schema, includeSystem }) => {
|
|
68
|
+
const projectCwd = resolve(cwd);
|
|
69
|
+
// Precondition check: @restforgejs/platform must be present in node_modules.
|
|
70
|
+
// Treated as a non-error precondition per the authoring guide §3.4.
|
|
71
|
+
try {
|
|
72
|
+
await access(join(projectCwd, 'node_modules', '@restforgejs', 'platform'));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: 'text',
|
|
79
|
+
text: `Precondition not met: the RESTForge package is not installed in this project.
|
|
80
|
+
|
|
81
|
+
Project path: ${projectCwd}
|
|
82
|
+
Expected location: node_modules/@restforgejs/platform
|
|
83
|
+
Requested config: ${config}
|
|
84
|
+
Requested schema filter: ${schema ?? 'all user-owned schemas'}
|
|
85
|
+
Requested includeSystem: ${includeSystem ?? false}
|
|
86
|
+
|
|
87
|
+
For the assistant:
|
|
88
|
+
- The user needs to install the RESTForge package before the database table list can be retrieved.
|
|
89
|
+
- Suggest installing the package first, then retry listing the tables.
|
|
90
|
+
- 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.`,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
isError: false, // per §3.4
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Forward only the arguments the user supplied. CLI defaults remain in
|
|
97
|
+
// effect when the user does not specify them. per §3.5
|
|
98
|
+
const cliArgs = [
|
|
99
|
+
'restforge',
|
|
100
|
+
'schema',
|
|
101
|
+
'list',
|
|
102
|
+
`--config=${config}`,
|
|
103
|
+
];
|
|
104
|
+
if (schema !== undefined)
|
|
105
|
+
cliArgs.push(`--schema=${schema}`);
|
|
106
|
+
if (includeSystem !== undefined)
|
|
107
|
+
cliArgs.push(`--include-system=${includeSystem}`);
|
|
108
|
+
const result = await execProcess('npx', cliArgs, {
|
|
109
|
+
cwd: projectCwd,
|
|
110
|
+
timeout: 30_000,
|
|
111
|
+
env: { NODE_ENV: 'production' },
|
|
112
|
+
stripFinalNewline: true,
|
|
113
|
+
});
|
|
114
|
+
// Branch C: CLI failure — real error per §3.4; structured per §3.5.
|
|
115
|
+
if (!result.success) {
|
|
116
|
+
return {
|
|
117
|
+
content: [
|
|
118
|
+
{
|
|
119
|
+
type: 'text',
|
|
120
|
+
text: `Failed to list database tables.
|
|
121
|
+
|
|
122
|
+
Project path: ${projectCwd}
|
|
123
|
+
Config: ${config}
|
|
124
|
+
Schema filter: ${schema ?? 'all user-owned schemas'}
|
|
125
|
+
includeSystem: ${includeSystem ?? false}
|
|
126
|
+
Command: ${result.command}
|
|
127
|
+
Exit code: ${result.exitCode}
|
|
128
|
+
|
|
129
|
+
--- CLI output ---
|
|
130
|
+
stdout:
|
|
131
|
+
${result.stdout}
|
|
132
|
+
|
|
133
|
+
stderr:
|
|
134
|
+
${result.stderr}
|
|
135
|
+
--- end CLI output ---
|
|
136
|
+
|
|
137
|
+
For the assistant:
|
|
138
|
+
- Tell the user that listing the database tables did not complete successfully.
|
|
139
|
+
- Summarise the most likely cause from the CLI output in plain language. Common causes:
|
|
140
|
+
* Config file not found — suggest verifying the path and that the file exists in the project.
|
|
141
|
+
* Database connection failed — suggest verifying the credentials, that the host is reachable, and that the port is open.
|
|
142
|
+
* Unknown command 'schema list' — the installed RESTForge version may be older than this CLI subcommand; suggest upgrading the package.
|
|
143
|
+
- Do not paste the raw stdout/stderr unless the user explicitly asks. Do not mention internal tool names.
|
|
144
|
+
- Offer to retry once the underlying issue is resolved.`,
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
isError: true, // per §3.4
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// Branch D: JSON parse failure — real error per §3.4 (CLI succeeded but produced invalid output).
|
|
151
|
+
let parsed;
|
|
152
|
+
try {
|
|
153
|
+
parsed = JSON.parse(result.stdout);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
157
|
+
return {
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
type: 'text',
|
|
161
|
+
text: `Failed to parse the database table list as JSON.
|
|
162
|
+
|
|
163
|
+
Project path: ${projectCwd}
|
|
164
|
+
Config: ${config}
|
|
165
|
+
Reason: ${msg}
|
|
166
|
+
|
|
167
|
+
--- Raw stdout ---
|
|
168
|
+
${result.stdout}
|
|
169
|
+
--- end Raw stdout ---
|
|
170
|
+
|
|
171
|
+
For the assistant:
|
|
172
|
+
- The CLI returned output that is not valid JSON.
|
|
173
|
+
- Summarise this to the user in plain language; do not paste the raw stdout unless they explicitly ask.
|
|
174
|
+
- Suggest checking that the installed RESTForge package version is compatible. Do not mention internal tool names.`,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
isError: true, // per §3.4
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// Defensive labeled facts: if the JSON shape changes upstream, fall back to 'unknown' / 'n/a'
|
|
181
|
+
// rather than crashing. Mirror the catalog tools pattern.
|
|
182
|
+
const root = (parsed ?? {});
|
|
183
|
+
const summary = (root.summary ?? {});
|
|
184
|
+
const totalTables = typeof summary.totalTables === 'number' ? summary.totalTables : 'unknown';
|
|
185
|
+
const databaseType = typeof summary.database === 'string' ? summary.database : 'unknown';
|
|
186
|
+
const schemasInResult = Array.isArray(summary.schemas)
|
|
187
|
+
? `[${summary.schemas.map((s) => JSON.stringify(s)).join(', ')}]`
|
|
188
|
+
: 'unknown';
|
|
189
|
+
const prettyJson = JSON.stringify(parsed, null, 2);
|
|
190
|
+
// Branch B: success — one-line summary + labeled facts + fenced JSON output per §3.5.
|
|
191
|
+
return {
|
|
192
|
+
content: [
|
|
193
|
+
{
|
|
194
|
+
type: 'text',
|
|
195
|
+
text: `Database tables retrieved successfully.
|
|
196
|
+
|
|
197
|
+
Project path: ${projectCwd}
|
|
198
|
+
Config: ${config}
|
|
199
|
+
Database: ${databaseType}
|
|
200
|
+
Schema filter: ${schema ?? 'all user-owned schemas'}
|
|
201
|
+
totalTables: ${totalTables}
|
|
202
|
+
schemas in result: ${schemasInResult}
|
|
203
|
+
|
|
204
|
+
--- Database Tables (JSON) ---
|
|
205
|
+
${prettyJson}
|
|
206
|
+
--- end Database Tables (JSON) ---
|
|
207
|
+
|
|
208
|
+
For the assistant:
|
|
209
|
+
- Confirm to the user that the table list was retrieved. Mention the database type and the total count in plain language.
|
|
210
|
+
- If a specific table was named in the user's question, find it in the list and confirm whether it exists; if not found, say so explicitly.
|
|
211
|
+
- Do not paste the full JSON unless the user asks. Summarise instead — for many tables, group by schema or list only the names relevant to the user's task.
|
|
212
|
+
- This is a read-only operation: it queries the database catalog tables (information_schema / pg_catalog / all_tables) without modifying anything.
|
|
213
|
+
- For deeper details on a specific table (columns, primary key, foreign keys, indexes), suggest the table-describe action as the next step. Do not mention internal tool names.
|
|
214
|
+
- Match the user's language.`,
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
};
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=list-tables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-tables.js","sourceRoot":"","sources":["../../../src/tools/codegen/list-tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,UAAU,yBAAyB,CAAC,MAAiB;IACzD,MAAM,CAAC,YAAY,CACjB,qBAAqB,EACrB;QACE,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2IAiCwH;QACrI,WAAW,EAAE;YACX,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CAAC,2GAA2G,CAAC;YACxH,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,OAAO,CAAC,mBAAmB,CAAC;iBAC5B,QAAQ,CAAC,sFAAsF,CAAC;YACnG,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,EAAE;iBACV,QAAQ,CAAC,2LAA2L,CAAC;YACxM,aAAa,EAAE,CAAC;iBACb,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,2LAA2L,CAAC;SACzM;QACD,WAAW,EAAE;YACX,KAAK,EAAE,sBAAsB;YAC7B,YAAY,EAAE,IAAI;YAClB,cAAc,EAAE,IAAI;SACrB;KACF,EACD,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,EAAE;QAC/C,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAEhC,6EAA6E;QAC7E,oEAAoE;QACpE,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC,CAAC;QAC7E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;;gBAEJ,UAAU;;oBAEN,MAAM;2BACC,MAAM,IAAI,wBAAwB;2BAClC,aAAa,IAAI,KAAK;;;;;gKAK+G;qBACnJ;iBACF;gBACD,OAAO,EAAE,KAAK,EAAE,WAAW;aAC5B,CAAC;QACJ,CAAC;QAED,uEAAuE;QACvE,uDAAuD;QACvD,MAAM,OAAO,GAAG;YACd,WAAW;YACX,QAAQ;YACR,MAAM;YACN,YAAY,MAAM,EAAE;SACrB,CAAC;QACF,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC;QAC7D,IAAI,aAAa,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,aAAa,EAAE,CAAC,CAAC;QAEnF,MAAM,MAAM,GAAG,MAAM,WAAW,CAC9B,KAAK,EACL,OAAO,EACP;YACE,GAAG,EAAE,UAAU;YACf,OAAO,EAAE,MAAM;YACf,GAAG,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE;YAC/B,iBAAiB,EAAE,IAAI;SACxB,CACF,CAAC;QAEF,oEAAoE;QACpE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;;gBAEJ,UAAU;UAChB,MAAM;iBACC,MAAM,IAAI,wBAAwB;iBAClC,aAAa,IAAI,KAAK;WAC5B,MAAM,CAAC,OAAO;aACZ,MAAM,CAAC,QAAQ;;;;EAI1B,MAAM,CAAC,MAAM;;;EAGb,MAAM,CAAC,MAAM;;;;;;;;;;wDAUyC;qBAC3C;iBACF;gBACD,OAAO,EAAE,IAAI,EAAE,WAAW;aAC3B,CAAC;QACJ,CAAC;QAED,kGAAkG;QAClG,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;;gBAEJ,UAAU;UAChB,MAAM;UACN,GAAG;;;EAGX,MAAM,CAAC,MAAM;;;;;;mHAMoG;qBACtG;iBACF;gBACD,OAAO,EAAE,IAAI,EAAE,WAAW;aAC3B,CAAC;QACJ,CAAC;QAED,8FAA8F;QAC9F,0DAA0D;QAC1D,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAA4B,CAAC;QACvD,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAA4B,CAAC;QAChE,MAAM,WAAW,GACf,OAAO,OAAO,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5E,MAAM,YAAY,GAChB,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;YACpD,CAAC,CAAC,IAAK,OAAO,CAAC,OAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAChF,CAAC,CAAC,SAAS,CAAC;QAEd,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAEnD,sFAAsF;QACtF,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE;;gBAEF,UAAU;UAChB,MAAM;YACJ,YAAY;iBACP,MAAM,IAAI,wBAAwB;eACpC,WAAW;qBACL,eAAe;;;EAGlC,UAAU;;;;;;;;;6BASiB;iBAClB;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,177 @@
|
|
|
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 registerCodegenSyncPayload(server) {
|
|
6
|
+
server.registerTool('codegen_sync_payload', {
|
|
7
|
+
title: 'Sync Payload',
|
|
8
|
+
description: `Apply schema drift to existing payload spec files in a project, archiving the previous version of each updated file, by running restforge payload --sync.
|
|
9
|
+
|
|
10
|
+
USE WHEN:
|
|
11
|
+
- The user asks to apply schema drift changes to payload files, sync payload files with the database, or update payload JSON files to match the current schema
|
|
12
|
+
- The user asks things like "sinkronisasi payload", "update payload sesuai database", "apply schema drift", "sync payload files", "terapkan perubahan schema", "samakan payload dengan database"
|
|
13
|
+
- After 'codegen_diff_payload' reported column-level differences and the user has reviewed them, wanting to apply the changes
|
|
14
|
+
- After ALTER TABLE in the database when the user wants to bring all payload files in line with the new schema
|
|
15
|
+
- The user mentions creating an archive of the previous payload before regenerating endpoints
|
|
16
|
+
- Before applying changes, strongly consider calling 'codegen_diff_payload' first to confirm what will change in each file (read-before-write per §5.3 — sync overwrites the active payload file and produces an archive that the user may want to inspect later).
|
|
17
|
+
|
|
18
|
+
DO NOT USE FOR:
|
|
19
|
+
- Just checking which payload files have drift (without modifying anything) -> use 'codegen_validate_payload'
|
|
20
|
+
- Looking at the per-column differences without applying them -> use 'codegen_diff_payload'
|
|
21
|
+
- Generating a payload from scratch for a table that has no payload yet -> use 'codegen_generate_payload'
|
|
22
|
+
- Cleaning up or deleting old '.archive.NNN' files — this tool does not handle archive cleanup; the user must remove archive files manually if desired
|
|
23
|
+
|
|
24
|
+
This tool runs: npx restforge payload --sync --config=<config> [--table=<table>] [--output=<output>] in the given cwd.
|
|
25
|
+
The CLI reads existing payload JSON files from the output directory, connects to the database described
|
|
26
|
+
in the config file, and rewrites each payload file whose schema has drifted. Before overwriting, the
|
|
27
|
+
previous file content is renamed to '<filename>.archive.NNN' (NNN is a sequential number starting at 001).
|
|
28
|
+
Files that are already in sync are not touched. The CLI prints a per-file status (typically [SKIP],
|
|
29
|
+
[ARCHIVE], [SYNCED]) followed by a Summary section with totals.
|
|
30
|
+
|
|
31
|
+
If the sync run fails partway through (e.g. database connection drops), the CLI restores the archived
|
|
32
|
+
file back to its original name so the active payload is not left corrupted.
|
|
33
|
+
|
|
34
|
+
Preconditions:
|
|
35
|
+
- The project must have @restforgejs/platform installed in node_modules.
|
|
36
|
+
- The config file (default 'db-connection.env') must exist in the project and contain valid
|
|
37
|
+
database credentials. This tool does not pre-check that — if the CLI fails, the failure response
|
|
38
|
+
will surface the underlying cause.
|
|
39
|
+
|
|
40
|
+
PRESENTATION GUIDANCE:
|
|
41
|
+
- Match the user's language. If the user writes in Indonesian, respond in Indonesian.
|
|
42
|
+
- Never mention internal tool names in the reply to the user. Describe actions by what they do (e.g. "update the payload files", "see the column-level differences first", "regenerate the endpoint code from the updated payload").
|
|
43
|
+
- Speak in plain language. Summarise the result; do not paste raw CLI output unless the user explicitly asks.
|
|
44
|
+
- When a precondition is not met, frame it as a question or next-step suggestion rather than an error.`,
|
|
45
|
+
inputSchema: {
|
|
46
|
+
cwd: z
|
|
47
|
+
.string()
|
|
48
|
+
.min(1)
|
|
49
|
+
.describe('Absolute path of the project folder (must contain node_modules/@restforgejs/platform and the config file)'),
|
|
50
|
+
config: z
|
|
51
|
+
.string()
|
|
52
|
+
.min(1)
|
|
53
|
+
.default('db-connection.env')
|
|
54
|
+
.describe('Config file name (relative to project) used by the CLI to connect to the database'),
|
|
55
|
+
table: z
|
|
56
|
+
.string()
|
|
57
|
+
.min(1)
|
|
58
|
+
.optional()
|
|
59
|
+
.describe('Specific table name to sync (e.g. supplier or core.supplier). When omitted, all payload files in the output directory are synced.'),
|
|
60
|
+
output: z
|
|
61
|
+
.string()
|
|
62
|
+
.min(1)
|
|
63
|
+
.optional()
|
|
64
|
+
.describe('Payload directory relative to project (e.g. payload). When omitted, the CLI uses its default (payload/).'),
|
|
65
|
+
},
|
|
66
|
+
annotations: {
|
|
67
|
+
title: 'Sync Payload',
|
|
68
|
+
readOnlyHint: false, // tool menulis ulang file payload + membuat file archive
|
|
69
|
+
idempotentHint: false, // memanggil ulang dapat menambah file archive baru jika DB berubah lagi di antara panggilan
|
|
70
|
+
},
|
|
71
|
+
}, async ({ cwd, config, table, output }) => {
|
|
72
|
+
const projectCwd = resolve(cwd);
|
|
73
|
+
// Precondition check: @restforgejs/platform must be present in node_modules.
|
|
74
|
+
// Treated as a non-error precondition per the authoring guide §3.4.
|
|
75
|
+
try {
|
|
76
|
+
await access(join(projectCwd, 'node_modules', '@restforgejs', 'platform'));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return {
|
|
80
|
+
content: [
|
|
81
|
+
{
|
|
82
|
+
type: 'text',
|
|
83
|
+
text: `Precondition not met: the RESTForge package is not installed in this project.
|
|
84
|
+
|
|
85
|
+
Project path: ${projectCwd}
|
|
86
|
+
Expected location: node_modules/@restforgejs/platform
|
|
87
|
+
Requested table: ${table ?? 'all'}
|
|
88
|
+
Requested config: ${config}
|
|
89
|
+
Requested output: ${output ?? 'default (payload/)'}
|
|
90
|
+
|
|
91
|
+
For the assistant:
|
|
92
|
+
- The user needs to install the RESTForge package before payload files can be synced with the database schema.
|
|
93
|
+
- Use the appropriate package-installation tool to do this, then retry syncing the payload.
|
|
94
|
+
- 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.`,
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
isError: false, // per §3.4
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Forward only the arguments the user supplied. Defaults inside restforge
|
|
101
|
+
// (e.g. payload/ as the default output dir, all files when --table is omitted)
|
|
102
|
+
// should remain in effect when the user does not specify them. per §3.5
|
|
103
|
+
const args = ['restforge', 'payload', 'sync', `--config=${config}`];
|
|
104
|
+
if (table)
|
|
105
|
+
args.push(`--table=${table}`);
|
|
106
|
+
if (output)
|
|
107
|
+
args.push(`--output=${output}`);
|
|
108
|
+
// Timeout raised to 60s (vs 30s for validate/diff): sync writes payload files
|
|
109
|
+
// and renames each previous version to a sequential archive, which can take
|
|
110
|
+
// longer than read-only operations on projects with many payload files.
|
|
111
|
+
const result = await execProcess('npx', args, { cwd: projectCwd, timeout: 60_000 });
|
|
112
|
+
// CLI failure: real error per §3.4; structured per §3.5.
|
|
113
|
+
if (!result.success) {
|
|
114
|
+
return {
|
|
115
|
+
content: [
|
|
116
|
+
{
|
|
117
|
+
type: 'text',
|
|
118
|
+
text: `Failed to sync payload.
|
|
119
|
+
|
|
120
|
+
Project path: ${projectCwd}
|
|
121
|
+
Config: ${config}
|
|
122
|
+
Table: ${table ?? 'all'}
|
|
123
|
+
Output: ${output ?? 'default (payload/)'}
|
|
124
|
+
Command: ${result.command}
|
|
125
|
+
Exit code: ${result.exitCode}
|
|
126
|
+
|
|
127
|
+
--- CLI output ---
|
|
128
|
+
stdout:
|
|
129
|
+
${result.stdout}
|
|
130
|
+
|
|
131
|
+
stderr:
|
|
132
|
+
${result.stderr}
|
|
133
|
+
--- end CLI output ---
|
|
134
|
+
|
|
135
|
+
For the assistant:
|
|
136
|
+
- Tell the user that updating the payload files did not complete successfully.
|
|
137
|
+
- Summarise the likely cause from the CLI output in plain language (common causes: the config file is missing or has incomplete credentials, the database is unreachable, the requested table does not exist, or the payload directory is empty). Do not paste the raw stdout/stderr unless the user explicitly asks.
|
|
138
|
+
- Reassure the user: when a sync run fails, the CLI automatically restores any payload file that was just archived back to its original name, so the active payload files are not left in a corrupted state.
|
|
139
|
+
- Offer to retry once the underlying issue is resolved. Do not mention internal tool names.`,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
isError: true, // per §3.4
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// Success: one-line summary + labeled facts + fenced raw output per §3.5.
|
|
146
|
+
// The CLI prints per-file status lines ([SKIP] / [ARCHIVE] / [SYNCED]) and a
|
|
147
|
+
// Summary section with totals. The model should extract the counts and the
|
|
148
|
+
// archive filenames from stdout when talking to the user. per §5.2 cross-ref
|
|
149
|
+
// back to validate/diff.
|
|
150
|
+
return {
|
|
151
|
+
content: [
|
|
152
|
+
{
|
|
153
|
+
type: 'text',
|
|
154
|
+
text: `Payload sync completed.
|
|
155
|
+
|
|
156
|
+
Project path: ${projectCwd}
|
|
157
|
+
Config: ${config}
|
|
158
|
+
Table: ${table ?? 'all'}
|
|
159
|
+
Output: ${output ?? 'default (payload/)'}
|
|
160
|
+
Command: ${result.command}
|
|
161
|
+
|
|
162
|
+
--- CLI output ---
|
|
163
|
+
${result.stdout}
|
|
164
|
+
--- end CLI output ---
|
|
165
|
+
|
|
166
|
+
For the assistant:
|
|
167
|
+
- Read the Summary section in the CLI output above and tell the user how many payload files were SYNCED and how many were SKIPPED (already in sync). Do not paste the raw CLI output unless the user explicitly asks.
|
|
168
|
+
- For each file that was updated, the previous version of the file was renamed to '<filename>.archive.NNN' (NNN is a sequential number, starting at 001) in the same payload directory. Mention this to the user in plain language so they know the old version is still on disk and available for manual rollback if needed. If the CLI output lists specific archive filenames, you may relay them to the user.
|
|
169
|
+
- Important: warn the user that any module or endpoint that was previously generated from the older payload still reflects the old schema. To bring those endpoints in line with the new schema, the user needs to regenerate the endpoint code from the updated payload as a follow-up step. Describe this in plain language; do not name the internal tool.
|
|
170
|
+
- If no files were synced (every file was SKIPPED because it was already in sync), confirm in plain language that the payload files already match the database and no changes were applied.
|
|
171
|
+
- Keep the reply concise. Do not mention internal tool names.`,
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=sync-payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-payload.js","sourceRoot":"","sources":["../../../src/tools/codegen/sync-payload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uGAoCoF;QACjG,WAAW,EAAE;YACX,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CAAC,2GAA2G,CAAC;YACxH,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,OAAO,CAAC,mBAAmB,CAAC;iBAC5B,QAAQ,CAAC,mFAAmF,CAAC;YAChG,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,EAAE;iBACV,QAAQ,CAAC,mIAAmI,CAAC;YAChJ,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,EAAE;iBACV,QAAQ,CAAC,0GAA0G,CAAC;SACxH;QACD,WAAW,EAAE;YACX,KAAK,EAAE,cAAc;YACrB,YAAY,EAAE,KAAK,EAAK,yDAAyD;YACjF,cAAc,EAAE,KAAK,EAAG,4FAA4F;SACrH;KACF,EACD,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;QACvC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAEhC,6EAA6E;QAC7E,oEAAoE;QACpE,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC,CAAC;QAC7E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;;gBAEJ,UAAU;;mBAEP,KAAK,IAAI,KAAK;oBACb,MAAM;oBACN,MAAM,IAAI,oBAAoB;;;;;gKAK8G;qBACnJ;iBACF;gBACD,OAAO,EAAE,KAAK,EAAE,WAAW;aAC5B,CAAC;QACJ,CAAC;QAED,0EAA0E;QAC1E,+EAA+E;QAC/E,wEAAwE;QACxE,MAAM,IAAI,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,MAAM,EAAE,CAAC,CAAC;QACpE,IAAI,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC;QACzC,IAAI,MAAM;YAAE,IAAI,CAAC,IAAI,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC;QAE5C,8EAA8E;QAC9E,4EAA4E;QAC5E,wEAAwE;QACxE,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAEpF,yDAAyD;QACzD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE;;gBAEJ,UAAU;UAChB,MAAM;SACP,KAAK,IAAI,KAAK;UACb,MAAM,IAAI,oBAAoB;WAC7B,MAAM,CAAC,OAAO;aACZ,MAAM,CAAC,QAAQ;;;;EAI1B,MAAM,CAAC,MAAM;;;EAGb,MAAM,CAAC,MAAM;;;;;;;4FAO6E;qBAC/E;iBACF;gBACD,OAAO,EAAE,IAAI,EAAE,WAAW;aAC3B,CAAC;QACJ,CAAC;QAED,0EAA0E;QAC1E,6EAA6E;QAC7E,2EAA2E;QAC3E,6EAA6E;QAC7E,yBAAyB;QACzB,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE;;gBAEF,UAAU;UAChB,MAAM;SACP,KAAK,IAAI,KAAK;UACb,MAAM,IAAI,oBAAoB;WAC7B,MAAM,CAAC,OAAO;;;EAGvB,MAAM,CAAC,MAAM;;;;;;;;8DAQ+C;iBACnD;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|