@kinetica/admin-agent 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- package/dist/admin-agent.js +1842 -1807
- package/package.json +1 -1
package/dist/admin-agent.js
CHANGED
|
@@ -116,1129 +116,596 @@ ${import_picocolors.default.dim(`model: ${model}`)}` : subtitle;
|
|
|
116
116
|
return subtitle;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
// src/
|
|
120
|
-
var
|
|
119
|
+
// src/cli/select-model.ts
|
|
120
|
+
var import_prompts5 = require("@inquirer/prompts");
|
|
121
121
|
|
|
122
|
-
// src/
|
|
123
|
-
var
|
|
122
|
+
// src/agent/run-agent.ts
|
|
123
|
+
var import_claude_agent_sdk4 = require("@anthropic-ai/claude-agent-sdk");
|
|
124
|
+
var import_prompts4 = require("@inquirer/prompts");
|
|
125
|
+
var import_picocolors8 = __toESM(require("picocolors"));
|
|
124
126
|
|
|
125
|
-
// src/
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
const platform = process.platform;
|
|
130
|
-
const { command, args } = platform === "darwin" ? { command: "open", args: [url] } : platform === "win32" ? { command: "cmd", args: ["/c", "start", "", url] } : { command: "xdg-open", args: [url] };
|
|
131
|
-
const child = (0, import_child_process.spawn)(command, args, { detached: true, stdio: "ignore" });
|
|
132
|
-
child.unref();
|
|
133
|
-
return true;
|
|
134
|
-
} catch {
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
127
|
+
// src/agent/diagnostic-sql.ts
|
|
128
|
+
function has(columns, name) {
|
|
129
|
+
return columns.includes(name);
|
|
137
130
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
131
|
+
var FALLBACK_QUERY_HISTORY_SQL = `-- Slow queries in the last hour
|
|
132
|
+
SELECT query_id, user_name, query_text, start_time, stop_time,
|
|
133
|
+
TIMESTAMPDIFF(SECOND, start_time, stop_time) AS elapsed_sec
|
|
134
|
+
FROM ki_catalog.ki_query_history
|
|
135
|
+
WHERE start_time > NOW() - INTERVAL '1' HOUR
|
|
136
|
+
ORDER BY elapsed_sec DESC
|
|
137
|
+
LIMIT 20;`;
|
|
138
|
+
var FALLBACK_ACTIVE_QUERIES_SQL = `-- Currently active queries
|
|
139
|
+
SELECT query_id, user_name, query_text, start_time, execution_status
|
|
140
|
+
FROM ki_catalog.ki_query_active_all
|
|
141
|
+
ORDER BY start_time ASC;`;
|
|
142
|
+
var FALLBACK_TIERED_OBJECTS_SQL = `-- Objects in disk tier (potential memory pressure)
|
|
143
|
+
-- NOTE: id is a string (e.g. @schema@oid[col][0]), NOT a numeric OID. Filter by table: WHERE id LIKE '%table_name%'
|
|
144
|
+
-- For per-table tier placement, prefer kinetica_resource_objects with table_names filter.
|
|
145
|
+
SELECT id, tier, size, source_rank, owner_resource_group
|
|
146
|
+
FROM ki_catalog.ki_tiered_objects
|
|
147
|
+
WHERE tier != 'VRAM'
|
|
148
|
+
ORDER BY size DESC
|
|
149
|
+
LIMIT 20;`;
|
|
150
|
+
var FALLBACK_OBJ_STAT_SQL = `-- Table sizes and row counts
|
|
151
|
+
SELECT object_name, total_bytes, row_count
|
|
152
|
+
FROM ki_catalog.ki_obj_stat
|
|
153
|
+
ORDER BY total_bytes DESC
|
|
154
|
+
LIMIT 30;`;
|
|
155
|
+
var FALLBACK_COLUMNS_SQL = `-- Structural column metadata (use kinetica_show_table for Kinetica-native types)
|
|
156
|
+
SELECT c.table_name, c.column_name, c.column_position
|
|
157
|
+
FROM ki_catalog.ki_columns c
|
|
158
|
+
WHERE c.table_name = '<TABLE_NAME>'
|
|
159
|
+
ORDER BY c.column_position;`;
|
|
160
|
+
var FALLBACK_DATATYPES_SQL = `-- Resolve column_type_oid to human-readable type name
|
|
161
|
+
SELECT oid, name, sql_typename
|
|
162
|
+
FROM ki_catalog.ki_datatypes
|
|
163
|
+
ORDER BY oid;`;
|
|
164
|
+
var FALLBACK_QUERY_SPAN_METRICS_SQL = `-- Query span metrics for a specific query
|
|
165
|
+
SELECT query_id, span_id, parent_span_id, operator, sql_step,
|
|
166
|
+
metric_data, start_time, stop_time, source_rank
|
|
167
|
+
FROM ki_catalog.ki_query_span_metrics_all
|
|
168
|
+
WHERE query_id = '<QUERY_ID>'
|
|
169
|
+
ORDER BY start_time;`;
|
|
170
|
+
var FALLBACK_QUERY_WORKERS_SQL = `-- Active query workers (non-idle)
|
|
171
|
+
SELECT job_id, worker_id, type, status, elapsed_time_ms, source_rank
|
|
172
|
+
FROM ki_catalog.ki_query_workers
|
|
173
|
+
WHERE status != 'IDLE'
|
|
174
|
+
ORDER BY elapsed_time_ms DESC;`;
|
|
175
|
+
var FALLBACK_OBJECTS_SQL = `-- Object registry and metadata
|
|
176
|
+
SELECT oid, object_name, schema_name, type_id, persistence, obj_kind,
|
|
177
|
+
creation_time, last_read_time, read_count, write_count
|
|
178
|
+
FROM ki_catalog.ki_objects
|
|
179
|
+
ORDER BY last_read_time DESC
|
|
180
|
+
LIMIT 30;`;
|
|
181
|
+
var FALLBACK_PARTITIONS_SQL = `-- Partition sizes and tier distribution
|
|
182
|
+
SELECT oid, object_name, schema_name, rank_num, partition_type,
|
|
183
|
+
partition_id, num_rows, actual_bytes, tier
|
|
184
|
+
FROM ki_catalog.ki_partitions
|
|
185
|
+
ORDER BY actual_bytes DESC
|
|
186
|
+
LIMIT 30;`;
|
|
187
|
+
var FALLBACK_INDEXES_SQL = `-- Index definitions
|
|
188
|
+
SELECT oid, object_name, schema_name, index_type, index_columns
|
|
189
|
+
FROM ki_catalog.ki_indexes
|
|
190
|
+
ORDER BY object_name;`;
|
|
191
|
+
var FALLBACK_PERIODIC_OBJECTS_SQL = `-- Periodic refresh schedules
|
|
192
|
+
SELECT oid, object_name, schema_name, last_refresh_time,
|
|
193
|
+
next_refresh_time, additional_info
|
|
194
|
+
FROM ki_catalog.ki_periodic_objects
|
|
195
|
+
ORDER BY next_refresh_time;`;
|
|
196
|
+
var FALLBACK_USERS_AND_ROLES_SQL = `-- Users and roles
|
|
197
|
+
SELECT oid, name, can_login, is_superuser, resource_group
|
|
198
|
+
FROM ki_catalog.ki_users_and_roles
|
|
199
|
+
ORDER BY name;`;
|
|
200
|
+
var FALLBACK_OBJECT_PERMISSIONS_SQL = `-- Object permissions
|
|
201
|
+
SELECT role_name, permission_type, object_type, object_name, with_grant_option
|
|
202
|
+
FROM ki_catalog.ki_object_permissions
|
|
203
|
+
ORDER BY object_name, role_name;`;
|
|
204
|
+
var FALLBACK_DEPEND_SQL = `-- Object dependency graph
|
|
205
|
+
SELECT src_obj_oid, src_obj_kind, dep_obj_oid, dep_obj_kind, mv_oid, dep_kind
|
|
206
|
+
FROM ki_catalog.ki_depend;`;
|
|
207
|
+
var FALLBACK_LOAD_HISTORY_SQL = `-- Recent data load history
|
|
208
|
+
SELECT table_oid, datasource_oid, user_name, load_kind,
|
|
209
|
+
start_time, end_time, rows_inserted, event_message
|
|
210
|
+
FROM ki_catalog.ki_load_history
|
|
211
|
+
WHERE start_time > NOW() - INTERVAL '1' HOUR
|
|
212
|
+
ORDER BY start_time DESC
|
|
213
|
+
LIMIT 20;`;
|
|
214
|
+
var FALLBACK_BACKUP_HISTORY_SQL = `-- Backup history
|
|
215
|
+
SELECT backup_name, operation, status, start_time, end_time,
|
|
216
|
+
num_files, num_bytes, num_records
|
|
217
|
+
FROM ki_catalog.ki_backup_history
|
|
218
|
+
ORDER BY start_time DESC
|
|
219
|
+
LIMIT 20;`;
|
|
220
|
+
var FALLBACK_KAFKA_LAG_INFO_SQL = `-- Kafka consumer lag
|
|
221
|
+
SELECT datasource_oid, table_oid, schema_name, table_name,
|
|
222
|
+
partition_id, highest_offset, last_committed_offset
|
|
223
|
+
FROM ki_catalog.ki_kafka_lag_info
|
|
224
|
+
ORDER BY datasource_oid, partition_id;`;
|
|
225
|
+
function buildQueryHistorySql(columns) {
|
|
226
|
+
const select2 = columns.join(", ");
|
|
227
|
+
const elapsed = has(columns, "start_time") && has(columns, "stop_time") ? ",\n TIMESTAMPDIFF(SECOND, start_time, stop_time) AS elapsed_sec" : "";
|
|
228
|
+
const where = has(columns, "start_time") ? "\nWHERE start_time > NOW() - INTERVAL '1' HOUR" : "";
|
|
229
|
+
const orderBy = has(columns, "start_time") && has(columns, "stop_time") ? (
|
|
230
|
+
// Kinetica does not support timestamp arithmetic in ORDER BY; use the elapsed_sec alias instead
|
|
231
|
+
"\nORDER BY elapsed_sec DESC"
|
|
232
|
+
) : "";
|
|
233
|
+
return `-- Slow queries in the last hour
|
|
234
|
+
SELECT ${select2}${elapsed}
|
|
235
|
+
FROM ki_catalog.ki_query_history${where}${orderBy}
|
|
236
|
+
LIMIT 20;`;
|
|
169
237
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
authQuery.accountInfo(),
|
|
177
|
-
new Promise(
|
|
178
|
-
(_, reject) => setTimeout(() => reject(new Error("Probe timed out")), PROBE_TIMEOUT_MS)
|
|
179
|
-
)
|
|
180
|
-
]);
|
|
181
|
-
if (info.email || info.apiKeySource) {
|
|
182
|
-
return info;
|
|
183
|
-
}
|
|
184
|
-
return null;
|
|
185
|
-
} catch {
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
238
|
+
function buildActiveQueriesSql(columns) {
|
|
239
|
+
const select2 = columns.join(", ");
|
|
240
|
+
const orderBy = has(columns, "start_time") ? "\nORDER BY start_time ASC" : "";
|
|
241
|
+
return `-- Currently active queries
|
|
242
|
+
SELECT ${select2}
|
|
243
|
+
FROM ki_catalog.ki_query_active_all${orderBy};`;
|
|
188
244
|
}
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
const env = options.forceLogin ? (() => {
|
|
200
|
-
const { ANTHROPIC_API_KEY: _stripped, ...rest } = process.env;
|
|
201
|
-
return { ...rest, CLAUDE_AGENT_SDK_CLIENT_APP: "admin-agent" };
|
|
202
|
-
})() : { ...process.env, CLAUDE_AGENT_SDK_CLIENT_APP: "admin-agent" };
|
|
203
|
-
const abortController = new AbortController();
|
|
204
|
-
async function* hangingPrompt() {
|
|
205
|
-
await new Promise(() => {
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
const authQuery = (0, import_claude_agent_sdk.query)({
|
|
209
|
-
prompt: hangingPrompt(),
|
|
210
|
-
options: {
|
|
211
|
-
persistSession: false,
|
|
212
|
-
abortController,
|
|
213
|
-
env,
|
|
214
|
-
...options.loginMethod ? { forceLoginMethod: options.loginMethod } : {},
|
|
215
|
-
...options.loginOrgUUID ? { forceLoginOrgUUID: options.loginOrgUUID } : {}
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
try {
|
|
219
|
-
if (!options.forceLogin) {
|
|
220
|
-
const cached = await probeCachedCredentials(authQuery);
|
|
221
|
-
if (cached) {
|
|
222
|
-
return { method: "oauth", email: cached.email };
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
return await resolveAuthentication(authQuery, {
|
|
226
|
-
forceLogin: options.forceLogin,
|
|
227
|
-
loginWithClaudeAi: (options.loginMethod ?? "claudeai") === "claudeai",
|
|
228
|
-
hasApiKey
|
|
229
|
-
});
|
|
230
|
-
} finally {
|
|
231
|
-
abortController.abort();
|
|
232
|
-
await authQuery.return().catch(() => {
|
|
233
|
-
});
|
|
234
|
-
}
|
|
245
|
+
function buildTieredObjectsSql(columns) {
|
|
246
|
+
const select2 = columns.join(", ");
|
|
247
|
+
const where = has(columns, "tier") ? "\nWHERE tier != 'VRAM'" : "";
|
|
248
|
+
const orderBy = has(columns, "size") ? "\nORDER BY size DESC" : "";
|
|
249
|
+
const idNote = has(columns, "id") ? "\n-- NOTE: id is a string (e.g. @schema@oid[col][0]), NOT a numeric OID. Filter by table: WHERE id LIKE '%table_name%'" : "";
|
|
250
|
+
return `-- Objects in disk tier (potential memory pressure)${idNote}
|
|
251
|
+
SELECT ${select2}
|
|
252
|
+
FROM ki_catalog.ki_tiered_objects${where}${orderBy}
|
|
253
|
+
LIMIT 20;`;
|
|
235
254
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
function resolveSdkCliPath() {
|
|
244
|
-
const require_ = (0, import_node_module.createRequire)(__filename);
|
|
245
|
-
return import_node_path.default.join(import_node_path.default.dirname(require_.resolve("@anthropic-ai/claude-agent-sdk")), "cli.js");
|
|
255
|
+
function buildObjStatSql(columns) {
|
|
256
|
+
const select2 = columns.join(", ");
|
|
257
|
+
const orderBy = has(columns, "total_bytes") ? "\nORDER BY total_bytes DESC" : "";
|
|
258
|
+
return `-- Table sizes and row counts
|
|
259
|
+
SELECT ${select2}
|
|
260
|
+
FROM ki_catalog.ki_obj_stat${orderBy}
|
|
261
|
+
LIMIT 30;`;
|
|
246
262
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
var import_picocolors3 = __toESM(require("picocolors"));
|
|
269
|
-
function parseEnvContent(content) {
|
|
270
|
-
const entries = [];
|
|
271
|
-
for (const line of content.split("\n")) {
|
|
272
|
-
const trimmed = line.trim();
|
|
273
|
-
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
274
|
-
const match = /^([A-Za-z_]\w*)=(.*)/.exec(trimmed);
|
|
275
|
-
if (!match) continue;
|
|
276
|
-
const key = match[1];
|
|
277
|
-
let value = match[2];
|
|
278
|
-
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
279
|
-
value = value.slice(1, -1).replace(/\\(["\\])/g, "$1");
|
|
280
|
-
} else if (value.startsWith("'") && value.endsWith("'") && value.length >= 2) {
|
|
281
|
-
value = value.slice(1, -1);
|
|
282
|
-
}
|
|
283
|
-
entries.push([key, value]);
|
|
284
|
-
}
|
|
285
|
-
return new Map(entries);
|
|
263
|
+
var STRUCTURAL_COLUMNS = /* @__PURE__ */ new Set([
|
|
264
|
+
"table_name",
|
|
265
|
+
"column_name",
|
|
266
|
+
"column_position",
|
|
267
|
+
"is_nullable",
|
|
268
|
+
"is_shard_key",
|
|
269
|
+
"is_primary_key",
|
|
270
|
+
"is_dict_encoded",
|
|
271
|
+
"default_value",
|
|
272
|
+
"bytes_on_disk_uncompressed",
|
|
273
|
+
"bytes_on_disk_compressed"
|
|
274
|
+
]);
|
|
275
|
+
function buildColumnsSql(columns) {
|
|
276
|
+
const filtered = columns.filter((c) => STRUCTURAL_COLUMNS.has(c));
|
|
277
|
+
const selectCols = filtered.length > 0 ? filtered : columns;
|
|
278
|
+
const select2 = selectCols.map((c) => `c.${c}`).join(", ");
|
|
279
|
+
const where = has(selectCols, "table_name") ? "\nWHERE c.table_name = '<TABLE_NAME>'" : "";
|
|
280
|
+
const orderBy = has(selectCols, "column_position") ? "\nORDER BY c.column_position" : has(selectCols, "column_name") ? "\nORDER BY c.column_name" : "";
|
|
281
|
+
return `-- Structural column metadata (use kinetica_show_table for Kinetica-native types)
|
|
282
|
+
SELECT ${select2}
|
|
283
|
+
FROM ki_catalog.ki_columns c${where}${orderBy};`;
|
|
286
284
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
if (!REQUIRES_QUOTING.test(value)) {
|
|
296
|
-
return value;
|
|
297
|
-
}
|
|
298
|
-
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
299
|
-
return `"${escaped}"`;
|
|
285
|
+
function buildDatatypesSql(columns) {
|
|
286
|
+
const select2 = columns.join(", ");
|
|
287
|
+
const orderBy = has(columns, "oid") ? "\nORDER BY oid" : "";
|
|
288
|
+
return `-- Resolve column_type_oid to human-readable type name
|
|
289
|
+
SELECT ${select2}
|
|
290
|
+
FROM ki_catalog.ki_datatypes${orderBy};`;
|
|
300
291
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
KINETICA_PASS=
|
|
309
|
-
`;
|
|
310
|
-
function buildEnvContent(url, user, existingContent) {
|
|
311
|
-
const safeUrl = escapeEnvValue(url);
|
|
312
|
-
const safeUser = escapeEnvValue(user);
|
|
313
|
-
if (!existingContent?.trim()) {
|
|
314
|
-
return ENV_TEMPLATE.replace("{url}", safeUrl).replace("{user}", safeUser);
|
|
315
|
-
}
|
|
316
|
-
const lines = existingContent.split("\n");
|
|
317
|
-
let urlReplaced = false;
|
|
318
|
-
let userReplaced = false;
|
|
319
|
-
const updated = lines.map((line) => {
|
|
320
|
-
if (/^KINETICA_URL=/.exec(line)) {
|
|
321
|
-
urlReplaced = true;
|
|
322
|
-
return `KINETICA_URL=${safeUrl}`;
|
|
323
|
-
}
|
|
324
|
-
if (/^KINETICA_USER=/.exec(line)) {
|
|
325
|
-
userReplaced = true;
|
|
326
|
-
return `KINETICA_USER=${safeUser}`;
|
|
327
|
-
}
|
|
328
|
-
return line;
|
|
329
|
-
});
|
|
330
|
-
if (!urlReplaced) updated.push(`KINETICA_URL=${safeUrl}`);
|
|
331
|
-
if (!userReplaced) updated.push(`KINETICA_USER=${safeUser}`);
|
|
332
|
-
return updated.join("\n");
|
|
292
|
+
function buildQuerySpanMetricsSql(columns) {
|
|
293
|
+
const select2 = columns.join(", ");
|
|
294
|
+
const where = has(columns, "query_id") ? "\nWHERE query_id = '<QUERY_ID>'" : "";
|
|
295
|
+
const orderBy = has(columns, "start_time") ? "\nORDER BY start_time" : "";
|
|
296
|
+
return `-- Query span metrics for a specific query
|
|
297
|
+
SELECT ${select2}
|
|
298
|
+
FROM ki_catalog.ki_query_span_metrics_all${where}${orderBy};`;
|
|
333
299
|
}
|
|
334
|
-
function
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
env[key] = value;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
} catch {
|
|
345
|
-
}
|
|
300
|
+
function buildQueryWorkersSql(columns) {
|
|
301
|
+
const select2 = columns.join(", ");
|
|
302
|
+
const where = has(columns, "status") ? "\nWHERE status != 'IDLE'" : "";
|
|
303
|
+
const orderBy = has(columns, "elapsed_time_ms") ? "\nORDER BY elapsed_time_ms DESC" : "";
|
|
304
|
+
return `-- Active query workers (non-idle)
|
|
305
|
+
SELECT ${select2}
|
|
306
|
+
FROM ki_catalog.ki_query_workers${where}${orderBy};`;
|
|
346
307
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
if (!shouldSave) return;
|
|
355
|
-
const filePath = (0, import_path2.join)(dir ?? process.cwd(), ".env");
|
|
356
|
-
let existing;
|
|
357
|
-
try {
|
|
358
|
-
existing = await (0, import_promises.readFile)(filePath, "utf8");
|
|
359
|
-
} catch {
|
|
360
|
-
}
|
|
361
|
-
const content = buildEnvContent(url, user, existing);
|
|
362
|
-
await (0, import_promises.writeFile)(filePath, content, "utf8");
|
|
363
|
-
console.error(import_picocolors3.default.dim("Saved to .env"));
|
|
364
|
-
} catch (err) {
|
|
365
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
366
|
-
console.error(import_picocolors3.default.yellow(`Could not save .env file: ${message}`));
|
|
367
|
-
}
|
|
308
|
+
function buildObjectsSql(columns) {
|
|
309
|
+
const select2 = columns.join(", ");
|
|
310
|
+
const orderBy = has(columns, "last_read_time") ? "\nORDER BY last_read_time DESC" : has(columns, "creation_time") ? "\nORDER BY creation_time DESC" : "";
|
|
311
|
+
return `-- Object registry and metadata
|
|
312
|
+
SELECT ${select2}
|
|
313
|
+
FROM ki_catalog.ki_objects${orderBy}
|
|
314
|
+
LIMIT 30;`;
|
|
368
315
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
var import_picocolors4 = __toESM(require("picocolors"));
|
|
377
|
-
async function collectCredentials() {
|
|
378
|
-
const prompted = /* @__PURE__ */ new Set();
|
|
379
|
-
const envUrl = process.env.KINETICA_URL;
|
|
380
|
-
const envUser = process.env.KINETICA_USER;
|
|
381
|
-
if (envUrl && envUser && process.stdin.isTTY) {
|
|
382
|
-
console.error(import_picocolors4.default.dim(`Saved connection: ${envUrl} (${envUser})`));
|
|
383
|
-
const useSaved = await (0, import_prompts2.confirm)({
|
|
384
|
-
message: "Use saved connection?",
|
|
385
|
-
default: true
|
|
386
|
-
});
|
|
387
|
-
if (!useSaved) {
|
|
388
|
-
prompted.add("url");
|
|
389
|
-
prompted.add("user");
|
|
390
|
-
const url2 = await (0, import_prompts2.input)({ message: "Kinetica endpoint URL:" });
|
|
391
|
-
const user2 = await (0, import_prompts2.input)({ message: "Admin username:" });
|
|
392
|
-
const pass2 = await (0, import_prompts2.password)({ message: "Admin password:", mask: "*" });
|
|
393
|
-
return { credentials: { url: url2, user: user2, pass: pass2 }, prompted };
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
const url = envUrl ?? (prompted.add("url"), await (0, import_prompts2.input)({ message: "Kinetica endpoint URL:" }));
|
|
397
|
-
const user = envUser ?? (prompted.add("user"), await (0, import_prompts2.input)({ message: "Admin username:" }));
|
|
398
|
-
const pass = process.env.KINETICA_PASS ?? await (0, import_prompts2.password)({ message: "Admin password:", mask: "*" });
|
|
399
|
-
return { credentials: { url, user, pass }, prompted };
|
|
316
|
+
function buildPartitionsSql(columns) {
|
|
317
|
+
const select2 = columns.join(", ");
|
|
318
|
+
const orderBy = has(columns, "actual_bytes") ? "\nORDER BY actual_bytes DESC" : "";
|
|
319
|
+
return `-- Partition sizes and tier distribution
|
|
320
|
+
SELECT ${select2}
|
|
321
|
+
FROM ki_catalog.ki_partitions${orderBy}
|
|
322
|
+
LIMIT 30;`;
|
|
400
323
|
}
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
const
|
|
404
|
-
return
|
|
324
|
+
function buildIndexesSql(columns) {
|
|
325
|
+
const select2 = columns.join(", ");
|
|
326
|
+
const orderBy = has(columns, "object_name") ? "\nORDER BY object_name" : "";
|
|
327
|
+
return `-- Index definitions
|
|
328
|
+
SELECT ${select2}
|
|
329
|
+
FROM ki_catalog.ki_indexes${orderBy};`;
|
|
405
330
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
return parsed.origin;
|
|
331
|
+
function buildPeriodicObjectsSql(columns) {
|
|
332
|
+
const select2 = columns.join(", ");
|
|
333
|
+
const orderBy = has(columns, "next_refresh_time") ? "\nORDER BY next_refresh_time" : "";
|
|
334
|
+
return `-- Periodic refresh schedules
|
|
335
|
+
SELECT ${select2}
|
|
336
|
+
FROM ki_catalog.ki_periodic_objects${orderBy};`;
|
|
413
337
|
}
|
|
414
|
-
function
|
|
415
|
-
const
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
return fetch(fullUrl, {
|
|
421
|
-
method: "POST",
|
|
422
|
-
headers: {
|
|
423
|
-
Authorization: authHeader,
|
|
424
|
-
"Content-Type": "application/json"
|
|
425
|
-
},
|
|
426
|
-
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
427
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
428
|
-
});
|
|
429
|
-
};
|
|
430
|
-
return {
|
|
431
|
-
baseUrl: url,
|
|
432
|
-
makeRequest: (endpoint, body) => doFetch(`${url}${endpoint}`, body),
|
|
433
|
-
makeRequestToPort: (port, endpoint, body) => doFetch(`${replacePort(url, port)}${endpoint}`, body)
|
|
434
|
-
};
|
|
338
|
+
function buildUsersAndRolesSql(columns) {
|
|
339
|
+
const select2 = columns.join(", ");
|
|
340
|
+
const orderBy = has(columns, "name") ? "\nORDER BY name" : "";
|
|
341
|
+
return `-- Users and roles
|
|
342
|
+
SELECT ${select2}
|
|
343
|
+
FROM ki_catalog.ki_users_and_roles${orderBy};`;
|
|
435
344
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
var ANY_PROTOCOL_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
443
|
-
function hasProtocol(input5) {
|
|
444
|
-
return HTTP_PROTOCOL_RE.test(input5);
|
|
345
|
+
function buildObjectPermissionsSql(columns) {
|
|
346
|
+
const select2 = columns.join(", ");
|
|
347
|
+
const orderBy = has(columns, "object_name") && has(columns, "role_name") ? "\nORDER BY object_name, role_name" : has(columns, "object_name") ? "\nORDER BY object_name" : "";
|
|
348
|
+
return `-- Object permissions
|
|
349
|
+
SELECT ${select2}
|
|
350
|
+
FROM ki_catalog.ki_object_permissions${orderBy};`;
|
|
445
351
|
}
|
|
446
|
-
function
|
|
447
|
-
|
|
352
|
+
function buildDependSql(columns) {
|
|
353
|
+
const select2 = columns.join(", ");
|
|
354
|
+
return `-- Object dependency graph
|
|
355
|
+
SELECT ${select2}
|
|
356
|
+
FROM ki_catalog.ki_depend;`;
|
|
448
357
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
return false;
|
|
458
|
-
}
|
|
358
|
+
function buildLoadHistorySql(columns) {
|
|
359
|
+
const select2 = columns.join(", ");
|
|
360
|
+
const where = has(columns, "start_time") ? "\nWHERE start_time > NOW() - INTERVAL '1' HOUR" : "";
|
|
361
|
+
const orderBy = has(columns, "start_time") ? "\nORDER BY start_time DESC" : "";
|
|
362
|
+
return `-- Recent data load history
|
|
363
|
+
SELECT ${select2}
|
|
364
|
+
FROM ki_catalog.ki_load_history${where}${orderBy}
|
|
365
|
+
LIMIT 20;`;
|
|
459
366
|
}
|
|
460
|
-
function
|
|
461
|
-
|
|
367
|
+
function buildBackupHistorySql(columns) {
|
|
368
|
+
const select2 = columns.join(", ");
|
|
369
|
+
const orderBy = has(columns, "start_time") ? "\nORDER BY start_time DESC" : "";
|
|
370
|
+
return `-- Backup history
|
|
371
|
+
SELECT ${select2}
|
|
372
|
+
FROM ki_catalog.ki_backup_history${orderBy}
|
|
373
|
+
LIMIT 20;`;
|
|
462
374
|
}
|
|
463
|
-
function
|
|
464
|
-
|
|
375
|
+
function buildKafkaLagInfoSql(columns) {
|
|
376
|
+
const select2 = columns.join(", ");
|
|
377
|
+
const orderBy = has(columns, "datasource_oid") && has(columns, "partition_id") ? "\nORDER BY datasource_oid, partition_id" : has(columns, "datasource_oid") ? "\nORDER BY datasource_oid" : "";
|
|
378
|
+
return `-- Kafka consumer lag
|
|
379
|
+
SELECT ${select2}
|
|
380
|
+
FROM ki_catalog.ki_kafka_lag_info${orderBy};`;
|
|
465
381
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}
|
|
486
|
-
|
|
382
|
+
var BUILDER_REGISTRY = [
|
|
383
|
+
// Query History and Performance
|
|
384
|
+
{
|
|
385
|
+
table: "ki_query_history",
|
|
386
|
+
section: "Query History and Performance",
|
|
387
|
+
build: buildQueryHistorySql,
|
|
388
|
+
fallback: FALLBACK_QUERY_HISTORY_SQL
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
table: "ki_query_active_all",
|
|
392
|
+
section: "Query History and Performance",
|
|
393
|
+
build: buildActiveQueriesSql,
|
|
394
|
+
fallback: FALLBACK_ACTIVE_QUERIES_SQL
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
table: "ki_query_span_metrics_all",
|
|
398
|
+
section: "Query History and Performance",
|
|
399
|
+
build: buildQuerySpanMetricsSql,
|
|
400
|
+
fallback: FALLBACK_QUERY_SPAN_METRICS_SQL
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
table: "ki_query_workers",
|
|
404
|
+
section: "Query History and Performance",
|
|
405
|
+
build: buildQueryWorkersSql,
|
|
406
|
+
fallback: FALLBACK_QUERY_WORKERS_SQL
|
|
407
|
+
},
|
|
408
|
+
// Memory and Storage Tiers
|
|
409
|
+
{
|
|
410
|
+
table: "ki_tiered_objects",
|
|
411
|
+
section: "Memory and Storage Tiers",
|
|
412
|
+
build: buildTieredObjectsSql,
|
|
413
|
+
fallback: FALLBACK_TIERED_OBJECTS_SQL
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
table: "ki_obj_stat",
|
|
417
|
+
section: "Memory and Storage Tiers",
|
|
418
|
+
build: buildObjStatSql,
|
|
419
|
+
fallback: FALLBACK_OBJ_STAT_SQL
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
table: "ki_partitions",
|
|
423
|
+
section: "Memory and Storage Tiers",
|
|
424
|
+
build: buildPartitionsSql,
|
|
425
|
+
fallback: FALLBACK_PARTITIONS_SQL
|
|
426
|
+
},
|
|
427
|
+
// Object Registry and Metadata
|
|
428
|
+
{
|
|
429
|
+
table: "ki_objects",
|
|
430
|
+
section: "Object Registry and Metadata",
|
|
431
|
+
build: buildObjectsSql,
|
|
432
|
+
fallback: FALLBACK_OBJECTS_SQL
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
table: "ki_indexes",
|
|
436
|
+
section: "Object Registry and Metadata",
|
|
437
|
+
build: buildIndexesSql,
|
|
438
|
+
fallback: FALLBACK_INDEXES_SQL
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
table: "ki_periodic_objects",
|
|
442
|
+
section: "Object Registry and Metadata",
|
|
443
|
+
build: buildPeriodicObjectsSql,
|
|
444
|
+
fallback: FALLBACK_PERIODIC_OBJECTS_SQL
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
table: "ki_depend",
|
|
448
|
+
section: "Object Registry and Metadata",
|
|
449
|
+
build: buildDependSql,
|
|
450
|
+
fallback: FALLBACK_DEPEND_SQL
|
|
451
|
+
},
|
|
452
|
+
// Security and Access Control
|
|
453
|
+
{
|
|
454
|
+
table: "ki_users_and_roles",
|
|
455
|
+
section: "Security and Access Control",
|
|
456
|
+
build: buildUsersAndRolesSql,
|
|
457
|
+
fallback: FALLBACK_USERS_AND_ROLES_SQL
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
table: "ki_object_permissions",
|
|
461
|
+
section: "Security and Access Control",
|
|
462
|
+
build: buildObjectPermissionsSql,
|
|
463
|
+
fallback: FALLBACK_OBJECT_PERMISSIONS_SQL
|
|
464
|
+
},
|
|
465
|
+
// Data Ingestion and Operations
|
|
466
|
+
{
|
|
467
|
+
table: "ki_load_history",
|
|
468
|
+
section: "Data Ingestion and Operations",
|
|
469
|
+
build: buildLoadHistorySql,
|
|
470
|
+
fallback: FALLBACK_LOAD_HISTORY_SQL
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
table: "ki_backup_history",
|
|
474
|
+
section: "Data Ingestion and Operations",
|
|
475
|
+
build: buildBackupHistorySql,
|
|
476
|
+
fallback: FALLBACK_BACKUP_HISTORY_SQL
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
table: "ki_kafka_lag_info",
|
|
480
|
+
section: "Data Ingestion and Operations",
|
|
481
|
+
build: buildKafkaLagInfoSql,
|
|
482
|
+
fallback: FALLBACK_KAFKA_LAG_INFO_SQL
|
|
483
|
+
},
|
|
484
|
+
// Schema Inspection
|
|
485
|
+
{
|
|
486
|
+
table: "ki_columns",
|
|
487
|
+
section: "Schema Inspection",
|
|
488
|
+
build: buildColumnsSql,
|
|
489
|
+
fallback: FALLBACK_COLUMNS_SQL
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
table: "ki_datatypes",
|
|
493
|
+
section: "Schema Inspection",
|
|
494
|
+
build: buildDatatypesSql,
|
|
495
|
+
fallback: FALLBACK_DATATYPES_SQL
|
|
487
496
|
}
|
|
497
|
+
];
|
|
498
|
+
|
|
499
|
+
// src/tools/index.ts
|
|
500
|
+
var import_claude_agent_sdk2 = require("@anthropic-ai/claude-agent-sdk");
|
|
501
|
+
var import_zod16 = require("zod");
|
|
502
|
+
var import_picocolors4 = __toESM(require("picocolors"));
|
|
503
|
+
|
|
504
|
+
// src/approval/registry.ts
|
|
505
|
+
var DEFAULT_READ_ONLY_TOOLS = /* @__PURE__ */ new Set();
|
|
506
|
+
function createRegistry(tools = DEFAULT_READ_ONLY_TOOLS) {
|
|
507
|
+
return {
|
|
508
|
+
isReadOnlyTool: (toolName) => tools.has(toolName),
|
|
509
|
+
// Returns a NEW registry — never mutates the current one
|
|
510
|
+
registerReadOnlyTool: (toolName) => createRegistry(/* @__PURE__ */ new Set([...tools, toolName])),
|
|
511
|
+
tools
|
|
512
|
+
};
|
|
488
513
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
const scheme = normalized.split("://")[0];
|
|
500
|
-
return {
|
|
501
|
-
ok: false,
|
|
502
|
-
error: `Unsupported protocol: ${scheme}. Use http:// or https://`
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
const httpsUrl = `https://${normalized}`;
|
|
506
|
-
const httpUrl = `http://${normalized}`;
|
|
507
|
-
console.error(import_picocolors5.default.dim("Detecting protocol..."));
|
|
508
|
-
if (await probeProtocol(httpsUrl)) {
|
|
509
|
-
return { ok: true, url: httpsUrl };
|
|
510
|
-
}
|
|
511
|
-
if (isHttpsOnly()) {
|
|
512
|
-
return {
|
|
513
|
-
ok: false,
|
|
514
|
-
error: `HTTPS probe to ${httpsUrl} failed and KINETICA_HTTPS_ONLY=1; refusing to fall back to plaintext HTTP.`
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
if (!await probeProtocol(httpUrl)) {
|
|
518
|
-
return {
|
|
519
|
-
ok: false,
|
|
520
|
-
error: `Could not connect to ${normalized} via https:// or http://`
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
if (!isInteractive()) {
|
|
524
|
-
return {
|
|
525
|
-
ok: false,
|
|
526
|
-
error: `HTTPS unavailable at ${normalized} and terminal is non-interactive. Pass an explicit http:// prefix to allow plaintext HTTP, or point the URL at an HTTPS endpoint.`
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
const approved = await confirmHttpFallback(normalized);
|
|
530
|
-
if (!approved) {
|
|
531
|
-
return {
|
|
532
|
-
ok: false,
|
|
533
|
-
error: "User declined plaintext HTTP fallback"
|
|
534
|
-
};
|
|
514
|
+
var defaultRegistry = createRegistry();
|
|
515
|
+
var isReadOnlyTool = defaultRegistry.isReadOnlyTool;
|
|
516
|
+
var READ_ONLY_TOOLS = defaultRegistry.tools;
|
|
517
|
+
|
|
518
|
+
// src/output/stringify.ts
|
|
519
|
+
function stringifyValue(v) {
|
|
520
|
+
if (v === void 0 || v === null) return "";
|
|
521
|
+
if (typeof v === "string") return v;
|
|
522
|
+
if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") {
|
|
523
|
+
return v.toString();
|
|
535
524
|
}
|
|
536
|
-
|
|
525
|
+
if (typeof v === "object") return JSON.stringify(v);
|
|
526
|
+
if (typeof v === "symbol") return v.toString();
|
|
527
|
+
return "";
|
|
537
528
|
}
|
|
538
529
|
|
|
539
|
-
// src/
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
function extractVersion(responseBody) {
|
|
544
|
-
try {
|
|
545
|
-
const outer = JSON.parse(responseBody);
|
|
546
|
-
if (typeof outer.data_str !== "string") return void 0;
|
|
547
|
-
const inner = JSON.parse(outer.data_str);
|
|
548
|
-
const systemStr = inner.status_map?.system;
|
|
549
|
-
if (typeof systemStr !== "string") return void 0;
|
|
550
|
-
const system = JSON.parse(systemStr);
|
|
551
|
-
return typeof system.version === "string" ? system.version : void 0;
|
|
552
|
-
} catch {
|
|
553
|
-
return void 0;
|
|
530
|
+
// src/output/format.ts
|
|
531
|
+
function formatOutput(json) {
|
|
532
|
+
if (json === null || json === void 0) {
|
|
533
|
+
return "(empty)";
|
|
554
534
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
return
|
|
560
|
-
} catch {
|
|
561
|
-
return void 0;
|
|
535
|
+
if (Array.isArray(json)) {
|
|
536
|
+
return formatArray(json);
|
|
537
|
+
}
|
|
538
|
+
if (typeof json === "object") {
|
|
539
|
+
return formatObject(json);
|
|
562
540
|
}
|
|
541
|
+
return stringifyValue(json);
|
|
563
542
|
}
|
|
564
|
-
|
|
565
|
-
if (
|
|
566
|
-
return
|
|
543
|
+
function formatArray(arr) {
|
|
544
|
+
if (arr.length === 0) {
|
|
545
|
+
return "(no results)";
|
|
567
546
|
}
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const body = await response.text();
|
|
572
|
-
return { ok: true, version: extractVersionFromHostManager(body) };
|
|
573
|
-
} catch {
|
|
574
|
-
return { ok: false };
|
|
547
|
+
const first = arr[0];
|
|
548
|
+
if (typeof first === "object" && first !== null && !Array.isArray(first)) {
|
|
549
|
+
return formatTableArray(arr);
|
|
575
550
|
}
|
|
551
|
+
return arr.map(stringifyValue).join("\n");
|
|
576
552
|
}
|
|
577
|
-
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
553
|
+
function formatTableArray(rows) {
|
|
554
|
+
const headers = Object.keys(rows[0]);
|
|
555
|
+
const cells = rows.map((row) => headers.map((h) => stringifyValue(row[h])));
|
|
556
|
+
const colWidths = headers.map(
|
|
557
|
+
(h, i) => Math.max(h.length, ...cells.map((row) => row[i].length), 3)
|
|
558
|
+
);
|
|
559
|
+
const pad = (text, col) => text.padEnd(colWidths[col]);
|
|
560
|
+
const headerRow = `| ${headers.map((h, i) => pad(h, i)).join(" | ")} |`;
|
|
561
|
+
const separatorRow = `| ${colWidths.map((w) => "-".repeat(w)).join(" | ")} |`;
|
|
562
|
+
const dataRows = cells.map((row) => `| ${row.map((cell, i) => pad(cell, i)).join(" | ")} |`);
|
|
563
|
+
return [headerRow, separatorRow, ...dataRows].join("\n");
|
|
564
|
+
}
|
|
565
|
+
function formatObject(obj) {
|
|
566
|
+
return Object.entries(obj).map(([key, value]) => {
|
|
567
|
+
if (typeof value === "object" && value !== null) {
|
|
568
|
+
return `**${key}:**
|
|
569
|
+
${formatOutput(value)}`;
|
|
570
|
+
}
|
|
571
|
+
return `**${key}:** ${stringifyValue(value)}`;
|
|
572
|
+
}).join("\n");
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// src/output/format-tool-name.ts
|
|
576
|
+
function formatToolName(toolName) {
|
|
577
|
+
const stripped = toolName.replace(/^mcp__[^_]+__/, "").replace(/^kinetica_/, "");
|
|
578
|
+
return stripped.replace(/_/g, " ");
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// src/types/index.ts
|
|
582
|
+
var DEFAULT_TRUNCATION = {
|
|
583
|
+
headLines: 150,
|
|
584
|
+
tailLines: 50
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
// src/output/truncate.ts
|
|
588
|
+
function truncateOutput(text, options = DEFAULT_TRUNCATION) {
|
|
589
|
+
if (text === "") return "";
|
|
590
|
+
const lines = text.split("\n");
|
|
591
|
+
const { headLines, tailLines } = options;
|
|
592
|
+
const threshold = headLines + tailLines;
|
|
593
|
+
if (lines.length <= threshold) {
|
|
594
|
+
return text;
|
|
582
595
|
}
|
|
583
|
-
const
|
|
584
|
-
|
|
596
|
+
const truncatedCount = lines.length - headLines - tailLines;
|
|
597
|
+
const head = lines.slice(0, headLines);
|
|
598
|
+
const tail = lines.slice(lines.length - tailLines);
|
|
599
|
+
return [...head, "", `[... ${truncatedCount} lines truncated ...]`, "", ...tail].join("\n");
|
|
585
600
|
}
|
|
586
|
-
|
|
587
|
-
|
|
601
|
+
|
|
602
|
+
// src/output/reshape.ts
|
|
603
|
+
function safeString(v) {
|
|
604
|
+
if (typeof v === "object" && v !== null) {
|
|
605
|
+
return JSON.stringify(v);
|
|
606
|
+
}
|
|
607
|
+
return String(v);
|
|
588
608
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
609
|
+
function flatObjectToRows(obj, keyLabel = "key", valueLabel = "value") {
|
|
610
|
+
return Object.entries(obj).map(([k, v]) => ({
|
|
611
|
+
[keyLabel]: k,
|
|
612
|
+
[valueLabel]: safeString(v)
|
|
613
|
+
}));
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// src/tools/rest/parse-data-str.ts
|
|
617
|
+
function parseDataStr(outerDataStr, raw) {
|
|
618
|
+
if (typeof outerDataStr !== "string") {
|
|
619
|
+
return { ok: true, data: void 0 };
|
|
595
620
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
621
|
+
try {
|
|
622
|
+
return { ok: true, data: JSON.parse(outerDataStr) };
|
|
623
|
+
} catch (err) {
|
|
624
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
625
|
+
return { ok: false, status: 200, error: `data_str parse error: ${message}`, raw };
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/tools/rest/health.ts
|
|
630
|
+
async function healthCheck(session2) {
|
|
631
|
+
let response;
|
|
632
|
+
let rawText;
|
|
633
|
+
try {
|
|
634
|
+
response = await session2.makeRequest("/show/system/status", {});
|
|
635
|
+
rawText = await response.text();
|
|
636
|
+
} catch (err) {
|
|
637
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
638
|
+
return {
|
|
639
|
+
ok: false,
|
|
640
|
+
status: 0,
|
|
641
|
+
error: message,
|
|
642
|
+
raw: ""
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
if (!response.ok) {
|
|
646
|
+
return {
|
|
647
|
+
ok: false,
|
|
648
|
+
status: response.status,
|
|
649
|
+
error: `HTTP ${response.status}`,
|
|
650
|
+
raw: rawText
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
let parsed;
|
|
654
|
+
try {
|
|
655
|
+
parsed = JSON.parse(rawText);
|
|
656
|
+
} catch (err) {
|
|
657
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
658
|
+
return {
|
|
659
|
+
ok: false,
|
|
660
|
+
status: 200,
|
|
661
|
+
error: `JSON parse error: ${message}`,
|
|
662
|
+
raw: rawText
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
const inner = parseDataStr(parsed.data_str, rawText);
|
|
666
|
+
if (!inner.ok) return inner;
|
|
667
|
+
const statusMap = inner.data?.status_map ?? {};
|
|
668
|
+
return {
|
|
669
|
+
ok: true,
|
|
670
|
+
data: flatObjectToRows(statusMap, "component", "status")
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// src/tools/rest/decode-nested-json.ts
|
|
675
|
+
function decodeNestedJsonStrings(obj) {
|
|
676
|
+
return Object.fromEntries(
|
|
677
|
+
Object.entries(obj).map(([k, v]) => {
|
|
678
|
+
if (typeof v !== "string") return [k, v];
|
|
679
|
+
try {
|
|
680
|
+
const parsed = JSON.parse(v);
|
|
681
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
682
|
+
return [k, decodeNestedJsonStrings(parsed)];
|
|
629
683
|
}
|
|
630
|
-
|
|
631
|
-
|
|
684
|
+
return [k, parsed];
|
|
685
|
+
} catch {
|
|
686
|
+
return [k, v];
|
|
632
687
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
await offerSaveCredentials(resolvedUrl, currentUser);
|
|
644
|
-
}
|
|
645
|
-
return { session: session2, kineticaVersion: hmResult.version, degraded: true };
|
|
646
|
-
}
|
|
647
|
-
console.error(import_picocolors6.default.red("Host manager also unreachable. Exiting."));
|
|
648
|
-
process.exit(1);
|
|
688
|
+
})
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// src/tools/rest/flatten-rank-stats.ts
|
|
693
|
+
function findTier(tiers, prefix) {
|
|
694
|
+
for (const [name, data] of Object.entries(tiers)) {
|
|
695
|
+
if (name === prefix || name.startsWith(`${prefix}.`) || name.startsWith(`${prefix}_`) || name.startsWith(prefix) && /^\d/.test(name.slice(prefix.length))) {
|
|
696
|
+
if (typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
697
|
+
return data;
|
|
649
698
|
}
|
|
650
699
|
}
|
|
651
700
|
}
|
|
652
|
-
|
|
701
|
+
return void 0;
|
|
653
702
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
// src/agent/diagnostic-sql.ts
|
|
661
|
-
function has(columns, name) {
|
|
662
|
-
return columns.includes(name);
|
|
663
|
-
}
|
|
664
|
-
var FALLBACK_QUERY_HISTORY_SQL = `-- Slow queries in the last hour
|
|
665
|
-
SELECT query_id, user_name, query_text, start_time, stop_time,
|
|
666
|
-
TIMESTAMPDIFF(SECOND, start_time, stop_time) AS elapsed_sec
|
|
667
|
-
FROM ki_catalog.ki_query_history
|
|
668
|
-
WHERE start_time > NOW() - INTERVAL '1' HOUR
|
|
669
|
-
ORDER BY elapsed_sec DESC
|
|
670
|
-
LIMIT 20;`;
|
|
671
|
-
var FALLBACK_ACTIVE_QUERIES_SQL = `-- Currently active queries
|
|
672
|
-
SELECT query_id, user_name, query_text, start_time, execution_status
|
|
673
|
-
FROM ki_catalog.ki_query_active_all
|
|
674
|
-
ORDER BY start_time ASC;`;
|
|
675
|
-
var FALLBACK_TIERED_OBJECTS_SQL = `-- Objects in disk tier (potential memory pressure)
|
|
676
|
-
-- NOTE: id is a string (e.g. @schema@oid[col][0]), NOT a numeric OID. Filter by table: WHERE id LIKE '%table_name%'
|
|
677
|
-
-- For per-table tier placement, prefer kinetica_resource_objects with table_names filter.
|
|
678
|
-
SELECT id, tier, size, source_rank, owner_resource_group
|
|
679
|
-
FROM ki_catalog.ki_tiered_objects
|
|
680
|
-
WHERE tier != 'VRAM'
|
|
681
|
-
ORDER BY size DESC
|
|
682
|
-
LIMIT 20;`;
|
|
683
|
-
var FALLBACK_OBJ_STAT_SQL = `-- Table sizes and row counts
|
|
684
|
-
SELECT object_name, total_bytes, row_count
|
|
685
|
-
FROM ki_catalog.ki_obj_stat
|
|
686
|
-
ORDER BY total_bytes DESC
|
|
687
|
-
LIMIT 30;`;
|
|
688
|
-
var FALLBACK_COLUMNS_SQL = `-- Structural column metadata (use kinetica_show_table for Kinetica-native types)
|
|
689
|
-
SELECT c.table_name, c.column_name, c.column_position
|
|
690
|
-
FROM ki_catalog.ki_columns c
|
|
691
|
-
WHERE c.table_name = '<TABLE_NAME>'
|
|
692
|
-
ORDER BY c.column_position;`;
|
|
693
|
-
var FALLBACK_DATATYPES_SQL = `-- Resolve column_type_oid to human-readable type name
|
|
694
|
-
SELECT oid, name, sql_typename
|
|
695
|
-
FROM ki_catalog.ki_datatypes
|
|
696
|
-
ORDER BY oid;`;
|
|
697
|
-
var FALLBACK_QUERY_SPAN_METRICS_SQL = `-- Query span metrics for a specific query
|
|
698
|
-
SELECT query_id, span_id, parent_span_id, operator, sql_step,
|
|
699
|
-
metric_data, start_time, stop_time, source_rank
|
|
700
|
-
FROM ki_catalog.ki_query_span_metrics_all
|
|
701
|
-
WHERE query_id = '<QUERY_ID>'
|
|
702
|
-
ORDER BY start_time;`;
|
|
703
|
-
var FALLBACK_QUERY_WORKERS_SQL = `-- Active query workers (non-idle)
|
|
704
|
-
SELECT job_id, worker_id, type, status, elapsed_time_ms, source_rank
|
|
705
|
-
FROM ki_catalog.ki_query_workers
|
|
706
|
-
WHERE status != 'IDLE'
|
|
707
|
-
ORDER BY elapsed_time_ms DESC;`;
|
|
708
|
-
var FALLBACK_OBJECTS_SQL = `-- Object registry and metadata
|
|
709
|
-
SELECT oid, object_name, schema_name, type_id, persistence, obj_kind,
|
|
710
|
-
creation_time, last_read_time, read_count, write_count
|
|
711
|
-
FROM ki_catalog.ki_objects
|
|
712
|
-
ORDER BY last_read_time DESC
|
|
713
|
-
LIMIT 30;`;
|
|
714
|
-
var FALLBACK_PARTITIONS_SQL = `-- Partition sizes and tier distribution
|
|
715
|
-
SELECT oid, object_name, schema_name, rank_num, partition_type,
|
|
716
|
-
partition_id, num_rows, actual_bytes, tier
|
|
717
|
-
FROM ki_catalog.ki_partitions
|
|
718
|
-
ORDER BY actual_bytes DESC
|
|
719
|
-
LIMIT 30;`;
|
|
720
|
-
var FALLBACK_INDEXES_SQL = `-- Index definitions
|
|
721
|
-
SELECT oid, object_name, schema_name, index_type, index_columns
|
|
722
|
-
FROM ki_catalog.ki_indexes
|
|
723
|
-
ORDER BY object_name;`;
|
|
724
|
-
var FALLBACK_PERIODIC_OBJECTS_SQL = `-- Periodic refresh schedules
|
|
725
|
-
SELECT oid, object_name, schema_name, last_refresh_time,
|
|
726
|
-
next_refresh_time, additional_info
|
|
727
|
-
FROM ki_catalog.ki_periodic_objects
|
|
728
|
-
ORDER BY next_refresh_time;`;
|
|
729
|
-
var FALLBACK_USERS_AND_ROLES_SQL = `-- Users and roles
|
|
730
|
-
SELECT oid, name, can_login, is_superuser, resource_group
|
|
731
|
-
FROM ki_catalog.ki_users_and_roles
|
|
732
|
-
ORDER BY name;`;
|
|
733
|
-
var FALLBACK_OBJECT_PERMISSIONS_SQL = `-- Object permissions
|
|
734
|
-
SELECT role_name, permission_type, object_type, object_name, with_grant_option
|
|
735
|
-
FROM ki_catalog.ki_object_permissions
|
|
736
|
-
ORDER BY object_name, role_name;`;
|
|
737
|
-
var FALLBACK_DEPEND_SQL = `-- Object dependency graph
|
|
738
|
-
SELECT src_obj_oid, src_obj_kind, dep_obj_oid, dep_obj_kind, mv_oid, dep_kind
|
|
739
|
-
FROM ki_catalog.ki_depend;`;
|
|
740
|
-
var FALLBACK_LOAD_HISTORY_SQL = `-- Recent data load history
|
|
741
|
-
SELECT table_oid, datasource_oid, user_name, load_kind,
|
|
742
|
-
start_time, end_time, rows_inserted, event_message
|
|
743
|
-
FROM ki_catalog.ki_load_history
|
|
744
|
-
WHERE start_time > NOW() - INTERVAL '1' HOUR
|
|
745
|
-
ORDER BY start_time DESC
|
|
746
|
-
LIMIT 20;`;
|
|
747
|
-
var FALLBACK_BACKUP_HISTORY_SQL = `-- Backup history
|
|
748
|
-
SELECT backup_name, operation, status, start_time, end_time,
|
|
749
|
-
num_files, num_bytes, num_records
|
|
750
|
-
FROM ki_catalog.ki_backup_history
|
|
751
|
-
ORDER BY start_time DESC
|
|
752
|
-
LIMIT 20;`;
|
|
753
|
-
var FALLBACK_KAFKA_LAG_INFO_SQL = `-- Kafka consumer lag
|
|
754
|
-
SELECT datasource_oid, table_oid, schema_name, table_name,
|
|
755
|
-
partition_id, highest_offset, last_committed_offset
|
|
756
|
-
FROM ki_catalog.ki_kafka_lag_info
|
|
757
|
-
ORDER BY datasource_oid, partition_id;`;
|
|
758
|
-
function buildQueryHistorySql(columns) {
|
|
759
|
-
const select = columns.join(", ");
|
|
760
|
-
const elapsed = has(columns, "start_time") && has(columns, "stop_time") ? ",\n TIMESTAMPDIFF(SECOND, start_time, stop_time) AS elapsed_sec" : "";
|
|
761
|
-
const where = has(columns, "start_time") ? "\nWHERE start_time > NOW() - INTERVAL '1' HOUR" : "";
|
|
762
|
-
const orderBy = has(columns, "start_time") && has(columns, "stop_time") ? (
|
|
763
|
-
// Kinetica does not support timestamp arithmetic in ORDER BY; use the elapsed_sec alias instead
|
|
764
|
-
"\nORDER BY elapsed_sec DESC"
|
|
765
|
-
) : "";
|
|
766
|
-
return `-- Slow queries in the last hour
|
|
767
|
-
SELECT ${select}${elapsed}
|
|
768
|
-
FROM ki_catalog.ki_query_history${where}${orderBy}
|
|
769
|
-
LIMIT 20;`;
|
|
770
|
-
}
|
|
771
|
-
function buildActiveQueriesSql(columns) {
|
|
772
|
-
const select = columns.join(", ");
|
|
773
|
-
const orderBy = has(columns, "start_time") ? "\nORDER BY start_time ASC" : "";
|
|
774
|
-
return `-- Currently active queries
|
|
775
|
-
SELECT ${select}
|
|
776
|
-
FROM ki_catalog.ki_query_active_all${orderBy};`;
|
|
777
|
-
}
|
|
778
|
-
function buildTieredObjectsSql(columns) {
|
|
779
|
-
const select = columns.join(", ");
|
|
780
|
-
const where = has(columns, "tier") ? "\nWHERE tier != 'VRAM'" : "";
|
|
781
|
-
const orderBy = has(columns, "size") ? "\nORDER BY size DESC" : "";
|
|
782
|
-
const idNote = has(columns, "id") ? "\n-- NOTE: id is a string (e.g. @schema@oid[col][0]), NOT a numeric OID. Filter by table: WHERE id LIKE '%table_name%'" : "";
|
|
783
|
-
return `-- Objects in disk tier (potential memory pressure)${idNote}
|
|
784
|
-
SELECT ${select}
|
|
785
|
-
FROM ki_catalog.ki_tiered_objects${where}${orderBy}
|
|
786
|
-
LIMIT 20;`;
|
|
787
|
-
}
|
|
788
|
-
function buildObjStatSql(columns) {
|
|
789
|
-
const select = columns.join(", ");
|
|
790
|
-
const orderBy = has(columns, "total_bytes") ? "\nORDER BY total_bytes DESC" : "";
|
|
791
|
-
return `-- Table sizes and row counts
|
|
792
|
-
SELECT ${select}
|
|
793
|
-
FROM ki_catalog.ki_obj_stat${orderBy}
|
|
794
|
-
LIMIT 30;`;
|
|
795
|
-
}
|
|
796
|
-
var STRUCTURAL_COLUMNS = /* @__PURE__ */ new Set([
|
|
797
|
-
"table_name",
|
|
798
|
-
"column_name",
|
|
799
|
-
"column_position",
|
|
800
|
-
"is_nullable",
|
|
801
|
-
"is_shard_key",
|
|
802
|
-
"is_primary_key",
|
|
803
|
-
"is_dict_encoded",
|
|
804
|
-
"default_value",
|
|
805
|
-
"bytes_on_disk_uncompressed",
|
|
806
|
-
"bytes_on_disk_compressed"
|
|
807
|
-
]);
|
|
808
|
-
function buildColumnsSql(columns) {
|
|
809
|
-
const filtered = columns.filter((c) => STRUCTURAL_COLUMNS.has(c));
|
|
810
|
-
const selectCols = filtered.length > 0 ? filtered : columns;
|
|
811
|
-
const select = selectCols.map((c) => `c.${c}`).join(", ");
|
|
812
|
-
const where = has(selectCols, "table_name") ? "\nWHERE c.table_name = '<TABLE_NAME>'" : "";
|
|
813
|
-
const orderBy = has(selectCols, "column_position") ? "\nORDER BY c.column_position" : has(selectCols, "column_name") ? "\nORDER BY c.column_name" : "";
|
|
814
|
-
return `-- Structural column metadata (use kinetica_show_table for Kinetica-native types)
|
|
815
|
-
SELECT ${select}
|
|
816
|
-
FROM ki_catalog.ki_columns c${where}${orderBy};`;
|
|
817
|
-
}
|
|
818
|
-
function buildDatatypesSql(columns) {
|
|
819
|
-
const select = columns.join(", ");
|
|
820
|
-
const orderBy = has(columns, "oid") ? "\nORDER BY oid" : "";
|
|
821
|
-
return `-- Resolve column_type_oid to human-readable type name
|
|
822
|
-
SELECT ${select}
|
|
823
|
-
FROM ki_catalog.ki_datatypes${orderBy};`;
|
|
824
|
-
}
|
|
825
|
-
function buildQuerySpanMetricsSql(columns) {
|
|
826
|
-
const select = columns.join(", ");
|
|
827
|
-
const where = has(columns, "query_id") ? "\nWHERE query_id = '<QUERY_ID>'" : "";
|
|
828
|
-
const orderBy = has(columns, "start_time") ? "\nORDER BY start_time" : "";
|
|
829
|
-
return `-- Query span metrics for a specific query
|
|
830
|
-
SELECT ${select}
|
|
831
|
-
FROM ki_catalog.ki_query_span_metrics_all${where}${orderBy};`;
|
|
832
|
-
}
|
|
833
|
-
function buildQueryWorkersSql(columns) {
|
|
834
|
-
const select = columns.join(", ");
|
|
835
|
-
const where = has(columns, "status") ? "\nWHERE status != 'IDLE'" : "";
|
|
836
|
-
const orderBy = has(columns, "elapsed_time_ms") ? "\nORDER BY elapsed_time_ms DESC" : "";
|
|
837
|
-
return `-- Active query workers (non-idle)
|
|
838
|
-
SELECT ${select}
|
|
839
|
-
FROM ki_catalog.ki_query_workers${where}${orderBy};`;
|
|
840
|
-
}
|
|
841
|
-
function buildObjectsSql(columns) {
|
|
842
|
-
const select = columns.join(", ");
|
|
843
|
-
const orderBy = has(columns, "last_read_time") ? "\nORDER BY last_read_time DESC" : has(columns, "creation_time") ? "\nORDER BY creation_time DESC" : "";
|
|
844
|
-
return `-- Object registry and metadata
|
|
845
|
-
SELECT ${select}
|
|
846
|
-
FROM ki_catalog.ki_objects${orderBy}
|
|
847
|
-
LIMIT 30;`;
|
|
848
|
-
}
|
|
849
|
-
function buildPartitionsSql(columns) {
|
|
850
|
-
const select = columns.join(", ");
|
|
851
|
-
const orderBy = has(columns, "actual_bytes") ? "\nORDER BY actual_bytes DESC" : "";
|
|
852
|
-
return `-- Partition sizes and tier distribution
|
|
853
|
-
SELECT ${select}
|
|
854
|
-
FROM ki_catalog.ki_partitions${orderBy}
|
|
855
|
-
LIMIT 30;`;
|
|
856
|
-
}
|
|
857
|
-
function buildIndexesSql(columns) {
|
|
858
|
-
const select = columns.join(", ");
|
|
859
|
-
const orderBy = has(columns, "object_name") ? "\nORDER BY object_name" : "";
|
|
860
|
-
return `-- Index definitions
|
|
861
|
-
SELECT ${select}
|
|
862
|
-
FROM ki_catalog.ki_indexes${orderBy};`;
|
|
863
|
-
}
|
|
864
|
-
function buildPeriodicObjectsSql(columns) {
|
|
865
|
-
const select = columns.join(", ");
|
|
866
|
-
const orderBy = has(columns, "next_refresh_time") ? "\nORDER BY next_refresh_time" : "";
|
|
867
|
-
return `-- Periodic refresh schedules
|
|
868
|
-
SELECT ${select}
|
|
869
|
-
FROM ki_catalog.ki_periodic_objects${orderBy};`;
|
|
870
|
-
}
|
|
871
|
-
function buildUsersAndRolesSql(columns) {
|
|
872
|
-
const select = columns.join(", ");
|
|
873
|
-
const orderBy = has(columns, "name") ? "\nORDER BY name" : "";
|
|
874
|
-
return `-- Users and roles
|
|
875
|
-
SELECT ${select}
|
|
876
|
-
FROM ki_catalog.ki_users_and_roles${orderBy};`;
|
|
877
|
-
}
|
|
878
|
-
function buildObjectPermissionsSql(columns) {
|
|
879
|
-
const select = columns.join(", ");
|
|
880
|
-
const orderBy = has(columns, "object_name") && has(columns, "role_name") ? "\nORDER BY object_name, role_name" : has(columns, "object_name") ? "\nORDER BY object_name" : "";
|
|
881
|
-
return `-- Object permissions
|
|
882
|
-
SELECT ${select}
|
|
883
|
-
FROM ki_catalog.ki_object_permissions${orderBy};`;
|
|
884
|
-
}
|
|
885
|
-
function buildDependSql(columns) {
|
|
886
|
-
const select = columns.join(", ");
|
|
887
|
-
return `-- Object dependency graph
|
|
888
|
-
SELECT ${select}
|
|
889
|
-
FROM ki_catalog.ki_depend;`;
|
|
890
|
-
}
|
|
891
|
-
function buildLoadHistorySql(columns) {
|
|
892
|
-
const select = columns.join(", ");
|
|
893
|
-
const where = has(columns, "start_time") ? "\nWHERE start_time > NOW() - INTERVAL '1' HOUR" : "";
|
|
894
|
-
const orderBy = has(columns, "start_time") ? "\nORDER BY start_time DESC" : "";
|
|
895
|
-
return `-- Recent data load history
|
|
896
|
-
SELECT ${select}
|
|
897
|
-
FROM ki_catalog.ki_load_history${where}${orderBy}
|
|
898
|
-
LIMIT 20;`;
|
|
899
|
-
}
|
|
900
|
-
function buildBackupHistorySql(columns) {
|
|
901
|
-
const select = columns.join(", ");
|
|
902
|
-
const orderBy = has(columns, "start_time") ? "\nORDER BY start_time DESC" : "";
|
|
903
|
-
return `-- Backup history
|
|
904
|
-
SELECT ${select}
|
|
905
|
-
FROM ki_catalog.ki_backup_history${orderBy}
|
|
906
|
-
LIMIT 20;`;
|
|
907
|
-
}
|
|
908
|
-
function buildKafkaLagInfoSql(columns) {
|
|
909
|
-
const select = columns.join(", ");
|
|
910
|
-
const orderBy = has(columns, "datasource_oid") && has(columns, "partition_id") ? "\nORDER BY datasource_oid, partition_id" : has(columns, "datasource_oid") ? "\nORDER BY datasource_oid" : "";
|
|
911
|
-
return `-- Kafka consumer lag
|
|
912
|
-
SELECT ${select}
|
|
913
|
-
FROM ki_catalog.ki_kafka_lag_info${orderBy};`;
|
|
914
|
-
}
|
|
915
|
-
var BUILDER_REGISTRY = [
|
|
916
|
-
// Query History and Performance
|
|
917
|
-
{
|
|
918
|
-
table: "ki_query_history",
|
|
919
|
-
section: "Query History and Performance",
|
|
920
|
-
build: buildQueryHistorySql,
|
|
921
|
-
fallback: FALLBACK_QUERY_HISTORY_SQL
|
|
922
|
-
},
|
|
923
|
-
{
|
|
924
|
-
table: "ki_query_active_all",
|
|
925
|
-
section: "Query History and Performance",
|
|
926
|
-
build: buildActiveQueriesSql,
|
|
927
|
-
fallback: FALLBACK_ACTIVE_QUERIES_SQL
|
|
928
|
-
},
|
|
929
|
-
{
|
|
930
|
-
table: "ki_query_span_metrics_all",
|
|
931
|
-
section: "Query History and Performance",
|
|
932
|
-
build: buildQuerySpanMetricsSql,
|
|
933
|
-
fallback: FALLBACK_QUERY_SPAN_METRICS_SQL
|
|
934
|
-
},
|
|
935
|
-
{
|
|
936
|
-
table: "ki_query_workers",
|
|
937
|
-
section: "Query History and Performance",
|
|
938
|
-
build: buildQueryWorkersSql,
|
|
939
|
-
fallback: FALLBACK_QUERY_WORKERS_SQL
|
|
940
|
-
},
|
|
941
|
-
// Memory and Storage Tiers
|
|
942
|
-
{
|
|
943
|
-
table: "ki_tiered_objects",
|
|
944
|
-
section: "Memory and Storage Tiers",
|
|
945
|
-
build: buildTieredObjectsSql,
|
|
946
|
-
fallback: FALLBACK_TIERED_OBJECTS_SQL
|
|
947
|
-
},
|
|
948
|
-
{
|
|
949
|
-
table: "ki_obj_stat",
|
|
950
|
-
section: "Memory and Storage Tiers",
|
|
951
|
-
build: buildObjStatSql,
|
|
952
|
-
fallback: FALLBACK_OBJ_STAT_SQL
|
|
953
|
-
},
|
|
954
|
-
{
|
|
955
|
-
table: "ki_partitions",
|
|
956
|
-
section: "Memory and Storage Tiers",
|
|
957
|
-
build: buildPartitionsSql,
|
|
958
|
-
fallback: FALLBACK_PARTITIONS_SQL
|
|
959
|
-
},
|
|
960
|
-
// Object Registry and Metadata
|
|
961
|
-
{
|
|
962
|
-
table: "ki_objects",
|
|
963
|
-
section: "Object Registry and Metadata",
|
|
964
|
-
build: buildObjectsSql,
|
|
965
|
-
fallback: FALLBACK_OBJECTS_SQL
|
|
966
|
-
},
|
|
967
|
-
{
|
|
968
|
-
table: "ki_indexes",
|
|
969
|
-
section: "Object Registry and Metadata",
|
|
970
|
-
build: buildIndexesSql,
|
|
971
|
-
fallback: FALLBACK_INDEXES_SQL
|
|
972
|
-
},
|
|
973
|
-
{
|
|
974
|
-
table: "ki_periodic_objects",
|
|
975
|
-
section: "Object Registry and Metadata",
|
|
976
|
-
build: buildPeriodicObjectsSql,
|
|
977
|
-
fallback: FALLBACK_PERIODIC_OBJECTS_SQL
|
|
978
|
-
},
|
|
979
|
-
{
|
|
980
|
-
table: "ki_depend",
|
|
981
|
-
section: "Object Registry and Metadata",
|
|
982
|
-
build: buildDependSql,
|
|
983
|
-
fallback: FALLBACK_DEPEND_SQL
|
|
984
|
-
},
|
|
985
|
-
// Security and Access Control
|
|
986
|
-
{
|
|
987
|
-
table: "ki_users_and_roles",
|
|
988
|
-
section: "Security and Access Control",
|
|
989
|
-
build: buildUsersAndRolesSql,
|
|
990
|
-
fallback: FALLBACK_USERS_AND_ROLES_SQL
|
|
991
|
-
},
|
|
992
|
-
{
|
|
993
|
-
table: "ki_object_permissions",
|
|
994
|
-
section: "Security and Access Control",
|
|
995
|
-
build: buildObjectPermissionsSql,
|
|
996
|
-
fallback: FALLBACK_OBJECT_PERMISSIONS_SQL
|
|
997
|
-
},
|
|
998
|
-
// Data Ingestion and Operations
|
|
999
|
-
{
|
|
1000
|
-
table: "ki_load_history",
|
|
1001
|
-
section: "Data Ingestion and Operations",
|
|
1002
|
-
build: buildLoadHistorySql,
|
|
1003
|
-
fallback: FALLBACK_LOAD_HISTORY_SQL
|
|
1004
|
-
},
|
|
1005
|
-
{
|
|
1006
|
-
table: "ki_backup_history",
|
|
1007
|
-
section: "Data Ingestion and Operations",
|
|
1008
|
-
build: buildBackupHistorySql,
|
|
1009
|
-
fallback: FALLBACK_BACKUP_HISTORY_SQL
|
|
1010
|
-
},
|
|
1011
|
-
{
|
|
1012
|
-
table: "ki_kafka_lag_info",
|
|
1013
|
-
section: "Data Ingestion and Operations",
|
|
1014
|
-
build: buildKafkaLagInfoSql,
|
|
1015
|
-
fallback: FALLBACK_KAFKA_LAG_INFO_SQL
|
|
1016
|
-
},
|
|
1017
|
-
// Schema Inspection
|
|
1018
|
-
{
|
|
1019
|
-
table: "ki_columns",
|
|
1020
|
-
section: "Schema Inspection",
|
|
1021
|
-
build: buildColumnsSql,
|
|
1022
|
-
fallback: FALLBACK_COLUMNS_SQL
|
|
1023
|
-
},
|
|
1024
|
-
{
|
|
1025
|
-
table: "ki_datatypes",
|
|
1026
|
-
section: "Schema Inspection",
|
|
1027
|
-
build: buildDatatypesSql,
|
|
1028
|
-
fallback: FALLBACK_DATATYPES_SQL
|
|
1029
|
-
}
|
|
1030
|
-
];
|
|
1031
|
-
|
|
1032
|
-
// src/tools/index.ts
|
|
1033
|
-
var import_claude_agent_sdk3 = require("@anthropic-ai/claude-agent-sdk");
|
|
1034
|
-
var import_zod16 = require("zod");
|
|
1035
|
-
var import_picocolors9 = __toESM(require("picocolors"));
|
|
1036
|
-
|
|
1037
|
-
// src/approval/registry.ts
|
|
1038
|
-
var DEFAULT_READ_ONLY_TOOLS = /* @__PURE__ */ new Set();
|
|
1039
|
-
function createRegistry(tools = DEFAULT_READ_ONLY_TOOLS) {
|
|
1040
|
-
return {
|
|
1041
|
-
isReadOnlyTool: (toolName) => tools.has(toolName),
|
|
1042
|
-
// Returns a NEW registry — never mutates the current one
|
|
1043
|
-
registerReadOnlyTool: (toolName) => createRegistry(/* @__PURE__ */ new Set([...tools, toolName])),
|
|
1044
|
-
tools
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
var defaultRegistry = createRegistry();
|
|
1048
|
-
var isReadOnlyTool = defaultRegistry.isReadOnlyTool;
|
|
1049
|
-
var READ_ONLY_TOOLS = defaultRegistry.tools;
|
|
1050
|
-
|
|
1051
|
-
// src/output/stringify.ts
|
|
1052
|
-
function stringifyValue(v) {
|
|
1053
|
-
if (v === void 0 || v === null) return "";
|
|
1054
|
-
if (typeof v === "string") return v;
|
|
1055
|
-
if (typeof v === "number" || typeof v === "boolean" || typeof v === "bigint") {
|
|
1056
|
-
return v.toString();
|
|
1057
|
-
}
|
|
1058
|
-
if (typeof v === "object") return JSON.stringify(v);
|
|
1059
|
-
if (typeof v === "symbol") return v.toString();
|
|
1060
|
-
return "";
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
// src/output/format.ts
|
|
1064
|
-
function formatOutput(json) {
|
|
1065
|
-
if (json === null || json === void 0) {
|
|
1066
|
-
return "(empty)";
|
|
1067
|
-
}
|
|
1068
|
-
if (Array.isArray(json)) {
|
|
1069
|
-
return formatArray(json);
|
|
1070
|
-
}
|
|
1071
|
-
if (typeof json === "object") {
|
|
1072
|
-
return formatObject(json);
|
|
1073
|
-
}
|
|
1074
|
-
return stringifyValue(json);
|
|
1075
|
-
}
|
|
1076
|
-
function formatArray(arr) {
|
|
1077
|
-
if (arr.length === 0) {
|
|
1078
|
-
return "(no results)";
|
|
1079
|
-
}
|
|
1080
|
-
const first = arr[0];
|
|
1081
|
-
if (typeof first === "object" && first !== null && !Array.isArray(first)) {
|
|
1082
|
-
return formatTableArray(arr);
|
|
1083
|
-
}
|
|
1084
|
-
return arr.map(stringifyValue).join("\n");
|
|
1085
|
-
}
|
|
1086
|
-
function formatTableArray(rows) {
|
|
1087
|
-
const headers = Object.keys(rows[0]);
|
|
1088
|
-
const cells = rows.map((row) => headers.map((h) => stringifyValue(row[h])));
|
|
1089
|
-
const colWidths = headers.map(
|
|
1090
|
-
(h, i) => Math.max(h.length, ...cells.map((row) => row[i].length), 3)
|
|
1091
|
-
);
|
|
1092
|
-
const pad = (text, col) => text.padEnd(colWidths[col]);
|
|
1093
|
-
const headerRow = `| ${headers.map((h, i) => pad(h, i)).join(" | ")} |`;
|
|
1094
|
-
const separatorRow = `| ${colWidths.map((w) => "-".repeat(w)).join(" | ")} |`;
|
|
1095
|
-
const dataRows = cells.map((row) => `| ${row.map((cell, i) => pad(cell, i)).join(" | ")} |`);
|
|
1096
|
-
return [headerRow, separatorRow, ...dataRows].join("\n");
|
|
1097
|
-
}
|
|
1098
|
-
function formatObject(obj) {
|
|
1099
|
-
return Object.entries(obj).map(([key, value]) => {
|
|
1100
|
-
if (typeof value === "object" && value !== null) {
|
|
1101
|
-
return `**${key}:**
|
|
1102
|
-
${formatOutput(value)}`;
|
|
1103
|
-
}
|
|
1104
|
-
return `**${key}:** ${stringifyValue(value)}`;
|
|
1105
|
-
}).join("\n");
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
// src/output/format-tool-name.ts
|
|
1109
|
-
function formatToolName(toolName) {
|
|
1110
|
-
const stripped = toolName.replace(/^mcp__[^_]+__/, "").replace(/^kinetica_/, "");
|
|
1111
|
-
return stripped.replace(/_/g, " ");
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
// src/types/index.ts
|
|
1115
|
-
var DEFAULT_TRUNCATION = {
|
|
1116
|
-
headLines: 150,
|
|
1117
|
-
tailLines: 50
|
|
1118
|
-
};
|
|
1119
|
-
|
|
1120
|
-
// src/output/truncate.ts
|
|
1121
|
-
function truncateOutput(text, options = DEFAULT_TRUNCATION) {
|
|
1122
|
-
if (text === "") return "";
|
|
1123
|
-
const lines = text.split("\n");
|
|
1124
|
-
const { headLines, tailLines } = options;
|
|
1125
|
-
const threshold = headLines + tailLines;
|
|
1126
|
-
if (lines.length <= threshold) {
|
|
1127
|
-
return text;
|
|
1128
|
-
}
|
|
1129
|
-
const truncatedCount = lines.length - headLines - tailLines;
|
|
1130
|
-
const head = lines.slice(0, headLines);
|
|
1131
|
-
const tail = lines.slice(lines.length - tailLines);
|
|
1132
|
-
return [...head, "", `[... ${truncatedCount} lines truncated ...]`, "", ...tail].join("\n");
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
// src/output/reshape.ts
|
|
1136
|
-
function safeString(v) {
|
|
1137
|
-
if (typeof v === "object" && v !== null) {
|
|
1138
|
-
return JSON.stringify(v);
|
|
1139
|
-
}
|
|
1140
|
-
return String(v);
|
|
1141
|
-
}
|
|
1142
|
-
function flatObjectToRows(obj, keyLabel = "key", valueLabel = "value") {
|
|
1143
|
-
return Object.entries(obj).map(([k, v]) => ({
|
|
1144
|
-
[keyLabel]: k,
|
|
1145
|
-
[valueLabel]: safeString(v)
|
|
1146
|
-
}));
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
// src/tools/rest/parse-data-str.ts
|
|
1150
|
-
function parseDataStr(outerDataStr, raw) {
|
|
1151
|
-
if (typeof outerDataStr !== "string") {
|
|
1152
|
-
return { ok: true, data: void 0 };
|
|
1153
|
-
}
|
|
1154
|
-
try {
|
|
1155
|
-
return { ok: true, data: JSON.parse(outerDataStr) };
|
|
1156
|
-
} catch (err) {
|
|
1157
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1158
|
-
return { ok: false, status: 200, error: `data_str parse error: ${message}`, raw };
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
// src/tools/rest/health.ts
|
|
1163
|
-
async function healthCheck(session2) {
|
|
1164
|
-
let response;
|
|
1165
|
-
let rawText;
|
|
1166
|
-
try {
|
|
1167
|
-
response = await session2.makeRequest("/show/system/status", {});
|
|
1168
|
-
rawText = await response.text();
|
|
1169
|
-
} catch (err) {
|
|
1170
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1171
|
-
return {
|
|
1172
|
-
ok: false,
|
|
1173
|
-
status: 0,
|
|
1174
|
-
error: message,
|
|
1175
|
-
raw: ""
|
|
1176
|
-
};
|
|
1177
|
-
}
|
|
1178
|
-
if (!response.ok) {
|
|
1179
|
-
return {
|
|
1180
|
-
ok: false,
|
|
1181
|
-
status: response.status,
|
|
1182
|
-
error: `HTTP ${response.status}`,
|
|
1183
|
-
raw: rawText
|
|
1184
|
-
};
|
|
1185
|
-
}
|
|
1186
|
-
let parsed;
|
|
1187
|
-
try {
|
|
1188
|
-
parsed = JSON.parse(rawText);
|
|
1189
|
-
} catch (err) {
|
|
1190
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1191
|
-
return {
|
|
1192
|
-
ok: false,
|
|
1193
|
-
status: 200,
|
|
1194
|
-
error: `JSON parse error: ${message}`,
|
|
1195
|
-
raw: rawText
|
|
1196
|
-
};
|
|
1197
|
-
}
|
|
1198
|
-
const inner = parseDataStr(parsed.data_str, rawText);
|
|
1199
|
-
if (!inner.ok) return inner;
|
|
1200
|
-
const statusMap = inner.data?.status_map ?? {};
|
|
1201
|
-
return {
|
|
1202
|
-
ok: true,
|
|
1203
|
-
data: flatObjectToRows(statusMap, "component", "status")
|
|
1204
|
-
};
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
// src/tools/rest/decode-nested-json.ts
|
|
1208
|
-
function decodeNestedJsonStrings(obj) {
|
|
1209
|
-
return Object.fromEntries(
|
|
1210
|
-
Object.entries(obj).map(([k, v]) => {
|
|
1211
|
-
if (typeof v !== "string") return [k, v];
|
|
1212
|
-
try {
|
|
1213
|
-
const parsed = JSON.parse(v);
|
|
1214
|
-
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
1215
|
-
return [k, decodeNestedJsonStrings(parsed)];
|
|
1216
|
-
}
|
|
1217
|
-
return [k, parsed];
|
|
1218
|
-
} catch {
|
|
1219
|
-
return [k, v];
|
|
1220
|
-
}
|
|
1221
|
-
})
|
|
1222
|
-
);
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
// src/tools/rest/flatten-rank-stats.ts
|
|
1226
|
-
function findTier(tiers, prefix) {
|
|
1227
|
-
for (const [name, data] of Object.entries(tiers)) {
|
|
1228
|
-
if (name === prefix || name.startsWith(`${prefix}.`) || name.startsWith(`${prefix}_`) || name.startsWith(prefix) && /^\d/.test(name.slice(prefix.length))) {
|
|
1229
|
-
if (typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
1230
|
-
return data;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
return void 0;
|
|
1235
|
-
}
|
|
1236
|
-
function overallField(tier, field) {
|
|
1237
|
-
if (!tier) return "";
|
|
1238
|
-
const overall = tier.overall;
|
|
1239
|
-
if (typeof overall !== "object" || overall === null) return "";
|
|
1240
|
-
const value = overall[field];
|
|
1241
|
-
return stringifyValue(value);
|
|
703
|
+
function overallField(tier, field) {
|
|
704
|
+
if (!tier) return "";
|
|
705
|
+
const overall = tier.overall;
|
|
706
|
+
if (typeof overall !== "object" || overall === null) return "";
|
|
707
|
+
const value = overall[field];
|
|
708
|
+
return stringifyValue(value);
|
|
1242
709
|
}
|
|
1243
710
|
function computePercent(used, limit) {
|
|
1244
711
|
if (!used || !limit) return "";
|
|
@@ -1448,15 +915,15 @@ function applyFilters(propertyMap, input5) {
|
|
|
1448
915
|
}
|
|
1449
916
|
|
|
1450
917
|
// src/tools/rest/discover-hm-port.ts
|
|
1451
|
-
var
|
|
918
|
+
var DEFAULT_HM_PORT = 9300;
|
|
1452
919
|
async function discoverHmPort(session2) {
|
|
1453
920
|
const result = await getSystemProperties(session2, { key_pattern: "hm_http_port" });
|
|
1454
|
-
if (!result.ok) return
|
|
921
|
+
if (!result.ok) return DEFAULT_HM_PORT;
|
|
1455
922
|
const rows = result.data;
|
|
1456
923
|
const entry = rows.find((r) => r.property?.includes("hm_http_port"));
|
|
1457
|
-
if (!entry?.value) return
|
|
924
|
+
if (!entry?.value) return DEFAULT_HM_PORT;
|
|
1458
925
|
const port = parseInt(entry.value, 10);
|
|
1459
|
-
return Number.isFinite(port) ? port :
|
|
926
|
+
return Number.isFinite(port) ? port : DEFAULT_HM_PORT;
|
|
1460
927
|
}
|
|
1461
928
|
|
|
1462
929
|
// src/tools/rest/cluster.ts
|
|
@@ -3055,28 +2522,28 @@ async function alterConfiguration(session2, input5) {
|
|
|
3055
2522
|
|
|
3056
2523
|
// src/tools/mutation/alter-table-columns.ts
|
|
3057
2524
|
var import_zod15 = require("zod");
|
|
3058
|
-
var
|
|
3059
|
-
var
|
|
3060
|
-
var
|
|
2525
|
+
var import_prompts2 = require("@inquirer/prompts");
|
|
2526
|
+
var import_picocolors3 = __toESM(require("picocolors"));
|
|
2527
|
+
var import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
|
|
3061
2528
|
|
|
3062
2529
|
// src/approval/checklist.ts
|
|
3063
|
-
var
|
|
3064
|
-
var
|
|
3065
|
-
var DIVIDER =
|
|
2530
|
+
var import_prompts = require("@inquirer/prompts");
|
|
2531
|
+
var import_picocolors2 = __toESM(require("picocolors"));
|
|
2532
|
+
var DIVIDER = import_picocolors2.default.dim("\u2500".repeat(60));
|
|
3066
2533
|
function renderChecklist(header, summary, items) {
|
|
3067
2534
|
const lines = [
|
|
3068
2535
|
"",
|
|
3069
2536
|
DIVIDER,
|
|
3070
|
-
` ${
|
|
2537
|
+
` ${import_picocolors2.default.bold(import_picocolors2.default.yellow(header))}`,
|
|
3071
2538
|
"",
|
|
3072
|
-
` ${
|
|
2539
|
+
` ${import_picocolors2.default.dim("Summary:")} ${summary}`,
|
|
3073
2540
|
"",
|
|
3074
|
-
` ${
|
|
2541
|
+
` ${import_picocolors2.default.bold(`${items.length} proposed column change(s):`)}`,
|
|
3075
2542
|
""
|
|
3076
2543
|
];
|
|
3077
2544
|
for (let i = 0; i < items.length; i++) {
|
|
3078
2545
|
const item = items[i];
|
|
3079
|
-
lines.push(` ${
|
|
2546
|
+
lines.push(` ${import_picocolors2.default.bold(`${i + 1}.`)} ${item.label}`, ` ${import_picocolors2.default.dim(item.description)}`);
|
|
3080
2547
|
}
|
|
3081
2548
|
lines.push("", DIVIDER, "");
|
|
3082
2549
|
return lines.join("\n");
|
|
@@ -3085,7 +2552,7 @@ async function showChecklist(header, summary, items) {
|
|
|
3085
2552
|
const panel = renderChecklist(header, summary, items);
|
|
3086
2553
|
process.stderr.write(panel);
|
|
3087
2554
|
try {
|
|
3088
|
-
const selected = await (0,
|
|
2555
|
+
const selected = await (0, import_prompts.checkbox)({
|
|
3089
2556
|
message: "Select columns to alter (space to toggle, enter to confirm):",
|
|
3090
2557
|
choices: items.map((item, i) => ({
|
|
3091
2558
|
value: i,
|
|
@@ -3120,12 +2587,12 @@ function buildAlterTableSql(tableName, columns) {
|
|
|
3120
2587
|
return `ALTER TABLE ${tableName}
|
|
3121
2588
|
${clauses.join(",\n")}`;
|
|
3122
2589
|
}
|
|
3123
|
-
var SQL_DIVIDER =
|
|
2590
|
+
var SQL_DIVIDER = import_picocolors3.default.dim("\u2500".repeat(60));
|
|
3124
2591
|
async function confirmSqlExecution(sql) {
|
|
3125
2592
|
const panel = [
|
|
3126
2593
|
"",
|
|
3127
2594
|
SQL_DIVIDER,
|
|
3128
|
-
` ${
|
|
2595
|
+
` ${import_picocolors3.default.bold(import_picocolors3.default.yellow("Generated SQL:"))}`,
|
|
3129
2596
|
"",
|
|
3130
2597
|
sql.split("\n").map((line) => ` ${line}`).join("\n"),
|
|
3131
2598
|
"",
|
|
@@ -3134,7 +2601,7 @@ async function confirmSqlExecution(sql) {
|
|
|
3134
2601
|
].join("\n");
|
|
3135
2602
|
process.stderr.write(panel);
|
|
3136
2603
|
try {
|
|
3137
|
-
const response = await (0,
|
|
2604
|
+
const response = await (0, import_prompts2.input)({ message: "Execute? (y/n):" });
|
|
3138
2605
|
return response.trim().toLowerCase() === "y";
|
|
3139
2606
|
} catch {
|
|
3140
2607
|
return false;
|
|
@@ -3200,7 +2667,7 @@ async function alterTableColumns(session2, toolInput) {
|
|
|
3200
2667
|
};
|
|
3201
2668
|
}
|
|
3202
2669
|
function makeAlterTableColumnsTool(session2, deps) {
|
|
3203
|
-
return (0,
|
|
2670
|
+
return (0, import_claude_agent_sdk.tool)(
|
|
3204
2671
|
"kinetica_alter_table_columns",
|
|
3205
2672
|
"Batch multiple column type/property changes on a SINGLE table into one efficient ALTER TABLE statement. Use when recommending 2+ column changes on the same table (e.g., adding DICT encoding to multiple columns, adding TEXT_SEARCH, changing column types). Each column change requires: column_name, new_definition (full type definition with properties and nullability), and description (human-readable reason). The operator selects which columns to alter via interactive checklist, then confirms the generated SQL. For a single column change, use kinetica_execute_mutation_sql directly. Kinetica ALTER TABLE syntax requires repeating the FULL column definition \u2014 properties go INSIDE parentheses: VARCHAR(50, DICT), not VARCHAR(50) DICT.",
|
|
3206
2673
|
AlterTableColumnsSchema.shape,
|
|
@@ -3313,19 +2780,19 @@ function applyOutputPipeline(result) {
|
|
|
3313
2780
|
return truncateOutput(formatOutput(payload));
|
|
3314
2781
|
}
|
|
3315
2782
|
function logMutationAudit(toolName, result, input5) {
|
|
3316
|
-
const statusLabel = result.ok ?
|
|
2783
|
+
const statusLabel = result.ok ? import_picocolors4.default.bold(import_picocolors4.default.green("EXECUTED")) : import_picocolors4.default.bold(import_picocolors4.default.red("FAILED"));
|
|
3317
2784
|
const redacted = redactAuditInput(input5);
|
|
3318
2785
|
const inputSummary = Object.entries(redacted).map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`).join(", ");
|
|
3319
2786
|
const displayName = formatToolName(toolName);
|
|
3320
2787
|
process.stderr.write(
|
|
3321
|
-
` ${
|
|
3322
|
-
${
|
|
2788
|
+
` ${import_picocolors4.default.dim("MUTATION")} ${statusLabel} ${displayName}
|
|
2789
|
+
${import_picocolors4.default.dim(inputSummary)}
|
|
3323
2790
|
|
|
3324
2791
|
`
|
|
3325
2792
|
);
|
|
3326
2793
|
}
|
|
3327
2794
|
function makeAlterSystemPropertiesTool(session2) {
|
|
3328
|
-
return (0,
|
|
2795
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3329
2796
|
"kinetica_alter_system_properties",
|
|
3330
2797
|
"Apply runtime configuration changes to Kinetica via /alter/system/properties. Accepts a map of property name to new value. Captures current values before applying and verifies changes after. Only the 43 documented properties are accepted \u2014 requests with unsupported property names are rejected before any API call. Blocked for safety: ai_api_key, external_files_directory. Common properties (7.2.x): subtask_concurrency_limit, request_timeout, max_get_records_size, concurrent_kernel_execution, max_concurrent_kernels, tcs_per_tom, tps_per_tom, chunk_size, enable_audit, egress_parquet_compression, background_worker_threads. All property names require 'conf.' prefix in /show/system/properties but NOT in /alter/system/properties.",
|
|
3331
2798
|
AlterSystemPropertiesSchema.shape,
|
|
@@ -3339,7 +2806,7 @@ function makeAlterSystemPropertiesTool(session2) {
|
|
|
3339
2806
|
);
|
|
3340
2807
|
}
|
|
3341
2808
|
function makeExecuteMutationSqlTool(session2) {
|
|
3342
|
-
return (0,
|
|
2809
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3343
2810
|
"kinetica_execute_mutation_sql",
|
|
3344
2811
|
"Execute a SQL mutation statement on Kinetica (CREATE INDEX, ALTER TABLE, ALTER SYSTEM SET, REFRESH MATERIALIZED VIEW, etc.). ANALYZE TABLE is NOT supported by Kinetica \u2014 do not call it. DROP, TRUNCATE, DELETE, and UPDATE are always rejected \u2014 even when wrapped in a CTE (WITH ... DELETE/UPDATE). Requires user approval before execution.",
|
|
3345
2812
|
ExecuteMutationSqlSchema.shape,
|
|
@@ -3353,7 +2820,7 @@ function makeExecuteMutationSqlTool(session2) {
|
|
|
3353
2820
|
);
|
|
3354
2821
|
}
|
|
3355
2822
|
function makeAdminRebalanceTool(session2) {
|
|
3356
|
-
return (0,
|
|
2823
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3357
2824
|
"kinetica_admin_rebalance",
|
|
3358
2825
|
"Trigger shard data rebalancing across Kinetica cluster ranks via /admin/rebalance. Options: rebalance_sharded_data, rebalance_unsharded_data, table_includes, table_excludes, aggressiveness (1-5, capped for safety), compact_after_rebalance, compact_only. Captures before/after shard distribution for verification. WARNING: rebalance causes delayed query responses while running \u2014 use low aggressiveness (1-3) during production hours. NOTE: On single-worker-rank clusters, /admin/rebalance returns 'Database must be offline' because there is only one data rank \u2014 rebalance is only meaningful with 2+ worker ranks.",
|
|
3359
2826
|
AdminRebalanceSchema.shape,
|
|
@@ -3367,7 +2834,7 @@ function makeAdminRebalanceTool(session2) {
|
|
|
3367
2834
|
);
|
|
3368
2835
|
}
|
|
3369
2836
|
function makeAlterConfigurationTool(session2) {
|
|
3370
|
-
return (0,
|
|
2837
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3371
2838
|
"kinetica_alter_configuration",
|
|
3372
2839
|
"Replace the full gpudb.conf configuration on the Kinetica host manager via /admin/alter/configuration (port 9300). Requires the complete config_string content \u2014 the entire file is replaced. Captures before/after config summaries for verification. WARNING: This replaces the ENTIRE configuration file. Always read the current config via kinetica_show_configuration first, make targeted changes to specific lines, and submit the full modified content. Never compose a config from scratch. Requires host manager connectivity and user approval.",
|
|
3373
2840
|
AlterConfigurationSchema.shape,
|
|
@@ -3381,7 +2848,7 @@ function makeAlterConfigurationTool(session2) {
|
|
|
3381
2848
|
);
|
|
3382
2849
|
}
|
|
3383
2850
|
function makeHealthCheckTool(session2) {
|
|
3384
|
-
return (0,
|
|
2851
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3385
2852
|
"kinetica_health_check",
|
|
3386
2853
|
"Query Kinetica system health status via /show/system/status. Returns 11 components as rows (component, status): system (cluster status, leader, offline), ranks (per-rank status/mode/accepting_jobs/read_only), hosts (hostname, memory, GPU IDs, sub-service statuses), http_server (connections current/refused, thread capacity), ha_cluster_info/ha_status, graph, text, migrations, triggers, symbols. NOTE: Each status value is a JSON-encoded string \u2014 parse mentally to extract nested fields. Healthy baseline: system.status='running', all ranks rank_status='running' + rank_mode='run', hosts.status='running', http_server.connections.refused=0.",
|
|
3387
2854
|
{},
|
|
@@ -3393,7 +2860,7 @@ function makeHealthCheckTool(session2) {
|
|
|
3393
2860
|
);
|
|
3394
2861
|
}
|
|
3395
2862
|
function makeGetMetricsTool(session2) {
|
|
3396
|
-
return (0,
|
|
2863
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3397
2864
|
"kinetica_get_metrics",
|
|
3398
2865
|
"Retrieve per-rank storage tier metrics from /show/resource/statistics. Returns rows with: rank, ram_used, ram_limit, ram_percent (computed as 'X.Y%' e.g. '9.6%'), persist_used, disk_used, vram_used (all string values). Rank 0 is the head/coordinator node with minimal RAM (~794MB limit) and empty persist/disk/vram fields. Worker ranks (1+) hold the actual data with ~5.6GB RAM limit. Empty string means tier not configured; '0' means configured but unused. Healthy baseline: ram_percent under 80%. Optional node_id to annotate which node was requested.",
|
|
3399
2866
|
{ node_id: import_zod16.z.string().optional() },
|
|
@@ -3405,7 +2872,7 @@ function makeGetMetricsTool(session2) {
|
|
|
3405
2872
|
);
|
|
3406
2873
|
}
|
|
3407
2874
|
function makeClusterStatusTool(session2) {
|
|
3408
|
-
return (0,
|
|
2875
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3409
2876
|
"kinetica_cluster_status",
|
|
3410
2877
|
"Get full cluster overview via 4 sub-calls: (1) /admin/show/cluster/operations \u2014 in_progress flag, percent_complete, rebalance/add/remove status; (2) /admin/show/shards \u2014 summarized as shard distribution per rank (total_shards, rank_count, per-rank shard_count/percent, balanced flag, shard_array_version); (3) /admin/show/alerts on host manager port \u2014 recent alerts (gracefully degrades if unavailable); (4) /admin/show/jobs \u2014 active async jobs as {job_id, status, endpoint} objects. Healthy baseline: operations.in_progress=false, shards.balanced=true, empty alerts/jobs arrays.",
|
|
3411
2878
|
{},
|
|
@@ -3417,7 +2884,7 @@ function makeClusterStatusTool(session2) {
|
|
|
3417
2884
|
);
|
|
3418
2885
|
}
|
|
3419
2886
|
function makeNodeDetailsTool(session2) {
|
|
3420
|
-
return (0,
|
|
2887
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3421
2888
|
"kinetica_node_details",
|
|
3422
2889
|
"Get per-rank resource statistics from /show/resource/statistics (same endpoint as kinetica_get_metrics). Without node_id: returns summary rows for all ranks. With node_id: returns detailed tier + resource-group breakdown for that rank. Per-tier fields: limit, used, free, percent_used, num_evictable_objs, num_unevictable_objs, plus stats: evictions, pins, unpins, watermark_cycles, allocs, reallocs, deallocs (RAM/VRAM tiers) or reads, writes, deletes (PERSIST/DISK tiers). Per-resource-group fields: name, thread_running_count, data (bytes). Rank 0 is the head node \u2014 only RAM tier (no PERSIST/DISK/VRAM), no resource groups.",
|
|
3423
2890
|
{ node_id: import_zod16.z.string().optional() },
|
|
@@ -3429,7 +2896,7 @@ function makeNodeDetailsTool(session2) {
|
|
|
3429
2896
|
);
|
|
3430
2897
|
}
|
|
3431
2898
|
function makeGetLogsTool(session2) {
|
|
3432
|
-
return (0,
|
|
2899
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3433
2900
|
"kinetica_get_logs",
|
|
3434
2901
|
"Retrieve application logs via /admin/show/logs. Sources: kinetica, rank, syslog, gadmin, reveal, workbench. Severity: DEBUG|INFO|WARN|ERROR|FATAL. Time: duration (1h, 30m) or start_time+end_time. WARNING: This endpoint is NOT available on Kinetica 7.2.x \u2014 it always returns a stub response. Use kinetica_execute_sql to query ki_catalog.ki_query_history (for query errors) or ki_catalog.ki_query_span_metrics_all (for operation-level events) instead.",
|
|
3435
2902
|
GetLogsSchema.shape,
|
|
@@ -3442,7 +2909,7 @@ function makeGetLogsTool(session2) {
|
|
|
3442
2909
|
);
|
|
3443
2910
|
}
|
|
3444
2911
|
function makeShowConfigurationTool(session2) {
|
|
3445
|
-
return (0,
|
|
2912
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3446
2913
|
"kinetica_show_configuration",
|
|
3447
2914
|
"Retrieve the full gpudb.conf configuration file from the Kinetica host manager via /admin/show/configuration (port 9300). Returns the raw config_string in INI format with all sections and comments. Use this to inspect the complete server configuration for drift detection, misconfiguration diagnosis, or before proposing config changes via kinetica_alter_configuration. Requires host manager connectivity.",
|
|
3448
2915
|
ShowConfigurationSchema.shape,
|
|
@@ -3455,7 +2922,7 @@ function makeShowConfigurationTool(session2) {
|
|
|
3455
2922
|
);
|
|
3456
2923
|
}
|
|
3457
2924
|
function makeGetSystemPropertiesTool(session2) {
|
|
3458
|
-
return (0,
|
|
2925
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3459
2926
|
"kinetica_get_system_properties",
|
|
3460
2927
|
"Read Kinetica system configuration properties from /show/system/properties. Returns 260+ property rows as {property, value} pairs (all values are strings). Filter by category prefix (e.g., 'conf.tier', 'conf.sql', 'version') or key_pattern substring. Key property groups: conf.tier.* (RAM limits, watermarks, tier strategy), conf.sql.* (parallel_execution, planner timeout), conf.enable_* (authorization, HA, ML, text_search), version.* (gpudb_core_version, compute_engine). Omit both filters to get the full property_map.",
|
|
3461
2928
|
GetSystemPropertiesSchema.shape,
|
|
@@ -3468,7 +2935,7 @@ function makeGetSystemPropertiesTool(session2) {
|
|
|
3468
2935
|
);
|
|
3469
2936
|
}
|
|
3470
2937
|
function makeExecuteSqlTool(session2, catalogSchemas) {
|
|
3471
|
-
return (0,
|
|
2938
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3472
2939
|
"kinetica_execute_sql",
|
|
3473
2940
|
"Execute a read-only SQL query (SELECT, WITH, or EXPLAIN) against Kinetica. Key system tables: ki_catalog.ki_query_history (slow queries \u2014 columns: job_id, query_id, user_name, endpoint, execution_status, error_message, query_text, start_time, stop_time, sql_step_count, refresh_id, resource_group, source_ip), ki_catalog.ki_query_active_all (running queries \u2014 columns: job_id, query_id, user_name, resource_group, source_ip, endpoint, execution_status, error_message, start_time, query_text, sql_step_count, refresh_id, is_mh, is_perpetual, is_cancellable, is_using_timeout, source_rank), ki_catalog.ki_tiered_objects (per-object tier placement \u2014 size, id (string like @schema@oid[col][0] \u2014 NOT a numeric OID, do not join with ki_objects.oid), priority, tier, evictable, locked, pin_count, ram_evictions, persist_evictions, owner_resource_group, source_rank, outer_object; for per-table tier data prefer kinetica_resource_objects with table_names filter), ki_catalog.ki_obj_stat (table sizes \u2014 oid, schema_name, object_name, row_count, bytes_per_row, total_bytes), ki_catalog.ki_columns (column metadata), ki_catalog.ki_objects (object registry with obj_kind R=table/V=view). WARNING: ki_catalog.ki_tables and ki_catalog.ki_version do NOT exist in Kinetica 7.2.x \u2014 use ki_objects and /show/system/status instead.",
|
|
3474
2941
|
ExecuteSqlSchema.shape,
|
|
@@ -3485,7 +2952,7 @@ function makeExecuteSqlTool(session2, catalogSchemas) {
|
|
|
3485
2952
|
);
|
|
3486
2953
|
}
|
|
3487
2954
|
function makeExplainQueryTool(session2) {
|
|
3488
|
-
return (0,
|
|
2955
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3489
2956
|
"kinetica_explain_query",
|
|
3490
2957
|
"Get the execution plan for a SQL statement. Pass the SELECT statement without the EXPLAIN keyword \u2014 it will be added automatically. Returns rows with columns: ID (step number), ENDPOINT (internal REST endpoint used, e.g., /get/records/bycolumn), INPUT_TABLES, OUTPUT_TABLE, DEPENDENCIES (step IDs this depends on; -1 means none). Use to understand which internal operations a query triggers and verify index usage.",
|
|
3491
2958
|
ExplainQuerySchema.shape,
|
|
@@ -3497,7 +2964,7 @@ function makeExplainQueryTool(session2) {
|
|
|
3497
2964
|
);
|
|
3498
2965
|
}
|
|
3499
2966
|
function makeSystemTimingTool(session2) {
|
|
3500
|
-
return (0,
|
|
2967
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3501
2968
|
"kinetica_system_timing",
|
|
3502
2969
|
"Show endpoint response timing statistics from /show/system/timing. Returns the last ~100 API calls as {endpoint, time_in_ms, job_id} rows with sub-millisecond precision. job_id='0' means synchronous execution at head node; non-zero means async. Typical baselines: /show/system/properties <4ms, /show/security <1ms, /execute/sql 14-1300ms, /admin/verifydb 500-4500ms. Use to identify slow API endpoints or confirm whether a specific call was abnormally slow.",
|
|
3503
2970
|
{},
|
|
@@ -3509,7 +2976,7 @@ function makeSystemTimingTool(session2) {
|
|
|
3509
2976
|
);
|
|
3510
2977
|
}
|
|
3511
2978
|
function makeResourceGroupsTool(session2) {
|
|
3512
|
-
return (0,
|
|
2979
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3513
2980
|
"kinetica_resource_groups",
|
|
3514
2981
|
"List resource groups and their configuration from /show/resourcegroups. Returns {groups, rank_usage, info}. Groups include: name, RAM.max_memory, VRAM.GPU0.max_memory, max_cpu_concurrency, max_data, max_scheduling_priority (100=system, 50=default), max_tier_priority. Value '9223372036854775807' (Long.MAX_VALUE) means unlimited. Default groups are kinetica_system_resource_group (priority 100) and kinetica_default_resource_group (priority 50). rank_usage maps rank IDs to JSON-encoded per-group usage (thread_running_count, data bytes, RAM.used, VRAM.GPU0.used). Only worker ranks appear in rank_usage \u2014 rank 0 (head) has no entry. Set show_tier_usage=true for rank-level breakdown.",
|
|
3515
2982
|
ResourceGroupsSchema.shape,
|
|
@@ -3522,7 +2989,7 @@ function makeResourceGroupsTool(session2) {
|
|
|
3522
2989
|
);
|
|
3523
2990
|
}
|
|
3524
2991
|
function makeVerifyDbTool(session2) {
|
|
3525
|
-
return (0,
|
|
2992
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3526
2993
|
"kinetica_verify_db",
|
|
3527
2994
|
"Run a read-only database integrity verification via /admin/verifydb. Checks for null values, persistence issues, and rank0 consistency. Always runs in concurrent_safe mode. Returns {verified_ok: boolean, error_list: [], orphaned_tables_total_size: number}. Healthy: verified_ok=true, empty error_list, orphaned_tables_total_size of -1 (not checked) or 0. WARNING: This is the slowest diagnostic tool \u2014 typically takes 500ms-4500ms. Use sparingly and only when data integrity issues are suspected.",
|
|
3528
2995
|
VerifyDbSchema.shape,
|
|
@@ -3535,7 +3002,7 @@ function makeVerifyDbTool(session2) {
|
|
|
3535
3002
|
);
|
|
3536
3003
|
}
|
|
3537
3004
|
function makeShowSecurityTool(session2) {
|
|
3538
|
-
return (0,
|
|
3005
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3539
3006
|
"kinetica_show_security",
|
|
3540
3007
|
"Show security configuration from /show/security. Returns {types, roles, permissions, resource_groups, info}. When enable_authorization=false (check permissions[''].enable_authorization), types/roles/resource_groups are empty objects \u2014 the tool provides minimal data. When authorization is enabled: types maps usernames to 'internal_user'/'external_user', roles maps role names to member arrays, permissions maps users to permission arrays, resource_groups maps users to group names. Use to audit access control or diagnose authorization failures.",
|
|
3541
3008
|
ShowSecuritySchema.shape,
|
|
@@ -3548,7 +3015,7 @@ function makeShowSecurityTool(session2) {
|
|
|
3548
3015
|
);
|
|
3549
3016
|
}
|
|
3550
3017
|
function makeShowTableTool(session2) {
|
|
3551
|
-
return (0,
|
|
3018
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3552
3019
|
"kinetica_show_table",
|
|
3553
3020
|
"Show table metadata from /show/table. When a specific table_name is provided: returns table metadata with Kinetica-native column types, per-column properties (DICT, TEXT_SEARCH, COMPRESS, etc.), and index definitions from ki_catalog.ki_indexes (index_type, index_columns) \u2014 this is the preferred method for full table inspection. When table_name is omitted or empty: returns schema-level (collection) entries with sizes, but the processed output may be empty \u2014 use kinetica_execute_sql with 'SELECT * FROM ki_catalog.ki_objects WHERE obj_kind = ''R'' ORDER BY schema_name' for reliable table listing instead.",
|
|
3554
3021
|
ShowTableSchema.shape,
|
|
@@ -3561,7 +3028,7 @@ function makeShowTableTool(session2) {
|
|
|
3561
3028
|
);
|
|
3562
3029
|
}
|
|
3563
3030
|
function makeResourceObjectsTool(session2) {
|
|
3564
|
-
return (0,
|
|
3031
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3565
3032
|
"kinetica_resource_objects",
|
|
3566
3033
|
`Show per-rank resource tier usage from /show/resource/objects. Returns {rank_objects: {rank_id: JSON_string_with_objects_array}, info}. Each object has: id (naming convention: @table@oid[column][chunk] for data, AttrIndex[...] for indexes, PKIndex_... for PK hashes), size (bytes), priority (1=system, 5=user, 9=temp), tier ('RAM' or 'PERSIST'), evictable (boolean), locked (boolean), pin_count, ram_evictions, persist_evictions, owner_resource_group. The rank_objects JSON is {"objects": [...]} \u2014 an array nested under an 'objects' key. Only worker ranks have data \u2014 rank 0 (head) has no resource objects. Healthy: zero evictions, zero pin_count at rest.`,
|
|
3567
3034
|
ResourceObjectsSchema.shape,
|
|
@@ -3574,7 +3041,7 @@ function makeResourceObjectsTool(session2) {
|
|
|
3574
3041
|
);
|
|
3575
3042
|
}
|
|
3576
3043
|
function makeHostManagerStatusTool(session2) {
|
|
3577
|
-
return (0,
|
|
3044
|
+
return (0, import_claude_agent_sdk2.tool)(
|
|
3578
3045
|
"kinetica_host_manager_status",
|
|
3579
3046
|
"Query the Kinetica host manager root endpoint (port 9300) for cluster-wide status. Returns a flat key-value map including: version, hostname, system_mode ('run'/'stop'), system_status ('running'/'stopped'), system_idle_time (seconds), cluster_leader (IP), cluster_operation ('none'/'rebalance'/etc.), license_type/status/expiration, per-host and per-rank mode/status/pid, and service statuses (ml, httpd, query_planner, reveal, stats, graph, text). Healthy baseline: system_mode='run', system_status='running', all rankN_status='running', license_status='ok'. Does NOT require Kinetica DB authentication \u2014 queries the host manager service directly.",
|
|
3580
3047
|
{},
|
|
@@ -3726,17 +3193,17 @@ function buildEvidenceChecklist() {
|
|
|
3726
3193
|
|
|
3727
3194
|
// src/agent/report-template.ts
|
|
3728
3195
|
var import_node_fs2 = require("fs");
|
|
3729
|
-
var
|
|
3196
|
+
var import_node_path2 = require("path");
|
|
3730
3197
|
|
|
3731
3198
|
// src/agent/load-playbooks.ts
|
|
3732
|
-
var
|
|
3733
|
-
var
|
|
3199
|
+
var import_promises = require("fs/promises");
|
|
3200
|
+
var import_node_path = require("path");
|
|
3734
3201
|
var import_node_fs = require("fs");
|
|
3735
3202
|
function findPackageRoot(startDir) {
|
|
3736
3203
|
let dir = startDir;
|
|
3737
|
-
while (dir !== (0,
|
|
3738
|
-
if ((0, import_node_fs.existsSync)((0,
|
|
3739
|
-
dir = (0,
|
|
3204
|
+
while (dir !== (0, import_node_path.dirname)(dir)) {
|
|
3205
|
+
if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "package.json"))) return dir;
|
|
3206
|
+
dir = (0, import_node_path.dirname)(dir);
|
|
3740
3207
|
}
|
|
3741
3208
|
return startDir;
|
|
3742
3209
|
}
|
|
@@ -3769,13 +3236,13 @@ function extractBody(raw) {
|
|
|
3769
3236
|
}
|
|
3770
3237
|
async function loadPlaybooks(playbooksDir) {
|
|
3771
3238
|
try {
|
|
3772
|
-
const dir = playbooksDir ?? (0,
|
|
3239
|
+
const dir = playbooksDir ?? (0, import_node_path.join)(findPackageRoot(__dirname), "knowledge", "playbooks");
|
|
3773
3240
|
if (!(0, import_node_fs.existsSync)(dir)) return [];
|
|
3774
|
-
const files = await (0,
|
|
3241
|
+
const files = await (0, import_promises.readdir)(dir);
|
|
3775
3242
|
const mdFiles = files.filter((f) => f.endsWith(".md")).sort();
|
|
3776
3243
|
const playbooks = [];
|
|
3777
3244
|
for (const file of mdFiles) {
|
|
3778
|
-
const raw = await (0,
|
|
3245
|
+
const raw = await (0, import_promises.readFile)((0, import_node_path.join)(dir, file), "utf-8");
|
|
3779
3246
|
const frontmatter = parseFrontmatter(raw);
|
|
3780
3247
|
if (!frontmatter) continue;
|
|
3781
3248
|
playbooks.push({
|
|
@@ -3794,7 +3261,7 @@ async function loadPlaybooks(playbooksDir) {
|
|
|
3794
3261
|
function loadReportTemplateSync() {
|
|
3795
3262
|
try {
|
|
3796
3263
|
const root = findPackageRoot(__dirname);
|
|
3797
|
-
const path2 = (0,
|
|
3264
|
+
const path2 = (0, import_node_path2.join)(root, "knowledge", "templates", "report.md");
|
|
3798
3265
|
return (0, import_node_fs2.readFileSync)(path2, "utf-8");
|
|
3799
3266
|
} catch (err) {
|
|
3800
3267
|
console.warn(`[report-template] failed to load knowledge/templates/report.md: ${String(err)}`);
|
|
@@ -4112,744 +3579,1307 @@ At the end of each investigation, generate a structured markdown report using th
|
|
|
4112
3579
|
\`\`\`markdown
|
|
4113
3580
|
` + REPORT_TEMPLATE + `\`\`\`
|
|
4114
3581
|
|
|
4115
|
-
**CRITICAL:** Use this exact section order. The metadata table comes first. Summary before Remediation. Evidence Collected before Evidence Gaps. Mutations Applied before Post-Remediation Verification. Do NOT reorder sections.
|
|
3582
|
+
**CRITICAL:** Use this exact section order. The metadata table comes first. Summary before Remediation. Evidence Collected before Evidence Gaps. Mutations Applied before Post-Remediation Verification. Do NOT reorder sections.
|
|
3583
|
+
|
|
3584
|
+
**Section order:** Metadata -> Summary -> Remediation -> Root Cause Analysis -> Evidence Collected -> Evidence Gaps -> Mutations Applied -> Post-Remediation Verification
|
|
3585
|
+
|
|
3586
|
+
**Evidence Collected guidance:** Include only the key data points that led to your conclusion. No raw JSON dumps. No full log output. Extract the 3-10 most relevant findings.
|
|
3587
|
+
`;
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
// src/agent/discover-schemas.ts
|
|
3591
|
+
var TARGET_TABLES = [
|
|
3592
|
+
"ki_query_history",
|
|
3593
|
+
"ki_query_active_all",
|
|
3594
|
+
"ki_query_span_metrics_all",
|
|
3595
|
+
"ki_query_workers",
|
|
3596
|
+
"ki_tiered_objects",
|
|
3597
|
+
"ki_obj_stat",
|
|
3598
|
+
"ki_partitions",
|
|
3599
|
+
"ki_objects",
|
|
3600
|
+
"ki_indexes",
|
|
3601
|
+
"ki_periodic_objects",
|
|
3602
|
+
"ki_depend",
|
|
3603
|
+
"ki_users_and_roles",
|
|
3604
|
+
"ki_object_permissions",
|
|
3605
|
+
"ki_load_history",
|
|
3606
|
+
"ki_backup_history",
|
|
3607
|
+
"ki_kafka_lag_info",
|
|
3608
|
+
"ki_columns",
|
|
3609
|
+
"ki_datatypes"
|
|
3610
|
+
];
|
|
3611
|
+
async function discoverCatalogSchemas(session2) {
|
|
3612
|
+
try {
|
|
3613
|
+
const tableList = TARGET_TABLES.map((t) => `'${t}'`).join(", ");
|
|
3614
|
+
const statement = `SELECT table_name, column_name FROM ki_catalog.ki_columns WHERE table_name IN (${tableList}) ORDER BY table_name, column_name`;
|
|
3615
|
+
const result = await executeSql(session2, statement, 1e3);
|
|
3616
|
+
if (!result.ok) {
|
|
3617
|
+
return void 0;
|
|
3618
|
+
}
|
|
3619
|
+
const rows = result.data;
|
|
3620
|
+
if (rows.length === 0) {
|
|
3621
|
+
return void 0;
|
|
3622
|
+
}
|
|
3623
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
3624
|
+
for (const row of rows) {
|
|
3625
|
+
const existing = grouped.get(row.table_name) ?? [];
|
|
3626
|
+
grouped.set(row.table_name, [...existing, row.column_name]);
|
|
3627
|
+
}
|
|
3628
|
+
return { tables: grouped };
|
|
3629
|
+
} catch {
|
|
3630
|
+
return void 0;
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
|
|
3634
|
+
// src/agent/load-references.ts
|
|
3635
|
+
var import_promises2 = require("fs/promises");
|
|
3636
|
+
var import_node_path3 = require("path");
|
|
3637
|
+
var import_node_fs3 = require("fs");
|
|
3638
|
+
async function loadReferences(refsDir) {
|
|
3639
|
+
try {
|
|
3640
|
+
const dir = refsDir ?? (0, import_node_path3.join)(findPackageRoot(__dirname), "knowledge", "references");
|
|
3641
|
+
if (!(0, import_node_fs3.existsSync)(dir)) return [];
|
|
3642
|
+
const files = await (0, import_promises2.readdir)(dir);
|
|
3643
|
+
const mdFiles = files.filter((f) => f.endsWith(".md")).sort();
|
|
3644
|
+
const references = [];
|
|
3645
|
+
for (const file of mdFiles) {
|
|
3646
|
+
const raw = await (0, import_promises2.readFile)((0, import_node_path3.join)(dir, file), "utf-8");
|
|
3647
|
+
const frontmatter = parseFrontmatter(raw);
|
|
3648
|
+
if (!frontmatter) continue;
|
|
3649
|
+
references.push({
|
|
3650
|
+
title: frontmatter.title,
|
|
3651
|
+
category: frontmatter.category,
|
|
3652
|
+
keywords: frontmatter.keywords,
|
|
3653
|
+
body: extractBody(raw),
|
|
3654
|
+
filename: file
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3657
|
+
return references;
|
|
3658
|
+
} catch {
|
|
3659
|
+
return [];
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
// src/agent/prompt-budget.ts
|
|
3664
|
+
var CHARS_PER_TOKEN = 4;
|
|
3665
|
+
var DEFAULT_PROMPT_BUDGET_TOKENS = 2e4;
|
|
3666
|
+
function estimateTokens(text) {
|
|
3667
|
+
if (!text) return 0;
|
|
3668
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
3669
|
+
}
|
|
3670
|
+
function checkPromptBudget(prompt, opts) {
|
|
3671
|
+
const threshold = opts?.warnAtTokens ?? DEFAULT_PROMPT_BUDGET_TOKENS;
|
|
3672
|
+
const tokens = estimateTokens(prompt);
|
|
3673
|
+
return {
|
|
3674
|
+
tokens,
|
|
3675
|
+
chars: prompt ? prompt.length : 0,
|
|
3676
|
+
threshold,
|
|
3677
|
+
overBudget: tokens > threshold
|
|
3678
|
+
};
|
|
3679
|
+
}
|
|
3680
|
+
|
|
3681
|
+
// src/report/save-report.ts
|
|
3682
|
+
var import_promises3 = require("fs/promises");
|
|
3683
|
+
var import_node_path4 = require("path");
|
|
3684
|
+
var import_claude_agent_sdk3 = require("@anthropic-ai/claude-agent-sdk");
|
|
3685
|
+
var import_zod17 = require("zod");
|
|
3686
|
+
var PARTIAL_MARKER = "(PARTIAL -- investigation interrupted)\n\n";
|
|
3687
|
+
function formatTimestamp(date) {
|
|
3688
|
+
const year = date.getUTCFullYear();
|
|
3689
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
3690
|
+
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
3691
|
+
const hours = String(date.getUTCHours()).padStart(2, "0");
|
|
3692
|
+
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
|
|
3693
|
+
const seconds = String(date.getUTCSeconds()).padStart(2, "0");
|
|
3694
|
+
return `${year}-${month}-${day}-${hours}${minutes}${seconds}`;
|
|
3695
|
+
}
|
|
3696
|
+
function makeSaveReportTool() {
|
|
3697
|
+
return (0, import_claude_agent_sdk3.tool)(
|
|
3698
|
+
"save_report",
|
|
3699
|
+
"Save a diagnostic report to disk. Automatically scrubs credentials, creates a timestamped filename in reports/, and auto-creates the directory. Use at the end of each investigation or when interrupted.",
|
|
3700
|
+
{
|
|
3701
|
+
content: import_zod17.z.string().describe("The full markdown diagnostic report content"),
|
|
3702
|
+
partial: import_zod17.z.boolean().optional().describe(
|
|
3703
|
+
"Set to true if the investigation was interrupted (e.g., Ctrl+C). Prepends a PARTIAL marker to the report."
|
|
3704
|
+
)
|
|
3705
|
+
},
|
|
3706
|
+
async (args) => {
|
|
3707
|
+
const rawContent = args.partial ? `${PARTIAL_MARKER}${args.content}` : args.content;
|
|
3708
|
+
const scrubbed = scrubCredentials(rawContent);
|
|
3709
|
+
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
3710
|
+
const filename = `kinetica-diag-${timestamp}.md`;
|
|
3711
|
+
const dir = (0, import_node_path4.resolve)(process.cwd(), "reports");
|
|
3712
|
+
await (0, import_promises3.mkdir)(dir, { recursive: true });
|
|
3713
|
+
const filepath = (0, import_node_path4.join)(dir, filename);
|
|
3714
|
+
await (0, import_promises3.writeFile)(filepath, scrubbed, "utf-8");
|
|
3715
|
+
return {
|
|
3716
|
+
content: [{ type: "text", text: `Report saved: ${filepath}` }]
|
|
3717
|
+
};
|
|
3718
|
+
},
|
|
3719
|
+
{ annotations: { readOnly: true } }
|
|
3720
|
+
);
|
|
3721
|
+
}
|
|
3722
|
+
|
|
3723
|
+
// src/approval/gate.ts
|
|
3724
|
+
var import_prompts3 = require("@inquirer/prompts");
|
|
3725
|
+
|
|
3726
|
+
// src/approval/display.ts
|
|
3727
|
+
var import_picocolors5 = __toESM(require("picocolors"));
|
|
3728
|
+
var IMPACT_FALLBACK = "Impact unknown \u2014 review parameters carefully";
|
|
3729
|
+
var DIVIDER2 = import_picocolors5.default.dim("\u2500".repeat(50));
|
|
3730
|
+
var LABEL_WIDTH = 8;
|
|
3731
|
+
function formatLabel(label) {
|
|
3732
|
+
return ` ${label.padEnd(LABEL_WIDTH)}: `;
|
|
3733
|
+
}
|
|
3734
|
+
function renderApprovalPanel(toolName, toolInput, impact, beforeAfter, reasoningSummary) {
|
|
3735
|
+
const header = import_picocolors5.default.bold(import_picocolors5.default.yellow(" Mutation Approval Required"));
|
|
3736
|
+
const action = `${formatLabel("Action")}${import_picocolors5.default.bold(formatToolName(toolName))}`;
|
|
3737
|
+
const paramEntries = Object.entries(toolInput);
|
|
3738
|
+
const paramSection = paramEntries.length === 0 ? " (no parameters)" : paramEntries.map(([key, value]) => {
|
|
3739
|
+
const formatted = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
3740
|
+
return ` ${import_picocolors5.default.dim(key)}: ${formatted}`;
|
|
3741
|
+
}).join("\n");
|
|
3742
|
+
const impactLine = `${formatLabel("Impact")}${impact ?? IMPACT_FALLBACK}`;
|
|
3743
|
+
const prompt = import_picocolors5.default.dim(
|
|
3744
|
+
`${formatLabel("Respond")}y (proceed) | n (abort) | explain (show reasoning)`
|
|
3745
|
+
);
|
|
3746
|
+
const hasBeforeAfter = beforeAfter !== void 0 && beforeAfter.length > 0;
|
|
3747
|
+
const beforeAfterSection = hasBeforeAfter ? beforeAfter.map(
|
|
3748
|
+
(entry) => ` ${import_picocolors5.default.dim(entry.key)}: ${entry.current} ${import_picocolors5.default.yellow("->")} ${entry.proposed}`
|
|
3749
|
+
).join("\n") : null;
|
|
3750
|
+
const hasReasoning = reasoningSummary !== void 0 && reasoningSummary.length > 0;
|
|
3751
|
+
const reasoningSection = hasReasoning ? `${formatLabel("Reason")}${reasoningSummary}` : null;
|
|
3752
|
+
const sections = ["", DIVIDER2, header, "", action, paramSection, ""];
|
|
3753
|
+
if (beforeAfterSection !== null) {
|
|
3754
|
+
sections.push(beforeAfterSection, "");
|
|
3755
|
+
}
|
|
3756
|
+
if (reasoningSection !== null) {
|
|
3757
|
+
sections.push(reasoningSection, "");
|
|
3758
|
+
}
|
|
3759
|
+
sections.push(impactLine, "", prompt, DIVIDER2, "");
|
|
3760
|
+
return sections.join("\n");
|
|
3761
|
+
}
|
|
3762
|
+
|
|
3763
|
+
// src/approval/gate.ts
|
|
3764
|
+
var DENY_MESSAGE = "User denied this mutation. Skip and continue with the investigation.";
|
|
3765
|
+
var REASONING_FALLBACK = "Reasoning not available. Review the action details above before proceeding.";
|
|
3766
|
+
function createApprovalGate(isReadOnly) {
|
|
3767
|
+
return async (toolName, toolInput, options) => {
|
|
3768
|
+
if (isReadOnly(toolName)) {
|
|
3769
|
+
return {
|
|
3770
|
+
behavior: "allow",
|
|
3771
|
+
updatedInput: toolInput,
|
|
3772
|
+
toolUseID: options.toolUseID
|
|
3773
|
+
};
|
|
3774
|
+
}
|
|
3775
|
+
const impact = options.decisionReason;
|
|
3776
|
+
const panel = renderApprovalPanel(toolName, toolInput, impact);
|
|
3777
|
+
console.error(panel);
|
|
3778
|
+
while (true) {
|
|
3779
|
+
try {
|
|
3780
|
+
const raw = await (0, import_prompts3.input)({ message: "Proceed? (y/n/explain):" }, { signal: options.signal });
|
|
3781
|
+
const normalized = raw.trim().toLowerCase();
|
|
3782
|
+
if (normalized === "y") {
|
|
3783
|
+
process.stderr.write("\n");
|
|
3784
|
+
return {
|
|
3785
|
+
behavior: "allow",
|
|
3786
|
+
updatedInput: toolInput,
|
|
3787
|
+
toolUseID: options.toolUseID
|
|
3788
|
+
};
|
|
3789
|
+
}
|
|
3790
|
+
if (normalized === "n") {
|
|
3791
|
+
process.stderr.write("\n");
|
|
3792
|
+
return {
|
|
3793
|
+
behavior: "deny",
|
|
3794
|
+
message: DENY_MESSAGE,
|
|
3795
|
+
toolUseID: options.toolUseID
|
|
3796
|
+
};
|
|
3797
|
+
}
|
|
3798
|
+
if (normalized === "explain") {
|
|
3799
|
+
const reasoning = options.decisionReason;
|
|
3800
|
+
if (reasoning) {
|
|
3801
|
+
console.error(`
|
|
3802
|
+
Agent reasoning: ${reasoning}
|
|
3803
|
+
`);
|
|
3804
|
+
} else {
|
|
3805
|
+
console.error(`
|
|
3806
|
+
${REASONING_FALLBACK}
|
|
3807
|
+
`);
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
} catch {
|
|
3811
|
+
return {
|
|
3812
|
+
behavior: "deny",
|
|
3813
|
+
message: DENY_MESSAGE,
|
|
3814
|
+
toolUseID: options.toolUseID
|
|
3815
|
+
};
|
|
3816
|
+
}
|
|
3817
|
+
}
|
|
3818
|
+
};
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
// src/agent/turn-gate.ts
|
|
3822
|
+
function createTurnGate() {
|
|
3823
|
+
let resolve2 = () => {
|
|
3824
|
+
};
|
|
3825
|
+
let promise = new Promise((r) => {
|
|
3826
|
+
resolve2 = r;
|
|
3827
|
+
});
|
|
3828
|
+
return Object.freeze({
|
|
3829
|
+
wait: () => promise,
|
|
3830
|
+
open: () => {
|
|
3831
|
+
resolve2();
|
|
3832
|
+
},
|
|
3833
|
+
close: () => {
|
|
3834
|
+
promise = new Promise((r) => {
|
|
3835
|
+
resolve2 = r;
|
|
3836
|
+
});
|
|
3837
|
+
}
|
|
3838
|
+
});
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
// src/output/render-markdown.ts
|
|
3842
|
+
var import_picocolors6 = __toESM(require("picocolors"));
|
|
3843
|
+
var BOLD_RE = /\*\*(.+?)\*\*/g;
|
|
3844
|
+
var HEADING_RE = /^(#{1,6})\s+(.+)$/;
|
|
3845
|
+
function renderMarkdownLine(line) {
|
|
3846
|
+
const headingMatch = HEADING_RE.exec(line);
|
|
3847
|
+
if (headingMatch) {
|
|
3848
|
+
return import_picocolors6.default.bold(headingMatch[2]);
|
|
3849
|
+
}
|
|
3850
|
+
if (line.includes("**")) {
|
|
3851
|
+
return line.replace(BOLD_RE, (_, text) => import_picocolors6.default.bold(text));
|
|
3852
|
+
}
|
|
3853
|
+
return line;
|
|
3854
|
+
}
|
|
3855
|
+
|
|
3856
|
+
// src/output/reformat-tables.ts
|
|
3857
|
+
var SEPARATOR_CELL_RE = /^:?-+:?$/;
|
|
3858
|
+
var BOLD_MARKERS_RE = /\*\*(.+?)\*\*/g;
|
|
3859
|
+
function visualWidth(text) {
|
|
3860
|
+
return text.replace(BOLD_MARKERS_RE, "$1").length;
|
|
3861
|
+
}
|
|
3862
|
+
function isSeparatorCell(cell) {
|
|
3863
|
+
return SEPARATOR_CELL_RE.test(cell);
|
|
3864
|
+
}
|
|
3865
|
+
function isSeparatorRow(cells) {
|
|
3866
|
+
return cells.length > 0 && cells.every(isSeparatorCell);
|
|
3867
|
+
}
|
|
3868
|
+
function parseCells(line) {
|
|
3869
|
+
return line.split("|").slice(1, -1).map((c) => c.trim());
|
|
3870
|
+
}
|
|
3871
|
+
function reformatTableBlock(lines) {
|
|
3872
|
+
const parsed = lines.map(parseCells);
|
|
3873
|
+
const colCount = Math.max(...parsed.map((row) => row.length));
|
|
3874
|
+
const normalised = parsed.map((row) => {
|
|
3875
|
+
const padded = [...row];
|
|
3876
|
+
while (padded.length < colCount) {
|
|
3877
|
+
padded.push("");
|
|
3878
|
+
}
|
|
3879
|
+
return padded;
|
|
3880
|
+
});
|
|
3881
|
+
const colWidths = Array.from(
|
|
3882
|
+
{ length: colCount },
|
|
3883
|
+
(_, col) => Math.max(
|
|
3884
|
+
3,
|
|
3885
|
+
...normalised.filter((row) => !isSeparatorRow(row)).map((row) => visualWidth(row[col]))
|
|
3886
|
+
)
|
|
3887
|
+
);
|
|
3888
|
+
const borderRow = `+${colWidths.map((w) => "-".repeat(w + 2)).join("+")}+`;
|
|
3889
|
+
const bodyRows = normalised.map((row) => {
|
|
3890
|
+
if (isSeparatorRow(row)) {
|
|
3891
|
+
return borderRow;
|
|
3892
|
+
}
|
|
3893
|
+
const cells = row.map((cell, col) => {
|
|
3894
|
+
const rendered = renderMarkdownLine(cell);
|
|
3895
|
+
const pad = colWidths[col] - visualWidth(cell);
|
|
3896
|
+
return rendered + " ".repeat(Math.max(0, pad));
|
|
3897
|
+
});
|
|
3898
|
+
return `| ${cells.join(" | ")} |`;
|
|
3899
|
+
});
|
|
3900
|
+
return [borderRow, ...bodyRows, borderRow];
|
|
3901
|
+
}
|
|
4116
3902
|
|
|
4117
|
-
|
|
3903
|
+
// src/output/streaming-table-aligner.ts
|
|
3904
|
+
var TABLE_LINE_RE = /^\|.*\|$/;
|
|
3905
|
+
function createStreamingTableAligner() {
|
|
3906
|
+
let lineBuffer = "";
|
|
3907
|
+
let tableLines = [];
|
|
3908
|
+
function flushTable() {
|
|
3909
|
+
if (tableLines.length === 0) return "";
|
|
3910
|
+
const aligned = reformatTableBlock(tableLines);
|
|
3911
|
+
tableLines = [];
|
|
3912
|
+
return aligned.join("\n") + "\n";
|
|
3913
|
+
}
|
|
3914
|
+
function push(text) {
|
|
3915
|
+
if (!text) return "";
|
|
3916
|
+
const combined = lineBuffer + text;
|
|
3917
|
+
const segments = combined.split("\n");
|
|
3918
|
+
lineBuffer = segments[segments.length - 1];
|
|
3919
|
+
const completeLines = segments.slice(0, -1);
|
|
3920
|
+
let output = "";
|
|
3921
|
+
for (const line of completeLines) {
|
|
3922
|
+
const trimmed = line.trim();
|
|
3923
|
+
if (TABLE_LINE_RE.test(trimmed)) {
|
|
3924
|
+
tableLines.push(trimmed);
|
|
3925
|
+
} else {
|
|
3926
|
+
output += flushTable();
|
|
3927
|
+
output += renderMarkdownLine(line) + "\n";
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
return output;
|
|
3931
|
+
}
|
|
3932
|
+
function flush() {
|
|
3933
|
+
let output = flushTable();
|
|
3934
|
+
if (lineBuffer) {
|
|
3935
|
+
output += renderMarkdownLine(lineBuffer);
|
|
3936
|
+
lineBuffer = "";
|
|
3937
|
+
}
|
|
3938
|
+
return output;
|
|
3939
|
+
}
|
|
3940
|
+
return Object.freeze({ push, flush });
|
|
3941
|
+
}
|
|
4118
3942
|
|
|
4119
|
-
|
|
4120
|
-
|
|
3943
|
+
// src/output/spinner.ts
|
|
3944
|
+
var import_picocolors7 = __toESM(require("picocolors"));
|
|
3945
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3946
|
+
var FRAME_INTERVAL_MS = 80;
|
|
3947
|
+
var DEFAULT_LABEL = "Thinking";
|
|
3948
|
+
function createSpinner() {
|
|
3949
|
+
let timer = null;
|
|
3950
|
+
let frameIndex = 0;
|
|
3951
|
+
const start = (label = DEFAULT_LABEL) => {
|
|
3952
|
+
if (timer !== null) return;
|
|
3953
|
+
frameIndex = 0;
|
|
3954
|
+
timer = setInterval(() => {
|
|
3955
|
+
const frame = FRAMES[frameIndex % FRAMES.length];
|
|
3956
|
+
process.stderr.write(`\r${import_picocolors7.default.dim(`${frame} ${label}...`)}`);
|
|
3957
|
+
frameIndex += 1;
|
|
3958
|
+
}, FRAME_INTERVAL_MS);
|
|
3959
|
+
timer.unref();
|
|
3960
|
+
};
|
|
3961
|
+
const stop = () => {
|
|
3962
|
+
if (timer === null) return;
|
|
3963
|
+
clearInterval(timer);
|
|
3964
|
+
timer = null;
|
|
3965
|
+
process.stderr.write("\r\x1B[K");
|
|
3966
|
+
};
|
|
3967
|
+
const isRunning = () => timer !== null;
|
|
3968
|
+
return Object.freeze({ start, stop, isRunning });
|
|
4121
3969
|
}
|
|
4122
3970
|
|
|
4123
|
-
// src/agent/
|
|
4124
|
-
var
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
"ki_indexes",
|
|
4134
|
-
"ki_periodic_objects",
|
|
4135
|
-
"ki_depend",
|
|
4136
|
-
"ki_users_and_roles",
|
|
4137
|
-
"ki_object_permissions",
|
|
4138
|
-
"ki_load_history",
|
|
4139
|
-
"ki_backup_history",
|
|
4140
|
-
"ki_kafka_lag_info",
|
|
4141
|
-
"ki_columns",
|
|
4142
|
-
"ki_datatypes"
|
|
3971
|
+
// src/agent/run-agent.ts
|
|
3972
|
+
var MCP_SERVER_NAME = "kinetica-diagnostics";
|
|
3973
|
+
var EXIT_COMMANDS = /* @__PURE__ */ new Set(["exit", "quit", "end", "q"]);
|
|
3974
|
+
var SUPPORTED_MODELS = ["sonnet", "haiku", "opus"];
|
|
3975
|
+
var DEFAULT_AGENT_MODEL = "sonnet";
|
|
3976
|
+
var DEFAULT_MAX_BUDGET_USD = 5;
|
|
3977
|
+
var ALLOWED_TOOL_NAMES = [
|
|
3978
|
+
...DIAGNOSTIC_TOOL_NAMES.map((name) => `mcp__${MCP_SERVER_NAME}__${name}`),
|
|
3979
|
+
`mcp__${MCP_SERVER_NAME}__save_report`,
|
|
3980
|
+
`mcp__${MCP_SERVER_NAME}__${ALTER_TABLE_COLUMNS_TOOL_NAME}`
|
|
4143
3981
|
];
|
|
4144
|
-
|
|
3982
|
+
var DISALLOWED_TOOLS = ["Bash", "Edit", "Write", "MultiEdit"];
|
|
3983
|
+
var ERROR_LABELS = {
|
|
3984
|
+
authentication_failed: "Authentication failed \u2014 check your API key or re-run with --login",
|
|
3985
|
+
billing_error: "Billing error \u2014 check your Anthropic account",
|
|
3986
|
+
rate_limit: "Rate limit exceeded",
|
|
3987
|
+
server_error: "Anthropic API server error",
|
|
3988
|
+
invalid_request: "Invalid API request",
|
|
3989
|
+
max_output_tokens: "Response exceeded maximum output length",
|
|
3990
|
+
unknown: "Unknown API error"
|
|
3991
|
+
};
|
|
3992
|
+
function isExitCommand(text) {
|
|
3993
|
+
return EXIT_COMMANDS.has(text.trim().toLowerCase());
|
|
3994
|
+
}
|
|
3995
|
+
function makeUserMessage(content) {
|
|
3996
|
+
return {
|
|
3997
|
+
type: "user",
|
|
3998
|
+
message: { role: "user", content },
|
|
3999
|
+
parent_tool_use_id: null,
|
|
4000
|
+
session_id: ""
|
|
4001
|
+
};
|
|
4002
|
+
}
|
|
4003
|
+
async function* makeInteractivePrompt(abortController, turnGate, spinner) {
|
|
4004
|
+
while (!abortController.signal.aborted) {
|
|
4005
|
+
try {
|
|
4006
|
+
process.stderr.write("\n");
|
|
4007
|
+
const issue = await (0, import_prompts4.input)({ message: "Describe the issue to investigate:" });
|
|
4008
|
+
process.stderr.write("\n");
|
|
4009
|
+
const trimmed = issue.trim();
|
|
4010
|
+
if (!trimmed) continue;
|
|
4011
|
+
if (isExitCommand(trimmed)) return;
|
|
4012
|
+
spinner.start();
|
|
4013
|
+
yield makeUserMessage(trimmed);
|
|
4014
|
+
break;
|
|
4015
|
+
} catch {
|
|
4016
|
+
return;
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
while (!abortController.signal.aborted) {
|
|
4020
|
+
try {
|
|
4021
|
+
await turnGate.wait();
|
|
4022
|
+
if (abortController.signal.aborted) break;
|
|
4023
|
+
process.stderr.write("\n");
|
|
4024
|
+
const response = await (0, import_prompts4.input)({ message: "You:" });
|
|
4025
|
+
process.stderr.write("\n");
|
|
4026
|
+
const trimmed = response.trim();
|
|
4027
|
+
if (!trimmed) continue;
|
|
4028
|
+
if (isExitCommand(trimmed)) return;
|
|
4029
|
+
turnGate.close();
|
|
4030
|
+
spinner.start();
|
|
4031
|
+
yield makeUserMessage(trimmed);
|
|
4032
|
+
} catch {
|
|
4033
|
+
return;
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
async function displayDegradedStatus(session2) {
|
|
4038
|
+
const [statusResult, alertsResult] = await Promise.all([
|
|
4039
|
+
hostManagerStatus(session2),
|
|
4040
|
+
hostManagerAlerts(session2)
|
|
4041
|
+
]);
|
|
4042
|
+
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"));
|
|
4043
|
+
if (statusResult.ok) {
|
|
4044
|
+
const rows = statusResult.data;
|
|
4045
|
+
const maxKeyLen = rows.reduce((max, r) => Math.max(max, r.key.length), 0);
|
|
4046
|
+
for (const row of rows) {
|
|
4047
|
+
process.stderr.write(` ${import_picocolors8.default.dim(row.key.padEnd(maxKeyLen))} ${row.value}
|
|
4048
|
+
`);
|
|
4049
|
+
}
|
|
4050
|
+
} else {
|
|
4051
|
+
process.stderr.write(` ${import_picocolors8.default.red(`Error: ${statusResult.error}`)}
|
|
4052
|
+
`);
|
|
4053
|
+
}
|
|
4054
|
+
process.stderr.write("\n");
|
|
4055
|
+
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"));
|
|
4056
|
+
if (alertsResult.ok) {
|
|
4057
|
+
const alerts = alertsResult.data;
|
|
4058
|
+
if (alerts.length === 0) {
|
|
4059
|
+
process.stderr.write(` ${import_picocolors8.default.dim("No recent alerts.")}
|
|
4060
|
+
`);
|
|
4061
|
+
} else {
|
|
4062
|
+
for (const alert of alerts) {
|
|
4063
|
+
process.stderr.write(` ${import_picocolors8.default.dim(alert.timestamp)} ${alert.type} ${alert.params}
|
|
4064
|
+
`);
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
} else {
|
|
4068
|
+
process.stderr.write(` ${import_picocolors8.default.dim(`Unavailable: ${alertsResult.error}`)}
|
|
4069
|
+
`);
|
|
4070
|
+
}
|
|
4071
|
+
process.stderr.write("\n");
|
|
4072
|
+
}
|
|
4073
|
+
async function runAgent(session2, kineticaVersion, degraded, model) {
|
|
4074
|
+
const [catalogSchemas, playbooks, references] = await Promise.all([
|
|
4075
|
+
degraded ? Promise.resolve(void 0) : discoverCatalogSchemas(session2),
|
|
4076
|
+
loadPlaybooks(),
|
|
4077
|
+
loadReferences()
|
|
4078
|
+
]);
|
|
4079
|
+
const systemPrompt = buildSystemPrompt(
|
|
4080
|
+
kineticaVersion,
|
|
4081
|
+
catalogSchemas,
|
|
4082
|
+
playbooks,
|
|
4083
|
+
references,
|
|
4084
|
+
degraded
|
|
4085
|
+
);
|
|
4086
|
+
const budget = checkPromptBudget(systemPrompt);
|
|
4087
|
+
if (process.env.DEBUG) {
|
|
4088
|
+
process.stderr.write(
|
|
4089
|
+
import_picocolors8.default.dim(`system prompt: ~${budget.tokens} tokens (${budget.chars} chars)
|
|
4090
|
+
`)
|
|
4091
|
+
);
|
|
4092
|
+
}
|
|
4093
|
+
if (budget.overBudget) {
|
|
4094
|
+
process.stderr.write(
|
|
4095
|
+
import_picocolors8.default.yellow(
|
|
4096
|
+
`\u26A0 system prompt is ~${budget.tokens} tokens (threshold ${budget.threshold}) \u2014 knowledge corpus is getting expensive; consider keyword-based playbook selection.
|
|
4097
|
+
`
|
|
4098
|
+
)
|
|
4099
|
+
);
|
|
4100
|
+
}
|
|
4101
|
+
const diagnosticTools = makeDiagnosticTools(session2, catalogSchemas);
|
|
4102
|
+
const mutationTools = makeMutationTools(session2);
|
|
4103
|
+
const saveReportTool = makeSaveReportTool();
|
|
4104
|
+
const alterTableColumnsTool = makeAlterTableColumnsToolWithDeps(session2);
|
|
4105
|
+
const server = (0, import_claude_agent_sdk4.createSdkMcpServer)({
|
|
4106
|
+
name: MCP_SERVER_NAME,
|
|
4107
|
+
version: "1.0.0",
|
|
4108
|
+
tools: [...diagnosticTools, ...mutationTools, saveReportTool, alterTableColumnsTool]
|
|
4109
|
+
});
|
|
4110
|
+
const spinner = createSpinner();
|
|
4111
|
+
const registry = createDiagnosticRegistry();
|
|
4112
|
+
const approvalGate = createApprovalGate(registry.isReadOnlyTool);
|
|
4113
|
+
const canUseTool = async (toolName, toolInput, options2) => {
|
|
4114
|
+
spinner.stop();
|
|
4115
|
+
return approvalGate(toolName, toolInput, options2);
|
|
4116
|
+
};
|
|
4117
|
+
const abortController = new AbortController();
|
|
4118
|
+
const effectiveModel = model ?? DEFAULT_AGENT_MODEL;
|
|
4119
|
+
const options = {
|
|
4120
|
+
mcpServers: { [MCP_SERVER_NAME]: server },
|
|
4121
|
+
allowedTools: ALLOWED_TOOL_NAMES,
|
|
4122
|
+
disallowedTools: [...DISALLOWED_TOOLS],
|
|
4123
|
+
canUseTool,
|
|
4124
|
+
systemPrompt,
|
|
4125
|
+
model: effectiveModel,
|
|
4126
|
+
fallbackModel: "haiku",
|
|
4127
|
+
thinking: { type: "adaptive" },
|
|
4128
|
+
maxTurns: 100,
|
|
4129
|
+
maxBudgetUsd: DEFAULT_MAX_BUDGET_USD,
|
|
4130
|
+
persistSession: false,
|
|
4131
|
+
includePartialMessages: true,
|
|
4132
|
+
abortController,
|
|
4133
|
+
env: { ...process.env, CLAUDE_AGENT_SDK_CLIENT_APP: "admin-agent" }
|
|
4134
|
+
};
|
|
4135
|
+
if (degraded) {
|
|
4136
|
+
process.stderr.write("\nKinetica Diagnostic Session Ready (DEGRADED MODE)\n");
|
|
4137
|
+
process.stderr.write(
|
|
4138
|
+
"DB engine (port 9191) is unreachable. Only host manager tools are available.\n\n"
|
|
4139
|
+
);
|
|
4140
|
+
await displayDegradedStatus(session2);
|
|
4141
|
+
process.stderr.write("Type 'exit' to end the session.\n\n");
|
|
4142
|
+
} else {
|
|
4143
|
+
process.stderr.write("\nKinetica Diagnostic Session Ready\n");
|
|
4144
|
+
process.stderr.write("Type 'exit' to end the session.\n\n");
|
|
4145
|
+
}
|
|
4146
|
+
const turnGate = createTurnGate();
|
|
4147
|
+
const agentQuery = (0, import_claude_agent_sdk4.query)({
|
|
4148
|
+
prompt: makeInteractivePrompt(abortController, turnGate, spinner),
|
|
4149
|
+
options
|
|
4150
|
+
});
|
|
4151
|
+
process.once("SIGINT", () => {
|
|
4152
|
+
spinner.stop();
|
|
4153
|
+
process.stderr.write("\nInterrupted \u2014 aborting investigation...\n");
|
|
4154
|
+
abortController.abort();
|
|
4155
|
+
agentQuery.close();
|
|
4156
|
+
});
|
|
4157
|
+
let numTurns = 0;
|
|
4158
|
+
let totalCostUsd = 0;
|
|
4159
|
+
let durationMs = 0;
|
|
4160
|
+
let durationApiMs = 0;
|
|
4161
|
+
let cacheReadTokens = 0;
|
|
4162
|
+
let cacheCreationTokens = 0;
|
|
4163
|
+
let lastStreamCharWasNewline = true;
|
|
4164
|
+
const tableAligner = createStreamingTableAligner();
|
|
4165
|
+
let hadNonAbortError = false;
|
|
4145
4166
|
try {
|
|
4146
|
-
const
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4167
|
+
for await (const message of agentQuery) {
|
|
4168
|
+
if (message.type === "stream_event") {
|
|
4169
|
+
const { event: evt } = message;
|
|
4170
|
+
if (evt.type === "content_block_delta" && evt.delta.type === "text_delta") {
|
|
4171
|
+
const text = evt.delta.text ?? "";
|
|
4172
|
+
if (text) {
|
|
4173
|
+
spinner.stop();
|
|
4174
|
+
const output = tableAligner.push(text);
|
|
4175
|
+
if (output) {
|
|
4176
|
+
process.stderr.write(output);
|
|
4177
|
+
lastStreamCharWasNewline = output.endsWith("\n");
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4181
|
+
} else if (message.type === "assistant") {
|
|
4182
|
+
const assistantMsg = message;
|
|
4183
|
+
const remaining = tableAligner.flush();
|
|
4184
|
+
if (remaining) {
|
|
4185
|
+
process.stderr.write(remaining);
|
|
4186
|
+
lastStreamCharWasNewline = remaining.endsWith("\n");
|
|
4187
|
+
}
|
|
4188
|
+
if (!lastStreamCharWasNewline) {
|
|
4189
|
+
process.stderr.write("\n");
|
|
4190
|
+
lastStreamCharWasNewline = true;
|
|
4191
|
+
}
|
|
4192
|
+
if (assistantMsg.message.stop_reason === "end_turn") {
|
|
4193
|
+
spinner.stop();
|
|
4194
|
+
turnGate.open();
|
|
4195
|
+
} else if (assistantMsg.message.stop_reason === "tool_use") {
|
|
4196
|
+
spinner.start("Investigating");
|
|
4197
|
+
} else {
|
|
4198
|
+
spinner.stop();
|
|
4199
|
+
}
|
|
4200
|
+
if (assistantMsg.error) {
|
|
4201
|
+
spinner.stop();
|
|
4202
|
+
const label = ERROR_LABELS[assistantMsg.error] ?? assistantMsg.error;
|
|
4203
|
+
process.stderr.write(import_picocolors8.default.yellow(`
|
|
4204
|
+
API error: ${label}
|
|
4205
|
+
`));
|
|
4206
|
+
turnGate.open();
|
|
4207
|
+
}
|
|
4208
|
+
} else if (message.type === "result") {
|
|
4209
|
+
spinner.stop();
|
|
4210
|
+
const resultMsg = message;
|
|
4211
|
+
numTurns = resultMsg.num_turns;
|
|
4212
|
+
totalCostUsd = resultMsg.total_cost_usd;
|
|
4213
|
+
durationMs = resultMsg.duration_ms;
|
|
4214
|
+
durationApiMs = resultMsg.duration_api_ms;
|
|
4215
|
+
const usages = Object.values(resultMsg.modelUsage ?? {});
|
|
4216
|
+
cacheReadTokens = usages.reduce((sum, u) => sum + (u.cacheReadInputTokens ?? 0), 0);
|
|
4217
|
+
cacheCreationTokens = usages.reduce((sum, u) => sum + (u.cacheCreationInputTokens ?? 0), 0);
|
|
4218
|
+
if (resultMsg.subtype === "error_max_turns") {
|
|
4219
|
+
process.stderr.write(
|
|
4220
|
+
"\nInvestigation hit turn limit. Partial report may be available.\n"
|
|
4221
|
+
);
|
|
4222
|
+
} else if (resultMsg.subtype === "error_max_budget_usd") {
|
|
4223
|
+
process.stderr.write("\nBudget limit reached.\n");
|
|
4224
|
+
} else if (resultMsg.subtype === "error_during_execution") {
|
|
4225
|
+
process.stderr.write(
|
|
4226
|
+
"\nExecution error \u2014 the agent encountered an unrecoverable failure.\n"
|
|
4227
|
+
);
|
|
4228
|
+
} else if (resultMsg.subtype !== "success") {
|
|
4229
|
+
process.stderr.write(`
|
|
4230
|
+
Agent session ended with error: ${resultMsg.subtype}
|
|
4231
|
+
`);
|
|
4232
|
+
}
|
|
4233
|
+
if (resultMsg.permission_denials.length > 0) {
|
|
4234
|
+
const denied = resultMsg.permission_denials.map((d) => d.tool_name).join(", ");
|
|
4235
|
+
process.stderr.write(`
|
|
4236
|
+
Permission denials: ${denied}
|
|
4237
|
+
`);
|
|
4238
|
+
}
|
|
4239
|
+
turnGate.open();
|
|
4240
|
+
} else if (message.type === "system") {
|
|
4241
|
+
const sysMsg = message;
|
|
4242
|
+
if (sysMsg.subtype === "init") {
|
|
4243
|
+
const initMsg = message;
|
|
4244
|
+
const failed = (initMsg.mcp_servers ?? []).filter(
|
|
4245
|
+
(s) => s.name === MCP_SERVER_NAME && s.status !== "connected"
|
|
4246
|
+
);
|
|
4247
|
+
for (const s of failed) {
|
|
4248
|
+
process.stderr.write(
|
|
4249
|
+
`
|
|
4250
|
+
Warning: MCP server "${s.name}" failed to connect (${s.status})
|
|
4251
|
+
`
|
|
4252
|
+
);
|
|
4253
|
+
}
|
|
4254
|
+
} else if (sysMsg.subtype === "api_retry") {
|
|
4255
|
+
const retryMsg = message;
|
|
4256
|
+
const statusStr = retryMsg.error_status !== null ? ` (HTTP ${retryMsg.error_status})` : "";
|
|
4257
|
+
const delaySec = Math.round(retryMsg.retry_delay_ms / 1e3);
|
|
4258
|
+
process.stderr.write(
|
|
4259
|
+
import_picocolors8.default.yellow(
|
|
4260
|
+
`
|
|
4261
|
+
API error${statusStr}. Retrying (attempt ${retryMsg.attempt}/${retryMsg.max_retries}) in ${delaySec}s...
|
|
4262
|
+
`
|
|
4263
|
+
)
|
|
4264
|
+
);
|
|
4265
|
+
} else if (sysMsg.subtype === "compact_boundary") {
|
|
4266
|
+
const compactMsg = message;
|
|
4267
|
+
const preTokens = compactMsg.compact_metadata.pre_tokens;
|
|
4268
|
+
process.stderr.write(
|
|
4269
|
+
`
|
|
4270
|
+
[Context compressed (${preTokens} tokens before compaction) \u2014 investigation continues]
|
|
4271
|
+
`
|
|
4272
|
+
);
|
|
4273
|
+
}
|
|
4274
|
+
} else if (message.type === "rate_limit_event") {
|
|
4275
|
+
const rateMsg = message;
|
|
4276
|
+
const { status, resetsAt } = rateMsg.rate_limit_info;
|
|
4277
|
+
if (status === "rejected") {
|
|
4278
|
+
const resetStr = resetsAt ? ` Resets at ${new Date(resetsAt * 1e3).toISOString()}.` : "";
|
|
4279
|
+
process.stderr.write(`
|
|
4280
|
+
Rate limited \u2014 requests rejected.${resetStr}
|
|
4281
|
+
`);
|
|
4282
|
+
} else if (status === "allowed_warning") {
|
|
4283
|
+
process.stderr.write("\nApproaching rate limit \u2014 investigation may slow.\n");
|
|
4284
|
+
}
|
|
4285
|
+
} else if (message.type === "control_request") {
|
|
4286
|
+
const controlMsg = message;
|
|
4287
|
+
if (controlMsg.request.subtype === "claude_authenticate") {
|
|
4288
|
+
process.stderr.write(import_picocolors8.default.yellow("\nRe-authentication requested by SDK...\n"));
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4151
4291
|
}
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4292
|
+
} catch (error) {
|
|
4293
|
+
spinner.stop();
|
|
4294
|
+
if (error instanceof import_claude_agent_sdk4.AbortError) {
|
|
4295
|
+
hadNonAbortError = false;
|
|
4296
|
+
} else {
|
|
4297
|
+
hadNonAbortError = true;
|
|
4298
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4299
|
+
process.stderr.write(import_picocolors8.default.red(`
|
|
4300
|
+
Agent error: ${message}
|
|
4301
|
+
`));
|
|
4155
4302
|
}
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4303
|
+
} finally {
|
|
4304
|
+
spinner.stop();
|
|
4305
|
+
const remaining = tableAligner.flush();
|
|
4306
|
+
if (remaining) {
|
|
4307
|
+
process.stderr.write(remaining);
|
|
4160
4308
|
}
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
title: frontmatter.title,
|
|
4184
|
-
category: frontmatter.category,
|
|
4185
|
-
keywords: frontmatter.keywords,
|
|
4186
|
-
body: extractBody(raw),
|
|
4187
|
-
filename: file
|
|
4188
|
-
});
|
|
4309
|
+
turnGate.open();
|
|
4310
|
+
const durationSec = Math.round(durationMs / 1e3);
|
|
4311
|
+
const apiPct = durationMs > 0 ? Math.round(durationApiMs / durationMs * 100) : 0;
|
|
4312
|
+
const costStr = totalCostUsd > 0 ? ` Cost: $${totalCostUsd.toFixed(4)}.` : "";
|
|
4313
|
+
if (process.env.DEBUG && (cacheReadTokens > 0 || cacheCreationTokens > 0)) {
|
|
4314
|
+
process.stderr.write(
|
|
4315
|
+
import_picocolors8.default.dim(
|
|
4316
|
+
`cache: ${cacheReadTokens} read / ${cacheCreationTokens} created input tokens (read > 0 confirms the system prompt is served from cache)
|
|
4317
|
+
`
|
|
4318
|
+
)
|
|
4319
|
+
);
|
|
4320
|
+
}
|
|
4321
|
+
if (hadNonAbortError) {
|
|
4322
|
+
process.stderr.write(`
|
|
4323
|
+
Session ended due to error. Turns: ${numTurns}.${costStr}
|
|
4324
|
+
`);
|
|
4325
|
+
} else {
|
|
4326
|
+
process.stderr.write(
|
|
4327
|
+
`
|
|
4328
|
+
Session ended. Turns: ${numTurns}. Duration: ${durationSec}s (${apiPct}% API).${costStr}
|
|
4329
|
+
`
|
|
4330
|
+
);
|
|
4189
4331
|
}
|
|
4190
|
-
return references;
|
|
4191
|
-
} catch {
|
|
4192
|
-
return [];
|
|
4193
4332
|
}
|
|
4194
4333
|
}
|
|
4195
4334
|
|
|
4196
|
-
// src/
|
|
4197
|
-
var
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
chars: prompt ? prompt.length : 0,
|
|
4209
|
-
threshold,
|
|
4210
|
-
overBudget: tokens > threshold
|
|
4211
|
-
};
|
|
4335
|
+
// src/cli/select-model.ts
|
|
4336
|
+
var MODEL_LABELS = {
|
|
4337
|
+
sonnet: "Sonnet \u2014 balanced, best general coding (default)",
|
|
4338
|
+
haiku: "Haiku \u2014 fastest & cheapest, lighter reasoning",
|
|
4339
|
+
opus: "Opus \u2014 deepest reasoning, slower & pricier"
|
|
4340
|
+
};
|
|
4341
|
+
async function selectModel() {
|
|
4342
|
+
return (0, import_prompts5.select)({
|
|
4343
|
+
message: "Select model for this session:",
|
|
4344
|
+
default: DEFAULT_AGENT_MODEL,
|
|
4345
|
+
choices: SUPPORTED_MODELS.map((value) => ({ value, name: MODEL_LABELS[value] }))
|
|
4346
|
+
});
|
|
4212
4347
|
}
|
|
4213
4348
|
|
|
4214
|
-
// src/
|
|
4215
|
-
var
|
|
4216
|
-
var import_node_path5 = require("path");
|
|
4217
|
-
var import_claude_agent_sdk4 = require("@anthropic-ai/claude-agent-sdk");
|
|
4218
|
-
var import_zod17 = require("zod");
|
|
4219
|
-
var PARTIAL_MARKER = "(PARTIAL -- investigation interrupted)\n\n";
|
|
4220
|
-
function formatTimestamp(date) {
|
|
4221
|
-
const year = date.getUTCFullYear();
|
|
4222
|
-
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
4223
|
-
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
4224
|
-
const hours = String(date.getUTCHours()).padStart(2, "0");
|
|
4225
|
-
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
|
|
4226
|
-
const seconds = String(date.getUTCSeconds()).padStart(2, "0");
|
|
4227
|
-
return `${year}-${month}-${day}-${hours}${minutes}${seconds}`;
|
|
4228
|
-
}
|
|
4229
|
-
function makeSaveReportTool() {
|
|
4230
|
-
return (0, import_claude_agent_sdk4.tool)(
|
|
4231
|
-
"save_report",
|
|
4232
|
-
"Save a diagnostic report to disk. Automatically scrubs credentials, creates a timestamped filename in reports/, and auto-creates the directory. Use at the end of each investigation or when interrupted.",
|
|
4233
|
-
{
|
|
4234
|
-
content: import_zod17.z.string().describe("The full markdown diagnostic report content"),
|
|
4235
|
-
partial: import_zod17.z.boolean().optional().describe(
|
|
4236
|
-
"Set to true if the investigation was interrupted (e.g., Ctrl+C). Prepends a PARTIAL marker to the report."
|
|
4237
|
-
)
|
|
4238
|
-
},
|
|
4239
|
-
async (args) => {
|
|
4240
|
-
const rawContent = args.partial ? `${PARTIAL_MARKER}${args.content}` : args.content;
|
|
4241
|
-
const scrubbed = scrubCredentials(rawContent);
|
|
4242
|
-
const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
|
|
4243
|
-
const filename = `kinetica-diag-${timestamp}.md`;
|
|
4244
|
-
const dir = (0, import_node_path5.resolve)(process.cwd(), "reports");
|
|
4245
|
-
await (0, import_promises4.mkdir)(dir, { recursive: true });
|
|
4246
|
-
const filepath = (0, import_node_path5.join)(dir, filename);
|
|
4247
|
-
await (0, import_promises4.writeFile)(filepath, scrubbed, "utf-8");
|
|
4248
|
-
return {
|
|
4249
|
-
content: [{ type: "text", text: `Report saved: ${filepath}` }]
|
|
4250
|
-
};
|
|
4251
|
-
},
|
|
4252
|
-
{ annotations: { readOnly: true } }
|
|
4253
|
-
);
|
|
4254
|
-
}
|
|
4349
|
+
// src/auth/preflight.ts
|
|
4350
|
+
var import_claude_agent_sdk5 = require("@anthropic-ai/claude-agent-sdk");
|
|
4255
4351
|
|
|
4256
|
-
// src/
|
|
4257
|
-
var
|
|
4352
|
+
// src/auth/oauth-flow.ts
|
|
4353
|
+
var import_picocolors9 = __toESM(require("picocolors"));
|
|
4258
4354
|
|
|
4259
|
-
// src/
|
|
4260
|
-
var
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4355
|
+
// src/auth/open-browser.ts
|
|
4356
|
+
var import_child_process = require("child_process");
|
|
4357
|
+
function openBrowser(url) {
|
|
4358
|
+
try {
|
|
4359
|
+
const platform = process.platform;
|
|
4360
|
+
const { command, args } = platform === "darwin" ? { command: "open", args: [url] } : platform === "win32" ? { command: "cmd", args: ["/c", "start", "", url] } : { command: "xdg-open", args: [url] };
|
|
4361
|
+
const child = (0, import_child_process.spawn)(command, args, { detached: true, stdio: "ignore" });
|
|
4362
|
+
child.unref();
|
|
4363
|
+
return true;
|
|
4364
|
+
} catch {
|
|
4365
|
+
return false;
|
|
4366
|
+
}
|
|
4266
4367
|
}
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
const formatted = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
4273
|
-
return ` ${import_picocolors10.default.dim(key)}: ${formatted}`;
|
|
4274
|
-
}).join("\n");
|
|
4275
|
-
const impactLine = `${formatLabel("Impact")}${impact ?? IMPACT_FALLBACK}`;
|
|
4276
|
-
const prompt = import_picocolors10.default.dim(
|
|
4277
|
-
`${formatLabel("Respond")}y (proceed) | n (abort) | explain (show reasoning)`
|
|
4278
|
-
);
|
|
4279
|
-
const hasBeforeAfter = beforeAfter !== void 0 && beforeAfter.length > 0;
|
|
4280
|
-
const beforeAfterSection = hasBeforeAfter ? beforeAfter.map(
|
|
4281
|
-
(entry) => ` ${import_picocolors10.default.dim(entry.key)}: ${entry.current} ${import_picocolors10.default.yellow("->")} ${entry.proposed}`
|
|
4282
|
-
).join("\n") : null;
|
|
4283
|
-
const hasReasoning = reasoningSummary !== void 0 && reasoningSummary.length > 0;
|
|
4284
|
-
const reasoningSection = hasReasoning ? `${formatLabel("Reason")}${reasoningSummary}` : null;
|
|
4285
|
-
const sections = ["", DIVIDER2, header, "", action, paramSection, ""];
|
|
4286
|
-
if (beforeAfterSection !== null) {
|
|
4287
|
-
sections.push(beforeAfterSection, "");
|
|
4368
|
+
|
|
4369
|
+
// src/auth/oauth-flow.ts
|
|
4370
|
+
async function resolveAuthentication(agentQuery, options) {
|
|
4371
|
+
if (options.hasApiKey && !options.forceLogin) {
|
|
4372
|
+
return { method: "api_key" };
|
|
4288
4373
|
}
|
|
4289
|
-
|
|
4290
|
-
|
|
4374
|
+
const query3 = agentQuery;
|
|
4375
|
+
try {
|
|
4376
|
+
const { manualUrl, automaticUrl } = await query3.claudeAuthenticate(options.loginWithClaudeAi);
|
|
4377
|
+
const opened = openBrowser(automaticUrl);
|
|
4378
|
+
if (opened) {
|
|
4379
|
+
process.stderr.write(import_picocolors9.default.dim("Browser opened for login. Waiting for authentication...\n"));
|
|
4380
|
+
} else {
|
|
4381
|
+
process.stderr.write(`
|
|
4382
|
+
Open this URL in your browser to log in:
|
|
4383
|
+
${import_picocolors9.default.bold(manualUrl)}
|
|
4384
|
+
|
|
4385
|
+
`);
|
|
4386
|
+
process.stderr.write(import_picocolors9.default.dim("Waiting for browser login to complete...\n"));
|
|
4387
|
+
}
|
|
4388
|
+
await query3.claudeOAuthWaitForCompletion();
|
|
4389
|
+
return { method: "oauth" };
|
|
4390
|
+
} catch (err) {
|
|
4391
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4392
|
+
process.stderr.write(
|
|
4393
|
+
import_picocolors9.default.yellow(`
|
|
4394
|
+
Warning: OAuth login failed (${message}). SDK may retry automatically.
|
|
4395
|
+
`)
|
|
4396
|
+
);
|
|
4397
|
+
return { method: "oauth" };
|
|
4291
4398
|
}
|
|
4292
|
-
sections.push(impactLine, "", prompt, DIVIDER2, "");
|
|
4293
|
-
return sections.join("\n");
|
|
4294
4399
|
}
|
|
4295
4400
|
|
|
4296
|
-
// src/
|
|
4297
|
-
var
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4401
|
+
// src/auth/preflight.ts
|
|
4402
|
+
var PROBE_TIMEOUT_MS = 1e4;
|
|
4403
|
+
async function probeCachedCredentials(authQuery) {
|
|
4404
|
+
try {
|
|
4405
|
+
const info = await Promise.race([
|
|
4406
|
+
authQuery.accountInfo(),
|
|
4407
|
+
new Promise(
|
|
4408
|
+
(_, reject) => setTimeout(() => reject(new Error("Probe timed out")), PROBE_TIMEOUT_MS)
|
|
4409
|
+
)
|
|
4410
|
+
]);
|
|
4411
|
+
if (info.email || info.apiKeySource) {
|
|
4412
|
+
return info;
|
|
4307
4413
|
}
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
toolUseID: options.toolUseID
|
|
4348
|
-
};
|
|
4414
|
+
return null;
|
|
4415
|
+
} catch {
|
|
4416
|
+
return null;
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
async function authenticateAnthropic(options) {
|
|
4420
|
+
const hasApiKey = Boolean(process.env.ANTHROPIC_API_KEY);
|
|
4421
|
+
if (hasApiKey && !options.forceLogin) {
|
|
4422
|
+
return { method: "api_key" };
|
|
4423
|
+
}
|
|
4424
|
+
if (!process.stdin.isTTY) {
|
|
4425
|
+
throw new Error(
|
|
4426
|
+
"No ANTHROPIC_API_KEY set and terminal is non-interactive. Set ANTHROPIC_API_KEY or run in an interactive terminal with --login."
|
|
4427
|
+
);
|
|
4428
|
+
}
|
|
4429
|
+
const env = options.forceLogin ? (() => {
|
|
4430
|
+
const { ANTHROPIC_API_KEY: _stripped, ...rest } = process.env;
|
|
4431
|
+
return { ...rest, CLAUDE_AGENT_SDK_CLIENT_APP: "admin-agent" };
|
|
4432
|
+
})() : { ...process.env, CLAUDE_AGENT_SDK_CLIENT_APP: "admin-agent" };
|
|
4433
|
+
const abortController = new AbortController();
|
|
4434
|
+
async function* hangingPrompt() {
|
|
4435
|
+
await new Promise(() => {
|
|
4436
|
+
});
|
|
4437
|
+
}
|
|
4438
|
+
const authQuery = (0, import_claude_agent_sdk5.query)({
|
|
4439
|
+
prompt: hangingPrompt(),
|
|
4440
|
+
options: {
|
|
4441
|
+
persistSession: false,
|
|
4442
|
+
abortController,
|
|
4443
|
+
env,
|
|
4444
|
+
...options.loginMethod ? { forceLoginMethod: options.loginMethod } : {},
|
|
4445
|
+
...options.loginOrgUUID ? { forceLoginOrgUUID: options.loginOrgUUID } : {}
|
|
4446
|
+
}
|
|
4447
|
+
});
|
|
4448
|
+
try {
|
|
4449
|
+
if (!options.forceLogin) {
|
|
4450
|
+
const cached = await probeCachedCredentials(authQuery);
|
|
4451
|
+
if (cached) {
|
|
4452
|
+
return { method: "oauth", email: cached.email };
|
|
4349
4453
|
}
|
|
4350
4454
|
}
|
|
4351
|
-
|
|
4455
|
+
return await resolveAuthentication(authQuery, {
|
|
4456
|
+
forceLogin: options.forceLogin,
|
|
4457
|
+
loginWithClaudeAi: (options.loginMethod ?? "claudeai") === "claudeai",
|
|
4458
|
+
hasApiKey
|
|
4459
|
+
});
|
|
4460
|
+
} finally {
|
|
4461
|
+
abortController.abort();
|
|
4462
|
+
await authQuery.return().catch(() => {
|
|
4463
|
+
});
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
|
|
4467
|
+
// src/auth/logout.ts
|
|
4468
|
+
var import_child_process2 = require("child_process");
|
|
4469
|
+
var import_node_module = require("module");
|
|
4470
|
+
var import_node_path5 = __toESM(require("path"));
|
|
4471
|
+
var import_util = require("util");
|
|
4472
|
+
var execFileAsync = (0, import_util.promisify)(import_child_process2.execFile);
|
|
4473
|
+
function resolveSdkCliPath() {
|
|
4474
|
+
const require_ = (0, import_node_module.createRequire)(__filename);
|
|
4475
|
+
return import_node_path5.default.join(import_node_path5.default.dirname(require_.resolve("@anthropic-ai/claude-agent-sdk")), "cli.js");
|
|
4476
|
+
}
|
|
4477
|
+
async function logout() {
|
|
4478
|
+
try {
|
|
4479
|
+
const sdkCliPath = resolveSdkCliPath();
|
|
4480
|
+
const { stdout, stderr } = await execFileAsync(process.execPath, [
|
|
4481
|
+
sdkCliPath,
|
|
4482
|
+
"auth",
|
|
4483
|
+
"logout"
|
|
4484
|
+
]);
|
|
4485
|
+
const output = (stdout || stderr || "").trim();
|
|
4486
|
+
return { success: true, message: output || "Logged out successfully." };
|
|
4487
|
+
} catch (err) {
|
|
4488
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4489
|
+
return { success: false, message: `Logout failed: ${message}` };
|
|
4490
|
+
}
|
|
4352
4491
|
}
|
|
4353
4492
|
|
|
4354
|
-
// src/
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4493
|
+
// src/session/env-file.ts
|
|
4494
|
+
var import_fs2 = require("fs");
|
|
4495
|
+
var import_promises4 = require("fs/promises");
|
|
4496
|
+
var import_path2 = require("path");
|
|
4497
|
+
var import_prompts6 = require("@inquirer/prompts");
|
|
4498
|
+
var import_picocolors10 = __toESM(require("picocolors"));
|
|
4499
|
+
function parseEnvContent(content) {
|
|
4500
|
+
const entries = [];
|
|
4501
|
+
for (const line of content.split("\n")) {
|
|
4502
|
+
const trimmed = line.trim();
|
|
4503
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
4504
|
+
const match = /^([A-Za-z_]\w*)=(.*)/.exec(trimmed);
|
|
4505
|
+
if (!match) continue;
|
|
4506
|
+
const key = match[1];
|
|
4507
|
+
let value = match[2];
|
|
4508
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
4509
|
+
value = value.slice(1, -1).replace(/\\(["\\])/g, "$1");
|
|
4510
|
+
} else if (value.startsWith("'") && value.endsWith("'") && value.length >= 2) {
|
|
4511
|
+
value = value.slice(1, -1);
|
|
4370
4512
|
}
|
|
4371
|
-
|
|
4513
|
+
entries.push([key, value]);
|
|
4514
|
+
}
|
|
4515
|
+
return new Map(entries);
|
|
4372
4516
|
}
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
if (headingMatch) {
|
|
4381
|
-
return import_picocolors11.default.bold(headingMatch[2]);
|
|
4517
|
+
var FORBIDDEN_VALUE_CHARS = /[\n\r\0]/;
|
|
4518
|
+
var REQUIRES_QUOTING = /[\s"'#]/;
|
|
4519
|
+
function escapeEnvValue(value) {
|
|
4520
|
+
if (FORBIDDEN_VALUE_CHARS.test(value)) {
|
|
4521
|
+
throw new Error(
|
|
4522
|
+
"Env value contains a forbidden character (newline or null byte); refusing to write"
|
|
4523
|
+
);
|
|
4382
4524
|
}
|
|
4383
|
-
if (
|
|
4384
|
-
return
|
|
4525
|
+
if (!REQUIRES_QUOTING.test(value)) {
|
|
4526
|
+
return value;
|
|
4385
4527
|
}
|
|
4386
|
-
|
|
4528
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
4529
|
+
return `"${escaped}"`;
|
|
4387
4530
|
}
|
|
4531
|
+
var ENV_TEMPLATE = `# Anthropic API key (optional \u2014 if not set, OAuth login via browser is used)
|
|
4532
|
+
ANTHROPIC_API_KEY=
|
|
4388
4533
|
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
function
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
}
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
const padded = [...row];
|
|
4409
|
-
while (padded.length < colCount) {
|
|
4410
|
-
padded.push("");
|
|
4534
|
+
# Kinetica connection details (prompted interactively if not set)
|
|
4535
|
+
# If password is omitted, the agent will prompt for it at startup
|
|
4536
|
+
KINETICA_URL={url}
|
|
4537
|
+
KINETICA_USER={user}
|
|
4538
|
+
KINETICA_PASS=
|
|
4539
|
+
`;
|
|
4540
|
+
function buildEnvContent(url, user, existingContent) {
|
|
4541
|
+
const safeUrl = escapeEnvValue(url);
|
|
4542
|
+
const safeUser = escapeEnvValue(user);
|
|
4543
|
+
if (!existingContent?.trim()) {
|
|
4544
|
+
return ENV_TEMPLATE.replace("{url}", safeUrl).replace("{user}", safeUser);
|
|
4545
|
+
}
|
|
4546
|
+
const lines = existingContent.split("\n");
|
|
4547
|
+
let urlReplaced = false;
|
|
4548
|
+
let userReplaced = false;
|
|
4549
|
+
const updated = lines.map((line) => {
|
|
4550
|
+
if (/^KINETICA_URL=/.exec(line)) {
|
|
4551
|
+
urlReplaced = true;
|
|
4552
|
+
return `KINETICA_URL=${safeUrl}`;
|
|
4411
4553
|
}
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
{ length: colCount },
|
|
4416
|
-
(_, col) => Math.max(
|
|
4417
|
-
3,
|
|
4418
|
-
...normalised.filter((row) => !isSeparatorRow(row)).map((row) => visualWidth(row[col]))
|
|
4419
|
-
)
|
|
4420
|
-
);
|
|
4421
|
-
const borderRow = `+${colWidths.map((w) => "-".repeat(w + 2)).join("+")}+`;
|
|
4422
|
-
const bodyRows = normalised.map((row) => {
|
|
4423
|
-
if (isSeparatorRow(row)) {
|
|
4424
|
-
return borderRow;
|
|
4554
|
+
if (/^KINETICA_USER=/.exec(line)) {
|
|
4555
|
+
userReplaced = true;
|
|
4556
|
+
return `KINETICA_USER=${safeUser}`;
|
|
4425
4557
|
}
|
|
4426
|
-
|
|
4427
|
-
const rendered = renderMarkdownLine(cell);
|
|
4428
|
-
const pad = colWidths[col] - visualWidth(cell);
|
|
4429
|
-
return rendered + " ".repeat(Math.max(0, pad));
|
|
4430
|
-
});
|
|
4431
|
-
return `| ${cells.join(" | ")} |`;
|
|
4558
|
+
return line;
|
|
4432
4559
|
});
|
|
4433
|
-
|
|
4560
|
+
if (!urlReplaced) updated.push(`KINETICA_URL=${safeUrl}`);
|
|
4561
|
+
if (!userReplaced) updated.push(`KINETICA_USER=${safeUser}`);
|
|
4562
|
+
return updated.join("\n");
|
|
4434
4563
|
}
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
const aligned = reformatTableBlock(tableLines);
|
|
4444
|
-
tableLines = [];
|
|
4445
|
-
return aligned.join("\n") + "\n";
|
|
4446
|
-
}
|
|
4447
|
-
function push(text) {
|
|
4448
|
-
if (!text) return "";
|
|
4449
|
-
const combined = lineBuffer + text;
|
|
4450
|
-
const segments = combined.split("\n");
|
|
4451
|
-
lineBuffer = segments[segments.length - 1];
|
|
4452
|
-
const completeLines = segments.slice(0, -1);
|
|
4453
|
-
let output = "";
|
|
4454
|
-
for (const line of completeLines) {
|
|
4455
|
-
const trimmed = line.trim();
|
|
4456
|
-
if (TABLE_LINE_RE.test(trimmed)) {
|
|
4457
|
-
tableLines.push(trimmed);
|
|
4458
|
-
} else {
|
|
4459
|
-
output += flushTable();
|
|
4460
|
-
output += renderMarkdownLine(line) + "\n";
|
|
4564
|
+
function loadEnvFile(dir, env = process.env) {
|
|
4565
|
+
try {
|
|
4566
|
+
const filePath = (0, import_path2.join)(dir ?? process.cwd(), ".env");
|
|
4567
|
+
const content = (0, import_fs2.readFileSync)(filePath, "utf8");
|
|
4568
|
+
const parsed = parseEnvContent(content);
|
|
4569
|
+
for (const [key, value] of parsed) {
|
|
4570
|
+
if (env[key] === void 0 && value !== "") {
|
|
4571
|
+
env[key] = value;
|
|
4461
4572
|
}
|
|
4462
4573
|
}
|
|
4463
|
-
|
|
4464
|
-
}
|
|
4465
|
-
function flush() {
|
|
4466
|
-
let output = flushTable();
|
|
4467
|
-
if (lineBuffer) {
|
|
4468
|
-
output += renderMarkdownLine(lineBuffer);
|
|
4469
|
-
lineBuffer = "";
|
|
4470
|
-
}
|
|
4471
|
-
return output;
|
|
4574
|
+
} catch {
|
|
4472
4575
|
}
|
|
4473
|
-
return Object.freeze({ push, flush });
|
|
4474
|
-
}
|
|
4475
|
-
|
|
4476
|
-
// src/output/spinner.ts
|
|
4477
|
-
var import_picocolors12 = __toESM(require("picocolors"));
|
|
4478
|
-
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
4479
|
-
var FRAME_INTERVAL_MS = 80;
|
|
4480
|
-
var DEFAULT_LABEL = "Thinking";
|
|
4481
|
-
function createSpinner() {
|
|
4482
|
-
let timer = null;
|
|
4483
|
-
let frameIndex = 0;
|
|
4484
|
-
const start = (label = DEFAULT_LABEL) => {
|
|
4485
|
-
if (timer !== null) return;
|
|
4486
|
-
frameIndex = 0;
|
|
4487
|
-
timer = setInterval(() => {
|
|
4488
|
-
const frame = FRAMES[frameIndex % FRAMES.length];
|
|
4489
|
-
process.stderr.write(`\r${import_picocolors12.default.dim(`${frame} ${label}...`)}`);
|
|
4490
|
-
frameIndex += 1;
|
|
4491
|
-
}, FRAME_INTERVAL_MS);
|
|
4492
|
-
timer.unref();
|
|
4493
|
-
};
|
|
4494
|
-
const stop = () => {
|
|
4495
|
-
if (timer === null) return;
|
|
4496
|
-
clearInterval(timer);
|
|
4497
|
-
timer = null;
|
|
4498
|
-
process.stderr.write("\r\x1B[K");
|
|
4499
|
-
};
|
|
4500
|
-
const isRunning = () => timer !== null;
|
|
4501
|
-
return Object.freeze({ start, stop, isRunning });
|
|
4502
|
-
}
|
|
4503
|
-
|
|
4504
|
-
// src/agent/run-agent.ts
|
|
4505
|
-
var MCP_SERVER_NAME = "kinetica-diagnostics";
|
|
4506
|
-
var EXIT_COMMANDS = /* @__PURE__ */ new Set(["exit", "quit", "end", "q"]);
|
|
4507
|
-
var SUPPORTED_MODELS = ["sonnet", "haiku", "opus"];
|
|
4508
|
-
var DEFAULT_AGENT_MODEL = "sonnet";
|
|
4509
|
-
var DEFAULT_MAX_BUDGET_USD = 5;
|
|
4510
|
-
var ALLOWED_TOOL_NAMES = [
|
|
4511
|
-
...DIAGNOSTIC_TOOL_NAMES.map((name) => `mcp__${MCP_SERVER_NAME}__${name}`),
|
|
4512
|
-
`mcp__${MCP_SERVER_NAME}__save_report`,
|
|
4513
|
-
`mcp__${MCP_SERVER_NAME}__${ALTER_TABLE_COLUMNS_TOOL_NAME}`
|
|
4514
|
-
];
|
|
4515
|
-
var DISALLOWED_TOOLS = ["Bash", "Edit", "Write", "MultiEdit"];
|
|
4516
|
-
var ERROR_LABELS = {
|
|
4517
|
-
authentication_failed: "Authentication failed \u2014 check your API key or re-run with --login",
|
|
4518
|
-
billing_error: "Billing error \u2014 check your Anthropic account",
|
|
4519
|
-
rate_limit: "Rate limit exceeded",
|
|
4520
|
-
server_error: "Anthropic API server error",
|
|
4521
|
-
invalid_request: "Invalid API request",
|
|
4522
|
-
max_output_tokens: "Response exceeded maximum output length",
|
|
4523
|
-
unknown: "Unknown API error"
|
|
4524
|
-
};
|
|
4525
|
-
function isExitCommand(text) {
|
|
4526
|
-
return EXIT_COMMANDS.has(text.trim().toLowerCase());
|
|
4527
|
-
}
|
|
4528
|
-
function makeUserMessage(content) {
|
|
4529
|
-
return {
|
|
4530
|
-
type: "user",
|
|
4531
|
-
message: { role: "user", content },
|
|
4532
|
-
parent_tool_use_id: null,
|
|
4533
|
-
session_id: ""
|
|
4534
|
-
};
|
|
4535
4576
|
}
|
|
4536
|
-
async function
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
yield makeUserMessage(trimmed);
|
|
4547
|
-
break;
|
|
4548
|
-
} catch {
|
|
4549
|
-
return;
|
|
4550
|
-
}
|
|
4551
|
-
}
|
|
4552
|
-
while (!abortController.signal.aborted) {
|
|
4577
|
+
async function offerSaveCredentials(url, user, dir) {
|
|
4578
|
+
if (!process.stdin.isTTY) return;
|
|
4579
|
+
try {
|
|
4580
|
+
const shouldSave = await (0, import_prompts6.confirm)({
|
|
4581
|
+
message: "Save KINETICA_URL and KINETICA_USER to .env? (password is never saved)",
|
|
4582
|
+
default: true
|
|
4583
|
+
});
|
|
4584
|
+
if (!shouldSave) return;
|
|
4585
|
+
const filePath = (0, import_path2.join)(dir ?? process.cwd(), ".env");
|
|
4586
|
+
let existing;
|
|
4553
4587
|
try {
|
|
4554
|
-
await
|
|
4555
|
-
if (abortController.signal.aborted) break;
|
|
4556
|
-
process.stderr.write("\n");
|
|
4557
|
-
const response = await (0, import_prompts8.input)({ message: "You:" });
|
|
4558
|
-
process.stderr.write("\n");
|
|
4559
|
-
const trimmed = response.trim();
|
|
4560
|
-
if (!trimmed) continue;
|
|
4561
|
-
if (isExitCommand(trimmed)) return;
|
|
4562
|
-
turnGate.close();
|
|
4563
|
-
spinner.start();
|
|
4564
|
-
yield makeUserMessage(trimmed);
|
|
4588
|
+
existing = await (0, import_promises4.readFile)(filePath, "utf8");
|
|
4565
4589
|
} catch {
|
|
4566
|
-
return;
|
|
4567
4590
|
}
|
|
4591
|
+
const content = buildEnvContent(url, user, existing);
|
|
4592
|
+
await (0, import_promises4.writeFile)(filePath, content, "utf8");
|
|
4593
|
+
console.error(import_picocolors10.default.dim("Saved to .env"));
|
|
4594
|
+
} catch (err) {
|
|
4595
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4596
|
+
console.error(import_picocolors10.default.yellow(`Could not save .env file: ${message}`));
|
|
4568
4597
|
}
|
|
4569
4598
|
}
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4599
|
+
|
|
4600
|
+
// src/session/verify.ts
|
|
4601
|
+
var import_picocolors13 = __toESM(require("picocolors"));
|
|
4602
|
+
var import_prompts9 = require("@inquirer/prompts");
|
|
4603
|
+
|
|
4604
|
+
// src/session/collect.ts
|
|
4605
|
+
var import_prompts7 = require("@inquirer/prompts");
|
|
4606
|
+
var import_picocolors11 = __toESM(require("picocolors"));
|
|
4607
|
+
async function collectCredentials() {
|
|
4608
|
+
const prompted = /* @__PURE__ */ new Set();
|
|
4609
|
+
const envUrl = process.env.KINETICA_URL;
|
|
4610
|
+
const envUser = process.env.KINETICA_USER;
|
|
4611
|
+
if (envUrl && envUser && process.stdin.isTTY) {
|
|
4612
|
+
console.error(import_picocolors11.default.dim(`Saved connection: ${envUrl} (${envUser})`));
|
|
4613
|
+
const useSaved = await (0, import_prompts7.confirm)({
|
|
4614
|
+
message: "Use saved connection?",
|
|
4615
|
+
default: true
|
|
4616
|
+
});
|
|
4617
|
+
if (!useSaved) {
|
|
4618
|
+
prompted.add("url");
|
|
4619
|
+
prompted.add("user");
|
|
4620
|
+
const url2 = await (0, import_prompts7.input)({ message: "Kinetica endpoint URL:" });
|
|
4621
|
+
const user2 = await (0, import_prompts7.input)({ message: "Admin username:" });
|
|
4622
|
+
const pass2 = await (0, import_prompts7.password)({ message: "Admin password:", mask: "*" });
|
|
4623
|
+
return { credentials: { url: url2, user: user2, pass: pass2 }, prompted };
|
|
4582
4624
|
}
|
|
4583
|
-
} else {
|
|
4584
|
-
process.stderr.write(` ${import_picocolors13.default.red(`Error: ${statusResult.error}`)}
|
|
4585
|
-
`);
|
|
4586
4625
|
}
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4626
|
+
const url = envUrl ?? (prompted.add("url"), await (0, import_prompts7.input)({ message: "Kinetica endpoint URL:" }));
|
|
4627
|
+
const user = envUser ?? (prompted.add("user"), await (0, import_prompts7.input)({ message: "Admin username:" }));
|
|
4628
|
+
const pass = process.env.KINETICA_PASS ?? await (0, import_prompts7.password)({ message: "Admin password:", mask: "*" });
|
|
4629
|
+
return { credentials: { url, user, pass }, prompted };
|
|
4630
|
+
}
|
|
4631
|
+
async function repromptCredentials() {
|
|
4632
|
+
const user = await (0, import_prompts7.input)({ message: "Admin username:" });
|
|
4633
|
+
const pass = await (0, import_prompts7.password)({ message: "Admin password:", mask: "*" });
|
|
4634
|
+
return { user, pass };
|
|
4635
|
+
}
|
|
4636
|
+
|
|
4637
|
+
// src/session/KineticaSession.ts
|
|
4638
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
4639
|
+
function replacePort(baseUrl, port) {
|
|
4640
|
+
const parsed = new URL(baseUrl);
|
|
4641
|
+
parsed.port = String(port);
|
|
4642
|
+
return parsed.origin;
|
|
4643
|
+
}
|
|
4644
|
+
function createSession(url, user, pass) {
|
|
4645
|
+
const authHeader = "Basic " + Buffer.from(`${user}:${pass}`).toString("base64");
|
|
4646
|
+
const doFetch = async (fullUrl, body) => {
|
|
4647
|
+
if (process.env.DEBUG) {
|
|
4648
|
+
console.error(`[DEBUG] POST ${fullUrl}`);
|
|
4599
4649
|
}
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4650
|
+
return fetch(fullUrl, {
|
|
4651
|
+
method: "POST",
|
|
4652
|
+
headers: {
|
|
4653
|
+
Authorization: authHeader,
|
|
4654
|
+
"Content-Type": "application/json"
|
|
4655
|
+
},
|
|
4656
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
4657
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
4658
|
+
});
|
|
4659
|
+
};
|
|
4660
|
+
return {
|
|
4661
|
+
baseUrl: url,
|
|
4662
|
+
makeRequest: (endpoint, body) => doFetch(`${url}${endpoint}`, body),
|
|
4663
|
+
makeRequestToPort: (port, endpoint, body) => doFetch(`${replacePort(url, port)}${endpoint}`, body)
|
|
4664
|
+
};
|
|
4605
4665
|
}
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
);
|
|
4666
|
+
|
|
4667
|
+
// src/session/resolve-url.ts
|
|
4668
|
+
var import_picocolors12 = __toESM(require("picocolors"));
|
|
4669
|
+
var import_prompts8 = require("@inquirer/prompts");
|
|
4670
|
+
var PROBE_TIMEOUT_MS2 = 3e3;
|
|
4671
|
+
var HTTP_PROTOCOL_RE = /^https?:\/\//i;
|
|
4672
|
+
var ANY_PROTOCOL_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
4673
|
+
function hasProtocol(input5) {
|
|
4674
|
+
return HTTP_PROTOCOL_RE.test(input5);
|
|
4675
|
+
}
|
|
4676
|
+
function stripTrailingSlashes(url) {
|
|
4677
|
+
return url.replace(/\/+$/, "");
|
|
4678
|
+
}
|
|
4679
|
+
async function probeProtocol(url) {
|
|
4680
|
+
try {
|
|
4681
|
+
await fetch(url, {
|
|
4682
|
+
method: "HEAD",
|
|
4683
|
+
signal: AbortSignal.timeout(PROBE_TIMEOUT_MS2)
|
|
4684
|
+
});
|
|
4685
|
+
return true;
|
|
4686
|
+
} catch {
|
|
4687
|
+
return false;
|
|
4625
4688
|
}
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4689
|
+
}
|
|
4690
|
+
function isHttpsOnly() {
|
|
4691
|
+
return process.env.KINETICA_HTTPS_ONLY === "1";
|
|
4692
|
+
}
|
|
4693
|
+
function isInteractive() {
|
|
4694
|
+
return Boolean(process.stdin.isTTY);
|
|
4695
|
+
}
|
|
4696
|
+
async function confirmHttpFallback(host) {
|
|
4697
|
+
process.stderr.write(
|
|
4698
|
+
"\n" + import_picocolors12.default.red(
|
|
4699
|
+
import_picocolors12.default.bold(
|
|
4700
|
+
` WARNING: HTTPS unavailable at ${host}.
|
|
4701
|
+
Falling back to plaintext HTTP will transmit your Kinetica credentials in the clear.
|
|
4630
4702
|
`
|
|
4631
4703
|
)
|
|
4632
|
-
)
|
|
4704
|
+
) + import_picocolors12.default.dim(
|
|
4705
|
+
` Set KINETICA_HTTPS_ONLY=1 to refuse this fallback automatically, or pass an explicit http:// prefix to silence this prompt.
|
|
4706
|
+
|
|
4707
|
+
`
|
|
4708
|
+
)
|
|
4709
|
+
);
|
|
4710
|
+
try {
|
|
4711
|
+
return await (0, import_prompts8.confirm)({
|
|
4712
|
+
message: "Continue over plaintext HTTP?",
|
|
4713
|
+
default: false
|
|
4714
|
+
});
|
|
4715
|
+
} catch {
|
|
4716
|
+
return false;
|
|
4633
4717
|
}
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
const
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
const
|
|
4652
|
-
const
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4718
|
+
}
|
|
4719
|
+
async function resolveUrl(input5) {
|
|
4720
|
+
const trimmed = input5.trim();
|
|
4721
|
+
if (trimmed === "") {
|
|
4722
|
+
return { ok: false, error: "URL is empty" };
|
|
4723
|
+
}
|
|
4724
|
+
const normalized = stripTrailingSlashes(trimmed);
|
|
4725
|
+
if (hasProtocol(normalized)) {
|
|
4726
|
+
return { ok: true, url: normalized };
|
|
4727
|
+
}
|
|
4728
|
+
if (ANY_PROTOCOL_RE.test(normalized)) {
|
|
4729
|
+
const scheme = normalized.split("://")[0];
|
|
4730
|
+
return {
|
|
4731
|
+
ok: false,
|
|
4732
|
+
error: `Unsupported protocol: ${scheme}. Use http:// or https://`
|
|
4733
|
+
};
|
|
4734
|
+
}
|
|
4735
|
+
const httpsUrl = `https://${normalized}`;
|
|
4736
|
+
const httpUrl = `http://${normalized}`;
|
|
4737
|
+
console.error(import_picocolors12.default.dim("Detecting protocol..."));
|
|
4738
|
+
if (await probeProtocol(httpsUrl)) {
|
|
4739
|
+
return { ok: true, url: httpsUrl };
|
|
4740
|
+
}
|
|
4741
|
+
if (isHttpsOnly()) {
|
|
4742
|
+
return {
|
|
4743
|
+
ok: false,
|
|
4744
|
+
error: `HTTPS probe to ${httpsUrl} failed and KINETICA_HTTPS_ONLY=1; refusing to fall back to plaintext HTTP.`
|
|
4745
|
+
};
|
|
4746
|
+
}
|
|
4747
|
+
if (!await probeProtocol(httpUrl)) {
|
|
4748
|
+
return {
|
|
4749
|
+
ok: false,
|
|
4750
|
+
error: `Could not connect to ${normalized} via https:// or http://`
|
|
4751
|
+
};
|
|
4752
|
+
}
|
|
4753
|
+
if (!isInteractive()) {
|
|
4754
|
+
return {
|
|
4755
|
+
ok: false,
|
|
4756
|
+
error: `HTTPS unavailable at ${normalized} and terminal is non-interactive. Pass an explicit http:// prefix to allow plaintext HTTP, or point the URL at an HTTPS endpoint.`
|
|
4757
|
+
};
|
|
4758
|
+
}
|
|
4759
|
+
const approved = await confirmHttpFallback(normalized);
|
|
4760
|
+
if (!approved) {
|
|
4761
|
+
return {
|
|
4762
|
+
ok: false,
|
|
4763
|
+
error: "User declined plaintext HTTP fallback"
|
|
4764
|
+
};
|
|
4765
|
+
}
|
|
4766
|
+
return { ok: true, url: httpUrl };
|
|
4767
|
+
}
|
|
4768
|
+
|
|
4769
|
+
// src/session/verify.ts
|
|
4770
|
+
var MAX_RETRIES = 3;
|
|
4771
|
+
var MAX_REPROMPTS = 2;
|
|
4772
|
+
var DEFAULT_HM_PORT2 = 9300;
|
|
4773
|
+
function extractVersion(responseBody) {
|
|
4774
|
+
try {
|
|
4775
|
+
const outer = JSON.parse(responseBody);
|
|
4776
|
+
if (typeof outer.data_str !== "string") return void 0;
|
|
4777
|
+
const inner = JSON.parse(outer.data_str);
|
|
4778
|
+
const systemStr = inner.status_map?.system;
|
|
4779
|
+
if (typeof systemStr !== "string") return void 0;
|
|
4780
|
+
const system = JSON.parse(systemStr);
|
|
4781
|
+
return typeof system.version === "string" ? system.version : void 0;
|
|
4782
|
+
} catch {
|
|
4783
|
+
return void 0;
|
|
4678
4784
|
}
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
prompt: makeInteractivePrompt(abortController, turnGate, spinner),
|
|
4682
|
-
options
|
|
4683
|
-
});
|
|
4684
|
-
process.once("SIGINT", () => {
|
|
4685
|
-
spinner.stop();
|
|
4686
|
-
process.stderr.write("\nInterrupted \u2014 aborting investigation...\n");
|
|
4687
|
-
abortController.abort();
|
|
4688
|
-
agentQuery.close();
|
|
4689
|
-
});
|
|
4690
|
-
let numTurns = 0;
|
|
4691
|
-
let totalCostUsd = 0;
|
|
4692
|
-
let durationMs = 0;
|
|
4693
|
-
let durationApiMs = 0;
|
|
4694
|
-
let lastStreamCharWasNewline = true;
|
|
4695
|
-
const tableAligner = createStreamingTableAligner();
|
|
4696
|
-
let hadNonAbortError = false;
|
|
4785
|
+
}
|
|
4786
|
+
function extractVersionFromHostManager(responseBody) {
|
|
4697
4787
|
try {
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4788
|
+
const parsed = JSON.parse(responseBody);
|
|
4789
|
+
return typeof parsed.version === "string" ? parsed.version : void 0;
|
|
4790
|
+
} catch {
|
|
4791
|
+
return void 0;
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
async function probeHostManager(session2) {
|
|
4795
|
+
if (!session2.makeRequestToPort) {
|
|
4796
|
+
return { ok: false };
|
|
4797
|
+
}
|
|
4798
|
+
try {
|
|
4799
|
+
const response = await session2.makeRequestToPort(DEFAULT_HM_PORT2, "/", void 0);
|
|
4800
|
+
if (!response.ok) return { ok: false };
|
|
4801
|
+
const body = await response.text();
|
|
4802
|
+
return { ok: true, version: extractVersionFromHostManager(body) };
|
|
4803
|
+
} catch {
|
|
4804
|
+
return { ok: false };
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4807
|
+
async function verifyConnectivity(session2) {
|
|
4808
|
+
const response = await session2.makeRequest("/show/system/status", {});
|
|
4809
|
+
if (!response.ok) {
|
|
4810
|
+
const body2 = await response.text();
|
|
4811
|
+
throw new Error(`HTTP ${response.status}: ${body2}`);
|
|
4812
|
+
}
|
|
4813
|
+
const body = await response.text();
|
|
4814
|
+
return extractVersion(body);
|
|
4815
|
+
}
|
|
4816
|
+
function isCredentialError(errorMessage) {
|
|
4817
|
+
return errorMessage.startsWith("HTTP 401") || errorMessage.startsWith("HTTP 403");
|
|
4818
|
+
}
|
|
4819
|
+
async function connectWithRetry() {
|
|
4820
|
+
const { credentials, prompted } = await collectCredentials();
|
|
4821
|
+
const resolved = await resolveUrl(credentials.url);
|
|
4822
|
+
if (!resolved.ok) {
|
|
4823
|
+
console.error(import_picocolors13.default.red(resolved.error));
|
|
4824
|
+
process.exit(1);
|
|
4825
|
+
}
|
|
4826
|
+
const resolvedUrl = resolved.url;
|
|
4827
|
+
let currentUser = credentials.user;
|
|
4828
|
+
let currentPass = credentials.pass;
|
|
4829
|
+
let wasReprompted = false;
|
|
4830
|
+
let repromptCount = 0;
|
|
4831
|
+
let session2 = createSession(resolvedUrl, currentUser, currentPass);
|
|
4832
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
4833
|
+
try {
|
|
4834
|
+
const kineticaVersion = await verifyConnectivity(session2);
|
|
4835
|
+
console.error(import_picocolors13.default.green("Connected to Kinetica successfully."));
|
|
4836
|
+
if (prompted.size > 0 || wasReprompted) {
|
|
4837
|
+
await offerSaveCredentials(resolvedUrl, currentUser);
|
|
4838
|
+
}
|
|
4839
|
+
return { session: session2, kineticaVersion, degraded: false };
|
|
4840
|
+
} catch (err) {
|
|
4841
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4842
|
+
console.error(import_picocolors13.default.red(`Connection failed (attempt ${attempt}/${MAX_RETRIES}): ${msg}`));
|
|
4843
|
+
if (isCredentialError(msg)) {
|
|
4844
|
+
if (process.stdin.isTTY && repromptCount < MAX_REPROMPTS) {
|
|
4845
|
+
const shouldRetry = await (0, import_prompts9.confirm)({
|
|
4846
|
+
message: "Credentials may be incorrect. Re-enter?",
|
|
4847
|
+
default: true
|
|
4848
|
+
});
|
|
4849
|
+
if (shouldRetry) {
|
|
4850
|
+
const fresh = await repromptCredentials();
|
|
4851
|
+
currentUser = fresh.user;
|
|
4852
|
+
currentPass = fresh.pass;
|
|
4853
|
+
wasReprompted = true;
|
|
4854
|
+
repromptCount++;
|
|
4855
|
+
session2 = createSession(resolvedUrl, currentUser, currentPass);
|
|
4856
|
+
attempt = 0;
|
|
4857
|
+
continue;
|
|
4710
4858
|
}
|
|
4711
4859
|
}
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
process.stderr.write("\n");
|
|
4721
|
-
lastStreamCharWasNewline = true;
|
|
4722
|
-
}
|
|
4723
|
-
if (assistantMsg.message.stop_reason === "end_turn") {
|
|
4724
|
-
spinner.stop();
|
|
4725
|
-
turnGate.open();
|
|
4726
|
-
} else if (assistantMsg.message.stop_reason === "tool_use") {
|
|
4727
|
-
spinner.start("Investigating");
|
|
4728
|
-
} else {
|
|
4729
|
-
spinner.stop();
|
|
4730
|
-
}
|
|
4731
|
-
if (assistantMsg.error) {
|
|
4732
|
-
spinner.stop();
|
|
4733
|
-
const label = ERROR_LABELS[assistantMsg.error] ?? assistantMsg.error;
|
|
4734
|
-
process.stderr.write(import_picocolors13.default.yellow(`
|
|
4735
|
-
API error: ${label}
|
|
4736
|
-
`));
|
|
4737
|
-
turnGate.open();
|
|
4738
|
-
}
|
|
4739
|
-
} else if (message.type === "result") {
|
|
4740
|
-
spinner.stop();
|
|
4741
|
-
const resultMsg = message;
|
|
4742
|
-
numTurns = resultMsg.num_turns;
|
|
4743
|
-
totalCostUsd = resultMsg.total_cost_usd;
|
|
4744
|
-
durationMs = resultMsg.duration_ms;
|
|
4745
|
-
durationApiMs = resultMsg.duration_api_ms;
|
|
4746
|
-
if (resultMsg.subtype === "error_max_turns") {
|
|
4747
|
-
process.stderr.write(
|
|
4748
|
-
"\nInvestigation hit turn limit. Partial report may be available.\n"
|
|
4749
|
-
);
|
|
4750
|
-
} else if (resultMsg.subtype === "error_max_budget_usd") {
|
|
4751
|
-
process.stderr.write("\nBudget limit reached.\n");
|
|
4752
|
-
} else if (resultMsg.subtype === "error_during_execution") {
|
|
4753
|
-
process.stderr.write(
|
|
4754
|
-
"\nExecution error \u2014 the agent encountered an unrecoverable failure.\n"
|
|
4755
|
-
);
|
|
4756
|
-
} else if (resultMsg.subtype !== "success") {
|
|
4757
|
-
process.stderr.write(`
|
|
4758
|
-
Agent session ended with error: ${resultMsg.subtype}
|
|
4759
|
-
`);
|
|
4760
|
-
}
|
|
4761
|
-
if (resultMsg.permission_denials.length > 0) {
|
|
4762
|
-
const denied = resultMsg.permission_denials.map((d) => d.tool_name).join(", ");
|
|
4763
|
-
process.stderr.write(`
|
|
4764
|
-
Permission denials: ${denied}
|
|
4765
|
-
`);
|
|
4766
|
-
}
|
|
4767
|
-
turnGate.open();
|
|
4768
|
-
} else if (message.type === "system") {
|
|
4769
|
-
const sysMsg = message;
|
|
4770
|
-
if (sysMsg.subtype === "init") {
|
|
4771
|
-
const initMsg = message;
|
|
4772
|
-
const failed = (initMsg.mcp_servers ?? []).filter(
|
|
4773
|
-
(s) => s.name === MCP_SERVER_NAME && s.status !== "connected"
|
|
4774
|
-
);
|
|
4775
|
-
for (const s of failed) {
|
|
4776
|
-
process.stderr.write(
|
|
4777
|
-
`
|
|
4778
|
-
Warning: MCP server "${s.name}" failed to connect (${s.status})
|
|
4779
|
-
`
|
|
4780
|
-
);
|
|
4781
|
-
}
|
|
4782
|
-
} else if (sysMsg.subtype === "api_retry") {
|
|
4783
|
-
const retryMsg = message;
|
|
4784
|
-
const statusStr = retryMsg.error_status !== null ? ` (HTTP ${retryMsg.error_status})` : "";
|
|
4785
|
-
const delaySec = Math.round(retryMsg.retry_delay_ms / 1e3);
|
|
4786
|
-
process.stderr.write(
|
|
4860
|
+
console.error(import_picocolors13.default.red("Authentication failed. Exiting."));
|
|
4861
|
+
process.exit(1);
|
|
4862
|
+
}
|
|
4863
|
+
if (attempt === MAX_RETRIES) {
|
|
4864
|
+
console.error(import_picocolors13.default.yellow("DB engine unreachable. Probing host manager on port 9300..."));
|
|
4865
|
+
const hmResult = await probeHostManager(session2);
|
|
4866
|
+
if (hmResult.ok) {
|
|
4867
|
+
console.error(
|
|
4787
4868
|
import_picocolors13.default.yellow(
|
|
4788
|
-
|
|
4789
|
-
API error${statusStr}. Retrying (attempt ${retryMsg.attempt}/${retryMsg.max_retries}) in ${delaySec}s...
|
|
4790
|
-
`
|
|
4869
|
+
"Connected in DEGRADED MODE (host manager only). Most diagnostic tools will be unavailable."
|
|
4791
4870
|
)
|
|
4792
4871
|
);
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
`
|
|
4798
|
-
[Context compressed (${preTokens} tokens before compaction) \u2014 investigation continues]
|
|
4799
|
-
`
|
|
4800
|
-
);
|
|
4801
|
-
}
|
|
4802
|
-
} else if (message.type === "rate_limit_event") {
|
|
4803
|
-
const rateMsg = message;
|
|
4804
|
-
const { status, resetsAt } = rateMsg.rate_limit_info;
|
|
4805
|
-
if (status === "rejected") {
|
|
4806
|
-
const resetStr = resetsAt ? ` Resets at ${new Date(resetsAt * 1e3).toISOString()}.` : "";
|
|
4807
|
-
process.stderr.write(`
|
|
4808
|
-
Rate limited \u2014 requests rejected.${resetStr}
|
|
4809
|
-
`);
|
|
4810
|
-
} else if (status === "allowed_warning") {
|
|
4811
|
-
process.stderr.write("\nApproaching rate limit \u2014 investigation may slow.\n");
|
|
4812
|
-
}
|
|
4813
|
-
} else if (message.type === "control_request") {
|
|
4814
|
-
const controlMsg = message;
|
|
4815
|
-
if (controlMsg.request.subtype === "claude_authenticate") {
|
|
4816
|
-
process.stderr.write(import_picocolors13.default.yellow("\nRe-authentication requested by SDK...\n"));
|
|
4872
|
+
if (prompted.size > 0 || wasReprompted) {
|
|
4873
|
+
await offerSaveCredentials(resolvedUrl, currentUser);
|
|
4874
|
+
}
|
|
4875
|
+
return { session: session2, kineticaVersion: hmResult.version, degraded: true };
|
|
4817
4876
|
}
|
|
4877
|
+
console.error(import_picocolors13.default.red("Host manager also unreachable. Exiting."));
|
|
4878
|
+
process.exit(1);
|
|
4818
4879
|
}
|
|
4819
4880
|
}
|
|
4820
|
-
} catch (error) {
|
|
4821
|
-
spinner.stop();
|
|
4822
|
-
if (error instanceof import_claude_agent_sdk5.AbortError) {
|
|
4823
|
-
hadNonAbortError = false;
|
|
4824
|
-
} else {
|
|
4825
|
-
hadNonAbortError = true;
|
|
4826
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4827
|
-
process.stderr.write(import_picocolors13.default.red(`
|
|
4828
|
-
Agent error: ${message}
|
|
4829
|
-
`));
|
|
4830
|
-
}
|
|
4831
|
-
} finally {
|
|
4832
|
-
spinner.stop();
|
|
4833
|
-
const remaining = tableAligner.flush();
|
|
4834
|
-
if (remaining) {
|
|
4835
|
-
process.stderr.write(remaining);
|
|
4836
|
-
}
|
|
4837
|
-
turnGate.open();
|
|
4838
|
-
const durationSec = Math.round(durationMs / 1e3);
|
|
4839
|
-
const apiPct = durationMs > 0 ? Math.round(durationApiMs / durationMs * 100) : 0;
|
|
4840
|
-
const costStr = totalCostUsd > 0 ? ` Cost: $${totalCostUsd.toFixed(4)}.` : "";
|
|
4841
|
-
if (hadNonAbortError) {
|
|
4842
|
-
process.stderr.write(`
|
|
4843
|
-
Session ended due to error. Turns: ${numTurns}.${costStr}
|
|
4844
|
-
`);
|
|
4845
|
-
} else {
|
|
4846
|
-
process.stderr.write(
|
|
4847
|
-
`
|
|
4848
|
-
Session ended. Turns: ${numTurns}. Duration: ${durationSec}s (${apiPct}% API).${costStr}
|
|
4849
|
-
`
|
|
4850
|
-
);
|
|
4851
|
-
}
|
|
4852
4881
|
}
|
|
4882
|
+
throw new Error("unreachable");
|
|
4853
4883
|
}
|
|
4854
4884
|
|
|
4855
4885
|
// src/cli/index.ts
|
|
@@ -4925,8 +4955,13 @@ async function main() {
|
|
|
4925
4955
|
}
|
|
4926
4956
|
}
|
|
4927
4957
|
loadEnvFile();
|
|
4958
|
+
printBanner();
|
|
4959
|
+
if (model === void 0 && process.stdin.isTTY) {
|
|
4960
|
+
model = await selectModel();
|
|
4961
|
+
}
|
|
4928
4962
|
const effectiveModel = model ?? DEFAULT_AGENT_MODEL;
|
|
4929
|
-
|
|
4963
|
+
process.stderr.write(import_picocolors14.default.dim(`model: ${effectiveModel}
|
|
4964
|
+
`));
|
|
4930
4965
|
const authResult = await authenticateAnthropic({ forceLogin, loginMethod, loginOrgUUID });
|
|
4931
4966
|
if (authResult.method === "oauth") {
|
|
4932
4967
|
const acctInfo = authResult.email ? ` (${authResult.email})` : "";
|