@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.
@@ -37,10 +37,10 @@ __export(cli_exports, {
37
37
  verbose: () => verbose
38
38
  });
39
39
  module.exports = __toCommonJS(cli_exports);
40
- var import_picocolors14 = __toESM(require("picocolors"));
40
+ var import_picocolors15 = __toESM(require("picocolors"));
41
41
 
42
42
  // src/cli/banner.ts
43
- var import_picocolors = __toESM(require("picocolors"));
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(PURPLE, HOT_PINK, i / maxIdx);
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 ${import_picocolors.default.dim(`v${version}`)}`;
134
+ const subtitle = `admin-agent ${import_picocolors2.default.dim(`v${version}`)}`;
114
135
  const header = model ? `${subtitle}
115
- ${import_picocolors.default.dim(`Model: ${model}`)}` : subtitle;
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/cli/select-model.ts
121
- var import_prompts6 = require("@inquirer/prompts");
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 import_prompts5 = require("@inquirer/prompts");
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 import_picocolors4 = __toESM(require("picocolors"));
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, input5) {
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, input5);
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, input5) {
904
- const { category, key_pattern } = input5;
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(input5) {
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: input5.source,
1149
- min_severity: input5.min_severity,
1150
- duration: input5.duration,
1151
- start_time: input5.start_time,
1152
- end_time: input5.end_time,
1153
- node_id: input5.node_id,
1154
- limit: input5.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, input5) {
1213
+ async function getLogs(session2, input2) {
1160
1214
  const params = {
1161
- source: input5.source,
1162
- severity: input5.min_severity,
1163
- limit: input5.limit
1215
+ source: input2.source,
1216
+ severity: input2.min_severity,
1217
+ limit: input2.limit
1164
1218
  };
1165
- if (input5.duration !== void 0) {
1166
- params.duration = input5.duration;
1219
+ if (input2.duration !== void 0) {
1220
+ params.duration = input2.duration;
1167
1221
  }
1168
- if (input5.start_time !== void 0) {
1169
- params.start_time = input5.start_time;
1222
+ if (input2.start_time !== void 0) {
1223
+ params.start_time = input2.start_time;
1170
1224
  }
1171
- if (input5.end_time !== void 0) {
1172
- params.end_time = input5.end_time;
1225
+ if (input2.end_time !== void 0) {
1226
+ params.end_time = input2.end_time;
1173
1227
  }
1174
- if (input5.node_id !== void 0) {
1175
- params.node_id = input5.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(input5);
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(input5);
1244
+ return buildStubResponse(input2);
1191
1245
  }
1192
1246
  } catch {
1193
- return buildStubResponse(input5);
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, input5) {
1392
+ async function getResourceGroups(session2, input2) {
1339
1393
  try {
1340
1394
  const response = await session2.makeRequest("/show/resourcegroups", {
1341
- names: input5.names,
1395
+ names: input2.names,
1342
1396
  options: {
1343
- show_tier_usage: String(input5.show_tier_usage ?? false),
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, input5) {
1448
+ async function verifyDb(session2, input2) {
1395
1449
  const options = {
1396
1450
  concurrent_safe: "true"
1397
1451
  };
1398
- if (input5.verify_nulls !== void 0) {
1399
- options.verify_nulls = String(input5.verify_nulls);
1452
+ if (input2.verify_nulls !== void 0) {
1453
+ options.verify_nulls = String(input2.verify_nulls);
1400
1454
  }
1401
- if (input5.verify_persist !== void 0) {
1402
- options.verify_persist = String(input5.verify_persist);
1455
+ if (input2.verify_persist !== void 0) {
1456
+ options.verify_persist = String(input2.verify_persist);
1403
1457
  }
1404
- if (input5.verify_rank0 !== void 0) {
1405
- options.verify_rank0 = String(input5.verify_rank0);
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, input5) {
1512
+ async function showSecurity(session2, input2) {
1459
1513
  try {
1460
1514
  const response = await session2.makeRequest("/show/security", {
1461
- names: input5.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(input5) {
1558
- if (input5.get_column_info === true) return "true";
1559
- if (input5.get_column_info === false) return "false";
1560
- return input5.table_name !== "" ? "true" : "false";
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, input5) {
1680
+ async function showTable(session2, input2) {
1627
1681
  const options = {
1628
- get_sizes: String(input5.get_sizes ?? true),
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(input5)
1685
+ get_column_info: resolveColumnInfoOption(input2)
1632
1686
  };
1633
- if (input5.get_access_data !== void 0) {
1634
- options.get_access_data = String(input5.get_access_data);
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: input5.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, input5.table_name);
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, input5) {
1779
+ async function getResourceObjects(session2, input2) {
1726
1780
  const options = {
1727
- table_names: input5.table_names ?? "*",
1728
- limit: String(input5.limit ?? 100)
1781
+ table_names: input2.table_names ?? "*",
1782
+ limit: String(input2.limit ?? 100)
1729
1783
  };
1730
- if (input5.tiers !== void 0) {
1731
- options.tiers = input5.tiers;
1784
+ if (input2.tiers !== void 0) {
1785
+ options.tiers = input2.tiers;
1732
1786
  }
1733
- if (input5.order_by !== void 0) {
1734
- options.order_by = input5.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, input5) {
2139
- const requestedKeys = Object.keys(input5.property_updates_map);
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: input5.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(input5.property_updates_map, afterState);
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, input5) {
2414
+ async function adminRebalance(session2, input2) {
2361
2415
  const beforeState = await readShardState(session2);
2362
2416
  const options = {};
2363
- if (input5.rebalance_sharded_data !== void 0) {
2364
- options.rebalance_sharded_data = String(input5.rebalance_sharded_data);
2417
+ if (input2.rebalance_sharded_data !== void 0) {
2418
+ options.rebalance_sharded_data = String(input2.rebalance_sharded_data);
2365
2419
  }
2366
- if (input5.rebalance_unsharded_data !== void 0) {
2367
- options.rebalance_unsharded_data = String(input5.rebalance_unsharded_data);
2420
+ if (input2.rebalance_unsharded_data !== void 0) {
2421
+ options.rebalance_unsharded_data = String(input2.rebalance_unsharded_data);
2368
2422
  }
2369
- if (input5.table_includes !== void 0) {
2370
- options.table_includes = input5.table_includes;
2423
+ if (input2.table_includes !== void 0) {
2424
+ options.table_includes = input2.table_includes;
2371
2425
  }
2372
- if (input5.table_excludes !== void 0) {
2373
- options.table_excludes = input5.table_excludes;
2426
+ if (input2.table_excludes !== void 0) {
2427
+ options.table_excludes = input2.table_excludes;
2374
2428
  }
2375
- if (input5.aggressiveness !== void 0) {
2376
- options.aggressiveness = String(input5.aggressiveness);
2429
+ if (input2.aggressiveness !== void 0) {
2430
+ options.aggressiveness = String(input2.aggressiveness);
2377
2431
  }
2378
- if (input5.compact_after_rebalance !== void 0) {
2379
- options.compact_after_rebalance = String(input5.compact_after_rebalance);
2432
+ if (input2.compact_after_rebalance !== void 0) {
2433
+ options.compact_after_rebalance = String(input2.compact_after_rebalance);
2380
2434
  }
2381
- if (input5.compact_only !== void 0) {
2382
- options.compact_only = String(input5.compact_only);
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, input5) {
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: input5.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 import_prompts2 = require("@inquirer/prompts");
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 import_prompts = require("@inquirer/prompts");
2532
- var import_picocolors2 = __toESM(require("picocolors"));
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
- ` ${import_picocolors2.default.bold(import_picocolors2.default.yellow(header))}`,
2590
+ ` ${import_picocolors3.default.bold(import_picocolors3.default.yellow(header))}`,
2539
2591
  "",
2540
- ` ${import_picocolors2.default.dim("Summary:")} ${summary}`,
2592
+ ` ${import_picocolors3.default.dim("Summary:")} ${summary}`,
2541
2593
  "",
2542
- ` ${import_picocolors2.default.bold(`${items.length} proposed column change(s):`)}`,
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(` ${import_picocolors2.default.bold(`${i + 1}.`)} ${item.label}`, ` ${import_picocolors2.default.dim(item.description)}`);
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 (0, import_prompts.checkbox)({
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 = import_picocolors3.default.dim("\u2500".repeat(60));
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
- ` ${import_picocolors3.default.bold(import_picocolors3.default.yellow("Generated SQL:"))}`,
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 (0, import_prompts2.input)({ message: "Execute? (y/n):" });
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(input5) {
2744
- return Object.fromEntries(Object.entries(input5).map(([k, v]) => [k, redactNamedField(k, v)]));
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, input5) {
2782
- const statusLabel = result.ok ? import_picocolors4.default.bold(import_picocolors4.default.green("EXECUTED")) : import_picocolors4.default.bold(import_picocolors4.default.red("FAILED"));
2783
- const redacted = redactAuditInput(input5);
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
- ` ${import_picocolors4.default.dim("MUTATION")} ${statusLabel} ${displayName}
2788
- ${import_picocolors4.default.dim(inputSummary)}
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 input5 = safeCount(usage.inputTokens);
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 (input5 * price.inputPerMTok + output * price.outputPerMTok + cacheRead * price.cacheReadPerMTok + cacheCreation * price.cacheCreationPerMTok) / 1e6;
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/known-files.ts
3827
- var KNOWN_BUNDLE_FILES = {
3828
- // Host resources
3829
- "cpu.txt": "CPU topology, NUMA, and interrupts (lscpu, numactl, /proc/cpuinfo, /proc/interrupts)",
3830
- "mem.txt": "Memory usage, /proc/meminfo, and transparent-hugepage setting (free -m -t)",
3831
- "disk.txt": "Filesystems, mounts, block devices, and disk stats (df, mount, lsblk, fdisk, /etc/fstab, /proc/diskstats)",
3832
- "gpu.txt": "NVIDIA GPU inventory and state (nvidia-smi -L/-q, modinfo nvidia)",
3833
- "net.txt": "Network interfaces, sockets, and DNS (hostname, ifconfig, netstat, /etc/resolv.conf)",
3834
- // Processes
3835
- "ps.txt": "Full process list (ps -auxww, ps -ejHlfww)",
3836
- "gpudb-exe.txt": "Running gpudb processes (ps auxfwww | grep gpudb)",
3837
- // Hardware / firmware
3838
- "dmidecode.txt": "BIOS / DMI hardware inventory (dmidecode)",
3839
- "lshw.txt": "Hardware listing (lshw -short -numeric)",
3840
- "pci.txt": "PCI devices and I/O resources (lspci, /proc/ioports, /proc/iomem)",
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 describeBundleFile(entry) {
3883
- return KNOWN_BUNDLE_FILES[basename(entry.relPath)] ?? KIND_DESCRIPTIONS[entry.kind] ?? "";
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/tools/bundle/list-files.ts
3887
- var BundleListFilesSchema = import_zod18.z.object({
3888
- kind: import_zod18.z.string().optional()
3889
- });
3890
- async function bundleListFiles(source, args = {}) {
3891
- const all = source.listFiles();
3892
- const filtered = args.kind ? all.filter((e) => e.kind === args.kind) : all;
3893
- const { totalFiles, totalBytes, byKind, ranks, services } = source.inventory();
3894
- const version = await source.detectVersion();
3895
- const errors = await source.collectionErrors();
3896
- const files = filtered.map((e) => ({
3897
- file: e.relPath,
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
- // src/tools/bundle/log-timeline.ts
3920
- var import_zod19 = require("zod");
3921
- var BundleLogTimelineSchema = import_zod19.z.object({
3922
- min_severity: import_zod19.z.enum(["INFO", "WARN", "UERR", "ERROR", "FATAL"]).optional(),
3923
- granularity: import_zod19.z.enum(["day", "hour", "minute"]).optional(),
3924
- rank: import_zod19.z.string().describe('Numeric rank only, e.g. "r0"/"r1". For the host manager use host_manager.').optional(),
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/tools/bundle/search-logs.ts
3960
- var import_zod20 = require("zod");
3961
- var BundleSearchLogsSchema = import_zod20.z.object({
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/sysinfo-block.ts
4071
- var SEPARATOR_RE = /^-{3,}$/;
4072
- var EXEC_CMD_RE = /^EXEC_CMD:\s?(.*)$/;
4073
- var EXEC_END_RE = /^EXEC_END with exit code (\d+)\s*:?\s*(.*)$/;
4074
- var SHOWING_RE = /^### Showing whole log file\s*:/;
4075
- function trimBlankEdges(lines) {
4076
- let start = 0;
4077
- let end = lines.length;
4078
- while (start < end && lines[start].trim() === "") start++;
4079
- while (end > start && lines[end - 1].trim() === "") end--;
4080
- return lines.slice(start, end).join("\n");
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
- closeBlock();
4122
- return { ...header !== void 0 ? { header } : {}, blocks };
4123
- }
4124
-
4125
- // src/bundle/parse-ini.ts
4126
- var SECTION_RE = /^\[(.+)\]$/;
4127
- function parseIni(content) {
4128
- const entries = [];
4129
- let section = "";
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 entries;
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 match = PREFIX_RE.exec(line);
4016
+ const effective = unwrapLokiJsonl(line) ?? line;
4017
+ const match = PREFIX_RE.exec(effective);
4179
4018
  if (!match) {
4180
- return { message: line, raw: line };
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 import_promises4 = require("fs/promises");
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 basename2(relPath) {
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(/(\.log)+$/, "").replace(/^core-gpudb-/, "").replace(/^gpudb-/, "").replace(/-node\w+$/, "");
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 = basename2(relPath);
4292
+ const base = basename(relPath);
4359
4293
  const dir = dirOf(relPath);
4360
4294
  const host = inferHost(relPath);
4361
- if (base.endsWith(".conf")) {
4362
- return { kind: "config", ...host ? { host } : {} };
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
- if (base === "logfiles.txt") {
4365
- return { kind: "manifest", ...host ? { host } : {} };
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
- if (base === "errors.txt" || base.endsWith("erros.txt")) {
4368
- return { kind: "collection-errors", ...host ? { host } : {} };
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 (base === "gpudb.txt") {
4371
- return { kind: "version-info", ...host ? { host } : {} };
4325
+ if (/\bps\b|\/proc\/|environ|grep .*gpudb/.test(cmd)) {
4326
+ return { kind: "process-info", detail: "process snapshot command" };
4372
4327
  }
4373
- const exeId = EXE_ID_RE.exec(base);
4374
- if (exeId) {
4375
- return { kind: "process-info", ...rankOrService(exeId[1]), ...host ? { host } : {} };
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
- if (base.endsWith(".log")) {
4378
- const rolling = ROLLING_ID_RE.exec(base);
4379
- if (rolling) {
4380
- return { kind: "core-log", ...rankOrService(rolling[1]), ...host ? { host } : {} };
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
- if (dir === "logs") {
4383
- return { kind: "loki-tail", component: componentName(base), ...host ? { host } : {} };
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
- if (base.endsWith(".txt")) {
4388
- return { kind: "os-diag", ...host ? { host } : {} };
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 { kind: "unknown", ...host ? { host } : {} };
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, import_promises4.readdir)(rootDir, { recursive: true });
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, import_promises4.lstat)(absPath);
4432
+ const s = await (0, import_promises5.lstat)(absPath);
4407
4433
  if (s.isSymbolicLink() || !s.isFile()) return null;
4408
- const c = classifyFile(relPath);
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((e) => e.kind === "component-log" && e.component === opts.component);
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
- return index.filter((e) => e.kind === "core-log" && e.service === "host-manager");
4435
- }
4436
- let core = index.filter(
4437
- (e) => e.kind === "core-log" && (opts.rank === void 0 || e.rank === opts.rank)
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
- if (core.length === 0) {
4440
- core = index.filter((e) => e.kind === "loki-tail");
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.rank) rankSet.add(e.rank);
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
- services: [...serviceSet].sort()
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, import_promises5.readFile)(versionFile.absPath, "utf-8"));
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, import_promises5.readFile)(configFile.absPath, "utf-8"));
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, import_promises5.readFile)(configFile.absPath, "utf-8"));
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, import_promises5.readFile)(abs, "utf-8"));
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, import_promises5.readFile)(file.absPath, "utf-8");
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, import_promises6.stat)(bundlePath);
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 import_prompts3 = require("@inquirer/prompts");
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 input5 = term.trim() === "" ? "." : term;
4995
- const endsWithSep = input5.endsWith("/");
4996
- const baseDir = endsWithSep ? input5 : (0, import_node_path7.dirname)(input5) || ".";
4997
- const prefix = endsWithSep ? "" : (0, import_node_path7.basename)(input5);
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, import_promises7.readdir)(resolved, { withFileTypes: true });
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 (0, import_prompts3.search)({
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 import_picocolors5 = __toESM(require("picocolors"));
5368
+ var import_picocolors6 = __toESM(require("picocolors"));
5041
5369
  var IMPACT_FALLBACK = "Impact unknown \u2014 review parameters carefully";
5042
- var DIVIDER2 = import_picocolors5.default.dim("\u2500".repeat(50));
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 = import_picocolors5.default.bold(import_picocolors5.default.yellow(" Mutation Approval Required"));
5049
- const action = `${formatLabel("Action")}${import_picocolors5.default.bold(formatToolName(toolName))}`;
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 ` ${import_picocolors5.default.dim(key)}: ${formatted}`;
5381
+ return ` ${import_picocolors6.default.dim(key)}: ${formatted}`;
5054
5382
  }).join("\n");
5055
5383
  const impactLine = `${formatLabel("Impact")}${impact ?? IMPACT_FALLBACK}`;
5056
- const prompt = import_picocolors5.default.dim(
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) => ` ${import_picocolors5.default.dim(entry.key)}: ${entry.current} ${import_picocolors5.default.yellow("->")} ${entry.proposed}`
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 (0, import_prompts4.input)({ message: "Proceed? (y/n/explain):" }, { signal: options.signal });
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 import_picocolors6 = __toESM(require("picocolors"));
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
- const headingMatch = HEADING_RE.exec(line);
5160
- if (headingMatch) {
5161
- return import_picocolors6.default.bold(headingMatch[2]);
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
- if (line.includes("**")) {
5164
- return line.replace(BOLD_RE, (_, text2) => import_picocolors6.default.bold(text2));
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) => visualWidth(row[col]))
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 = renderMarkdownLine(cell);
5208
- const pad = colWidths[col] - visualWidth(cell);
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 import_picocolors7 = __toESM(require("picocolors"));
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${import_picocolors7.default.dim(`${frame} ${label}...`)}`);
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 (0, import_prompts5.input)({ message: "Describe the issue to investigate:" });
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 (0, import_prompts5.input)({ message: "You:" });
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(import_picocolors8.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"));
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(` ${import_picocolors8.default.dim(row.key.padEnd(maxKeyLen))} ${row.value}
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(` ${import_picocolors8.default.red(`Error: ${statusResult.error}`)}
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(import_picocolors8.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"));
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(` ${import_picocolors8.default.dim("No recent alerts.")}
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(` ${import_picocolors8.default.dim(alert.timestamp)} ${alert.type} ${alert.params}
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(` ${import_picocolors8.default.dim(`Unavailable: ${alertsResult.error}`)}
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
- import_picocolors8.default.dim(`System prompt: ~${budget.tokens} tokens (${budget.chars} chars)
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
- import_picocolors8.default.yellow(
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 (0, import_prompts5.input)({
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 ? import_picocolors8.default.dim(`Budget guard: $${resolvedBudgetUsd.toFixed(2)} (raise with --max-budget)
5514
- `) : import_picocolors8.default.dim("Budget guard: subscription (Pro/Max) \u2014 turn-limited\n");
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
- import_picocolors8.default.dim("Tip: ask me to analyze a support bundle to add offline log/config analysis.\n")
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
- import_picocolors8.default.yellow(
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(import_picocolors8.default.yellow(`
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
- import_picocolors8.default.yellow(
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
- import_picocolors8.default.yellow(
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
- import_picocolors8.default.yellow(
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(import_picocolors8.default.yellow("\nRe-authentication requested by SDK...\n"));
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(import_picocolors8.default.red(`
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
- import_picocolors8.default.dim(
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 (0, import_prompts6.select)({
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 import_picocolors9 = __toESM(require("picocolors"));
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(import_picocolors9.default.dim("Browser opened for login. Waiting for authentication...\n"));
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
- ${import_picocolors9.default.bold(manualUrl)}
6215
+ ${import_picocolors10.default.bold(manualUrl)}
5838
6216
 
5839
6217
  `);
5840
- process.stderr.write(import_picocolors9.default.dim("Waiting for browser login to complete...\n"));
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
- import_picocolors9.default.yellow(`
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 import_promises8 = require("fs/promises");
6327
+ var import_promises9 = require("fs/promises");
5950
6328
  var import_path2 = require("path");
5951
- var import_prompts7 = require("@inquirer/prompts");
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 (0, import_prompts7.confirm)({
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, import_promises8.readFile)(filePath, "utf8");
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, import_promises8.writeFile)(filePath, content, "utf8");
6047
- console.error(import_picocolors10.default.dim("Saved to .env"));
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(import_picocolors10.default.yellow(`Could not save .env file: ${message}`));
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 import_picocolors13 = __toESM(require("picocolors"));
6056
- var import_prompts10 = require("@inquirer/prompts");
6432
+ var import_picocolors14 = __toESM(require("picocolors"));
6057
6433
 
6058
6434
  // src/session/collect.ts
6059
- var import_prompts8 = require("@inquirer/prompts");
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(import_picocolors11.default.dim(`Saved connection: ${envUrl} (${envUser})`));
6067
- const useSaved = await (0, import_prompts8.confirm)({
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 (0, import_prompts8.input)({ message: "Kinetica endpoint URL:" });
6075
- const user2 = await (0, import_prompts8.input)({ message: "Admin username:" });
6076
- const pass2 = await (0, import_prompts8.password)({ message: "Admin password:", mask: "*" });
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 (0, import_prompts8.input)({ message: "Kinetica endpoint URL:" }));
6081
- const user = envUser ?? (prompted.add("user"), await (0, import_prompts8.input)({ message: "Admin username:" }));
6082
- const pass = process.env.KINETICA_PASS ?? await (0, import_prompts8.password)({ message: "Admin password:", mask: "*" });
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 (0, import_prompts8.input)({ message: "Admin username:" });
6087
- const pass = await (0, import_prompts8.password)({ message: "Admin password:", mask: "*" });
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 import_picocolors12 = __toESM(require("picocolors"));
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(input5) {
6129
- return HTTP_PROTOCOL_RE.test(input5);
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" + import_picocolors12.default.red(
6154
- import_picocolors12.default.bold(
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
- ) + import_picocolors12.default.dim(
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 (0, import_prompts9.confirm)({
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(input5, options = {}) {
6175
- const trimmed = input5.trim();
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(import_picocolors12.default.dim("Detecting protocol..."));
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(import_picocolors13.default.red(resolved.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(import_picocolors13.default.green("Connected to Kinetica successfully."));
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(import_picocolors13.default.red(`Connection failed (attempt ${attempt}/${MAX_RETRIES}): ${msg}`));
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 (0, import_prompts10.confirm)({
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(import_picocolors13.default.red("Authentication failed. Exiting."));
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(import_picocolors13.default.yellow("DB engine unreachable. Probing host manager on port 9300..."));
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
- import_picocolors13.default.yellow(
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(import_picocolors13.default.red("Host manager also unreachable. Exiting."));
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
- import_picocolors14.default.red(`Error: unknown --model value "${modelValue}". Valid models: ${valid}
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
- import_picocolors14.default.red(
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
- import_picocolors14.default.red(
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(import_picocolors14.default.dim(`Model: ${effectiveModel}
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(import_picocolors14.default.dim(`Authenticated via OAuth${acctInfo}
6850
+ process.stderr.write(import_picocolors15.default.dim(`Authenticated via OAuth${acctInfo}
6477
6851
  `));
6478
6852
  } else {
6479
- process.stderr.write(import_picocolors14.default.dim("Authenticated via API key\n"));
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(import_picocolors14.default.red(`Error: ${result.error}
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.missingExpected.length > 0) {
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
- import_picocolors14.default.yellow(
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 ? import_picocolors14.default.dim("Live connection available \u2014 bundle + live verification enabled.\n") : import_picocolors14.default.dim("No reachable live connection \u2014 offline bundle analysis only.\n")
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
- import_picocolors14.default.yellow(
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(import_picocolors14.default.red(`Error: ${message}
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");