usetraceforge-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +53 -0
- package/dist/installers/nextjs.js +60 -0
- package/package.json +28 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { intro, outro, text, select, spinner } from "@clack/prompts";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { execa } from "execa";
|
|
5
|
+
import { installNextJs } from "./installers/nextjs.js";
|
|
6
|
+
async function main() {
|
|
7
|
+
console.log();
|
|
8
|
+
intro(chalk.bgBlue.white.bold(" TraceForge Wizard "));
|
|
9
|
+
const apiKey = await text({
|
|
10
|
+
message: "What is your TraceForge API Key?",
|
|
11
|
+
placeholder: "tf_...",
|
|
12
|
+
validate(value) {
|
|
13
|
+
if (value.length === 0)
|
|
14
|
+
return "API Key is required";
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
if (typeof apiKey !== "string") {
|
|
18
|
+
outro(chalk.red("Installation cancelled."));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const framework = await select({
|
|
22
|
+
message: "Which framework are you using?",
|
|
23
|
+
options: [
|
|
24
|
+
{ value: "nextjs", label: "Next.js (React)" },
|
|
25
|
+
{ value: "express", label: "Express (Node.js)" },
|
|
26
|
+
],
|
|
27
|
+
});
|
|
28
|
+
if (typeof framework !== "string") {
|
|
29
|
+
outro(chalk.red("Installation cancelled."));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
const s = spinner();
|
|
33
|
+
s.start("Installing usetraceforge SDK...");
|
|
34
|
+
try {
|
|
35
|
+
await execa("npm", ["install", "usetraceforge@latest"]);
|
|
36
|
+
s.stop(chalk.green("SDK installed successfully."));
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
s.stop(chalk.red("Failed to install SDK. Please run 'npm install usetraceforge' manually."));
|
|
40
|
+
}
|
|
41
|
+
if (framework === "nextjs") {
|
|
42
|
+
await installNextJs(apiKey);
|
|
43
|
+
}
|
|
44
|
+
else if (framework === "express") {
|
|
45
|
+
// await installExpress(apiKey);
|
|
46
|
+
console.log(chalk.yellow("Express auto-installation coming soon."));
|
|
47
|
+
}
|
|
48
|
+
outro(chalk.green("✨ You're all set! TraceForge is now protecting your application."));
|
|
49
|
+
}
|
|
50
|
+
main().catch((err) => {
|
|
51
|
+
console.error(err);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { spinner } from "@clack/prompts";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
export async function installNextJs(apiKey) {
|
|
6
|
+
const s = spinner();
|
|
7
|
+
s.start("Configuring Next.js...");
|
|
8
|
+
try {
|
|
9
|
+
// 1. Add API key to .env.local
|
|
10
|
+
const envPath = path.resolve(process.cwd(), ".env.local");
|
|
11
|
+
const envVar = `\nNEXT_PUBLIC_TRACEFORGE_API_KEY="${apiKey}"\n`;
|
|
12
|
+
if (fs.existsSync(envPath)) {
|
|
13
|
+
const content = fs.readFileSync(envPath, "utf-8");
|
|
14
|
+
if (!content.includes("NEXT_PUBLIC_TRACEFORGE_API_KEY")) {
|
|
15
|
+
fs.appendFileSync(envPath, envVar);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
fs.writeFileSync(envPath, envVar);
|
|
20
|
+
}
|
|
21
|
+
// 2. Find layout.tsx
|
|
22
|
+
const layoutPaths = [
|
|
23
|
+
path.resolve(process.cwd(), "app/layout.tsx"),
|
|
24
|
+
path.resolve(process.cwd(), "src/app/layout.tsx"),
|
|
25
|
+
path.resolve(process.cwd(), "app/layout.jsx"),
|
|
26
|
+
path.resolve(process.cwd(), "src/app/layout.jsx"),
|
|
27
|
+
];
|
|
28
|
+
let targetLayout = null;
|
|
29
|
+
for (const p of layoutPaths) {
|
|
30
|
+
if (fs.existsSync(p)) {
|
|
31
|
+
targetLayout = p;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (!targetLayout) {
|
|
36
|
+
s.stop(chalk.yellow("Could not find app/layout.tsx. Please configure TraceForge manually."));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// 3. Inject code into layout.tsx
|
|
40
|
+
let layoutCode = fs.readFileSync(targetLayout, "utf-8");
|
|
41
|
+
// Check if already injected
|
|
42
|
+
if (layoutCode.includes("TraceForgeErrorBoundary")) {
|
|
43
|
+
s.stop(chalk.green("TraceForge is already configured in layout.tsx!"));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Add import statement
|
|
47
|
+
const importStatement = `import { TraceForgeErrorBoundary } from "usetraceforge/react";\n`;
|
|
48
|
+
layoutCode = importStatement + layoutCode;
|
|
49
|
+
// Replace <body> with <body><TraceForgeErrorBoundary>
|
|
50
|
+
layoutCode = layoutCode.replace(/(<body[^>]*>)/g, `$1\n <TraceForgeErrorBoundary>`);
|
|
51
|
+
// Replace </body> with </TraceForgeErrorBoundary></body>
|
|
52
|
+
layoutCode = layoutCode.replace(/<\/body>/g, ` </TraceForgeErrorBoundary>\n </body>`);
|
|
53
|
+
fs.writeFileSync(targetLayout, layoutCode);
|
|
54
|
+
s.stop(chalk.green("Next.js configuration complete!"));
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
s.stop(chalk.red("Failed to configure Next.js automatically."));
|
|
58
|
+
console.error(error);
|
|
59
|
+
}
|
|
60
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "usetraceforge-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TraceForge CLI Wizard for 2-click installations",
|
|
5
|
+
"bin": {
|
|
6
|
+
"traceforge": "./dist/index.js"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "npx tsc -p tsconfig.json",
|
|
14
|
+
"dev": "tsx src/index.ts",
|
|
15
|
+
"prepack": "npm run build",
|
|
16
|
+
"publish:public": "npm publish --access public"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@clack/prompts": "^0.7.0",
|
|
20
|
+
"chalk": "^5.3.0",
|
|
21
|
+
"execa": "^9.3.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^20.0.0",
|
|
25
|
+
"typescript": "^5.5.4",
|
|
26
|
+
"tsx": "^4.16.2"
|
|
27
|
+
}
|
|
28
|
+
}
|