@kinetica/admin-agent 0.2.0 → 0.2.2
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/README.md +16 -12
- package/dist/admin-agent.js +979 -600
- package/knowledge/references/bundle/support-bundle.md +33 -1
- package/knowledge/references/rank-architecture.md +12 -0
- package/package.json +1 -1
package/dist/admin-agent.js
CHANGED
|
@@ -37,10 +37,10 @@ __export(cli_exports, {
|
|
|
37
37
|
verbose: () => verbose
|
|
38
38
|
});
|
|
39
39
|
module.exports = __toCommonJS(cli_exports);
|
|
40
|
-
var
|
|
40
|
+
var import_picocolors15 = __toESM(require("picocolors"));
|
|
41
41
|
|
|
42
42
|
// src/cli/banner.ts
|
|
43
|
-
var
|
|
43
|
+
var import_picocolors2 = __toESM(require("picocolors"));
|
|
44
44
|
|
|
45
45
|
// src/cli/version.ts
|
|
46
46
|
var import_fs = require("fs");
|
|
@@ -62,6 +62,29 @@ function getVersion() {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// src/output/brand-colors.ts
|
|
66
|
+
var import_picocolors = __toESM(require("picocolors"));
|
|
67
|
+
var BRAND_PURPLE = [147, 51, 234];
|
|
68
|
+
var BRAND_PINK = [236, 72, 153];
|
|
69
|
+
var SOFTEN_TARGET = [210, 210, 210];
|
|
70
|
+
var SOFTEN_PURPLE = 0.55;
|
|
71
|
+
var SOFTEN_PINK = 0.45;
|
|
72
|
+
function soften([r, g, b], amount) {
|
|
73
|
+
const [tr, tg, tb] = SOFTEN_TARGET;
|
|
74
|
+
return [
|
|
75
|
+
Math.round(r + (tr - r) * amount),
|
|
76
|
+
Math.round(g + (tg - g) * amount),
|
|
77
|
+
Math.round(b + (tb - b) * amount)
|
|
78
|
+
];
|
|
79
|
+
}
|
|
80
|
+
var TEXT_PURPLE = soften(BRAND_PURPLE, SOFTEN_PURPLE);
|
|
81
|
+
var TEXT_PINK = soften(BRAND_PINK, SOFTEN_PINK);
|
|
82
|
+
function truecolor([r, g, b]) {
|
|
83
|
+
return (s) => import_picocolors.default.isColorSupported ? `\x1B[38;2;${r};${g};${b}m${s}\x1B[39m` : s;
|
|
84
|
+
}
|
|
85
|
+
var purple = truecolor(TEXT_PURPLE);
|
|
86
|
+
var pink = truecolor(TEXT_PINK);
|
|
87
|
+
|
|
65
88
|
// src/cli/banner.ts
|
|
66
89
|
var LOGO = ` \u2588\u2588\u2557 \u2588\u2588\u2557\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\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557
|
|
67
90
|
\u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
@@ -86,13 +109,11 @@ function dimRgb(color, factor) {
|
|
|
86
109
|
var SHADOW_CHARS = /* @__PURE__ */ new Set(["\u2557", "\u2554", "\u2551", "\u255D", "\u255A", "\u2550"]);
|
|
87
110
|
var DIM_FACTOR = 0.4;
|
|
88
111
|
function gradientize(text2) {
|
|
89
|
-
const PURPLE = [147, 51, 234];
|
|
90
|
-
const HOT_PINK = [236, 72, 153];
|
|
91
112
|
const RESET = "\x1B[0m";
|
|
92
113
|
const lines = text2.split("\n");
|
|
93
114
|
const maxIdx = Math.max(lines.length - 1, 1);
|
|
94
115
|
return lines.map((line, i) => {
|
|
95
|
-
const bright = lerpRgb(
|
|
116
|
+
const bright = lerpRgb(BRAND_PURPLE, BRAND_PINK, i / maxIdx);
|
|
96
117
|
const dim = dimRgb(bright, DIM_FACTOR);
|
|
97
118
|
let result = "";
|
|
98
119
|
let currentMode = null;
|
|
@@ -110,20 +131,53 @@ function gradientize(text2) {
|
|
|
110
131
|
}
|
|
111
132
|
function printBanner(model) {
|
|
112
133
|
const version = getVersion();
|
|
113
|
-
const subtitle = `admin-agent ${
|
|
134
|
+
const subtitle = `admin-agent ${import_picocolors2.default.dim(`v${version}`)}`;
|
|
114
135
|
const header = model ? `${subtitle}
|
|
115
|
-
${
|
|
136
|
+
${import_picocolors2.default.dim(`Model: ${model}`)}` : subtitle;
|
|
116
137
|
process.stderr.write("\n\n" + gradientize(LOGO) + "\n\n" + header + "\n");
|
|
117
138
|
return subtitle;
|
|
118
139
|
}
|
|
119
140
|
|
|
120
|
-
// src/
|
|
121
|
-
var
|
|
141
|
+
// src/output/themed-prompts.ts
|
|
142
|
+
var import_prompts = require("@inquirer/prompts");
|
|
143
|
+
|
|
144
|
+
// src/output/prompt-theme.ts
|
|
145
|
+
var PROMPT_THEME = {
|
|
146
|
+
style: {
|
|
147
|
+
answer: pink,
|
|
148
|
+
highlight: pink
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// src/output/themed-prompts.ts
|
|
153
|
+
function input(...args) {
|
|
154
|
+
const [config, ...rest] = args;
|
|
155
|
+
return (0, import_prompts.input)({ theme: PROMPT_THEME, ...config }, ...rest);
|
|
156
|
+
}
|
|
157
|
+
function confirm(...args) {
|
|
158
|
+
const [config, ...rest] = args;
|
|
159
|
+
return (0, import_prompts.confirm)({ theme: PROMPT_THEME, ...config }, ...rest);
|
|
160
|
+
}
|
|
161
|
+
function password(...args) {
|
|
162
|
+
const [config, ...rest] = args;
|
|
163
|
+
return (0, import_prompts.password)({ theme: PROMPT_THEME, ...config }, ...rest);
|
|
164
|
+
}
|
|
165
|
+
function select(...args) {
|
|
166
|
+
const [config, ...rest] = args;
|
|
167
|
+
return (0, import_prompts.select)({ theme: PROMPT_THEME, ...config }, ...rest);
|
|
168
|
+
}
|
|
169
|
+
function search(...args) {
|
|
170
|
+
const [config, ...rest] = args;
|
|
171
|
+
return (0, import_prompts.search)({ theme: PROMPT_THEME, ...config }, ...rest);
|
|
172
|
+
}
|
|
173
|
+
function checkbox(...args) {
|
|
174
|
+
const [config, ...rest] = args;
|
|
175
|
+
return (0, import_prompts.checkbox)({ theme: PROMPT_THEME, ...config }, ...rest);
|
|
176
|
+
}
|
|
122
177
|
|
|
123
178
|
// src/agent/run-agent.ts
|
|
124
179
|
var import_claude_agent_sdk5 = require("@anthropic-ai/claude-agent-sdk");
|
|
125
|
-
var
|
|
126
|
-
var import_picocolors8 = __toESM(require("picocolors"));
|
|
180
|
+
var import_picocolors9 = __toESM(require("picocolors"));
|
|
127
181
|
|
|
128
182
|
// src/agent/diagnostic-sql.ts
|
|
129
183
|
function has(columns, name) {
|
|
@@ -500,7 +554,7 @@ var BUILDER_REGISTRY = [
|
|
|
500
554
|
// src/tools/index.ts
|
|
501
555
|
var import_claude_agent_sdk2 = require("@anthropic-ai/claude-agent-sdk");
|
|
502
556
|
var import_zod16 = require("zod");
|
|
503
|
-
var
|
|
557
|
+
var import_picocolors5 = __toESM(require("picocolors"));
|
|
504
558
|
|
|
505
559
|
// src/approval/registry.ts
|
|
506
560
|
var DEFAULT_READ_ONLY_TOOLS = /* @__PURE__ */ new Set();
|
|
@@ -854,7 +908,7 @@ var GetSystemPropertiesSchema = import_zod.z.object({
|
|
|
854
908
|
category: import_zod.z.string().optional(),
|
|
855
909
|
key_pattern: import_zod.z.string().optional()
|
|
856
910
|
});
|
|
857
|
-
async function getSystemProperties(session2,
|
|
911
|
+
async function getSystemProperties(session2, input2) {
|
|
858
912
|
try {
|
|
859
913
|
const response = await session2.makeRequest("/show/system/properties", {
|
|
860
914
|
options: {}
|
|
@@ -884,7 +938,7 @@ async function getSystemProperties(session2, input5) {
|
|
|
884
938
|
const inner = parseDataStr(parsed.data_str, raw);
|
|
885
939
|
if (!inner.ok) return inner;
|
|
886
940
|
const propertyMap = inner.data?.property_map ?? {};
|
|
887
|
-
const filteredMap = applyFilters(propertyMap,
|
|
941
|
+
const filteredMap = applyFilters(propertyMap, input2);
|
|
888
942
|
return {
|
|
889
943
|
ok: true,
|
|
890
944
|
data: flatObjectToRows(filteredMap, "property", "value"),
|
|
@@ -900,8 +954,8 @@ async function getSystemProperties(session2, input5) {
|
|
|
900
954
|
};
|
|
901
955
|
}
|
|
902
956
|
}
|
|
903
|
-
function applyFilters(propertyMap,
|
|
904
|
-
const { category, key_pattern } =
|
|
957
|
+
function applyFilters(propertyMap, input2) {
|
|
958
|
+
const { category, key_pattern } = input2;
|
|
905
959
|
if (category === void 0 && key_pattern === void 0) {
|
|
906
960
|
return propertyMap;
|
|
907
961
|
}
|
|
@@ -1137,7 +1191,7 @@ var GetLogsSchema = import_zod2.z.object({
|
|
|
1137
1191
|
message: "duration and start_time/end_time are mutually exclusive -- use one or the other, not both"
|
|
1138
1192
|
}
|
|
1139
1193
|
);
|
|
1140
|
-
function buildStubResponse(
|
|
1194
|
+
function buildStubResponse(input2) {
|
|
1141
1195
|
return {
|
|
1142
1196
|
ok: true,
|
|
1143
1197
|
data: {
|
|
@@ -1145,39 +1199,39 @@ function buildStubResponse(input5) {
|
|
|
1145
1199
|
endpoint: "/admin/show/logs",
|
|
1146
1200
|
status: "stub",
|
|
1147
1201
|
requested_params: {
|
|
1148
|
-
source:
|
|
1149
|
-
min_severity:
|
|
1150
|
-
duration:
|
|
1151
|
-
start_time:
|
|
1152
|
-
end_time:
|
|
1153
|
-
node_id:
|
|
1154
|
-
limit:
|
|
1202
|
+
source: input2.source,
|
|
1203
|
+
min_severity: input2.min_severity,
|
|
1204
|
+
duration: input2.duration,
|
|
1205
|
+
start_time: input2.start_time,
|
|
1206
|
+
end_time: input2.end_time,
|
|
1207
|
+
node_id: input2.node_id,
|
|
1208
|
+
limit: input2.limit
|
|
1155
1209
|
}
|
|
1156
1210
|
}
|
|
1157
1211
|
};
|
|
1158
1212
|
}
|
|
1159
|
-
async function getLogs(session2,
|
|
1213
|
+
async function getLogs(session2, input2) {
|
|
1160
1214
|
const params = {
|
|
1161
|
-
source:
|
|
1162
|
-
severity:
|
|
1163
|
-
limit:
|
|
1215
|
+
source: input2.source,
|
|
1216
|
+
severity: input2.min_severity,
|
|
1217
|
+
limit: input2.limit
|
|
1164
1218
|
};
|
|
1165
|
-
if (
|
|
1166
|
-
params.duration =
|
|
1219
|
+
if (input2.duration !== void 0) {
|
|
1220
|
+
params.duration = input2.duration;
|
|
1167
1221
|
}
|
|
1168
|
-
if (
|
|
1169
|
-
params.start_time =
|
|
1222
|
+
if (input2.start_time !== void 0) {
|
|
1223
|
+
params.start_time = input2.start_time;
|
|
1170
1224
|
}
|
|
1171
|
-
if (
|
|
1172
|
-
params.end_time =
|
|
1225
|
+
if (input2.end_time !== void 0) {
|
|
1226
|
+
params.end_time = input2.end_time;
|
|
1173
1227
|
}
|
|
1174
|
-
if (
|
|
1175
|
-
params.node_id =
|
|
1228
|
+
if (input2.node_id !== void 0) {
|
|
1229
|
+
params.node_id = input2.node_id;
|
|
1176
1230
|
}
|
|
1177
1231
|
try {
|
|
1178
1232
|
const response = await session2.makeRequest("/admin/show/logs", params);
|
|
1179
1233
|
if (!response.ok) {
|
|
1180
|
-
return buildStubResponse(
|
|
1234
|
+
return buildStubResponse(input2);
|
|
1181
1235
|
}
|
|
1182
1236
|
const raw = await response.text();
|
|
1183
1237
|
try {
|
|
@@ -1187,10 +1241,10 @@ async function getLogs(session2, input5) {
|
|
|
1187
1241
|
data
|
|
1188
1242
|
};
|
|
1189
1243
|
} catch {
|
|
1190
|
-
return buildStubResponse(
|
|
1244
|
+
return buildStubResponse(input2);
|
|
1191
1245
|
}
|
|
1192
1246
|
} catch {
|
|
1193
|
-
return buildStubResponse(
|
|
1247
|
+
return buildStubResponse(input2);
|
|
1194
1248
|
}
|
|
1195
1249
|
}
|
|
1196
1250
|
|
|
@@ -1335,12 +1389,12 @@ var ResourceGroupsSchema = import_zod4.z.object({
|
|
|
1335
1389
|
names: import_zod4.z.array(import_zod4.z.string()).optional().default([""]),
|
|
1336
1390
|
show_tier_usage: import_zod4.z.boolean().optional()
|
|
1337
1391
|
});
|
|
1338
|
-
async function getResourceGroups(session2,
|
|
1392
|
+
async function getResourceGroups(session2, input2) {
|
|
1339
1393
|
try {
|
|
1340
1394
|
const response = await session2.makeRequest("/show/resourcegroups", {
|
|
1341
|
-
names:
|
|
1395
|
+
names: input2.names,
|
|
1342
1396
|
options: {
|
|
1343
|
-
show_tier_usage: String(
|
|
1397
|
+
show_tier_usage: String(input2.show_tier_usage ?? false),
|
|
1344
1398
|
show_default_values: "true",
|
|
1345
1399
|
show_default_group: "true"
|
|
1346
1400
|
}
|
|
@@ -1391,18 +1445,18 @@ var VerifyDbSchema = import_zod5.z.object({
|
|
|
1391
1445
|
verify_persist: import_zod5.z.boolean().optional(),
|
|
1392
1446
|
verify_rank0: import_zod5.z.boolean().optional()
|
|
1393
1447
|
});
|
|
1394
|
-
async function verifyDb(session2,
|
|
1448
|
+
async function verifyDb(session2, input2) {
|
|
1395
1449
|
const options = {
|
|
1396
1450
|
concurrent_safe: "true"
|
|
1397
1451
|
};
|
|
1398
|
-
if (
|
|
1399
|
-
options.verify_nulls = String(
|
|
1452
|
+
if (input2.verify_nulls !== void 0) {
|
|
1453
|
+
options.verify_nulls = String(input2.verify_nulls);
|
|
1400
1454
|
}
|
|
1401
|
-
if (
|
|
1402
|
-
options.verify_persist = String(
|
|
1455
|
+
if (input2.verify_persist !== void 0) {
|
|
1456
|
+
options.verify_persist = String(input2.verify_persist);
|
|
1403
1457
|
}
|
|
1404
|
-
if (
|
|
1405
|
-
options.verify_rank0 = String(
|
|
1458
|
+
if (input2.verify_rank0 !== void 0) {
|
|
1459
|
+
options.verify_rank0 = String(input2.verify_rank0);
|
|
1406
1460
|
}
|
|
1407
1461
|
try {
|
|
1408
1462
|
const response = await session2.makeRequest("/admin/verifydb", { options });
|
|
@@ -1455,10 +1509,10 @@ var import_zod6 = require("zod");
|
|
|
1455
1509
|
var ShowSecuritySchema = import_zod6.z.object({
|
|
1456
1510
|
names: import_zod6.z.array(import_zod6.z.string()).optional().default([""])
|
|
1457
1511
|
});
|
|
1458
|
-
async function showSecurity(session2,
|
|
1512
|
+
async function showSecurity(session2, input2) {
|
|
1459
1513
|
try {
|
|
1460
1514
|
const response = await session2.makeRequest("/show/security", {
|
|
1461
|
-
names:
|
|
1515
|
+
names: input2.names,
|
|
1462
1516
|
options: {}
|
|
1463
1517
|
});
|
|
1464
1518
|
if (!response.ok) {
|
|
@@ -1554,10 +1608,10 @@ var ShowTableSchema = import_zod7.z.object({
|
|
|
1554
1608
|
get_access_data: import_zod7.z.boolean().optional(),
|
|
1555
1609
|
get_column_info: import_zod7.z.boolean().optional()
|
|
1556
1610
|
});
|
|
1557
|
-
function resolveColumnInfoOption(
|
|
1558
|
-
if (
|
|
1559
|
-
if (
|
|
1560
|
-
return
|
|
1611
|
+
function resolveColumnInfoOption(input2) {
|
|
1612
|
+
if (input2.get_column_info === true) return "true";
|
|
1613
|
+
if (input2.get_column_info === false) return "false";
|
|
1614
|
+
return input2.table_name !== "" ? "true" : "false";
|
|
1561
1615
|
}
|
|
1562
1616
|
function parseColumnProperties(propertiesJson) {
|
|
1563
1617
|
try {
|
|
@@ -1623,19 +1677,19 @@ async function fetchIndexes(session2, tableName) {
|
|
|
1623
1677
|
return [];
|
|
1624
1678
|
}
|
|
1625
1679
|
}
|
|
1626
|
-
async function showTable(session2,
|
|
1680
|
+
async function showTable(session2, input2) {
|
|
1627
1681
|
const options = {
|
|
1628
|
-
get_sizes: String(
|
|
1682
|
+
get_sizes: String(input2.get_sizes ?? true),
|
|
1629
1683
|
show_children: "false",
|
|
1630
1684
|
no_error_if_not_exists: "true",
|
|
1631
|
-
get_column_info: resolveColumnInfoOption(
|
|
1685
|
+
get_column_info: resolveColumnInfoOption(input2)
|
|
1632
1686
|
};
|
|
1633
|
-
if (
|
|
1634
|
-
options.get_access_data = String(
|
|
1687
|
+
if (input2.get_access_data !== void 0) {
|
|
1688
|
+
options.get_access_data = String(input2.get_access_data);
|
|
1635
1689
|
}
|
|
1636
1690
|
try {
|
|
1637
1691
|
const response = await session2.makeRequest("/show/table", {
|
|
1638
|
-
table_name:
|
|
1692
|
+
table_name: input2.table_name,
|
|
1639
1693
|
options
|
|
1640
1694
|
});
|
|
1641
1695
|
if (!response.ok) {
|
|
@@ -1687,7 +1741,7 @@ async function showTable(session2, input5) {
|
|
|
1687
1741
|
properties: properties[0] ?? ""
|
|
1688
1742
|
};
|
|
1689
1743
|
const columns = buildColumnEntries(typeSchemas?.[0], typeSchemas ? properties[0] : void 0);
|
|
1690
|
-
const indexes = await fetchIndexes(session2,
|
|
1744
|
+
const indexes = await fetchIndexes(session2, input2.table_name);
|
|
1691
1745
|
return {
|
|
1692
1746
|
ok: true,
|
|
1693
1747
|
data: { table, columns, indexes }
|
|
@@ -1722,16 +1776,16 @@ var ResourceObjectsSchema = import_zod8.z.object({
|
|
|
1722
1776
|
order_by: import_zod8.z.string().optional(),
|
|
1723
1777
|
limit: import_zod8.z.number().int().min(1).max(1e4).optional().default(100)
|
|
1724
1778
|
});
|
|
1725
|
-
async function getResourceObjects(session2,
|
|
1779
|
+
async function getResourceObjects(session2, input2) {
|
|
1726
1780
|
const options = {
|
|
1727
|
-
table_names:
|
|
1728
|
-
limit: String(
|
|
1781
|
+
table_names: input2.table_names ?? "*",
|
|
1782
|
+
limit: String(input2.limit ?? 100)
|
|
1729
1783
|
};
|
|
1730
|
-
if (
|
|
1731
|
-
options.tiers =
|
|
1784
|
+
if (input2.tiers !== void 0) {
|
|
1785
|
+
options.tiers = input2.tiers;
|
|
1732
1786
|
}
|
|
1733
|
-
if (
|
|
1734
|
-
options.order_by =
|
|
1787
|
+
if (input2.order_by !== void 0) {
|
|
1788
|
+
options.order_by = input2.order_by;
|
|
1735
1789
|
}
|
|
1736
1790
|
try {
|
|
1737
1791
|
const response = await session2.makeRequest("/show/resource/objects", {
|
|
@@ -2135,8 +2189,8 @@ function computeVerification(requestedMap, afterState) {
|
|
|
2135
2189
|
}
|
|
2136
2190
|
return "confirmed";
|
|
2137
2191
|
}
|
|
2138
|
-
async function alterSystemProperties(session2,
|
|
2139
|
-
const requestedKeys = Object.keys(
|
|
2192
|
+
async function alterSystemProperties(session2, input2) {
|
|
2193
|
+
const requestedKeys = Object.keys(input2.property_updates_map);
|
|
2140
2194
|
const disallowed = findDisallowedProperties(requestedKeys);
|
|
2141
2195
|
if (disallowed.length > 0) {
|
|
2142
2196
|
return {
|
|
@@ -2151,7 +2205,7 @@ async function alterSystemProperties(session2, input5) {
|
|
|
2151
2205
|
let rawText;
|
|
2152
2206
|
try {
|
|
2153
2207
|
mutationResponse = await session2.makeRequest("/alter/system/properties", {
|
|
2154
|
-
property_updates_map:
|
|
2208
|
+
property_updates_map: input2.property_updates_map
|
|
2155
2209
|
});
|
|
2156
2210
|
rawText = await mutationResponse.text();
|
|
2157
2211
|
} catch (error) {
|
|
@@ -2213,7 +2267,7 @@ async function alterSystemProperties(session2, input5) {
|
|
|
2213
2267
|
afterState = Object.fromEntries(
|
|
2214
2268
|
requestedKeys.filter((key) => Object.prototype.hasOwnProperty.call(verifyPropertyMap, key)).map((key) => [key, verifyPropertyMap[key]])
|
|
2215
2269
|
);
|
|
2216
|
-
verification = computeVerification(
|
|
2270
|
+
verification = computeVerification(input2.property_updates_map, afterState);
|
|
2217
2271
|
}
|
|
2218
2272
|
}
|
|
2219
2273
|
} catch {
|
|
@@ -2357,29 +2411,29 @@ async function readShardState(session2) {
|
|
|
2357
2411
|
return {};
|
|
2358
2412
|
}
|
|
2359
2413
|
}
|
|
2360
|
-
async function adminRebalance(session2,
|
|
2414
|
+
async function adminRebalance(session2, input2) {
|
|
2361
2415
|
const beforeState = await readShardState(session2);
|
|
2362
2416
|
const options = {};
|
|
2363
|
-
if (
|
|
2364
|
-
options.rebalance_sharded_data = String(
|
|
2417
|
+
if (input2.rebalance_sharded_data !== void 0) {
|
|
2418
|
+
options.rebalance_sharded_data = String(input2.rebalance_sharded_data);
|
|
2365
2419
|
}
|
|
2366
|
-
if (
|
|
2367
|
-
options.rebalance_unsharded_data = String(
|
|
2420
|
+
if (input2.rebalance_unsharded_data !== void 0) {
|
|
2421
|
+
options.rebalance_unsharded_data = String(input2.rebalance_unsharded_data);
|
|
2368
2422
|
}
|
|
2369
|
-
if (
|
|
2370
|
-
options.table_includes =
|
|
2423
|
+
if (input2.table_includes !== void 0) {
|
|
2424
|
+
options.table_includes = input2.table_includes;
|
|
2371
2425
|
}
|
|
2372
|
-
if (
|
|
2373
|
-
options.table_excludes =
|
|
2426
|
+
if (input2.table_excludes !== void 0) {
|
|
2427
|
+
options.table_excludes = input2.table_excludes;
|
|
2374
2428
|
}
|
|
2375
|
-
if (
|
|
2376
|
-
options.aggressiveness = String(
|
|
2429
|
+
if (input2.aggressiveness !== void 0) {
|
|
2430
|
+
options.aggressiveness = String(input2.aggressiveness);
|
|
2377
2431
|
}
|
|
2378
|
-
if (
|
|
2379
|
-
options.compact_after_rebalance = String(
|
|
2432
|
+
if (input2.compact_after_rebalance !== void 0) {
|
|
2433
|
+
options.compact_after_rebalance = String(input2.compact_after_rebalance);
|
|
2380
2434
|
}
|
|
2381
|
-
if (
|
|
2382
|
-
options.compact_only = String(
|
|
2435
|
+
if (input2.compact_only !== void 0) {
|
|
2436
|
+
options.compact_only = String(input2.compact_only);
|
|
2383
2437
|
}
|
|
2384
2438
|
try {
|
|
2385
2439
|
const response = await session2.makeRequest("/admin/rebalance", { options });
|
|
@@ -2452,7 +2506,7 @@ async function readCurrentConfig(session2) {
|
|
|
2452
2506
|
return void 0;
|
|
2453
2507
|
}
|
|
2454
2508
|
}
|
|
2455
|
-
async function alterConfiguration(session2,
|
|
2509
|
+
async function alterConfiguration(session2, input2) {
|
|
2456
2510
|
if (!session2.makeRequestToPort) {
|
|
2457
2511
|
return {
|
|
2458
2512
|
ok: false,
|
|
@@ -2468,7 +2522,7 @@ async function alterConfiguration(session2, input5) {
|
|
|
2468
2522
|
let rawText;
|
|
2469
2523
|
try {
|
|
2470
2524
|
mutationResponse = await session2.makeRequestToPort(hmPort, "/admin/alter/configuration", {
|
|
2471
|
-
config_string:
|
|
2525
|
+
config_string: input2.config_string
|
|
2472
2526
|
});
|
|
2473
2527
|
rawText = await mutationResponse.text();
|
|
2474
2528
|
} catch (error) {
|
|
@@ -2523,28 +2577,26 @@ async function alterConfiguration(session2, input5) {
|
|
|
2523
2577
|
|
|
2524
2578
|
// src/tools/mutation/alter-table-columns.ts
|
|
2525
2579
|
var import_zod15 = require("zod");
|
|
2526
|
-
var
|
|
2527
|
-
var import_picocolors3 = __toESM(require("picocolors"));
|
|
2580
|
+
var import_picocolors4 = __toESM(require("picocolors"));
|
|
2528
2581
|
var import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
|
|
2529
2582
|
|
|
2530
2583
|
// src/approval/checklist.ts
|
|
2531
|
-
var
|
|
2532
|
-
var
|
|
2533
|
-
var DIVIDER = import_picocolors2.default.dim("\u2500".repeat(60));
|
|
2584
|
+
var import_picocolors3 = __toESM(require("picocolors"));
|
|
2585
|
+
var DIVIDER = import_picocolors3.default.dim("\u2500".repeat(60));
|
|
2534
2586
|
function renderChecklist(header, summary, items) {
|
|
2535
2587
|
const lines = [
|
|
2536
2588
|
"",
|
|
2537
2589
|
DIVIDER,
|
|
2538
|
-
` ${
|
|
2590
|
+
` ${import_picocolors3.default.bold(import_picocolors3.default.yellow(header))}`,
|
|
2539
2591
|
"",
|
|
2540
|
-
` ${
|
|
2592
|
+
` ${import_picocolors3.default.dim("Summary:")} ${summary}`,
|
|
2541
2593
|
"",
|
|
2542
|
-
` ${
|
|
2594
|
+
` ${import_picocolors3.default.bold(`${items.length} proposed column change(s):`)}`,
|
|
2543
2595
|
""
|
|
2544
2596
|
];
|
|
2545
2597
|
for (let i = 0; i < items.length; i++) {
|
|
2546
2598
|
const item = items[i];
|
|
2547
|
-
lines.push(` ${
|
|
2599
|
+
lines.push(` ${import_picocolors3.default.bold(`${i + 1}.`)} ${item.label}`, ` ${import_picocolors3.default.dim(item.description)}`);
|
|
2548
2600
|
}
|
|
2549
2601
|
lines.push("", DIVIDER, "");
|
|
2550
2602
|
return lines.join("\n");
|
|
@@ -2553,7 +2605,7 @@ async function showChecklist(header, summary, items) {
|
|
|
2553
2605
|
const panel = renderChecklist(header, summary, items);
|
|
2554
2606
|
process.stderr.write(panel);
|
|
2555
2607
|
try {
|
|
2556
|
-
const selected = await
|
|
2608
|
+
const selected = await checkbox({
|
|
2557
2609
|
message: "Select columns to alter (space to toggle, enter to confirm):",
|
|
2558
2610
|
choices: items.map((item, i) => ({
|
|
2559
2611
|
value: i,
|
|
@@ -2588,12 +2640,12 @@ function buildAlterTableSql(tableName, columns) {
|
|
|
2588
2640
|
return `ALTER TABLE ${tableName}
|
|
2589
2641
|
${clauses.join(",\n")}`;
|
|
2590
2642
|
}
|
|
2591
|
-
var SQL_DIVIDER =
|
|
2643
|
+
var SQL_DIVIDER = import_picocolors4.default.dim("\u2500".repeat(60));
|
|
2592
2644
|
async function confirmSqlExecution(sql) {
|
|
2593
2645
|
const panel = [
|
|
2594
2646
|
"",
|
|
2595
2647
|
SQL_DIVIDER,
|
|
2596
|
-
` ${
|
|
2648
|
+
` ${import_picocolors4.default.bold(import_picocolors4.default.yellow("Generated SQL:"))}`,
|
|
2597
2649
|
"",
|
|
2598
2650
|
sql.split("\n").map((line) => ` ${line}`).join("\n"),
|
|
2599
2651
|
"",
|
|
@@ -2602,7 +2654,7 @@ async function confirmSqlExecution(sql) {
|
|
|
2602
2654
|
].join("\n");
|
|
2603
2655
|
process.stderr.write(panel);
|
|
2604
2656
|
try {
|
|
2605
|
-
const response = await
|
|
2657
|
+
const response = await input({ message: "Execute? (y/n):" });
|
|
2606
2658
|
return response.trim().toLowerCase() === "y";
|
|
2607
2659
|
} catch {
|
|
2608
2660
|
return false;
|
|
@@ -2740,8 +2792,8 @@ function redactNamedField(key, value) {
|
|
|
2740
2792
|
}
|
|
2741
2793
|
return redactValue(value);
|
|
2742
2794
|
}
|
|
2743
|
-
function redactAuditInput(
|
|
2744
|
-
return Object.fromEntries(Object.entries(
|
|
2795
|
+
function redactAuditInput(input2) {
|
|
2796
|
+
return Object.fromEntries(Object.entries(input2).map(([k, v]) => [k, redactNamedField(k, v)]));
|
|
2745
2797
|
}
|
|
2746
2798
|
|
|
2747
2799
|
// src/tools/index.ts
|
|
@@ -2778,14 +2830,14 @@ function applyOutputPipeline(result) {
|
|
|
2778
2830
|
${body}` : body;
|
|
2779
2831
|
return truncateOutput(withNote);
|
|
2780
2832
|
}
|
|
2781
|
-
function logMutationAudit(toolName, result,
|
|
2782
|
-
const statusLabel = result.ok ?
|
|
2783
|
-
const redacted = redactAuditInput(
|
|
2833
|
+
function logMutationAudit(toolName, result, input2) {
|
|
2834
|
+
const statusLabel = result.ok ? import_picocolors5.default.bold(import_picocolors5.default.green("EXECUTED")) : import_picocolors5.default.bold(import_picocolors5.default.red("FAILED"));
|
|
2835
|
+
const redacted = redactAuditInput(input2);
|
|
2784
2836
|
const inputSummary = Object.entries(redacted).map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
2785
2837
|
const displayName = formatToolName(toolName);
|
|
2786
2838
|
process.stderr.write(
|
|
2787
|
-
` ${
|
|
2788
|
-
${
|
|
2839
|
+
` ${import_picocolors5.default.dim("MUTATION")} ${statusLabel} ${displayName}
|
|
2840
|
+
${import_picocolors5.default.dim(inputSummary)}
|
|
2789
2841
|
|
|
2790
2842
|
`
|
|
2791
2843
|
);
|
|
@@ -3739,11 +3791,11 @@ function isValidBudget(value) {
|
|
|
3739
3791
|
function estimateTurnCostUsd(usage, model) {
|
|
3740
3792
|
if (!usage) return 0;
|
|
3741
3793
|
const price = MODEL_PRICING[model];
|
|
3742
|
-
const
|
|
3794
|
+
const input2 = safeCount(usage.inputTokens);
|
|
3743
3795
|
const output = safeCount(usage.outputTokens);
|
|
3744
3796
|
const cacheRead = safeCount(usage.cacheReadInputTokens);
|
|
3745
3797
|
const cacheCreation = safeCount(usage.cacheCreationInputTokens);
|
|
3746
|
-
return (
|
|
3798
|
+
return (input2 * price.inputPerMTok + output * price.outputPerMTok + cacheRead * price.cacheReadPerMTok + cacheCreation * price.cacheCreationPerMTok) / 1e6;
|
|
3747
3799
|
}
|
|
3748
3800
|
function resolveMaxBudgetUsd(flagValue2, env = process.env) {
|
|
3749
3801
|
if (isValidBudget(flagValue2)) return flagValue2;
|
|
@@ -3823,340 +3875,126 @@ var import_claude_agent_sdk4 = require("@anthropic-ai/claude-agent-sdk");
|
|
|
3823
3875
|
// src/tools/bundle/list-files.ts
|
|
3824
3876
|
var import_zod18 = require("zod");
|
|
3825
3877
|
|
|
3826
|
-
// src/bundle/
|
|
3827
|
-
var
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
// Kernel / OS
|
|
3842
|
-
"dmesg.txt": "Kernel ring buffer \u2014 boot and runtime kernel messages (dmesg -T)",
|
|
3843
|
-
"dmesg-timestamp.txt": "Kernel ring buffer with human-readable timestamps",
|
|
3844
|
-
"sysctl.txt": "Kernel tunables (sysctl -a)",
|
|
3845
|
-
"sys.txt": "OS identity, uptime, ulimits, kernel cmdline, clocksource, and loaded modules (uname, ulimit, /proc/cmdline, lsmod)",
|
|
3846
|
-
"lsof.txt": "Open files and network sockets (lsof -n -P)",
|
|
3847
|
-
"lslocks.txt": "Held file locks (lslocks)",
|
|
3848
|
-
// Packages / linker / accounts
|
|
3849
|
-
"deb.txt": "Installed Debian packages and verification (dpkg -l, dpkg -V)",
|
|
3850
|
-
"rpm.txt": "Installed RPM packages (rpm -qa)",
|
|
3851
|
-
"ld.so.conf.txt": "Dynamic-linker library search paths (/etc/ld.so.conf)",
|
|
3852
|
-
"user.txt": "Users, groups, and the gpudb service account (whoami, id, /etc/passwd, /etc/group)",
|
|
3853
|
-
"sudoers.txt": "Sudo configuration (/etc/sudoers)",
|
|
3854
|
-
"etc_profile.txt": "Login shell profile (/etc/profile)",
|
|
3855
|
-
"etc_bashrc.txt": "System bashrc (/etc/bashrc)",
|
|
3856
|
-
"etc_host.txt": "Static hostname resolution (/etc/hosts)",
|
|
3857
|
-
// Kinetica-specific
|
|
3858
|
-
"gpudb.txt": "GPUdb version/build, binary md5 + ldd, and the captured gpudb.conf / gpudb_logger.conf ($GPUDB_EXE -v)",
|
|
3859
|
-
"gpudb_core_etc_gpudb.conf": "The live gpudb.conf at capture time (the database's main config)",
|
|
3860
|
-
"gpudb_core_etc_gpudb_logger.conf": "The logging configuration (gpudb_logger.conf)",
|
|
3861
|
-
"loki-info.txt": "Loki log-index stats: labels, series, and per-class volume (logcli)",
|
|
3862
|
-
"sql-queries.txt": "SQL query log extracted from Loki (logcli)",
|
|
3863
|
-
"tables.txt": "Table schemas and column types (gadmin --schema), when collected",
|
|
3864
|
-
"logfiles.txt": "Manifest: the log directories/files the collector enumerated",
|
|
3865
|
-
"errors.txt": "Collection commands that FAILED during capture (Evidence Gaps)",
|
|
3866
|
-
"proc-logs-erros.txt": "Per-process log-collection failures during capture (Evidence Gaps)"
|
|
3867
|
-
};
|
|
3868
|
-
var KIND_DESCRIPTIONS = {
|
|
3869
|
-
"core-log": "Per-rank rolling Kinetica core log (the primary incident narrative)",
|
|
3870
|
-
"component-log": "Component service log (sql-engine, httpd, reveal, tomcat, stats, \u2026)",
|
|
3871
|
-
"loki-tail": "Last-2h Loki tail for a service (small; searched only when no core logs exist)",
|
|
3872
|
-
"process-info": "Per-rank process snapshot: command line, PID, and environment (/proc/<pid>/environ)",
|
|
3873
|
-
config: "Kinetica configuration file",
|
|
3874
|
-
"version-info": "GPUdb version/build information",
|
|
3875
|
-
"collection-errors": "Collection commands that FAILED during capture (Evidence Gaps)",
|
|
3876
|
-
manifest: "Manifest of log directories/files the collector enumerated"
|
|
3877
|
-
};
|
|
3878
|
-
function basename(relPath) {
|
|
3879
|
-
const parts = relPath.split("/");
|
|
3880
|
-
return parts[parts.length - 1] ?? relPath;
|
|
3878
|
+
// src/bundle/BundleSource.ts
|
|
3879
|
+
var import_promises6 = require("fs/promises");
|
|
3880
|
+
var import_node_path6 = require("path");
|
|
3881
|
+
|
|
3882
|
+
// src/bundle/sysinfo-block.ts
|
|
3883
|
+
var SEPARATOR_RE = /^-{3,}$/;
|
|
3884
|
+
var EXEC_CMD_RE = /^EXEC_CMD:\s?(.*)$/;
|
|
3885
|
+
var EXEC_END_RE = /^EXEC_END with exit code (\d+)\s*:?\s*(.*)$/;
|
|
3886
|
+
var SHOWING_RE = /^### Showing whole log file\s*:/;
|
|
3887
|
+
function trimBlankEdges(lines) {
|
|
3888
|
+
let start = 0;
|
|
3889
|
+
let end = lines.length;
|
|
3890
|
+
while (start < end && lines[start].trim() === "") start++;
|
|
3891
|
+
while (end > start && lines[end - 1].trim() === "") end--;
|
|
3892
|
+
return lines.slice(start, end).join("\n");
|
|
3881
3893
|
}
|
|
3882
|
-
function
|
|
3883
|
-
|
|
3894
|
+
function parseSysinfo(content) {
|
|
3895
|
+
const lines = content.split("\n");
|
|
3896
|
+
let header;
|
|
3897
|
+
const blocks = [];
|
|
3898
|
+
let current;
|
|
3899
|
+
let sawCommand = false;
|
|
3900
|
+
const closeBlock = (exitCode, exitMessage) => {
|
|
3901
|
+
if (!current) return;
|
|
3902
|
+
blocks.push({
|
|
3903
|
+
command: current.command,
|
|
3904
|
+
output: trimBlankEdges(current.output),
|
|
3905
|
+
...exitCode !== void 0 ? { exitCode } : {},
|
|
3906
|
+
...exitMessage !== void 0 && exitMessage !== "" ? { exitMessage } : {}
|
|
3907
|
+
});
|
|
3908
|
+
current = void 0;
|
|
3909
|
+
};
|
|
3910
|
+
for (const line of lines) {
|
|
3911
|
+
if (SEPARATOR_RE.test(line)) continue;
|
|
3912
|
+
const cmdMatch = EXEC_CMD_RE.exec(line);
|
|
3913
|
+
if (cmdMatch) {
|
|
3914
|
+
closeBlock();
|
|
3915
|
+
current = { command: cmdMatch[1].trim(), output: [] };
|
|
3916
|
+
sawCommand = true;
|
|
3917
|
+
continue;
|
|
3918
|
+
}
|
|
3919
|
+
const endMatch = EXEC_END_RE.exec(line);
|
|
3920
|
+
if (endMatch && current) {
|
|
3921
|
+
closeBlock(Number(endMatch[1]), endMatch[2].trim());
|
|
3922
|
+
continue;
|
|
3923
|
+
}
|
|
3924
|
+
if (current) {
|
|
3925
|
+
if (SHOWING_RE.test(line)) continue;
|
|
3926
|
+
current.output.push(line);
|
|
3927
|
+
continue;
|
|
3928
|
+
}
|
|
3929
|
+
if (header === void 0 && !sawCommand && line.trim() !== "") {
|
|
3930
|
+
header = line.trim();
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
closeBlock();
|
|
3934
|
+
return { ...header !== void 0 ? { header } : {}, blocks };
|
|
3884
3935
|
}
|
|
3885
3936
|
|
|
3886
|
-
// src/
|
|
3887
|
-
var
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
const
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
kind: e.kind,
|
|
3899
|
-
rank: e.rank ?? "",
|
|
3900
|
-
size_kb: Math.round(e.sizeBytes / 1024),
|
|
3901
|
-
// What the file contains — so the agent can pick the right one without reading it.
|
|
3902
|
-
description: describeBundleFile(e)
|
|
3903
|
-
}));
|
|
3904
|
-
return {
|
|
3905
|
-
ok: true,
|
|
3906
|
-
data: {
|
|
3907
|
-
detected_version: version ?? "unknown",
|
|
3908
|
-
ranks_present: ranks.join(", ") || "none",
|
|
3909
|
-
services_present: services.join(", ") || "none",
|
|
3910
|
-
total_files: totalFiles,
|
|
3911
|
-
total_size_mb: Number((totalBytes / 1e6).toFixed(1)),
|
|
3912
|
-
counts_by_kind: byKind,
|
|
3913
|
-
failed_collections: errors.length,
|
|
3914
|
-
files
|
|
3937
|
+
// src/bundle/parse-ini.ts
|
|
3938
|
+
var SECTION_RE = /^\[(.+)\]$/;
|
|
3939
|
+
function parseIni(content) {
|
|
3940
|
+
const entries = [];
|
|
3941
|
+
let section = "";
|
|
3942
|
+
for (const rawLine of content.split("\n")) {
|
|
3943
|
+
const line = rawLine.trim();
|
|
3944
|
+
if (line === "" || line.startsWith("#") || line.startsWith(";")) continue;
|
|
3945
|
+
const sectionMatch = SECTION_RE.exec(line);
|
|
3946
|
+
if (sectionMatch) {
|
|
3947
|
+
section = sectionMatch[1].trim();
|
|
3948
|
+
continue;
|
|
3915
3949
|
}
|
|
3916
|
-
|
|
3950
|
+
const eq = line.indexOf("=");
|
|
3951
|
+
if (eq === -1) continue;
|
|
3952
|
+
const key = line.slice(0, eq).trim();
|
|
3953
|
+
const value = line.slice(eq + 1).trim();
|
|
3954
|
+
if (key) entries.push({ section, key, value });
|
|
3955
|
+
}
|
|
3956
|
+
return entries;
|
|
3917
3957
|
}
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
host_manager: import_zod19.z.boolean().describe("Bucket the host-manager (hm) log \u2014 a singleton service, not a rank.").optional(),
|
|
3926
|
-
component: import_zod19.z.string().optional(),
|
|
3927
|
-
include_components: import_zod19.z.boolean().optional()
|
|
3928
|
-
});
|
|
3929
|
-
async function bundleLogTimeline(source, args = {}) {
|
|
3930
|
-
const query3 = {
|
|
3931
|
-
...args.min_severity !== void 0 ? { minSeverity: args.min_severity } : {},
|
|
3932
|
-
...args.granularity !== void 0 ? { granularity: args.granularity } : {},
|
|
3933
|
-
...args.rank !== void 0 ? { rank: args.rank } : {},
|
|
3934
|
-
...args.host_manager !== void 0 ? { hostManager: args.host_manager } : {},
|
|
3935
|
-
...args.component !== void 0 ? { component: args.component } : {},
|
|
3936
|
-
...args.include_components !== void 0 ? { includeComponents: args.include_components } : {}
|
|
3937
|
-
};
|
|
3938
|
-
const result = await source.logTimeline(query3);
|
|
3939
|
-
const severities = [...new Set(result.buckets.flatMap((b) => Object.keys(b.counts)))];
|
|
3940
|
-
const order = ["FATAL", "ERROR", "UERR", "WARN", "INFO"];
|
|
3941
|
-
severities.sort((a, b) => order.indexOf(a) - order.indexOf(b));
|
|
3942
|
-
const rows = result.buckets.map((b) => {
|
|
3943
|
-
const row = { time_bucket: b.bucket };
|
|
3944
|
-
for (const sev of severities) row[sev] = b.counts[sev] ?? 0;
|
|
3945
|
-
row.total = b.total;
|
|
3946
|
-
return row;
|
|
3958
|
+
function filterIni(entries, opts = {}) {
|
|
3959
|
+
const section = opts.section?.toLowerCase();
|
|
3960
|
+
const key = opts.key?.toLowerCase();
|
|
3961
|
+
return entries.filter((e) => {
|
|
3962
|
+
if (section !== void 0 && e.section.toLowerCase() !== section) return false;
|
|
3963
|
+
if (key !== void 0 && !e.key.toLowerCase().includes(key)) return false;
|
|
3964
|
+
return true;
|
|
3947
3965
|
});
|
|
3948
|
-
return {
|
|
3949
|
-
ok: true,
|
|
3950
|
-
note: result.totalCounted === 0 ? "No lines at or above the severity threshold \u2014 try a lower min_severity." : `${result.totalCounted} event(s) across ${result.buckets.length} bucket(s), ${result.filesScanned.length} file(s).`,
|
|
3951
|
-
data: {
|
|
3952
|
-
lines_scanned: result.linesScanned,
|
|
3953
|
-
files_scanned: result.filesScanned.join(", ") || "none",
|
|
3954
|
-
buckets: rows
|
|
3955
|
-
}
|
|
3956
|
-
};
|
|
3957
3966
|
}
|
|
3958
3967
|
|
|
3959
|
-
// src/
|
|
3960
|
-
var
|
|
3961
|
-
var
|
|
3962
|
-
regex: import_zod20.z.string().optional(),
|
|
3963
|
-
min_severity: import_zod20.z.enum(["INFO", "WARN", "UERR", "ERROR", "FATAL"]).optional(),
|
|
3964
|
-
from_ts: import_zod20.z.string().optional(),
|
|
3965
|
-
to_ts: import_zod20.z.string().optional(),
|
|
3966
|
-
rank: import_zod20.z.string().describe('Numeric rank only, e.g. "r0"/"r1". For the host manager use host_manager.').optional(),
|
|
3967
|
-
host_manager: import_zod20.z.boolean().describe("Search the host-manager (hm) log \u2014 a singleton service, not a rank.").optional(),
|
|
3968
|
-
component: import_zod20.z.string().optional(),
|
|
3969
|
-
include_components: import_zod20.z.boolean().optional(),
|
|
3970
|
-
max_matches: import_zod20.z.number().int().min(1).max(1e3).optional()
|
|
3971
|
-
});
|
|
3972
|
-
async function bundleSearchLogs(source, args = {}) {
|
|
3973
|
-
const query3 = {
|
|
3974
|
-
...args.regex !== void 0 ? { regex: args.regex } : {},
|
|
3975
|
-
...args.min_severity !== void 0 ? { minSeverity: args.min_severity } : {},
|
|
3976
|
-
...args.from_ts !== void 0 ? { fromTs: args.from_ts } : {},
|
|
3977
|
-
...args.to_ts !== void 0 ? { toTs: args.to_ts } : {},
|
|
3978
|
-
...args.rank !== void 0 ? { rank: args.rank } : {},
|
|
3979
|
-
...args.host_manager !== void 0 ? { hostManager: args.host_manager } : {},
|
|
3980
|
-
...args.component !== void 0 ? { component: args.component } : {},
|
|
3981
|
-
...args.include_components !== void 0 ? { includeComponents: args.include_components } : {},
|
|
3982
|
-
...args.max_matches !== void 0 ? { maxMatches: args.max_matches } : {}
|
|
3983
|
-
};
|
|
3984
|
-
const result = await source.searchLogs(query3);
|
|
3985
|
-
const note = result.capped ? `Showing ${result.matches.length} of ${result.totalMatched} matches across ${result.filesScanned.length} file(s) (display capped). Narrow with a tighter regex, severity, or time window to surface the specific lines.` : `${result.totalMatched} match(es) across ${result.filesScanned.length} file(s).`;
|
|
3986
|
-
return {
|
|
3987
|
-
ok: true,
|
|
3988
|
-
note,
|
|
3989
|
-
data: {
|
|
3990
|
-
total_matched: result.totalMatched,
|
|
3991
|
-
lines_scanned: result.linesScanned,
|
|
3992
|
-
files_scanned: result.filesScanned.join(", ") || "none",
|
|
3993
|
-
capped: result.capped,
|
|
3994
|
-
matches: result.matches.map((m) => ({
|
|
3995
|
-
file: m.file,
|
|
3996
|
-
line: m.lineNumber,
|
|
3997
|
-
timestamp: m.timestamp ?? "",
|
|
3998
|
-
severity: m.severity ?? "",
|
|
3999
|
-
rank: m.rank ?? "",
|
|
4000
|
-
message: m.message
|
|
4001
|
-
}))
|
|
4002
|
-
}
|
|
4003
|
-
};
|
|
4004
|
-
}
|
|
4005
|
-
|
|
4006
|
-
// src/tools/bundle/read-config.ts
|
|
4007
|
-
var import_zod21 = require("zod");
|
|
4008
|
-
var BundleReadConfigSchema = import_zod21.z.object({
|
|
4009
|
-
section: import_zod21.z.string().optional(),
|
|
4010
|
-
key: import_zod21.z.string().optional()
|
|
4011
|
-
});
|
|
4012
|
-
async function bundleReadConfig(source, args = {}) {
|
|
4013
|
-
const result = await source.readConfig({
|
|
4014
|
-
...args.section !== void 0 ? { section: args.section } : {},
|
|
4015
|
-
...args.key !== void 0 ? { key: args.key } : {}
|
|
4016
|
-
});
|
|
4017
|
-
if ("error" in result) {
|
|
4018
|
-
return { ok: false, status: 0, error: result.error, raw: "" };
|
|
4019
|
-
}
|
|
4020
|
-
if (result.entries.length === 0 && args.section !== void 0) {
|
|
4021
|
-
const all = await source.readConfig(args.key !== void 0 ? { key: args.key } : {});
|
|
4022
|
-
const sections = "error" in all ? [] : [...new Set(all.entries.map((e) => e.section))].sort();
|
|
4023
|
-
const sectionList = sections.map((s) => s === "" ? "(flat/top-level)" : s).join(", ");
|
|
4024
|
-
return {
|
|
4025
|
-
ok: true,
|
|
4026
|
-
note: `No entries in section "${args.section}" of ${result.file}. gpudb.conf is largely flat \u2014 retry filtering by key only. Sections present: ${sectionList || "(none)"}.`,
|
|
4027
|
-
data: { section_not_found: args.section, available_sections: sections }
|
|
4028
|
-
};
|
|
4029
|
-
}
|
|
4030
|
-
return {
|
|
4031
|
-
ok: true,
|
|
4032
|
-
note: `${result.entries.length} entr(y/ies) from ${result.file}.`,
|
|
4033
|
-
data: result.entries.map((e) => ({ section: e.section, key: e.key, value: e.value }))
|
|
4034
|
-
};
|
|
4035
|
-
}
|
|
4036
|
-
|
|
4037
|
-
// src/tools/bundle/read-sysinfo.ts
|
|
4038
|
-
var import_zod22 = require("zod");
|
|
4039
|
-
var BundleReadSysinfoSchema = import_zod22.z.object({
|
|
4040
|
-
name: import_zod22.z.string().min(1)
|
|
4041
|
-
});
|
|
4042
|
-
async function bundleReadSysinfo(source, args) {
|
|
4043
|
-
const result = await source.readSysinfo(args.name);
|
|
4044
|
-
if ("error" in result) {
|
|
4045
|
-
return { ok: false, status: 0, error: result.error, raw: "" };
|
|
4046
|
-
}
|
|
4047
|
-
return {
|
|
4048
|
-
ok: true,
|
|
4049
|
-
data: {
|
|
4050
|
-
...result.header !== void 0 ? { source_file: result.header } : {},
|
|
4051
|
-
blocks: result.blocks.map((b) => ({
|
|
4052
|
-
command: b.command,
|
|
4053
|
-
...b.exitCode !== void 0 ? { exit_code: b.exitCode } : {},
|
|
4054
|
-
output: b.output
|
|
4055
|
-
}))
|
|
4056
|
-
}
|
|
4057
|
-
};
|
|
4058
|
-
}
|
|
4059
|
-
|
|
4060
|
-
// src/tools/bundle/load-bundle.ts
|
|
4061
|
-
var import_zod23 = require("zod");
|
|
4062
|
-
|
|
4063
|
-
// src/bundle/verify-bundle.ts
|
|
4064
|
-
var import_promises6 = require("fs/promises");
|
|
4065
|
-
|
|
4066
|
-
// src/bundle/BundleSource.ts
|
|
4067
|
-
var import_promises5 = require("fs/promises");
|
|
4068
|
-
var import_node_path6 = require("path");
|
|
3968
|
+
// src/bundle/log-search.ts
|
|
3969
|
+
var import_node_fs4 = require("fs");
|
|
3970
|
+
var import_node_readline = require("readline");
|
|
4069
3971
|
|
|
4070
|
-
// src/bundle/
|
|
4071
|
-
var
|
|
4072
|
-
var
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
let
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
function parseSysinfo(content) {
|
|
4083
|
-
const lines = content.split("\n");
|
|
4084
|
-
let header;
|
|
4085
|
-
const blocks = [];
|
|
4086
|
-
let current;
|
|
4087
|
-
let sawCommand = false;
|
|
4088
|
-
const closeBlock = (exitCode, exitMessage) => {
|
|
4089
|
-
if (!current) return;
|
|
4090
|
-
blocks.push({
|
|
4091
|
-
command: current.command,
|
|
4092
|
-
output: trimBlankEdges(current.output),
|
|
4093
|
-
...exitCode !== void 0 ? { exitCode } : {},
|
|
4094
|
-
...exitMessage !== void 0 && exitMessage !== "" ? { exitMessage } : {}
|
|
4095
|
-
});
|
|
4096
|
-
current = void 0;
|
|
4097
|
-
};
|
|
4098
|
-
for (const line of lines) {
|
|
4099
|
-
if (SEPARATOR_RE.test(line)) continue;
|
|
4100
|
-
const cmdMatch = EXEC_CMD_RE.exec(line);
|
|
4101
|
-
if (cmdMatch) {
|
|
4102
|
-
closeBlock();
|
|
4103
|
-
current = { command: cmdMatch[1].trim(), output: [] };
|
|
4104
|
-
sawCommand = true;
|
|
4105
|
-
continue;
|
|
4106
|
-
}
|
|
4107
|
-
const endMatch = EXEC_END_RE.exec(line);
|
|
4108
|
-
if (endMatch && current) {
|
|
4109
|
-
closeBlock(Number(endMatch[1]), endMatch[2].trim());
|
|
4110
|
-
continue;
|
|
4111
|
-
}
|
|
4112
|
-
if (current) {
|
|
4113
|
-
if (SHOWING_RE.test(line)) continue;
|
|
4114
|
-
current.output.push(line);
|
|
4115
|
-
continue;
|
|
4116
|
-
}
|
|
4117
|
-
if (header === void 0 && !sawCommand && line.trim() !== "") {
|
|
4118
|
-
header = line.trim();
|
|
4119
|
-
}
|
|
3972
|
+
// src/bundle/unwrap-loki-jsonl.ts
|
|
3973
|
+
var LEADING_TS_RE = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+)\s+/;
|
|
3974
|
+
var HEADER_BODY_SEP = " : ";
|
|
3975
|
+
function unwrapLokiJsonl(line) {
|
|
3976
|
+
let i = 0;
|
|
3977
|
+
while (i < line.length && line.charCodeAt(i) <= 32) i++;
|
|
3978
|
+
if (line.charCodeAt(i) !== 123) return void 0;
|
|
3979
|
+
let obj;
|
|
3980
|
+
try {
|
|
3981
|
+
obj = JSON.parse(line);
|
|
3982
|
+
} catch {
|
|
3983
|
+
return void 0;
|
|
4120
3984
|
}
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
for (const rawLine of content.split("\n")) {
|
|
4131
|
-
const line = rawLine.trim();
|
|
4132
|
-
if (line === "" || line.startsWith("#") || line.startsWith(";")) continue;
|
|
4133
|
-
const sectionMatch = SECTION_RE.exec(line);
|
|
4134
|
-
if (sectionMatch) {
|
|
4135
|
-
section = sectionMatch[1].trim();
|
|
4136
|
-
continue;
|
|
4137
|
-
}
|
|
4138
|
-
const eq = line.indexOf("=");
|
|
4139
|
-
if (eq === -1) continue;
|
|
4140
|
-
const key = line.slice(0, eq).trim();
|
|
4141
|
-
const value = line.slice(eq + 1).trim();
|
|
4142
|
-
if (key) entries.push({ section, key, value });
|
|
3985
|
+
if (typeof obj !== "object" || obj === null) return void 0;
|
|
3986
|
+
const inner = obj.line;
|
|
3987
|
+
if (typeof inner !== "string") return void 0;
|
|
3988
|
+
const tsMatch = LEADING_TS_RE.exec(inner);
|
|
3989
|
+
const sepIdx = inner.indexOf(HEADER_BODY_SEP);
|
|
3990
|
+
if (tsMatch && sepIdx !== -1) {
|
|
3991
|
+
const ts = tsMatch[1];
|
|
3992
|
+
const body = inner.slice(sepIdx + HEADER_BODY_SEP.length).trim();
|
|
3993
|
+
return `${ts} ${body}`;
|
|
4143
3994
|
}
|
|
4144
|
-
return
|
|
4145
|
-
}
|
|
4146
|
-
function filterIni(entries, opts = {}) {
|
|
4147
|
-
const section = opts.section?.toLowerCase();
|
|
4148
|
-
const key = opts.key?.toLowerCase();
|
|
4149
|
-
return entries.filter((e) => {
|
|
4150
|
-
if (section !== void 0 && e.section.toLowerCase() !== section) return false;
|
|
4151
|
-
if (key !== void 0 && !e.key.toLowerCase().includes(key)) return false;
|
|
4152
|
-
return true;
|
|
4153
|
-
});
|
|
3995
|
+
return inner;
|
|
4154
3996
|
}
|
|
4155
3997
|
|
|
4156
|
-
// src/bundle/log-search.ts
|
|
4157
|
-
var import_node_fs4 = require("fs");
|
|
4158
|
-
var import_node_readline = require("readline");
|
|
4159
|
-
|
|
4160
3998
|
// src/bundle/parse-log-line.ts
|
|
4161
3999
|
var PREFIX_RE = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+)\s+([A-Z]+)\s+\(([^)]*)\)\s*(.*)$/;
|
|
4162
4000
|
var CORE_TAIL_RE = /^(\S+)\s+(\S+:\d+)\s+-\s+(.*)$/;
|
|
@@ -4175,9 +4013,10 @@ function severityRank(severity) {
|
|
|
4175
4013
|
return SEVERITY_RANK[severity] ?? -1;
|
|
4176
4014
|
}
|
|
4177
4015
|
function parseLogLine(line) {
|
|
4178
|
-
const
|
|
4016
|
+
const effective = unwrapLokiJsonl(line) ?? line;
|
|
4017
|
+
const match = PREFIX_RE.exec(effective);
|
|
4179
4018
|
if (!match) {
|
|
4180
|
-
return { message:
|
|
4019
|
+
return { message: effective, raw: line };
|
|
4181
4020
|
}
|
|
4182
4021
|
const [, timestamp, severity, paren, rest] = match;
|
|
4183
4022
|
const parts = paren.split(",");
|
|
@@ -4330,17 +4169,26 @@ async function aggregateTimeline(filePath, query3 = {}) {
|
|
|
4330
4169
|
}
|
|
4331
4170
|
|
|
4332
4171
|
// src/bundle/bundle-index.ts
|
|
4333
|
-
var
|
|
4172
|
+
var import_promises5 = require("fs/promises");
|
|
4334
4173
|
var import_node_path5 = require("path");
|
|
4335
4174
|
|
|
4336
4175
|
// src/bundle/classify-file.ts
|
|
4337
|
-
var ROLLING_ID_RE = /core-gpudb-rolling-(r\d+|hm)\.log
|
|
4176
|
+
var ROLLING_ID_RE = /(?:core-)?gpudb-rolling-(r\d+|hm)\.log(?:\.\d+)?$/;
|
|
4338
4177
|
var EXE_ID_RE = /gpudb-exe-(r\d+|hm)-/;
|
|
4339
4178
|
var HOST_RE = /\b(node\w+)\b/;
|
|
4179
|
+
var CONF_RE = /\.conf$/i;
|
|
4180
|
+
var CONF_ALT_RE = /\.(cfg|ini)$/i;
|
|
4181
|
+
var LOG_RE = /\.log(?:\.\d+)?$/;
|
|
4182
|
+
var LOGISH_RE = /\.(?:log|out|err)(?:\.\d+)?$/i;
|
|
4183
|
+
var LOKI_RANK_RE = /^rank(\d+)\.log$/;
|
|
4184
|
+
var LOKI_HM_BASE = "hostmanager.log";
|
|
4185
|
+
var HM_TOKEN_RE = /host-?manager/i;
|
|
4186
|
+
var RANK_TOKEN_RE = /(?:\brank[-_]?|\br)(\d{1,2})\b/i;
|
|
4187
|
+
var LOG_DIR_RE = /(?:^|\/)(?:logs|logs-local|log)(?:\/|$)/;
|
|
4340
4188
|
function rankOrService(id) {
|
|
4341
4189
|
return id === "hm" ? { service: "host-manager" } : { rank: id };
|
|
4342
4190
|
}
|
|
4343
|
-
function
|
|
4191
|
+
function basename(relPath) {
|
|
4344
4192
|
const parts = relPath.split("/");
|
|
4345
4193
|
return parts[parts.length - 1] ?? relPath;
|
|
4346
4194
|
}
|
|
@@ -4352,65 +4200,247 @@ function inferHost(relPath) {
|
|
|
4352
4200
|
return HOST_RE.exec(relPath)?.[1] ?? void 0;
|
|
4353
4201
|
}
|
|
4354
4202
|
function componentName(base) {
|
|
4355
|
-
return base.replace(/(
|
|
4203
|
+
return base.replace(/\.\d+$/, "").replace(/(?:\.(?:log|out|err))+$/i, "").replace(/^core-gpudb-/, "").replace(/^gpudb-/, "").replace(/-node\w+$/, "");
|
|
4204
|
+
}
|
|
4205
|
+
function cls(kind, confidence, reason, parts = {}) {
|
|
4206
|
+
return {
|
|
4207
|
+
kind,
|
|
4208
|
+
confidence,
|
|
4209
|
+
reason,
|
|
4210
|
+
...parts.rank !== void 0 ? { rank: parts.rank } : {},
|
|
4211
|
+
...parts.inferredRank !== void 0 ? { inferredRank: parts.inferredRank } : {},
|
|
4212
|
+
...parts.service !== void 0 ? { service: parts.service } : {},
|
|
4213
|
+
...parts.component !== void 0 ? { component: parts.component } : {},
|
|
4214
|
+
...parts.host !== void 0 ? { host: parts.host } : {}
|
|
4215
|
+
};
|
|
4356
4216
|
}
|
|
4217
|
+
var MATCHERS = [
|
|
4218
|
+
// ── Tier A: canonical filenames / locations (exact) ──────────────────────────
|
|
4219
|
+
(c) => CONF_RE.test(c.base) ? cls("config", "exact", "config (.conf)", { host: c.host }) : null,
|
|
4220
|
+
(c) => CONF_ALT_RE.test(c.base) ? cls("config", "inferred", "config-like extension (.cfg/.ini)", { host: c.host }) : null,
|
|
4221
|
+
(c) => c.base === "logfiles.txt" ? cls("manifest", "exact", "collector manifest", { host: c.host }) : null,
|
|
4222
|
+
(c) => c.base === "errors.txt" || c.base.endsWith("erros.txt") ? cls("collection-errors", "exact", "collection-errors summary", { host: c.host }) : null,
|
|
4223
|
+
(c) => c.base === "gpudb.txt" ? cls("version-info", "exact", "gpudb.txt", { host: c.host }) : null,
|
|
4224
|
+
(c) => {
|
|
4225
|
+
const m = EXE_ID_RE.exec(c.base);
|
|
4226
|
+
return m ? cls("process-info", "exact", "gpudb-exe process capture", {
|
|
4227
|
+
...rankOrService(m[1]),
|
|
4228
|
+
host: c.host
|
|
4229
|
+
}) : null;
|
|
4230
|
+
},
|
|
4231
|
+
(c) => {
|
|
4232
|
+
const m = ROLLING_ID_RE.exec(c.base);
|
|
4233
|
+
if (!m) return null;
|
|
4234
|
+
const reason = c.base.startsWith("core-") ? "core rolling-log pattern" : "rolling-log pattern (no core- prefix)";
|
|
4235
|
+
return cls("core-log", "exact", reason, { ...rankOrService(m[1]), host: c.host });
|
|
4236
|
+
},
|
|
4237
|
+
(c) => {
|
|
4238
|
+
if (c.dir !== "logs" || !LOG_RE.test(c.base)) return null;
|
|
4239
|
+
const lr = LOKI_RANK_RE.exec(c.base);
|
|
4240
|
+
const lokiId = lr ? `r${lr[1]}` : c.base === LOKI_HM_BASE ? "hm" : void 0;
|
|
4241
|
+
return lokiId !== void 0 ? cls("loki-tail", "exact", "Loki per-rank/host-manager export under logs/", {
|
|
4242
|
+
...rankOrService(lokiId),
|
|
4243
|
+
host: c.host
|
|
4244
|
+
}) : cls("loki-tail", "exact", "Loki component tail under logs/", {
|
|
4245
|
+
component: componentName(c.base),
|
|
4246
|
+
host: c.host
|
|
4247
|
+
});
|
|
4248
|
+
},
|
|
4249
|
+
(c) => c.dir === "logs-local" && LOG_RE.test(c.base) ? cls("component-log", "exact", "component log under logs-local/", {
|
|
4250
|
+
component: componentName(c.base),
|
|
4251
|
+
host: c.host
|
|
4252
|
+
}) : null,
|
|
4253
|
+
// ── Tier B: off-shape name/extension heuristics (inferred) ───────────────────
|
|
4254
|
+
// Host-manager service logs in a flat layout: the rolling-hm log is already caught
|
|
4255
|
+
// above; this catches the service log and the process stdout (.out). This MUST come
|
|
4256
|
+
// before the generic gpudb-prefixed matcher below — both would classify a
|
|
4257
|
+
// `gpudb-host-manager-*.log` as a component-log, but only this one adds the
|
|
4258
|
+
// `service: "host-manager"` tag. Kept separate (not folded into the gpudb matcher) so
|
|
4259
|
+
// a host-manager log WITHOUT a gpudb prefix (e.g. a renamed `hostmanager-*.out`) still
|
|
4260
|
+
// gets the service tag rather than falling through to a plain component-log.
|
|
4261
|
+
(c) => HM_TOKEN_RE.test(c.base) && LOGISH_RE.test(c.base) ? cls("component-log", "inferred", "host-manager service log (name match)", {
|
|
4262
|
+
service: "host-manager",
|
|
4263
|
+
component: componentName(c.base),
|
|
4264
|
+
host: c.host
|
|
4265
|
+
}) : null,
|
|
4266
|
+
// Any other gpudb-prefixed log-ish file in a non-canonical location.
|
|
4267
|
+
(c) => (c.base.startsWith("gpudb") || c.base.startsWith("core-gpudb")) && LOGISH_RE.test(c.base) ? cls("component-log", "inferred", "gpudb log (name match, non-canonical location)", {
|
|
4268
|
+
component: componentName(c.base),
|
|
4269
|
+
host: c.host
|
|
4270
|
+
}) : null,
|
|
4271
|
+
// A log-ish file sitting in a log-named directory, or carrying a rank token.
|
|
4272
|
+
(c) => {
|
|
4273
|
+
if (!LOGISH_RE.test(c.base)) return null;
|
|
4274
|
+
const inLogDir = LOG_DIR_RE.test(c.relPath);
|
|
4275
|
+
const rm = RANK_TOKEN_RE.exec(c.base);
|
|
4276
|
+
if (!inLogDir && !rm) return null;
|
|
4277
|
+
const rank = rm ? `r${rm[1]}` : void 0;
|
|
4278
|
+
const reason = rank ? "log-like file with a rank token" : "log-like file in a log directory";
|
|
4279
|
+
return cls(c.dir === "logs" ? "loki-tail" : "component-log", "inferred", reason, {
|
|
4280
|
+
...rank !== void 0 ? { rank, inferredRank: true } : { component: componentName(c.base) },
|
|
4281
|
+
host: c.host
|
|
4282
|
+
});
|
|
4283
|
+
},
|
|
4284
|
+
// ── Tier C: extension-only fallbacks (weak) ──────────────────────────────────
|
|
4285
|
+
(c) => c.base.endsWith(".txt") ? cls("os-diag", "weak", "fallback: .txt extension", { host: c.host }) : null,
|
|
4286
|
+
(c) => LOGISH_RE.test(c.base) ? cls("component-log", "weak", "fallback: log-like extension", {
|
|
4287
|
+
component: componentName(c.base),
|
|
4288
|
+
host: c.host
|
|
4289
|
+
}) : null
|
|
4290
|
+
];
|
|
4357
4291
|
function classifyFile(relPath) {
|
|
4358
|
-
const base =
|
|
4292
|
+
const base = basename(relPath);
|
|
4359
4293
|
const dir = dirOf(relPath);
|
|
4360
4294
|
const host = inferHost(relPath);
|
|
4361
|
-
|
|
4362
|
-
|
|
4295
|
+
const ctx = { relPath, base, dir, ...host !== void 0 ? { host } : {} };
|
|
4296
|
+
for (const matcher of MATCHERS) {
|
|
4297
|
+
const result = matcher(ctx);
|
|
4298
|
+
if (result) return result;
|
|
4363
4299
|
}
|
|
4364
|
-
|
|
4365
|
-
|
|
4300
|
+
return cls("unknown", "weak", "unrecognized file", { host });
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4303
|
+
// src/bundle/sniff-file.ts
|
|
4304
|
+
var import_promises4 = require("fs/promises");
|
|
4305
|
+
var SNIFF_HEAD_BYTES = 8192;
|
|
4306
|
+
var SNIFF_MAX_LINES = 20;
|
|
4307
|
+
async function readHead(absPath, headBytes) {
|
|
4308
|
+
let fh;
|
|
4309
|
+
try {
|
|
4310
|
+
fh = await (0, import_promises4.open)(absPath, "r");
|
|
4311
|
+
const buf = Buffer.alloc(headBytes);
|
|
4312
|
+
const { bytesRead } = await fh.read(buf, 0, headBytes, 0);
|
|
4313
|
+
return buf.subarray(0, bytesRead).toString("utf-8");
|
|
4314
|
+
} catch {
|
|
4315
|
+
return "";
|
|
4316
|
+
} finally {
|
|
4317
|
+
await fh?.close().catch(() => void 0);
|
|
4366
4318
|
}
|
|
4367
|
-
|
|
4368
|
-
|
|
4319
|
+
}
|
|
4320
|
+
function refineSysinfoKind(command) {
|
|
4321
|
+
const cmd = command.toLowerCase();
|
|
4322
|
+
if (/-v\b|--version|\bgpudb_logger\b/.test(cmd) && cmd.includes("gpudb")) {
|
|
4323
|
+
return { kind: "version-info", detail: "version command" };
|
|
4369
4324
|
}
|
|
4370
|
-
if (
|
|
4371
|
-
return { kind: "
|
|
4325
|
+
if (/\bps\b|\/proc\/|environ|grep .*gpudb/.test(cmd)) {
|
|
4326
|
+
return { kind: "process-info", detail: "process snapshot command" };
|
|
4372
4327
|
}
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4328
|
+
return { kind: "os-diag", detail: "host-diagnostic command" };
|
|
4329
|
+
}
|
|
4330
|
+
function logLineResult(rank, severity, isHm) {
|
|
4331
|
+
if (rank !== void 0) {
|
|
4332
|
+
return { kind: "core-log", reason: `log line parsed (${severity}, rank ${rank})`, rank };
|
|
4333
|
+
}
|
|
4334
|
+
if (isHm) {
|
|
4335
|
+
return {
|
|
4336
|
+
kind: "component-log",
|
|
4337
|
+
reason: `log line parsed (${severity}, host-manager)`,
|
|
4338
|
+
service: "host-manager"
|
|
4339
|
+
};
|
|
4376
4340
|
}
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4341
|
+
return { kind: "component-log", reason: `log line parsed (${severity})` };
|
|
4342
|
+
}
|
|
4343
|
+
async function sniffFile(absPath, opts = {}) {
|
|
4344
|
+
const headBytes = opts.headBytes ?? SNIFF_HEAD_BYTES;
|
|
4345
|
+
const maxLines = opts.maxLines ?? SNIFF_MAX_LINES;
|
|
4346
|
+
const text2 = await readHead(absPath, headBytes);
|
|
4347
|
+
if (text2 === "") return void 0;
|
|
4348
|
+
const lines = [];
|
|
4349
|
+
for (const raw of text2.split("\n")) {
|
|
4350
|
+
const trimmed = raw.trim();
|
|
4351
|
+
if (trimmed === "") continue;
|
|
4352
|
+
lines.push(raw);
|
|
4353
|
+
if (lines.length >= maxLines) break;
|
|
4354
|
+
}
|
|
4355
|
+
if (lines.length === 0) return void 0;
|
|
4356
|
+
for (const line of lines) {
|
|
4357
|
+
const m = EXEC_CMD_RE.exec(line.trim());
|
|
4358
|
+
if (m) {
|
|
4359
|
+
const { kind, detail } = refineSysinfoKind(m[1]);
|
|
4360
|
+
return { kind, reason: `EXEC_CMD header (${detail})` };
|
|
4381
4361
|
}
|
|
4382
|
-
|
|
4383
|
-
|
|
4362
|
+
}
|
|
4363
|
+
const unwrapped = unwrapLokiJsonl(lines[0]);
|
|
4364
|
+
if (unwrapped !== void 0) {
|
|
4365
|
+
const p = parseLogLine(unwrapped);
|
|
4366
|
+
const rank = p.rank;
|
|
4367
|
+
return {
|
|
4368
|
+
kind: "loki-tail",
|
|
4369
|
+
reason: `Loki JSONL record${rank ? ` (rank ${rank})` : ""}`,
|
|
4370
|
+
...rank !== void 0 ? { rank } : {}
|
|
4371
|
+
};
|
|
4372
|
+
}
|
|
4373
|
+
for (const line of lines) {
|
|
4374
|
+
const p = parseLogLine(line);
|
|
4375
|
+
if (p.severity !== void 0 && severityRank(p.severity) >= 0) {
|
|
4376
|
+
const isHm = p.context?.startsWith("hm/") ?? false;
|
|
4377
|
+
return logLineResult(p.rank, p.severity, isHm);
|
|
4384
4378
|
}
|
|
4385
|
-
return { kind: "component-log", component: componentName(base), ...host ? { host } : {} };
|
|
4386
4379
|
}
|
|
4387
|
-
|
|
4388
|
-
|
|
4380
|
+
const hasSection = lines.some((l) => SECTION_RE.test(l.trim()));
|
|
4381
|
+
if (hasSection && parseIni(text2).length >= 2) {
|
|
4382
|
+
return { kind: "config", reason: "INI section + key/value entries" };
|
|
4389
4383
|
}
|
|
4390
|
-
return
|
|
4384
|
+
return void 0;
|
|
4391
4385
|
}
|
|
4392
4386
|
|
|
4393
4387
|
// src/bundle/bundle-index.ts
|
|
4388
|
+
async function refineWithContent(c, absPath) {
|
|
4389
|
+
if (c.confidence !== "weak" || c.kind === "os-diag") return c;
|
|
4390
|
+
const sniff = await sniffFile(absPath);
|
|
4391
|
+
if (!sniff) return c;
|
|
4392
|
+
const addsKind = sniff.kind !== c.kind;
|
|
4393
|
+
const addsRank = sniff.rank !== void 0 && c.rank === void 0;
|
|
4394
|
+
const addsService = sniff.service !== void 0 && c.service === void 0;
|
|
4395
|
+
if (!addsKind && !addsRank && !addsService) return c;
|
|
4396
|
+
return {
|
|
4397
|
+
...c,
|
|
4398
|
+
kind: sniff.kind,
|
|
4399
|
+
confidence: "inferred",
|
|
4400
|
+
reason: `content: ${sniff.reason}`,
|
|
4401
|
+
...sniff.rank !== void 0 ? { rank: sniff.rank } : {},
|
|
4402
|
+
...sniff.service !== void 0 ? { service: sniff.service } : {}
|
|
4403
|
+
};
|
|
4404
|
+
}
|
|
4394
4405
|
async function buildIndex(rootDir) {
|
|
4395
4406
|
let relPaths;
|
|
4407
|
+
let realRoot;
|
|
4396
4408
|
try {
|
|
4397
|
-
relPaths = await (0,
|
|
4409
|
+
relPaths = await (0, import_promises5.readdir)(rootDir, { recursive: true });
|
|
4410
|
+
realRoot = await (0, import_promises5.realpath)(rootDir);
|
|
4398
4411
|
} catch {
|
|
4399
4412
|
return [];
|
|
4400
4413
|
}
|
|
4414
|
+
const dirConfined = /* @__PURE__ */ new Map();
|
|
4415
|
+
const isDirConfined = (dir) => {
|
|
4416
|
+
let verdict = dirConfined.get(dir);
|
|
4417
|
+
if (verdict === void 0) {
|
|
4418
|
+
verdict = (0, import_promises5.realpath)(dir).then(
|
|
4419
|
+
(realDir) => realDir === realRoot || realDir.startsWith(realRoot + import_node_path5.sep),
|
|
4420
|
+
() => false
|
|
4421
|
+
// an unresolvable directory (broken/cyclic symlink) → drop its entries
|
|
4422
|
+
);
|
|
4423
|
+
dirConfined.set(dir, verdict);
|
|
4424
|
+
}
|
|
4425
|
+
return verdict;
|
|
4426
|
+
};
|
|
4401
4427
|
const settled = await Promise.all(
|
|
4402
4428
|
relPaths.map(async (rel) => {
|
|
4403
4429
|
const relPath = rel.split("\\").join("/");
|
|
4404
4430
|
const absPath = (0, import_node_path5.join)(rootDir, rel);
|
|
4405
4431
|
try {
|
|
4406
|
-
const s = await (0,
|
|
4432
|
+
const s = await (0, import_promises5.lstat)(absPath);
|
|
4407
4433
|
if (s.isSymbolicLink() || !s.isFile()) return null;
|
|
4408
|
-
|
|
4434
|
+
if (!await isDirConfined((0, import_node_path5.dirname)(absPath))) return null;
|
|
4435
|
+
const c = await refineWithContent(classifyFile(relPath), absPath);
|
|
4409
4436
|
return {
|
|
4410
4437
|
relPath,
|
|
4411
4438
|
absPath,
|
|
4412
4439
|
kind: c.kind,
|
|
4440
|
+
confidence: c.confidence,
|
|
4441
|
+
...c.reason !== void 0 ? { reason: c.reason } : {},
|
|
4413
4442
|
...c.rank !== void 0 ? { rank: c.rank } : {},
|
|
4443
|
+
...c.inferredRank !== void 0 ? { inferredRank: c.inferredRank } : {},
|
|
4414
4444
|
...c.service !== void 0 ? { service: c.service } : {},
|
|
4415
4445
|
...c.host !== void 0 ? { host: c.host } : {},
|
|
4416
4446
|
...c.component !== void 0 ? { component: c.component } : {},
|
|
@@ -4426,19 +4456,46 @@ async function buildIndex(rootDir) {
|
|
|
4426
4456
|
|
|
4427
4457
|
// src/bundle/BundleSource.ts
|
|
4428
4458
|
var GPUDB_VERSION_RE = /GPUdb version\s*:\s*(\S+)/;
|
|
4459
|
+
var ANCHOR_KINDS = ["config", "version-info"];
|
|
4460
|
+
var MIN_ANCHORS_FOR_CANONICAL = 2;
|
|
4461
|
+
var PARTIAL_INFERRED_FRACTION = 0.25;
|
|
4462
|
+
function assessLayout(inventory) {
|
|
4463
|
+
const anchorsPresent = ANCHOR_KINDS.filter((k) => (inventory.byKind[k] ?? 0) > 0).length;
|
|
4464
|
+
const inferredFraction = inventory.totalFiles > 0 ? inventory.inferredFiles / inventory.totalFiles : 0;
|
|
4465
|
+
let layout;
|
|
4466
|
+
if (anchorsPresent === 0) layout = "unfamiliar";
|
|
4467
|
+
else if (anchorsPresent >= MIN_ANCHORS_FOR_CANONICAL && inferredFraction < PARTIAL_INFERRED_FRACTION)
|
|
4468
|
+
layout = "canonical";
|
|
4469
|
+
else layout = "partial";
|
|
4470
|
+
if (layout === "canonical") return { layout };
|
|
4471
|
+
const bits = [`${inventory.inferredFiles}/${inventory.totalFiles} files classified by inference`];
|
|
4472
|
+
if (inventory.unknownFiles > 0) bits.push(`${inventory.unknownFiles} unclassified`);
|
|
4473
|
+
if (inventory.inferredRanks.length > 0)
|
|
4474
|
+
bits.push(`inferred ranks ${inventory.inferredRanks.join(", ")} (unconfirmed)`);
|
|
4475
|
+
const layoutWarning = layout === "unfamiliar" ? `This bundle does not match the canonical gpudb_sysinfo layout \u2014 no config/version/host-diagnostic files were found. Working from inference: ${bits.join("; ")}.` : `This bundle only partially matches the canonical layout: ${bits.join("; ")}.`;
|
|
4476
|
+
return { layout, layoutWarning };
|
|
4477
|
+
}
|
|
4429
4478
|
function selectLogFiles(index, opts) {
|
|
4430
4479
|
if (opts.component !== void 0) {
|
|
4431
|
-
return index.filter(
|
|
4480
|
+
return index.filter(
|
|
4481
|
+
(e) => (e.kind === "component-log" || e.kind === "loki-tail") && e.component === opts.component
|
|
4482
|
+
);
|
|
4432
4483
|
}
|
|
4433
4484
|
if (opts.hostManager) {
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4485
|
+
const hmCore = index.filter((e) => e.kind === "core-log" && e.service === "host-manager");
|
|
4486
|
+
if (hmCore.length > 0) return hmCore;
|
|
4487
|
+
return index.filter((e) => e.kind === "loki-tail" && e.service === "host-manager");
|
|
4488
|
+
}
|
|
4489
|
+
const matchesRank = (e) => opts.rank === void 0 || e.rank === opts.rank;
|
|
4490
|
+
const coreLogs = index.filter((e) => e.kind === "core-log" && matchesRank(e));
|
|
4491
|
+
const ranksWithCore = new Set(
|
|
4492
|
+
coreLogs.map((e) => e.rank).filter((r) => r !== void 0)
|
|
4438
4493
|
);
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4494
|
+
const supplementalTails = index.filter(
|
|
4495
|
+
(e) => e.kind === "loki-tail" && e.rank !== void 0 && matchesRank(e) && !ranksWithCore.has(e.rank)
|
|
4496
|
+
);
|
|
4497
|
+
const rankBearing = [...coreLogs, ...supplementalTails];
|
|
4498
|
+
const core = rankBearing.length > 0 ? rankBearing : index.filter((e) => e.kind === "loki-tail" && matchesRank(e));
|
|
4442
4499
|
if (opts.includeComponents) {
|
|
4443
4500
|
return [...core, ...index.filter((e) => e.kind === "component-log")];
|
|
4444
4501
|
}
|
|
@@ -4472,12 +4529,17 @@ async function createBundleSource(rootDir) {
|
|
|
4472
4529
|
const inventoryValue = (() => {
|
|
4473
4530
|
const byKind = {};
|
|
4474
4531
|
const rankSet = /* @__PURE__ */ new Set();
|
|
4532
|
+
const inferredRankSet = /* @__PURE__ */ new Set();
|
|
4475
4533
|
const serviceSet = /* @__PURE__ */ new Set();
|
|
4476
4534
|
let totalBytes = 0;
|
|
4535
|
+
let inferredFiles = 0;
|
|
4536
|
+
let unknownFiles = 0;
|
|
4477
4537
|
for (const e of index) {
|
|
4478
4538
|
byKind[e.kind] = (byKind[e.kind] ?? 0) + 1;
|
|
4479
4539
|
totalBytes += e.sizeBytes;
|
|
4480
|
-
if (e.
|
|
4540
|
+
if (e.confidence === "inferred") inferredFiles++;
|
|
4541
|
+
if (e.kind === "unknown") unknownFiles++;
|
|
4542
|
+
if (e.rank) (e.inferredRank ? inferredRankSet : rankSet).add(e.rank);
|
|
4481
4543
|
if (e.service) serviceSet.add(e.service);
|
|
4482
4544
|
}
|
|
4483
4545
|
return {
|
|
@@ -4485,14 +4547,17 @@ async function createBundleSource(rootDir) {
|
|
|
4485
4547
|
totalBytes,
|
|
4486
4548
|
byKind,
|
|
4487
4549
|
ranks: [...rankSet].sort(),
|
|
4488
|
-
|
|
4550
|
+
inferredRanks: [...inferredRankSet].filter((r) => !rankSet.has(r)).sort(),
|
|
4551
|
+
services: [...serviceSet].sort(),
|
|
4552
|
+
inferredFiles,
|
|
4553
|
+
unknownFiles
|
|
4489
4554
|
};
|
|
4490
4555
|
})();
|
|
4491
4556
|
const detectVersion = async () => {
|
|
4492
4557
|
const versionFile = findByKind("version-info");
|
|
4493
4558
|
if (versionFile) {
|
|
4494
4559
|
try {
|
|
4495
|
-
const parsed = parseSysinfo(await (0,
|
|
4560
|
+
const parsed = parseSysinfo(await (0, import_promises6.readFile)(versionFile.absPath, "utf-8"));
|
|
4496
4561
|
for (const block of parsed.blocks) {
|
|
4497
4562
|
const m = GPUDB_VERSION_RE.exec(block.output);
|
|
4498
4563
|
if (m) return m[1];
|
|
@@ -4503,7 +4568,7 @@ async function createBundleSource(rootDir) {
|
|
|
4503
4568
|
const configFile = findByKind("config");
|
|
4504
4569
|
if (configFile) {
|
|
4505
4570
|
try {
|
|
4506
|
-
const entries = parseIni(await (0,
|
|
4571
|
+
const entries = parseIni(await (0, import_promises6.readFile)(configFile.absPath, "utf-8"));
|
|
4507
4572
|
return entries.find((e) => e.key === "file_version")?.value;
|
|
4508
4573
|
} catch {
|
|
4509
4574
|
return void 0;
|
|
@@ -4515,7 +4580,7 @@ async function createBundleSource(rootDir) {
|
|
|
4515
4580
|
const configFile = index.find((e) => e.kind === "config" && e.relPath.endsWith("gpudb.conf")) ?? findByKind("config");
|
|
4516
4581
|
if (!configFile) return { error: "no gpudb.conf found in bundle" };
|
|
4517
4582
|
try {
|
|
4518
|
-
const entries = parseIni(await (0,
|
|
4583
|
+
const entries = parseIni(await (0, import_promises6.readFile)(configFile.absPath, "utf-8"));
|
|
4519
4584
|
return { entries: filterIni(entries, opts), file: configFile.relPath };
|
|
4520
4585
|
} catch (err) {
|
|
4521
4586
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
@@ -4529,7 +4594,7 @@ async function createBundleSource(rootDir) {
|
|
|
4529
4594
|
const abs = resolve3(entry.relPath);
|
|
4530
4595
|
if (!abs) return { error: `path "${name}" escapes the bundle root` };
|
|
4531
4596
|
try {
|
|
4532
|
-
return parseSysinfo(await (0,
|
|
4597
|
+
return parseSysinfo(await (0, import_promises6.readFile)(abs, "utf-8"));
|
|
4533
4598
|
} catch (err) {
|
|
4534
4599
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
4535
4600
|
}
|
|
@@ -4588,7 +4653,7 @@ async function createBundleSource(rootDir) {
|
|
|
4588
4653
|
const lines = [];
|
|
4589
4654
|
for (const file of files) {
|
|
4590
4655
|
try {
|
|
4591
|
-
const content = await (0,
|
|
4656
|
+
const content = await (0, import_promises6.readFile)(file.absPath, "utf-8");
|
|
4592
4657
|
for (const line of content.split("\n")) {
|
|
4593
4658
|
const trimmed = line.trim();
|
|
4594
4659
|
if (trimmed !== "" && !/^-{3,}$/.test(trimmed)) lines.push(trimmed);
|
|
@@ -4612,13 +4677,277 @@ async function createBundleSource(rootDir) {
|
|
|
4612
4677
|
};
|
|
4613
4678
|
}
|
|
4614
4679
|
|
|
4680
|
+
// src/bundle/known-files.ts
|
|
4681
|
+
var KNOWN_BUNDLE_FILES = {
|
|
4682
|
+
// Host resources
|
|
4683
|
+
"cpu.txt": "CPU topology, NUMA, and interrupts (lscpu, numactl, /proc/cpuinfo, /proc/interrupts)",
|
|
4684
|
+
"mem.txt": "Memory usage, /proc/meminfo, and transparent-hugepage setting (free -m -t)",
|
|
4685
|
+
"disk.txt": "Filesystems, mounts, block devices, and disk stats (df, mount, lsblk, fdisk, /etc/fstab, /proc/diskstats)",
|
|
4686
|
+
"gpu.txt": "NVIDIA GPU inventory and state (nvidia-smi -L/-q, modinfo nvidia)",
|
|
4687
|
+
"net.txt": "Network interfaces, sockets, and DNS (hostname, ifconfig, netstat, /etc/resolv.conf)",
|
|
4688
|
+
// Processes
|
|
4689
|
+
"ps.txt": "Full process list (ps -auxww, ps -ejHlfww)",
|
|
4690
|
+
"gpudb-exe.txt": "Running gpudb processes (ps auxfwww | grep gpudb)",
|
|
4691
|
+
// Hardware / firmware
|
|
4692
|
+
"dmidecode.txt": "BIOS / DMI hardware inventory (dmidecode)",
|
|
4693
|
+
"lshw.txt": "Hardware listing (lshw -short -numeric)",
|
|
4694
|
+
"pci.txt": "PCI devices and I/O resources (lspci, /proc/ioports, /proc/iomem)",
|
|
4695
|
+
// Kernel / OS
|
|
4696
|
+
"dmesg.txt": "Kernel ring buffer \u2014 boot and runtime kernel messages (dmesg -T)",
|
|
4697
|
+
"dmesg-timestamp.txt": "Kernel ring buffer with human-readable timestamps",
|
|
4698
|
+
"sysctl.txt": "Kernel tunables (sysctl -a)",
|
|
4699
|
+
"sys.txt": "OS identity, uptime, ulimits, kernel cmdline, clocksource, and loaded modules (uname, ulimit, /proc/cmdline, lsmod)",
|
|
4700
|
+
"lsof.txt": "Open files and network sockets (lsof -n -P)",
|
|
4701
|
+
"lslocks.txt": "Held file locks (lslocks)",
|
|
4702
|
+
// Packages / linker / accounts
|
|
4703
|
+
"deb.txt": "Installed Debian packages and verification (dpkg -l, dpkg -V)",
|
|
4704
|
+
"rpm.txt": "Installed RPM packages (rpm -qa)",
|
|
4705
|
+
"ld.so.conf.txt": "Dynamic-linker library search paths (/etc/ld.so.conf)",
|
|
4706
|
+
"user.txt": "Users, groups, and the gpudb service account (whoami, id, /etc/passwd, /etc/group)",
|
|
4707
|
+
"sudoers.txt": "Sudo configuration (/etc/sudoers)",
|
|
4708
|
+
"etc_profile.txt": "Login shell profile (/etc/profile)",
|
|
4709
|
+
"etc_bashrc.txt": "System bashrc (/etc/bashrc)",
|
|
4710
|
+
"etc_host.txt": "Static hostname resolution (/etc/hosts)",
|
|
4711
|
+
// Kinetica-specific
|
|
4712
|
+
"gpudb.txt": "GPUdb version/build, binary md5 + ldd, and the captured gpudb.conf / gpudb_logger.conf ($GPUDB_EXE -v)",
|
|
4713
|
+
"gpudb_core_etc_gpudb.conf": "The live gpudb.conf at capture time (the database's main config)",
|
|
4714
|
+
"gpudb_core_etc_gpudb_logger.conf": "The logging configuration (gpudb_logger.conf)",
|
|
4715
|
+
"loki-info.txt": "Loki log-index stats: labels, series, and per-class volume (logcli)",
|
|
4716
|
+
"sql-queries.txt": "SQL query log extracted from Loki (logcli)",
|
|
4717
|
+
"tables.txt": "Table schemas and column types (gadmin --schema), when collected",
|
|
4718
|
+
"logfiles.txt": "Manifest: the log directories/files the collector enumerated",
|
|
4719
|
+
"errors.txt": "Collection commands that FAILED during capture (Evidence Gaps)",
|
|
4720
|
+
"proc-logs-erros.txt": "Per-process log-collection failures during capture (Evidence Gaps)"
|
|
4721
|
+
};
|
|
4722
|
+
var KIND_DESCRIPTIONS = {
|
|
4723
|
+
"core-log": "Per-rank rolling Kinetica core log (the primary incident narrative)",
|
|
4724
|
+
"component-log": "Component service log (sql-engine, httpd, reveal, tomcat, stats, \u2026)",
|
|
4725
|
+
"loki-tail": "Last-2h Loki tail for a service (small; searched only when no core logs exist)",
|
|
4726
|
+
"process-info": "Per-rank process snapshot: command line, PID, and environment (/proc/<pid>/environ)",
|
|
4727
|
+
config: "Kinetica configuration file",
|
|
4728
|
+
"version-info": "GPUdb version/build information",
|
|
4729
|
+
"collection-errors": "Collection commands that FAILED during capture (Evidence Gaps)",
|
|
4730
|
+
manifest: "Manifest of log directories/files the collector enumerated"
|
|
4731
|
+
};
|
|
4732
|
+
function basename2(relPath) {
|
|
4733
|
+
const parts = relPath.split("/");
|
|
4734
|
+
return parts[parts.length - 1] ?? relPath;
|
|
4735
|
+
}
|
|
4736
|
+
function describeBundleFile(entry) {
|
|
4737
|
+
return KNOWN_BUNDLE_FILES[basename2(entry.relPath)] ?? KIND_DESCRIPTIONS[entry.kind] ?? "";
|
|
4738
|
+
}
|
|
4739
|
+
|
|
4740
|
+
// src/tools/bundle/list-files.ts
|
|
4741
|
+
var BundleListFilesSchema = import_zod18.z.object({
|
|
4742
|
+
kind: import_zod18.z.string().optional()
|
|
4743
|
+
});
|
|
4744
|
+
var MAX_UNKNOWN_LISTED = 40;
|
|
4745
|
+
async function bundleListFiles(source, args = {}) {
|
|
4746
|
+
const all = source.listFiles();
|
|
4747
|
+
const filtered = args.kind ? all.filter((e) => e.kind === args.kind) : all;
|
|
4748
|
+
const inventory = source.inventory();
|
|
4749
|
+
const {
|
|
4750
|
+
totalFiles,
|
|
4751
|
+
totalBytes,
|
|
4752
|
+
byKind,
|
|
4753
|
+
ranks,
|
|
4754
|
+
inferredRanks,
|
|
4755
|
+
services,
|
|
4756
|
+
inferredFiles,
|
|
4757
|
+
unknownFiles
|
|
4758
|
+
} = inventory;
|
|
4759
|
+
const { layout, layoutWarning } = assessLayout(inventory);
|
|
4760
|
+
const version = await source.detectVersion();
|
|
4761
|
+
const errors = await source.collectionErrors();
|
|
4762
|
+
const files = filtered.map((e) => ({
|
|
4763
|
+
file: e.relPath,
|
|
4764
|
+
kind: e.kind,
|
|
4765
|
+
// How sure the classification is: exact (canonical name) | inferred (heuristic) | weak.
|
|
4766
|
+
confidence: e.confidence,
|
|
4767
|
+
...e.reason !== void 0 ? { why: e.reason } : {},
|
|
4768
|
+
rank: e.rank ?? "",
|
|
4769
|
+
size_kb: Math.round(e.sizeBytes / 1024),
|
|
4770
|
+
// What the file contains — so the agent can pick the right one without reading it.
|
|
4771
|
+
description: describeBundleFile(e)
|
|
4772
|
+
}));
|
|
4773
|
+
const unknownPaths = all.filter((e) => e.kind === "unknown").map((e) => e.relPath);
|
|
4774
|
+
return {
|
|
4775
|
+
ok: true,
|
|
4776
|
+
data: {
|
|
4777
|
+
detected_version: version ?? "unknown",
|
|
4778
|
+
// How well the bundle matches the canonical gpudb_sysinfo layout.
|
|
4779
|
+
layout_match: layout,
|
|
4780
|
+
...layoutWarning !== void 0 ? { layout_note: layoutWarning } : {},
|
|
4781
|
+
ranks_present: ranks.join(", ") || "none",
|
|
4782
|
+
...inferredRanks.length > 0 ? { inferred_ranks_unconfirmed: inferredRanks.join(", ") } : {},
|
|
4783
|
+
services_present: services.join(", ") || "none",
|
|
4784
|
+
total_files: totalFiles,
|
|
4785
|
+
total_size_mb: Number((totalBytes / 1e6).toFixed(1)),
|
|
4786
|
+
counts_by_kind: byKind,
|
|
4787
|
+
inferred_files: inferredFiles,
|
|
4788
|
+
unknown_files: unknownFiles,
|
|
4789
|
+
...unknownPaths.length > 0 ? {
|
|
4790
|
+
unknown_file_paths: unknownPaths.slice(0, MAX_UNKNOWN_LISTED),
|
|
4791
|
+
...unknownPaths.length > MAX_UNKNOWN_LISTED ? { unknown_file_paths_truncated: unknownPaths.length - MAX_UNKNOWN_LISTED } : {}
|
|
4792
|
+
} : {},
|
|
4793
|
+
failed_collections: errors.length,
|
|
4794
|
+
files
|
|
4795
|
+
}
|
|
4796
|
+
};
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4799
|
+
// src/tools/bundle/log-timeline.ts
|
|
4800
|
+
var import_zod19 = require("zod");
|
|
4801
|
+
var BundleLogTimelineSchema = import_zod19.z.object({
|
|
4802
|
+
min_severity: import_zod19.z.enum(["INFO", "WARN", "UERR", "ERROR", "FATAL"]).optional(),
|
|
4803
|
+
granularity: import_zod19.z.enum(["day", "hour", "minute"]).optional(),
|
|
4804
|
+
rank: import_zod19.z.string().describe('Numeric rank only, e.g. "r0"/"r1". For the host manager use host_manager.').optional(),
|
|
4805
|
+
host_manager: import_zod19.z.boolean().describe("Bucket the host-manager (hm) log \u2014 a singleton service, not a rank.").optional(),
|
|
4806
|
+
component: import_zod19.z.string().optional(),
|
|
4807
|
+
include_components: import_zod19.z.boolean().optional()
|
|
4808
|
+
});
|
|
4809
|
+
async function bundleLogTimeline(source, args = {}) {
|
|
4810
|
+
const query3 = {
|
|
4811
|
+
...args.min_severity !== void 0 ? { minSeverity: args.min_severity } : {},
|
|
4812
|
+
...args.granularity !== void 0 ? { granularity: args.granularity } : {},
|
|
4813
|
+
...args.rank !== void 0 ? { rank: args.rank } : {},
|
|
4814
|
+
...args.host_manager !== void 0 ? { hostManager: args.host_manager } : {},
|
|
4815
|
+
...args.component !== void 0 ? { component: args.component } : {},
|
|
4816
|
+
...args.include_components !== void 0 ? { includeComponents: args.include_components } : {}
|
|
4817
|
+
};
|
|
4818
|
+
const result = await source.logTimeline(query3);
|
|
4819
|
+
const severities = [...new Set(result.buckets.flatMap((b) => Object.keys(b.counts)))];
|
|
4820
|
+
const order = ["FATAL", "ERROR", "UERR", "WARN", "INFO"];
|
|
4821
|
+
severities.sort((a, b) => order.indexOf(a) - order.indexOf(b));
|
|
4822
|
+
const rows = result.buckets.map((b) => {
|
|
4823
|
+
const row = { time_bucket: b.bucket };
|
|
4824
|
+
for (const sev of severities) row[sev] = b.counts[sev] ?? 0;
|
|
4825
|
+
row.total = b.total;
|
|
4826
|
+
return row;
|
|
4827
|
+
});
|
|
4828
|
+
return {
|
|
4829
|
+
ok: true,
|
|
4830
|
+
note: result.totalCounted === 0 ? "No lines at or above the severity threshold \u2014 try a lower min_severity." : `${result.totalCounted} event(s) across ${result.buckets.length} bucket(s), ${result.filesScanned.length} file(s).`,
|
|
4831
|
+
data: {
|
|
4832
|
+
lines_scanned: result.linesScanned,
|
|
4833
|
+
files_scanned: result.filesScanned.join(", ") || "none",
|
|
4834
|
+
buckets: rows
|
|
4835
|
+
}
|
|
4836
|
+
};
|
|
4837
|
+
}
|
|
4838
|
+
|
|
4839
|
+
// src/tools/bundle/search-logs.ts
|
|
4840
|
+
var import_zod20 = require("zod");
|
|
4841
|
+
var BundleSearchLogsSchema = import_zod20.z.object({
|
|
4842
|
+
regex: import_zod20.z.string().optional(),
|
|
4843
|
+
min_severity: import_zod20.z.enum(["INFO", "WARN", "UERR", "ERROR", "FATAL"]).optional(),
|
|
4844
|
+
from_ts: import_zod20.z.string().optional(),
|
|
4845
|
+
to_ts: import_zod20.z.string().optional(),
|
|
4846
|
+
rank: import_zod20.z.string().describe('Numeric rank only, e.g. "r0"/"r1". For the host manager use host_manager.').optional(),
|
|
4847
|
+
host_manager: import_zod20.z.boolean().describe("Search the host-manager (hm) log \u2014 a singleton service, not a rank.").optional(),
|
|
4848
|
+
component: import_zod20.z.string().optional(),
|
|
4849
|
+
include_components: import_zod20.z.boolean().optional(),
|
|
4850
|
+
max_matches: import_zod20.z.number().int().min(1).max(1e3).optional()
|
|
4851
|
+
});
|
|
4852
|
+
async function bundleSearchLogs(source, args = {}) {
|
|
4853
|
+
const query3 = {
|
|
4854
|
+
...args.regex !== void 0 ? { regex: args.regex } : {},
|
|
4855
|
+
...args.min_severity !== void 0 ? { minSeverity: args.min_severity } : {},
|
|
4856
|
+
...args.from_ts !== void 0 ? { fromTs: args.from_ts } : {},
|
|
4857
|
+
...args.to_ts !== void 0 ? { toTs: args.to_ts } : {},
|
|
4858
|
+
...args.rank !== void 0 ? { rank: args.rank } : {},
|
|
4859
|
+
...args.host_manager !== void 0 ? { hostManager: args.host_manager } : {},
|
|
4860
|
+
...args.component !== void 0 ? { component: args.component } : {},
|
|
4861
|
+
...args.include_components !== void 0 ? { includeComponents: args.include_components } : {},
|
|
4862
|
+
...args.max_matches !== void 0 ? { maxMatches: args.max_matches } : {}
|
|
4863
|
+
};
|
|
4864
|
+
const result = await source.searchLogs(query3);
|
|
4865
|
+
const note = result.capped ? `Showing ${result.matches.length} of ${result.totalMatched} matches across ${result.filesScanned.length} file(s) (display capped). Narrow with a tighter regex, severity, or time window to surface the specific lines.` : `${result.totalMatched} match(es) across ${result.filesScanned.length} file(s).`;
|
|
4866
|
+
return {
|
|
4867
|
+
ok: true,
|
|
4868
|
+
note,
|
|
4869
|
+
data: {
|
|
4870
|
+
total_matched: result.totalMatched,
|
|
4871
|
+
lines_scanned: result.linesScanned,
|
|
4872
|
+
files_scanned: result.filesScanned.join(", ") || "none",
|
|
4873
|
+
capped: result.capped,
|
|
4874
|
+
matches: result.matches.map((m) => ({
|
|
4875
|
+
file: m.file,
|
|
4876
|
+
line: m.lineNumber,
|
|
4877
|
+
timestamp: m.timestamp ?? "",
|
|
4878
|
+
severity: m.severity ?? "",
|
|
4879
|
+
rank: m.rank ?? "",
|
|
4880
|
+
message: m.message
|
|
4881
|
+
}))
|
|
4882
|
+
}
|
|
4883
|
+
};
|
|
4884
|
+
}
|
|
4885
|
+
|
|
4886
|
+
// src/tools/bundle/read-config.ts
|
|
4887
|
+
var import_zod21 = require("zod");
|
|
4888
|
+
var BundleReadConfigSchema = import_zod21.z.object({
|
|
4889
|
+
section: import_zod21.z.string().optional(),
|
|
4890
|
+
key: import_zod21.z.string().optional()
|
|
4891
|
+
});
|
|
4892
|
+
async function bundleReadConfig(source, args = {}) {
|
|
4893
|
+
const result = await source.readConfig({
|
|
4894
|
+
...args.section !== void 0 ? { section: args.section } : {},
|
|
4895
|
+
...args.key !== void 0 ? { key: args.key } : {}
|
|
4896
|
+
});
|
|
4897
|
+
if ("error" in result) {
|
|
4898
|
+
return { ok: false, status: 0, error: result.error, raw: "" };
|
|
4899
|
+
}
|
|
4900
|
+
if (result.entries.length === 0 && args.section !== void 0) {
|
|
4901
|
+
const all = await source.readConfig(args.key !== void 0 ? { key: args.key } : {});
|
|
4902
|
+
const sections = "error" in all ? [] : [...new Set(all.entries.map((e) => e.section))].sort();
|
|
4903
|
+
const sectionList = sections.map((s) => s === "" ? "(flat/top-level)" : s).join(", ");
|
|
4904
|
+
return {
|
|
4905
|
+
ok: true,
|
|
4906
|
+
note: `No entries in section "${args.section}" of ${result.file}. gpudb.conf is largely flat \u2014 retry filtering by key only. Sections present: ${sectionList || "(none)"}.`,
|
|
4907
|
+
data: { section_not_found: args.section, available_sections: sections }
|
|
4908
|
+
};
|
|
4909
|
+
}
|
|
4910
|
+
return {
|
|
4911
|
+
ok: true,
|
|
4912
|
+
note: `${result.entries.length} entr(y/ies) from ${result.file}.`,
|
|
4913
|
+
data: result.entries.map((e) => ({ section: e.section, key: e.key, value: e.value }))
|
|
4914
|
+
};
|
|
4915
|
+
}
|
|
4916
|
+
|
|
4917
|
+
// src/tools/bundle/read-sysinfo.ts
|
|
4918
|
+
var import_zod22 = require("zod");
|
|
4919
|
+
var BundleReadSysinfoSchema = import_zod22.z.object({
|
|
4920
|
+
name: import_zod22.z.string().min(1)
|
|
4921
|
+
});
|
|
4922
|
+
async function bundleReadSysinfo(source, args) {
|
|
4923
|
+
const result = await source.readSysinfo(args.name);
|
|
4924
|
+
if ("error" in result) {
|
|
4925
|
+
return { ok: false, status: 0, error: result.error, raw: "" };
|
|
4926
|
+
}
|
|
4927
|
+
return {
|
|
4928
|
+
ok: true,
|
|
4929
|
+
data: {
|
|
4930
|
+
...result.header !== void 0 ? { source_file: result.header } : {},
|
|
4931
|
+
blocks: result.blocks.map((b) => ({
|
|
4932
|
+
command: b.command,
|
|
4933
|
+
...b.exitCode !== void 0 ? { exit_code: b.exitCode } : {},
|
|
4934
|
+
output: b.output
|
|
4935
|
+
}))
|
|
4936
|
+
}
|
|
4937
|
+
};
|
|
4938
|
+
}
|
|
4939
|
+
|
|
4940
|
+
// src/tools/bundle/load-bundle.ts
|
|
4941
|
+
var import_zod23 = require("zod");
|
|
4942
|
+
|
|
4615
4943
|
// src/bundle/verify-bundle.ts
|
|
4944
|
+
var import_promises7 = require("fs/promises");
|
|
4616
4945
|
var ARCHIVE_RE = /\.(tgz|tar\.gz|tar|gz|zip)$/i;
|
|
4617
4946
|
var EXPECTED_KINDS = ["config", "core-log"];
|
|
4618
4947
|
async function verifyBundle(bundlePath) {
|
|
4619
4948
|
let info;
|
|
4620
4949
|
try {
|
|
4621
|
-
info = await (0,
|
|
4950
|
+
info = await (0, import_promises7.stat)(bundlePath);
|
|
4622
4951
|
} catch {
|
|
4623
4952
|
return { ok: false, error: `bundle path does not exist: ${bundlePath}` };
|
|
4624
4953
|
}
|
|
@@ -4638,12 +4967,15 @@ async function verifyBundle(bundlePath) {
|
|
|
4638
4967
|
}
|
|
4639
4968
|
const missingExpected = EXPECTED_KINDS.filter((k) => (inventory.byKind[k] ?? 0) === 0);
|
|
4640
4969
|
const kineticaVersion = await bundleSource.detectVersion();
|
|
4970
|
+
const { layout, layoutWarning } = assessLayout(inventory);
|
|
4641
4971
|
return {
|
|
4642
4972
|
ok: true,
|
|
4643
4973
|
bundleSource,
|
|
4644
4974
|
...kineticaVersion !== void 0 ? { kineticaVersion } : {},
|
|
4645
4975
|
inventory,
|
|
4646
|
-
missingExpected
|
|
4976
|
+
missingExpected,
|
|
4977
|
+
layout,
|
|
4978
|
+
...layoutWarning !== void 0 ? { layoutWarning } : {}
|
|
4647
4979
|
};
|
|
4648
4980
|
}
|
|
4649
4981
|
|
|
@@ -4862,7 +5194,7 @@ Before gathering evidence, announce a brief 2-3 line plan: restate the issue, li
|
|
|
4862
5194
|
|
|
4863
5195
|
### Round 1 \u2014 Orient
|
|
4864
5196
|
|
|
4865
|
-
- ${t}kinetica_bundle_list_files${t} \u2014 **ALWAYS FIRST.** Learn the detected version, which ranks are present, what file kinds exist, and how many collections failed.
|
|
5197
|
+
- ${t}kinetica_bundle_list_files${t} \u2014 **ALWAYS FIRST.** Learn the detected version, which ranks are present, what file kinds exist, and how many collections failed. Check ${t}layout_match${t}: if it is not ${t}canonical${t}, this bundle is off-shape (e.g. a logs-only dump) \u2014 read the ${t}layout_note${t}, treat any ${t}unknown_file_paths${t} as evidence to inspect by hand (open one with ${t}kinetica_bundle_read_sysinfo${t}), and trust ${t}ranks_present${t} over ${t}inferred_ranks_unconfirmed${t}. See the support-bundle reference ("When the bundle doesn't match the expected layout").
|
|
4866
5198
|
- ${t}kinetica_bundle_log_timeline${t} (min_severity: WARN) \u2014 get the incident shape: when did WARN/ERROR/FATAL spike, and on which rank?
|
|
4867
5199
|
|
|
4868
5200
|
### Round 2 \u2014 Drill Down
|
|
@@ -4982,8 +5314,7 @@ function createBundleHolder(initial) {
|
|
|
4982
5314
|
}
|
|
4983
5315
|
|
|
4984
5316
|
// src/cli/pick-bundle-path.ts
|
|
4985
|
-
var
|
|
4986
|
-
var import_promises7 = require("fs/promises");
|
|
5317
|
+
var import_promises8 = require("fs/promises");
|
|
4987
5318
|
var import_node_path7 = require("path");
|
|
4988
5319
|
function isPermissionError(err) {
|
|
4989
5320
|
if (typeof err !== "object" || err === null || !("code" in err)) return false;
|
|
@@ -4991,14 +5322,14 @@ function isPermissionError(err) {
|
|
|
4991
5322
|
return code === "EACCES" || code === "EPERM";
|
|
4992
5323
|
}
|
|
4993
5324
|
async function listDirectoryCandidates(term) {
|
|
4994
|
-
const
|
|
4995
|
-
const endsWithSep =
|
|
4996
|
-
const baseDir = endsWithSep ?
|
|
4997
|
-
const prefix = endsWithSep ? "" : (0, import_node_path7.basename)(
|
|
5325
|
+
const input2 = term.trim() === "" ? "." : term;
|
|
5326
|
+
const endsWithSep = input2.endsWith("/");
|
|
5327
|
+
const baseDir = endsWithSep ? input2 : (0, import_node_path7.dirname)(input2) || ".";
|
|
5328
|
+
const prefix = endsWithSep ? "" : (0, import_node_path7.basename)(input2);
|
|
4998
5329
|
const resolved = (0, import_node_path7.resolve)(baseDir);
|
|
4999
5330
|
let entries;
|
|
5000
5331
|
try {
|
|
5001
|
-
entries = await (0,
|
|
5332
|
+
entries = await (0, import_promises8.readdir)(resolved, { withFileTypes: true });
|
|
5002
5333
|
} catch (err) {
|
|
5003
5334
|
if (isPermissionError(err)) return { kind: "denied", dir: resolved };
|
|
5004
5335
|
return { kind: "ok", candidates: [] };
|
|
@@ -5024,7 +5355,7 @@ function listingToChoices(listing) {
|
|
|
5024
5355
|
async function promptBundleDirectory() {
|
|
5025
5356
|
if (!process.stdin.isTTY) return void 0;
|
|
5026
5357
|
try {
|
|
5027
|
-
return await
|
|
5358
|
+
return await search({
|
|
5028
5359
|
message: "Select the support bundle directory (type to filter):",
|
|
5029
5360
|
source: async (term) => listingToChoices(await listDirectoryCandidates(term ?? ""))
|
|
5030
5361
|
});
|
|
@@ -5033,32 +5364,29 @@ async function promptBundleDirectory() {
|
|
|
5033
5364
|
}
|
|
5034
5365
|
}
|
|
5035
5366
|
|
|
5036
|
-
// src/approval/gate.ts
|
|
5037
|
-
var import_prompts4 = require("@inquirer/prompts");
|
|
5038
|
-
|
|
5039
5367
|
// src/approval/display.ts
|
|
5040
|
-
var
|
|
5368
|
+
var import_picocolors6 = __toESM(require("picocolors"));
|
|
5041
5369
|
var IMPACT_FALLBACK = "Impact unknown \u2014 review parameters carefully";
|
|
5042
|
-
var DIVIDER2 =
|
|
5370
|
+
var DIVIDER2 = import_picocolors6.default.dim("\u2500".repeat(50));
|
|
5043
5371
|
var LABEL_WIDTH = 8;
|
|
5044
5372
|
function formatLabel(label) {
|
|
5045
5373
|
return ` ${label.padEnd(LABEL_WIDTH)}: `;
|
|
5046
5374
|
}
|
|
5047
5375
|
function renderApprovalPanel(toolName, toolInput, impact, beforeAfter, reasoningSummary) {
|
|
5048
|
-
const header =
|
|
5049
|
-
const action = `${formatLabel("Action")}${
|
|
5376
|
+
const header = import_picocolors6.default.bold(import_picocolors6.default.yellow(" Mutation Approval Required"));
|
|
5377
|
+
const action = `${formatLabel("Action")}${import_picocolors6.default.bold(formatToolName(toolName))}`;
|
|
5050
5378
|
const paramEntries = Object.entries(toolInput);
|
|
5051
5379
|
const paramSection = paramEntries.length === 0 ? " (no parameters)" : paramEntries.map(([key, value]) => {
|
|
5052
5380
|
const formatted = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
5053
|
-
return ` ${
|
|
5381
|
+
return ` ${import_picocolors6.default.dim(key)}: ${formatted}`;
|
|
5054
5382
|
}).join("\n");
|
|
5055
5383
|
const impactLine = `${formatLabel("Impact")}${impact ?? IMPACT_FALLBACK}`;
|
|
5056
|
-
const prompt =
|
|
5384
|
+
const prompt = import_picocolors6.default.dim(
|
|
5057
5385
|
`${formatLabel("Respond")}y (proceed) | n (abort) | explain (show reasoning)`
|
|
5058
5386
|
);
|
|
5059
5387
|
const hasBeforeAfter = beforeAfter !== void 0 && beforeAfter.length > 0;
|
|
5060
5388
|
const beforeAfterSection = hasBeforeAfter ? beforeAfter.map(
|
|
5061
|
-
(entry) => ` ${
|
|
5389
|
+
(entry) => ` ${import_picocolors6.default.dim(entry.key)}: ${entry.current} ${import_picocolors6.default.yellow("->")} ${entry.proposed}`
|
|
5062
5390
|
).join("\n") : null;
|
|
5063
5391
|
const hasReasoning = reasoningSummary !== void 0 && reasoningSummary.length > 0;
|
|
5064
5392
|
const reasoningSection = hasReasoning ? `${formatLabel("Reason")}${reasoningSummary}` : null;
|
|
@@ -5090,7 +5418,7 @@ function createApprovalGate(isReadOnly) {
|
|
|
5090
5418
|
console.error(panel);
|
|
5091
5419
|
while (true) {
|
|
5092
5420
|
try {
|
|
5093
|
-
const raw = await
|
|
5421
|
+
const raw = await input({ message: "Proceed? (y/n/explain):" }, { signal: options.signal });
|
|
5094
5422
|
const normalized = raw.trim().toLowerCase();
|
|
5095
5423
|
if (normalized === "y") {
|
|
5096
5424
|
process.stderr.write("\n");
|
|
@@ -5152,26 +5480,74 @@ function createTurnGate() {
|
|
|
5152
5480
|
}
|
|
5153
5481
|
|
|
5154
5482
|
// src/output/render-markdown.ts
|
|
5155
|
-
var
|
|
5156
|
-
var BOLD_RE = /\*\*(.+?)\*\*/g;
|
|
5483
|
+
var import_picocolors7 = __toESM(require("picocolors"));
|
|
5157
5484
|
var HEADING_RE = /^(#{1,6})\s+(.+)$/;
|
|
5485
|
+
var HRULE_RE = /^\s*([-*_])\1{2,}\s*$/;
|
|
5486
|
+
var BULLET_RE = /^(\s*)[-*+]\s+(.*)$/;
|
|
5487
|
+
var BOLD_RE = /\*\*(.+?)\*\*/g;
|
|
5488
|
+
var INLINE_CODE_RE = /`([^`]+)`/g;
|
|
5489
|
+
var UPPERWORD_RE = /[A-Z]{2,}/;
|
|
5490
|
+
var ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
5491
|
+
var DEFAULT_WIDTH = 80;
|
|
5492
|
+
function terminalWidth() {
|
|
5493
|
+
const cols = process.stderr.columns;
|
|
5494
|
+
return typeof cols === "number" && cols > 0 ? cols : DEFAULT_WIDTH;
|
|
5495
|
+
}
|
|
5496
|
+
var SEVERITY_RULES = [
|
|
5497
|
+
{ re: /\b(FATAL|ERROR|UERR|CRITICAL|SEGV|SIGSEGV)\b/, color: import_picocolors7.default.red, glyph: "\u2717" },
|
|
5498
|
+
{ re: /\b(WARN|WARNING)\b/, color: import_picocolors7.default.yellow, glyph: "\u26A0" },
|
|
5499
|
+
{ re: /\b(OK|PASS|PASSED|HEALTHY)\b/, color: import_picocolors7.default.green, glyph: "\u2713" }
|
|
5500
|
+
];
|
|
5501
|
+
function firstSeverityRule(text2) {
|
|
5502
|
+
return SEVERITY_RULES.find((rule) => rule.re.test(text2));
|
|
5503
|
+
}
|
|
5504
|
+
function styleInline(text2) {
|
|
5505
|
+
let out = text2;
|
|
5506
|
+
if (out.includes("`")) out = out.replace(INLINE_CODE_RE, (_, code) => purple(code));
|
|
5507
|
+
if (out.includes("**")) out = out.replace(BOLD_RE, (_, b) => import_picocolors7.default.bold(b));
|
|
5508
|
+
if (UPPERWORD_RE.test(out)) {
|
|
5509
|
+
for (const rule of SEVERITY_RULES) {
|
|
5510
|
+
out = out.replace(rule.re, (token) => rule.color(token));
|
|
5511
|
+
}
|
|
5512
|
+
}
|
|
5513
|
+
return out;
|
|
5514
|
+
}
|
|
5515
|
+
function bulletFor(itemText) {
|
|
5516
|
+
const rule = firstSeverityRule(itemText);
|
|
5517
|
+
return rule ? rule.color(rule.glyph) : purple("\u2022");
|
|
5518
|
+
}
|
|
5519
|
+
function visibleWidth(text2) {
|
|
5520
|
+
return styleInline(text2).replace(ANSI_RE, "").length;
|
|
5521
|
+
}
|
|
5158
5522
|
function renderMarkdownLine(line) {
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5523
|
+
if (HRULE_RE.test(line)) {
|
|
5524
|
+
return import_picocolors7.default.dim("\u2500".repeat(terminalWidth()));
|
|
5525
|
+
}
|
|
5526
|
+
const heading = HEADING_RE.exec(line);
|
|
5527
|
+
if (heading) {
|
|
5528
|
+
const level = heading[1].length;
|
|
5529
|
+
const text2 = heading[2];
|
|
5530
|
+
if (level === 1) {
|
|
5531
|
+
return `
|
|
5532
|
+
${import_picocolors7.default.bold(pink(text2))}
|
|
5533
|
+
${import_picocolors7.default.dim("\u2500".repeat(terminalWidth()))}`;
|
|
5534
|
+
}
|
|
5535
|
+
if (level === 2) {
|
|
5536
|
+
return `
|
|
5537
|
+
${import_picocolors7.default.bold(pink(`\u258C ${text2}`))}`;
|
|
5538
|
+
}
|
|
5539
|
+
return import_picocolors7.default.bold(purple(text2));
|
|
5162
5540
|
}
|
|
5163
|
-
|
|
5164
|
-
|
|
5541
|
+
const bullet = BULLET_RE.exec(line);
|
|
5542
|
+
if (bullet) {
|
|
5543
|
+
const [, indent, item] = bullet;
|
|
5544
|
+
return `${indent}${bulletFor(item)} ${styleInline(item)}`;
|
|
5165
5545
|
}
|
|
5166
|
-
return line;
|
|
5546
|
+
return styleInline(line);
|
|
5167
5547
|
}
|
|
5168
5548
|
|
|
5169
5549
|
// src/output/reformat-tables.ts
|
|
5170
5550
|
var SEPARATOR_CELL_RE = /^:?-+:?$/;
|
|
5171
|
-
var BOLD_MARKERS_RE = /\*\*(.+?)\*\*/g;
|
|
5172
|
-
function visualWidth(text2) {
|
|
5173
|
-
return text2.replace(BOLD_MARKERS_RE, "$1").length;
|
|
5174
|
-
}
|
|
5175
5551
|
function isSeparatorCell(cell) {
|
|
5176
5552
|
return SEPARATOR_CELL_RE.test(cell);
|
|
5177
5553
|
}
|
|
@@ -5195,7 +5571,7 @@ function reformatTableBlock(lines) {
|
|
|
5195
5571
|
{ length: colCount },
|
|
5196
5572
|
(_, col) => Math.max(
|
|
5197
5573
|
3,
|
|
5198
|
-
...normalised.filter((row) => !isSeparatorRow(row)).map((row) =>
|
|
5574
|
+
...normalised.filter((row) => !isSeparatorRow(row)).map((row) => visibleWidth(row[col]))
|
|
5199
5575
|
)
|
|
5200
5576
|
);
|
|
5201
5577
|
const borderRow = `+${colWidths.map((w) => "-".repeat(w + 2)).join("+")}+`;
|
|
@@ -5204,8 +5580,8 @@ function reformatTableBlock(lines) {
|
|
|
5204
5580
|
return borderRow;
|
|
5205
5581
|
}
|
|
5206
5582
|
const cells = row.map((cell, col) => {
|
|
5207
|
-
const rendered =
|
|
5208
|
-
const pad = colWidths[col] -
|
|
5583
|
+
const rendered = styleInline(cell);
|
|
5584
|
+
const pad = colWidths[col] - visibleWidth(cell);
|
|
5209
5585
|
return rendered + " ".repeat(Math.max(0, pad));
|
|
5210
5586
|
});
|
|
5211
5587
|
return `| ${cells.join(" | ")} |`;
|
|
@@ -5254,7 +5630,7 @@ function createStreamingTableAligner() {
|
|
|
5254
5630
|
}
|
|
5255
5631
|
|
|
5256
5632
|
// src/output/spinner.ts
|
|
5257
|
-
var
|
|
5633
|
+
var import_picocolors8 = __toESM(require("picocolors"));
|
|
5258
5634
|
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
5259
5635
|
var FRAME_INTERVAL_MS = 80;
|
|
5260
5636
|
var DEFAULT_LABEL = "Thinking";
|
|
@@ -5266,7 +5642,7 @@ function createSpinner() {
|
|
|
5266
5642
|
frameIndex = 0;
|
|
5267
5643
|
timer = setInterval(() => {
|
|
5268
5644
|
const frame = FRAMES[frameIndex % FRAMES.length];
|
|
5269
|
-
process.stderr.write(`\r${
|
|
5645
|
+
process.stderr.write(`\r${import_picocolors8.default.dim(`${frame} ${label}...`)}`);
|
|
5270
5646
|
frameIndex += 1;
|
|
5271
5647
|
}, FRAME_INTERVAL_MS);
|
|
5272
5648
|
timer.unref();
|
|
@@ -5339,7 +5715,9 @@ async function* makeInteractivePrompt(abortController, turnGate, spinner) {
|
|
|
5339
5715
|
while (!abortController.signal.aborted) {
|
|
5340
5716
|
try {
|
|
5341
5717
|
process.stderr.write("\n");
|
|
5342
|
-
const issue = await
|
|
5718
|
+
const issue = await input({
|
|
5719
|
+
message: "Describe the issue to investigate:"
|
|
5720
|
+
});
|
|
5343
5721
|
process.stderr.write("\n");
|
|
5344
5722
|
const trimmed = issue.trim();
|
|
5345
5723
|
if (!trimmed) continue;
|
|
@@ -5356,7 +5734,7 @@ async function* makeInteractivePrompt(abortController, turnGate, spinner) {
|
|
|
5356
5734
|
await turnGate.wait();
|
|
5357
5735
|
if (abortController.signal.aborted) break;
|
|
5358
5736
|
process.stderr.write("\n");
|
|
5359
|
-
const response = await
|
|
5737
|
+
const response = await input({ message: "You:" });
|
|
5360
5738
|
process.stderr.write("\n");
|
|
5361
5739
|
const trimmed = response.trim();
|
|
5362
5740
|
if (!trimmed) continue;
|
|
@@ -5374,33 +5752,33 @@ async function displayDegradedStatus(session2) {
|
|
|
5374
5752
|
hostManagerStatus(session2),
|
|
5375
5753
|
hostManagerAlerts(session2)
|
|
5376
5754
|
]);
|
|
5377
|
-
process.stderr.write(
|
|
5755
|
+
process.stderr.write(import_picocolors9.default.bold("\u2500\u2500 Host Manager Status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
|
|
5378
5756
|
if (statusResult.ok) {
|
|
5379
5757
|
const rows = statusResult.data;
|
|
5380
5758
|
const maxKeyLen = rows.reduce((max, r) => Math.max(max, r.key.length), 0);
|
|
5381
5759
|
for (const row of rows) {
|
|
5382
|
-
process.stderr.write(` ${
|
|
5760
|
+
process.stderr.write(` ${import_picocolors9.default.dim(row.key.padEnd(maxKeyLen))} ${row.value}
|
|
5383
5761
|
`);
|
|
5384
5762
|
}
|
|
5385
5763
|
} else {
|
|
5386
|
-
process.stderr.write(` ${
|
|
5764
|
+
process.stderr.write(` ${import_picocolors9.default.red(`Error: ${statusResult.error}`)}
|
|
5387
5765
|
`);
|
|
5388
5766
|
}
|
|
5389
5767
|
process.stderr.write("\n");
|
|
5390
|
-
process.stderr.write(
|
|
5768
|
+
process.stderr.write(import_picocolors9.default.bold("\u2500\u2500 Recent Alerts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
|
|
5391
5769
|
if (alertsResult.ok) {
|
|
5392
5770
|
const alerts = alertsResult.data;
|
|
5393
5771
|
if (alerts.length === 0) {
|
|
5394
|
-
process.stderr.write(` ${
|
|
5772
|
+
process.stderr.write(` ${import_picocolors9.default.dim("No recent alerts.")}
|
|
5395
5773
|
`);
|
|
5396
5774
|
} else {
|
|
5397
5775
|
for (const alert of alerts) {
|
|
5398
|
-
process.stderr.write(` ${
|
|
5776
|
+
process.stderr.write(` ${import_picocolors9.default.dim(alert.timestamp)} ${alert.type} ${alert.params}
|
|
5399
5777
|
`);
|
|
5400
5778
|
}
|
|
5401
5779
|
}
|
|
5402
5780
|
} else {
|
|
5403
|
-
process.stderr.write(` ${
|
|
5781
|
+
process.stderr.write(` ${import_picocolors9.default.dim(`Unavailable: ${alertsResult.error}`)}
|
|
5404
5782
|
`);
|
|
5405
5783
|
}
|
|
5406
5784
|
process.stderr.write("\n");
|
|
@@ -5429,13 +5807,13 @@ async function runAgent(session2, kineticaVersion, degraded, model, runOptions)
|
|
|
5429
5807
|
const budget = checkPromptBudget(systemPrompt);
|
|
5430
5808
|
if (process.env.DEBUG) {
|
|
5431
5809
|
process.stderr.write(
|
|
5432
|
-
|
|
5810
|
+
import_picocolors9.default.dim(`System prompt: ~${budget.tokens} tokens (${budget.chars} chars)
|
|
5433
5811
|
`)
|
|
5434
5812
|
);
|
|
5435
5813
|
}
|
|
5436
5814
|
if (budget.overBudget) {
|
|
5437
5815
|
process.stderr.write(
|
|
5438
|
-
|
|
5816
|
+
import_picocolors9.default.yellow(
|
|
5439
5817
|
`\u26A0 system prompt is ~${budget.tokens} tokens (threshold ${budget.threshold}) \u2014 knowledge corpus is getting expensive; consider keyword-based playbook selection.
|
|
5440
5818
|
`
|
|
5441
5819
|
)
|
|
@@ -5453,7 +5831,7 @@ async function runAgent(session2, kineticaVersion, degraded, model, runOptions)
|
|
|
5453
5831
|
const confirmPath = async (path2) => {
|
|
5454
5832
|
spinner.stop();
|
|
5455
5833
|
try {
|
|
5456
|
-
const answer = await
|
|
5834
|
+
const answer = await input({
|
|
5457
5835
|
message: `Load support bundle from "${path2}"? The agent will be able to read files under that directory. (y/n):`
|
|
5458
5836
|
});
|
|
5459
5837
|
return answer.trim().toLowerCase() === "y";
|
|
@@ -5510,8 +5888,8 @@ async function runAgent(session2, kineticaVersion, degraded, model, runOptions)
|
|
|
5510
5888
|
abortController,
|
|
5511
5889
|
env: { ...process.env, CLAUDE_AGENT_SDK_CLIENT_APP: "admin-agent" }
|
|
5512
5890
|
};
|
|
5513
|
-
const guardLine = dollarCapped ?
|
|
5514
|
-
`) :
|
|
5891
|
+
const guardLine = dollarCapped ? import_picocolors9.default.dim(`Budget guard: $${resolvedBudgetUsd.toFixed(2)} (raise with --max-budget)
|
|
5892
|
+
`) : import_picocolors9.default.dim("Budget guard: subscription (Pro/Max) \u2014 turn-limited\n");
|
|
5515
5893
|
const bundleSummary = () => {
|
|
5516
5894
|
const src = bundleHolder.get();
|
|
5517
5895
|
if (!src) return "";
|
|
@@ -5536,7 +5914,7 @@ async function runAgent(session2, kineticaVersion, degraded, model, runOptions)
|
|
|
5536
5914
|
);
|
|
5537
5915
|
} else {
|
|
5538
5916
|
process.stderr.write(
|
|
5539
|
-
|
|
5917
|
+
import_picocolors9.default.dim("Tip: ask me to analyze a support bundle to add offline log/config analysis.\n")
|
|
5540
5918
|
);
|
|
5541
5919
|
}
|
|
5542
5920
|
} else {
|
|
@@ -5603,7 +5981,7 @@ async function runAgent(session2, kineticaVersion, degraded, model, runOptions)
|
|
|
5603
5981
|
if (budgetTracker.shouldWarn()) {
|
|
5604
5982
|
spinner.stop();
|
|
5605
5983
|
process.stderr.write(
|
|
5606
|
-
|
|
5984
|
+
import_picocolors9.default.yellow(
|
|
5607
5985
|
`
|
|
5608
5986
|
\u26A0 Approaching budget guard (~$${budgetTracker.spentUsd().toFixed(2)} / $${resolvedBudgetUsd.toFixed(2)}) \u2014 wrapping up soon. Save a partial report now if you want to preserve findings.
|
|
5609
5987
|
`
|
|
@@ -5626,7 +6004,7 @@ async function runAgent(session2, kineticaVersion, degraded, model, runOptions)
|
|
|
5626
6004
|
if (assistantMsg.error) {
|
|
5627
6005
|
spinner.stop();
|
|
5628
6006
|
const label = ERROR_LABELS[assistantMsg.error] ?? assistantMsg.error;
|
|
5629
|
-
process.stderr.write(
|
|
6007
|
+
process.stderr.write(import_picocolors9.default.yellow(`
|
|
5630
6008
|
API error: ${label}
|
|
5631
6009
|
`));
|
|
5632
6010
|
turnGate.open();
|
|
@@ -5643,7 +6021,7 @@ API error: ${label}
|
|
|
5643
6021
|
cacheCreationTokens = usages.reduce((sum, u) => sum + (u.cacheCreationInputTokens ?? 0), 0);
|
|
5644
6022
|
if (resultMsg.subtype === "error_max_turns") {
|
|
5645
6023
|
process.stderr.write(
|
|
5646
|
-
|
|
6024
|
+
import_picocolors9.default.yellow(
|
|
5647
6025
|
`
|
|
5648
6026
|
Reached the turn limit (${numTurns} turns) \u2014 a safety guard, not an error. Any report the agent saved is in reports/. Start a fresh session to continue.
|
|
5649
6027
|
`
|
|
@@ -5652,7 +6030,7 @@ Reached the turn limit (${numTurns} turns) \u2014 a safety guard, not an error.
|
|
|
5652
6030
|
} else if (resultMsg.subtype === "error_max_budget_usd") {
|
|
5653
6031
|
const spentStr = totalCostUsd > 0 ? ` ($${totalCostUsd.toFixed(2)} spent)` : "";
|
|
5654
6032
|
process.stderr.write(
|
|
5655
|
-
|
|
6033
|
+
import_picocolors9.default.yellow(
|
|
5656
6034
|
`
|
|
5657
6035
|
Reached the $${resolvedBudgetUsd.toFixed(2)} budget guard${spentStr} \u2014 a safety limit, not an error. Re-run with --max-budget=<amount> (or set ADMIN_AGENT_MAX_BUDGET) for more headroom. Any report the agent saved is in reports/.
|
|
5658
6036
|
`
|
|
@@ -5711,7 +6089,7 @@ Warning: MCP server "${s.name}" failed to connect (${s.status})
|
|
|
5711
6089
|
const statusStr = retryMsg.error_status !== null ? ` (HTTP ${retryMsg.error_status})` : "";
|
|
5712
6090
|
const delaySec = Math.round(retryMsg.retry_delay_ms / 1e3);
|
|
5713
6091
|
process.stderr.write(
|
|
5714
|
-
|
|
6092
|
+
import_picocolors9.default.yellow(
|
|
5715
6093
|
`
|
|
5716
6094
|
API error${statusStr}. Retrying (attempt ${retryMsg.attempt}/${retryMsg.max_retries}) in ${delaySec}s...
|
|
5717
6095
|
`
|
|
@@ -5740,7 +6118,7 @@ Rate limited \u2014 requests rejected.${resetStr}
|
|
|
5740
6118
|
} else if (message.type === "control_request") {
|
|
5741
6119
|
const controlMsg = message;
|
|
5742
6120
|
if (controlMsg.request.subtype === "claude_authenticate") {
|
|
5743
|
-
process.stderr.write(
|
|
6121
|
+
process.stderr.write(import_picocolors9.default.yellow("\nRe-authentication requested by SDK...\n"));
|
|
5744
6122
|
}
|
|
5745
6123
|
}
|
|
5746
6124
|
}
|
|
@@ -5751,7 +6129,7 @@ Rate limited \u2014 requests rejected.${resetStr}
|
|
|
5751
6129
|
} else {
|
|
5752
6130
|
hadNonAbortError = true;
|
|
5753
6131
|
const message = error instanceof Error ? error.message : String(error);
|
|
5754
|
-
process.stderr.write(
|
|
6132
|
+
process.stderr.write(import_picocolors9.default.red(`
|
|
5755
6133
|
Agent error: ${message}
|
|
5756
6134
|
`));
|
|
5757
6135
|
}
|
|
@@ -5765,7 +6143,7 @@ Agent error: ${message}
|
|
|
5765
6143
|
const sessionCost = dollarCapped ? totalCostUsd : void 0;
|
|
5766
6144
|
if (process.env.DEBUG && (cacheReadTokens > 0 || cacheCreationTokens > 0)) {
|
|
5767
6145
|
process.stderr.write(
|
|
5768
|
-
|
|
6146
|
+
import_picocolors9.default.dim(
|
|
5769
6147
|
`Cache: ${cacheReadTokens} read / ${cacheCreationTokens} created input tokens (read > 0 confirms the system prompt is served from cache)
|
|
5770
6148
|
`
|
|
5771
6149
|
)
|
|
@@ -5793,7 +6171,7 @@ var MODEL_LABELS = {
|
|
|
5793
6171
|
opus: "Opus \u2014 deepest reasoning, slower & pricier"
|
|
5794
6172
|
};
|
|
5795
6173
|
async function selectModel() {
|
|
5796
|
-
return
|
|
6174
|
+
return select({
|
|
5797
6175
|
message: "Select model for this session:",
|
|
5798
6176
|
default: DEFAULT_AGENT_MODEL,
|
|
5799
6177
|
choices: SUPPORTED_MODELS.map((value) => ({ value, name: MODEL_LABELS[value] }))
|
|
@@ -5804,7 +6182,7 @@ async function selectModel() {
|
|
|
5804
6182
|
var import_claude_agent_sdk6 = require("@anthropic-ai/claude-agent-sdk");
|
|
5805
6183
|
|
|
5806
6184
|
// src/auth/oauth-flow.ts
|
|
5807
|
-
var
|
|
6185
|
+
var import_picocolors10 = __toESM(require("picocolors"));
|
|
5808
6186
|
|
|
5809
6187
|
// src/auth/open-browser.ts
|
|
5810
6188
|
var import_child_process = require("child_process");
|
|
@@ -5830,21 +6208,21 @@ async function resolveAuthentication(agentQuery, options) {
|
|
|
5830
6208
|
const { manualUrl, automaticUrl } = await query3.claudeAuthenticate(options.loginWithClaudeAi);
|
|
5831
6209
|
const opened = openBrowser(automaticUrl);
|
|
5832
6210
|
if (opened) {
|
|
5833
|
-
process.stderr.write(
|
|
6211
|
+
process.stderr.write(import_picocolors10.default.dim("Browser opened for login. Waiting for authentication...\n"));
|
|
5834
6212
|
} else {
|
|
5835
6213
|
process.stderr.write(`
|
|
5836
6214
|
Open this URL in your browser to log in:
|
|
5837
|
-
${
|
|
6215
|
+
${import_picocolors10.default.bold(manualUrl)}
|
|
5838
6216
|
|
|
5839
6217
|
`);
|
|
5840
|
-
process.stderr.write(
|
|
6218
|
+
process.stderr.write(import_picocolors10.default.dim("Waiting for browser login to complete...\n"));
|
|
5841
6219
|
}
|
|
5842
6220
|
await query3.claudeOAuthWaitForCompletion();
|
|
5843
6221
|
return { method: "oauth" };
|
|
5844
6222
|
} catch (err) {
|
|
5845
6223
|
const message = err instanceof Error ? err.message : String(err);
|
|
5846
6224
|
process.stderr.write(
|
|
5847
|
-
|
|
6225
|
+
import_picocolors10.default.yellow(`
|
|
5848
6226
|
Warning: OAuth login failed (${message}). SDK may retry automatically.
|
|
5849
6227
|
`)
|
|
5850
6228
|
);
|
|
@@ -5946,10 +6324,9 @@ async function logout() {
|
|
|
5946
6324
|
|
|
5947
6325
|
// src/session/env-file.ts
|
|
5948
6326
|
var import_fs2 = require("fs");
|
|
5949
|
-
var
|
|
6327
|
+
var import_promises9 = require("fs/promises");
|
|
5950
6328
|
var import_path2 = require("path");
|
|
5951
|
-
var
|
|
5952
|
-
var import_picocolors10 = __toESM(require("picocolors"));
|
|
6329
|
+
var import_picocolors11 = __toESM(require("picocolors"));
|
|
5953
6330
|
function parseEnvContent(content) {
|
|
5954
6331
|
const entries = [];
|
|
5955
6332
|
for (const line of content.split("\n")) {
|
|
@@ -6031,7 +6408,7 @@ function loadEnvFile(dir, env = process.env) {
|
|
|
6031
6408
|
async function offerSaveCredentials(url, user, dir) {
|
|
6032
6409
|
if (!process.stdin.isTTY) return;
|
|
6033
6410
|
try {
|
|
6034
|
-
const shouldSave = await
|
|
6411
|
+
const shouldSave = await confirm({
|
|
6035
6412
|
message: "Save KINETICA_URL and KINETICA_USER to .env? (password is never saved)",
|
|
6036
6413
|
default: true
|
|
6037
6414
|
});
|
|
@@ -6039,52 +6416,50 @@ async function offerSaveCredentials(url, user, dir) {
|
|
|
6039
6416
|
const filePath = (0, import_path2.join)(dir ?? process.cwd(), ".env");
|
|
6040
6417
|
let existing;
|
|
6041
6418
|
try {
|
|
6042
|
-
existing = await (0,
|
|
6419
|
+
existing = await (0, import_promises9.readFile)(filePath, "utf8");
|
|
6043
6420
|
} catch {
|
|
6044
6421
|
}
|
|
6045
6422
|
const content = buildEnvContent(url, user, existing);
|
|
6046
|
-
await (0,
|
|
6047
|
-
console.error(
|
|
6423
|
+
await (0, import_promises9.writeFile)(filePath, content, "utf8");
|
|
6424
|
+
console.error(import_picocolors11.default.dim("Saved to .env"));
|
|
6048
6425
|
} catch (err) {
|
|
6049
6426
|
const message = err instanceof Error ? err.message : String(err);
|
|
6050
|
-
console.error(
|
|
6427
|
+
console.error(import_picocolors11.default.yellow(`Could not save .env file: ${message}`));
|
|
6051
6428
|
}
|
|
6052
6429
|
}
|
|
6053
6430
|
|
|
6054
6431
|
// src/session/verify.ts
|
|
6055
|
-
var
|
|
6056
|
-
var import_prompts10 = require("@inquirer/prompts");
|
|
6432
|
+
var import_picocolors14 = __toESM(require("picocolors"));
|
|
6057
6433
|
|
|
6058
6434
|
// src/session/collect.ts
|
|
6059
|
-
var
|
|
6060
|
-
var import_picocolors11 = __toESM(require("picocolors"));
|
|
6435
|
+
var import_picocolors12 = __toESM(require("picocolors"));
|
|
6061
6436
|
async function collectCredentials() {
|
|
6062
6437
|
const prompted = /* @__PURE__ */ new Set();
|
|
6063
6438
|
const envUrl = process.env.KINETICA_URL;
|
|
6064
6439
|
const envUser = process.env.KINETICA_USER;
|
|
6065
6440
|
if (envUrl && envUser && process.stdin.isTTY) {
|
|
6066
|
-
console.error(
|
|
6067
|
-
const useSaved = await
|
|
6441
|
+
console.error(import_picocolors12.default.dim(`Saved connection: ${envUrl} (${envUser})`));
|
|
6442
|
+
const useSaved = await confirm({
|
|
6068
6443
|
message: "Use saved connection?",
|
|
6069
6444
|
default: true
|
|
6070
6445
|
});
|
|
6071
6446
|
if (!useSaved) {
|
|
6072
6447
|
prompted.add("url");
|
|
6073
6448
|
prompted.add("user");
|
|
6074
|
-
const url2 = await
|
|
6075
|
-
const user2 = await
|
|
6076
|
-
const pass2 = await
|
|
6449
|
+
const url2 = await input({ message: "Kinetica endpoint URL:" });
|
|
6450
|
+
const user2 = await input({ message: "Admin username:" });
|
|
6451
|
+
const pass2 = await password({ message: "Admin password:", mask: "*" });
|
|
6077
6452
|
return { credentials: { url: url2, user: user2, pass: pass2 }, prompted };
|
|
6078
6453
|
}
|
|
6079
6454
|
}
|
|
6080
|
-
const url = envUrl ?? (prompted.add("url"), await
|
|
6081
|
-
const user = envUser ?? (prompted.add("user"), await
|
|
6082
|
-
const pass = process.env.KINETICA_PASS ?? await
|
|
6455
|
+
const url = envUrl ?? (prompted.add("url"), await input({ message: "Kinetica endpoint URL:" }));
|
|
6456
|
+
const user = envUser ?? (prompted.add("user"), await input({ message: "Admin username:" }));
|
|
6457
|
+
const pass = process.env.KINETICA_PASS ?? await password({ message: "Admin password:", mask: "*" });
|
|
6083
6458
|
return { credentials: { url, user, pass }, prompted };
|
|
6084
6459
|
}
|
|
6085
6460
|
async function repromptCredentials() {
|
|
6086
|
-
const user = await
|
|
6087
|
-
const pass = await
|
|
6461
|
+
const user = await input({ message: "Admin username:" });
|
|
6462
|
+
const pass = await password({ message: "Admin password:", mask: "*" });
|
|
6088
6463
|
return { user, pass };
|
|
6089
6464
|
}
|
|
6090
6465
|
|
|
@@ -6120,13 +6495,12 @@ function createSession(url, user, pass, options) {
|
|
|
6120
6495
|
}
|
|
6121
6496
|
|
|
6122
6497
|
// src/session/resolve-url.ts
|
|
6123
|
-
var
|
|
6124
|
-
var import_prompts9 = require("@inquirer/prompts");
|
|
6498
|
+
var import_picocolors13 = __toESM(require("picocolors"));
|
|
6125
6499
|
var PROBE_TIMEOUT_MS2 = 3e3;
|
|
6126
6500
|
var HTTP_PROTOCOL_RE = /^https?:\/\//i;
|
|
6127
6501
|
var ANY_PROTOCOL_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
6128
|
-
function hasProtocol(
|
|
6129
|
-
return HTTP_PROTOCOL_RE.test(
|
|
6502
|
+
function hasProtocol(input2) {
|
|
6503
|
+
return HTTP_PROTOCOL_RE.test(input2);
|
|
6130
6504
|
}
|
|
6131
6505
|
function stripTrailingSlashes(url) {
|
|
6132
6506
|
return url.replace(/\/+$/, "");
|
|
@@ -6150,20 +6524,20 @@ function isInteractive() {
|
|
|
6150
6524
|
}
|
|
6151
6525
|
async function confirmHttpFallback(host) {
|
|
6152
6526
|
process.stderr.write(
|
|
6153
|
-
"\n" +
|
|
6154
|
-
|
|
6527
|
+
"\n" + import_picocolors13.default.red(
|
|
6528
|
+
import_picocolors13.default.bold(
|
|
6155
6529
|
` WARNING: HTTPS unavailable at ${host}.
|
|
6156
6530
|
Falling back to plaintext HTTP will transmit your Kinetica credentials in the clear.
|
|
6157
6531
|
`
|
|
6158
6532
|
)
|
|
6159
|
-
) +
|
|
6533
|
+
) + import_picocolors13.default.dim(
|
|
6160
6534
|
` Set KINETICA_HTTPS_ONLY=1 to refuse this fallback automatically, or pass an explicit http:// prefix to silence this prompt.
|
|
6161
6535
|
|
|
6162
6536
|
`
|
|
6163
6537
|
)
|
|
6164
6538
|
);
|
|
6165
6539
|
try {
|
|
6166
|
-
return await
|
|
6540
|
+
return await confirm({
|
|
6167
6541
|
message: "Continue over plaintext HTTP?",
|
|
6168
6542
|
default: false
|
|
6169
6543
|
});
|
|
@@ -6171,8 +6545,8 @@ async function confirmHttpFallback(host) {
|
|
|
6171
6545
|
return false;
|
|
6172
6546
|
}
|
|
6173
6547
|
}
|
|
6174
|
-
async function resolveUrl(
|
|
6175
|
-
const trimmed =
|
|
6548
|
+
async function resolveUrl(input2, options = {}) {
|
|
6549
|
+
const trimmed = input2.trim();
|
|
6176
6550
|
if (trimmed === "") {
|
|
6177
6551
|
return { ok: false, error: "URL is empty" };
|
|
6178
6552
|
}
|
|
@@ -6189,7 +6563,7 @@ async function resolveUrl(input5, options = {}) {
|
|
|
6189
6563
|
}
|
|
6190
6564
|
const httpsUrl = `https://${normalized}`;
|
|
6191
6565
|
const httpUrl = `http://${normalized}`;
|
|
6192
|
-
console.error(
|
|
6566
|
+
console.error(import_picocolors13.default.dim("Detecting protocol..."));
|
|
6193
6567
|
if (await probeProtocol(httpsUrl)) {
|
|
6194
6568
|
return { ok: true, url: httpsUrl };
|
|
6195
6569
|
}
|
|
@@ -6294,7 +6668,7 @@ async function connectWithRetry() {
|
|
|
6294
6668
|
const { credentials, prompted } = await collectCredentials();
|
|
6295
6669
|
const resolved = await resolveUrl(credentials.url);
|
|
6296
6670
|
if (!resolved.ok) {
|
|
6297
|
-
console.error(
|
|
6671
|
+
console.error(import_picocolors14.default.red(resolved.error));
|
|
6298
6672
|
process.exit(1);
|
|
6299
6673
|
}
|
|
6300
6674
|
const resolvedUrl = resolved.url;
|
|
@@ -6306,17 +6680,17 @@ async function connectWithRetry() {
|
|
|
6306
6680
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
6307
6681
|
try {
|
|
6308
6682
|
const kineticaVersion = await verifyConnectivity(session2);
|
|
6309
|
-
console.error(
|
|
6683
|
+
console.error(import_picocolors14.default.green("Connected to Kinetica successfully."));
|
|
6310
6684
|
if (prompted.size > 0 || wasReprompted) {
|
|
6311
6685
|
await offerSaveCredentials(resolvedUrl, currentUser);
|
|
6312
6686
|
}
|
|
6313
6687
|
return { session: session2, kineticaVersion, degraded: false };
|
|
6314
6688
|
} catch (err) {
|
|
6315
6689
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6316
|
-
console.error(
|
|
6690
|
+
console.error(import_picocolors14.default.red(`Connection failed (attempt ${attempt}/${MAX_RETRIES}): ${msg}`));
|
|
6317
6691
|
if (isCredentialError(msg)) {
|
|
6318
6692
|
if (process.stdin.isTTY && repromptCount < MAX_REPROMPTS) {
|
|
6319
|
-
const shouldRetry = await
|
|
6693
|
+
const shouldRetry = await confirm({
|
|
6320
6694
|
message: "Credentials may be incorrect. Re-enter?",
|
|
6321
6695
|
default: true
|
|
6322
6696
|
});
|
|
@@ -6331,15 +6705,15 @@ async function connectWithRetry() {
|
|
|
6331
6705
|
continue;
|
|
6332
6706
|
}
|
|
6333
6707
|
}
|
|
6334
|
-
console.error(
|
|
6708
|
+
console.error(import_picocolors14.default.red("Authentication failed. Exiting."));
|
|
6335
6709
|
process.exit(1);
|
|
6336
6710
|
}
|
|
6337
6711
|
if (attempt === MAX_RETRIES) {
|
|
6338
|
-
console.error(
|
|
6712
|
+
console.error(import_picocolors14.default.yellow("DB engine unreachable. Probing host manager on port 9300..."));
|
|
6339
6713
|
const hmResult = await probeHostManager(session2);
|
|
6340
6714
|
if (hmResult.ok) {
|
|
6341
6715
|
console.error(
|
|
6342
|
-
|
|
6716
|
+
import_picocolors14.default.yellow(
|
|
6343
6717
|
"Connected in DEGRADED MODE (host manager only). Most diagnostic tools will be unavailable."
|
|
6344
6718
|
)
|
|
6345
6719
|
);
|
|
@@ -6348,7 +6722,7 @@ async function connectWithRetry() {
|
|
|
6348
6722
|
}
|
|
6349
6723
|
return { session: session2, kineticaVersion: hmResult.version, degraded: true };
|
|
6350
6724
|
}
|
|
6351
|
-
console.error(
|
|
6725
|
+
console.error(import_picocolors14.default.red("Host manager also unreachable. Exiting."));
|
|
6352
6726
|
process.exit(1);
|
|
6353
6727
|
}
|
|
6354
6728
|
}
|
|
@@ -6429,7 +6803,7 @@ async function main() {
|
|
|
6429
6803
|
} else {
|
|
6430
6804
|
const valid = SUPPORTED_MODELS.join(", ");
|
|
6431
6805
|
process.stderr.write(
|
|
6432
|
-
|
|
6806
|
+
import_picocolors15.default.red(`Error: unknown --model value "${modelValue}". Valid models: ${valid}
|
|
6433
6807
|
`)
|
|
6434
6808
|
);
|
|
6435
6809
|
process.exitCode = 1;
|
|
@@ -6442,7 +6816,7 @@ async function main() {
|
|
|
6442
6816
|
const parsed = Number(budgetValue);
|
|
6443
6817
|
if (!isValidBudget(parsed)) {
|
|
6444
6818
|
process.stderr.write(
|
|
6445
|
-
|
|
6819
|
+
import_picocolors15.default.red(
|
|
6446
6820
|
`Error: invalid --max-budget value "${budgetValue}". Use a positive number, e.g. --max-budget=10
|
|
6447
6821
|
`
|
|
6448
6822
|
)
|
|
@@ -6455,7 +6829,7 @@ async function main() {
|
|
|
6455
6829
|
const bundlePath = flagValue(args, "--bundle");
|
|
6456
6830
|
if (bundlePath?.trim() === "") {
|
|
6457
6831
|
process.stderr.write(
|
|
6458
|
-
|
|
6832
|
+
import_picocolors15.default.red(
|
|
6459
6833
|
"Error: --bundle requires a directory path, e.g. --bundle=/path/to/extracted-bundle\n"
|
|
6460
6834
|
)
|
|
6461
6835
|
);
|
|
@@ -6468,28 +6842,33 @@ async function main() {
|
|
|
6468
6842
|
model = await selectModel();
|
|
6469
6843
|
}
|
|
6470
6844
|
const effectiveModel = model ?? DEFAULT_AGENT_MODEL;
|
|
6471
|
-
process.stderr.write(
|
|
6845
|
+
process.stderr.write(import_picocolors15.default.dim(`Model: ${effectiveModel}
|
|
6472
6846
|
`));
|
|
6473
6847
|
const authResult = await authenticateAnthropic({ forceLogin, loginMethod, loginOrgUUID });
|
|
6474
6848
|
if (authResult.method === "oauth") {
|
|
6475
6849
|
const acctInfo = authResult.email ? ` (${authResult.email})` : "";
|
|
6476
|
-
process.stderr.write(
|
|
6850
|
+
process.stderr.write(import_picocolors15.default.dim(`Authenticated via OAuth${acctInfo}
|
|
6477
6851
|
`));
|
|
6478
6852
|
} else {
|
|
6479
|
-
process.stderr.write(
|
|
6853
|
+
process.stderr.write(import_picocolors15.default.dim("Authenticated via API key\n"));
|
|
6480
6854
|
}
|
|
6481
6855
|
const maxBudgetUsd = resolveMaxBudgetUsd(maxBudgetFlag);
|
|
6482
6856
|
if (bundlePath !== void 0) {
|
|
6483
6857
|
const result = await verifyBundle(bundlePath);
|
|
6484
6858
|
if (!result.ok) {
|
|
6485
|
-
process.stderr.write(
|
|
6859
|
+
process.stderr.write(import_picocolors15.default.red(`Error: ${result.error}
|
|
6486
6860
|
`));
|
|
6487
6861
|
process.exitCode = 1;
|
|
6488
6862
|
return;
|
|
6489
6863
|
}
|
|
6490
|
-
if (result.
|
|
6864
|
+
if (result.layoutWarning !== void 0) {
|
|
6865
|
+
process.stderr.write(
|
|
6866
|
+
import_picocolors15.default.yellow(`Warning: ${result.layoutWarning} Diagnosing with what is present.
|
|
6867
|
+
`)
|
|
6868
|
+
);
|
|
6869
|
+
} else if (result.missingExpected.length > 0) {
|
|
6491
6870
|
process.stderr.write(
|
|
6492
|
-
|
|
6871
|
+
import_picocolors15.default.yellow(
|
|
6493
6872
|
`Warning: bundle is missing expected artifact(s): ${result.missingExpected.join(", ")}. Diagnosing with what is present.
|
|
6494
6873
|
`
|
|
6495
6874
|
)
|
|
@@ -6497,11 +6876,11 @@ async function main() {
|
|
|
6497
6876
|
}
|
|
6498
6877
|
const live = await connectBestEffort();
|
|
6499
6878
|
process.stderr.write(
|
|
6500
|
-
live ?
|
|
6879
|
+
live ? import_picocolors15.default.dim("Live connection available \u2014 bundle + live verification enabled.\n") : import_picocolors15.default.dim("No reachable live connection \u2014 offline bundle analysis only.\n")
|
|
6501
6880
|
);
|
|
6502
6881
|
if (live?.kineticaVersion && result.kineticaVersion && live.kineticaVersion !== result.kineticaVersion) {
|
|
6503
6882
|
process.stderr.write(
|
|
6504
|
-
|
|
6883
|
+
import_picocolors15.default.yellow(
|
|
6505
6884
|
`Warning: live cluster version (${live.kineticaVersion}) differs from the bundle's captured version (${result.kineticaVersion}). Reasoning over the bundle uses the captured version; the live cluster may have been upgraded since capture.
|
|
6506
6885
|
`
|
|
6507
6886
|
)
|
|
@@ -6533,7 +6912,7 @@ function getSession() {
|
|
|
6533
6912
|
if (process.env.NODE_ENV !== "test") {
|
|
6534
6913
|
main().catch((err) => {
|
|
6535
6914
|
const message = err instanceof Error ? err.message : String(err);
|
|
6536
|
-
process.stderr.write(
|
|
6915
|
+
process.stderr.write(import_picocolors15.default.red(`Error: ${message}
|
|
6537
6916
|
`));
|
|
6538
6917
|
if (verbose && err instanceof Error && err.stack) {
|
|
6539
6918
|
process.stderr.write(err.stack + "\n");
|