@insforge/cli 0.1.32 → 0.1.34
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 +158 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -373,6 +373,11 @@ async function refreshAccessToken(apiUrl) {
|
|
|
373
373
|
saveCredentials(updated);
|
|
374
374
|
return data.access_token;
|
|
375
375
|
} catch {
|
|
376
|
+
if (process.stdout.isTTY) {
|
|
377
|
+
clack2.log.warn("Session expired. Please log in again.");
|
|
378
|
+
const newCreds = await performOAuthLogin(apiUrl);
|
|
379
|
+
return newCreds.access_token;
|
|
380
|
+
}
|
|
376
381
|
throw new AuthError("Failed to refresh token. Run `insforge login` again.");
|
|
377
382
|
}
|
|
378
383
|
}
|
|
@@ -2037,6 +2042,91 @@ async function waitForProjectActive(projectId, apiUrl, timeoutMs = 12e4) {
|
|
|
2037
2042
|
}
|
|
2038
2043
|
throw new CLIError("Project creation timed out. Check the dashboard for status.");
|
|
2039
2044
|
}
|
|
2045
|
+
var INSFORGE_BANNER = [
|
|
2046
|
+
"\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",
|
|
2047
|
+
"\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",
|
|
2048
|
+
"\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 ",
|
|
2049
|
+
"\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 ",
|
|
2050
|
+
"\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",
|
|
2051
|
+
"\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"
|
|
2052
|
+
];
|
|
2053
|
+
async function animateBanner() {
|
|
2054
|
+
const isTTY = process.stderr.isTTY;
|
|
2055
|
+
if (!isTTY || process.env.CI) {
|
|
2056
|
+
for (const line of INSFORGE_BANNER) {
|
|
2057
|
+
process.stderr.write(`${line}
|
|
2058
|
+
`);
|
|
2059
|
+
}
|
|
2060
|
+
process.stderr.write("\n");
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
const totalLines = INSFORGE_BANNER.length;
|
|
2064
|
+
const maxLen = Math.max(...INSFORGE_BANNER.map((l) => l.length));
|
|
2065
|
+
const cols = process.stderr.columns ?? 0;
|
|
2066
|
+
if (cols > 0 && cols < maxLen) {
|
|
2067
|
+
for (const line of INSFORGE_BANNER) {
|
|
2068
|
+
process.stderr.write(`\x1B[97m${line}\x1B[0m
|
|
2069
|
+
`);
|
|
2070
|
+
}
|
|
2071
|
+
process.stderr.write("\n");
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2074
|
+
const REVEAL_STEPS = 10;
|
|
2075
|
+
const REVEAL_DELAY = 30;
|
|
2076
|
+
for (let lineIdx = 0; lineIdx < totalLines; lineIdx++) {
|
|
2077
|
+
const line = INSFORGE_BANNER[lineIdx];
|
|
2078
|
+
for (let step = 0; step <= REVEAL_STEPS; step++) {
|
|
2079
|
+
const pos = Math.floor(step / REVEAL_STEPS * line.length);
|
|
2080
|
+
let rendered = "";
|
|
2081
|
+
for (let i = 0; i < line.length; i++) {
|
|
2082
|
+
if (i < pos) {
|
|
2083
|
+
rendered += `\x1B[97m${line[i]}\x1B[0m`;
|
|
2084
|
+
} else if (i === pos) {
|
|
2085
|
+
rendered += `\x1B[1;37m${line[i]}\x1B[0m`;
|
|
2086
|
+
} else {
|
|
2087
|
+
rendered += `\x1B[90m${line[i]}\x1B[0m`;
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
process.stderr.write(`\r${rendered}`);
|
|
2091
|
+
await new Promise((r) => setTimeout(r, REVEAL_DELAY));
|
|
2092
|
+
}
|
|
2093
|
+
process.stderr.write("\n");
|
|
2094
|
+
}
|
|
2095
|
+
const SHIMMER_STEPS = 16;
|
|
2096
|
+
const SHIMMER_DELAY = 40;
|
|
2097
|
+
const SHIMMER_WIDTH = 4;
|
|
2098
|
+
for (let step = 0; step < SHIMMER_STEPS; step++) {
|
|
2099
|
+
const shimmerPos = Math.floor(step / SHIMMER_STEPS * (maxLen + SHIMMER_WIDTH));
|
|
2100
|
+
process.stderr.write(`\x1B[${totalLines}A`);
|
|
2101
|
+
for (const line of INSFORGE_BANNER) {
|
|
2102
|
+
let rendered = "";
|
|
2103
|
+
for (let i = 0; i < line.length; i++) {
|
|
2104
|
+
const dist = Math.abs(i - shimmerPos);
|
|
2105
|
+
if (dist === 0) {
|
|
2106
|
+
rendered += `\x1B[1;97m${line[i]}\x1B[0m`;
|
|
2107
|
+
} else if (dist <= SHIMMER_WIDTH) {
|
|
2108
|
+
rendered += `\x1B[37m${line[i]}\x1B[0m`;
|
|
2109
|
+
} else {
|
|
2110
|
+
rendered += `\x1B[90m${line[i]}\x1B[0m`;
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
process.stderr.write(`${rendered}
|
|
2114
|
+
`);
|
|
2115
|
+
}
|
|
2116
|
+
await new Promise((r) => setTimeout(r, SHIMMER_DELAY));
|
|
2117
|
+
}
|
|
2118
|
+
process.stderr.write(`\x1B[${totalLines}A`);
|
|
2119
|
+
for (const line of INSFORGE_BANNER) {
|
|
2120
|
+
process.stderr.write(`\x1B[97m${line}\x1B[0m
|
|
2121
|
+
`);
|
|
2122
|
+
}
|
|
2123
|
+
process.stderr.write("\n");
|
|
2124
|
+
}
|
|
2125
|
+
function getDefaultProjectName() {
|
|
2126
|
+
const dirName = path3.basename(process.cwd());
|
|
2127
|
+
const sanitized = dirName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2128
|
+
return sanitized.length >= 2 ? sanitized : "";
|
|
2129
|
+
}
|
|
2040
2130
|
async function copyDir(src, dest) {
|
|
2041
2131
|
const entries = await fs3.readdir(src, { withFileTypes: true });
|
|
2042
2132
|
for (const entry of entries) {
|
|
@@ -2056,7 +2146,8 @@ function registerCreateCommand(program2) {
|
|
|
2056
2146
|
try {
|
|
2057
2147
|
await requireAuth(apiUrl, false);
|
|
2058
2148
|
if (!json) {
|
|
2059
|
-
|
|
2149
|
+
await animateBanner();
|
|
2150
|
+
clack10.intro("Let's build something great");
|
|
2060
2151
|
}
|
|
2061
2152
|
let orgId = opts.orgId;
|
|
2062
2153
|
if (!orgId) {
|
|
@@ -2083,13 +2174,19 @@ function registerCreateCommand(program2) {
|
|
|
2083
2174
|
let projectName = opts.name;
|
|
2084
2175
|
if (!projectName) {
|
|
2085
2176
|
if (json) throw new CLIError("--name is required in JSON mode.");
|
|
2177
|
+
const defaultName = getDefaultProjectName();
|
|
2086
2178
|
const name = await clack10.text({
|
|
2087
2179
|
message: "Project name:",
|
|
2180
|
+
...defaultName ? { defaultValue: defaultName, placeholder: defaultName } : {},
|
|
2088
2181
|
validate: (v) => v.length >= 2 ? void 0 : "Name must be at least 2 characters"
|
|
2089
2182
|
});
|
|
2090
2183
|
if (clack10.isCancel(name)) process.exit(0);
|
|
2091
2184
|
projectName = name;
|
|
2092
2185
|
}
|
|
2186
|
+
projectName = path3.basename(projectName).replace(/[^a-zA-Z0-9._-]/g, "-").replace(/\.+/g, ".");
|
|
2187
|
+
if (projectName.length < 2 || projectName === "." || projectName === "..") {
|
|
2188
|
+
throw new CLIError("Project name must be at least 2 safe characters (letters, numbers, hyphens).");
|
|
2189
|
+
}
|
|
2093
2190
|
const validTemplates = ["react", "nextjs", "chatbot", "crm", "e-commerce", "empty"];
|
|
2094
2191
|
let template = opts.template;
|
|
2095
2192
|
if (template && !validTemplates.includes(template)) {
|
|
@@ -2099,19 +2196,30 @@ function registerCreateCommand(program2) {
|
|
|
2099
2196
|
if (json) {
|
|
2100
2197
|
template = "empty";
|
|
2101
2198
|
} else {
|
|
2102
|
-
const
|
|
2103
|
-
message: "
|
|
2199
|
+
const approach = await clack10.select({
|
|
2200
|
+
message: "How would you like to start?",
|
|
2104
2201
|
options: [
|
|
2105
|
-
{ value: "
|
|
2106
|
-
{ value: "
|
|
2107
|
-
{ value: "chatbot", label: "AI Chatbot with Next.js" },
|
|
2108
|
-
{ value: "crm", label: "CRM with Next.js" },
|
|
2109
|
-
{ value: "e-commerce", label: "E-Commerce store with Next.js" },
|
|
2110
|
-
{ value: "empty", label: "Empty project" }
|
|
2202
|
+
{ value: "blank", label: "Blank project", hint: "Start from scratch with .env.local ready" },
|
|
2203
|
+
{ value: "template", label: "Start from a template", hint: "Pre-built starter apps" }
|
|
2111
2204
|
]
|
|
2112
2205
|
});
|
|
2113
|
-
if (clack10.isCancel(
|
|
2114
|
-
|
|
2206
|
+
if (clack10.isCancel(approach)) process.exit(0);
|
|
2207
|
+
if (approach === "blank") {
|
|
2208
|
+
template = "empty";
|
|
2209
|
+
} else {
|
|
2210
|
+
const selected = await clack10.select({
|
|
2211
|
+
message: "Choose a starter template:",
|
|
2212
|
+
options: [
|
|
2213
|
+
{ value: "react", label: "Web app template with React" },
|
|
2214
|
+
{ value: "nextjs", label: "Web app template with Next.js" },
|
|
2215
|
+
{ value: "chatbot", label: "AI Chatbot with Next.js" },
|
|
2216
|
+
{ value: "crm", label: "CRM with Next.js" },
|
|
2217
|
+
{ value: "e-commerce", label: "E-Commerce store with Next.js" }
|
|
2218
|
+
]
|
|
2219
|
+
});
|
|
2220
|
+
if (clack10.isCancel(selected)) process.exit(0);
|
|
2221
|
+
template = selected;
|
|
2222
|
+
}
|
|
2115
2223
|
}
|
|
2116
2224
|
}
|
|
2117
2225
|
const s = !json ? clack10.spinner() : null;
|
|
@@ -2132,11 +2240,39 @@ function registerCreateCommand(program2) {
|
|
|
2132
2240
|
saveProjectConfig(projectConfig);
|
|
2133
2241
|
s?.stop(`Project "${project.name}" created and linked`);
|
|
2134
2242
|
const hasTemplate = template !== "empty";
|
|
2135
|
-
const githubTemplates = ["chatbot", "crm", "e-commerce"];
|
|
2243
|
+
const githubTemplates = ["chatbot", "crm", "e-commerce", "nextjs", "react"];
|
|
2136
2244
|
if (githubTemplates.includes(template)) {
|
|
2137
2245
|
await downloadGitHubTemplate(template, projectConfig, json);
|
|
2138
2246
|
} else if (hasTemplate) {
|
|
2139
2247
|
await downloadTemplate(template, projectConfig, projectName, json, apiUrl);
|
|
2248
|
+
} else {
|
|
2249
|
+
try {
|
|
2250
|
+
const anonKey = await getAnonKey();
|
|
2251
|
+
if (!anonKey) {
|
|
2252
|
+
if (!json) clack10.log.warn("Could not retrieve anon key. You can add it to .env.local manually.");
|
|
2253
|
+
} else {
|
|
2254
|
+
const envPath = path3.join(process.cwd(), ".env.local");
|
|
2255
|
+
const envContent = [
|
|
2256
|
+
"# InsForge",
|
|
2257
|
+
`NEXT_PUBLIC_INSFORGE_URL=${projectConfig.oss_host}`,
|
|
2258
|
+
`NEXT_PUBLIC_INSFORGE_ANON_KEY=${anonKey}`,
|
|
2259
|
+
""
|
|
2260
|
+
].join("\n");
|
|
2261
|
+
await fs3.writeFile(envPath, envContent, { flag: "wx" });
|
|
2262
|
+
if (!json) {
|
|
2263
|
+
clack10.log.success("Created .env.local with your InsForge credentials");
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
} catch (err) {
|
|
2267
|
+
const error = err;
|
|
2268
|
+
if (!json) {
|
|
2269
|
+
if (error.code === "EEXIST") {
|
|
2270
|
+
clack10.log.warn(".env.local already exists; skipping InsForge key seeding.");
|
|
2271
|
+
} else {
|
|
2272
|
+
clack10.log.warn(`Failed to create .env.local: ${error.message}`);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2140
2276
|
}
|
|
2141
2277
|
await installSkills(json);
|
|
2142
2278
|
await reportCliUsage("cli.create", true, 6);
|
|
@@ -2279,7 +2415,16 @@ async function downloadGitHubTemplate(templateName, projectConfig, json) {
|
|
|
2279
2415
|
return `${prefix}${_value}`;
|
|
2280
2416
|
}
|
|
2281
2417
|
);
|
|
2282
|
-
|
|
2418
|
+
const envLocalPath = path3.join(cwd, ".env.local");
|
|
2419
|
+
try {
|
|
2420
|
+
await fs3.writeFile(envLocalPath, envContent, { flag: "wx" });
|
|
2421
|
+
} catch (e) {
|
|
2422
|
+
if (e.code === "EEXIST") {
|
|
2423
|
+
if (!json) clack10.log.warn(".env.local already exists; skipping env seeding.");
|
|
2424
|
+
} else {
|
|
2425
|
+
throw e;
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2283
2428
|
}
|
|
2284
2429
|
s?.stop(`${templateName} template downloaded`);
|
|
2285
2430
|
const migrationPath = path3.join(cwd, "migrations", "db_init.sql");
|