@insforge/cli 0.1.47 → 0.1.49
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 +306 -87
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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;
|
|
@@ -757,6 +817,7 @@ import { promisify as promisify3 } from "util";
|
|
|
757
817
|
import * as fs4 from "fs/promises";
|
|
758
818
|
import * as path4 from "path";
|
|
759
819
|
import * as clack8 from "@clack/prompts";
|
|
820
|
+
import pc from "picocolors";
|
|
760
821
|
|
|
761
822
|
// src/lib/skills.ts
|
|
762
823
|
import { exec } from "child_process";
|
|
@@ -918,7 +979,7 @@ function trackDiagnose(subcommand, config) {
|
|
|
918
979
|
project_name: config.project_name,
|
|
919
980
|
org_id: config.org_id,
|
|
920
981
|
region: config.region,
|
|
921
|
-
oss_mode: config.project_id ===
|
|
982
|
+
oss_mode: config.project_id === FAKE_PROJECT_ID
|
|
922
983
|
});
|
|
923
984
|
}
|
|
924
985
|
async function shutdownAnalytics() {
|
|
@@ -1719,11 +1780,11 @@ function registerProjectLinkCommand(program2) {
|
|
|
1719
1780
|
throw new CLIError("Invalid --api-base-url. Please provide a valid URL.");
|
|
1720
1781
|
}
|
|
1721
1782
|
const projectConfig2 = {
|
|
1722
|
-
project_id:
|
|
1783
|
+
project_id: FAKE_PROJECT_ID,
|
|
1723
1784
|
project_name: "oss-project",
|
|
1724
|
-
org_id:
|
|
1725
|
-
appkey: "
|
|
1726
|
-
region: "
|
|
1785
|
+
org_id: FAKE_ORG_ID,
|
|
1786
|
+
appkey: "ossfkey",
|
|
1787
|
+
region: "us-test",
|
|
1727
1788
|
api_key: opts.apiKey,
|
|
1728
1789
|
oss_host: opts.apiBaseUrl.replace(/\/$/, "")
|
|
1729
1790
|
// remove trailing slash if any
|
|
@@ -1893,11 +1954,14 @@ function registerProjectLinkCommand(program2) {
|
|
|
1893
1954
|
await reportCliUsage("cli.link", true, 6, projectConfig);
|
|
1894
1955
|
if (!json) {
|
|
1895
1956
|
const dashboardUrl = `${getFrontendUrl()}/dashboard/project/${project.id}`;
|
|
1896
|
-
clack8.log.step(`Dashboard: ${dashboardUrl}`);
|
|
1957
|
+
clack8.log.step(`Dashboard: ${pc.underline(dashboardUrl)}`);
|
|
1897
1958
|
if (templateDownloaded) {
|
|
1898
|
-
const
|
|
1899
|
-
|
|
1900
|
-
|
|
1959
|
+
const runCommand = `${pc.cyan("cd")} ${pc.green(dirName)} ${pc.dim("&&")} ${pc.cyan("npm run dev")}`;
|
|
1960
|
+
const steps = [
|
|
1961
|
+
`${pc.bold("1.")} ${runCommand}`,
|
|
1962
|
+
`${pc.bold("2.")} Open ${pc.cyan("Claude Code")} or ${pc.cyan("Cursor")} and prompt your agent to add more features`
|
|
1963
|
+
];
|
|
1964
|
+
clack8.note(steps.join("\n"), "What's next");
|
|
1901
1965
|
} else {
|
|
1902
1966
|
clack8.log.warn("Template download failed. You can retry or set up manually.");
|
|
1903
1967
|
}
|
|
@@ -4113,6 +4177,10 @@ function formatSize2(gb) {
|
|
|
4113
4177
|
return `${gb.toFixed(2)} GB`;
|
|
4114
4178
|
}
|
|
4115
4179
|
|
|
4180
|
+
// src/commands/diagnose/index.ts
|
|
4181
|
+
import * as os from "os";
|
|
4182
|
+
import * as clack14 from "@clack/prompts";
|
|
4183
|
+
|
|
4116
4184
|
// src/commands/diagnose/metrics.ts
|
|
4117
4185
|
var METRIC_LABELS = {
|
|
4118
4186
|
cpu_usage: "CPU Usage",
|
|
@@ -4179,7 +4247,7 @@ function registerDiagnoseMetricsCommand(diagnoseCmd2) {
|
|
|
4179
4247
|
await requireAuth(apiUrl);
|
|
4180
4248
|
const config = getProjectConfig();
|
|
4181
4249
|
if (!config) throw new ProjectNotLinkedError();
|
|
4182
|
-
if (config.project_id ===
|
|
4250
|
+
if (config.project_id === FAKE_PROJECT_ID) {
|
|
4183
4251
|
throw new CLIError(
|
|
4184
4252
|
"Metrics requires InsForge Platform login. Not available when linked via --api-key."
|
|
4185
4253
|
);
|
|
@@ -4243,7 +4311,7 @@ function registerDiagnoseAdvisorCommand(diagnoseCmd2) {
|
|
|
4243
4311
|
await requireAuth(apiUrl);
|
|
4244
4312
|
const config = getProjectConfig();
|
|
4245
4313
|
if (!config) throw new ProjectNotLinkedError();
|
|
4246
|
-
if (config.project_id ===
|
|
4314
|
+
if (config.project_id === FAKE_PROJECT_ID) {
|
|
4247
4315
|
throw new CLIError(
|
|
4248
4316
|
"Advisor requires InsForge Platform login. Not available when linked via --api-key."
|
|
4249
4317
|
);
|
|
@@ -4581,82 +4649,234 @@ function registerDiagnoseLogsCommand(diagnoseCmd2) {
|
|
|
4581
4649
|
function sectionHeader(title) {
|
|
4582
4650
|
return `\u2500\u2500 ${title} ${"\u2500".repeat(Math.max(0, 44 - title.length))}`;
|
|
4583
4651
|
}
|
|
4652
|
+
async function collectDiagnosticData(projectId, ossMode, apiUrl) {
|
|
4653
|
+
const metricsPromise = ossMode ? Promise.reject(new Error("Platform login required (linked via --api-key)")) : fetchMetricsSummary(projectId, apiUrl);
|
|
4654
|
+
const advisorPromise = ossMode ? Promise.reject(new Error("Platform login required (linked via --api-key)")) : fetchAdvisorSummary(projectId, apiUrl);
|
|
4655
|
+
const [metricsResult, advisorResult, dbResult, logsResult] = await Promise.allSettled([
|
|
4656
|
+
metricsPromise,
|
|
4657
|
+
advisorPromise,
|
|
4658
|
+
runDbChecks(),
|
|
4659
|
+
fetchLogsSummary(100)
|
|
4660
|
+
]);
|
|
4661
|
+
const errors = [];
|
|
4662
|
+
let metrics = null;
|
|
4663
|
+
let advisor = null;
|
|
4664
|
+
let db = null;
|
|
4665
|
+
let logs = null;
|
|
4666
|
+
if (metricsResult.status === "fulfilled") {
|
|
4667
|
+
const data = metricsResult.value;
|
|
4668
|
+
metrics = data.metrics.filter((m) => m.data.length > 0).map((m) => {
|
|
4669
|
+
let sum = 0;
|
|
4670
|
+
let max = -Infinity;
|
|
4671
|
+
for (const d of m.data) {
|
|
4672
|
+
sum += d.value;
|
|
4673
|
+
if (d.value > max) max = d.value;
|
|
4674
|
+
}
|
|
4675
|
+
return {
|
|
4676
|
+
metric: m.metric,
|
|
4677
|
+
latest: m.data[m.data.length - 1].value,
|
|
4678
|
+
avg: sum / m.data.length,
|
|
4679
|
+
max
|
|
4680
|
+
};
|
|
4681
|
+
});
|
|
4682
|
+
} else {
|
|
4683
|
+
errors.push(metricsResult.reason?.message ?? "Metrics unavailable");
|
|
4684
|
+
}
|
|
4685
|
+
if (advisorResult.status === "fulfilled") {
|
|
4686
|
+
advisor = advisorResult.value;
|
|
4687
|
+
} else {
|
|
4688
|
+
errors.push(advisorResult.reason?.message ?? "Advisor unavailable");
|
|
4689
|
+
}
|
|
4690
|
+
if (dbResult.status === "fulfilled") {
|
|
4691
|
+
db = dbResult.value;
|
|
4692
|
+
} else {
|
|
4693
|
+
errors.push(dbResult.reason?.message ?? "DB checks unavailable");
|
|
4694
|
+
}
|
|
4695
|
+
if (logsResult.status === "fulfilled") {
|
|
4696
|
+
logs = logsResult.value;
|
|
4697
|
+
} else {
|
|
4698
|
+
errors.push(logsResult.reason?.message ?? "Logs unavailable");
|
|
4699
|
+
}
|
|
4700
|
+
return { metrics, advisor, db, logs, errors };
|
|
4701
|
+
}
|
|
4584
4702
|
function registerDiagnoseCommands(diagnoseCmd2) {
|
|
4585
|
-
diagnoseCmd2.description("Backend diagnostics \u2014 run with no subcommand for a full health report").action(async (
|
|
4703
|
+
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) => {
|
|
4586
4704
|
const { json, apiUrl } = getRootOpts(cmd);
|
|
4705
|
+
const usageEvent = opts.ai ? "cli.diagnose.ai" : "cli.diagnose";
|
|
4587
4706
|
try {
|
|
4588
4707
|
await requireAuth(apiUrl);
|
|
4589
4708
|
const config = getProjectConfig();
|
|
4590
4709
|
if (!config) throw new ProjectNotLinkedError();
|
|
4591
4710
|
const projectId = config.project_id;
|
|
4592
4711
|
const projectName = config.project_name;
|
|
4593
|
-
const ossMode = config.project_id ===
|
|
4594
|
-
trackDiagnose("report", config);
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4712
|
+
const ossMode = config.project_id === FAKE_PROJECT_ID;
|
|
4713
|
+
trackDiagnose(opts.ai ? "ai" : "report", config);
|
|
4714
|
+
if (opts.ai) {
|
|
4715
|
+
const question = String(opts.ai).trim();
|
|
4716
|
+
if (question.length === 0 || question.length > 2e3) {
|
|
4717
|
+
throw new CLIError("Question must be between 1 and 2000 characters.");
|
|
4718
|
+
}
|
|
4719
|
+
const s = !json ? clack14.spinner() : null;
|
|
4720
|
+
s?.start("Collecting diagnostic data...");
|
|
4721
|
+
const data2 = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
4722
|
+
const cliVersion = "0.1.49";
|
|
4723
|
+
s?.stop("Data collected");
|
|
4724
|
+
if (!json) {
|
|
4725
|
+
console.log(`
|
|
4726
|
+
AI Diagnosis \u2014 ${projectName}
|
|
4727
|
+
`);
|
|
4728
|
+
console.log(sectionHeader("Question"));
|
|
4729
|
+
console.log(` ${question}
|
|
4730
|
+
`);
|
|
4731
|
+
console.log(sectionHeader("Analysis"));
|
|
4732
|
+
}
|
|
4733
|
+
let sessionId;
|
|
4734
|
+
let fullText = "";
|
|
4735
|
+
const jsonEvents = [];
|
|
4736
|
+
let streamError;
|
|
4737
|
+
const context = {
|
|
4738
|
+
context_version: "diagnostic-v1",
|
|
4739
|
+
client_info: {
|
|
4740
|
+
cli_version: cliVersion,
|
|
4741
|
+
node_version: process.version,
|
|
4742
|
+
os: `${os.platform()} ${os.release()}`
|
|
4743
|
+
}
|
|
4744
|
+
};
|
|
4745
|
+
if (Array.isArray(data2.metrics) && data2.metrics.length > 0) {
|
|
4746
|
+
context.metrics = data2.metrics;
|
|
4747
|
+
}
|
|
4748
|
+
if (data2.advisor) {
|
|
4749
|
+
const adv = data2.advisor;
|
|
4750
|
+
const summary = adv.summary;
|
|
4751
|
+
const rawErrors = adv.collectorErrors;
|
|
4752
|
+
if (summary) {
|
|
4753
|
+
context.advisor = {
|
|
4754
|
+
summary: {
|
|
4755
|
+
total: summary.total ?? 0,
|
|
4756
|
+
critical: summary.critical ?? 0,
|
|
4757
|
+
warning: summary.warning ?? 0,
|
|
4758
|
+
info: summary.info ?? 0
|
|
4759
|
+
},
|
|
4760
|
+
collectorErrors: rawErrors?.map(
|
|
4761
|
+
(e) => typeof e === "string" ? e : JSON.stringify(e)
|
|
4762
|
+
) ?? []
|
|
4621
4763
|
};
|
|
4622
|
-
}
|
|
4623
|
-
} else {
|
|
4624
|
-
report.metrics = null;
|
|
4625
|
-
errors.push(metricsResult.reason?.message ?? "Metrics unavailable");
|
|
4764
|
+
}
|
|
4626
4765
|
}
|
|
4627
|
-
if (
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4766
|
+
if (data2.db) {
|
|
4767
|
+
const rawDb = data2.db;
|
|
4768
|
+
const safeDb = {};
|
|
4769
|
+
for (const [key, rows] of Object.entries(rawDb)) {
|
|
4770
|
+
safeDb[key] = rows.map((row) => {
|
|
4771
|
+
const out = {};
|
|
4772
|
+
for (const [k, v] of Object.entries(row)) {
|
|
4773
|
+
out[k] = v === null || v === void 0 ? "" : String(v);
|
|
4774
|
+
}
|
|
4775
|
+
return out;
|
|
4776
|
+
});
|
|
4777
|
+
}
|
|
4778
|
+
if (Object.keys(safeDb).length > 0) {
|
|
4779
|
+
context.db = safeDb;
|
|
4780
|
+
}
|
|
4632
4781
|
}
|
|
4633
|
-
if (
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4782
|
+
if (Array.isArray(data2.logs) && data2.logs.length > 0) {
|
|
4783
|
+
context.logs = data2.logs.map((s2) => ({
|
|
4784
|
+
source: s2.source,
|
|
4785
|
+
total: s2.total,
|
|
4786
|
+
errors: s2.errors.map((e) => ({
|
|
4787
|
+
timestamp: e.timestamp ?? "",
|
|
4788
|
+
message: e.message ?? "",
|
|
4789
|
+
source: e.source ?? ""
|
|
4790
|
+
}))
|
|
4791
|
+
}));
|
|
4638
4792
|
}
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4793
|
+
await streamDiagnosticAnalysis({
|
|
4794
|
+
project_id: projectId,
|
|
4795
|
+
question,
|
|
4796
|
+
context
|
|
4797
|
+
}, (event) => {
|
|
4798
|
+
if (event.type === "done") {
|
|
4799
|
+
sessionId = event.data.session_id;
|
|
4800
|
+
}
|
|
4801
|
+
if (event.type === "error") {
|
|
4802
|
+
streamError = new CLIError(String(event.data.message ?? "Unknown diagnostic error"));
|
|
4803
|
+
}
|
|
4804
|
+
if (json) {
|
|
4805
|
+
jsonEvents.push({ type: event.type, ...event.data });
|
|
4806
|
+
return;
|
|
4807
|
+
}
|
|
4808
|
+
switch (event.type) {
|
|
4809
|
+
case "delta":
|
|
4810
|
+
process.stdout.write(String(event.data.text ?? ""));
|
|
4811
|
+
fullText += String(event.data.text ?? "");
|
|
4812
|
+
break;
|
|
4813
|
+
case "tool_call":
|
|
4814
|
+
console.log(`
|
|
4815
|
+
[calling ${event.data.tool_name}...]`);
|
|
4816
|
+
break;
|
|
4817
|
+
case "tool_result":
|
|
4818
|
+
break;
|
|
4819
|
+
case "done":
|
|
4820
|
+
break;
|
|
4821
|
+
case "error":
|
|
4822
|
+
console.error(`
|
|
4823
|
+
Error: ${streamError?.message ?? "Unknown error"}`);
|
|
4824
|
+
break;
|
|
4825
|
+
}
|
|
4826
|
+
}, apiUrl);
|
|
4827
|
+
if (streamError) {
|
|
4828
|
+
throw streamError;
|
|
4829
|
+
}
|
|
4830
|
+
if (!json) {
|
|
4831
|
+
if (fullText && !fullText.endsWith("\n")) console.log("");
|
|
4832
|
+
console.log("");
|
|
4833
|
+
}
|
|
4834
|
+
if (json) {
|
|
4835
|
+
outputJson({ sessionId, events: jsonEvents });
|
|
4644
4836
|
}
|
|
4645
|
-
|
|
4646
|
-
|
|
4837
|
+
if (!json && sessionId) {
|
|
4838
|
+
const ratingChoice = await clack14.select({
|
|
4839
|
+
message: "Was this analysis helpful?",
|
|
4840
|
+
options: [
|
|
4841
|
+
{ value: "skip", label: "Skip", hint: "no rating" },
|
|
4842
|
+
{ value: "helpful", label: "Helpful", hint: "solved or pointed in right direction" },
|
|
4843
|
+
{ value: "not_helpful", label: "Not helpful", hint: "didn't apply to the problem" },
|
|
4844
|
+
{ value: "incorrect", label: "Incorrect", hint: "diagnosis was wrong or misleading" }
|
|
4845
|
+
]
|
|
4846
|
+
});
|
|
4847
|
+
if (!clack14.isCancel(ratingChoice) && ratingChoice !== "skip") {
|
|
4848
|
+
try {
|
|
4849
|
+
await rateDiagnosticSession(
|
|
4850
|
+
sessionId,
|
|
4851
|
+
ratingChoice,
|
|
4852
|
+
void 0,
|
|
4853
|
+
apiUrl
|
|
4854
|
+
);
|
|
4855
|
+
clack14.log.success("Thanks for your feedback!");
|
|
4856
|
+
} catch {
|
|
4857
|
+
clack14.log.warn("Failed to submit rating.");
|
|
4858
|
+
}
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4861
|
+
await reportCliUsage(usageEvent, true);
|
|
4862
|
+
return;
|
|
4863
|
+
}
|
|
4864
|
+
const data = await collectDiagnosticData(projectId, ossMode, apiUrl);
|
|
4865
|
+
if (json) {
|
|
4866
|
+
outputJson({ project: projectName, ...data });
|
|
4647
4867
|
} else {
|
|
4648
4868
|
console.log(`
|
|
4649
4869
|
InsForge Health Report \u2014 ${projectName}
|
|
4650
4870
|
`);
|
|
4651
4871
|
console.log(sectionHeader("System Metrics (last 1h)"));
|
|
4652
|
-
if (
|
|
4653
|
-
const
|
|
4654
|
-
if (
|
|
4872
|
+
if (data.metrics) {
|
|
4873
|
+
const metricsArr = data.metrics;
|
|
4874
|
+
if (metricsArr.length === 0) {
|
|
4655
4875
|
console.log(" No metrics data available.");
|
|
4656
4876
|
} else {
|
|
4657
4877
|
const vals = {};
|
|
4658
|
-
for (const m of
|
|
4659
|
-
|
|
4878
|
+
for (const m of metricsArr) {
|
|
4879
|
+
vals[m.metric] = m.latest;
|
|
4660
4880
|
}
|
|
4661
4881
|
const cpu = vals.cpu_usage !== void 0 ? `${vals.cpu_usage.toFixed(1)}%` : "N/A";
|
|
4662
4882
|
const mem = vals.memory_usage !== void 0 ? `${vals.memory_usage.toFixed(1)}%` : "N/A";
|
|
@@ -4667,20 +4887,19 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
4667
4887
|
console.log(` Disk: ${disk} Network: \u2193${netIn} \u2191${netOut}`);
|
|
4668
4888
|
}
|
|
4669
4889
|
} else {
|
|
4670
|
-
console.log(` N/A \u2014 ${
|
|
4890
|
+
console.log(` N/A \u2014 ${data.errors.find((e) => e.includes("Metrics") || e.includes("Platform")) ?? "unavailable"}`);
|
|
4671
4891
|
}
|
|
4672
4892
|
console.log("\n" + sectionHeader("Advisor Scan"));
|
|
4673
|
-
if (
|
|
4674
|
-
const scan =
|
|
4675
|
-
const s = scan.summary;
|
|
4893
|
+
if (data.advisor) {
|
|
4894
|
+
const scan = data.advisor;
|
|
4676
4895
|
const date = new Date(scan.scannedAt).toLocaleDateString();
|
|
4677
|
-
console.log(` ${date} (${scan.status}) \u2014 ${
|
|
4896
|
+
console.log(` ${date} (${scan.status}) \u2014 ${scan.summary.critical} critical \xB7 ${scan.summary.warning} warning \xB7 ${scan.summary.info} info`);
|
|
4678
4897
|
} else {
|
|
4679
|
-
console.log(` N/A \u2014 ${
|
|
4898
|
+
console.log(` N/A \u2014 ${data.errors.find((e) => e.includes("Advisor") || e.includes("Platform")) ?? "unavailable"}`);
|
|
4680
4899
|
}
|
|
4681
4900
|
console.log("\n" + sectionHeader("Database"));
|
|
4682
|
-
if (
|
|
4683
|
-
const db =
|
|
4901
|
+
if (data.db) {
|
|
4902
|
+
const db = data.db;
|
|
4684
4903
|
const conn = db.connections?.[0];
|
|
4685
4904
|
const cache = db["cache-hit"]?.[0];
|
|
4686
4905
|
const deadTuples = (db.bloat ?? []).reduce(
|
|
@@ -4695,21 +4914,21 @@ function registerDiagnoseCommands(diagnoseCmd2) {
|
|
|
4695
4914
|
` Dead tuples: ${deadTuples.toLocaleString()} Locks waiting: ${lockCount}`
|
|
4696
4915
|
);
|
|
4697
4916
|
} else {
|
|
4698
|
-
console.log(` N/A \u2014 ${
|
|
4917
|
+
console.log(` N/A \u2014 ${data.errors.find((e) => e.includes("DB")) ?? "unavailable"}`);
|
|
4699
4918
|
}
|
|
4700
4919
|
console.log("\n" + sectionHeader("Recent Errors (last 100 logs/source)"));
|
|
4701
|
-
if (
|
|
4702
|
-
const summaries =
|
|
4920
|
+
if (data.logs) {
|
|
4921
|
+
const summaries = data.logs;
|
|
4703
4922
|
const parts = summaries.map((s) => `${s.source}: ${s.errors.length}`);
|
|
4704
4923
|
console.log(` ${parts.join(" ")}`);
|
|
4705
4924
|
} else {
|
|
4706
|
-
console.log(` N/A \u2014 ${
|
|
4925
|
+
console.log(` N/A \u2014 ${data.errors.find((e) => e.includes("Logs")) ?? "unavailable"}`);
|
|
4707
4926
|
}
|
|
4708
4927
|
console.log("");
|
|
4709
4928
|
}
|
|
4710
|
-
await reportCliUsage(
|
|
4929
|
+
await reportCliUsage(usageEvent, true);
|
|
4711
4930
|
} catch (err) {
|
|
4712
|
-
await reportCliUsage(
|
|
4931
|
+
await reportCliUsage(usageEvent, false);
|
|
4713
4932
|
handleError(err, json);
|
|
4714
4933
|
} finally {
|
|
4715
4934
|
await shutdownAnalytics();
|
|
@@ -4830,7 +5049,7 @@ async function showInteractiveMenu() {
|
|
|
4830
5049
|
} catch {
|
|
4831
5050
|
}
|
|
4832
5051
|
console.log(INSFORGE_LOGO);
|
|
4833
|
-
|
|
5052
|
+
clack15.intro(`InsForge CLI v${pkg.version}`);
|
|
4834
5053
|
const options = [];
|
|
4835
5054
|
if (!isLoggedIn) {
|
|
4836
5055
|
options.push({ value: "login", label: "Log in to InsForge" });
|
|
@@ -4846,12 +5065,12 @@ async function showInteractiveMenu() {
|
|
|
4846
5065
|
{ value: "docs", label: "View documentation" },
|
|
4847
5066
|
{ value: "help", label: "Show all commands" }
|
|
4848
5067
|
);
|
|
4849
|
-
const action = await
|
|
5068
|
+
const action = await clack15.select({
|
|
4850
5069
|
message: "What would you like to do?",
|
|
4851
5070
|
options
|
|
4852
5071
|
});
|
|
4853
|
-
if (
|
|
4854
|
-
|
|
5072
|
+
if (clack15.isCancel(action)) {
|
|
5073
|
+
clack15.cancel("Bye!");
|
|
4855
5074
|
process.exit(0);
|
|
4856
5075
|
}
|
|
4857
5076
|
switch (action) {
|