@stackwright-pro/launch-stackwright-pro 0.4.0-alpha.12 → 0.4.0-alpha.122
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 +11 -20
- package/dist/index.js +224 -119
- package/dist/index.js.map +1 -1
- package/package.json +17 -17
- 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 -73
- package/templates/pro/pro-providers.tsx +46 -0
- package/templates/pro/scripts/pro-content-plugin.js +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
PROPRIETARY SOFTWARE LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Per Aspera LLC. All Rights Reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
proprietary and confidential property of Per Aspera LLC ("Company").
|
|
7
|
+
|
|
8
|
+
RESTRICTIONS: You may not use, copy, modify, merge, publish, distribute,
|
|
9
|
+
sublicense, sell, or otherwise exploit this Software or any portion thereof
|
|
10
|
+
without the express prior written consent of the Company.
|
|
11
|
+
|
|
12
|
+
GOVERNMENT USE: Use, duplication, or disclosure by the U.S. Government is
|
|
13
|
+
subject to restrictions as set forth in FAR 52.227-19 (Commercial Computer
|
|
14
|
+
Software - Restricted Rights) and DFARS 252.227-7013 (Rights in Technical
|
|
15
|
+
Data and Computer Software), as applicable.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED. IN NO EVENT SHALL THE COMPANY BE LIABLE FOR ANY CLAIM, DAMAGES, OR
|
|
19
|
+
OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE.
|
|
20
|
+
|
|
21
|
+
For licensing inquiries: legal@peraspera.com
|
package/README.md
CHANGED
|
@@ -21,10 +21,14 @@ to automatically wire up Pro features:
|
|
|
21
21
|
## Quick Start
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
|
|
24
|
+
# Works — project created in $cwd/my-app
|
|
25
|
+
pnpx @stackwright-pro/launch-stackwright-pro --name my-app -y
|
|
25
26
|
# Dependencies install automatically (~7s)
|
|
26
27
|
cd my-app
|
|
27
|
-
|
|
28
|
+
npx @stackwright-pro/raft # Start the otter raft
|
|
29
|
+
|
|
30
|
+
# Equivalent explicit form
|
|
31
|
+
pnpx @stackwright-pro/launch-stackwright-pro my-app --name my-app -y
|
|
28
32
|
```
|
|
29
33
|
|
|
30
34
|
### With an OpenAPI Spec
|
|
@@ -72,7 +76,7 @@ Options:
|
|
|
72
76
|
--force Overwrite existing directory
|
|
73
77
|
--skip-otters Skip otter raft setup
|
|
74
78
|
-y, --yes Skip prompts, use defaults
|
|
75
|
-
--spec <
|
|
79
|
+
--spec <paths...> Paths to OpenAPI specs (can be specified multiple times)
|
|
76
80
|
--spec-name <name> Name for the API integration (default: derived from filename)
|
|
77
81
|
-V, --version Output the version number
|
|
78
82
|
-h, --help Display help
|
|
@@ -120,24 +124,11 @@ for architecture details, auth deep-dives, and deployment guides.
|
|
|
120
124
|
|
|
121
125
|
## Starting the Otter Raft
|
|
122
126
|
|
|
123
|
-
The otter raft runs in two modes:
|
|
124
|
-
|
|
125
|
-
### Interactive Mode (Default)
|
|
126
|
-
|
|
127
127
|
```bash
|
|
128
|
-
|
|
128
|
+
cd my-app
|
|
129
|
+
npx @stackwright-pro/raft
|
|
129
130
|
```
|
|
130
131
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
- Asks questions about your project via TUI
|
|
134
|
-
- Coordinates specialist otters (brand, theme, api, auth, page)
|
|
135
|
-
- Uses certificate pinning to ensure only approved otters are invoked
|
|
136
|
-
|
|
137
|
-
### Phases Mode (Debugging)
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
python -m stackwright_pro.raft.cli_adapter foreman --phases
|
|
141
|
-
```
|
|
132
|
+
The raft verifies otter integrity, loads project context from `.stackwright/init-context.json`, and spawns code-puppy in foreman mode. Tell the foreman about your specs in natural language — it coordinates the API, auth, data, and page otters to build your app.
|
|
142
133
|
|
|
143
|
-
|
|
134
|
+
All state lives in `.stackwright/` — you can interrupt and resume at any time.
|
package/dist/index.js
CHANGED
|
@@ -31,9 +31,9 @@ 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.121",
|
|
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
37
|
publishConfig: {
|
|
38
38
|
access: "public"
|
|
39
39
|
},
|
|
@@ -64,21 +64,26 @@ var require_package = __commonJS({
|
|
|
64
64
|
"test:coverage": "vitest run --coverage"
|
|
65
65
|
},
|
|
66
66
|
dependencies: {
|
|
67
|
-
"@stackwright-pro/
|
|
68
|
-
"@stackwright/
|
|
69
|
-
"@stackwright/
|
|
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:*",
|
|
72
|
+
"@stackwright-pro/scaffold-hooks": "workspace:*",
|
|
73
|
+
"@stackwright/cli": "^0.9.0",
|
|
74
|
+
"@stackwright/scaffold-core": "^0.3.3",
|
|
70
75
|
chalk: "^5.6.2",
|
|
71
|
-
commander: "^
|
|
72
|
-
"fs-extra": "^11.3",
|
|
73
|
-
"js-yaml": "^4.
|
|
76
|
+
commander: "^15.0.0",
|
|
77
|
+
"fs-extra": "^11.3.5",
|
|
78
|
+
"js-yaml": "^4.2.0"
|
|
74
79
|
},
|
|
75
80
|
devDependencies: {
|
|
76
81
|
"@types/fs-extra": "^11.0",
|
|
77
82
|
"@types/js-yaml": "^4.0.9",
|
|
78
|
-
"@types/node": "
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
vitest: "
|
|
83
|
+
"@types/node": "catalog:",
|
|
84
|
+
tsup: "catalog:",
|
|
85
|
+
typescript: "catalog:",
|
|
86
|
+
vitest: "catalog:"
|
|
82
87
|
}
|
|
83
88
|
};
|
|
84
89
|
}
|
|
@@ -86,84 +91,75 @@ var require_package = __commonJS({
|
|
|
86
91
|
|
|
87
92
|
// src/index.ts
|
|
88
93
|
var import_commander = require("commander");
|
|
89
|
-
var
|
|
94
|
+
var import_path2 = __toESM(require("path"));
|
|
90
95
|
var import_os = __toESM(require("os"));
|
|
91
|
-
var
|
|
96
|
+
var import_fs_extra2 = __toESM(require("fs-extra"));
|
|
92
97
|
var import_chalk = __toESM(require("chalk"));
|
|
93
|
-
var import_js_yaml = __toESM(require("js-yaml"));
|
|
94
98
|
var import_child_process = require("child_process");
|
|
95
99
|
var import_cli = require("@stackwright/cli");
|
|
96
100
|
var import_scaffold_core = require("@stackwright/scaffold-core");
|
|
97
101
|
var import_scaffold_hooks = require("@stackwright-pro/scaffold-hooks");
|
|
98
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
|
+
|
|
99
108
|
// src/generated/workspace-versions.ts
|
|
100
109
|
var PRO_VERSIONS = {
|
|
101
|
-
"@stackwright-pro/auth": "0.
|
|
102
|
-
"@stackwright-pro/auth-nextjs": "0.
|
|
103
|
-
"@stackwright-pro/
|
|
104
|
-
"@stackwright-pro/
|
|
105
|
-
"@stackwright-pro/
|
|
110
|
+
"@stackwright-pro/auth": "0.2.0-alpha.18",
|
|
111
|
+
"@stackwright-pro/auth-nextjs": "0.3.0-alpha.7",
|
|
112
|
+
"@stackwright-pro/display-components": "0.2.0-alpha.11",
|
|
113
|
+
"@stackwright-pro/mcp": "0.2.0-alpha.84",
|
|
114
|
+
"@stackwright-pro/openapi": "0.3.0-alpha.31",
|
|
115
|
+
"@stackwright-pro/otters": "1.0.0-alpha.53",
|
|
116
|
+
"@stackwright-pro/pulse": "0.3.0-alpha.26",
|
|
117
|
+
"@stackwright-pro/workflow": "0.1.0-alpha.21",
|
|
118
|
+
"@stackwright-pro/workflow-components": "0.1.0-alpha.23"
|
|
106
119
|
};
|
|
107
120
|
var OSS_VERSIONS = {
|
|
108
|
-
"@stackwright/build-scripts": "^0.
|
|
109
|
-
"@stackwright/
|
|
110
|
-
"@stackwright/
|
|
111
|
-
"@stackwright/
|
|
112
|
-
"@stackwright/
|
|
113
|
-
"@stackwright/
|
|
114
|
-
"@stackwright/
|
|
121
|
+
"@stackwright/build-scripts": "^0.9.0",
|
|
122
|
+
"@stackwright/collections": "^0.1.2",
|
|
123
|
+
"@stackwright/core": "^0.10.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.7.0",
|
|
129
|
+
"@stackwright/ui-shadcn": "^0.1.3"
|
|
115
130
|
};
|
|
116
131
|
|
|
117
|
-
// src/
|
|
118
|
-
(0, import_scaffold_hooks.registerProScaffoldHooks)();
|
|
119
|
-
var { version } = require_package();
|
|
120
|
-
async function copyProTemplate(templateName, destPath) {
|
|
121
|
-
const src = import_path.default.resolve(__dirname, "..", "templates", "pro", templateName);
|
|
122
|
-
await import_fs_extra.default.copy(src, destPath);
|
|
123
|
-
}
|
|
124
|
-
async function addAuthToStackwrightYml(targetDir) {
|
|
125
|
-
const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
|
|
126
|
-
const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
|
|
127
|
-
const config = import_js_yaml.default.load(content, { schema: import_js_yaml.default.CORE_SCHEMA });
|
|
128
|
-
config.auth = {
|
|
129
|
-
type: "pki",
|
|
130
|
-
profile: "dod_cac",
|
|
131
|
-
source: "gateway_headers",
|
|
132
|
-
roles: [
|
|
133
|
-
{ name: "ADMIN", permissions: ["*"] },
|
|
134
|
-
{ name: "ANALYST", permissions: ["data:read", "data:write"] },
|
|
135
|
-
{ name: "VIEWER", permissions: ["data:read"] }
|
|
136
|
-
],
|
|
137
|
-
protected_routes: [{ path: "/*", roles: ["VIEWER", "ANALYST", "ADMIN"] }],
|
|
138
|
-
public_routes: ["/", "/getting-started"]
|
|
139
|
-
};
|
|
140
|
-
await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
|
|
141
|
-
}
|
|
132
|
+
// src/scaffold.ts
|
|
142
133
|
async function addProDepsToPackageJson(targetDir) {
|
|
143
134
|
const pkgPath = import_path.default.join(targetDir, "package.json");
|
|
144
135
|
const content = await import_fs_extra.default.readFile(pkgPath, "utf-8");
|
|
145
136
|
const pkg = JSON.parse(content);
|
|
146
|
-
const dependencies = pkg.dependencies
|
|
147
|
-
const devDependencies = pkg.devDependencies
|
|
148
|
-
const scripts = pkg.scripts
|
|
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 } : {};
|
|
149
140
|
for (const [pkgName, pinnedVersion] of Object.entries(OSS_VERSIONS)) {
|
|
150
|
-
if (
|
|
141
|
+
if (pkgName in dependencies) {
|
|
151
142
|
dependencies[pkgName] = pinnedVersion;
|
|
152
143
|
}
|
|
153
|
-
if (
|
|
144
|
+
if (pkgName in devDependencies) {
|
|
154
145
|
devDependencies[pkgName] = pinnedVersion;
|
|
155
146
|
}
|
|
156
147
|
}
|
|
157
148
|
Object.assign(dependencies, {
|
|
149
|
+
"@stackwright-pro/display-components": PRO_VERSIONS["@stackwright-pro/display-components"],
|
|
158
150
|
"@stackwright-pro/mcp": PRO_VERSIONS["@stackwright-pro/mcp"],
|
|
159
151
|
"@stackwright-pro/otters": PRO_VERSIONS["@stackwright-pro/otters"],
|
|
160
152
|
"@stackwright-pro/openapi": PRO_VERSIONS["@stackwright-pro/openapi"],
|
|
161
153
|
"@stackwright-pro/auth": PRO_VERSIONS["@stackwright-pro/auth"],
|
|
162
154
|
"@stackwright-pro/auth-nextjs": PRO_VERSIONS["@stackwright-pro/auth-nextjs"],
|
|
163
|
-
|
|
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"
|
|
164
160
|
});
|
|
165
161
|
Object.assign(devDependencies, {
|
|
166
|
-
"@stoplight/prism-cli": "^5.
|
|
162
|
+
"@stoplight/prism-cli": "^5.15.10"
|
|
167
163
|
});
|
|
168
164
|
Object.assign(scripts, {
|
|
169
165
|
"dev:admin": "MOCK_USER=admin next dev",
|
|
@@ -177,6 +173,13 @@ async function addProDepsToPackageJson(targetDir) {
|
|
|
177
173
|
pkg.scripts = scripts;
|
|
178
174
|
await import_fs_extra.default.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
179
175
|
}
|
|
176
|
+
async function addAuthToStackwrightYml(targetDir) {
|
|
177
|
+
const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
|
|
178
|
+
const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
|
|
179
|
+
const config = import_js_yaml.default.load(content, { schema: import_js_yaml.default.CORE_SCHEMA });
|
|
180
|
+
config.auth = {};
|
|
181
|
+
await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
|
|
182
|
+
}
|
|
180
183
|
async function addIntegrationToStackwrightYml(targetDir, specName, specFilename, mockUrl) {
|
|
181
184
|
const ymlPath = import_path.default.join(targetDir, "stackwright.yml");
|
|
182
185
|
const content = await import_fs_extra.default.readFile(ymlPath, "utf-8");
|
|
@@ -190,20 +193,71 @@ async function addIntegrationToStackwrightYml(targetDir, specName, specFilename,
|
|
|
190
193
|
if (mockUrl) {
|
|
191
194
|
integration.mockUrl = mockUrl;
|
|
192
195
|
}
|
|
193
|
-
config.integrations = [
|
|
196
|
+
config.integrations = [
|
|
197
|
+
...Array.isArray(config.integrations) ? config.integrations : [],
|
|
198
|
+
integration
|
|
199
|
+
];
|
|
194
200
|
await import_fs_extra.default.writeFile(ymlPath, import_js_yaml.default.dump(config, { lineWidth: 120 }));
|
|
195
201
|
}
|
|
196
|
-
async function
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
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");
|
|
200
260
|
}
|
|
201
|
-
const specFilename = import_path.default.basename(resolvedSpec);
|
|
202
|
-
const derivedName = specName || import_path.default.basename(specFilename, import_path.default.extname(specFilename));
|
|
203
|
-
const specsDir = import_path.default.join(targetDir, "specs");
|
|
204
|
-
await import_fs_extra.default.ensureDir(specsDir);
|
|
205
|
-
await import_fs_extra.default.copy(resolvedSpec, import_path.default.join(specsDir, specFilename));
|
|
206
|
-
return { specFilename, derivedName };
|
|
207
261
|
}
|
|
208
262
|
function generateReadme(options) {
|
|
209
263
|
const { projectName, hasSpec, specName, specFilename, hasOtters, hasMock } = options;
|
|
@@ -226,7 +280,7 @@ export const EquipmentSchema = z.object({
|
|
|
226
280
|
export type Equipment = z.infer<typeof EquipmentSchema>;
|
|
227
281
|
|
|
228
282
|
// src/generated/${specName}/client.ts
|
|
229
|
-
export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
|
|
283
|
+
export class ${(specName ?? "api").charAt(0).toUpperCase() + (specName ?? "api").slice(1)}Client {
|
|
230
284
|
async getEquipment(id: string): Promise<Equipment> {
|
|
231
285
|
// Auto-wired with auth, validation, error handling
|
|
232
286
|
}
|
|
@@ -239,10 +293,10 @@ export class ${specName.charAt(0).toUpperCase() + specName.slice(1)}Client {
|
|
|
239
293
|
3. You import and use it:
|
|
240
294
|
|
|
241
295
|
\`\`\`typescript
|
|
242
|
-
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';
|
|
243
297
|
import type { Equipment } from '@/generated/${specName}/types';
|
|
244
298
|
|
|
245
|
-
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();
|
|
246
300
|
const gear: Equipment = await client.getEquipment('123');
|
|
247
301
|
\`\`\`
|
|
248
302
|
|
|
@@ -278,12 +332,10 @@ Your project includes **SIX specialized AI agents** in \`node_modules/@stackwrig
|
|
|
278
332
|
**Start the otter raft:**
|
|
279
333
|
|
|
280
334
|
\`\`\`bash
|
|
281
|
-
|
|
335
|
+
npx @stackwright-pro/raft
|
|
282
336
|
\`\`\`
|
|
283
337
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
The raft is a Python state machine with a Rich terminal UI \u2014 it asks questions about your brand, auth, and data preferences, then coordinates specialist otters to build your app. All user interaction happens in the terminal; code-puppy is called as a one-shot subprocess for creative tasks only.
|
|
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.
|
|
287
339
|
|
|
288
340
|
**Example session prompt:**
|
|
289
341
|
|
|
@@ -326,12 +378,16 @@ ${mockSection}## \u{1F4C1} Project Structure
|
|
|
326
378
|
|
|
327
379
|
\`\`\`
|
|
328
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
|
|
329
389
|
\u251C\u2500\u2500 pages/
|
|
330
|
-
\u2502 \
|
|
331
|
-
\u2502 \u251C\u2500\u2500 index.tsx # Home page (auto-generated by Stackwright)
|
|
332
|
-
\u2502 \u251C\u2500\u2500 about/
|
|
333
|
-
\u2502 \u2502 \u2514\u2500\u2500 content.yml # Page content in YAML
|
|
334
|
-
\u2502 \u2514\u2500\u2500 ...
|
|
390
|
+
\u2502 \u2514\u2500\u2500 content.yml # Root page content in YAML
|
|
335
391
|
\u251C\u2500\u2500 lib/
|
|
336
392
|
\u2502 \u2514\u2500\u2500 mock-auth.ts # Dev-only auth mocking (no backend needed)
|
|
337
393
|
\u251C\u2500\u2500 scripts/
|
|
@@ -351,7 +407,7 @@ ${mockSection}## \u{1F4C1} Project Structure
|
|
|
351
407
|
\`\`\`
|
|
352
408
|
|
|
353
409
|
**Key Files:**
|
|
354
|
-
- \`
|
|
410
|
+
- \`app/_components/providers.tsx\` - Wraps your app with \`AuthProvider\` for RBAC
|
|
355
411
|
- \`stackwright.yml\` - Single source of truth for theme, auth roles, and API wiring
|
|
356
412
|
- \`lib/mock-auth.ts\` - Mocks CAC/PIV headers locally so you can test auth flows
|
|
357
413
|
- \`scripts/prebuild.js\` - Runs before dev/build to generate types from your OpenAPI spec
|
|
@@ -461,8 +517,28 @@ ${aiAgentsSection}## \u{1F4DA} Learn More
|
|
|
461
517
|
**Questions?** File an issue or ping us in the discussions. We're here to help you ship faster.
|
|
462
518
|
`;
|
|
463
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
|
+
}
|
|
464
540
|
async function launch(targetDir, options) {
|
|
465
|
-
const dirName =
|
|
541
|
+
const dirName = import_path2.default.basename(targetDir);
|
|
466
542
|
console.log(import_chalk.default.cyan.bold("\n\u{1F6A2} Launching Stackwright Pro...\n"));
|
|
467
543
|
const scaffoldOpts = {
|
|
468
544
|
name: options.name || dirName,
|
|
@@ -478,11 +554,11 @@ async function launch(targetDir, options) {
|
|
|
478
554
|
await addProDepsToPackageJson(targetDir);
|
|
479
555
|
console.log(import_chalk.default.green("\u{1F9A6} Pro packages added to package.json"));
|
|
480
556
|
const verifyPkg = JSON.parse(
|
|
481
|
-
await
|
|
557
|
+
await import_fs_extra2.default.readFile(import_path2.default.join(targetDir, "package.json"), "utf-8")
|
|
482
558
|
);
|
|
483
|
-
const proDepCount = Object.keys(
|
|
484
|
-
|
|
485
|
-
).length;
|
|
559
|
+
const proDepCount = Object.keys(
|
|
560
|
+
typeof verifyPkg.dependencies === "object" && verifyPkg.dependencies !== null ? verifyPkg.dependencies : {}
|
|
561
|
+
).filter((k) => k.startsWith("@stackwright-pro")).length;
|
|
486
562
|
console.log(import_chalk.default.dim(` \u2192 package.json verified: ${proDepCount} @stackwright-pro/* deps ready`));
|
|
487
563
|
console.log(import_chalk.default.cyan("\n\u{1F4E6} Installing dependencies (this takes a moment)..."));
|
|
488
564
|
if (!options.skipInstall) {
|
|
@@ -517,33 +593,42 @@ async function launch(targetDir, options) {
|
|
|
517
593
|
const dependencyMode = "standalone";
|
|
518
594
|
await (0, import_scaffold_core.runScaffoldHooks)("postInstall", {
|
|
519
595
|
targetDir,
|
|
520
|
-
projectName: options.name ||
|
|
521
|
-
siteTitle: options.title || options.name ||
|
|
596
|
+
projectName: options.name || import_path2.default.basename(targetDir),
|
|
597
|
+
siteTitle: options.title || options.name || import_path2.default.basename(targetDir),
|
|
522
598
|
themeId: options.theme || "corporate",
|
|
523
599
|
packageJson: {},
|
|
524
600
|
dependencyMode
|
|
525
601
|
});
|
|
526
|
-
await copyProTemplate(
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
await copyProTemplate("
|
|
531
|
-
await copyProTemplate("
|
|
532
|
-
await
|
|
533
|
-
await copyProTemplate("
|
|
534
|
-
|
|
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)"));
|
|
535
618
|
await addAuthToStackwrightYml(targetDir);
|
|
536
619
|
let specInfo = null;
|
|
537
620
|
const MOCK_URL = "http://localhost:4010";
|
|
538
|
-
if (options.spec) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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`));
|
|
547
632
|
if (options.mock) {
|
|
548
633
|
console.log(import_chalk.default.green("\u{1F3AD} Prism mock server configured"));
|
|
549
634
|
}
|
|
@@ -553,9 +638,9 @@ async function launch(targetDir, options) {
|
|
|
553
638
|
"\u26A0\uFE0F No spec provided with --mock. Using sample Petstore spec for demo. Replace with your API spec."
|
|
554
639
|
)
|
|
555
640
|
);
|
|
556
|
-
const specsDir =
|
|
557
|
-
await
|
|
558
|
-
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"));
|
|
559
644
|
specInfo = { specFilename: "petstore.yaml", derivedName: "petstore" };
|
|
560
645
|
await addIntegrationToStackwrightYml(
|
|
561
646
|
targetDir,
|
|
@@ -574,13 +659,33 @@ async function launch(targetDir, options) {
|
|
|
574
659
|
hasOtters: !options.skipOtters,
|
|
575
660
|
hasMock: !!options.mock
|
|
576
661
|
});
|
|
577
|
-
await
|
|
662
|
+
await import_fs_extra2.default.writeFile(import_path2.default.join(targetDir, "README.md"), readmeContent);
|
|
578
663
|
console.log(import_chalk.default.green("\u{1F4C4} README.md created"));
|
|
579
|
-
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) || ".";
|
|
580
685
|
console.log(import_chalk.default.cyan.bold("\n\u{1F389} All set! Here's what to do next:\n"));
|
|
581
686
|
console.log(import_chalk.default.white(` 1. cd ${relDir}`));
|
|
582
|
-
console.log(import_chalk.default.white(" 2.
|
|
583
|
-
console.log(import_chalk.default.dim(" (
|
|
687
|
+
console.log(import_chalk.default.white(" 2. npx @stackwright-pro/raft"));
|
|
688
|
+
console.log(import_chalk.default.dim(" (use --verbose for troubleshooting)"));
|
|
584
689
|
if (specInfo) {
|
|
585
690
|
console.log(import_chalk.default.cyan(`
|
|
586
691
|
\u{1F4E1} Your API spec was copied to specs/${specInfo.specFilename}`));
|
|
@@ -598,8 +703,8 @@ async function launch(targetDir, options) {
|
|
|
598
703
|
async function main() {
|
|
599
704
|
const program = new import_commander.Command();
|
|
600
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(
|
|
601
|
-
"--spec <
|
|
602
|
-
"
|
|
706
|
+
"--spec <paths...>",
|
|
707
|
+
"Paths to OpenAPI specs (YAML or JSON) \u2014 copies into project and wires up integrations (can be specified multiple times)"
|
|
603
708
|
).option(
|
|
604
709
|
"--spec-name <name>",
|
|
605
710
|
"Name for the API integration (default: derived from spec filename)"
|
|
@@ -608,8 +713,8 @@ async function main() {
|
|
|
608
713
|
"Skip automatic pnpm install (useful in CI or when using a different package manager)"
|
|
609
714
|
).action(async (directory, options) => {
|
|
610
715
|
const homeDir = import_os.default.homedir();
|
|
611
|
-
const targetDir =
|
|
612
|
-
if (!targetDir.startsWith(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) {
|
|
613
718
|
console.warn(import_chalk.default.yellow(`\u26A0\uFE0F Scaffolding outside your home directory: ${targetDir}`));
|
|
614
719
|
}
|
|
615
720
|
await launch(targetDir, options);
|