@insforge/cli 0.1.46 → 0.1.48
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 +316 -97
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { readFileSync as readFileSync7 } from "fs";
|
|
|
5
5
|
import { join as join9, dirname } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { Command } from "commander";
|
|
8
|
-
import * as
|
|
8
|
+
import * as clack15 from "@clack/prompts";
|
|
9
9
|
|
|
10
10
|
// src/lib/config.ts
|
|
11
11
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
@@ -16,6 +16,8 @@ var CREDENTIALS_FILE = join(GLOBAL_DIR, "credentials.json");
|
|
|
16
16
|
var CONFIG_FILE = join(GLOBAL_DIR, "config.json");
|
|
17
17
|
var DEFAULT_PLATFORM_URL = "https://api.insforge.dev";
|
|
18
18
|
var DEFAULT_FRONTEND_URL = "https://insforge.dev";
|
|
19
|
+
var FAKE_PROJECT_ID = "fa4e0000-1234-5678-90ab-cd1234567890";
|
|
20
|
+
var FAKE_ORG_ID = "fa4e0001-1234-5678-90ab-cd1234567890";
|
|
19
21
|
function ensureGlobalDir() {
|
|
20
22
|
if (!existsSync(GLOBAL_DIR)) {
|
|
21
23
|
mkdirSync(GLOBAL_DIR, { recursive: true });
|
|
@@ -365,7 +367,7 @@ ${authUrl}`);
|
|
|
365
367
|
import * as clack2 from "@clack/prompts";
|
|
366
368
|
async function requireAuth(apiUrl, allowOssBypass = true) {
|
|
367
369
|
const projConfig = getProjectConfig();
|
|
368
|
-
if (allowOssBypass && projConfig?.project_id ===
|
|
370
|
+
if (allowOssBypass && projConfig?.project_id === FAKE_PROJECT_ID) {
|
|
369
371
|
return {
|
|
370
372
|
access_token: "oss-token",
|
|
371
373
|
refresh_token: "oss-refresh",
|
|
@@ -440,6 +442,13 @@ async function platformFetch(path5, options = {}, apiUrl) {
|
|
|
440
442
|
...options.headers ?? {}
|
|
441
443
|
};
|
|
442
444
|
const fullUrl = `${baseUrl}${path5}`;
|
|
445
|
+
if (process.env.INSFORGE_DEBUG) {
|
|
446
|
+
console.error(`[DEBUG] ${options.method ?? "GET"} ${fullUrl}`);
|
|
447
|
+
console.error(`[DEBUG] Headers: ${JSON.stringify(headers, null, 2)}`);
|
|
448
|
+
if (options.body) {
|
|
449
|
+
console.error(`[DEBUG] Body: ${typeof options.body === "string" ? options.body : JSON.stringify(options.body)}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
443
452
|
let res;
|
|
444
453
|
try {
|
|
445
454
|
res = await fetch(fullUrl, { ...options, headers });
|
|
@@ -463,7 +472,8 @@ async function platformFetch(path5, options = {}, apiUrl) {
|
|
|
463
472
|
}
|
|
464
473
|
if (!res.ok) {
|
|
465
474
|
const err = await res.json().catch(() => ({}));
|
|
466
|
-
|
|
475
|
+
const msg = err.message ? `${err.error ?? res.status}: ${err.message}` : err.error ?? `Request failed: ${res.status}`;
|
|
476
|
+
throw new CLIError(msg, res.status === 403 ? 5 : 1);
|
|
467
477
|
}
|
|
468
478
|
return res;
|
|
469
479
|
}
|
|
@@ -527,6 +537,56 @@ async function reportAgentConnected(payload, apiUrl) {
|
|
|
527
537
|
body: JSON.stringify(payload)
|
|
528
538
|
});
|
|
529
539
|
}
|
|
540
|
+
async function streamDiagnosticAnalysis(payload, onEvent, apiUrl) {
|
|
541
|
+
const res = await platformFetch("/diagnostic/v1/analyze", {
|
|
542
|
+
method: "POST",
|
|
543
|
+
body: JSON.stringify(payload)
|
|
544
|
+
}, apiUrl);
|
|
545
|
+
const body = res.body;
|
|
546
|
+
if (!body) throw new CLIError("No response body from diagnostic API.");
|
|
547
|
+
const reader = body.getReader();
|
|
548
|
+
const decoder = new TextDecoder();
|
|
549
|
+
let buffer = "";
|
|
550
|
+
let currentEvent = "delta";
|
|
551
|
+
const VALID_EVENTS = /* @__PURE__ */ new Set(["delta", "tool_call", "tool_result", "done", "error"]);
|
|
552
|
+
const processLine = (line) => {
|
|
553
|
+
if (line.startsWith("event:")) {
|
|
554
|
+
const evt = line.slice(6).trim();
|
|
555
|
+
currentEvent = VALID_EVENTS.has(evt) ? evt : null;
|
|
556
|
+
} else if (line.startsWith("data:")) {
|
|
557
|
+
if (!currentEvent) return;
|
|
558
|
+
const raw = line.slice(5).trim();
|
|
559
|
+
if (!raw) return;
|
|
560
|
+
try {
|
|
561
|
+
const data = JSON.parse(raw);
|
|
562
|
+
onEvent({ type: currentEvent, data });
|
|
563
|
+
} catch {
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
for (; ; ) {
|
|
568
|
+
const { done, value } = await reader.read();
|
|
569
|
+
if (done) break;
|
|
570
|
+
buffer += decoder.decode(value, { stream: true });
|
|
571
|
+
const lines = buffer.split("\n");
|
|
572
|
+
buffer = lines.pop() ?? "";
|
|
573
|
+
for (const line of lines) {
|
|
574
|
+
processLine(line);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
buffer += decoder.decode();
|
|
578
|
+
if (buffer.trim()) {
|
|
579
|
+
processLine(buffer);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
async function rateDiagnosticSession(sessionId, rating, comment, apiUrl) {
|
|
583
|
+
const body = { rating };
|
|
584
|
+
if (comment) body.comment = comment;
|
|
585
|
+
await platformFetch(`/diagnostic/v1/sessions/${sessionId}/rating`, {
|
|
586
|
+
method: "POST",
|
|
587
|
+
body: JSON.stringify(body)
|
|
588
|
+
}, apiUrl);
|
|
589
|
+
}
|
|
530
590
|
async function createProject(orgId, name, region, apiUrl) {
|
|
531
591
|
const body = { name };
|
|
532
592
|
if (region) body.region = region;
|
|
@@ -918,7 +978,7 @@ function trackDiagnose(subcommand, config) {
|
|
|
918
978
|
project_name: config.project_name,
|
|
919
979
|
org_id: config.org_id,
|
|
920
980
|
region: config.region,
|
|
921
|
-
oss_mode: config.project_id ===
|
|
981
|
+
oss_mode: config.project_id === FAKE_PROJECT_ID
|
|
922
982
|
});
|
|
923
983
|
}
|
|
924
984
|
async function shutdownAnalytics() {
|
|
@@ -1719,11 +1779,11 @@ function registerProjectLinkCommand(program2) {
|
|
|
1719
1779
|
throw new CLIError("Invalid --api-base-url. Please provide a valid URL.");
|
|
1720
1780
|
}
|
|
1721
1781
|
const projectConfig2 = {
|
|
1722
|
-
project_id:
|
|
1782
|
+
project_id: FAKE_PROJECT_ID,
|
|
1723
1783
|
project_name: "oss-project",
|
|
1724
|
-
org_id:
|
|
1725
|
-
appkey: "
|
|
1726
|
-
region: "
|
|
1784
|
+
org_id: FAKE_ORG_ID,
|
|
1785
|
+
appkey: "ossfkey",
|
|
1786
|
+
region: "us-test",
|
|
1727
1787
|
api_key: opts.apiKey,
|
|
1728
1788
|
oss_host: opts.apiBaseUrl.replace(/\/$/, "")
|
|
1729
1789
|
// remove trailing slash if any
|
|
@@ -1833,8 +1893,6 @@ function registerProjectLinkCommand(program2) {
|
|
|
1833
1893
|
} else {
|
|
1834
1894
|
outputSuccess(`Linked to project "${project.name}" (${project.appkey}.${project.region})`);
|
|
1835
1895
|
}
|
|
1836
|
-
await installSkills(json);
|
|
1837
|
-
await reportCliUsage("cli.link", true, 6, projectConfig);
|
|
1838
1896
|
try {
|
|
1839
1897
|
await reportAgentConnected({ project_id: project.id }, apiUrl);
|
|
1840
1898
|
} catch {
|
|
@@ -1891,6 +1949,8 @@ function registerProjectLinkCommand(program2) {
|
|
|
1891
1949
|
clack8.log.info("Run `npm install` manually to install dependencies.");
|
|
1892
1950
|
}
|
|
1893
1951
|
}
|
|
1952
|
+
await installSkills(json);
|
|
1953
|
+
await reportCliUsage("cli.link", true, 6, projectConfig);
|
|
1894
1954
|
if (!json) {
|
|
1895
1955
|
const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
|
|
1896
1956
|
clack8.log.step(`Dashboard: ${dashboardUrl}`);
|
|
@@ -1902,20 +1962,24 @@ function registerProjectLinkCommand(program2) {
|
|
|
1902
1962
|
clack8.log.warn("Template download failed. You can retry or set up manually.");
|
|
1903
1963
|
}
|
|
1904
1964
|
}
|
|
1905
|
-
} else
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1965
|
+
} else {
|
|
1966
|
+
await installSkills(json);
|
|
1967
|
+
await reportCliUsage("cli.link", true, 6, projectConfig);
|
|
1968
|
+
if (!json) {
|
|
1969
|
+
const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
|
|
1970
|
+
clack8.log.step(`Dashboard: ${dashboardUrl}`);
|
|
1971
|
+
const prompts = [
|
|
1972
|
+
"Build a todo app with Google OAuth sign-in",
|
|
1973
|
+
"Build an Instagram clone where users can upload photos, like, and comment",
|
|
1974
|
+
"Build an AI chatbot with conversation history and deploy it to a live URL"
|
|
1975
|
+
];
|
|
1976
|
+
clack8.note(
|
|
1977
|
+
`Open your coding agent (Claude Code, Codex, Cursor, etc.) and try:
|
|
1915
1978
|
|
|
1916
1979
|
${prompts.map((p) => `\u2022 "${p}"`).join("\n")}`,
|
|
1917
|
-
|
|
1918
|
-
|
|
1980
|
+
"Start building"
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1919
1983
|
}
|
|
1920
1984
|
} catch (err) {
|
|
1921
1985
|
await reportCliUsage("cli.link", false);
|
|
@@ -4109,6 +4173,10 @@ function formatSize2(gb) {
|
|
|
4109
4173
|
return `${gb.toFixed(2)} GB`;
|
|
4110
4174
|
}
|
|
4111
4175
|
|
|
4176
|
+
// src/commands/diagnose/index.ts
|
|
4177
|
+
import * as os from "os";
|
|
4178
|
+
import * as clack14 from "@clack/prompts";
|
|
4179
|
+
|
|
4112
4180
|
// src/commands/diagnose/metrics.ts
|
|
4113
4181
|
var METRIC_LABELS = {
|
|
4114
4182
|
cpu_usage: "CPU Usage",
|
|
@@ -4175,7 +4243,7 @@ function registerDiagnoseMetricsCommand(diagnoseCmd2) {
|
|
|
4175
4243
|
await requireAuth(apiUrl);
|
|
4176
4244
|
const config = getProjectConfig();
|
|
4177
4245
|
if (!config) throw new ProjectNotLinkedError();
|
|
4178
|
-
if (config.project_id ===
|
|
4246
|
+
if (config.project_id === FAKE_PROJECT_ID) {
|
|
4179
4247
|
throw new CLIError(
|
|
4180
4248
|
"Metrics requires InsForge Platform login. Not available when linked via --api-key."
|
|
4181
4249
|
);
|
|
@@ -4239,7 +4307,7 @@ function registerDiagnoseAdvisorCommand(diagnoseCmd2) {
|
|
|
4239
4307
|
await requireAuth(apiUrl);
|
|
4240
4308
|
const config = getProjectConfig();
|
|
4241
4309
|
if (!config) throw new ProjectNotLinkedError();
|
|
4242
|
-
if (config.project_id ===
|
|
4310
|
+
if (config.project_id === FAKE_PROJECT_ID) {
|
|
4243
4311
|
throw new CLIError(
|
|
4244
4312
|
"Advisor requires InsForge Platform login. Not available when linked via --api-key."
|
|
4245
4313
|
);
|
|
@@ -4577,82 +4645,234 @@ function registerDiagnoseLogsCommand(diagnoseCmd2) {
|
|
|
4577
4645
|
function sectionHeader(title) {
|
|
4578
4646
|
return `\u2500\u2500 ${title} ${"\u2500".repeat(Math.max(0, 44 - title.length))}`;
|
|
4579
4647
|
}
|
|
4648
|
+
async function collectDiagnosticData(projectId, ossMode, apiUrl) {
|
|
4649
|
+
const metricsPromise = ossMode ? Promise.reject(new Error("Platform login required (linked via --api-key)")) : fetchMetricsSummary(projectId, apiUrl);
|
|
4650
|
+
const advisorPromise = ossMode ? Promise.reject(new Error("Platform login required (linked via --api-key)")) : fetchAdvisorSummary(projectId, apiUrl);
|
|
4651
|
+
const [metricsResult, advisorResult, dbResult, logsResult] = await Promise.allSettled([
|
|
4652
|
+
metricsPromise,
|
|
4653
|
+
advisorPromise,
|
|
4654
|
+
runDbChecks(),
|
|
4655
|
+
fetchLogsSummary(100)
|
|
4656
|
+
]);
|
|
4657
|
+
const errors = [];
|
|
4658
|
+
let metrics = null;
|
|
4659
|
+
let advisor = null;
|
|
4660
|
+
let db = null;
|
|
4661
|
+
let logs = null;
|
|
4662
|
+
if (metricsResult.status === "fulfilled") {
|
|
4663
|
+
const data = metricsResult.value;
|
|
4664
|
+
metrics = data.metrics.filter((m) => m.data.length > 0).map((m) => {
|
|
4665
|
+
let sum = 0;
|
|
4666
|
+
let max = -Infinity;
|
|
4667
|
+
for (const d of m.data) {
|
|
4668
|
+
sum += d.value;
|
|
4669
|
+
if (d.value > max) max = d.value;
|
|
4670
|
+
}
|
|
4671
|
+
return {
|
|
4672
|
+
metric: m.metric,
|
|
4673
|
+
latest: m.data[m.data.length - 1].value,
|
|
4674
|
+
avg: sum / m.data.length,
|
|
4675
|
+
max
|
|
4676
|
+
};
|
|
4677
|
+
});
|
|
4678
|
+
} else {
|
|
4679
|
+
errors.push(metricsResult.reason?.message ?? "Metrics unavailable");
|
|
4680
|
+
}
|
|
4681
|
+
if (advisorResult.status === "fulfilled") {
|
|
4682
|
+
advisor = advisorResult.value;
|
|
4683
|
+
} else {
|
|
4684
|
+
errors.push(advisorResult.reason?.message ?? "Advisor unavailable");
|
|
4685
|
+
}
|
|
4686
|
+
if (dbResult.status === "fulfilled") {
|
|
4687
|
+
db = dbResult.value;
|
|
4688
|
+
} else {
|
|
4689
|
+
errors.push(dbResult.reason?.message ?? "DB checks unavailable");
|
|
4690
|
+
}
|
|
4691
|
+
if (logsResult.status === "fulfilled") {
|
|
4692
|
+
logs = logsResult.value;
|
|
4693
|
+
} else {
|
|
4694
|
+
errors.push(logsResult.reason?.message ?? "Logs unavailable");
|
|
4695
|
+
}
|
|
4696
|
+
return { metrics, advisor, db, logs, errors };
|
|
4697
|
+
}
|
|
4580
4698
|
function registerDiagnoseCommands(diagnoseCmd2) {
|
|
4581
|
-
diagnoseCmd2.description("Backend diagnostics \u2014 run with no subcommand for a full health report").action(async (
|
|
4699
|
+
diagnoseCmd2.description("Backend diagnostics \u2014 run with no subcommand for a full health report").option("--ai <question>", "Ask AI to analyze your diagnostic data (1-2000 chars)").action(async (opts, cmd) => {
|
|
4582
4700
|
const { json, apiUrl } = getRootOpts(cmd);
|
|
4701
|
+
const usageEvent = opts.ai ? "cli.diagnose.ai" : "cli.diagnose";
|
|
4583
4702
|
try {
|
|
4584
4703
|
await requireAuth(apiUrl);
|
|
4585
4704
|
const config = getProjectConfig();
|
|
4586
4705
|
if (!config) throw new ProjectNotLinkedError();
|
|
4587
4706
|
const projectId = config.project_id;
|
|
4588
4707
|
const projectName = config.project_name;
|
|
4589
|
-
const ossMode = config.project_id ===
|
|
4590
|
-
trackDiagnose("report", config);
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4708
|
+
const ossMode = config.project_id === FAKE_PROJECT_ID;
|
|
4709
|
+
trackDiagnose(opts.ai ? "ai" : "report", config);
|
|
4710
|
+
if (opts.ai) {
|
|
4711
|
+
const question = String(opts.ai).trim();
|
|
4712
|
+
if (question.length === 0 || question.length > 2e3) {
|
|
4713
|
+
throw new CLIError("Question must be between 1 and 2000 characters.");
|
|
4714
|
+
}
|
|
4715
|
+
const s = !json ? clack14.spinner() : null;
|
|
4716
|
+
s?.start("Collecting diagnostic data...");
|
|
4717
|
+
const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
4718
|
+
const cliVersion = "0.1.48";
|
|
4719
|
+
s?.stop("Data collected");
|
|
4720
|
+
if (!json) {
|
|
4721
|
+
console.log(`
|
|
4722
|
+
AI Diagnosis \u2014 ${projectName}
|
|
4723
|
+
`);
|
|
4724
|
+
console.log(sectionHeader("Question"));
|
|
4725
|
+
console.log(` ${question}
|
|
4726
|
+
`);
|
|
4727
|
+
console.log(sectionHeader("Analysis"));
|
|
4728
|
+
}
|
|
4729
|
+
let sessionId;
|
|
4730
|
+
let fullText = "";
|
|
4731
|
+
const jsonEvents = [];
|
|
4732
|
+
let streamError;
|
|
4733
|
+
const context = {
|
|
4734
|
+
context_version: "diagnostic-v1",
|
|
4735
|
+
client_info: {
|
|
4736
|
+
cli_version: cliVersion,
|
|
4737
|
+
node_version: process.version,
|
|
4738
|
+
os: `${os.platform()} ${os.release()}`
|
|
4739
|
+
}
|
|
4740
|
+
};
|
|
4741
|
+
if (Array.isArray(data2.metrics) && data2.metrics.length > 0) {
|
|
4742
|
+
context.metrics = data2.metrics;
|
|
4743
|
+
}
|
|
4744
|
+
if (data2.advisor) {
|
|
4745
|
+
const adv = data2.advisor;
|
|
4746
|
+
const summary = adv.summary;
|
|
4747
|
+
const rawErrors = adv.collectorErrors;
|
|
4748
|
+
if (summary) {
|
|
4749
|
+
context.advisor = {
|
|
4750
|
+
summary: {
|
|
4751
|
+
total: summary.total ?? 0,
|
|
4752
|
+
critical: summary.critical ?? 0,
|
|
4753
|
+
warning: summary.warning ?? 0,
|
|
4754
|
+
info: summary.info ?? 0
|
|
4755
|
+
},
|
|
4756
|
+
collectorErrors: rawErrors?.map(
|
|
4757
|
+
(e) => typeof e === "string" ? e : JSON.stringify(e)
|
|
4758
|
+
) ?? []
|
|
4617
4759
|
};
|
|
4618
|
-
}
|
|
4619
|
-
} else {
|
|
4620
|
-
report.metrics = null;
|
|
4621
|
-
errors.push(metricsResult.reason?.message ?? "Metrics unavailable");
|
|
4760
|
+
}
|
|
4622
4761
|
}
|
|
4623
|
-
if (
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4762
|
+
if (data2.db) {
|
|
4763
|
+
const rawDb = data2.db;
|
|
4764
|
+
const safeDb = {};
|
|
4765
|
+
for (const [key, rows] of Object.entries(rawDb)) {
|
|
4766
|
+
safeDb[key] = rows.map((row) => {
|
|
4767
|
+
const out = {};
|
|
4768
|
+
for (const [k, v] of Object.entries(row)) {
|
|
4769
|
+
out[k] = v === null || v === void 0 ? "" : String(v);
|
|
4770
|
+
}
|
|
4771
|
+
return out;
|
|
4772
|
+
});
|
|
4773
|
+
}
|
|
4774
|
+
if (Object.keys(safeDb).length > 0) {
|
|
4775
|
+
context.db = safeDb;
|
|
4776
|
+
}
|
|
4628
4777
|
}
|
|
4629
|
-
if (
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4778
|
+
if (Array.isArray(data2.logs) && data2.logs.length > 0) {
|
|
4779
|
+
context.logs = data2.logs.map((s2) => ({
|
|
4780
|
+
source: s2.source,
|
|
4781
|
+
total: s2.total,
|
|
4782
|
+
errors: s2.errors.map((e) => ({
|
|
4783
|
+
timestamp: e.timestamp ?? "",
|
|
4784
|
+
message: e.message ?? "",
|
|
4785
|
+
source: e.source ?? ""
|
|
4786
|
+
}))
|
|
4787
|
+
}));
|
|
4634
4788
|
}
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4789
|
+
await streamDiagnosticAnalysis({
|
|
4790
|
+
project_id: projectId,
|
|
4791
|
+
question,
|
|
4792
|
+
context
|
|
4793
|
+
}, (event) => {
|
|
4794
|
+
if (event.type === "done") {
|
|
4795
|
+
sessionId = event.data.session_id;
|
|
4796
|
+
}
|
|
4797
|
+
if (event.type === "error") {
|
|
4798
|
+
streamError = new CLIError(String(event.data.message ?? "Unknown diagnostic error"));
|
|
4799
|
+
}
|
|
4800
|
+
if (json) {
|
|
4801
|
+
jsonEvents.push({ type: event.type, ...event.data });
|
|
4802
|
+
return;
|
|
4803
|
+
}
|
|
4804
|
+
switch (event.type) {
|
|
4805
|
+
case "delta":
|
|
4806
|
+
process.stdout.write(String(event.data.text ?? ""));
|
|
4807
|
+
fullText += String(event.data.text ?? "");
|
|
4808
|
+
break;
|
|
4809
|
+
case "tool_call":
|
|
4810
|
+
console.log(`
|
|
4811
|
+
[calling ${event.data.tool_name}...]`);
|
|
4812
|
+
break;
|
|
4813
|
+
case "tool_result":
|
|
4814
|
+
break;
|
|
4815
|
+
case "done":
|
|
4816
|
+
break;
|
|
4817
|
+
case "error":
|
|
4818
|
+
console.error(`
|
|
4819
|
+
Error: ${streamError?.message ?? "Unknown error"}`);
|
|
4820
|
+
break;
|
|
4821
|
+
}
|
|
4822
|
+
}, apiUrl);
|
|
4823
|
+
if (streamError) {
|
|
4824
|
+
throw streamError;
|
|
4825
|
+
}
|
|
4826
|
+
if (!json) {
|
|
4827
|
+
if (fullText && !fullText.endsWith("\n")) console.log("");
|
|
4828
|
+
console.log("");
|
|
4829
|
+
}
|
|
4830
|
+
if (json) {
|
|
4831
|
+
outputJson({ sessionId, events: jsonEvents });
|
|
4640
4832
|
}
|
|
4641
|
-
|
|
4642
|
-
|
|
4833
|
+
if (!json && sessionId) {
|
|
4834
|
+
const ratingChoice = await clack14.select({
|
|
4835
|
+
message: "Was this analysis helpful?",
|
|
4836
|
+
options: [
|
|
4837
|
+
{ value: "skip", label: "Skip", hint: "no rating" },
|
|
4838
|
+
{ value: "helpful", label: "Helpful", hint: "solved or pointed in right direction" },
|
|
4839
|
+
{ value: "not_helpful", label: "Not helpful", hint: "didn't apply to the problem" },
|
|
4840
|
+
{ value: "incorrect", label: "Incorrect", hint: "diagnosis was wrong or misleading" }
|
|
4841
|
+
]
|
|
4842
|
+
});
|
|
4843
|
+
if (!clack14.isCancel(ratingChoice) && ratingChoice !== "skip") {
|
|
4844
|
+
try {
|
|
4845
|
+
await rateDiagnosticSession(
|
|
4846
|
+
sessionId,
|
|
4847
|
+
ratingChoice,
|
|
4848
|
+
void 0,
|
|
4849
|
+
apiUrl
|
|
4850
|
+
);
|
|
4851
|
+
clack14.log.success("Thanks for your feedback!");
|
|
4852
|
+
} catch {
|
|
4853
|
+
clack14.log.warn("Failed to submit rating.");
|
|
4854
|
+
}
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4857
|
+
await reportCliUsage(usageEvent, true);
|
|
4858
|
+
return;
|
|
4859
|
+
}
|
|
4860
|
+
const data = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
4861
|
+
if (json) {
|
|
4862
|
+
outputJson({ project: projectName, ...data });
|
|
4643
4863
|
} else {
|
|
4644
4864
|
console.log(`
|
|
4645
4865
|
InsForge Health Report \u2014 ${projectName}
|
|
4646
4866
|
`);
|
|
4647
4867
|
console.log(sectionHeader("System Metrics (last 1h)"));
|
|
4648
|
-
if (
|
|
4649
|
-
const
|
|
4650
|
-
if (
|
|
4868
|
+
if (data.metrics) {
|
|
4869
|
+
const metricsArr = data.metrics;
|
|
4870
|
+
if (metricsArr.length === 0) {
|
|
4651
4871
|
console.log(" No metrics data available.");
|
|
4652
4872
|
} else {
|
|
4653
4873
|
const vals = {};
|
|
4654
|
-
for (const m of
|
|
4655
|
-
|
|
4874
|
+
for (const m of metricsArr) {
|
|
4875
|
+
vals[m.metric] = m.latest;
|
|
4656
4876
|
}
|
|
4657
4877
|
const cpu = vals.cpu_usage !== void 0 ? `${vals.cpu_usage.toFixed(1)}%` : "N/A";
|
|
4658
4878
|
const mem = vals.memory_usage !== void 0 ? `${vals.memory_usage.toFixed(1)}%` : "N/A";
|
|
@@ -4663,20 +4883,19 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
4663
4883
|
console.log(` Disk: ${disk} Network: \u2193${netIn} \u2191${netOut}`);
|
|
4664
4884
|
}
|
|
4665
4885
|
} else {
|
|
4666
|
-
console.log(` N/A \u2014 ${
|
|
4886
|
+
console.log(` N/A \u2014 ${data.errors.find((e) => e.includes("Metrics") || e.includes("Platform")) ?? "unavailable"}`);
|
|
4667
4887
|
}
|
|
4668
4888
|
console.log("\n" + sectionHeader("Advisor Scan"));
|
|
4669
|
-
if (
|
|
4670
|
-
const scan =
|
|
4671
|
-
const s = scan.summary;
|
|
4889
|
+
if (data.advisor) {
|
|
4890
|
+
const scan = data.advisor;
|
|
4672
4891
|
const date = new Date(scan.scannedAt).toLocaleDateString();
|
|
4673
|
-
console.log(` ${date} (${scan.status}) \u2014 ${
|
|
4892
|
+
console.log(` ${date} (${scan.status}) \u2014 ${scan.summary.critical} critical \xB7 ${scan.summary.warning} warning \xB7 ${scan.summary.info} info`);
|
|
4674
4893
|
} else {
|
|
4675
|
-
console.log(` N/A \u2014 ${
|
|
4894
|
+
console.log(` N/A \u2014 ${data.errors.find((e) => e.includes("Advisor") || e.includes("Platform")) ?? "unavailable"}`);
|
|
4676
4895
|
}
|
|
4677
4896
|
console.log("\n" + sectionHeader("Database"));
|
|
4678
|
-
if (
|
|
4679
|
-
const db =
|
|
4897
|
+
if (data.db) {
|
|
4898
|
+
const db = data.db;
|
|
4680
4899
|
const conn = db.connections?.[0];
|
|
4681
4900
|
const cache = db["cache-hit"]?.[0];
|
|
4682
4901
|
const deadTuples = (db.bloat ?? []).reduce(
|
|
@@ -4691,21 +4910,21 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
4691
4910
|
` Dead tuples: ${deadTuples.toLocaleString()} Locks waiting: ${lockCount}`
|
|
4692
4911
|
);
|
|
4693
4912
|
} else {
|
|
4694
|
-
console.log(` N/A \u2014 ${
|
|
4913
|
+
console.log(` N/A \u2014 ${data.errors.find((e) => e.includes("DB")) ?? "unavailable"}`);
|
|
4695
4914
|
}
|
|
4696
4915
|
console.log("\n" + sectionHeader("Recent Errors (last 100 logs/source)"));
|
|
4697
|
-
if (
|
|
4698
|
-
const summaries =
|
|
4916
|
+
if (data.logs) {
|
|
4917
|
+
const summaries = data.logs;
|
|
4699
4918
|
const parts = summaries.map((s) => `${s.source}: ${s.errors.length}`);
|
|
4700
4919
|
console.log(` ${parts.join(" ")}`);
|
|
4701
4920
|
} else {
|
|
4702
|
-
console.log(` N/A \u2014 ${
|
|
4921
|
+
console.log(` N/A \u2014 ${data.errors.find((e) => e.includes("Logs")) ?? "unavailable"}`);
|
|
4703
4922
|
}
|
|
4704
4923
|
console.log("");
|
|
4705
4924
|
}
|
|
4706
|
-
await reportCliUsage(
|
|
4925
|
+
await reportCliUsage(usageEvent, true);
|
|
4707
4926
|
} catch (err) {
|
|
4708
|
-
await reportCliUsage(
|
|
4927
|
+
await reportCliUsage(usageEvent, false);
|
|
4709
4928
|
handleError(err, json);
|
|
4710
4929
|
} finally {
|
|
4711
4930
|
await shutdownAnalytics();
|
|
@@ -4826,7 +5045,7 @@ async function showInteractiveMenu() {
|
|
|
4826
5045
|
} catch {
|
|
4827
5046
|
}
|
|
4828
5047
|
console.log(INSFORGE_LOGO);
|
|
4829
|
-
|
|
5048
|
+
clack15.intro(`InsForge CLI v${pkg.version}`);
|
|
4830
5049
|
const options = [];
|
|
4831
5050
|
if (!isLoggedIn) {
|
|
4832
5051
|
options.push({ value: "login", label: "Log in to InsForge" });
|
|
@@ -4842,12 +5061,12 @@ async function showInteractiveMenu() {
|
|
|
4842
5061
|
{ value: "docs", label: "View documentation" },
|
|
4843
5062
|
{ value: "help", label: "Show all commands" }
|
|
4844
5063
|
);
|
|
4845
|
-
const action = await
|
|
5064
|
+
const action = await clack15.select({
|
|
4846
5065
|
message: "What would you like to do?",
|
|
4847
5066
|
options
|
|
4848
5067
|
});
|
|
4849
|
-
if (
|
|
4850
|
-
|
|
5068
|
+
if (clack15.isCancel(action)) {
|
|
5069
|
+
clack15.cancel("Bye!");
|
|
4851
5070
|
process.exit(0);
|
|
4852
5071
|
}
|
|
4853
5072
|
switch (action) {
|