@stackwright-pro/launch-stackwright-pro 0.4.0-alpha.1 → 0.4.0-alpha.101
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 +21 -0
- package/README.md +24 -6
- package/dist/index.js +328 -102
- package/dist/index.js.map +1 -1
- package/package.json +22 -13
- package/templates/pro/_app.tsx +14 -4
- package/templates/pro/content.yml +1 -1
- package/templates/pro/next.config.js +3 -0
- package/templates/pro/prebuild.js +119 -74
- package/templates/pro/pro-providers.tsx +46 -0
- package/templates/pro/scripts/pro-content-plugin.js +99 -0
package/dist/index.js
CHANGED
|
@@ -31,9 +31,12 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "@stackwright-pro/launch-stackwright-pro",
|
|
34
|
-
version: "0.4.0-alpha.
|
|
34
|
+
version: "0.4.0-alpha.96",
|
|
35
35
|
description: "Launch a new Stackwright Pro project with OpenAPI integration, auth, and the otter raft",
|
|
36
|
-
license: "
|
|
36
|
+
license: "SEE LICENSE IN LICENSE",
|
|
37
|
+
publishConfig: {
|
|
38
|
+
access: "public"
|
|
39
|
+
},
|
|
37
40
|
repository: {
|
|
38
41
|
type: "git",
|
|
39
42
|
url: "https://github.com/Per-Aspera-LLC/stackwright-pro"
|
|
@@ -54,27 +57,33 @@ var require_package = __commonJS({
|
|
|
54
57
|
"launch-stackwright-pro": "dist/index.js"
|
|
55
58
|
},
|
|
56
59
|
scripts: {
|
|
57
|
-
build: "tsup",
|
|
60
|
+
build: "node ../../scripts/sync-versions.mjs && tsup",
|
|
61
|
+
"sync-versions": "node ../../scripts/sync-versions.mjs",
|
|
58
62
|
dev: "tsup --watch",
|
|
59
63
|
test: "vitest run",
|
|
60
64
|
"test:coverage": "vitest run --coverage"
|
|
61
65
|
},
|
|
62
66
|
dependencies: {
|
|
67
|
+
"@stackwright-pro/auth": "workspace:*",
|
|
68
|
+
"@stackwright-pro/auth-nextjs": "workspace:*",
|
|
69
|
+
"@stackwright-pro/mcp": "workspace:*",
|
|
70
|
+
"@stackwright-pro/openapi": "workspace:*",
|
|
71
|
+
"@stackwright-pro/otters": "workspace:*",
|
|
63
72
|
"@stackwright-pro/scaffold-hooks": "workspace:*",
|
|
64
|
-
"@stackwright/cli": "^0.
|
|
65
|
-
"@stackwright/scaffold-core": "^0.
|
|
73
|
+
"@stackwright/cli": "^0.9.0",
|
|
74
|
+
"@stackwright/scaffold-core": "^0.3.2",
|
|
66
75
|
chalk: "^5.6.2",
|
|
67
|
-
commander: "^
|
|
68
|
-
"fs-extra": "^11.3",
|
|
69
|
-
"js-yaml": "^4.
|
|
76
|
+
commander: "^15.0.0",
|
|
77
|
+
"fs-extra": "^11.3.5",
|
|
78
|
+
"js-yaml": "^4.2.0"
|
|
70
79
|
},
|
|
71
80
|
devDependencies: {
|
|
72
81
|
"@types/fs-extra": "^11.0",
|
|
73
82
|
"@types/js-yaml": "^4.0.9",
|
|
74
|
-
"@types/node": "
|
|
75
|
-
typescript: "
|
|
76
|
-
tsup: "
|
|
77
|
-
vitest: "
|
|
83
|
+
"@types/node": "catalog:",
|
|
84
|
+
typescript: "catalog:",
|
|
85
|
+
tsup: "catalog:",
|
|
86
|
+
vitest: "catalog:"
|
|
78
87
|
}
|
|
79
88
|
};
|
|
80
89
|
}
|
|
@@ -82,40 +91,99 @@ var require_package = __commonJS({
|
|
|
82
91
|
|
|
83
92
|
// src/index.ts
|
|
84
93
|
var import_commander = require("commander");
|
|
85
|
-
var
|
|
86
|
-
var
|
|
94
|
+
var import_path2 = __toESM(require("path"));
|
|
95
|
+
var import_os = __toESM(require("os"));
|
|
96
|
+
var import_fs_extra2 = __toESM(require("fs-extra"));
|
|
87
97
|
var import_chalk = __toESM(require("chalk"));
|
|
88
|
-
var
|
|
98
|
+
var import_child_process = require("child_process");
|
|
89
99
|
var import_cli = require("@stackwright/cli");
|
|
100
|
+
var import_scaffold_core = require("@stackwright/scaffold-core");
|
|
90
101
|
var import_scaffold_hooks = require("@stackwright-pro/scaffold-hooks");
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
102
|
+
|
|
103
|
+
// src/scaffold.ts
|
|
104
|
+
var import_fs_extra = __toESM(require("fs-extra"));
|
|
105
|
+
var import_js_yaml = __toESM(require("js-yaml"));
|
|
106
|
+
var import_path = __toESM(require("path"));
|
|
107
|
+
|
|
108
|
+
// src/generated/workspace-versions.ts
|
|
109
|
+
var PRO_VERSIONS = {
|
|
110
|
+
"@stackwright-pro/auth": "0.2.0-alpha.11",
|
|
111
|
+
"@stackwright-pro/auth-nextjs": "0.2.0-alpha.13",
|
|
112
|
+
"@stackwright-pro/display-components": "0.2.0-alpha.10",
|
|
113
|
+
"@stackwright-pro/mcp": "0.2.0-alpha.61",
|
|
114
|
+
"@stackwright-pro/openapi": "0.3.0-alpha.24",
|
|
115
|
+
"@stackwright-pro/otters": "1.0.0-alpha.45",
|
|
116
|
+
"@stackwright-pro/pulse": "0.3.0-alpha.17",
|
|
117
|
+
"@stackwright-pro/workflow": "0.1.0-alpha.15",
|
|
118
|
+
"@stackwright-pro/workflow-components": "0.1.0-alpha.17"
|
|
119
|
+
};
|
|
120
|
+
var OSS_VERSIONS = {
|
|
121
|
+
"@stackwright/build-scripts": "^0.8.0",
|
|
122
|
+
"@stackwright/collections": "^0.1.2",
|
|
123
|
+
"@stackwright/core": "^0.9.0",
|
|
124
|
+
"@stackwright/icons": "^0.5.2",
|
|
125
|
+
"@stackwright/mcp": "^0.5.0",
|
|
126
|
+
"@stackwright/nextjs": "^0.6.0",
|
|
127
|
+
"@stackwright/otters": "^0.2.2",
|
|
128
|
+
"@stackwright/types": "^1.6.0",
|
|
129
|
+
"@stackwright/ui-shadcn": "^0.1.3"
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// src/scaffold.ts
|
|
133
|
+
async function addProDepsToPackageJson(targetDir) {
|
|
134
|
+
const pkgPath = import_path.default.join(targetDir, "package.json");
|
|
135
|
+
const content = await import_fs_extra.default.readFile(pkgPath, "utf-8");
|
|
136
|
+
const pkg = JSON.parse(content);
|
|
137
|
+
const dependencies = typeof pkg.dependencies === "object" && pkg.dependencies !== null ? { ...pkg.dependencies } : {};
|
|
138
|
+
const devDependencies = typeof pkg.devDependencies === "object" && pkg.devDependencies !== null ? { ...pkg.devDependencies } : {};
|
|
139
|
+
const scripts = typeof pkg.scripts === "object" && pkg.scripts !== null ? { ...pkg.scripts } : {};
|
|
140
|
+
for (const [pkgName, pinnedVersion] of Object.entries(OSS_VERSIONS)) {
|
|
141
|
+
if (pkgName in dependencies) {
|
|
142
|
+
dependencies[pkgName] = pinnedVersion;
|
|
143
|
+
}
|
|
144
|
+
if (pkgName in devDependencies) {
|
|
145
|
+
devDependencies[pkgName] = pinnedVersion;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
Object.assign(dependencies, {
|
|
149
|
+
"@stackwright-pro/display-components": PRO_VERSIONS["@stackwright-pro/display-components"],
|
|
150
|
+
"@stackwright-pro/mcp": PRO_VERSIONS["@stackwright-pro/mcp"],
|
|
151
|
+
"@stackwright-pro/otters": PRO_VERSIONS["@stackwright-pro/otters"],
|
|
152
|
+
"@stackwright-pro/openapi": PRO_VERSIONS["@stackwright-pro/openapi"],
|
|
153
|
+
"@stackwright-pro/auth": PRO_VERSIONS["@stackwright-pro/auth"],
|
|
154
|
+
"@stackwright-pro/auth-nextjs": PRO_VERSIONS["@stackwright-pro/auth-nextjs"],
|
|
155
|
+
"@stackwright-pro/pulse": PRO_VERSIONS["@stackwright-pro/pulse"],
|
|
156
|
+
"@stackwright-pro/workflow-components": PRO_VERSIONS["@stackwright-pro/workflow-components"],
|
|
157
|
+
"@stackwright-pro/workflow": PRO_VERSIONS["@stackwright-pro/workflow"],
|
|
158
|
+
"@stackwright/build-scripts": OSS_VERSIONS["@stackwright/build-scripts"],
|
|
159
|
+
zod: "^4.0.0"
|
|
160
|
+
});
|
|
161
|
+
Object.assign(devDependencies, {
|
|
162
|
+
"@stoplight/prism-cli": "^5.15.10"
|
|
163
|
+
});
|
|
164
|
+
Object.assign(scripts, {
|
|
165
|
+
"dev:admin": "MOCK_USER=admin next dev",
|
|
166
|
+
"dev:analyst": "MOCK_USER=analyst next dev",
|
|
167
|
+
"dev:viewer": "MOCK_USER=viewer next dev",
|
|
168
|
+
prebuild: "node scripts/prebuild.js",
|
|
169
|
+
predev: "node scripts/prebuild.js"
|
|
170
|
+
});
|
|
171
|
+
pkg.dependencies = dependencies;
|
|
172
|
+
pkg.devDependencies = devDependencies;
|
|
173
|
+
pkg.scripts = scripts;
|
|
174
|
+
await import_fs_extra.default.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
96
175
|
}
|
|
97
176
|
async function addAuthToStackwrightYml(targetDir) {
|
|
98
177
|
const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
|
|
99
178
|
const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
|
|
100
|
-
const config = import_js_yaml.default.load(content);
|
|
101
|
-
config.auth = {
|
|
102
|
-
type: "pki",
|
|
103
|
-
profile: "dod_cac",
|
|
104
|
-
source: "gateway_headers",
|
|
105
|
-
roles: [
|
|
106
|
-
{ name: "ADMIN", permissions: ["*"] },
|
|
107
|
-
{ name: "ANALYST", permissions: ["data:read", "data:write"] },
|
|
108
|
-
{ name: "VIEWER", permissions: ["data:read"] }
|
|
109
|
-
],
|
|
110
|
-
protected_routes: [{ path: "/*", roles: ["VIEWER", "ANALYST", "ADMIN"] }],
|
|
111
|
-
public_routes: ["/", "/getting-started"]
|
|
112
|
-
};
|
|
179
|
+
const config = import_js_yaml.default.load(content, { schema: import_js_yaml.default.CORE_SCHEMA });
|
|
180
|
+
config.auth = {};
|
|
113
181
|
await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
|
|
114
182
|
}
|
|
115
183
|
async function addIntegrationToStackwrightYml(targetDir, specName, specFilename, mockUrl) {
|
|
116
184
|
const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
|
|
117
185
|
const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
|
|
118
|
-
const config = import_js_yaml.default.load(content);
|
|
186
|
+
const config = import_js_yaml.default.load(content, { schema: import_js_yaml.default.CORE_SCHEMA });
|
|
119
187
|
const integration = {
|
|
120
188
|
type: "openapi",
|
|
121
189
|
name: specName,
|
|
@@ -125,20 +193,71 @@ async function addIntegrationToStackwrightYml(targetDir, specName, specFilename,
|
|
|
125
193
|
if (mockUrl) {
|
|
126
194
|
integration.mockUrl = mockUrl;
|
|
127
195
|
}
|
|
128
|
-
config.integrations = [
|
|
196
|
+
config.integrations = [
|
|
197
|
+
...Array.isArray(config.integrations) ? config.integrations : [],
|
|
198
|
+
integration
|
|
199
|
+
];
|
|
129
200
|
await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
|
|
130
201
|
}
|
|
131
|
-
async function
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
202
|
+
async function scaffoldWorkspaceConfig(targetDir) {
|
|
203
|
+
const workspaceDir = import_path.default.join(targetDir, ".code_puppy");
|
|
204
|
+
await import_fs_extra.default.ensureDir(workspaceDir);
|
|
205
|
+
const workspaceStats = await import_fs_extra.default.lstat(workspaceDir);
|
|
206
|
+
if (workspaceStats.isSymbolicLink()) {
|
|
207
|
+
throw new Error(".code_puppy/ is a symlink \u2014 refusing to write. Check for tampering.");
|
|
208
|
+
}
|
|
209
|
+
const agentsDir = import_path.default.join(workspaceDir, "agents");
|
|
210
|
+
await import_fs_extra.default.ensureDir(agentsDir);
|
|
211
|
+
if (await import_fs_extra.default.pathExists(agentsDir)) {
|
|
212
|
+
const agentsStats = await import_fs_extra.default.lstat(agentsDir);
|
|
213
|
+
if (agentsStats.isSymbolicLink()) {
|
|
214
|
+
throw new Error(".code_puppy/agents is a symlink \u2014 refusing to write. Check for tampering.");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const configPath = import_path.default.join(workspaceDir, "config.json");
|
|
218
|
+
let existingConfig = {};
|
|
219
|
+
if (import_fs_extra.default.existsSync(configPath)) {
|
|
220
|
+
try {
|
|
221
|
+
existingConfig = JSON.parse(await import_fs_extra.default.readFile(configPath, "utf-8"));
|
|
222
|
+
} catch {
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (await import_fs_extra.default.pathExists(configPath)) {
|
|
226
|
+
const configStats = await import_fs_extra.default.lstat(configPath);
|
|
227
|
+
if (configStats.isSymbolicLink()) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
".code_puppy/config.json is a symlink \u2014 refusing to write. Check for tampering."
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
await import_fs_extra.default.writeFile(
|
|
234
|
+
configPath,
|
|
235
|
+
JSON.stringify({ ...existingConfig, projectOnly: true }, null, 2) + "\n",
|
|
236
|
+
"utf-8"
|
|
237
|
+
);
|
|
238
|
+
const mcpServersPath = import_path.default.join(workspaceDir, "mcp_servers.json");
|
|
239
|
+
if (await import_fs_extra.default.pathExists(mcpServersPath)) {
|
|
240
|
+
const mcpStats = await import_fs_extra.default.lstat(mcpServersPath);
|
|
241
|
+
if (mcpStats.isSymbolicLink()) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
".code_puppy/mcp_servers.json is a symlink \u2014 refusing to write. Check for tampering."
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (!import_fs_extra.default.existsSync(mcpServersPath)) {
|
|
248
|
+
const mcpConfig = {
|
|
249
|
+
mcp_servers: {
|
|
250
|
+
"stackwright-pro-mcp": {
|
|
251
|
+
type: "stdio",
|
|
252
|
+
command: "pnpm",
|
|
253
|
+
args: ["exec", "stackwright-pro-mcp"],
|
|
254
|
+
enabled: true,
|
|
255
|
+
cwd: "${PROJECT_ROOT}"
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
await import_fs_extra.default.writeFile(mcpServersPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
|
|
135
260
|
}
|
|
136
|
-
const specFilename = import_path.default.basename(resolvedSpec);
|
|
137
|
-
const derivedName = specName || import_path.default.basename(specFilename, import_path.default.extname(specFilename));
|
|
138
|
-
const specsDir = import_path.default.join(targetDir, "specs");
|
|
139
|
-
await import_fs_extra.default.ensureDir(specsDir);
|
|
140
|
-
await import_fs_extra.default.copy(resolvedSpec, import_path.default.join(specsDir, specFilename));
|
|
141
|
-
return { specFilename, derivedName };
|
|
142
261
|
}
|
|
143
262
|
function generateReadme(options) {
|
|
144
263
|
const { projectName, hasSpec, specName, specFilename, hasOtters, hasMock } = options;
|
|
@@ -161,7 +280,7 @@ export const EquipmentSchema = z.object({
|
|
|
161
280
|
export type Equipment = z.infer<typeof EquipmentSchema>;
|
|
162
281
|
|
|
163
282
|
// src/generated/${specName}/client.ts
|
|
164
|
-
export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
|
|
283
|
+
export class ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client {
|
|
165
284
|
async getEquipment(id: string): Promise<Equipment> {
|
|
166
285
|
// Auto-wired with auth, validation, error handling
|
|
167
286
|
}
|
|
@@ -174,10 +293,10 @@ export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
|
|
|
174
293
|
3. You import and use it:
|
|
175
294
|
|
|
176
295
|
\`\`\`typescript
|
|
177
|
-
import { ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client } from '@/generated/${specName}/client';
|
|
296
|
+
import { ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client } from '@/generated/${specName}/client';
|
|
178
297
|
import type { Equipment } from '@/generated/${specName}/types';
|
|
179
298
|
|
|
180
|
-
const client = new ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client();
|
|
299
|
+
const client = new ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client();
|
|
181
300
|
const gear: Equipment = await client.getEquipment('123');
|
|
182
301
|
\`\`\`
|
|
183
302
|
|
|
@@ -201,26 +320,26 @@ The generated API client is automatically configured to use the mock server.
|
|
|
201
320
|
` : "";
|
|
202
321
|
const aiAgentsSection = hasOtters ? `## \u{1F9A6} AI Agents (The Otter Raft)
|
|
203
322
|
|
|
204
|
-
Your project includes **
|
|
323
|
+
Your project includes **SIX specialized AI agents** in \`node_modules/@stackwright-pro/otters/\`:
|
|
205
324
|
|
|
206
325
|
- \`stackwright-pro-foreman-otter\` - Master coordinator that orchestrates all specialist otters
|
|
207
326
|
- \`stackwright-pro-api-otter\` - Discovers entities from OpenAPI specs
|
|
208
|
-
- \`stackwright-pro-
|
|
327
|
+
- \`stackwright-pro-auth-otter\` - Configures CAC/OIDC auth, RBAC rules, and audit middleware
|
|
328
|
+
- \`stackwright-pro-data-otter\` - Configures endpoint filters & ISR revalidation
|
|
329
|
+
- \`stackwright-pro-page-otter\` - Generates pages auto-wired with data, theme, and auth
|
|
209
330
|
- \`stackwright-pro-dashboard-otter\` - Builds live data dashboards
|
|
210
331
|
|
|
211
|
-
**
|
|
332
|
+
**Start the otter raft:**
|
|
212
333
|
|
|
213
334
|
\`\`\`bash
|
|
214
|
-
|
|
335
|
+
npx @stackwright-pro/raft
|
|
215
336
|
\`\`\`
|
|
216
337
|
|
|
217
|
-
|
|
338
|
+
The raft verifies otter integrity, writes project context, and spawns code-puppy in foreman mode. The foreman asks questions about your brand, auth, and data preferences, then coordinates specialist otters to build your app. All state lives in \`.stackwright/\` \u2014 interrupt and resume anytime.
|
|
218
339
|
|
|
219
|
-
|
|
340
|
+
**Example session prompt:**
|
|
220
341
|
|
|
221
|
-
"
|
|
222
|
-
|
|
223
|
-
The Pro Foreman coordinates specialist otters, validates outputs, and renders previews\u2014all with live API data.
|
|
342
|
+
"Build me a logistics dashboard from our OpenAPI spec. Connect to /equipment and /supplies endpoints."
|
|
224
343
|
|
|
225
344
|
` : "";
|
|
226
345
|
return `# ${projectName}
|
|
@@ -230,10 +349,7 @@ A Stackwright Pro application with role-based auth, OpenAPI integration, and int
|
|
|
230
349
|
## \u{1F680} Quick Start
|
|
231
350
|
|
|
232
351
|
\`\`\`bash
|
|
233
|
-
#
|
|
234
|
-
pnpm install
|
|
235
|
-
|
|
236
|
-
# Start development server (no auth mock)
|
|
352
|
+
# Start development server
|
|
237
353
|
pnpm dev
|
|
238
354
|
|
|
239
355
|
# Open your browser
|
|
@@ -262,12 +378,16 @@ ${mockSection}## \u{1F4C1} Project Structure
|
|
|
262
378
|
|
|
263
379
|
\`\`\`
|
|
264
380
|
.
|
|
381
|
+
\u251C\u2500\u2500 app/
|
|
382
|
+
\u2502 \u251C\u2500\u2500 layout.tsx # Root layout (StackwrightLayout + Providers)
|
|
383
|
+
\u2502 \u251C\u2500\u2500 page.tsx # Home page (renders root content)
|
|
384
|
+
\u2502 \u251C\u2500\u2500 [...slug]/
|
|
385
|
+
\u2502 \u2502 \u2514\u2500\u2500 page.tsx # Dynamic pages from YAML content
|
|
386
|
+
\u2502 \u2514\u2500\u2500 _components/
|
|
387
|
+
\u2502 \u251C\u2500\u2500 providers.tsx # Pro: AuthProvider + component registration
|
|
388
|
+
\u2502 \u2514\u2500\u2500 page-client.tsx # Client-side page renderer
|
|
265
389
|
\u251C\u2500\u2500 pages/
|
|
266
|
-
\u2502 \
|
|
267
|
-
\u2502 \u251C\u2500\u2500 index.tsx # Home page (auto-generated by Stackwright)
|
|
268
|
-
\u2502 \u251C\u2500\u2500 about/
|
|
269
|
-
\u2502 \u2502 \u2514\u2500\u2500 content.yml # Page content in YAML
|
|
270
|
-
\u2502 \u2514\u2500\u2500 ...
|
|
390
|
+
\u2502 \u2514\u2500\u2500 content.yml # Root page content in YAML
|
|
271
391
|
\u251C\u2500\u2500 lib/
|
|
272
392
|
\u2502 \u2514\u2500\u2500 mock-auth.ts # Dev-only auth mocking (no backend needed)
|
|
273
393
|
\u251C\u2500\u2500 scripts/
|
|
@@ -287,7 +407,7 @@ ${mockSection}## \u{1F4C1} Project Structure
|
|
|
287
407
|
\`\`\`
|
|
288
408
|
|
|
289
409
|
**Key Files:**
|
|
290
|
-
- \`
|
|
410
|
+
- \`app/_components/providers.tsx\` - Wraps your app with \`AuthProvider\` for RBAC
|
|
291
411
|
- \`stackwright.yml\` - Single source of truth for theme, auth roles, and API wiring
|
|
292
412
|
- \`lib/mock-auth.ts\` - Mocks CAC/PIV headers locally so you can test auth flows
|
|
293
413
|
- \`scripts/prebuild.js\` - Runs before dev/build to generate types from your OpenAPI spec
|
|
@@ -397,39 +517,118 @@ ${aiAgentsSection}## \u{1F4DA} Learn More
|
|
|
397
517
|
**Questions?** File an issue or ping us in the discussions. We're here to help you ship faster.
|
|
398
518
|
`;
|
|
399
519
|
}
|
|
520
|
+
|
|
521
|
+
// src/index.ts
|
|
522
|
+
(0, import_scaffold_hooks.registerProScaffoldHooks)();
|
|
523
|
+
var { version } = require_package();
|
|
524
|
+
async function copyProTemplate(templateName, destPath) {
|
|
525
|
+
const src = import_path2.default.resolve(__dirname, "..", "templates", "pro", templateName);
|
|
526
|
+
await import_fs_extra2.default.copy(src, destPath);
|
|
527
|
+
}
|
|
528
|
+
async function handleSpec(targetDir, specPath, specName) {
|
|
529
|
+
const resolvedSpec = import_path2.default.resolve(specPath);
|
|
530
|
+
if (!import_fs_extra2.default.existsSync(resolvedSpec)) {
|
|
531
|
+
throw new Error(`Spec file not found: ${resolvedSpec}`);
|
|
532
|
+
}
|
|
533
|
+
const specFilename = import_path2.default.basename(resolvedSpec);
|
|
534
|
+
const derivedName = specName || import_path2.default.basename(specFilename, import_path2.default.extname(specFilename));
|
|
535
|
+
const specsDir = import_path2.default.join(targetDir, "specs");
|
|
536
|
+
await import_fs_extra2.default.ensureDir(specsDir);
|
|
537
|
+
await import_fs_extra2.default.copy(resolvedSpec, import_path2.default.join(specsDir, specFilename));
|
|
538
|
+
return { specFilename, derivedName };
|
|
539
|
+
}
|
|
400
540
|
async function launch(targetDir, options) {
|
|
401
|
-
const dirName =
|
|
541
|
+
const dirName = import_path2.default.basename(targetDir);
|
|
402
542
|
console.log(import_chalk.default.cyan.bold("\n\u{1F6A2} Launching Stackwright Pro...\n"));
|
|
403
543
|
const scaffoldOpts = {
|
|
404
544
|
name: options.name || dirName,
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
545
|
+
standalone: true,
|
|
546
|
+
// prevents `workspace:*` refs when CLI runs inside the Pro monorepo
|
|
547
|
+
...options.title !== void 0 && { title: options.title },
|
|
548
|
+
...options.theme !== void 0 && { theme: options.theme },
|
|
549
|
+
...options.force !== void 0 && { force: options.force },
|
|
550
|
+
...options.yes !== void 0 && { noInteractive: options.yes }
|
|
409
551
|
};
|
|
410
552
|
await (0, import_cli.scaffold)(targetDir, scaffoldOpts);
|
|
411
553
|
console.log(import_chalk.default.green("\u2705 Base project scaffolded (hooks added Pro deps + MCP config)"));
|
|
412
|
-
await
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
console.log(import_chalk.default.
|
|
554
|
+
await addProDepsToPackageJson(targetDir);
|
|
555
|
+
console.log(import_chalk.default.green("\u{1F9A6} Pro packages added to package.json"));
|
|
556
|
+
const verifyPkg = JSON.parse(
|
|
557
|
+
await import_fs_extra2.default.readFile(import_path2.default.join(targetDir, "package.json"), "utf-8")
|
|
558
|
+
);
|
|
559
|
+
const proDepCount = Object.keys(
|
|
560
|
+
typeof verifyPkg.dependencies === "object" && verifyPkg.dependencies !== null ? verifyPkg.dependencies : {}
|
|
561
|
+
).filter((k) => k.startsWith("@stackwright-pro")).length;
|
|
562
|
+
console.log(import_chalk.default.dim(` \u2192 package.json verified: ${proDepCount} @stackwright-pro/* deps ready`));
|
|
563
|
+
console.log(import_chalk.default.cyan("\n\u{1F4E6} Installing dependencies (this takes a moment)..."));
|
|
564
|
+
if (!options.skipInstall) {
|
|
565
|
+
try {
|
|
566
|
+
(0, import_child_process.execSync)("pnpm install --ignore-workspace", {
|
|
567
|
+
cwd: targetDir,
|
|
568
|
+
stdio: "inherit",
|
|
569
|
+
timeout: 12e4
|
|
570
|
+
});
|
|
571
|
+
console.log(import_chalk.default.green("\u2705 Dependencies installed"));
|
|
572
|
+
} catch (err) {
|
|
573
|
+
const isTimeout = err instanceof Error && (err.message.includes("ETIMEDOUT") || err.message.includes("timeout"));
|
|
574
|
+
console.error(
|
|
575
|
+
import_chalk.default.red("\n\u274C pnpm install failed. Auth and Pro packages are NOT installed.")
|
|
576
|
+
);
|
|
577
|
+
console.error(
|
|
578
|
+
import_chalk.default.red(" Your project is in an incomplete state \u2014 do NOT run or deploy it.")
|
|
579
|
+
);
|
|
580
|
+
if (isTimeout) {
|
|
581
|
+
console.error(
|
|
582
|
+
import_chalk.default.yellow(" The install timed out (>2 min). Check your network connection.")
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
console.error(
|
|
586
|
+
import_chalk.default.yellow(` Fix the error above, then run: pnpm install --ignore-workspace`)
|
|
587
|
+
);
|
|
588
|
+
process.exit(1);
|
|
589
|
+
}
|
|
590
|
+
} else {
|
|
591
|
+
console.log(import_chalk.default.dim("\u2139\uFE0F Skipping install (--skip-install). Run: pnpm install"));
|
|
592
|
+
}
|
|
593
|
+
const dependencyMode = "standalone";
|
|
594
|
+
await (0, import_scaffold_core.runScaffoldHooks)("postInstall", {
|
|
595
|
+
targetDir,
|
|
596
|
+
projectName: options.name || import_path2.default.basename(targetDir),
|
|
597
|
+
siteTitle: options.title || options.name || import_path2.default.basename(targetDir),
|
|
598
|
+
themeId: options.theme || "corporate",
|
|
599
|
+
packageJson: {},
|
|
600
|
+
dependencyMode
|
|
601
|
+
});
|
|
602
|
+
await copyProTemplate(
|
|
603
|
+
"pro-providers.tsx",
|
|
604
|
+
import_path2.default.join(targetDir, "app", "_components", "providers.tsx")
|
|
605
|
+
);
|
|
606
|
+
await copyProTemplate("content.yml", import_path2.default.join(targetDir, "pages", "content.yml"));
|
|
607
|
+
await copyProTemplate("next.config.js", import_path2.default.join(targetDir, "next.config.js"));
|
|
608
|
+
await import_fs_extra2.default.ensureDir(import_path2.default.join(targetDir, "lib"));
|
|
609
|
+
await copyProTemplate("mock-auth.ts", import_path2.default.join(targetDir, "lib", "mock-auth.ts"));
|
|
610
|
+
await copyProTemplate("yaml.d.ts", import_path2.default.join(targetDir, "yaml.d.ts"));
|
|
611
|
+
await import_fs_extra2.default.ensureDir(import_path2.default.join(targetDir, "scripts"));
|
|
612
|
+
await copyProTemplate("prebuild.js", import_path2.default.join(targetDir, "scripts", "prebuild.js"));
|
|
613
|
+
await copyProTemplate(
|
|
614
|
+
"scripts/pro-content-plugin.js",
|
|
615
|
+
import_path2.default.join(targetDir, "scripts", "pro-content-plugin.js")
|
|
616
|
+
);
|
|
617
|
+
console.log(import_chalk.default.green(" Auth integration added (App Router providers + RBAC)"));
|
|
421
618
|
await addAuthToStackwrightYml(targetDir);
|
|
422
619
|
let specInfo = null;
|
|
423
620
|
const MOCK_URL = "http://localhost:4010";
|
|
424
|
-
if (options.spec) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
621
|
+
if (options.spec && options.spec.length > 0) {
|
|
622
|
+
for (const specPath of options.spec) {
|
|
623
|
+
specInfo = await handleSpec(targetDir, specPath, options.specName);
|
|
624
|
+
await addIntegrationToStackwrightYml(
|
|
625
|
+
targetDir,
|
|
626
|
+
specInfo.derivedName,
|
|
627
|
+
specInfo.specFilename,
|
|
628
|
+
options.mock ? MOCK_URL : void 0
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
console.log(import_chalk.default.green(`\u{1F4E1} ${options.spec.length} OpenAPI integration(s) wired up`));
|
|
433
632
|
if (options.mock) {
|
|
434
633
|
console.log(import_chalk.default.green("\u{1F3AD} Prism mock server configured"));
|
|
435
634
|
}
|
|
@@ -439,9 +638,9 @@ async function launch(targetDir, options) {
|
|
|
439
638
|
"\u26A0\uFE0F No spec provided with --mock. Using sample Petstore spec for demo. Replace with your API spec."
|
|
440
639
|
)
|
|
441
640
|
);
|
|
442
|
-
const specsDir =
|
|
443
|
-
await
|
|
444
|
-
await copyProTemplate("petstore.yaml",
|
|
641
|
+
const specsDir = import_path2.default.join(targetDir, "specs");
|
|
642
|
+
await import_fs_extra2.default.ensureDir(specsDir);
|
|
643
|
+
await copyProTemplate("petstore.yaml", import_path2.default.join(specsDir, "petstore.yaml"));
|
|
445
644
|
specInfo = { specFilename: "petstore.yaml", derivedName: "petstore" };
|
|
446
645
|
await addIntegrationToStackwrightYml(
|
|
447
646
|
targetDir,
|
|
@@ -455,18 +654,38 @@ async function launch(targetDir, options) {
|
|
|
455
654
|
const readmeContent = generateReadme({
|
|
456
655
|
projectName: options.name || dirName,
|
|
457
656
|
hasSpec: !!specInfo,
|
|
458
|
-
specName: specInfo
|
|
459
|
-
specFilename: specInfo
|
|
657
|
+
...specInfo?.derivedName !== void 0 && { specName: specInfo.derivedName },
|
|
658
|
+
...specInfo?.specFilename !== void 0 && { specFilename: specInfo.specFilename },
|
|
460
659
|
hasOtters: !options.skipOtters,
|
|
461
660
|
hasMock: !!options.mock
|
|
462
661
|
});
|
|
463
|
-
await
|
|
662
|
+
await import_fs_extra2.default.writeFile(import_path2.default.join(targetDir, "README.md"), readmeContent);
|
|
464
663
|
console.log(import_chalk.default.green("\u{1F4C4} README.md created"));
|
|
465
|
-
const
|
|
664
|
+
const initContext = {
|
|
665
|
+
projectRoot: targetDir,
|
|
666
|
+
projectName: options.name || dirName,
|
|
667
|
+
generatedBy: "launch-stackwright-pro",
|
|
668
|
+
version: "1.0"
|
|
669
|
+
};
|
|
670
|
+
if (specInfo) {
|
|
671
|
+
initContext.specPath = `specs/${specInfo.specFilename}`;
|
|
672
|
+
initContext.specName = specInfo.derivedName;
|
|
673
|
+
}
|
|
674
|
+
initContext.theme = options.theme || "corporate";
|
|
675
|
+
const stackwrightDir = import_path2.default.join(targetDir, ".stackwright");
|
|
676
|
+
await import_fs_extra2.default.ensureDir(stackwrightDir);
|
|
677
|
+
await import_fs_extra2.default.writeFile(
|
|
678
|
+
import_path2.default.join(stackwrightDir, "init-context.json"),
|
|
679
|
+
JSON.stringify(initContext, null, 2) + "\n",
|
|
680
|
+
"utf-8"
|
|
681
|
+
);
|
|
682
|
+
await scaffoldWorkspaceConfig(targetDir);
|
|
683
|
+
console.log(import_chalk.default.green("\u{1F43E} .code_puppy/ workspace scaffolded (projectOnly: true)"));
|
|
684
|
+
const relDir = import_path2.default.relative(process.cwd(), targetDir) || ".";
|
|
466
685
|
console.log(import_chalk.default.cyan.bold("\n\u{1F389} All set! Here's what to do next:\n"));
|
|
467
686
|
console.log(import_chalk.default.white(` 1. cd ${relDir}`));
|
|
468
|
-
console.log(import_chalk.default.white(" 2.
|
|
469
|
-
console.log(import_chalk.default.
|
|
687
|
+
console.log(import_chalk.default.white(" 2. npx @stackwright-pro/raft"));
|
|
688
|
+
console.log(import_chalk.default.dim(" (use --verbose for troubleshooting)"));
|
|
470
689
|
if (specInfo) {
|
|
471
690
|
console.log(import_chalk.default.cyan(`
|
|
472
691
|
\u{1F4E1} Your API spec was copied to specs/${specInfo.specFilename}`));
|
|
@@ -484,13 +703,20 @@ async function launch(targetDir, options) {
|
|
|
484
703
|
async function main() {
|
|
485
704
|
const program = new import_commander.Command();
|
|
486
705
|
program.name("launch-stackwright-pro").description("\u{1F6A2} Launch a new Stackwright Pro project with auth, OpenAPI, and the otter raft").version(version).argument("[directory]", "Project directory", ".").option("--name <name>", "Project name (used in package.json)").option("--title <title>", "Site title shown in the app bar and browser tab").option("--theme <themeId>", "Theme ID (e.g., corporate, creative, minimal)").option("--force", "Launch even if the target directory is not empty").option("--skip-otters", "Skip copying otter raft configs").option("-y, --yes", "Skip all prompts, use defaults").option("--mock", "Configure Prism mock server for API development (runs on port 4010)").option(
|
|
487
|
-
"--spec <
|
|
488
|
-
"
|
|
706
|
+
"--spec <paths...>",
|
|
707
|
+
"Paths to OpenAPI specs (YAML or JSON) \u2014 copies into project and wires up integrations (can be specified multiple times)"
|
|
489
708
|
).option(
|
|
490
709
|
"--spec-name <name>",
|
|
491
710
|
"Name for the API integration (default: derived from spec filename)"
|
|
711
|
+
).option(
|
|
712
|
+
"--skip-install",
|
|
713
|
+
"Skip automatic pnpm install (useful in CI or when using a different package manager)"
|
|
492
714
|
).action(async (directory, options) => {
|
|
493
|
-
const
|
|
715
|
+
const homeDir = import_os.default.homedir();
|
|
716
|
+
const targetDir = directory !== "." ? import_path2.default.resolve(process.cwd(), directory) : options.name ? import_path2.default.resolve(process.cwd(), options.name) : process.cwd();
|
|
717
|
+
if (!targetDir.startsWith(homeDir + import_path2.default.sep) && targetDir !== homeDir) {
|
|
718
|
+
console.warn(import_chalk.default.yellow(`\u26A0\uFE0F Scaffolding outside your home directory: ${targetDir}`));
|
|
719
|
+
}
|
|
494
720
|
await launch(targetDir, options);
|
|
495
721
|
});
|
|
496
722
|
await program.parseAsync(process.argv);
|