@inkeep/agents-cli
Version:
Inkeep CLI tool
1,428 lines (1,410 loc) • 73.9 kB
JavaScript
#!/usr/bin/env node
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// ../node_modules/.pnpm/tsup@8.5.0_jiti@2.5.1_postcss@8.5.6_tsx@4.20.5_typescript@5.9.2_yaml@2.8.1/node_modules/tsup/assets/esm_shims.js
import path from "path";
import { fileURLToPath } from "url";
var init_esm_shims = __esm({
"../node_modules/.pnpm/tsup@8.5.0_jiti@2.5.1_postcss@8.5.6_tsx@4.20.5_typescript@5.9.2_yaml@2.8.1/node_modules/tsup/assets/esm_shims.js"() {
"use strict";
}
});
// src/utils/tsx-loader.ts
import { extname } from "path";
import { pathToFileURL } from "url";
import { register } from "tsx/esm/api";
async function importWithTypeScriptSupport(filePath) {
const ext = extname(filePath);
if (ext === ".ts") {
try {
const unregister = register();
try {
const fileUrl2 = pathToFileURL(filePath).href;
const module = await import(fileUrl2);
return module;
} finally {
unregister();
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
if (errorMessage.includes("Dynamic require") || errorMessage.includes("tsx") || errorMessage.includes("register")) {
throw new Error(
`Failed to load TypeScript file ${filePath}. Make sure tsx is installed: ${errorMessage}`
);
}
throw error;
}
}
const fileUrl = pathToFileURL(filePath).href;
return await import(fileUrl);
}
var init_tsx_loader = __esm({
"src/utils/tsx-loader.ts"() {
"use strict";
init_esm_shims();
}
});
// src/utils/config.ts
import { existsSync as existsSync4 } from "fs";
import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
import dotenv from "dotenv";
function findConfigFile(startPath = process.cwd()) {
let currentPath = resolve2(startPath);
const root = "/";
const configNames = ["inkeep.config.ts", "inkeep.config.js", ".inkeeprc.ts", ".inkeeprc.js"];
while (currentPath !== root) {
for (const configName of configNames) {
const configPath = join4(currentPath, configName);
if (existsSync4(configPath)) {
return configPath;
}
}
const parentPath = dirname3(currentPath);
if (parentPath === currentPath) {
break;
}
currentPath = parentPath;
}
return null;
}
async function loadConfigFromFile(configPath) {
let resolvedPath;
if (configPath) {
resolvedPath = resolve2(process.cwd(), configPath);
if (!existsSync4(resolvedPath)) {
throw new Error(`Config file not found: ${resolvedPath}`);
}
} else {
resolvedPath = findConfigFile();
if (!resolvedPath) {
return null;
}
}
try {
const module = await importWithTypeScriptSupport(resolvedPath);
const config = module.default || module.config;
if (!config) {
throw new Error(`No config exported from ${resolvedPath}`);
}
return config;
} catch (error) {
console.warn(`Warning: Failed to load config file ${resolvedPath}:`, error);
return null;
}
}
async function loadConfig(configPath) {
const config = {
agentsManageApiUrl: "http://localhost:3002",
agentsRunApiUrl: "http://localhost:3003",
manageUiUrl: "http://localhost:3000"
};
const fileConfig = await loadConfigFromFile(configPath);
if (fileConfig) {
Object.assign(config, fileConfig);
}
if (process.env.INKEEP_AGENTS_MANAGE_API_URL) {
config.agentsManageApiUrl = process.env.INKEEP_AGENTS_MANAGE_API_URL;
}
if (process.env.INKEEP_AGENTS_RUN_API_URL) {
config.agentsRunApiUrl = process.env.INKEEP_AGENTS_RUN_API_URL;
}
return config;
}
async function getTenantId(configPath) {
const config = await loadConfig(configPath);
return config.tenantId;
}
async function getProjectId(configPath) {
const config = await loadConfig(configPath);
return config.projectId || "default";
}
async function getAgentsManageApiUrl(overrideUrl, configPath) {
if (overrideUrl) {
return overrideUrl;
}
const config = await loadConfig(configPath);
return config.agentsManageApiUrl || "http://localhost:3002";
}
async function getAgentsRunApiUrl(overrideUrl, configPath) {
if (overrideUrl) {
return overrideUrl;
}
const config = await loadConfig(configPath);
return config.agentsRunApiUrl || "http://localhost:3003";
}
async function validateConfiguration(tenantIdFlag, agentsManageApiUrlFlag, agentsRunApiUrlFlag, configFilePath) {
if (configFilePath && tenantIdFlag) {
throw new Error(
"Invalid configuration combination:\nCannot use --config-file-path with --tenant-id.\nPlease use either:\n 1. --config-file-path alone\n 2. --tenant-id with --agents-manage-api-url and --agents-run-api-url\n 3. Default config file (inkeep.config.ts)"
);
}
if (configFilePath) {
const config2 = await loadConfig(configFilePath);
const tenantId2 = config2.tenantId;
const projectId2 = config2.projectId || "default";
const agentsManageApiUrl2 = agentsManageApiUrlFlag || config2.agentsManageApiUrl;
const agentsRunApiUrl2 = agentsRunApiUrlFlag || config2.agentsRunApiUrl;
if (!tenantId2) {
throw new Error(
`Tenant ID is missing from configuration file: ${configFilePath}
Please ensure your config file exports a valid configuration with tenantId.`
);
}
if (!agentsManageApiUrl2) {
throw new Error(
`Agents Manage API URL is missing from configuration file: ${configFilePath}
Please ensure your config file exports a valid configuration with agentsManageApiUrl.`
);
}
if (!agentsRunApiUrl2) {
throw new Error(
`Agents Run API URL is missing from configuration file: ${configFilePath}
Please ensure your config file exports a valid configuration with agentsRunApiUrl.`
);
}
const sources2 = {
tenantId: `config file (${configFilePath})`,
projectId: config2.projectId ? `config file (${configFilePath})` : "default",
agentsManageApiUrl: agentsManageApiUrlFlag ? "command-line flag (--agents-manage-api-url)" : `config file (${configFilePath})`,
agentsRunApiUrl: agentsRunApiUrlFlag ? "command-line flag (--agents-run-api-url)" : `config file (${configFilePath})`,
configFile: configFilePath
};
return {
tenantId: tenantId2,
projectId: projectId2,
agentsManageApiUrl: agentsManageApiUrl2,
agentsRunApiUrl: agentsRunApiUrl2,
manageUiUrl: config2.manageUiUrl,
modelSettings: config2.modelSettings || void 0,
sources: sources2
};
}
if (tenantIdFlag && agentsManageApiUrlFlag && agentsRunApiUrlFlag) {
const sources2 = {
tenantId: "command-line flag (--tenant-id)",
projectId: "default",
agentsManageApiUrl: "command-line flag (--agents-manage-api-url)",
agentsRunApiUrl: "command-line flag (--agents-run-api-url)"
};
return {
tenantId: tenantIdFlag,
projectId: "default",
agentsManageApiUrl: agentsManageApiUrlFlag,
agentsRunApiUrl: agentsRunApiUrlFlag,
manageUiUrl: void 0,
modelSettings: void 0,
sources: sources2
};
}
if (tenantIdFlag && !agentsManageApiUrlFlag && !agentsRunApiUrlFlag) {
throw new Error(
"Invalid configuration:\n--tenant-id requires --agents-manage-api-url and --agents-run-api-url to be provided as well.\nPlease provide both --tenant-id and --agents-manage-api-url and --agents-run-api-url together."
);
}
const config = await loadConfig();
const tenantId = config.tenantId;
const projectId = config.projectId || "default";
const agentsManageApiUrl = agentsManageApiUrlFlag || config.agentsManageApiUrl;
const agentsRunApiUrl = agentsRunApiUrlFlag || config.agentsRunApiUrl;
if (!tenantId) {
const configFile2 = findConfigFile();
if (!configFile2) {
throw new Error(
'No configuration found. Please use one of:\n 1. Create "inkeep.config.ts" by running "inkeep init"\n 2. Provide --config-file-path to specify a config file\n 3. Provide both --tenant-id and --agents-manage-api-url and --agents-run-api-url flags\n 4. Set INKEEP_AGENTS_MANAGE_API_URL and INKEEP_AGENTS_RUN_API_URL environment variables'
);
} else {
throw new Error(
`Tenant ID is missing from configuration file: ${configFile2}
Please either:
1. Update your configuration file with a tenantId
2. Provide both --tenant-id and --agents-manage-api-url and --agents-run-api-url flags
`
);
}
}
if (!agentsManageApiUrl) {
throw new Error(
"Agents Management API URL is missing. Please either:\n 1. Provide --agents-manage-api-url flag\n 2. Set INKEEP_AGENTS_MANAGE_API_URL environment variable\n 3. Add agentsManageApiUrl to your configuration file"
);
}
if (!agentsRunApiUrl) {
throw new Error(
"Agents Run API URL is missing. Please either:\n 1. Provide --agents-run-api-url flag\n 2. Set INKEEP_AGENTS_RUN_API_URL environment variable\n 3. Add agentsRunApiUrl to your configuration file"
);
}
const configFile = findConfigFile();
let agentsManageApiUrlSource = configFile ? `config file (${configFile})` : "default";
let agentsRunApiUrlSource = configFile ? `config file (${configFile})` : "default";
if (agentsManageApiUrlFlag) {
agentsManageApiUrlSource = "command-line flag (--agents-manage-api-url)";
} else if (process.env.INKEEP_AGENTS_MANAGE_API_URL === agentsManageApiUrl) {
agentsManageApiUrlSource = "environment variable (INKEEP_AGENTS_MANAGE_API_URL)";
} else if (agentsManageApiUrl === "http://localhost:3002" && !configFile) {
agentsManageApiUrlSource = "default value";
}
if (agentsRunApiUrlFlag) {
agentsRunApiUrlSource = "command-line flag (--agents-run-api-url)";
} else if (process.env.INKEEP_AGENTS_RUN_API_URL === agentsRunApiUrl) {
agentsRunApiUrlSource = "environment variable (INKEEP_AGENTS_RUN_API_URL)";
} else if (agentsRunApiUrl === "http://localhost:3003" && !configFile) {
agentsRunApiUrlSource = "default value";
}
const sources = {
tenantId: `config file (${configFile})`,
projectId: config.projectId ? configFile ? `config file (${configFile})` : "config" : "default",
agentsManageApiUrl: agentsManageApiUrlSource,
agentsRunApiUrl: agentsRunApiUrlSource,
configFile: configFile || void 0
};
return {
tenantId,
projectId,
agentsManageApiUrl,
agentsRunApiUrl,
manageUiUrl: config.manageUiUrl,
modelSettings: config.modelSettings || void 0,
sources
};
}
var init_config = __esm({
"src/utils/config.ts"() {
"use strict";
init_esm_shims();
init_tsx_loader();
dotenv.config({ quiet: true });
}
});
// src/api.ts
var BaseApiClient, ManagementApiClient, ExecutionApiClient;
var init_api = __esm({
"src/api.ts"() {
"use strict";
init_esm_shims();
init_config();
BaseApiClient = class {
apiUrl;
tenantId;
projectId;
constructor(apiUrl, tenantId, projectId) {
this.apiUrl = apiUrl;
this.tenantId = tenantId;
this.projectId = projectId;
}
checkTenantId() {
if (!this.tenantId) {
throw new Error("No tenant ID configured. Please run: inkeep init");
}
return this.tenantId;
}
getTenantId() {
return this.tenantId;
}
getProjectId() {
return this.projectId;
}
getApiUrl() {
return this.apiUrl;
}
};
ManagementApiClient = class _ManagementApiClient extends BaseApiClient {
constructor(apiUrl, tenantId, projectId) {
super(apiUrl, tenantId, projectId);
}
static async create(apiUrl, configPath, tenantIdOverride, projectIdOverride) {
const resolvedApiUrl = await getAgentsManageApiUrl(apiUrl, configPath);
const tenantId = tenantIdOverride || await getTenantId(configPath);
const projectId = projectIdOverride || await getProjectId(configPath);
return new _ManagementApiClient(resolvedApiUrl, tenantId, projectId);
}
async listGraphs() {
const tenantId = this.checkTenantId();
const projectId = this.getProjectId();
const response = await fetch(
`${this.apiUrl}/tenants/${tenantId}/crud/projects/${projectId}/agent-graphs`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
...process.env.INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET && {
Authorization: `Bearer ${process.env.INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET}`
}
}
}
);
if (!response.ok) {
throw new Error(`Failed to list graphs: ${response.statusText}`);
}
const data = await response.json();
return data.data || [];
}
async getGraph(graphId) {
const graphs = await this.listGraphs();
const graph = graphs.find((g) => g.id === graphId);
return graph || null;
}
async pushGraph(graphDefinition) {
const tenantId = this.checkTenantId();
const projectId = this.getProjectId();
graphDefinition.tenantId = tenantId;
const graphId = graphDefinition.id;
if (!graphId) {
throw new Error("Graph must have an id property");
}
const response = await fetch(
`${this.apiUrl}/tenants/${tenantId}/crud/projects/${projectId}/graph/${graphId}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
...process.env.INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET && {
Authorization: `Bearer ${process.env.INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET}`
}
},
body: JSON.stringify(graphDefinition)
}
);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to push graph: ${response.statusText}
${errorText}`);
}
const data = await response.json();
return data.data;
}
};
ExecutionApiClient = class _ExecutionApiClient extends BaseApiClient {
constructor(apiUrl, tenantId, projectId) {
super(apiUrl, tenantId, projectId);
}
static async create(apiUrl, configPath, tenantIdOverride, projectIdOverride) {
const resolvedApiUrl = await getAgentsRunApiUrl(apiUrl, configPath);
const tenantId = tenantIdOverride || await getTenantId(configPath);
const projectId = projectIdOverride || await getProjectId(configPath);
return new _ExecutionApiClient(resolvedApiUrl, tenantId, projectId);
}
async chatCompletion(graphId, messages, conversationId) {
const response = await fetch(`${this.apiUrl}/v1/chat/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "text/event-stream",
...process.env.INKEEP_AGENTS_RUN_API_BYPASS_SECRET && {
Authorization: `Bearer ${process.env.INKEEP_AGENTS_RUN_API_BYPASS_SECRET}`
},
"x-inkeep-tenant-id": this.tenantId || "test-tenant-id",
"x-inkeep-project-id": this.projectId,
"x-inkeep-graph-id": graphId
},
body: JSON.stringify({
model: "gpt-4o-mini",
// Required but will be overridden by graph config
messages,
conversationId,
stream: true
})
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Chat request failed: ${response.statusText}
${errorText}`);
}
const contentType = response.headers.get("content-type");
if (contentType?.includes("text/event-stream")) {
if (!response.body) {
throw new Error("No response body for streaming request");
}
return response.body;
} else {
const data = await response.json();
return data.choices?.[0]?.message?.content || data.result || "";
}
}
};
}
});
// src/commands/chat-enhanced.ts
var chat_enhanced_exports = {};
__export(chat_enhanced_exports, {
chatCommandEnhanced: () => chatCommandEnhanced
});
import * as readline from "readline";
import chalk7 from "chalk";
import inquirer3 from "inquirer";
import ora5 from "ora";
async function chatCommandEnhanced(graphIdInput, options) {
let config;
try {
config = await validateConfiguration(
options?.tenantId,
options?.agentsManageApiUrl,
options?.agentsRunApiUrl,
options?.configFilePath
);
} catch (error) {
console.error(chalk7.red(error.message));
process.exit(1);
}
console.log(chalk7.gray("Using configuration:"));
console.log(chalk7.gray(` \u2022 Tenant ID: ${config.sources.tenantId}`));
console.log(chalk7.gray(` \u2022 Management API: ${config.sources.agentsManageApiUrl}`));
console.log(chalk7.gray(` \u2022 Execution API: ${config.sources.agentsRunApiUrl}`));
console.log();
const managementApi = await ManagementApiClient.create(
config.agentsManageApiUrl,
options?.configFilePath,
config.tenantId
);
const executionApi = await ExecutionApiClient.create(
config.agentsRunApiUrl,
options?.configFilePath,
config.tenantId
);
let graphId = graphIdInput;
if (!graphId) {
const spinner2 = ora5("Fetching available graphs...").start();
try {
const graphs = await managementApi.listGraphs();
spinner2.stop();
if (graphs.length === 0) {
console.error(
chalk7.red("No graphs available. Define graphs in your project and run: inkeep push")
);
process.exit(1);
}
const graphChoices = graphs.map((g) => ({
name: `${chalk7.cyan(g.id)} - ${g.name || "Unnamed Graph"}`,
value: g.id,
short: g.id,
searchText: `${g.id} ${g.name || ""}`.toLowerCase()
}));
const answer = await inquirer3.prompt([
{
type: "list",
name: "graphId",
message: "Select a graph to chat with:",
choices: graphChoices,
pageSize: 10
}
]);
graphId = answer.graphId;
} catch (error) {
spinner2.fail("Failed to fetch graphs");
console.error(chalk7.red("Error:"), error instanceof Error ? error.message : error);
process.exit(1);
}
}
const spinner = ora5("Connecting to graph...").start();
try {
if (!graphId) {
throw new Error("No graph selected");
}
const graph = await managementApi.getGraph(graphId);
if (!graph) {
spinner.fail(`Graph "${graphId}" not found`);
const graphs = await managementApi.listGraphs();
if (graphs.length > 0) {
console.log(chalk7.yellow("\nAvailable graphs:"));
graphs.forEach((g) => {
console.log(chalk7.gray(` \u2022 ${g.id} - ${g.name || "Unnamed"}`));
});
console.log(chalk7.gray('\nRun "inkeep chat" without arguments for interactive selection'));
} else {
console.log(chalk7.yellow("\nNo graphs found. Please define graphs and push your project."));
}
process.exit(1);
}
spinner.succeed(`Connected to graph: ${chalk7.green(graph.name || graphId)}`);
if (graph.description) {
console.log(chalk7.gray(`Description: ${graph.description}`));
}
if (graph.defaultAgentId || graph.default_agent_id) {
console.log(chalk7.gray(`Default Agent: ${graph.defaultAgentId || graph.default_agent_id}`));
}
} catch (error) {
spinner.fail("Failed to connect to graph");
console.error(chalk7.red("Error:"), error instanceof Error ? error.message : error);
process.exit(1);
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: chalk7.cyan("You> ")
});
const conversationId = `cli-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const messages = [];
let debugMode = false;
console.log(chalk7.gray('\n\u{1F4AC} Chat session started. Type "exit" or press Ctrl+C to quit.'));
console.log(chalk7.gray("Commands: help, clear, history, reset, debug\n"));
async function handleStreamingResponse(stream, showDebug = false) {
const decoder = new TextDecoder();
const reader = stream.getReader();
let buffer = "";
let responseContent = "";
const debugOperations = [];
let hasStartedResponse = false;
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
if (line.startsWith("data: ")) {
const data = line.slice(6);
if (data === "[DONE]") continue;
try {
const parsed = JSON.parse(data);
const content = parsed.choices?.[0]?.delta?.content;
if (content) {
let currentPos = 0;
while (currentPos < content.length) {
if (content.substring(currentPos).startsWith('{"type":"data-operation"')) {
let braceCount = 0;
let jsonEnd = currentPos;
for (let i = currentPos; i < content.length; i++) {
if (content[i] === "{") braceCount++;
if (content[i] === "}") {
braceCount--;
if (braceCount === 0) {
jsonEnd = i + 1;
break;
}
}
}
const jsonStr = content.substring(currentPos, jsonEnd);
try {
const dataOp = JSON.parse(jsonStr);
debugOperations.push(dataOp);
if (showDebug && dataOp.type === "data-operation") {
const opType = dataOp.data?.type || "unknown";
const ctx = dataOp.data?.ctx || {};
let ctxDisplay = "";
if (opType === "agent_thinking" || opType === "iteration_start") {
ctxDisplay = ctx.agent || JSON.stringify(ctx);
} else if (opType === "task_creation") {
ctxDisplay = `agent: ${ctx.agent}`;
} else {
ctxDisplay = JSON.stringify(ctx);
}
if (opType === "completion" && hasStartedResponse) {
console.log("");
}
console.log(chalk7.gray(` [${opType}] ${ctxDisplay}`));
}
currentPos = jsonEnd;
} catch {
if (!hasStartedResponse) {
process.stdout.write(chalk7.green("Assistant> "));
hasStartedResponse = true;
}
process.stdout.write(content[currentPos]);
responseContent += content[currentPos];
currentPos++;
}
} else {
if (!hasStartedResponse) {
process.stdout.write(chalk7.green("Assistant> "));
hasStartedResponse = true;
}
process.stdout.write(content[currentPos]);
responseContent += content[currentPos];
currentPos++;
}
}
}
} catch {
}
}
}
}
} finally {
reader.releaseLock();
}
if (hasStartedResponse) {
console.log("\n");
} else {
console.log(`${chalk7.green("Assistant> ") + chalk7.gray("(no response)")}
`);
}
return responseContent;
}
rl.on("line", async (input) => {
const trimmedInput = input.trim();
const command = trimmedInput.toLowerCase().replace(/^\//, "");
if (command === "exit") {
console.log(chalk7.gray("Goodbye! \u{1F44B}"));
rl.close();
process.exit(0);
}
if (command === "clear") {
console.clear();
console.log(chalk7.gray("Screen cleared. Conversation context preserved.\n"));
rl.prompt();
return;
}
if (command === "help") {
console.log(chalk7.cyan("\n\u{1F4DA} Available commands:"));
console.log(chalk7.gray(" \u2022 exit - End the chat session"));
console.log(chalk7.gray(" \u2022 clear - Clear the screen (preserves context)"));
console.log(chalk7.gray(" \u2022 history - Show conversation history"));
console.log(chalk7.gray(" \u2022 reset - Reset conversation context"));
console.log(chalk7.gray(" \u2022 debug - Toggle debug mode (show/hide data operations)"));
console.log(chalk7.gray(" \u2022 help - Show this help message"));
console.log(chalk7.gray("\n Commands can be prefixed with / (e.g., /help)\n"));
rl.prompt();
return;
}
if (command === "debug") {
debugMode = !debugMode;
console.log(chalk7.yellow(`
\u{1F527} Debug mode: ${debugMode ? "ON" : "OFF"}`));
if (debugMode) {
console.log(chalk7.gray("Data operations will be shown during responses.\n"));
} else {
console.log(chalk7.gray("Data operations are hidden.\n"));
}
rl.prompt();
return;
}
if (command === "history") {
console.log(chalk7.cyan("\n\u{1F4DC} Conversation History:"));
if (messages.length === 0) {
console.log(chalk7.gray(" (No messages yet)\n"));
} else {
messages.forEach((msg, idx) => {
const role = msg.role === "user" ? chalk7.blue("You") : chalk7.green("Assistant");
const preview = msg.content.substring(0, 100);
const suffix = msg.content.length > 100 ? "..." : "";
console.log(` ${idx + 1}. ${role}: ${preview}${suffix}`);
});
console.log();
}
rl.prompt();
return;
}
if (command === "reset") {
messages.length = 0;
console.log(chalk7.yellow("\u26A0\uFE0F Conversation context has been reset.\n"));
rl.prompt();
return;
}
if (!trimmedInput) {
rl.prompt();
return;
}
messages.push({ role: "user", content: trimmedInput });
try {
if (!graphId) throw new Error("No graph selected");
const response = await executionApi.chatCompletion(graphId, messages, conversationId);
let assistantResponse;
if (typeof response === "string") {
console.log(chalk7.green("Assistant>"), response);
assistantResponse = response;
} else {
assistantResponse = await handleStreamingResponse(response, debugMode);
}
messages.push({ role: "assistant", content: assistantResponse });
} catch (error) {
console.error(chalk7.red("Error:"), error instanceof Error ? error.message : error);
}
rl.prompt();
});
rl.on("close", () => {
console.log(chalk7.gray("\n\u{1F4CA} Session Summary:"));
console.log(chalk7.gray(` \u2022 Graph: ${graphId}`));
console.log(chalk7.gray(` \u2022 Messages: ${messages.length}`));
console.log(chalk7.gray(` \u2022 Duration: ${(/* @__PURE__ */ new Date()).toLocaleTimeString()}`));
console.log(chalk7.gray("\nChat session ended."));
process.exit(0);
});
rl.prompt();
}
var init_chat_enhanced = __esm({
"src/commands/chat-enhanced.ts"() {
"use strict";
init_esm_shims();
init_api();
init_config();
}
});
// src/index.ts
init_esm_shims();
import { readFileSync as readFileSync3 } from "fs";
import { dirname as dirname4, join as join10 } from "path";
import { fileURLToPath as fileURLToPath2 } from "url";
import { Command } from "commander";
// src/commands/config.ts
init_esm_shims();
import { existsSync, readFileSync, writeFileSync } from "fs";
import { join } from "path";
import chalk from "chalk";
async function configGetCommand(key, options) {
const configPath = options?.configFilePath || join(process.cwd(), "inkeep.config.ts");
if (!existsSync(configPath)) {
console.error(chalk.red("No configuration file found."));
console.log(
chalk.gray(
'Run "inkeep init" to create one, or specify a config file with --config-file-path'
)
);
process.exit(1);
}
try {
const content = readFileSync(configPath, "utf-8");
const tenantIdMatch = content.match(/tenantId:\s*['"]([^'"]+)['"]/);
const apiUrlMatch = content.match(/apiUrl:\s*['"]([^'"]+)['"]/);
const config = {
tenantId: tenantIdMatch ? tenantIdMatch[1] : void 0,
apiUrl: apiUrlMatch ? apiUrlMatch[1] : void 0
};
if (key) {
const value = config[key];
if (value !== void 0) {
console.log(value);
} else {
console.error(chalk.red(`Unknown configuration key: ${key}`));
console.log(chalk.gray("Available keys: tenantId, apiUrl"));
process.exit(1);
}
} else {
console.log(chalk.cyan("Current configuration:"));
console.log(chalk.gray(" Config file:"), configPath);
console.log(chalk.gray(" Tenant ID:"), config.tenantId || chalk.yellow("(not set)"));
console.log(chalk.gray(" API URL:"), config.apiUrl || chalk.yellow("(not set)"));
}
} catch (error) {
console.error(chalk.red("Failed to read configuration:"), error);
process.exit(1);
}
}
async function configSetCommand(key, value, options) {
const configPath = options?.configFilePath || join(process.cwd(), "inkeep.config.ts");
if (!["tenantId", "apiUrl"].includes(key)) {
console.error(chalk.red(`Invalid configuration key: ${key}`));
console.log(chalk.gray("Available keys: tenantId, apiUrl"));
process.exit(1);
}
if (key === "apiUrl") {
try {
new URL(value);
} catch {
console.error(chalk.red("Invalid URL format"));
process.exit(1);
}
}
if (!existsSync(configPath)) {
const configContent = `import { defineConfig } from '@inkeep/agents-cli';
export default defineConfig({
tenantId: '${key === "tenantId" ? value : ""}',
projectId: '${key === "projectId" ? value : "default"}',
apiUrl: '${key === "apiUrl" ? value : "http://localhost:3002"}',
});
`;
try {
writeFileSync(configPath, configContent);
console.log(chalk.green("\u2713"), `Created config file and set ${key} to:`, chalk.cyan(value));
} catch (error) {
console.error(chalk.red("Failed to create config file:"), error);
process.exit(1);
}
} else {
try {
let content = readFileSync(configPath, "utf-8");
if (key === "tenantId") {
if (content.includes("tenantId:")) {
content = content.replace(/tenantId:\s*['"][^'"]*['"]/, `tenantId: '${value}'`);
} else {
content = content.replace(
/defineConfig\s*\(\s*{/,
`defineConfig({
tenantId: '${value}',`
);
}
} else if (key === "projectId") {
if (content.includes("projectId:")) {
content = content.replace(/projectId:\s*['"][^'"]*['"]/, `projectId: '${value}'`);
} else {
content = content.replace(
/(tenantId:\s*['"][^'"]*['"]),?/,
`$1,
projectId: '${value}',`
);
}
} else if (key === "apiUrl") {
if (content.includes("apiUrl:")) {
content = content.replace(/apiUrl:\s*['"][^'"]*['"]/, `apiUrl: '${value}'`);
} else {
content = content.replace(
/defineConfig\s*\(\s*{/,
`defineConfig({
apiUrl: '${value}',`
);
}
}
writeFileSync(configPath, content);
console.log(chalk.green("\u2713"), `Updated ${key} to:`, chalk.cyan(value));
} catch (error) {
console.error(chalk.red("Failed to update config file:"), error);
process.exit(1);
}
}
}
async function configListCommand(options) {
await configGetCommand(void 0, options);
}
// src/commands/dev.ts
init_esm_shims();
import { fork } from "child_process";
import { existsSync as existsSync2 } from "fs";
import { createRequire } from "module";
import { dirname, join as join2 } from "path";
import chalk2 from "chalk";
import ora from "ora";
var require2 = createRequire(import.meta.url);
function resolveWebRuntime() {
try {
const pkg = require2.resolve("@inkeep/agents-manage-ui/package.json");
const root = dirname(pkg);
return join2(root, ".next/standalone/agents-manage-ui");
} catch (err) {
throw new Error(`Could not find @inkeep/agents-manage-ui package. ${err}`);
}
}
function startWebApp({ port = 3e3, host = "localhost" }) {
const spinner = ora("Starting dashboard server...").start();
try {
const rt = resolveWebRuntime();
const entry = join2(rt, "server.js");
console.log(entry);
if (!existsSync2(entry)) {
spinner.fail("Dashboard server not found");
console.error(
chalk2.red("The dashboard server has not been built yet. Please run the following commands:")
);
console.error(chalk2.yellow(" cd agents-manage-ui"));
console.error(chalk2.yellow(" pnpm build"));
console.error(chalk2.yellow(" pnpm start"));
process.exit(1);
}
spinner.succeed("Starting dashboard server...");
const child = fork(entry, [], {
cwd: rt,
env: {
...process.env,
NODE_ENV: "production",
PORT: String(port),
HOSTNAME: host
},
stdio: "inherit"
});
console.log(chalk2.green(`\u{1F680} Dashboard server started at http://${host}:${port}`));
console.log(chalk2.gray("Press Ctrl+C to stop the server"));
process.on("SIGINT", () => {
console.log(chalk2.yellow("\n\u{1F6D1} Stopping dashboard server..."));
child.kill("SIGINT");
process.exit(0);
});
process.on("SIGTERM", () => {
child.kill("SIGTERM");
process.exit(0);
});
return child;
} catch (error) {
spinner.fail("Failed to start dashboard server");
console.error(chalk2.red("Error:"), error instanceof Error ? error.message : "Unknown error");
process.exit(1);
}
}
async function devCommand(options) {
const { port = 3e3, host = "localhost" } = options;
console.log(chalk2.blue("Inkeep Dashboard Server"));
console.log(chalk2.gray(`Starting server on ${host}:${port}`));
console.log("");
startWebApp({ port, host });
}
// src/commands/init.ts
init_esm_shims();
import { existsSync as existsSync3, readdirSync, writeFileSync as writeFileSync2 } from "fs";
import { basename, dirname as dirname2, join as join3, resolve } from "path";
import chalk3 from "chalk";
import inquirer2 from "inquirer";
// src/utils/model-config.ts
init_esm_shims();
import inquirer from "inquirer";
async function promptForModelConfiguration() {
const { providers } = await inquirer.prompt([
{
type: "checkbox",
name: "providers",
message: "Which AI providers would you like to configure?",
choices: [
{ name: "Anthropic (Claude)", value: "anthropic" },
{ name: "OpenAI (GPT)", value: "openai" }
],
validate: (input) => {
if (input.length === 0) {
return "Please select at least one provider";
}
return true;
}
}
]);
const anthropicModels = [
{ name: "Claude Opus 4.1", value: "anthropic/claude-opus-4-1-20250805" },
{ name: "Claude Sonnet 4", value: "anthropic/claude-sonnet-4-20250514" }
];
const openaiModels = [
{ name: "GPT-5", value: "openai/gpt-5-2025-08-07" },
{ name: "GPT-5 Mini", value: "openai/gpt-5-mini-2025-08-07" },
{ name: "GPT-5 Nano", value: "openai/gpt-5-nano-2025-08-07" },
{ name: "GPT-4.1", value: "openai/gpt-4.1-2025-04-14" },
{ name: "GPT-4.1 Mini", value: "openai/gpt-4.1-mini-2025-04-14" },
{ name: "GPT-4.1 Nano", value: "openai/gpt-4.1-nano-2025-04-14" }
];
const availableModels = [];
if (providers.includes("anthropic")) {
availableModels.push(...anthropicModels);
}
if (providers.includes("openai")) {
availableModels.push(...openaiModels);
}
const modelAnswers = await inquirer.prompt([
{
type: "list",
name: "baseModel",
message: "Select your default model for general tasks (required):",
choices: availableModels
},
{
type: "confirm",
name: "configureOptionalModels",
message: "Would you like to configure optional models for structured output and summaries?",
default: false
}
]);
let optionalModels = {};
if (modelAnswers.configureOptionalModels) {
const optionalChoices = [...availableModels, { name: "Use base model", value: null }];
optionalModels = await inquirer.prompt([
{
type: "list",
name: "structuredOutputModel",
message: "Select your model for structured output tasks (or use base model):",
choices: optionalChoices
},
{
type: "list",
name: "summarizerModel",
message: "Select your model for summaries and quick tasks (or use base model):",
choices: optionalChoices
}
]);
}
const modelSettings = {
base: {
model: modelAnswers.baseModel
}
};
if (optionalModels.structuredOutputModel) {
modelSettings.structuredOutput = {
model: optionalModels.structuredOutputModel
};
}
if (optionalModels.summarizerModel) {
modelSettings.summarizer = {
model: optionalModels.summarizerModel
};
}
return { modelSettings };
}
// src/commands/init.ts
function findProjectRoot(startPath) {
let currentPath = resolve(startPath);
const root = dirname2(currentPath);
const rootIndicators = [
"package.json",
".git",
".gitignore",
"tsconfig.json",
"package-lock.json",
"yarn.lock",
"pnpm-lock.yaml"
];
while (currentPath !== root) {
const files = readdirSync(currentPath);
if (rootIndicators.some((indicator) => files.includes(indicator))) {
return currentPath;
}
const parentPath = dirname2(currentPath);
if (parentPath === currentPath) {
break;
}
currentPath = parentPath;
}
return startPath;
}
async function initCommand(options) {
let configPath;
if (options?.path) {
const resolvedPath = resolve(process.cwd(), options.path);
if (options.path.endsWith(".ts") || options.path.endsWith(".js")) {
configPath = resolvedPath;
} else {
configPath = join3(resolvedPath, "inkeep.config.ts");
}
} else {
const projectRoot = findProjectRoot(process.cwd());
const suggestedPath = join3(projectRoot, "inkeep.config.ts");
if (options?.interactive === false) {
configPath = suggestedPath;
} else {
const { confirmedPath } = await inquirer2.prompt([
{
type: "input",
name: "confirmedPath",
message: "Where should the config file be created?",
default: suggestedPath,
validate: (input) => {
if (!input || input.trim() === "") {
return "Path is required";
}
const dir = input.endsWith(".ts") || input.endsWith(".js") ? dirname2(input) : input;
const resolvedDir = resolve(process.cwd(), dir);
if (!existsSync3(resolvedDir)) {
return `Directory does not exist: ${resolvedDir}`;
}
return true;
}
}
]);
const resolvedPath = resolve(process.cwd(), confirmedPath);
configPath = confirmedPath.endsWith(".ts") || confirmedPath.endsWith(".js") ? resolvedPath : join3(resolvedPath, "inkeep.config.ts");
}
}
if (existsSync3(configPath)) {
const { overwrite } = await inquirer2.prompt([
{
type: "confirm",
name: "overwrite",
message: `${basename(configPath)} already exists at this location. Do you want to overwrite it?`,
default: false
}
]);
if (!overwrite) {
console.log(chalk3.yellow("Init cancelled."));
return;
}
}
const answers = await inquirer2.prompt([
{
type: "input",
name: "tenantId",
message: "Enter your tenant ID:",
validate: (input) => {
if (!input || input.trim() === "") {
return "Tenant ID is required";
}
return true;
}
},
{
type: "input",
name: "projectId",
message: "Enter your project ID:",
default: "default",
validate: (input) => {
if (!input || input.trim() === "") {
return "Project ID is required";
}
return true;
}
},
{
type: "input",
name: "apiUrl",
message: "Enter the API URL:",
default: "http://localhost:3002",
validate: (input) => {
try {
new URL(input);
return true;
} catch {
return "Please enter a valid URL";
}
}
}
]);
const { modelSettings } = await promptForModelConfiguration();
const configContent = `import { defineConfig } from '@inkeep/agents-cli/config';
export default defineConfig({
tenantId: '${answers.tenantId}',
projectId: '${answers.projectId}',
agentsManageApiUrl: '${answers.apiUrl}',
agentsRunApiUrl: '${answers.apiUrl}',
modelSettings: ${JSON.stringify(modelSettings, null, 2)},
});
`;
try {
writeFileSync2(configPath, configContent);
console.log(chalk3.green("\u2713"), `Created ${chalk3.cyan(configPath)}`);
console.log(chalk3.gray("\nYou can now use the Inkeep CLI commands."));
console.log(chalk3.gray("For example: inkeep list-graphs"));
const configDir = dirname2(configPath);
if (configDir !== process.cwd()) {
console.log(chalk3.gray(`
Note: Config file created in ${configDir}`));
console.log(
chalk3.gray(`Use --config ${configPath} with commands, or run commands from that directory.`)
);
}
} catch (error) {
console.error(chalk3.red("Failed to create config file:"), error);
process.exit(1);
}
}
// src/commands/list-graphs.ts
init_esm_shims();
init_api();
init_config();
import chalk4 from "chalk";
import Table from "cli-table3";
import ora2 from "ora";
async function listGraphsCommand(options) {
let config;
try {
config = await validateConfiguration(
options.tenantId,
options.agentsManageApiUrl,
void 0,
// agentsRunApiUrl not needed for list-graphs
options.configFilePath
);
} catch (error) {
console.error(chalk4.red(error.message));
process.exit(1);
}
console.log(chalk4.gray("Using configuration:"));
console.log(chalk4.gray(` \u2022 Tenant ID: ${config.sources.tenantId}`));
console.log(chalk4.gray(` \u2022 API URL: ${config.sources.agentsManageApiUrl}`));
console.log();
const api = await ManagementApiClient.create(
config.agentsManageApiUrl,
options.configFilePath,
config.tenantId
);
const spinner = ora2("Fetching graphs...").start();
try {
const graphs = await api.listGraphs();
spinner.succeed(`Found ${graphs.length} graph(s)`);
if (graphs.length === 0) {
console.log(
chalk4.gray("No graphs found. Define graphs in your project and run: inkeep push")
);
return;
}
const table = new Table({
head: [
chalk4.cyan("Graph ID"),
chalk4.cyan("Name"),
chalk4.cyan("Default Agent"),
chalk4.cyan("Created")
],
style: {
head: [],
border: []
}
});
for (const graph of graphs) {
const createdDate = graph.createdAt ? new Date(graph.createdAt).toLocaleDateString() : "Unknown";
table.push([
graph.id || "",
graph.name || graph.id || "",
graph.defaultAgentId || chalk4.gray("None"),
createdDate
]);
}
console.log(`
${table.toString()}`);
} catch (error) {
spinner.fail("Failed to fetch graphs");
console.error(chalk4.red("Error:"), error instanceof Error ? error.message : error);
process.exit(1);
}
}
// src/commands/pull.ts
init_esm_shims();
init_tsx_loader();
import { existsSync as existsSync7, writeFileSync as writeFileSync3 } from "fs";
import { join as join7 } from "path";
import chalk5 from "chalk";
import ora3 from "ora";
// src/utils/project-directory.ts
init_esm_shims();
import { existsSync as existsSync5 } from "fs";
import { join as join5, resolve as resolve3 } from "path";
import { findUp } from "find-up";
async function findProjectDirectory(projectId) {
if (projectId) {
if (projectId.includes("/") || projectId.includes("\\")) {
const projectPath = resolve3(process.cwd(), projectId);
if (existsSync5(join5(projectPath, "inkeep.config.ts"))) {
return projectPath;
}
} else {
const projectPath = join5(process.cwd(), projectId);
if (existsSync5(join5(projectPath, "inkeep.config.ts"))) {
return projectPath;
}
}
return null;
}
const configPath = await findUp("inkeep.config.ts");
if (configPath) {
return resolve3(configPath, "..");
}
return null;
}
// src/utils/file-finder.ts
init_esm_shims();
import { existsSync as existsSync6, readdirSync as readdirSync2, statSync } from "fs";
import { join as join6, relative } from "path";
function findAllTypeScriptFiles(rootDir, excludeDirs = []) {
const tsFiles = [];
function scanDirectory(dir) {
if (!existsSync6(dir)) {
return;
}
const items = readdirSync2(dir);
for (const item of items) {
const fullPath = join6(dir, item);
const relativePath = relative(rootDir, fullPath);
const isExcludedDir = excludeDirs.some(
(excludeDir) => relativePath === excludeDir || relativePath.startsWith(`${excludeDir}/`)
);
if (isExcludedDir) {
continue;
}
const stat = statSync(fullPath);
if (stat.isDirectory()) {
scanDirectory(fullPath);
} else if (stat.isFile() && item.endsWith(".ts")) {
tsFiles.push(fullPath);
}
}
}
scanDirectory(rootDir);
return tsFiles.sort();
}
function categorizeTypeScriptFiles(files, rootDir) {
const indexFile = files.find((file) => file.endsWith("/index.ts") || file === join6(rootDir, "index.ts")) || null;
const configFiles = [];
const graphFiles = [];
const agentFiles = [];
const toolFiles = [];
const otherFiles = [];
for (const file of files) {
const fileName = file.split("/").pop() || "";
const relativePath = relative(rootDir, file);
if (file === indexFile) {
continue;
}
if (fileName.includes(".config.") || fileName.includes(".env.") || fileName === "inkeep.config.ts") {
configFiles.push(file);
} else if (fileName.includes(".graph.") || relativePath.includes("graphs/")) {
graphFiles.push(file);
} else if (fileName.includes("agent") || fileName.includes("Agent") || relativePath.includes("agents/")) {
agentFiles.push(file);
} else if (fileName.includes("tool") || fileName.includes("Tool") || relativePath.includes("tools/")) {
toolFiles.push(file);
} else {
otherFiles.push(file);
}
}
return {
indexFile,
configFiles,
graphFiles,
agentFiles,
toolFiles,
otherFiles
};
}
// src/commands/pull.llm-generate.ts
init_esm_shims();
import { anthropic, createAnthropic } from "@ai-sdk/anthropic";
import { createOpenAI, openai } from "@ai-sdk/openai";
import { generateText } from "ai";
function createModel(config) {
if (!config.model) {
throw new Error("Model configuration is required for pull command");
}
const modelString = config.model;
const providerOptions = config.providerOptions;
const { provider, modelName } = parseModelString(modelString);
switch (provider) {
case "anthropic":
if (providerOptions) {
const provider2 = createAnthropic(providerOptions);
return provider2(modelName);
}
return anthropic(modelName);
case "openai":
if (providerOptions) {
const provider2 = createOpenAI(providerOptions);
return provider2(modelName);
}
return openai(modelName);
default:
throw new Error(`Unsupported provider: ${provider}`);
}
}
function parseModelString(modelString) {
if (modelString.includes("/")) {
const [provider, ...modelParts] = modelString.split("/");
return {
provider: provider.toLowerCase(),
modelName: modelParts.join("/")
};
}
return {
provider: "anthropic",
modelName: modelString
};
}
async function generateTypeScriptFileWithLLM(graphData, graphId, outputFilePath, modelSettings, retryContext) {
const fs = await import("fs");
let existingContent = "";
let fileExists = false;
try {
existingContent = fs.readFileSync(outputFilePath, "utf-8");
fileExists = true;
} catch {
fileExists = false;
}
const model = createModel(modelSettings);
const prompt = createPrompt(graphData, graphId, existingContent, fileExists, retryContext);
try {
const { text } = await generateText({
model,
prompt,
temperature: 0.1,
// Low temperature for consistent code generation
maxOutputTokens: 16e3
// Increased to handle large TypeScript files
});
fs.writeFileSync(outputFilePath, text, "utf-8");
console.log(`\u2705 Successfully generated TypeScript file: ${outputFilePath}`);
} catch (error) {
console.error("\u274C Error generating TypeScript file with LLM:", error);
throw error;
}
}
function createPrompt(graphData, graphId, existingContent