@agentic-kit/db-tools 0.11.5 → 0.12.1
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/README.md +40 -1
- package/app-workspace.d.ts +29 -2
- package/app-workspace.js +99 -8
- package/blueprint-construction.d.ts +60 -0
- package/blueprint-construction.js +161 -0
- package/context.d.ts +5 -1
- package/context.js +30 -5
- package/db-probe.d.ts +25 -17
- package/db-probe.js +97 -37
- package/esm/app-workspace.d.ts +29 -2
- package/esm/app-workspace.js +96 -8
- package/esm/blueprint-construction.d.ts +60 -0
- package/esm/blueprint-construction.js +154 -0
- package/esm/context.d.ts +5 -1
- package/esm/context.js +31 -6
- package/esm/db-probe.d.ts +25 -17
- package/esm/db-probe.js +96 -37
- package/esm/graphql-http.d.ts +35 -0
- package/esm/graphql-http.js +134 -0
- package/esm/host.d.ts +46 -0
- package/esm/host.js +9 -0
- package/esm/index.d.ts +11 -2
- package/esm/index.js +6 -0
- package/esm/provision-database/env-file.d.ts +23 -2
- package/esm/provision-database/env-file.js +86 -27
- package/esm/provision-database/pending-ticket.d.ts +33 -0
- package/esm/provision-database/pending-ticket.js +63 -0
- package/esm/provision-database/provision.d.ts +71 -0
- package/esm/provision-database/provision.js +557 -0
- package/esm/provision-database/request-database.d.ts +55 -3
- package/esm/provision-database/request-database.js +118 -17
- package/esm/schema-model/normalize.d.ts +21 -0
- package/esm/schema-model/normalize.js +244 -0
- package/esm/schema-model.d.ts +110 -0
- package/esm/schema-model.js +303 -0
- package/esm/tools/add-policies.d.ts +1 -1
- package/esm/tools/add-records.d.ts +1 -0
- package/esm/tools/add-records.js +18 -6
- package/esm/tools/add-relation.js +2 -3
- package/esm/tools/create-api-key.d.ts +2 -0
- package/esm/tools/create-api-key.js +27 -12
- package/esm/tools/manage-entity-types.d.ts +1 -1
- package/esm/tools/provision-blueprint.d.ts +6 -0
- package/esm/tools/provision-blueprint.js +11 -4
- package/esm/tools/provision-database.d.ts +8 -11
- package/esm/tools/provision-database.js +23 -243
- package/esm/tools/run-codegen.d.ts +19 -2
- package/esm/tools/run-codegen.js +184 -97
- package/esm/tools/templates.js +3 -15
- package/graphql-http.d.ts +35 -0
- package/graphql-http.js +140 -0
- package/host.d.ts +46 -0
- package/host.js +10 -0
- package/index.d.ts +11 -2
- package/index.js +26 -1
- package/package.json +4 -4
- package/provision-database/env-file.d.ts +23 -2
- package/provision-database/env-file.js +89 -28
- package/provision-database/pending-ticket.d.ts +33 -0
- package/provision-database/pending-ticket.js +72 -0
- package/provision-database/provision.d.ts +71 -0
- package/provision-database/provision.js +565 -0
- package/provision-database/request-database.d.ts +55 -3
- package/provision-database/request-database.js +124 -18
- package/schema-model/normalize.d.ts +21 -0
- package/schema-model/normalize.js +250 -0
- package/schema-model.d.ts +110 -0
- package/schema-model.js +309 -0
- package/tools/add-policies.d.ts +1 -1
- package/tools/add-records.d.ts +1 -0
- package/tools/add-records.js +18 -6
- package/tools/add-relation.js +2 -3
- package/tools/create-api-key.d.ts +2 -0
- package/tools/create-api-key.js +27 -12
- package/tools/manage-entity-types.d.ts +1 -1
- package/tools/provision-blueprint.d.ts +6 -0
- package/tools/provision-blueprint.js +11 -4
- package/tools/provision-database.d.ts +8 -11
- package/tools/provision-database.js +23 -245
- package/tools/run-codegen.d.ts +19 -2
- package/tools/run-codegen.js +184 -96
- package/tools/templates.js +3 -15
package/tools/run-codegen.js
CHANGED
|
@@ -4,138 +4,226 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.prewarmAppWorkspace = exports.runCodegenTool = void 0;
|
|
7
|
+
exports.createRunCodegenTool = createRunCodegenTool;
|
|
7
8
|
const node_fs_1 = require("node:fs");
|
|
8
9
|
const promises_1 = require("node:fs/promises");
|
|
9
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
11
|
const zod_1 = require("zod");
|
|
11
12
|
const app_workspace_1 = require("../app-workspace");
|
|
12
13
|
Object.defineProperty(exports, "prewarmAppWorkspace", { enumerable: true, get: function () { return app_workspace_1.prewarmAppWorkspace; } });
|
|
14
|
+
const graphql_http_1 = require("../graphql-http");
|
|
13
15
|
const host_1 = require("../host");
|
|
16
|
+
const env_file_1 = require("../provision-database/env-file");
|
|
14
17
|
const barrels_1 = require("../run-codegen/barrels");
|
|
15
18
|
const endpoints_1 = require("../run-codegen/endpoints");
|
|
16
19
|
const DEFAULT_API_ENDPOINT = 'http://api.localhost:3000/graphql';
|
|
17
20
|
const RunCodegenZod = zod_1.z.object({});
|
|
18
|
-
function fail(message) {
|
|
19
|
-
return {
|
|
21
|
+
function fail(message, extra) {
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: 'text', text: message }],
|
|
24
|
+
details: { ...extra, success: false, message },
|
|
25
|
+
};
|
|
20
26
|
}
|
|
21
27
|
const FETCH_RESET_RE = /Failed to fetch schema|Connection reset|ECONNRESET|socket hang up/i;
|
|
28
|
+
// A refusal is an answer, not a dropped connection: retrying it only waits.
|
|
29
|
+
const REFUSED_RE = /\b403\b|Forbidden|ACCESS_SUSPENDED/;
|
|
22
30
|
// Read the binding provision_database wrote to the project .env. The
|
|
23
31
|
// API_ENDPOINT pin identifies which backend owns the binding — it wins over
|
|
24
32
|
// the app's current backend config, matching context.ts data-plane precedence.
|
|
25
33
|
async function readProjectEnv(cwd) {
|
|
26
34
|
try {
|
|
27
|
-
const env = await (0, promises_1.readFile)(node_path_1.default.join(cwd, '.env'), 'utf8');
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
const env = (0, env_file_1.readEnv)(await (0, promises_1.readFile)(node_path_1.default.join(cwd, '.env'), 'utf8'));
|
|
36
|
+
return {
|
|
37
|
+
databaseName: env.DATABASE_NAME?.trim() || undefined,
|
|
38
|
+
apiEndpoint: env.API_ENDPOINT?.trim() || undefined,
|
|
39
|
+
};
|
|
30
40
|
}
|
|
31
41
|
catch {
|
|
32
42
|
return {};
|
|
33
43
|
}
|
|
34
44
|
}
|
|
35
45
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
46
|
+
// Ask the app API itself; a 403 from codegen alone could be anything.
|
|
47
|
+
async function isDatabaseUnavailable(appEndpoint) {
|
|
48
|
+
const answer = await (0, graphql_http_1.postGraphQL)({ endpoint: appEndpoint, query: '{ __typename }' });
|
|
49
|
+
return (0, graphql_http_1.classifyBackendErrors)(answer.errors) === 'suspended';
|
|
50
|
+
}
|
|
51
|
+
const CODEGEN_ARGS = ['generate', '--config', './graphql-codegen.config.ts'];
|
|
52
|
+
// The template flow resolves codegen through npx. A composed app runs the codegen
|
|
53
|
+
// its lockfile pinned: the linked bin, or the app's own `codegen` script when the
|
|
54
|
+
// bin isn't linked, so nothing is resolved over the network.
|
|
55
|
+
function codegenCommand(appDir, composed) {
|
|
56
|
+
if (!composed)
|
|
57
|
+
return { cmd: 'npx', args: ['@constructive-io/graphql-codegen', ...CODEGEN_ARGS] };
|
|
58
|
+
const bin = node_path_1.default.join(appDir, 'node_modules', '.bin', 'graphql-codegen');
|
|
59
|
+
return (0, node_fs_1.existsSync)(bin) ? { cmd: bin, args: CODEGEN_ARGS } : { cmd: 'pnpm', args: ['run', 'codegen'] };
|
|
60
|
+
}
|
|
36
61
|
// The codegen CLI introspects three live vhosts (admin/auth/app). Under load the
|
|
37
62
|
// Constructive backend intermittently resets a connection mid-introspection
|
|
38
63
|
// ("Failed to fetch schema: Connection reset"), which is transient — a retry
|
|
39
64
|
// almost always clears it. Re-run the whole generate (it rebuilds all targets)
|
|
40
65
|
// up to `attempts` times, only on a fetch/reset failure; any other failure is
|
|
41
66
|
// real and returned immediately (CODEGEN-RETRY-001).
|
|
42
|
-
async function runCodegenWithRetry(appDir, env, attempts = 4) {
|
|
67
|
+
async function runCodegenWithRetry(appDir, env, exec, composed, attempts = 4) {
|
|
43
68
|
const sdk = node_path_1.default.join(appDir, 'src', 'graphql', 'sdk');
|
|
69
|
+
const { cmd, args } = codegenCommand(appDir, composed);
|
|
44
70
|
let last = { ok: false, out: 'codegen never ran' };
|
|
45
71
|
for (let i = 1; i <= attempts; i++) {
|
|
46
|
-
|
|
47
|
-
|
|
72
|
+
// A composed app ships SDK stubs so it builds before codegen; codegen prunes
|
|
73
|
+
// its own stale files, so the stubs stay until a run replaces them.
|
|
74
|
+
if (!composed) {
|
|
75
|
+
for (const sub of ['admin', 'auth', 'app']) {
|
|
76
|
+
await (0, promises_1.rm)(node_path_1.default.join(sdk, sub), { recursive: true, force: true });
|
|
77
|
+
}
|
|
48
78
|
}
|
|
49
|
-
last = await (
|
|
50
|
-
if (last.ok)
|
|
51
|
-
return last;
|
|
52
|
-
if (!FETCH_RESET_RE.test(last.out))
|
|
53
|
-
return last;
|
|
79
|
+
last = await exec(cmd, args, appDir, env);
|
|
80
|
+
if (last.ok || !FETCH_RESET_RE.test(last.out) || REFUSED_RE.test(last.out))
|
|
81
|
+
return { ...last, attempts: i };
|
|
54
82
|
if (i < attempts)
|
|
55
83
|
await sleep(2000 * i);
|
|
56
84
|
}
|
|
57
|
-
return last;
|
|
85
|
+
return { ...last, attempts };
|
|
58
86
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
const backend = (0, host_1.getHost)().backendConfig();
|
|
80
|
-
const apiEndpoint = envApiEndpoint || backend?.apiEndpoint || DEFAULT_API_ENDPOINT;
|
|
81
|
-
const planes = (0, endpoints_1.derivePlaneEndpoints)(apiEndpoint, dbName);
|
|
82
|
-
// The dev server reads .env.local (NEXT_PUBLIC_DB_NAME + per-DB endpoint
|
|
83
|
-
// overrides so it doesn't fall back to localhost vhosts on a remote
|
|
84
|
-
// backend); the codegen config reads process.env (passed directly below).
|
|
85
|
-
try {
|
|
86
|
-
await (0, promises_1.writeFile)(node_path_1.default.join(appDir, '.env.local'), (0, endpoints_1.envLocalContent)(dbName, planes));
|
|
87
|
-
}
|
|
88
|
-
catch (err) {
|
|
89
|
-
return fail(`Failed to write packages/app/.env.local: ${err instanceof Error ? err.message : String(err)}`);
|
|
90
|
-
}
|
|
91
|
-
// Hoist the graphql override to the root workspace (PNPM-GRAPHQL-OVERRIDE-001)
|
|
92
|
-
// and pin codegen to latest (TS-001) before the single clean install. When
|
|
93
|
-
// prewarmed by provision_database these are idempotent and the install is a
|
|
94
|
-
// fast no-op; otherwise this is the first install (PNPM-BUILDS-001).
|
|
95
|
-
await (0, app_workspace_1.ensureWorkspaceConfig)(cwd);
|
|
96
|
-
await (0, app_workspace_1.pinCodegenLatest)(appDir);
|
|
97
|
-
const install = await (0, app_workspace_1.run)('pnpm', ['install', '--no-frozen-lockfile'], cwd);
|
|
98
|
-
if (!install.ok && !app_workspace_1.IGNORED_BUILDS_RE.test(install.out)) {
|
|
99
|
-
return fail(`pnpm install failed:\n${install.out}`);
|
|
100
|
-
}
|
|
101
|
-
const codegen = await runCodegenWithRetry(appDir, {
|
|
102
|
-
NEXT_PUBLIC_DB_NAME: dbName,
|
|
103
|
-
...(planes ? (0, endpoints_1.codegenEndpointEnv)(planes) : {}),
|
|
104
|
-
});
|
|
105
|
-
if (!codegen.ok) {
|
|
106
|
-
// A reset that survives every internal retry is a backend OUTAGE, not a code
|
|
107
|
-
// or install problem — re-running run_codegen cannot fix it. Say so explicitly
|
|
108
|
-
// so the agent marks sdk-codegen `blocked` instead of looping the tool to the
|
|
109
|
-
// time cap (CODEGEN-OUTAGE-001 / THRASH-001).
|
|
110
|
-
if (FETCH_RESET_RE.test(codegen.out)) {
|
|
111
|
-
return fail('codegen failed: BACKEND OUTAGE — the Constructive backend reset the connection ' +
|
|
112
|
-
'during schema introspection, and it stayed down across every internal retry. ' +
|
|
113
|
-
'This is an EXTERNAL outage, not a code or install problem; re-running run_codegen ' +
|
|
114
|
-
'will NOT fix it. Call run_preflight once — if the backend is down, mark ' +
|
|
115
|
-
'sdk-codegen `blocked` (SERVER-001, BLOCKED-PROCEED-001) and hand back to ' +
|
|
116
|
-
'build-orchestrator. Do NOT loop on run_codegen (THRASH-001).\n\n' +
|
|
117
|
-
codegen.out);
|
|
87
|
+
// `exec` runs every child process (clone, install, codegen); tests inject it.
|
|
88
|
+
function createRunCodegenTool(exec = app_workspace_1.run) {
|
|
89
|
+
return {
|
|
90
|
+
name: 'run_codegen',
|
|
91
|
+
label: 'Run codegen',
|
|
92
|
+
description: "Generate the typed GraphQL SDK for the project's Next.js app (packages/app). Runs the exact deterministic sequence — scaffold packages/app from the constructive-app template if it doesn't exist yet, hoist the graphql override to the root workspace so a single grafast resolves, pin codegen to latest, do one clean install, run codegen (retrying transient backend connection resets), and normalize SDK barrels for Turbopack. A composed app (packages/app/.constructive/compose.json) skips the scaffold and those fixups: it installs from its own lockfile only when node_modules is missing and runs its local codegen. Run AFTER provisioning the database and creating schema; it scaffolds packages/app itself, so no separate `pgpm init` step is needed. The scaffold + install are usually prewarmed during provision_database, so this is fast.",
|
|
93
|
+
promptSnippet: 'run_codegen: scaffold packages/app (if missing) + regenerate the typed SDK (workspace-config + clean install + codegen-with-retry + barrel-normalize; a composed app keeps its lockfile and local codegen). Run after schema changes. Gated.',
|
|
94
|
+
parameters: RunCodegenZod,
|
|
95
|
+
async execute(_params, ctx) {
|
|
96
|
+
const started = Date.now();
|
|
97
|
+
const since = (t) => Date.now() - t;
|
|
98
|
+
const cwd = ctx.cwd;
|
|
99
|
+
const appDir = node_path_1.default.join(cwd, 'packages', 'app');
|
|
100
|
+
// A prewarm provision_database started may still be installing; let it
|
|
101
|
+
// finish so this run's install never races it.
|
|
102
|
+
await (0, app_workspace_1.settlePrewarm)(cwd);
|
|
103
|
+
const app = (0, app_workspace_1.detectAppKind)(appDir);
|
|
104
|
+
if (app.kind === 'invalid') {
|
|
105
|
+
return fail(`${app.reason} Recompose packages/app, or remove the marker to use the template flow.`);
|
|
118
106
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
107
|
+
const composed = app.kind === 'composed';
|
|
108
|
+
const appKind = composed ? 'composed' : 'template';
|
|
109
|
+
const timings = {
|
|
110
|
+
scaffoldMs: 0,
|
|
111
|
+
installMs: 0,
|
|
112
|
+
codegenMs: 0,
|
|
113
|
+
barrelsMs: 0,
|
|
114
|
+
totalMs: 0,
|
|
115
|
+
};
|
|
116
|
+
let installed;
|
|
117
|
+
let codegenAttempts;
|
|
118
|
+
const stepDetails = () => ({
|
|
119
|
+
appKind,
|
|
120
|
+
installed,
|
|
121
|
+
codegenAttempts,
|
|
122
|
+
timings: { ...timings, totalMs: since(started) },
|
|
123
|
+
});
|
|
124
|
+
// Materialize packages/app deterministically if it isn't there yet — this
|
|
125
|
+
// replaces the flaky `pgpm init` scaffold step (PGPM-001). When
|
|
126
|
+
// provision_database prewarmed it, this no-ops.
|
|
127
|
+
if (!composed) {
|
|
128
|
+
const t = Date.now();
|
|
129
|
+
const scaffold = await (0, app_workspace_1.ensureAppScaffold)(cwd, exec);
|
|
130
|
+
timings.scaffoldMs = since(t);
|
|
131
|
+
if (!scaffold.ok) {
|
|
132
|
+
return fail(scaffold.out, stepDetails());
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const { databaseName: dbName, apiEndpoint: envApiEndpoint } = await readProjectEnv(cwd);
|
|
136
|
+
if (!dbName) {
|
|
137
|
+
return fail('DATABASE_NAME missing from .env — provision the database first, then re-run.');
|
|
138
|
+
}
|
|
139
|
+
const backend = (0, host_1.getHost)().backendConfig();
|
|
140
|
+
const apiEndpoint = envApiEndpoint || backend?.apiEndpoint || DEFAULT_API_ENDPOINT;
|
|
141
|
+
const planes = (0, endpoints_1.derivePlaneEndpoints)(apiEndpoint, dbName);
|
|
142
|
+
// The dev server reads .env.local (NEXT_PUBLIC_DB_NAME + per-DB endpoint
|
|
143
|
+
// overrides so it doesn't fall back to localhost vhosts on a remote
|
|
144
|
+
// backend); the codegen config reads process.env (passed directly below).
|
|
145
|
+
try {
|
|
146
|
+
await (0, promises_1.writeFile)(node_path_1.default.join(appDir, '.env.local'), (0, endpoints_1.envLocalContent)(dbName, planes));
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
return fail(`Failed to write packages/app/.env.local: ${err instanceof Error ? err.message : String(err)}`);
|
|
150
|
+
}
|
|
151
|
+
const tInstall = Date.now();
|
|
152
|
+
if (composed) {
|
|
153
|
+
const install = await (0, app_workspace_1.ensureComposedInstall)(cwd, exec);
|
|
154
|
+
timings.installMs = since(tInstall);
|
|
155
|
+
installed = install.installed;
|
|
156
|
+
if (!install.ok) {
|
|
157
|
+
return fail(install.out, stepDetails());
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// Hoist the graphql override to the root workspace (PNPM-GRAPHQL-OVERRIDE-001)
|
|
162
|
+
// and pin codegen to latest (TS-001) before the single clean install. When
|
|
163
|
+
// prewarmed by provision_database these are idempotent and the install is a
|
|
164
|
+
// fast no-op; otherwise this is the first install (PNPM-BUILDS-001).
|
|
165
|
+
await (0, app_workspace_1.ensureWorkspaceConfig)(cwd);
|
|
166
|
+
await (0, app_workspace_1.pinCodegenLatest)(appDir);
|
|
167
|
+
const install = await exec('pnpm', ['install', '--no-frozen-lockfile'], cwd);
|
|
168
|
+
timings.installMs = since(tInstall);
|
|
169
|
+
installed = true;
|
|
170
|
+
if (!install.ok && !app_workspace_1.IGNORED_BUILDS_RE.test(install.out)) {
|
|
171
|
+
return fail(`pnpm install failed:\n${install.out}`, stepDetails());
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const tCodegen = Date.now();
|
|
175
|
+
const codegen = await runCodegenWithRetry(appDir, {
|
|
176
|
+
NEXT_PUBLIC_DB_NAME: dbName,
|
|
177
|
+
...(planes ? (0, endpoints_1.codegenEndpointEnv)(planes) : {}),
|
|
178
|
+
}, exec, composed);
|
|
179
|
+
timings.codegenMs = since(tCodegen);
|
|
180
|
+
codegenAttempts = codegen.attempts;
|
|
181
|
+
if (!codegen.ok) {
|
|
182
|
+
// A refused introspection may be the database itself: deleted or
|
|
183
|
+
// suspended databases answer ACCESS_SUSPENDED on every app API.
|
|
184
|
+
if (REFUSED_RE.test(codegen.out) && planes && (await isDatabaseUnavailable(planes.app))) {
|
|
185
|
+
return fail(`codegen failed: the app database "${dbName}" is unavailable (the backend answered ACCESS_SUSPENDED: it was deleted or is suspended). Re-running run_codegen won't help until it is restored.`, { ...stepDetails(), code: 'database-unavailable' });
|
|
186
|
+
}
|
|
187
|
+
// A reset that survives every internal retry is a backend OUTAGE, not a code
|
|
188
|
+
// or install problem — re-running run_codegen cannot fix it. Say so explicitly
|
|
189
|
+
// so the agent marks sdk-codegen `blocked` instead of looping the tool to the
|
|
190
|
+
// time cap (CODEGEN-OUTAGE-001 / THRASH-001).
|
|
191
|
+
if (FETCH_RESET_RE.test(codegen.out)) {
|
|
192
|
+
return fail('codegen failed: BACKEND OUTAGE — the Constructive backend reset the connection ' +
|
|
193
|
+
'during schema introspection, and it stayed down across every internal retry. ' +
|
|
194
|
+
'This is an EXTERNAL outage, not a code or install problem; re-running run_codegen ' +
|
|
195
|
+
'will NOT fix it. Call run_preflight once — if the backend is down, mark ' +
|
|
196
|
+
'sdk-codegen `blocked` (SERVER-001, BLOCKED-PROCEED-001) and hand back to ' +
|
|
197
|
+
'build-orchestrator. Do NOT loop on run_codegen (THRASH-001).\n\n' +
|
|
198
|
+
codegen.out, stepDetails());
|
|
199
|
+
}
|
|
200
|
+
return fail(`codegen failed:\n${codegen.out}`, stepDetails());
|
|
201
|
+
}
|
|
202
|
+
// Normalize directory barrels so Turbopack can resolve them (TURBOPACK-BARREL-001).
|
|
203
|
+
// Only directories that exist are rewritten, so a config without an admin
|
|
204
|
+
// target leaves nothing pointing at one.
|
|
205
|
+
const tBarrels = Date.now();
|
|
206
|
+
const sdkRoot = node_path_1.default.join(appDir, 'src', 'graphql', 'sdk');
|
|
207
|
+
const barrelsRewritten = (0, barrels_1.normalizeSdkBarrels)(sdkRoot);
|
|
208
|
+
timings.barrelsMs = since(tBarrels);
|
|
209
|
+
// Report which app submodules actually generated — a bare scaffold with only
|
|
210
|
+
// index.ts means codegen had no schema to generate against.
|
|
211
|
+
const appSdk = node_path_1.default.join(sdkRoot, 'app');
|
|
212
|
+
const generatedSubmodules = ['hooks', 'orm', 'types'].filter((sub) => (0, node_fs_1.existsSync)(node_path_1.default.join(appSdk, sub)));
|
|
213
|
+
const message = generatedSubmodules.length > 0
|
|
214
|
+
? `Codegen complete. Generated app submodules: ${generatedSubmodules.join(', ')}. Normalized ${barrelsRewritten} barrel file(s).`
|
|
215
|
+
: `Codegen ran, but no app submodules (hooks/orm/types) were generated — the database may have no entity tables yet. Normalized ${barrelsRewritten} barrel file(s).`;
|
|
216
|
+
return {
|
|
217
|
+
content: [{ type: 'text', text: message }],
|
|
218
|
+
details: {
|
|
219
|
+
success: generatedSubmodules.length > 0,
|
|
220
|
+
message,
|
|
221
|
+
barrelsRewritten,
|
|
222
|
+
generatedSubmodules,
|
|
223
|
+
...stepDetails(),
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
exports.runCodegenTool = createRunCodegenTool();
|
package/tools/templates.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.deleteTemplateTool = exports.updateTemplateTool = exports.applyTemplateT
|
|
|
4
4
|
exports.createTemplatePreviewTables = createTemplatePreviewTables;
|
|
5
5
|
const harness_1 = require("@agentic-kit/harness");
|
|
6
6
|
const zod_1 = require("zod");
|
|
7
|
+
const blueprint_construction_1 = require("../blueprint-construction");
|
|
7
8
|
const context_1 = require("../context");
|
|
8
9
|
// A stored blueprint field's `type` is an expanded object (`{ name: 'text' }`),
|
|
9
10
|
// not the bare string the agent passes to provision_blueprint. Flatten it to the
|
|
@@ -256,21 +257,8 @@ exports.applyTemplateTool = {
|
|
|
256
257
|
const blueprintId = copyResult.copyTemplateToBlueprint?.result;
|
|
257
258
|
if (!blueprintId)
|
|
258
259
|
throw new Error('Failed to copy template');
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
.unwrap();
|
|
262
|
-
const constructionId = constructResult.constructBlueprint?.result;
|
|
263
|
-
if (!constructionId)
|
|
264
|
-
throw new Error('constructBlueprint returned no construction id');
|
|
265
|
-
const constructions = await modules.blueprintConstruction
|
|
266
|
-
.findMany({
|
|
267
|
-
where: { id: { equalTo: constructionId } },
|
|
268
|
-
first: 1,
|
|
269
|
-
select: { tableMap: true },
|
|
270
|
-
})
|
|
271
|
-
.unwrap();
|
|
272
|
-
const tableMap = (constructions?.blueprintConstructions?.nodes?.[0]?.tableMap ?? {});
|
|
273
|
-
const createdCount = Object.keys(tableMap).length;
|
|
260
|
+
const { tables } = await (0, blueprint_construction_1.buildBlueprint)(modules, { blueprintId, schemaId }, { signal: ctx.signal });
|
|
261
|
+
const createdCount = Object.keys(tables).length;
|
|
274
262
|
return done(true, `Applied template "${params.templateName}" — created ${createdCount} table${createdCount === 1 ? '' : 's'}`);
|
|
275
263
|
}
|
|
276
264
|
catch (err) {
|