olakai-cli 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +542 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -668,6 +668,32 @@ async function getActivityKpis(options) {
|
|
|
668
668
|
}
|
|
669
669
|
return await response.json();
|
|
670
670
|
}
|
|
671
|
+
async function listSessions(options) {
|
|
672
|
+
const token = getValidToken();
|
|
673
|
+
if (!token) {
|
|
674
|
+
throw new Error("Not logged in. Run 'olakai login' first.");
|
|
675
|
+
}
|
|
676
|
+
const params = new URLSearchParams();
|
|
677
|
+
params.set("agentId", options.agentId);
|
|
678
|
+
if (options.since) params.set("since", options.since);
|
|
679
|
+
if (options.until) params.set("until", options.until);
|
|
680
|
+
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
681
|
+
if (options.offset !== void 0) params.set("offset", String(options.offset));
|
|
682
|
+
const url = `${getBaseUrl()}/api/activity/sessions?${params}`;
|
|
683
|
+
const response = await fetch(url, {
|
|
684
|
+
headers: {
|
|
685
|
+
Authorization: `Bearer ${token}`
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
if (!response.ok) {
|
|
689
|
+
if (response.status === 401) {
|
|
690
|
+
throw new Error("Session expired. Run 'olakai login' to authenticate again.");
|
|
691
|
+
}
|
|
692
|
+
const error = await response.json();
|
|
693
|
+
throw new Error(error.error || "Failed to list sessions");
|
|
694
|
+
}
|
|
695
|
+
return await response.json();
|
|
696
|
+
}
|
|
671
697
|
async function listCustomDataConfigs(agentId) {
|
|
672
698
|
const token = getValidToken();
|
|
673
699
|
if (!token) {
|
|
@@ -1902,15 +1928,15 @@ function formatActivityTable(data) {
|
|
|
1902
1928
|
return;
|
|
1903
1929
|
}
|
|
1904
1930
|
const headers = ["ID", "AGENT", "APP", "MODEL", "TOKENS", "TIME(ms)", "RISK", "STATUS"];
|
|
1905
|
-
const rows = data.prompts.map((
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
String(
|
|
1911
|
-
String(
|
|
1912
|
-
|
|
1913
|
-
|
|
1931
|
+
const rows = data.prompts.map((prompt2) => [
|
|
1932
|
+
prompt2.id.slice(0, 12) + "...",
|
|
1933
|
+
prompt2.agentName || (prompt2.agentId ? prompt2.agentId.slice(0, 12) + "..." : "-"),
|
|
1934
|
+
prompt2.app.slice(0, 12),
|
|
1935
|
+
prompt2.modelId?.slice(0, 15) || "-",
|
|
1936
|
+
String(prompt2.tokens),
|
|
1937
|
+
String(prompt2.requestTime),
|
|
1938
|
+
prompt2.isHighRisk ? "Yes" : "No",
|
|
1939
|
+
prompt2.decorationStatus.slice(0, 10)
|
|
1914
1940
|
]);
|
|
1915
1941
|
const widths = headers.map(
|
|
1916
1942
|
(h, i) => Math.max(h.length, ...rows.map((r) => r[i].length))
|
|
@@ -1926,51 +1952,51 @@ function formatActivityTable(data) {
|
|
|
1926
1952
|
console.log(`Use --offset ${data.offset + data.limit} to see more results`);
|
|
1927
1953
|
}
|
|
1928
1954
|
}
|
|
1929
|
-
function formatActivityDetail(
|
|
1930
|
-
console.log(`ID: ${
|
|
1931
|
-
console.log(`Created: ${
|
|
1932
|
-
console.log(`Agent: ${
|
|
1933
|
-
console.log(`Workflow: ${
|
|
1934
|
-
if (
|
|
1935
|
-
console.log(`Task Exec ID: ${
|
|
1936
|
-
}
|
|
1937
|
-
console.log(`App: ${
|
|
1938
|
-
console.log(`Model: ${
|
|
1939
|
-
console.log(`Tokens: ${
|
|
1940
|
-
console.log(`Request Time: ${
|
|
1941
|
-
console.log(`High Risk: ${
|
|
1942
|
-
console.log(`Blocked: ${
|
|
1943
|
-
console.log(`Status: ${
|
|
1944
|
-
if (
|
|
1945
|
-
console.log(`Sensitivity: ${
|
|
1946
|
-
}
|
|
1947
|
-
if (
|
|
1955
|
+
function formatActivityDetail(prompt2) {
|
|
1956
|
+
console.log(`ID: ${prompt2.id}`);
|
|
1957
|
+
console.log(`Created: ${prompt2.createdAt}`);
|
|
1958
|
+
console.log(`Agent: ${prompt2.agentName || "-"} (${prompt2.agentId || "-"})`);
|
|
1959
|
+
console.log(`Workflow: ${prompt2.workflowName || "-"} (${prompt2.workflowId || "-"})`);
|
|
1960
|
+
if (prompt2.taskExecutionId) {
|
|
1961
|
+
console.log(`Task Exec ID: ${prompt2.taskExecutionId}`);
|
|
1962
|
+
}
|
|
1963
|
+
console.log(`App: ${prompt2.app}`);
|
|
1964
|
+
console.log(`Model: ${prompt2.modelId || "-"} (${prompt2.modelType || "-"})`);
|
|
1965
|
+
console.log(`Tokens: ${prompt2.tokens}`);
|
|
1966
|
+
console.log(`Request Time: ${prompt2.requestTime}ms`);
|
|
1967
|
+
console.log(`High Risk: ${prompt2.isHighRisk ? "Yes" : "No"}`);
|
|
1968
|
+
console.log(`Blocked: ${prompt2.blocked ? "Yes" : "No"}`);
|
|
1969
|
+
console.log(`Status: ${prompt2.decorationStatus}`);
|
|
1970
|
+
if (prompt2.sensitivity && prompt2.sensitivity.length > 0) {
|
|
1971
|
+
console.log(`Sensitivity: ${prompt2.sensitivity.join(", ")}`);
|
|
1972
|
+
}
|
|
1973
|
+
if (prompt2.analytics) {
|
|
1948
1974
|
console.log("");
|
|
1949
1975
|
console.log("Analytics:");
|
|
1950
|
-
console.log(` Task: ${
|
|
1951
|
-
console.log(` Subtask: ${
|
|
1952
|
-
console.log(` Time Saved: ${
|
|
1953
|
-
console.log(` Risk Score: ${
|
|
1976
|
+
console.log(` Task: ${prompt2.analytics.task || "-"}`);
|
|
1977
|
+
console.log(` Subtask: ${prompt2.analytics.subtask || "-"}`);
|
|
1978
|
+
console.log(` Time Saved: ${prompt2.analytics.timesaved_minutes ?? "-"} minutes`);
|
|
1979
|
+
console.log(` Risk Score: ${prompt2.analytics.riskassessment_dangerousity ?? "-"}`);
|
|
1954
1980
|
}
|
|
1955
|
-
if (
|
|
1981
|
+
if (prompt2.kpiData && Object.keys(prompt2.kpiData).length > 0) {
|
|
1956
1982
|
console.log("");
|
|
1957
1983
|
console.log("KPIs:");
|
|
1958
|
-
for (const [key, value] of Object.entries(
|
|
1984
|
+
for (const [key, value] of Object.entries(prompt2.kpiData)) {
|
|
1959
1985
|
console.log(` ${key}: ${value}`);
|
|
1960
1986
|
}
|
|
1961
1987
|
}
|
|
1962
|
-
if (
|
|
1988
|
+
if (prompt2.prompt !== void 0) {
|
|
1963
1989
|
console.log("");
|
|
1964
1990
|
console.log("Prompt:");
|
|
1965
1991
|
console.log("---");
|
|
1966
|
-
console.log(
|
|
1992
|
+
console.log(prompt2.prompt.slice(0, 1e3) + (prompt2.prompt.length > 1e3 ? "..." : ""));
|
|
1967
1993
|
console.log("---");
|
|
1968
1994
|
}
|
|
1969
|
-
if (
|
|
1995
|
+
if (prompt2.response !== void 0) {
|
|
1970
1996
|
console.log("");
|
|
1971
1997
|
console.log("Response:");
|
|
1972
1998
|
console.log("---");
|
|
1973
|
-
console.log(
|
|
1999
|
+
console.log(prompt2.response.slice(0, 1e3) + (prompt2.response.length > 1e3 ? "..." : ""));
|
|
1974
2000
|
console.log("---");
|
|
1975
2001
|
}
|
|
1976
2002
|
}
|
|
@@ -2046,11 +2072,11 @@ async function listCommand5(options) {
|
|
|
2046
2072
|
}
|
|
2047
2073
|
async function getCommand5(id, options) {
|
|
2048
2074
|
try {
|
|
2049
|
-
const
|
|
2075
|
+
const prompt2 = await getActivity(id, options.includeContent);
|
|
2050
2076
|
if (options.json) {
|
|
2051
|
-
console.log(JSON.stringify(
|
|
2077
|
+
console.log(JSON.stringify(prompt2, null, 2));
|
|
2052
2078
|
} else {
|
|
2053
|
-
formatActivityDetail(
|
|
2079
|
+
formatActivityDetail(prompt2);
|
|
2054
2080
|
}
|
|
2055
2081
|
} catch (error) {
|
|
2056
2082
|
console.error(
|
|
@@ -2085,11 +2111,485 @@ async function kpisCommand(options) {
|
|
|
2085
2111
|
process.exit(1);
|
|
2086
2112
|
}
|
|
2087
2113
|
}
|
|
2114
|
+
function formatSessionsTable(data) {
|
|
2115
|
+
console.log("Session Decoration Status");
|
|
2116
|
+
console.log("");
|
|
2117
|
+
console.log("Summary:");
|
|
2118
|
+
console.log(` Total Sessions: ${data.summary.sessionCount}`);
|
|
2119
|
+
const statuses = ["DECORATED", "NEW", "DECORATION_FAILED", "DECORATION_OUTDATED", "SKIPPED"];
|
|
2120
|
+
for (const status of statuses) {
|
|
2121
|
+
const count = data.summary.byStatus[status] || 0;
|
|
2122
|
+
const pct = data.summary.sessionCount > 0 ? (count / data.summary.sessionCount * 100).toFixed(1) : "0.0";
|
|
2123
|
+
const label = status.charAt(0) + status.slice(1).toLowerCase().replace(/_/g, " ");
|
|
2124
|
+
console.log(` ${label.padEnd(17)} ${String(count).padStart(4)} (${pct}%)`);
|
|
2125
|
+
}
|
|
2126
|
+
if (data.sessions.length === 0) {
|
|
2127
|
+
console.log("");
|
|
2128
|
+
console.log("No sessions found.");
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
console.log("");
|
|
2132
|
+
const headers = ["ID", "STATUS", "DATE", "CANDIDATE", "ERRORS", "KPI DATA", "VERSION"];
|
|
2133
|
+
const rows = data.sessions.map((s) => [
|
|
2134
|
+
s.id.slice(0, 15) + "...",
|
|
2135
|
+
s.decorationStatus,
|
|
2136
|
+
s.decorationDate || "-",
|
|
2137
|
+
s.decorationCandidate ? "Yes" : "No",
|
|
2138
|
+
String(s.decorationErrorStreak),
|
|
2139
|
+
s.hasKpiData ? "Yes" : "No",
|
|
2140
|
+
s.decoratorVersion || "-"
|
|
2141
|
+
]);
|
|
2142
|
+
const widths = headers.map(
|
|
2143
|
+
(h, i) => Math.max(h.length, ...rows.map((r) => r[i].length))
|
|
2144
|
+
);
|
|
2145
|
+
console.log(headers.map((h, i) => h.padEnd(widths[i])).join(" "));
|
|
2146
|
+
console.log(widths.map((w) => "-".repeat(w)).join(" "));
|
|
2147
|
+
for (const row of rows) {
|
|
2148
|
+
console.log(row.map((cell, i) => cell.padEnd(widths[i])).join(" "));
|
|
2149
|
+
}
|
|
2150
|
+
console.log("");
|
|
2151
|
+
console.log(`Showing ${data.sessions.length} of ${data.total} results (offset: ${data.offset})`);
|
|
2152
|
+
if (data.hasMore) {
|
|
2153
|
+
console.log(`Use --offset ${data.offset + data.limit} to see more results`);
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
async function sessionsCommand(options) {
|
|
2157
|
+
try {
|
|
2158
|
+
const data = await listSessions({
|
|
2159
|
+
agentId: options.agentId,
|
|
2160
|
+
since: options.since,
|
|
2161
|
+
until: options.until,
|
|
2162
|
+
limit: options.limit ? parseInt(options.limit, 10) : void 0,
|
|
2163
|
+
offset: options.offset ? parseInt(options.offset, 10) : void 0
|
|
2164
|
+
});
|
|
2165
|
+
if (options.json) {
|
|
2166
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2167
|
+
} else {
|
|
2168
|
+
formatSessionsTable(data);
|
|
2169
|
+
}
|
|
2170
|
+
} catch (error) {
|
|
2171
|
+
console.error(
|
|
2172
|
+
`Error: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2173
|
+
);
|
|
2174
|
+
process.exit(1);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2088
2177
|
function registerActivityCommand(program2) {
|
|
2089
2178
|
const activity = program2.command("activity").description("Inspect AI activity and prompts");
|
|
2090
2179
|
activity.command("list").description("List prompt requests with filters").option("--agent-id <id>", "Filter by agent ID").option("--workflow-id <id>", "Filter by workflow ID").option("--since <date>", "Start date (ISO format)").option("--until <date>", "End date (ISO format)").option("--limit <n>", "Results per page", "20").option("--offset <n>", "Skip first N results", "0").option("--include-content", "Include prompt/response content").option("--include-analytics", "Include advanced analytics").option("--json", "Output as JSON").action(listCommand5);
|
|
2091
2180
|
activity.command("get <id>").description("Get prompt request details").option("--include-content", "Include prompt/response content").option("--json", "Output as JSON").action(getCommand5);
|
|
2092
2181
|
activity.command("kpis").description("Get aggregated KPI values").option("--agent-id <id>", "Agent ID").option("--workflow-id <id>", "Workflow ID").option("--since <date>", "Period start (ISO format)").option("--until <date>", "Period end (ISO format)").option("--period <type>", "Aggregation period (hourly, daily, weekly)").option("--include-atoms", "Include per-prompt KPI data").option("--json", "Output as JSON").action(kpisCommand);
|
|
2182
|
+
activity.command("sessions").description("Inspect chat/session decoration status for troubleshooting").requiredOption("--agent-id <id>", "Agent ID (required)").option("--since <date>", "Start date (ISO format)").option("--until <date>", "End date (ISO format)").option("--limit <n>", "Results per page", "20").option("--offset <n>", "Skip first N results", "0").option("--json", "Output as JSON").action(sessionsCommand);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
// src/commands/monitor.ts
|
|
2186
|
+
import * as fs2 from "fs";
|
|
2187
|
+
import * as path2 from "path";
|
|
2188
|
+
import * as readline from "readline";
|
|
2189
|
+
var CLAUDE_DIR = ".claude";
|
|
2190
|
+
var SETTINGS_FILE = "settings.json";
|
|
2191
|
+
var MONITOR_CONFIG_FILE = "olakai-monitor.json";
|
|
2192
|
+
var OLAKAI_HOOK_MARKER = "olakai monitor hook";
|
|
2193
|
+
var HOOK_DEFINITIONS = {
|
|
2194
|
+
Stop: [
|
|
2195
|
+
{
|
|
2196
|
+
matcher: "",
|
|
2197
|
+
hooks: [
|
|
2198
|
+
{
|
|
2199
|
+
type: "command",
|
|
2200
|
+
command: "olakai monitor hook stop"
|
|
2201
|
+
}
|
|
2202
|
+
]
|
|
2203
|
+
}
|
|
2204
|
+
]
|
|
2205
|
+
};
|
|
2206
|
+
function prompt(question) {
|
|
2207
|
+
const rl = readline.createInterface({
|
|
2208
|
+
input: process.stdin,
|
|
2209
|
+
output: process.stdout
|
|
2210
|
+
});
|
|
2211
|
+
return new Promise((resolve) => {
|
|
2212
|
+
rl.question(question, (answer) => {
|
|
2213
|
+
rl.close();
|
|
2214
|
+
resolve(answer.trim());
|
|
2215
|
+
});
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
function findProjectRoot(startDir) {
|
|
2219
|
+
let dir = startDir ?? process.cwd();
|
|
2220
|
+
while (true) {
|
|
2221
|
+
if (fs2.existsSync(path2.join(dir, CLAUDE_DIR))) {
|
|
2222
|
+
return dir;
|
|
2223
|
+
}
|
|
2224
|
+
const parent = path2.dirname(dir);
|
|
2225
|
+
if (parent === dir) break;
|
|
2226
|
+
dir = parent;
|
|
2227
|
+
}
|
|
2228
|
+
return startDir ?? process.cwd();
|
|
2229
|
+
}
|
|
2230
|
+
function getClaudeDir(projectRoot) {
|
|
2231
|
+
return path2.join(projectRoot, CLAUDE_DIR);
|
|
2232
|
+
}
|
|
2233
|
+
function getSettingsPath(projectRoot) {
|
|
2234
|
+
return path2.join(getClaudeDir(projectRoot), SETTINGS_FILE);
|
|
2235
|
+
}
|
|
2236
|
+
function getMonitorConfigPath(projectRoot) {
|
|
2237
|
+
return path2.join(getClaudeDir(projectRoot), MONITOR_CONFIG_FILE);
|
|
2238
|
+
}
|
|
2239
|
+
function readJsonFile(filePath) {
|
|
2240
|
+
try {
|
|
2241
|
+
if (!fs2.existsSync(filePath)) return null;
|
|
2242
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
2243
|
+
return JSON.parse(content);
|
|
2244
|
+
} catch {
|
|
2245
|
+
return null;
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
function writeJsonFile(filePath, data) {
|
|
2249
|
+
const dir = path2.dirname(filePath);
|
|
2250
|
+
if (!fs2.existsSync(dir)) {
|
|
2251
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
2252
|
+
}
|
|
2253
|
+
fs2.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
2254
|
+
}
|
|
2255
|
+
function loadMonitorConfig(projectRoot) {
|
|
2256
|
+
const root = projectRoot ?? findProjectRoot();
|
|
2257
|
+
return readJsonFile(getMonitorConfigPath(root));
|
|
2258
|
+
}
|
|
2259
|
+
function readStdin(timeoutMs = 3e3) {
|
|
2260
|
+
return new Promise((resolve) => {
|
|
2261
|
+
if (process.stdin.isTTY) {
|
|
2262
|
+
resolve("");
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
let data = "";
|
|
2266
|
+
const timer = setTimeout(() => {
|
|
2267
|
+
process.stdin.removeAllListeners();
|
|
2268
|
+
process.stdin.destroy();
|
|
2269
|
+
resolve(data);
|
|
2270
|
+
}, timeoutMs);
|
|
2271
|
+
process.stdin.setEncoding("utf-8");
|
|
2272
|
+
process.stdin.on("data", (chunk) => {
|
|
2273
|
+
data += chunk;
|
|
2274
|
+
});
|
|
2275
|
+
process.stdin.on("end", () => {
|
|
2276
|
+
clearTimeout(timer);
|
|
2277
|
+
resolve(data);
|
|
2278
|
+
});
|
|
2279
|
+
process.stdin.on("error", () => {
|
|
2280
|
+
clearTimeout(timer);
|
|
2281
|
+
resolve(data);
|
|
2282
|
+
});
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
async function initCommand() {
|
|
2286
|
+
const token = getValidToken();
|
|
2287
|
+
if (!token) {
|
|
2288
|
+
console.error("Not logged in. Run 'olakai login' first.");
|
|
2289
|
+
process.exit(1);
|
|
2290
|
+
}
|
|
2291
|
+
console.log("Setting up Claude Code monitoring for this workspace...\n");
|
|
2292
|
+
const projectRoot = process.cwd();
|
|
2293
|
+
const dirName = path2.basename(projectRoot);
|
|
2294
|
+
let agent;
|
|
2295
|
+
const choice = await prompt(
|
|
2296
|
+
"Create a new agent or use an existing one? (new/existing) [new]: "
|
|
2297
|
+
);
|
|
2298
|
+
if (choice.toLowerCase() === "existing" || choice.toLowerCase() === "e") {
|
|
2299
|
+
const agents = await listAgents();
|
|
2300
|
+
if (agents.length === 0) {
|
|
2301
|
+
console.log("No agents found. Creating a new one instead.\n");
|
|
2302
|
+
agent = await createNewAgent(dirName);
|
|
2303
|
+
} else {
|
|
2304
|
+
console.log("\nAvailable agents:");
|
|
2305
|
+
for (let i = 0; i < agents.length; i++) {
|
|
2306
|
+
console.log(` ${i + 1}. ${agents[i].name} (${agents[i].id.slice(0, 12)}...)`);
|
|
2307
|
+
}
|
|
2308
|
+
const selection = await prompt(`
|
|
2309
|
+
Select agent (1-${agents.length}): `);
|
|
2310
|
+
const idx = parseInt(selection, 10) - 1;
|
|
2311
|
+
if (isNaN(idx) || idx < 0 || idx >= agents.length) {
|
|
2312
|
+
console.error("Invalid selection.");
|
|
2313
|
+
process.exit(1);
|
|
2314
|
+
}
|
|
2315
|
+
agent = await getAgent(agents[idx].id);
|
|
2316
|
+
if (!agent.apiKey?.key && !agent.apiKey?.isActive) {
|
|
2317
|
+
console.log(
|
|
2318
|
+
"\nThis agent has no active API key. Please create a new agent instead,"
|
|
2319
|
+
);
|
|
2320
|
+
console.log("or generate an API key via the Olakai dashboard.\n");
|
|
2321
|
+
const createNew = await prompt("Create a new agent? (y/n) [y]: ");
|
|
2322
|
+
if (createNew.toLowerCase() !== "n") {
|
|
2323
|
+
agent = await createNewAgent(dirName);
|
|
2324
|
+
} else {
|
|
2325
|
+
process.exit(1);
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
} else {
|
|
2330
|
+
agent = await createNewAgent(dirName);
|
|
2331
|
+
}
|
|
2332
|
+
let apiKey = agent.apiKey?.key;
|
|
2333
|
+
if (!apiKey) {
|
|
2334
|
+
console.log(
|
|
2335
|
+
"\nThe API key for this agent is not available (it is only shown once at creation)."
|
|
2336
|
+
);
|
|
2337
|
+
apiKey = await prompt("Paste the API key for this agent: ");
|
|
2338
|
+
if (!apiKey) {
|
|
2339
|
+
console.error("API key is required for monitoring.");
|
|
2340
|
+
process.exit(1);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
const claudeDir = getClaudeDir(projectRoot);
|
|
2344
|
+
if (!fs2.existsSync(claudeDir)) {
|
|
2345
|
+
fs2.mkdirSync(claudeDir, { recursive: true });
|
|
2346
|
+
}
|
|
2347
|
+
const settingsPath = getSettingsPath(projectRoot);
|
|
2348
|
+
const existingSettings = readJsonFile(settingsPath) ?? {};
|
|
2349
|
+
const mergedHooks = {
|
|
2350
|
+
...existingSettings.hooks ?? {}
|
|
2351
|
+
};
|
|
2352
|
+
for (const [event, entries] of Object.entries(HOOK_DEFINITIONS)) {
|
|
2353
|
+
const existing = mergedHooks[event] ?? [];
|
|
2354
|
+
const filtered = existing.filter(
|
|
2355
|
+
(e) => !e.hooks.some((h) => h.command.includes(OLAKAI_HOOK_MARKER))
|
|
2356
|
+
);
|
|
2357
|
+
mergedHooks[event] = [...filtered, ...entries];
|
|
2358
|
+
}
|
|
2359
|
+
const updatedSettings = {
|
|
2360
|
+
...existingSettings,
|
|
2361
|
+
hooks: mergedHooks
|
|
2362
|
+
};
|
|
2363
|
+
writeJsonFile(settingsPath, updatedSettings);
|
|
2364
|
+
const monitoringEndpoint = `${getBaseUrl()}/api/monitoring/prompt`;
|
|
2365
|
+
const monitorConfig = {
|
|
2366
|
+
agentId: agent.id,
|
|
2367
|
+
apiKey,
|
|
2368
|
+
agentName: agent.name,
|
|
2369
|
+
source: "claude-code",
|
|
2370
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2371
|
+
monitoringEndpoint
|
|
2372
|
+
};
|
|
2373
|
+
const monitorConfigPath = getMonitorConfigPath(projectRoot);
|
|
2374
|
+
writeJsonFile(monitorConfigPath, monitorConfig);
|
|
2375
|
+
fs2.chmodSync(monitorConfigPath, 384);
|
|
2376
|
+
console.log("");
|
|
2377
|
+
console.log(`\u2713 Agent "${agent.name}" configured (ID: ${agent.id})`);
|
|
2378
|
+
if (agent.apiKey?.key) {
|
|
2379
|
+
console.log("\u2713 API key generated");
|
|
2380
|
+
}
|
|
2381
|
+
console.log(`\u2713 Claude Code hooks configured in ${CLAUDE_DIR}/${SETTINGS_FILE}`);
|
|
2382
|
+
console.log(`\u2713 Monitor config saved to ${CLAUDE_DIR}/${MONITOR_CONFIG_FILE}`);
|
|
2383
|
+
console.log("");
|
|
2384
|
+
console.log(
|
|
2385
|
+
"Monitoring is now active. Claude Code will report activity to Olakai"
|
|
2386
|
+
);
|
|
2387
|
+
console.log(
|
|
2388
|
+
`on each turn. View activity at: ${getBaseUrl()}/dashboard`
|
|
2389
|
+
);
|
|
2390
|
+
console.log("");
|
|
2391
|
+
console.log(
|
|
2392
|
+
`\u26A0 Ensure ${CLAUDE_DIR}/${MONITOR_CONFIG_FILE} is in your .gitignore (it contains your API key)`
|
|
2393
|
+
);
|
|
2394
|
+
console.log("");
|
|
2395
|
+
console.log("To check status: olakai monitor status");
|
|
2396
|
+
console.log("To disable: olakai monitor disable");
|
|
2397
|
+
}
|
|
2398
|
+
async function createNewAgent(defaultName) {
|
|
2399
|
+
const nameInput = await prompt(
|
|
2400
|
+
`Agent name [${defaultName}]: `
|
|
2401
|
+
);
|
|
2402
|
+
const agentName = nameInput || defaultName;
|
|
2403
|
+
const agent = await createAgent({
|
|
2404
|
+
name: agentName,
|
|
2405
|
+
description: `Claude Code local agent for ${agentName}`,
|
|
2406
|
+
role: "WORKER",
|
|
2407
|
+
createApiKey: true,
|
|
2408
|
+
category: "CODING",
|
|
2409
|
+
source: "CLAUDE_CODE"
|
|
2410
|
+
});
|
|
2411
|
+
return agent;
|
|
2412
|
+
}
|
|
2413
|
+
async function hookCommand(event) {
|
|
2414
|
+
try {
|
|
2415
|
+
const config = loadMonitorConfig();
|
|
2416
|
+
if (!config) {
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
const stdinData = await readStdin(3e3);
|
|
2420
|
+
let eventData = {};
|
|
2421
|
+
if (stdinData) {
|
|
2422
|
+
try {
|
|
2423
|
+
eventData = JSON.parse(stdinData);
|
|
2424
|
+
} catch {
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
const payload = buildPayload(event, eventData, config);
|
|
2429
|
+
if (!payload) {
|
|
2430
|
+
return;
|
|
2431
|
+
}
|
|
2432
|
+
const controller = new AbortController();
|
|
2433
|
+
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
2434
|
+
try {
|
|
2435
|
+
await fetch(config.monitoringEndpoint, {
|
|
2436
|
+
method: "POST",
|
|
2437
|
+
headers: {
|
|
2438
|
+
"x-api-key": config.apiKey,
|
|
2439
|
+
"Content-Type": "application/json"
|
|
2440
|
+
},
|
|
2441
|
+
body: JSON.stringify([payload]),
|
|
2442
|
+
signal: controller.signal
|
|
2443
|
+
});
|
|
2444
|
+
} finally {
|
|
2445
|
+
clearTimeout(timeoutId);
|
|
2446
|
+
}
|
|
2447
|
+
} catch {
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
function buildPayload(event, eventData, config) {
|
|
2451
|
+
const sessionId = eventData.session_id ?? `claude-code-${Date.now()}`;
|
|
2452
|
+
switch (event) {
|
|
2453
|
+
case "stop": {
|
|
2454
|
+
return {
|
|
2455
|
+
prompt: eventData.prompt ?? "",
|
|
2456
|
+
response: eventData.response ?? "",
|
|
2457
|
+
chatId: sessionId,
|
|
2458
|
+
source: config.source,
|
|
2459
|
+
modelName: eventData.model,
|
|
2460
|
+
tokens: eventData.total_tokens_in !== void 0 && eventData.total_tokens_out !== void 0 ? eventData.total_tokens_in + eventData.total_tokens_out : void 0,
|
|
2461
|
+
requestTime: eventData.duration_api_ms,
|
|
2462
|
+
customData: {
|
|
2463
|
+
hookEvent: "Stop",
|
|
2464
|
+
stopReason: eventData.stop_reason ?? "",
|
|
2465
|
+
totalTokensIn: eventData.total_tokens_in ?? 0,
|
|
2466
|
+
totalTokensOut: eventData.total_tokens_out ?? 0,
|
|
2467
|
+
totalCost: eventData.total_cost ?? 0,
|
|
2468
|
+
durationMs: eventData.duration_ms ?? 0,
|
|
2469
|
+
numTurns: eventData.num_turns ?? 0
|
|
2470
|
+
}
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2473
|
+
default:
|
|
2474
|
+
return null;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
async function statusCommand(options) {
|
|
2478
|
+
const projectRoot = findProjectRoot();
|
|
2479
|
+
const config = loadMonitorConfig(projectRoot);
|
|
2480
|
+
if (!config) {
|
|
2481
|
+
console.log("Monitoring is not configured for this workspace.");
|
|
2482
|
+
console.log("Run 'olakai monitor init' to set up monitoring.");
|
|
2483
|
+
process.exit(1);
|
|
2484
|
+
}
|
|
2485
|
+
const settings = readJsonFile(getSettingsPath(projectRoot));
|
|
2486
|
+
const hooksPresent = settings?.hooks ? Object.values(settings.hooks).some(
|
|
2487
|
+
(entries) => entries.some(
|
|
2488
|
+
(e) => e.hooks.some((h) => h.command.includes(OLAKAI_HOOK_MARKER))
|
|
2489
|
+
)
|
|
2490
|
+
) : false;
|
|
2491
|
+
if (options.json) {
|
|
2492
|
+
console.log(
|
|
2493
|
+
JSON.stringify(
|
|
2494
|
+
{
|
|
2495
|
+
...config,
|
|
2496
|
+
apiKey: config.apiKey.slice(0, 12) + "...",
|
|
2497
|
+
hooksConfigured: hooksPresent
|
|
2498
|
+
},
|
|
2499
|
+
null,
|
|
2500
|
+
2
|
|
2501
|
+
)
|
|
2502
|
+
);
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
console.log("Olakai Monitor Status");
|
|
2506
|
+
console.log("=====================");
|
|
2507
|
+
console.log(`Agent: ${config.agentName}`);
|
|
2508
|
+
console.log(`Agent ID: ${config.agentId}`);
|
|
2509
|
+
console.log(`API Key: ${config.apiKey.slice(0, 12)}...`);
|
|
2510
|
+
console.log(`Endpoint: ${config.monitoringEndpoint}`);
|
|
2511
|
+
console.log(`Source: ${config.source}`);
|
|
2512
|
+
console.log(`Configured: ${config.createdAt}`);
|
|
2513
|
+
console.log(`Hooks: ${hooksPresent ? "Active" : "Missing (run 'olakai monitor init' to restore)"}`);
|
|
2514
|
+
try {
|
|
2515
|
+
const token = getValidToken();
|
|
2516
|
+
if (token) {
|
|
2517
|
+
const params = new URLSearchParams({
|
|
2518
|
+
agentId: config.agentId,
|
|
2519
|
+
limit: "5"
|
|
2520
|
+
});
|
|
2521
|
+
const response = await fetch(
|
|
2522
|
+
`${getBaseUrl()}/api/activity/prompts?${params}`,
|
|
2523
|
+
{
|
|
2524
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
2525
|
+
}
|
|
2526
|
+
);
|
|
2527
|
+
if (response.ok) {
|
|
2528
|
+
const data = await response.json();
|
|
2529
|
+
if (data.prompts && data.prompts.length > 0) {
|
|
2530
|
+
console.log("");
|
|
2531
|
+
console.log("Recent Activity:");
|
|
2532
|
+
for (const p of data.prompts) {
|
|
2533
|
+
console.log(` ${p.createdAt} ${p.id.slice(0, 12)}...`);
|
|
2534
|
+
}
|
|
2535
|
+
} else {
|
|
2536
|
+
console.log("");
|
|
2537
|
+
console.log("No activity recorded yet.");
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
} catch {
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
async function disableCommand(options) {
|
|
2545
|
+
const projectRoot = findProjectRoot();
|
|
2546
|
+
const settingsPath = getSettingsPath(projectRoot);
|
|
2547
|
+
const settings = readJsonFile(settingsPath);
|
|
2548
|
+
if (settings?.hooks) {
|
|
2549
|
+
const cleanedHooks = {};
|
|
2550
|
+
for (const [event, entries] of Object.entries(settings.hooks)) {
|
|
2551
|
+
const filtered = entries.filter(
|
|
2552
|
+
(e) => !e.hooks.some((h) => h.command.includes(OLAKAI_HOOK_MARKER))
|
|
2553
|
+
);
|
|
2554
|
+
if (filtered.length > 0) {
|
|
2555
|
+
cleanedHooks[event] = filtered;
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
if (Object.keys(cleanedHooks).length > 0) {
|
|
2559
|
+
settings.hooks = cleanedHooks;
|
|
2560
|
+
} else {
|
|
2561
|
+
delete settings.hooks;
|
|
2562
|
+
}
|
|
2563
|
+
writeJsonFile(settingsPath, settings);
|
|
2564
|
+
console.log(`\u2713 Olakai hooks removed from ${CLAUDE_DIR}/${SETTINGS_FILE}`);
|
|
2565
|
+
} else {
|
|
2566
|
+
console.log("No hooks found in settings.json.");
|
|
2567
|
+
}
|
|
2568
|
+
if (!options.keepConfig) {
|
|
2569
|
+
const configPath = getMonitorConfigPath(projectRoot);
|
|
2570
|
+
if (fs2.existsSync(configPath)) {
|
|
2571
|
+
fs2.unlinkSync(configPath);
|
|
2572
|
+
console.log(`\u2713 Monitor config removed (${CLAUDE_DIR}/${MONITOR_CONFIG_FILE})`);
|
|
2573
|
+
}
|
|
2574
|
+
} else {
|
|
2575
|
+
console.log(
|
|
2576
|
+
`Monitor config retained at ${CLAUDE_DIR}/${MONITOR_CONFIG_FILE}`
|
|
2577
|
+
);
|
|
2578
|
+
}
|
|
2579
|
+
console.log("");
|
|
2580
|
+
console.log("Monitoring disabled. Run 'olakai monitor init' to re-enable.");
|
|
2581
|
+
}
|
|
2582
|
+
function registerMonitorCommand(program2) {
|
|
2583
|
+
const monitor = program2.command("monitor").description("Monitor Claude Code sessions with Olakai");
|
|
2584
|
+
monitor.command("init").description(
|
|
2585
|
+
"Set up Olakai monitoring for this Claude Code workspace"
|
|
2586
|
+
).action(initCommand);
|
|
2587
|
+
monitor.command("hook <event>").description("Hook handler called by Claude Code (internal use)").action(hookCommand);
|
|
2588
|
+
monitor.command("status").description("Show monitoring status for this workspace").option("--json", "Output as JSON").action(statusCommand);
|
|
2589
|
+
monitor.command("disable").description("Remove Olakai monitoring from this workspace").option(
|
|
2590
|
+
"--keep-config",
|
|
2591
|
+
"Keep the monitor config file (only remove hooks)"
|
|
2592
|
+
).action(disableCommand);
|
|
2093
2593
|
}
|
|
2094
2594
|
|
|
2095
2595
|
// src/index.ts
|
|
@@ -2120,5 +2620,6 @@ registerWorkflowsCommand(program);
|
|
|
2120
2620
|
registerKpisCommand(program);
|
|
2121
2621
|
registerCustomDataCommand(program);
|
|
2122
2622
|
registerActivityCommand(program);
|
|
2623
|
+
registerMonitorCommand(program);
|
|
2123
2624
|
program.parse();
|
|
2124
2625
|
//# sourceMappingURL=index.js.map
|