@insforge/cli 0.1.45 → 0.1.46
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 +124 -38
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -106,6 +106,39 @@ var ProjectNotLinkedError = class extends CLIError {
|
|
|
106
106
|
super("No project linked. Run `npx @insforge/cli link` first.", 3, "PROJECT_NOT_LINKED");
|
|
107
107
|
}
|
|
108
108
|
};
|
|
109
|
+
function formatFetchError(err, url) {
|
|
110
|
+
if (!(err instanceof Error)) return `Network request to ${url} failed: ${String(err)}`;
|
|
111
|
+
if (err.message !== "fetch failed") return err.message;
|
|
112
|
+
const cause = err.cause;
|
|
113
|
+
const code = cause?.code ?? cause?.errno;
|
|
114
|
+
const causeMsg = cause instanceof Error ? cause.message : typeof cause === "string" ? cause : void 0;
|
|
115
|
+
let host = url;
|
|
116
|
+
try {
|
|
117
|
+
host = new URL(url).host;
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
switch (code) {
|
|
121
|
+
case "ENOTFOUND":
|
|
122
|
+
case "EAI_AGAIN":
|
|
123
|
+
return `Cannot resolve ${host} (DNS lookup failed: ${code}). Check your internet connection or DNS settings.`;
|
|
124
|
+
case "ECONNREFUSED":
|
|
125
|
+
return `Connection to ${host} refused. The server may be down or blocked by a firewall.`;
|
|
126
|
+
case "ETIMEDOUT":
|
|
127
|
+
case "UND_ERR_CONNECT_TIMEOUT":
|
|
128
|
+
return `Connection to ${host} timed out. Check your network, VPN, or proxy settings.`;
|
|
129
|
+
case "ECONNRESET":
|
|
130
|
+
case "UND_ERR_SOCKET":
|
|
131
|
+
return `Connection to ${host} was reset. A proxy, VPN, or firewall may be interfering.`;
|
|
132
|
+
case "CERT_HAS_EXPIRED":
|
|
133
|
+
case "UNABLE_TO_VERIFY_LEAF_SIGNATURE":
|
|
134
|
+
case "SELF_SIGNED_CERT_IN_CHAIN":
|
|
135
|
+
case "DEPTH_ZERO_SELF_SIGNED_CERT":
|
|
136
|
+
return `TLS certificate error contacting ${host} (${code}). Your network may be intercepting HTTPS (corporate proxy / VPN).`;
|
|
137
|
+
}
|
|
138
|
+
if (code) return `Network error contacting ${host}: ${code}${causeMsg ? ` \u2014 ${causeMsg}` : ""}`;
|
|
139
|
+
if (causeMsg) return `Network error contacting ${host}: ${causeMsg}`;
|
|
140
|
+
return `Network error contacting ${host}.`;
|
|
141
|
+
}
|
|
109
142
|
function getDeploymentError(metadata) {
|
|
110
143
|
if (!metadata || typeof metadata.error !== "object" || !metadata.error) return null;
|
|
111
144
|
return metadata.error.errorMessage ?? null;
|
|
@@ -167,36 +200,48 @@ function buildAuthorizeUrl(params) {
|
|
|
167
200
|
return url.toString();
|
|
168
201
|
}
|
|
169
202
|
async function exchangeCodeForTokens(params) {
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
203
|
+
const tokenUrl = `${params.platformUrl}/api/oauth/v1/token`;
|
|
204
|
+
let res;
|
|
205
|
+
try {
|
|
206
|
+
res = await fetch(tokenUrl, {
|
|
207
|
+
method: "POST",
|
|
208
|
+
headers: { "Content-Type": "application/json" },
|
|
209
|
+
body: JSON.stringify({
|
|
210
|
+
grant_type: "authorization_code",
|
|
211
|
+
code: params.code,
|
|
212
|
+
redirect_uri: params.redirectUri,
|
|
213
|
+
client_id: params.clientId,
|
|
214
|
+
code_verifier: params.codeVerifier
|
|
215
|
+
})
|
|
216
|
+
});
|
|
217
|
+
} catch (err) {
|
|
218
|
+
throw new Error(`Token exchange failed \u2014 ${formatFetchError(err, tokenUrl)}`, { cause: err });
|
|
219
|
+
}
|
|
181
220
|
if (!res.ok) {
|
|
182
221
|
const err = await res.json().catch(() => ({}));
|
|
183
|
-
throw new Error(err.error_description ?? err.error ??
|
|
222
|
+
throw new Error(err.error_description ?? err.error ?? `Token exchange failed (HTTP ${res.status})`);
|
|
184
223
|
}
|
|
185
224
|
return await res.json();
|
|
186
225
|
}
|
|
187
226
|
async function refreshOAuthToken(params) {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
227
|
+
const tokenUrl = `${params.platformUrl}/api/oauth/v1/token`;
|
|
228
|
+
let res;
|
|
229
|
+
try {
|
|
230
|
+
res = await fetch(tokenUrl, {
|
|
231
|
+
method: "POST",
|
|
232
|
+
headers: { "Content-Type": "application/json" },
|
|
233
|
+
body: JSON.stringify({
|
|
234
|
+
grant_type: "refresh_token",
|
|
235
|
+
refresh_token: params.refreshToken,
|
|
236
|
+
client_id: params.clientId
|
|
237
|
+
})
|
|
238
|
+
});
|
|
239
|
+
} catch (err) {
|
|
240
|
+
throw new Error(`Token refresh failed \u2014 ${formatFetchError(err, tokenUrl)}`, { cause: err });
|
|
241
|
+
}
|
|
197
242
|
if (!res.ok) {
|
|
198
243
|
const err = await res.json().catch(() => ({}));
|
|
199
|
-
throw new Error(err.error_description ?? err.error ??
|
|
244
|
+
throw new Error(err.error_description ?? err.error ?? `Token refresh failed (HTTP ${res.status})`);
|
|
200
245
|
}
|
|
201
246
|
return await res.json();
|
|
202
247
|
}
|
|
@@ -394,11 +439,22 @@ async function platformFetch(path5, options = {}, apiUrl) {
|
|
|
394
439
|
Authorization: `Bearer ${token}`,
|
|
395
440
|
...options.headers ?? {}
|
|
396
441
|
};
|
|
397
|
-
const
|
|
442
|
+
const fullUrl = `${baseUrl}${path5}`;
|
|
443
|
+
let res;
|
|
444
|
+
try {
|
|
445
|
+
res = await fetch(fullUrl, { ...options, headers });
|
|
446
|
+
} catch (err) {
|
|
447
|
+
throw new CLIError(formatFetchError(err, fullUrl));
|
|
448
|
+
}
|
|
398
449
|
if (res.status === 401) {
|
|
399
450
|
const newToken = await refreshAccessToken(apiUrl);
|
|
400
451
|
headers.Authorization = `Bearer ${newToken}`;
|
|
401
|
-
|
|
452
|
+
let retryRes;
|
|
453
|
+
try {
|
|
454
|
+
retryRes = await fetch(fullUrl, { ...options, headers });
|
|
455
|
+
} catch (err) {
|
|
456
|
+
throw new CLIError(formatFetchError(err, fullUrl));
|
|
457
|
+
}
|
|
402
458
|
if (!retryRes.ok) {
|
|
403
459
|
const err = await retryRes.json().catch(() => ({}));
|
|
404
460
|
throw new CLIError(err.error ?? `Request failed: ${retryRes.status}`, retryRes.status === 403 ? 5 : 1);
|
|
@@ -709,6 +765,28 @@ import { join as join2 } from "path";
|
|
|
709
765
|
import { promisify } from "util";
|
|
710
766
|
import * as clack5 from "@clack/prompts";
|
|
711
767
|
var execAsync = promisify(exec);
|
|
768
|
+
var SKILL_INSTALL_TIMEOUT_MS = 6e4;
|
|
769
|
+
function describeExecError(err) {
|
|
770
|
+
const e = err;
|
|
771
|
+
if (e.killed && (e.signal === "SIGTERM" || e.signal === "SIGKILL")) {
|
|
772
|
+
return `timed out after ${SKILL_INSTALL_TIMEOUT_MS / 1e3}s \u2014 the npm registry may be slow or blocked by your network`;
|
|
773
|
+
}
|
|
774
|
+
if (e.code === "ENOENT") {
|
|
775
|
+
return "`npx` is not on your PATH \u2014 install Node.js 18+ and reopen your shell";
|
|
776
|
+
}
|
|
777
|
+
const stderr = (typeof e.stderr === "string" ? e.stderr : e.stderr?.toString()) ?? "";
|
|
778
|
+
if (/ENOTFOUND|EAI_AGAIN|getaddrinfo/i.test(stderr)) return "cannot reach the npm registry (DNS lookup failed) \u2014 check your internet connection";
|
|
779
|
+
if (/ECONNREFUSED/i.test(stderr)) return "connection to the npm registry was refused \u2014 a proxy or firewall is likely blocking it";
|
|
780
|
+
if (/ETIMEDOUT|ESOCKETTIMEDOUT|network timeout/i.test(stderr)) return "the npm registry timed out \u2014 check your VPN, proxy, or corporate network";
|
|
781
|
+
if (/CERT_HAS_EXPIRED|UNABLE_TO_VERIFY_LEAF_SIGNATURE|SELF_SIGNED_CERT/i.test(stderr)) return "TLS error reaching the npm registry \u2014 a corporate proxy may be intercepting HTTPS";
|
|
782
|
+
if (/\bE404\b|404 Not Found/i.test(stderr)) return "npm returned 404 \u2014 the `skills` package or a dependency could not be found (check your npm registry config)";
|
|
783
|
+
if (/EACCES|permission denied/i.test(stderr)) return "permission denied writing files \u2014 run from a directory you own, without sudo";
|
|
784
|
+
if (/ENOSPC|no space left/i.test(stderr)) return "no disk space left to install the package";
|
|
785
|
+
if (/\b401\b|EAUTH|authentication/i.test(stderr)) return "npm authentication failed \u2014 check ~/.npmrc";
|
|
786
|
+
if (typeof e.code === "number") return `npx exited with code ${e.code}`;
|
|
787
|
+
if (typeof e.code === "string") return e.code;
|
|
788
|
+
return e.message ?? "unknown error";
|
|
789
|
+
}
|
|
712
790
|
var GITIGNORE_ENTRIES = [
|
|
713
791
|
".insforge",
|
|
714
792
|
".agent",
|
|
@@ -741,33 +819,41 @@ async function installSkills(json) {
|
|
|
741
819
|
if (!json) clack5.log.info("Installing InsForge agent skills...");
|
|
742
820
|
await execAsync("npx skills add insforge/agent-skills -y -a antigravity -a augment -a claude-code -a cline -a codex -a cursor -a gemini-cli -a github-copilot -a kilo -a qoder -a qwen-code -a roo -a trae -a windsurf", {
|
|
743
821
|
cwd: process.cwd(),
|
|
744
|
-
timeout:
|
|
822
|
+
timeout: SKILL_INSTALL_TIMEOUT_MS
|
|
745
823
|
});
|
|
746
824
|
if (!json) clack5.log.success("InsForge agent skills installed.");
|
|
747
|
-
} catch {
|
|
748
|
-
if (!json)
|
|
825
|
+
} catch (err) {
|
|
826
|
+
if (!json) {
|
|
827
|
+
clack5.log.warn(`Could not install agent skills: ${describeExecError(err)}`);
|
|
828
|
+
clack5.log.info("Run `npx skills add insforge/agent-skills` once resolved to see the full output.");
|
|
829
|
+
}
|
|
749
830
|
}
|
|
750
831
|
try {
|
|
751
832
|
if (!json) clack5.log.info("Installing find-skills...");
|
|
752
833
|
await execAsync("npx skills add https://github.com/vercel-labs/skills --skill find-skills -y", {
|
|
753
834
|
cwd: process.cwd(),
|
|
754
|
-
timeout:
|
|
835
|
+
timeout: SKILL_INSTALL_TIMEOUT_MS
|
|
755
836
|
});
|
|
756
837
|
if (!json) clack5.log.success("find-skills installed.");
|
|
757
|
-
} catch {
|
|
758
|
-
if (!json)
|
|
838
|
+
} catch (err) {
|
|
839
|
+
if (!json) {
|
|
840
|
+
clack5.log.warn(`Could not install find-skills: ${describeExecError(err)}`);
|
|
841
|
+
clack5.log.info("Run `npx skills add https://github.com/vercel-labs/skills --skill find-skills` once resolved.");
|
|
842
|
+
}
|
|
759
843
|
}
|
|
760
844
|
try {
|
|
761
845
|
updateGitignore();
|
|
762
846
|
} catch {
|
|
763
847
|
}
|
|
764
848
|
}
|
|
765
|
-
async function reportCliUsage(toolName, success, maxRetries = 1) {
|
|
766
|
-
let config;
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
849
|
+
async function reportCliUsage(toolName, success, maxRetries = 1, explicitConfig) {
|
|
850
|
+
let config = explicitConfig;
|
|
851
|
+
if (!config) {
|
|
852
|
+
try {
|
|
853
|
+
config = getProjectConfig();
|
|
854
|
+
} catch {
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
771
857
|
}
|
|
772
858
|
if (!config) return;
|
|
773
859
|
const payload = JSON.stringify({
|
|
@@ -1650,7 +1736,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
1650
1736
|
}
|
|
1651
1737
|
trackCommand("link", "oss-org", { direct: true });
|
|
1652
1738
|
await installSkills(json);
|
|
1653
|
-
await reportCliUsage("cli.link_direct", true, 6);
|
|
1739
|
+
await reportCliUsage("cli.link_direct", true, 6, projectConfig2);
|
|
1654
1740
|
try {
|
|
1655
1741
|
const urlMatch = opts.apiBaseUrl.match(/^https?:\/\/([^.]+)\.[^.]+\.insforge\.app/);
|
|
1656
1742
|
if (urlMatch) {
|
|
@@ -1748,7 +1834,7 @@ function registerProjectLinkCommand(program2) {
|
|
|
1748
1834
|
outputSuccess(`Linked to project "${project.name}" (${project.appkey}.${project.region})`);
|
|
1749
1835
|
}
|
|
1750
1836
|
await installSkills(json);
|
|
1751
|
-
await reportCliUsage("cli.link", true, 6);
|
|
1837
|
+
await reportCliUsage("cli.link", true, 6, projectConfig);
|
|
1752
1838
|
try {
|
|
1753
1839
|
await reportAgentConnected({ project_id: project.id }, apiUrl);
|
|
1754
1840
|
} catch {
|