@insforge/cli 0.1.6 → 0.1.7
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 +240 -179
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ var GLOBAL_DIR = join(homedir(), ".insforge");
|
|
|
17
17
|
var CREDENTIALS_FILE = join(GLOBAL_DIR, "credentials.json");
|
|
18
18
|
var CONFIG_FILE = join(GLOBAL_DIR, "config.json");
|
|
19
19
|
var DEFAULT_PLATFORM_URL = "https://api.insforge.dev";
|
|
20
|
+
var DEFAULT_FRONTEND_URL = "https://insforge.dev";
|
|
20
21
|
function ensureGlobalDir() {
|
|
21
22
|
if (!existsSync(GLOBAL_DIR)) {
|
|
22
23
|
mkdirSync(GLOBAL_DIR, { recursive: true });
|
|
@@ -78,6 +79,9 @@ function saveProjectConfig(config) {
|
|
|
78
79
|
function getPlatformApiUrl(override) {
|
|
79
80
|
return process.env.INSFORGE_API_URL ?? override ?? getGlobalConfig().platform_api_url ?? DEFAULT_PLATFORM_URL;
|
|
80
81
|
}
|
|
82
|
+
function getFrontendUrl() {
|
|
83
|
+
return process.env.INSFORGE_FRONTEND_URL ?? DEFAULT_FRONTEND_URL;
|
|
84
|
+
}
|
|
81
85
|
function getAccessToken() {
|
|
82
86
|
return process.env.INSFORGE_ACCESS_TOKEN ?? getCredentials()?.access_token ?? null;
|
|
83
87
|
}
|
|
@@ -1568,10 +1572,10 @@ function registerStorageDeleteBucketCommand(storageCmd2) {
|
|
|
1568
1572
|
try {
|
|
1569
1573
|
await requireAuth();
|
|
1570
1574
|
if (!yes && !json) {
|
|
1571
|
-
const
|
|
1575
|
+
const confirm6 = await clack7.confirm({
|
|
1572
1576
|
message: `Delete bucket "${name}" and all its objects? This cannot be undone.`
|
|
1573
1577
|
});
|
|
1574
|
-
if (!
|
|
1578
|
+
if (!confirm6 || clack7.isCancel(confirm6)) {
|
|
1575
1579
|
process.exit(0);
|
|
1576
1580
|
}
|
|
1577
1581
|
}
|
|
@@ -1649,9 +1653,166 @@ function registerStorageListObjectsCommand(storageCmd2) {
|
|
|
1649
1653
|
import { exec as exec2 } from "child_process";
|
|
1650
1654
|
import { tmpdir } from "os";
|
|
1651
1655
|
import { promisify as promisify2 } from "util";
|
|
1652
|
-
import * as
|
|
1656
|
+
import * as fs2 from "fs/promises";
|
|
1657
|
+
import * as path2 from "path";
|
|
1658
|
+
import * as clack9 from "@clack/prompts";
|
|
1659
|
+
|
|
1660
|
+
// src/commands/deployments/deploy.ts
|
|
1653
1661
|
import * as path from "path";
|
|
1662
|
+
import * as fs from "fs/promises";
|
|
1654
1663
|
import * as clack8 from "@clack/prompts";
|
|
1664
|
+
import archiver from "archiver";
|
|
1665
|
+
var POLL_INTERVAL_MS = 5e3;
|
|
1666
|
+
var POLL_TIMEOUT_MS = 12e4;
|
|
1667
|
+
var EXCLUDE_PATTERNS = [
|
|
1668
|
+
"node_modules",
|
|
1669
|
+
".git",
|
|
1670
|
+
".next",
|
|
1671
|
+
".env",
|
|
1672
|
+
".env.local",
|
|
1673
|
+
"dist",
|
|
1674
|
+
"build",
|
|
1675
|
+
".DS_Store",
|
|
1676
|
+
".insforge"
|
|
1677
|
+
];
|
|
1678
|
+
function shouldExclude(name) {
|
|
1679
|
+
const normalized = name.replace(/\\/g, "/");
|
|
1680
|
+
for (const pattern of EXCLUDE_PATTERNS) {
|
|
1681
|
+
if (normalized === pattern || normalized.startsWith(pattern + "/") || normalized.endsWith("/" + pattern) || normalized.includes("/" + pattern + "/")) {
|
|
1682
|
+
return true;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
if (normalized.endsWith(".log")) return true;
|
|
1686
|
+
return false;
|
|
1687
|
+
}
|
|
1688
|
+
async function createZipBuffer(sourceDir) {
|
|
1689
|
+
return new Promise((resolve2, reject) => {
|
|
1690
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
1691
|
+
const chunks = [];
|
|
1692
|
+
archive.on("data", (chunk) => chunks.push(chunk));
|
|
1693
|
+
archive.on("end", () => resolve2(Buffer.concat(chunks)));
|
|
1694
|
+
archive.on("error", (err) => reject(err));
|
|
1695
|
+
archive.directory(sourceDir, false, (entry) => {
|
|
1696
|
+
if (shouldExclude(entry.name)) return false;
|
|
1697
|
+
return entry;
|
|
1698
|
+
});
|
|
1699
|
+
void archive.finalize();
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
async function deployProject(opts) {
|
|
1703
|
+
const { sourceDir, startBody = {}, spinner: s } = opts;
|
|
1704
|
+
s?.start("Creating deployment...");
|
|
1705
|
+
const createRes = await ossFetch("/api/deployments", { method: "POST" });
|
|
1706
|
+
const { id: deploymentId, uploadUrl, uploadFields } = await createRes.json();
|
|
1707
|
+
s?.message("Compressing source files...");
|
|
1708
|
+
const zipBuffer = await createZipBuffer(sourceDir);
|
|
1709
|
+
s?.message("Uploading...");
|
|
1710
|
+
const formData = new FormData();
|
|
1711
|
+
for (const [key, value] of Object.entries(uploadFields)) {
|
|
1712
|
+
formData.append(key, value);
|
|
1713
|
+
}
|
|
1714
|
+
formData.append(
|
|
1715
|
+
"file",
|
|
1716
|
+
new Blob([zipBuffer], { type: "application/zip" }),
|
|
1717
|
+
"deployment.zip"
|
|
1718
|
+
);
|
|
1719
|
+
const uploadRes = await fetch(uploadUrl, { method: "POST", body: formData });
|
|
1720
|
+
if (!uploadRes.ok) {
|
|
1721
|
+
const uploadErr = await uploadRes.text();
|
|
1722
|
+
throw new CLIError(`Failed to upload: ${uploadErr}`);
|
|
1723
|
+
}
|
|
1724
|
+
s?.message("Starting deployment...");
|
|
1725
|
+
const startRes = await ossFetch(`/api/deployments/${deploymentId}/start`, {
|
|
1726
|
+
method: "POST",
|
|
1727
|
+
body: JSON.stringify(startBody)
|
|
1728
|
+
});
|
|
1729
|
+
await startRes.json();
|
|
1730
|
+
s?.message("Building and deploying...");
|
|
1731
|
+
const startTime = Date.now();
|
|
1732
|
+
let deployment = null;
|
|
1733
|
+
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
1734
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
1735
|
+
try {
|
|
1736
|
+
const statusRes = await ossFetch(`/api/deployments/${deploymentId}`);
|
|
1737
|
+
deployment = await statusRes.json();
|
|
1738
|
+
if (deployment.status === "ready" || deployment.status === "READY") {
|
|
1739
|
+
break;
|
|
1740
|
+
}
|
|
1741
|
+
if (deployment.status === "error" || deployment.status === "ERROR" || deployment.status === "canceled") {
|
|
1742
|
+
s?.stop("Deployment failed");
|
|
1743
|
+
throw new CLIError(deployment.error ?? `Deployment failed with status: ${deployment.status}`);
|
|
1744
|
+
}
|
|
1745
|
+
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
1746
|
+
s?.message(`Building and deploying... (${elapsed}s, status: ${deployment.status})`);
|
|
1747
|
+
} catch (err) {
|
|
1748
|
+
if (err instanceof CLIError) throw err;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
const isReady = deployment?.status === "ready" || deployment?.status === "READY";
|
|
1752
|
+
const liveUrl = isReady ? deployment?.deploymentUrl ?? deployment?.url ?? null : null;
|
|
1753
|
+
return { deploymentId, deployment, isReady, liveUrl };
|
|
1754
|
+
}
|
|
1755
|
+
function registerDeploymentsDeployCommand(deploymentsCmd2) {
|
|
1756
|
+
deploymentsCmd2.command("deploy [directory]").description("Deploy a frontend project to Vercel").option("--env <vars>", `Environment variables as JSON (e.g. '{"KEY":"value"}')`).option("--meta <meta>", "Deployment metadata as JSON").action(async (directory, opts, cmd) => {
|
|
1757
|
+
const { json } = getRootOpts(cmd);
|
|
1758
|
+
try {
|
|
1759
|
+
await requireAuth();
|
|
1760
|
+
const config = getProjectConfig();
|
|
1761
|
+
if (!config) throw new ProjectNotLinkedError();
|
|
1762
|
+
const sourceDir = path.resolve(directory ?? ".");
|
|
1763
|
+
const stats = await fs.stat(sourceDir).catch(() => null);
|
|
1764
|
+
if (!stats?.isDirectory()) {
|
|
1765
|
+
throw new CLIError(`"${sourceDir}" is not a valid directory.`);
|
|
1766
|
+
}
|
|
1767
|
+
const s = !json ? clack8.spinner() : null;
|
|
1768
|
+
const startBody = {};
|
|
1769
|
+
if (opts.env) {
|
|
1770
|
+
try {
|
|
1771
|
+
const parsed = JSON.parse(opts.env);
|
|
1772
|
+
if (Array.isArray(parsed)) {
|
|
1773
|
+
startBody.envVars = parsed;
|
|
1774
|
+
} else {
|
|
1775
|
+
startBody.envVars = Object.entries(parsed).map(([key, value]) => ({ key, value }));
|
|
1776
|
+
}
|
|
1777
|
+
} catch {
|
|
1778
|
+
throw new CLIError("Invalid --env JSON.");
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
if (opts.meta) {
|
|
1782
|
+
try {
|
|
1783
|
+
startBody.meta = JSON.parse(opts.meta);
|
|
1784
|
+
} catch {
|
|
1785
|
+
throw new CLIError("Invalid --meta JSON.");
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
const result = await deployProject({ sourceDir, startBody, spinner: s });
|
|
1789
|
+
if (result.isReady) {
|
|
1790
|
+
s?.stop("Deployment complete");
|
|
1791
|
+
if (json) {
|
|
1792
|
+
outputJson(result.deployment);
|
|
1793
|
+
} else {
|
|
1794
|
+
if (result.liveUrl) {
|
|
1795
|
+
clack8.log.success(`Live at: ${result.liveUrl}`);
|
|
1796
|
+
}
|
|
1797
|
+
clack8.log.info(`Deployment ID: ${result.deploymentId}`);
|
|
1798
|
+
}
|
|
1799
|
+
} else {
|
|
1800
|
+
s?.stop("Deployment is still building");
|
|
1801
|
+
if (json) {
|
|
1802
|
+
outputJson({ id: result.deploymentId, status: result.deployment?.status ?? "building", timedOut: true });
|
|
1803
|
+
} else {
|
|
1804
|
+
clack8.log.info(`Deployment ID: ${result.deploymentId}`);
|
|
1805
|
+
clack8.log.warn("Deployment did not finish within 2 minutes.");
|
|
1806
|
+
clack8.log.info(`Check status with: insforge deployments status ${result.deploymentId}`);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
} catch (err) {
|
|
1810
|
+
handleError(err, json);
|
|
1811
|
+
}
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
// src/commands/create.ts
|
|
1655
1816
|
var execAsync2 = promisify2(exec2);
|
|
1656
1817
|
function buildOssHost2(appkey, region) {
|
|
1657
1818
|
return `https://${appkey}.${region}.insforge.app`;
|
|
@@ -1666,15 +1827,15 @@ async function waitForProjectActive(projectId, apiUrl, timeoutMs = 12e4) {
|
|
|
1666
1827
|
throw new CLIError("Project creation timed out. Check the dashboard for status.");
|
|
1667
1828
|
}
|
|
1668
1829
|
async function copyDir(src, dest) {
|
|
1669
|
-
const entries = await
|
|
1830
|
+
const entries = await fs2.readdir(src, { withFileTypes: true });
|
|
1670
1831
|
for (const entry of entries) {
|
|
1671
|
-
const srcPath =
|
|
1672
|
-
const destPath =
|
|
1832
|
+
const srcPath = path2.join(src, entry.name);
|
|
1833
|
+
const destPath = path2.join(dest, entry.name);
|
|
1673
1834
|
if (entry.isDirectory()) {
|
|
1674
|
-
await
|
|
1835
|
+
await fs2.mkdir(destPath, { recursive: true });
|
|
1675
1836
|
await copyDir(srcPath, destPath);
|
|
1676
1837
|
} else {
|
|
1677
|
-
await
|
|
1838
|
+
await fs2.copyFile(srcPath, destPath);
|
|
1678
1839
|
}
|
|
1679
1840
|
}
|
|
1680
1841
|
}
|
|
@@ -1684,7 +1845,7 @@ function registerCreateCommand(program2) {
|
|
|
1684
1845
|
try {
|
|
1685
1846
|
await requireAuth(apiUrl);
|
|
1686
1847
|
if (!json) {
|
|
1687
|
-
|
|
1848
|
+
clack9.intro("Create a new InsForge project");
|
|
1688
1849
|
}
|
|
1689
1850
|
let orgId = opts.orgId;
|
|
1690
1851
|
if (!orgId) {
|
|
@@ -1695,14 +1856,14 @@ function registerCreateCommand(program2) {
|
|
|
1695
1856
|
if (json) {
|
|
1696
1857
|
throw new CLIError("Specify --org-id in JSON mode.");
|
|
1697
1858
|
}
|
|
1698
|
-
const selected = await
|
|
1859
|
+
const selected = await clack9.select({
|
|
1699
1860
|
message: "Select an organization:",
|
|
1700
1861
|
options: orgs.map((o) => ({
|
|
1701
1862
|
value: o.id,
|
|
1702
1863
|
label: o.name
|
|
1703
1864
|
}))
|
|
1704
1865
|
});
|
|
1705
|
-
if (
|
|
1866
|
+
if (clack9.isCancel(selected)) process.exit(0);
|
|
1706
1867
|
orgId = selected;
|
|
1707
1868
|
}
|
|
1708
1869
|
const globalConfig = getGlobalConfig();
|
|
@@ -1711,11 +1872,11 @@ function registerCreateCommand(program2) {
|
|
|
1711
1872
|
let projectName = opts.name;
|
|
1712
1873
|
if (!projectName) {
|
|
1713
1874
|
if (json) throw new CLIError("--name is required in JSON mode.");
|
|
1714
|
-
const name = await
|
|
1875
|
+
const name = await clack9.text({
|
|
1715
1876
|
message: "Project name:",
|
|
1716
1877
|
validate: (v) => v.length >= 2 ? void 0 : "Name must be at least 2 characters"
|
|
1717
1878
|
});
|
|
1718
|
-
if (
|
|
1879
|
+
if (clack9.isCancel(name)) process.exit(0);
|
|
1719
1880
|
projectName = name;
|
|
1720
1881
|
}
|
|
1721
1882
|
let template = opts.template;
|
|
@@ -1723,7 +1884,7 @@ function registerCreateCommand(program2) {
|
|
|
1723
1884
|
if (json) {
|
|
1724
1885
|
template = "empty";
|
|
1725
1886
|
} else {
|
|
1726
|
-
const selected = await
|
|
1887
|
+
const selected = await clack9.select({
|
|
1727
1888
|
message: "Choose a starter template:",
|
|
1728
1889
|
options: [
|
|
1729
1890
|
{ value: "react", label: "Web app template with React" },
|
|
@@ -1731,11 +1892,11 @@ function registerCreateCommand(program2) {
|
|
|
1731
1892
|
{ value: "empty", label: "Empty project" }
|
|
1732
1893
|
]
|
|
1733
1894
|
});
|
|
1734
|
-
if (
|
|
1895
|
+
if (clack9.isCancel(selected)) process.exit(0);
|
|
1735
1896
|
template = selected;
|
|
1736
1897
|
}
|
|
1737
1898
|
}
|
|
1738
|
-
const s = !json ?
|
|
1899
|
+
const s = !json ? clack9.spinner() : null;
|
|
1739
1900
|
s?.start("Creating project...");
|
|
1740
1901
|
const project = await createProject(orgId, projectName, opts.region, apiUrl);
|
|
1741
1902
|
s?.message("Waiting for project to become active...");
|
|
@@ -1752,18 +1913,68 @@ function registerCreateCommand(program2) {
|
|
|
1752
1913
|
};
|
|
1753
1914
|
saveProjectConfig(projectConfig);
|
|
1754
1915
|
s?.stop(`Project "${project.name}" created and linked`);
|
|
1755
|
-
|
|
1916
|
+
const hasTemplate = template !== "empty";
|
|
1917
|
+
if (hasTemplate) {
|
|
1756
1918
|
await downloadTemplate(template, projectConfig, projectName, json, apiUrl);
|
|
1757
1919
|
}
|
|
1758
1920
|
await installSkills(json);
|
|
1921
|
+
if (hasTemplate) {
|
|
1922
|
+
const installSpinner = !json ? clack9.spinner() : null;
|
|
1923
|
+
installSpinner?.start("Installing dependencies...");
|
|
1924
|
+
try {
|
|
1925
|
+
await execAsync2("npm install", { cwd: process.cwd(), maxBuffer: 10 * 1024 * 1024 });
|
|
1926
|
+
installSpinner?.stop("Dependencies installed");
|
|
1927
|
+
} catch (err) {
|
|
1928
|
+
installSpinner?.stop("Failed to install dependencies");
|
|
1929
|
+
if (!json) {
|
|
1930
|
+
clack9.log.warn(`npm install failed: ${err.message}`);
|
|
1931
|
+
clack9.log.info("Run `npm install` manually to install dependencies.");
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
let liveUrl = null;
|
|
1936
|
+
if (hasTemplate && !json) {
|
|
1937
|
+
const shouldDeploy = await clack9.confirm({
|
|
1938
|
+
message: "Would you like to deploy now?"
|
|
1939
|
+
});
|
|
1940
|
+
if (!clack9.isCancel(shouldDeploy) && shouldDeploy) {
|
|
1941
|
+
try {
|
|
1942
|
+
const deploySpinner = clack9.spinner();
|
|
1943
|
+
const result = await deployProject({
|
|
1944
|
+
sourceDir: process.cwd(),
|
|
1945
|
+
spinner: deploySpinner
|
|
1946
|
+
});
|
|
1947
|
+
if (result.isReady) {
|
|
1948
|
+
deploySpinner.stop("Deployment complete");
|
|
1949
|
+
liveUrl = result.liveUrl;
|
|
1950
|
+
} else {
|
|
1951
|
+
deploySpinner.stop("Deployment is still building");
|
|
1952
|
+
clack9.log.info(`Deployment ID: ${result.deploymentId}`);
|
|
1953
|
+
clack9.log.warn("Deployment did not finish within 2 minutes.");
|
|
1954
|
+
clack9.log.info(`Check status with: insforge deployments status ${result.deploymentId}`);
|
|
1955
|
+
}
|
|
1956
|
+
} catch (err) {
|
|
1957
|
+
clack9.log.warn(`Deploy failed: ${err.message}`);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
|
|
1759
1962
|
if (json) {
|
|
1760
1963
|
outputJson({
|
|
1761
1964
|
success: true,
|
|
1762
1965
|
project: { id: project.id, name: project.name, appkey: project.appkey, region: project.region },
|
|
1763
|
-
template
|
|
1966
|
+
template,
|
|
1967
|
+
urls: {
|
|
1968
|
+
dashboard: dashboardUrl,
|
|
1969
|
+
...liveUrl ? { liveSite: liveUrl } : {}
|
|
1970
|
+
}
|
|
1764
1971
|
});
|
|
1765
1972
|
} else {
|
|
1766
|
-
|
|
1973
|
+
clack9.log.step(`Dashboard: ${dashboardUrl}`);
|
|
1974
|
+
if (liveUrl) {
|
|
1975
|
+
clack9.log.success(`Live site: ${liveUrl}`);
|
|
1976
|
+
}
|
|
1977
|
+
clack9.outro("Done!");
|
|
1767
1978
|
}
|
|
1768
1979
|
} catch (err) {
|
|
1769
1980
|
handleError(err, json);
|
|
@@ -1771,7 +1982,7 @@ function registerCreateCommand(program2) {
|
|
|
1771
1982
|
});
|
|
1772
1983
|
}
|
|
1773
1984
|
async function downloadTemplate(framework, projectConfig, projectName, json, _apiUrl) {
|
|
1774
|
-
const s = !json ?
|
|
1985
|
+
const s = !json ? clack9.spinner() : null;
|
|
1775
1986
|
s?.start("Downloading template...");
|
|
1776
1987
|
try {
|
|
1777
1988
|
const anonKey = await getAnonKey();
|
|
@@ -1780,9 +1991,9 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
|
|
|
1780
1991
|
}
|
|
1781
1992
|
const tempDir = tmpdir();
|
|
1782
1993
|
const targetDir = projectName;
|
|
1783
|
-
const templatePath =
|
|
1994
|
+
const templatePath = path2.join(tempDir, targetDir);
|
|
1784
1995
|
try {
|
|
1785
|
-
await
|
|
1996
|
+
await fs2.rm(templatePath, { recursive: true, force: true });
|
|
1786
1997
|
} catch {
|
|
1787
1998
|
}
|
|
1788
1999
|
const frame = framework === "nextjs" ? "nextjs" : "react";
|
|
@@ -1795,14 +2006,14 @@ async function downloadTemplate(framework, projectConfig, projectName, json, _ap
|
|
|
1795
2006
|
s?.message("Copying template files...");
|
|
1796
2007
|
const cwd = process.cwd();
|
|
1797
2008
|
await copyDir(templatePath, cwd);
|
|
1798
|
-
await
|
|
2009
|
+
await fs2.rm(templatePath, { recursive: true, force: true }).catch(() => {
|
|
1799
2010
|
});
|
|
1800
2011
|
s?.stop("Template files downloaded");
|
|
1801
2012
|
} catch (err) {
|
|
1802
2013
|
s?.stop("Template download failed");
|
|
1803
2014
|
if (!json) {
|
|
1804
|
-
|
|
1805
|
-
|
|
2015
|
+
clack9.log.warn(`Failed to download template: ${err.message}`);
|
|
2016
|
+
clack9.log.info("You can manually set up the template later.");
|
|
1806
2017
|
}
|
|
1807
2018
|
}
|
|
1808
2019
|
}
|
|
@@ -1913,156 +2124,6 @@ function registerListCommand(program2) {
|
|
|
1913
2124
|
});
|
|
1914
2125
|
}
|
|
1915
2126
|
|
|
1916
|
-
// src/commands/deployments/deploy.ts
|
|
1917
|
-
import * as path2 from "path";
|
|
1918
|
-
import * as fs2 from "fs/promises";
|
|
1919
|
-
import * as clack9 from "@clack/prompts";
|
|
1920
|
-
import archiver from "archiver";
|
|
1921
|
-
var POLL_INTERVAL_MS = 5e3;
|
|
1922
|
-
var POLL_TIMEOUT_MS = 12e4;
|
|
1923
|
-
var EXCLUDE_PATTERNS = [
|
|
1924
|
-
"node_modules",
|
|
1925
|
-
".git",
|
|
1926
|
-
".next",
|
|
1927
|
-
".env",
|
|
1928
|
-
".env.local",
|
|
1929
|
-
"dist",
|
|
1930
|
-
"build",
|
|
1931
|
-
".DS_Store",
|
|
1932
|
-
".insforge"
|
|
1933
|
-
];
|
|
1934
|
-
function shouldExclude(name) {
|
|
1935
|
-
const normalized = name.replace(/\\/g, "/");
|
|
1936
|
-
for (const pattern of EXCLUDE_PATTERNS) {
|
|
1937
|
-
if (normalized === pattern || normalized.startsWith(pattern + "/") || normalized.endsWith("/" + pattern) || normalized.includes("/" + pattern + "/")) {
|
|
1938
|
-
return true;
|
|
1939
|
-
}
|
|
1940
|
-
}
|
|
1941
|
-
if (normalized.endsWith(".log")) return true;
|
|
1942
|
-
return false;
|
|
1943
|
-
}
|
|
1944
|
-
async function createZipBuffer(sourceDir) {
|
|
1945
|
-
return new Promise((resolve2, reject) => {
|
|
1946
|
-
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
1947
|
-
const chunks = [];
|
|
1948
|
-
archive.on("data", (chunk) => chunks.push(chunk));
|
|
1949
|
-
archive.on("end", () => resolve2(Buffer.concat(chunks)));
|
|
1950
|
-
archive.on("error", (err) => reject(err));
|
|
1951
|
-
archive.directory(sourceDir, false, (entry) => {
|
|
1952
|
-
if (shouldExclude(entry.name)) return false;
|
|
1953
|
-
return entry;
|
|
1954
|
-
});
|
|
1955
|
-
void archive.finalize();
|
|
1956
|
-
});
|
|
1957
|
-
}
|
|
1958
|
-
function registerDeploymentsDeployCommand(deploymentsCmd2) {
|
|
1959
|
-
deploymentsCmd2.command("deploy [directory]").description("Deploy a frontend project to Vercel").option("--env <vars>", `Environment variables as JSON (e.g. '{"KEY":"value"}')`).option("--meta <meta>", "Deployment metadata as JSON").action(async (directory, opts, cmd) => {
|
|
1960
|
-
const { json } = getRootOpts(cmd);
|
|
1961
|
-
try {
|
|
1962
|
-
await requireAuth();
|
|
1963
|
-
const config = getProjectConfig();
|
|
1964
|
-
if (!config) throw new ProjectNotLinkedError();
|
|
1965
|
-
const sourceDir = path2.resolve(directory ?? ".");
|
|
1966
|
-
const stats = await fs2.stat(sourceDir).catch(() => null);
|
|
1967
|
-
if (!stats?.isDirectory()) {
|
|
1968
|
-
throw new CLIError(`"${sourceDir}" is not a valid directory.`);
|
|
1969
|
-
}
|
|
1970
|
-
const s = !json ? clack9.spinner() : null;
|
|
1971
|
-
s?.start("Creating deployment...");
|
|
1972
|
-
const createRes = await ossFetch("/api/deployments", { method: "POST" });
|
|
1973
|
-
const { id: deploymentId, uploadUrl, uploadFields } = await createRes.json();
|
|
1974
|
-
s?.message("Compressing source files...");
|
|
1975
|
-
const zipBuffer = await createZipBuffer(sourceDir);
|
|
1976
|
-
s?.message("Uploading...");
|
|
1977
|
-
const formData = new FormData();
|
|
1978
|
-
for (const [key, value] of Object.entries(uploadFields)) {
|
|
1979
|
-
formData.append(key, value);
|
|
1980
|
-
}
|
|
1981
|
-
formData.append(
|
|
1982
|
-
"file",
|
|
1983
|
-
new Blob([zipBuffer], { type: "application/zip" }),
|
|
1984
|
-
"deployment.zip"
|
|
1985
|
-
);
|
|
1986
|
-
const uploadRes = await fetch(uploadUrl, { method: "POST", body: formData });
|
|
1987
|
-
if (!uploadRes.ok) {
|
|
1988
|
-
const uploadErr = await uploadRes.text();
|
|
1989
|
-
throw new CLIError(`Failed to upload: ${uploadErr}`);
|
|
1990
|
-
}
|
|
1991
|
-
s?.message("Starting deployment...");
|
|
1992
|
-
const startBody = {};
|
|
1993
|
-
if (opts.env) {
|
|
1994
|
-
try {
|
|
1995
|
-
const parsed = JSON.parse(opts.env);
|
|
1996
|
-
if (Array.isArray(parsed)) {
|
|
1997
|
-
startBody.envVars = parsed;
|
|
1998
|
-
} else {
|
|
1999
|
-
startBody.envVars = Object.entries(parsed).map(([key, value]) => ({ key, value }));
|
|
2000
|
-
}
|
|
2001
|
-
} catch {
|
|
2002
|
-
throw new CLIError("Invalid --env JSON.");
|
|
2003
|
-
}
|
|
2004
|
-
}
|
|
2005
|
-
if (opts.meta) {
|
|
2006
|
-
try {
|
|
2007
|
-
startBody.meta = JSON.parse(opts.meta);
|
|
2008
|
-
} catch {
|
|
2009
|
-
throw new CLIError("Invalid --meta JSON.");
|
|
2010
|
-
}
|
|
2011
|
-
}
|
|
2012
|
-
const startRes = await ossFetch(`/api/deployments/${deploymentId}/start`, {
|
|
2013
|
-
method: "POST",
|
|
2014
|
-
body: JSON.stringify(startBody)
|
|
2015
|
-
});
|
|
2016
|
-
await startRes.json();
|
|
2017
|
-
s?.message("Building and deploying...");
|
|
2018
|
-
const startTime = Date.now();
|
|
2019
|
-
let deployment = null;
|
|
2020
|
-
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
2021
|
-
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
2022
|
-
try {
|
|
2023
|
-
const statusRes = await ossFetch(`/api/deployments/${deploymentId}`);
|
|
2024
|
-
deployment = await statusRes.json();
|
|
2025
|
-
if (deployment.status === "ready" || deployment.status === "READY") {
|
|
2026
|
-
break;
|
|
2027
|
-
}
|
|
2028
|
-
if (deployment.status === "error" || deployment.status === "ERROR" || deployment.status === "canceled") {
|
|
2029
|
-
s?.stop("Deployment failed");
|
|
2030
|
-
throw new CLIError(deployment.error ?? `Deployment failed with status: ${deployment.status}`);
|
|
2031
|
-
}
|
|
2032
|
-
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
2033
|
-
s?.message(`Building and deploying... (${elapsed}s, status: ${deployment.status})`);
|
|
2034
|
-
} catch (err) {
|
|
2035
|
-
if (err instanceof CLIError) throw err;
|
|
2036
|
-
}
|
|
2037
|
-
}
|
|
2038
|
-
const isReady = deployment?.status === "ready" || deployment?.status === "READY";
|
|
2039
|
-
if (isReady) {
|
|
2040
|
-
s?.stop("Deployment complete");
|
|
2041
|
-
if (json) {
|
|
2042
|
-
outputJson(deployment);
|
|
2043
|
-
} else {
|
|
2044
|
-
const liveUrl = deployment?.deploymentUrl ?? deployment?.url;
|
|
2045
|
-
if (liveUrl) {
|
|
2046
|
-
clack9.log.success(`Live at: ${liveUrl}`);
|
|
2047
|
-
}
|
|
2048
|
-
clack9.log.info(`Deployment ID: ${deploymentId}`);
|
|
2049
|
-
}
|
|
2050
|
-
} else {
|
|
2051
|
-
s?.stop("Deployment is still building");
|
|
2052
|
-
if (json) {
|
|
2053
|
-
outputJson({ id: deploymentId, status: deployment?.status ?? "building", timedOut: true });
|
|
2054
|
-
} else {
|
|
2055
|
-
clack9.log.info(`Deployment ID: ${deploymentId}`);
|
|
2056
|
-
clack9.log.warn("Deployment did not finish within 2 minutes.");
|
|
2057
|
-
clack9.log.info(`Check status with: insforge deployments status ${deploymentId}`);
|
|
2058
|
-
}
|
|
2059
|
-
}
|
|
2060
|
-
} catch (err) {
|
|
2061
|
-
handleError(err, json);
|
|
2062
|
-
}
|
|
2063
|
-
});
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
2127
|
// src/commands/deployments/list.ts
|
|
2067
2128
|
function registerDeploymentsListCommand(deploymentsCmd2) {
|
|
2068
2129
|
deploymentsCmd2.command("list").description("List all deployments").option("--limit <n>", "Limit number of results", "20").option("--offset <n>", "Offset for pagination", "0").action(async (opts, cmd) => {
|
|
@@ -2355,10 +2416,10 @@ function registerSecretsDeleteCommand(secretsCmd2) {
|
|
|
2355
2416
|
try {
|
|
2356
2417
|
await requireAuth();
|
|
2357
2418
|
if (!yes && !json) {
|
|
2358
|
-
const
|
|
2419
|
+
const confirm6 = await clack11.confirm({
|
|
2359
2420
|
message: `Delete secret "${key}"? This cannot be undone.`
|
|
2360
2421
|
});
|
|
2361
|
-
if (!
|
|
2422
|
+
if (!confirm6 || clack11.isCancel(confirm6)) {
|
|
2362
2423
|
process.exit(0);
|
|
2363
2424
|
}
|
|
2364
2425
|
}
|
|
@@ -2537,10 +2598,10 @@ function registerSchedulesDeleteCommand(schedulesCmd2) {
|
|
|
2537
2598
|
try {
|
|
2538
2599
|
await requireAuth();
|
|
2539
2600
|
if (!yes && !json) {
|
|
2540
|
-
const
|
|
2601
|
+
const confirm6 = await clack12.confirm({
|
|
2541
2602
|
message: `Delete schedule "${id}"? This cannot be undone.`
|
|
2542
2603
|
});
|
|
2543
|
-
if (!
|
|
2604
|
+
if (!confirm6 || clack12.isCancel(confirm6)) {
|
|
2544
2605
|
process.exit(0);
|
|
2545
2606
|
}
|
|
2546
2607
|
}
|