saastore-port 0.1.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/LICENSE +21 -0
- package/README.md +86 -0
- package/dist/analyze_repo.js +203 -0
- package/dist/analyze_repo.js.map +1 -0
- package/dist/apply_auth_rewire.js +64 -0
- package/dist/apply_auth_rewire.js.map +1 -0
- package/dist/boot_test_local.js +202 -0
- package/dist/boot_test_local.js.map +1 -0
- package/dist/generate_manifest.js +109 -0
- package/dist/generate_manifest.js.map +1 -0
- package/dist/package_and_push.js +151 -0
- package/dist/package_and_push.js.map +1 -0
- package/dist/propose_auth_rewire.js +113 -0
- package/dist/propose_auth_rewire.js.map +1 -0
- package/dist/server.js +276 -0
- package/dist/server.js.map +1 -0
- package/dist/validate_compliance.js +203 -0
- package/dist/validate_compliance.js.map +1 -0
- package/package.json +45 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* saastore-port — MCP server the seller runs locally in their repo.
|
|
4
|
+
*
|
|
5
|
+
* The seller's IDE-side agent (Claude Code, Cursor, Cline, Zed, ...) connects
|
|
6
|
+
* via stdio and calls our exposed tools. Token economics: the seller's agent
|
|
7
|
+
* uses the seller's own API key — saastore never sees it (see spec section 7.1).
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* npm install -g saastore-port # eventually; for PoC, npm install + npx
|
|
11
|
+
* cd <your-repo>
|
|
12
|
+
* <IDE config> -> add MCP server: command="saastore-port", cwd="<your-repo>"
|
|
13
|
+
*
|
|
14
|
+
* For the PoC this server registers two tools (analyze_repo + generate_manifest).
|
|
15
|
+
* The remaining six tools (propose_auth_rewire, apply_auth_rewire,
|
|
16
|
+
* validate_compliance, boot_test_local, package_image, push_image) land in
|
|
17
|
+
* follow-up commits as we walk the concierge first-seller pipeline.
|
|
18
|
+
*/
|
|
19
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
20
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
21
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
22
|
+
import { analyzeRepo } from "./analyze_repo.js";
|
|
23
|
+
import { applyAuthRewire } from "./apply_auth_rewire.js";
|
|
24
|
+
import { bootTestLocal } from "./boot_test_local.js";
|
|
25
|
+
import { generateManifest, } from "./generate_manifest.js";
|
|
26
|
+
import { packageImage, pushImage } from "./package_and_push.js";
|
|
27
|
+
import { proposeAuthRewire } from "./propose_auth_rewire.js";
|
|
28
|
+
import { validateCompliance } from "./validate_compliance.js";
|
|
29
|
+
const server = new Server({ name: "saastore-port", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
30
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
31
|
+
tools: [
|
|
32
|
+
{
|
|
33
|
+
name: "analyze_repo",
|
|
34
|
+
description: "Scan the seller's repository and detect framework, auth library, database, payment processors, and external APIs. Pass `path` (defaults to process.cwd()).",
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: {
|
|
38
|
+
path: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "Repo root to scan. Defaults to the server's CWD.",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "apply_auth_rewire",
|
|
47
|
+
description: "Apply the create-intent files from a RewireProposal (output of propose_auth_rewire). Modify/delete intents are returned as `next_steps` for the agent to handle. Idempotent: refuses to overwrite an existing file unless `force=true`.",
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: "object",
|
|
50
|
+
required: ["proposal"],
|
|
51
|
+
properties: {
|
|
52
|
+
proposal: {
|
|
53
|
+
type: "object",
|
|
54
|
+
description: "Output from propose_auth_rewire.",
|
|
55
|
+
},
|
|
56
|
+
root: {
|
|
57
|
+
type: "string",
|
|
58
|
+
description: "Repo root to write into. Defaults to CWD.",
|
|
59
|
+
},
|
|
60
|
+
force: { type: "boolean", description: "Overwrite existing files. Default false." },
|
|
61
|
+
dry_run: { type: "boolean", description: "Report what would happen without writing. Default false." },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "boot_test_local",
|
|
67
|
+
description: "Build the seller's Docker image, run it locally with a known SAASTORE_HMAC_SECRET, and probe /health + signed /whoami + bad-signature /whoami + stale-timestamp /whoami. Returns a per-step pass/fail result. Tears down the container at the end.",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
context: { type: "string", description: "Build context path. Defaults CWD." },
|
|
72
|
+
dockerfile: { type: "string", description: "Dockerfile relative to context. Default 'Dockerfile'." },
|
|
73
|
+
port: { type: "number", description: "Host port to bind on. Default 8765." },
|
|
74
|
+
container_port: { type: "number", description: "Container's listening port. Default 8000." },
|
|
75
|
+
secret: { type: "string", description: "Test HMAC secret." },
|
|
76
|
+
startup_timeout_s: { type: "number", description: "Seconds to wait for /health. Default 30." },
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "package_image",
|
|
82
|
+
description: "Build the seller's Docker image and stamp the manifest into an OCI label (org.saastore.manifest). Returns the tag + digest.",
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: "object",
|
|
85
|
+
required: ["tag", "manifest"],
|
|
86
|
+
properties: {
|
|
87
|
+
context: { type: "string", description: "Build context. Defaults CWD." },
|
|
88
|
+
dockerfile: { type: "string", description: "Dockerfile relative to context. Default 'Dockerfile'." },
|
|
89
|
+
tag: { type: "string", description: "Local image tag, e.g. 'saastore-sparkiz:v1'." },
|
|
90
|
+
manifest: {
|
|
91
|
+
type: "object",
|
|
92
|
+
description: "Manifest object to stamp into the LABEL.",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "push_image",
|
|
99
|
+
description: "Tag + push a previously packaged image to the saastore registry. Takes a saastore-issued OAuth token (NOT the seller's own docker login).",
|
|
100
|
+
inputSchema: {
|
|
101
|
+
type: "object",
|
|
102
|
+
required: ["tag", "remote", "token"],
|
|
103
|
+
properties: {
|
|
104
|
+
tag: { type: "string", description: "Local tag (must exist from package_image)." },
|
|
105
|
+
remote: { type: "string", description: "Remote reference, e.g. 'registry.fly.io/om-sparkiz:v1'." },
|
|
106
|
+
token: { type: "string", description: "Saastore-issued registry token. Use a secret-typed env var; never paste a token literal into chat." },
|
|
107
|
+
username: { type: "string", description: "Registry username. Default 'x' (Fly convention)." },
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "propose_auth_rewire",
|
|
113
|
+
description: "Return a structured list of changes (create / modify / delete) to wire the saastore SDK adapter into the seller's app and remove the now-dead auth UI. Per-framework kits; FastAPI ships in PoC, others land via concierge. Takes the output of analyze_repo.",
|
|
114
|
+
inputSchema: {
|
|
115
|
+
type: "object",
|
|
116
|
+
required: ["analysis"],
|
|
117
|
+
properties: {
|
|
118
|
+
analysis: {
|
|
119
|
+
type: "object",
|
|
120
|
+
description: "Output from analyze_repo.",
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "validate_compliance",
|
|
127
|
+
description: "Locally check the seller's working tree for violations the saastore-side pipeline will reject (auth routes still present, stripe SDK imports, missing manifest). Returns a structured findings list; `ok` is true when there are no `error`-severity findings.",
|
|
128
|
+
inputSchema: {
|
|
129
|
+
type: "object",
|
|
130
|
+
properties: {
|
|
131
|
+
path: {
|
|
132
|
+
type: "string",
|
|
133
|
+
description: "Repo root to scan. Defaults to the server's CWD.",
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "generate_manifest",
|
|
140
|
+
description: "Generate a saastore.yaml the seller commits in their repo. Takes the output of analyze_repo (`analysis`) plus seller-supplied fields (app_name, port, memory_mb, etc.).",
|
|
141
|
+
inputSchema: {
|
|
142
|
+
type: "object",
|
|
143
|
+
required: ["analysis", "input"],
|
|
144
|
+
properties: {
|
|
145
|
+
analysis: {
|
|
146
|
+
type: "object",
|
|
147
|
+
description: "Output from analyze_repo.",
|
|
148
|
+
},
|
|
149
|
+
input: {
|
|
150
|
+
type: "object",
|
|
151
|
+
required: ["app_name"],
|
|
152
|
+
description: "Seller-supplied manifest fields.",
|
|
153
|
+
properties: {
|
|
154
|
+
app_name: { type: "string" },
|
|
155
|
+
port: { type: "number" },
|
|
156
|
+
framework_override: { type: "string" },
|
|
157
|
+
adapter_override: { type: "string" },
|
|
158
|
+
removed_routes: { type: "array", items: { type: "string" } },
|
|
159
|
+
memory_mb: { type: "number" },
|
|
160
|
+
external_api_caps_usd: { type: "object" },
|
|
161
|
+
schema_owner: {
|
|
162
|
+
type: "string",
|
|
163
|
+
enum: ["app", "platform"],
|
|
164
|
+
},
|
|
165
|
+
migrate_command: { type: "string" },
|
|
166
|
+
s3_bucket: { type: "boolean" },
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
}));
|
|
174
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
175
|
+
const { name, arguments: args = {} } = request.params;
|
|
176
|
+
if (name === "analyze_repo") {
|
|
177
|
+
const path = args["path"] ?? process.cwd();
|
|
178
|
+
const analysis = await analyzeRepo(path);
|
|
179
|
+
return {
|
|
180
|
+
content: [
|
|
181
|
+
{ type: "text", text: JSON.stringify(analysis, null, 2) },
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (name === "apply_auth_rewire") {
|
|
186
|
+
const proposal = args["proposal"];
|
|
187
|
+
if (!proposal) {
|
|
188
|
+
throw new Error("apply_auth_rewire: 'proposal' is required");
|
|
189
|
+
}
|
|
190
|
+
const result = await applyAuthRewire(proposal, {
|
|
191
|
+
root: args["root"],
|
|
192
|
+
force: args["force"],
|
|
193
|
+
dry_run: args["dry_run"],
|
|
194
|
+
});
|
|
195
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
196
|
+
}
|
|
197
|
+
if (name === "boot_test_local") {
|
|
198
|
+
const result = await bootTestLocal({
|
|
199
|
+
context: args["context"],
|
|
200
|
+
dockerfile: args["dockerfile"],
|
|
201
|
+
port: args["port"],
|
|
202
|
+
container_port: args["container_port"],
|
|
203
|
+
secret: args["secret"],
|
|
204
|
+
startup_timeout_s: args["startup_timeout_s"],
|
|
205
|
+
});
|
|
206
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
207
|
+
}
|
|
208
|
+
if (name === "package_image") {
|
|
209
|
+
const tag = args["tag"];
|
|
210
|
+
const manifest = args["manifest"];
|
|
211
|
+
if (!tag || !manifest) {
|
|
212
|
+
throw new Error("package_image: 'tag' and 'manifest' are required");
|
|
213
|
+
}
|
|
214
|
+
const result = await packageImage({
|
|
215
|
+
tag,
|
|
216
|
+
manifest,
|
|
217
|
+
context: args["context"],
|
|
218
|
+
dockerfile: args["dockerfile"],
|
|
219
|
+
});
|
|
220
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
221
|
+
}
|
|
222
|
+
if (name === "push_image") {
|
|
223
|
+
const tag = args["tag"];
|
|
224
|
+
const remote = args["remote"];
|
|
225
|
+
const token = args["token"];
|
|
226
|
+
if (!tag || !remote || !token) {
|
|
227
|
+
throw new Error("push_image: 'tag', 'remote', 'token' are required");
|
|
228
|
+
}
|
|
229
|
+
const result = await pushImage({
|
|
230
|
+
tag,
|
|
231
|
+
remote,
|
|
232
|
+
token,
|
|
233
|
+
username: args["username"],
|
|
234
|
+
});
|
|
235
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
236
|
+
}
|
|
237
|
+
if (name === "propose_auth_rewire") {
|
|
238
|
+
const analysis = args["analysis"];
|
|
239
|
+
if (!analysis) {
|
|
240
|
+
throw new Error("propose_auth_rewire: 'analysis' is required");
|
|
241
|
+
}
|
|
242
|
+
const proposal = proposeAuthRewire(analysis);
|
|
243
|
+
return {
|
|
244
|
+
content: [
|
|
245
|
+
{ type: "text", text: JSON.stringify(proposal, null, 2) },
|
|
246
|
+
],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (name === "validate_compliance") {
|
|
250
|
+
const path = args["path"] ?? process.cwd();
|
|
251
|
+
const report = await validateCompliance(path);
|
|
252
|
+
return {
|
|
253
|
+
content: [
|
|
254
|
+
{ type: "text", text: JSON.stringify(report, null, 2) },
|
|
255
|
+
],
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (name === "generate_manifest") {
|
|
259
|
+
const analysis = args["analysis"];
|
|
260
|
+
const input = args["input"];
|
|
261
|
+
if (!analysis || !input) {
|
|
262
|
+
throw new Error("generate_manifest: 'analysis' and 'input' are required");
|
|
263
|
+
}
|
|
264
|
+
const { yaml, manifest } = generateManifest(analysis, input);
|
|
265
|
+
return {
|
|
266
|
+
content: [
|
|
267
|
+
{ type: "text", text: yaml },
|
|
268
|
+
{ type: "text", text: JSON.stringify({ structured: manifest }, null, 2) },
|
|
269
|
+
],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
throw new Error(`unknown tool: ${name}`);
|
|
273
|
+
});
|
|
274
|
+
const transport = new StdioServerTransport();
|
|
275
|
+
await server.connect(transport);
|
|
276
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* validate_compliance — local checks ensuring the seller's working tree
|
|
3
|
+
* won't be rejected by the saastore-side verification pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Catches the violations cheaply (locally, no LLM, no network) so the seller
|
|
6
|
+
* doesn't waste a push + Trivy + manifest-validate roundtrip to find out
|
|
7
|
+
* they still have a /login route handler. Mirrors the spec section 6.1 contract:
|
|
8
|
+
*
|
|
9
|
+
* - No auth UI: /login, /signup, /logout, /forgot-password
|
|
10
|
+
* - No own payment processing (Stripe etc.)
|
|
11
|
+
* - App reads identity from saastore headers, not its own session/cookies
|
|
12
|
+
*
|
|
13
|
+
* Returns structured findings the seller's agent presents back to them.
|
|
14
|
+
* Severity tiers:
|
|
15
|
+
* - "error" -> the verification pipeline WILL reject
|
|
16
|
+
* - "warn" -> pipeline might pass but PoC-flagged
|
|
17
|
+
* - "info" -> noteworthy detail (no action required)
|
|
18
|
+
*/
|
|
19
|
+
import { readFile } from "node:fs/promises";
|
|
20
|
+
import { existsSync } from "node:fs";
|
|
21
|
+
import { join, relative } from "node:path";
|
|
22
|
+
import { readdir } from "node:fs/promises";
|
|
23
|
+
// ----- Detection rules -----------------------------------------------------
|
|
24
|
+
/** Source-file extensions we scan. Skip lockfiles, build artifacts, etc. */
|
|
25
|
+
const SCANNED_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".rb", ".go", ".rs"];
|
|
26
|
+
const SKIP_DIRS = new Set([
|
|
27
|
+
"node_modules",
|
|
28
|
+
".git",
|
|
29
|
+
"dist",
|
|
30
|
+
"build",
|
|
31
|
+
".next",
|
|
32
|
+
"out",
|
|
33
|
+
"__pycache__",
|
|
34
|
+
".venv",
|
|
35
|
+
"venv",
|
|
36
|
+
"target",
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Patterns that signal an auth route handler. We look at the literal route
|
|
40
|
+
* string AND a handful of common decorator/method-call shapes. The aim is
|
|
41
|
+
* "noisy enough to catch routes" not "perfect AST analysis."
|
|
42
|
+
*/
|
|
43
|
+
const AUTH_ROUTE_PATTERNS = [
|
|
44
|
+
{
|
|
45
|
+
rule: "login-route",
|
|
46
|
+
pattern: /["']\/(?:api\/)?(?:auth\/)?(login|sign-?in|signin)\b/i,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
rule: "signup-route",
|
|
50
|
+
pattern: /["']\/(?:api\/)?(?:auth\/)?(signup|sign-?up|register)\b/i,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
rule: "logout-route",
|
|
54
|
+
pattern: /["']\/(?:api\/)?(?:auth\/)?(logout|sign-?out|signout)\b/i,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
rule: "forgot-password-route",
|
|
58
|
+
pattern: /["']\/(?:api\/)?(?:auth\/)?(forgot|reset)-password\b/i,
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
const PAYMENT_IMPORT_PATTERNS = [
|
|
62
|
+
{
|
|
63
|
+
rule: "stripe-import",
|
|
64
|
+
pattern: /(?:require\s*\(\s*["']stripe["']\s*\)|from\s+stripe\s+import|import\s+stripe\b|from\s+["']stripe["'])/i,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
rule: "paddle-import",
|
|
68
|
+
pattern: /["']@?paddle\/[a-z-]+["']|from\s+paddle/i,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
rule: "lemonsqueezy-import",
|
|
72
|
+
pattern: /["']@?lemonsqueezy\/[a-z-]+["']/i,
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
// ----- Helpers -------------------------------------------------------------
|
|
76
|
+
async function* walk(root) {
|
|
77
|
+
let entries;
|
|
78
|
+
try {
|
|
79
|
+
entries = (await readdir(root, { withFileTypes: true }));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
if (entry.isDirectory()) {
|
|
86
|
+
if (SKIP_DIRS.has(entry.name))
|
|
87
|
+
continue;
|
|
88
|
+
yield* walk(join(root, entry.name));
|
|
89
|
+
}
|
|
90
|
+
else if (entry.isFile()) {
|
|
91
|
+
const path = join(root, entry.name);
|
|
92
|
+
if (SCANNED_EXTENSIONS.some((ext) => path.endsWith(ext))) {
|
|
93
|
+
yield path;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function findMatchInFile(contents, patterns) {
|
|
99
|
+
const hits = [];
|
|
100
|
+
const lines = contents.split(/\r?\n/);
|
|
101
|
+
for (let i = 0; i < lines.length; i++) {
|
|
102
|
+
const text = lines[i];
|
|
103
|
+
for (const { rule, pattern } of patterns) {
|
|
104
|
+
if (pattern.test(text)) {
|
|
105
|
+
hits.push({ rule, line: i + 1, lineText: text.trim() });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return hits;
|
|
110
|
+
}
|
|
111
|
+
function relativeForward(root, path) {
|
|
112
|
+
return relative(root, path).replace(/\\/g, "/");
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Next.js conventional routes: `app/.../page.tsx` and `app/.../route.ts` (App
|
|
116
|
+
* Router), and `pages/api/...` (Pages Router). File paths themselves declare
|
|
117
|
+
* routes, so a path containing `login`, `signup`, `logout`, or `forgot-password`
|
|
118
|
+
* is an auth route even if the file contents look innocuous.
|
|
119
|
+
*/
|
|
120
|
+
const NEXT_AUTH_PATH_RULES = [
|
|
121
|
+
{ rule: "login-route", pattern: /(?:^|\/)(?:app|pages)\/(?:api\/)?(?:auth\/)?(login|sign-?in|signin)(?:[\/.]|$)/i },
|
|
122
|
+
{ rule: "signup-route", pattern: /(?:^|\/)(?:app|pages)\/(?:api\/)?(?:auth\/)?(signup|sign-?up|register)(?:[\/.]|$)/i },
|
|
123
|
+
{ rule: "logout-route", pattern: /(?:^|\/)(?:app|pages)\/(?:api\/)?(?:auth\/)?(logout|sign-?out|signout)(?:[\/.]|$)/i },
|
|
124
|
+
{ rule: "forgot-password-route", pattern: /(?:^|\/)(?:app|pages)\/(?:api\/)?(?:auth\/)?(forgot|reset)-password(?:[\/.]|$)/i },
|
|
125
|
+
];
|
|
126
|
+
// ----- Main entry ----------------------------------------------------------
|
|
127
|
+
export async function validateCompliance(root) {
|
|
128
|
+
const findings = [];
|
|
129
|
+
// --- Walk source files, applying per-line patterns ----------------------
|
|
130
|
+
for await (const file of walk(root)) {
|
|
131
|
+
let contents;
|
|
132
|
+
try {
|
|
133
|
+
contents = await readFile(file, "utf8");
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
// Path-based check first — Next.js's app router declares routes via file
|
|
139
|
+
// paths, so the file's *location* is itself the route declaration.
|
|
140
|
+
const relPath = relativeForward(root, file);
|
|
141
|
+
for (const { rule, pattern } of NEXT_AUTH_PATH_RULES) {
|
|
142
|
+
if (pattern.test(relPath)) {
|
|
143
|
+
findings.push({
|
|
144
|
+
severity: "error",
|
|
145
|
+
rule,
|
|
146
|
+
message: `Next.js auth route detected via file path — saastore handles authentication, this file must be removed before packaging.`,
|
|
147
|
+
file: relPath,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const authHits = findMatchInFile(contents, AUTH_ROUTE_PATTERNS);
|
|
152
|
+
for (const hit of authHits) {
|
|
153
|
+
findings.push({
|
|
154
|
+
severity: "error",
|
|
155
|
+
rule: hit.rule,
|
|
156
|
+
message: `auth route handler detected — saastore handles authentication, this route must be removed before packaging. Matched line: ${hit.lineText}`,
|
|
157
|
+
file: relativeForward(root, file),
|
|
158
|
+
line: hit.line,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const payHits = findMatchInFile(contents, PAYMENT_IMPORT_PATTERNS);
|
|
162
|
+
for (const hit of payHits) {
|
|
163
|
+
findings.push({
|
|
164
|
+
severity: "error",
|
|
165
|
+
rule: hit.rule,
|
|
166
|
+
message: `payment-processor import detected — saastore is merchant-of-record (spec section 6.1). Remove the SDK + any code that calls into it. Matched line: ${hit.lineText}`,
|
|
167
|
+
file: relativeForward(root, file),
|
|
168
|
+
line: hit.line,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// --- saastore.yaml presence ---------------------------------------------
|
|
173
|
+
if (!existsSync(join(root, "saastore.yaml"))) {
|
|
174
|
+
findings.push({
|
|
175
|
+
severity: "warn",
|
|
176
|
+
rule: "missing-manifest",
|
|
177
|
+
message: "no saastore.yaml at repo root — run the `generate_manifest` tool before packaging.",
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// --- SDK adapter present? ----------------------------------------------
|
|
181
|
+
// Heuristic: look for `saastore_user` / `saastore-fastapi` in package
|
|
182
|
+
// manifests so we at least nudge sellers who haven't wired the adapter.
|
|
183
|
+
const pkgJson = existsSync(join(root, "package.json"))
|
|
184
|
+
? await readFile(join(root, "package.json"), "utf8")
|
|
185
|
+
: "";
|
|
186
|
+
const pyProject = existsSync(join(root, "pyproject.toml"))
|
|
187
|
+
? await readFile(join(root, "pyproject.toml"), "utf8")
|
|
188
|
+
: "";
|
|
189
|
+
const reqTxt = existsSync(join(root, "requirements.txt"))
|
|
190
|
+
? await readFile(join(root, "requirements.txt"), "utf8")
|
|
191
|
+
: "";
|
|
192
|
+
const allManifests = pkgJson + "\n" + pyProject + "\n" + reqTxt;
|
|
193
|
+
if (allManifests.trim() && !/saastore-(fastapi|nextauth|passport|django|express|generic)/i.test(allManifests)) {
|
|
194
|
+
findings.push({
|
|
195
|
+
severity: "warn",
|
|
196
|
+
rule: "missing-saastore-adapter",
|
|
197
|
+
message: "no saastore SDK adapter dependency declared — install one (e.g. `pip install saastore-fastapi`) and wire it as the auth dependency before packaging.",
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const hasErrors = findings.some((f) => f.severity === "error");
|
|
201
|
+
return { ok: !hasErrors, findings };
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=validate_compliance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate_compliance.js","sourceRoot":"","sources":["../src/validate_compliance.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "saastore-port",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "MCP server that helps sellers port their app to saastore hosting (HMAC identity, manifest, packaging).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"saastore-port": "dist/server.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public",
|
|
16
|
+
"provenance": true
|
|
17
|
+
},
|
|
18
|
+
"keywords": ["mcp", "saastore", "model-context-protocol", "claude", "saas-hosting"],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://saastore.ai",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"start": "node dist/server.js",
|
|
26
|
+
"dev": "tsc --watch",
|
|
27
|
+
"test": "node --test --test-reporter spec --import tsx src/analyze_repo.test.ts src/generate_manifest.test.ts src/validate_compliance.test.ts src/propose_auth_rewire.test.ts src/apply_auth_rewire.test.ts",
|
|
28
|
+
"prepublishOnly": "npm run build && npm test"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
32
|
+
"yaml": "^2.5.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^22.7.0",
|
|
36
|
+
"tsx": "^4.19.0",
|
|
37
|
+
"typescript": "^5.6.0"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"author": "saastore.ai",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/michael-fishman/openmarket"
|
|
44
|
+
}
|
|
45
|
+
}
|