@insforge/cli 0.1.33 → 0.1.35
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 +187 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1870,6 +1870,31 @@ async function readEnvFile(cwd) {
|
|
|
1870
1870
|
return [];
|
|
1871
1871
|
}
|
|
1872
1872
|
|
|
1873
|
+
// src/lib/analytics.ts
|
|
1874
|
+
import { PostHog } from "posthog-node";
|
|
1875
|
+
var POSTHOG_API_KEY = "";
|
|
1876
|
+
var POSTHOG_HOST = process.env.POSTHOG_HOST || "https://us.i.posthog.com";
|
|
1877
|
+
var client = null;
|
|
1878
|
+
function getClient() {
|
|
1879
|
+
if (!POSTHOG_API_KEY) return null;
|
|
1880
|
+
if (!client) {
|
|
1881
|
+
client = new PostHog(POSTHOG_API_KEY, { host: POSTHOG_HOST });
|
|
1882
|
+
}
|
|
1883
|
+
return client;
|
|
1884
|
+
}
|
|
1885
|
+
function captureEvent(distinctId, event, properties) {
|
|
1886
|
+
try {
|
|
1887
|
+
getClient()?.capture({ distinctId, event, properties });
|
|
1888
|
+
} catch {
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
async function shutdownAnalytics() {
|
|
1892
|
+
try {
|
|
1893
|
+
if (client) await client.shutdown();
|
|
1894
|
+
} catch {
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1873
1898
|
// src/commands/deployments/deploy.ts
|
|
1874
1899
|
import * as path2 from "path";
|
|
1875
1900
|
import * as fs2 from "fs/promises";
|
|
@@ -2042,6 +2067,91 @@ async function waitForProjectActive(projectId, apiUrl, timeoutMs = 12e4) {
|
|
|
2042
2067
|
}
|
|
2043
2068
|
throw new CLIError("Project creation timed out. Check the dashboard for status.");
|
|
2044
2069
|
}
|
|
2070
|
+
var INSFORGE_BANNER = [
|
|
2071
|
+
"\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
|
|
2072
|
+
"\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D",
|
|
2073
|
+
"\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2557 ",
|
|
2074
|
+
"\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D ",
|
|
2075
|
+
"\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
|
|
2076
|
+
"\u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
|
|
2077
|
+
];
|
|
2078
|
+
async function animateBanner() {
|
|
2079
|
+
const isTTY = process.stderr.isTTY;
|
|
2080
|
+
if (!isTTY || process.env.CI) {
|
|
2081
|
+
for (const line of INSFORGE_BANNER) {
|
|
2082
|
+
process.stderr.write(`${line}
|
|
2083
|
+
`);
|
|
2084
|
+
}
|
|
2085
|
+
process.stderr.write("\n");
|
|
2086
|
+
return;
|
|
2087
|
+
}
|
|
2088
|
+
const totalLines = INSFORGE_BANNER.length;
|
|
2089
|
+
const maxLen = Math.max(...INSFORGE_BANNER.map((l) => l.length));
|
|
2090
|
+
const cols = process.stderr.columns ?? 0;
|
|
2091
|
+
if (cols > 0 && cols < maxLen) {
|
|
2092
|
+
for (const line of INSFORGE_BANNER) {
|
|
2093
|
+
process.stderr.write(`\x1B[97m${line}\x1B[0m
|
|
2094
|
+
`);
|
|
2095
|
+
}
|
|
2096
|
+
process.stderr.write("\n");
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
2099
|
+
const REVEAL_STEPS = 10;
|
|
2100
|
+
const REVEAL_DELAY = 30;
|
|
2101
|
+
for (let lineIdx = 0; lineIdx < totalLines; lineIdx++) {
|
|
2102
|
+
const line = INSFORGE_BANNER[lineIdx];
|
|
2103
|
+
for (let step = 0; step <= REVEAL_STEPS; step++) {
|
|
2104
|
+
const pos = Math.floor(step / REVEAL_STEPS * line.length);
|
|
2105
|
+
let rendered = "";
|
|
2106
|
+
for (let i = 0; i < line.length; i++) {
|
|
2107
|
+
if (i < pos) {
|
|
2108
|
+
rendered += `\x1B[97m${line[i]}\x1B[0m`;
|
|
2109
|
+
} else if (i === pos) {
|
|
2110
|
+
rendered += `\x1B[1;37m${line[i]}\x1B[0m`;
|
|
2111
|
+
} else {
|
|
2112
|
+
rendered += `\x1B[90m${line[i]}\x1B[0m`;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
process.stderr.write(`\r${rendered}`);
|
|
2116
|
+
await new Promise((r) => setTimeout(r, REVEAL_DELAY));
|
|
2117
|
+
}
|
|
2118
|
+
process.stderr.write("\n");
|
|
2119
|
+
}
|
|
2120
|
+
const SHIMMER_STEPS = 16;
|
|
2121
|
+
const SHIMMER_DELAY = 40;
|
|
2122
|
+
const SHIMMER_WIDTH = 4;
|
|
2123
|
+
for (let step = 0; step < SHIMMER_STEPS; step++) {
|
|
2124
|
+
const shimmerPos = Math.floor(step / SHIMMER_STEPS * (maxLen + SHIMMER_WIDTH));
|
|
2125
|
+
process.stderr.write(`\x1B[${totalLines}A`);
|
|
2126
|
+
for (const line of INSFORGE_BANNER) {
|
|
2127
|
+
let rendered = "";
|
|
2128
|
+
for (let i = 0; i < line.length; i++) {
|
|
2129
|
+
const dist = Math.abs(i - shimmerPos);
|
|
2130
|
+
if (dist === 0) {
|
|
2131
|
+
rendered += `\x1B[1;97m${line[i]}\x1B[0m`;
|
|
2132
|
+
} else if (dist <= SHIMMER_WIDTH) {
|
|
2133
|
+
rendered += `\x1B[37m${line[i]}\x1B[0m`;
|
|
2134
|
+
} else {
|
|
2135
|
+
rendered += `\x1B[90m${line[i]}\x1B[0m`;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
process.stderr.write(`${rendered}
|
|
2139
|
+
`);
|
|
2140
|
+
}
|
|
2141
|
+
await new Promise((r) => setTimeout(r, SHIMMER_DELAY));
|
|
2142
|
+
}
|
|
2143
|
+
process.stderr.write(`\x1B[${totalLines}A`);
|
|
2144
|
+
for (const line of INSFORGE_BANNER) {
|
|
2145
|
+
process.stderr.write(`\x1B[97m${line}\x1B[0m
|
|
2146
|
+
`);
|
|
2147
|
+
}
|
|
2148
|
+
process.stderr.write("\n");
|
|
2149
|
+
}
|
|
2150
|
+
function getDefaultProjectName() {
|
|
2151
|
+
const dirName = path3.basename(process.cwd());
|
|
2152
|
+
const sanitized = dirName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2153
|
+
return sanitized.length >= 2 ? sanitized : "";
|
|
2154
|
+
}
|
|
2045
2155
|
async function copyDir(src, dest) {
|
|
2046
2156
|
const entries = await fs3.readdir(src, { withFileTypes: true });
|
|
2047
2157
|
for (const entry of entries) {
|
|
@@ -2061,7 +2171,8 @@ function registerCreateCommand(program2) {
|
|
|
2061
2171
|
try {
|
|
2062
2172
|
await requireAuth(apiUrl, false);
|
|
2063
2173
|
if (!json) {
|
|
2064
|
-
|
|
2174
|
+
await animateBanner();
|
|
2175
|
+
clack10.intro("Let's build something great");
|
|
2065
2176
|
}
|
|
2066
2177
|
let orgId = opts.orgId;
|
|
2067
2178
|
if (!orgId) {
|
|
@@ -2088,13 +2199,19 @@ function registerCreateCommand(program2) {
|
|
|
2088
2199
|
let projectName = opts.name;
|
|
2089
2200
|
if (!projectName) {
|
|
2090
2201
|
if (json) throw new CLIError("--name is required in JSON mode.");
|
|
2202
|
+
const defaultName = getDefaultProjectName();
|
|
2091
2203
|
const name = await clack10.text({
|
|
2092
2204
|
message: "Project name:",
|
|
2205
|
+
...defaultName ? { defaultValue: defaultName, placeholder: defaultName } : {},
|
|
2093
2206
|
validate: (v) => v.length >= 2 ? void 0 : "Name must be at least 2 characters"
|
|
2094
2207
|
});
|
|
2095
2208
|
if (clack10.isCancel(name)) process.exit(0);
|
|
2096
2209
|
projectName = name;
|
|
2097
2210
|
}
|
|
2211
|
+
projectName = path3.basename(projectName).replace(/[^a-zA-Z0-9._-]/g, "-").replace(/\.+/g, ".");
|
|
2212
|
+
if (projectName.length < 2 || projectName === "." || projectName === "..") {
|
|
2213
|
+
throw new CLIError("Project name must be at least 2 safe characters (letters, numbers, hyphens).");
|
|
2214
|
+
}
|
|
2098
2215
|
const validTemplates = ["react", "nextjs", "chatbot", "crm", "e-commerce", "empty"];
|
|
2099
2216
|
let template = opts.template;
|
|
2100
2217
|
if (template && !validTemplates.includes(template)) {
|
|
@@ -2104,21 +2221,39 @@ function registerCreateCommand(program2) {
|
|
|
2104
2221
|
if (json) {
|
|
2105
2222
|
template = "empty";
|
|
2106
2223
|
} else {
|
|
2107
|
-
const
|
|
2108
|
-
message: "
|
|
2224
|
+
const approach = await clack10.select({
|
|
2225
|
+
message: "How would you like to start?",
|
|
2109
2226
|
options: [
|
|
2110
|
-
{ value: "
|
|
2111
|
-
{ value: "
|
|
2112
|
-
{ value: "chatbot", label: "AI Chatbot with Next.js" },
|
|
2113
|
-
{ value: "crm", label: "CRM with Next.js" },
|
|
2114
|
-
{ value: "e-commerce", label: "E-Commerce store with Next.js" },
|
|
2115
|
-
{ value: "empty", label: "Empty project" }
|
|
2227
|
+
{ value: "blank", label: "Blank project", hint: "Start from scratch with .env.local ready" },
|
|
2228
|
+
{ value: "template", label: "Start from a template", hint: "Pre-built starter apps" }
|
|
2116
2229
|
]
|
|
2117
2230
|
});
|
|
2118
|
-
if (clack10.isCancel(
|
|
2119
|
-
|
|
2231
|
+
if (clack10.isCancel(approach)) process.exit(0);
|
|
2232
|
+
captureEvent(orgId, "create_approach_selected", {
|
|
2233
|
+
approach
|
|
2234
|
+
});
|
|
2235
|
+
if (approach === "blank") {
|
|
2236
|
+
template = "empty";
|
|
2237
|
+
} else {
|
|
2238
|
+
const selected = await clack10.select({
|
|
2239
|
+
message: "Choose a starter template:",
|
|
2240
|
+
options: [
|
|
2241
|
+
{ value: "react", label: "Web app template with React" },
|
|
2242
|
+
{ value: "nextjs", label: "Web app template with Next.js" },
|
|
2243
|
+
{ value: "chatbot", label: "AI Chatbot with Next.js" },
|
|
2244
|
+
{ value: "crm", label: "CRM with Next.js" },
|
|
2245
|
+
{ value: "e-commerce", label: "E-Commerce store with Next.js" }
|
|
2246
|
+
]
|
|
2247
|
+
});
|
|
2248
|
+
if (clack10.isCancel(selected)) process.exit(0);
|
|
2249
|
+
template = selected;
|
|
2250
|
+
}
|
|
2120
2251
|
}
|
|
2121
2252
|
}
|
|
2253
|
+
captureEvent(orgId, "template_selected", {
|
|
2254
|
+
template,
|
|
2255
|
+
approach: template === "empty" ? "blank" : "template"
|
|
2256
|
+
});
|
|
2122
2257
|
const s = !json ? clack10.spinner() : null;
|
|
2123
2258
|
s?.start("Creating project...");
|
|
2124
2259
|
const project = await createProject(orgId, projectName, opts.region, apiUrl);
|
|
@@ -2137,11 +2272,39 @@ function registerCreateCommand(program2) {
|
|
|
2137
2272
|
saveProjectConfig(projectConfig);
|
|
2138
2273
|
s?.stop(`Project "${project.name}" created and linked`);
|
|
2139
2274
|
const hasTemplate = template !== "empty";
|
|
2140
|
-
const githubTemplates = ["chatbot", "crm", "e-commerce"];
|
|
2275
|
+
const githubTemplates = ["chatbot", "crm", "e-commerce", "nextjs", "react"];
|
|
2141
2276
|
if (githubTemplates.includes(template)) {
|
|
2142
2277
|
await downloadGitHubTemplate(template, projectConfig, json);
|
|
2143
2278
|
} else if (hasTemplate) {
|
|
2144
2279
|
await downloadTemplate(template, projectConfig, projectName, json, apiUrl);
|
|
2280
|
+
} else {
|
|
2281
|
+
try {
|
|
2282
|
+
const anonKey = await getAnonKey();
|
|
2283
|
+
if (!anonKey) {
|
|
2284
|
+
if (!json) clack10.log.warn("Could not retrieve anon key. You can add it to .env.local manually.");
|
|
2285
|
+
} else {
|
|
2286
|
+
const envPath = path3.join(process.cwd(), ".env.local");
|
|
2287
|
+
const envContent = [
|
|
2288
|
+
"# InsForge",
|
|
2289
|
+
`NEXT_PUBLIC_INSFORGE_URL=${projectConfig.oss_host}`,
|
|
2290
|
+
`NEXT_PUBLIC_INSFORGE_ANON_KEY=${anonKey}`,
|
|
2291
|
+
""
|
|
2292
|
+
].join("\n");
|
|
2293
|
+
await fs3.writeFile(envPath, envContent, { flag: "wx" });
|
|
2294
|
+
if (!json) {
|
|
2295
|
+
clack10.log.success("Created .env.local with your InsForge credentials");
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
} catch (err) {
|
|
2299
|
+
const error = err;
|
|
2300
|
+
if (!json) {
|
|
2301
|
+
if (error.code === "EEXIST") {
|
|
2302
|
+
clack10.log.warn(".env.local already exists; skipping InsForge key seeding.");
|
|
2303
|
+
} else {
|
|
2304
|
+
clack10.log.warn(`Failed to create .env.local: ${error.message}`);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2145
2308
|
}
|
|
2146
2309
|
await installSkills(json);
|
|
2147
2310
|
await reportCliUsage("cli.create", true, 6);
|
|
@@ -2211,6 +2374,8 @@ function registerCreateCommand(program2) {
|
|
|
2211
2374
|
}
|
|
2212
2375
|
} catch (err) {
|
|
2213
2376
|
handleError(err, json);
|
|
2377
|
+
} finally {
|
|
2378
|
+
await shutdownAnalytics();
|
|
2214
2379
|
}
|
|
2215
2380
|
});
|
|
2216
2381
|
}
|
|
@@ -2284,7 +2449,16 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
2284
2449
|
return `${prefix}${_value}`;
|
|
2285
2450
|
}
|
|
2286
2451
|
);
|
|
2287
|
-
|
|
2452
|
+
const envLocalPath = path3.join(cwd, ".env.local");
|
|
2453
|
+
try {
|
|
2454
|
+
await fs3.writeFile(envLocalPath, envContent, { flag: "wx" });
|
|
2455
|
+
} catch (e) {
|
|
2456
|
+
if (e.code === "EEXIST") {
|
|
2457
|
+
if (!json) clack10.log.warn(".env.local already exists; skipping env seeding.");
|
|
2458
|
+
} else {
|
|
2459
|
+
throw e;
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2288
2462
|
}
|
|
2289
2463
|
s?.stop(`${templateName} template downloaded`);
|
|
2290
2464
|
const migrationPath = path3.join(cwd, "migrations", "db_init.sql");
|