@inkeep/agents-cli 0.39.4 → 0.40.0

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.
Files changed (80) hide show
  1. package/dist/_virtual/rolldown_runtime.js +7 -0
  2. package/dist/api.js +185 -0
  3. package/dist/commands/add.js +139 -0
  4. package/dist/commands/config.js +86 -0
  5. package/dist/commands/dev.js +259 -0
  6. package/dist/commands/init.js +360 -0
  7. package/dist/commands/list-agents.js +56 -0
  8. package/dist/commands/login.js +179 -0
  9. package/dist/commands/logout.js +56 -0
  10. package/dist/commands/profile.js +276 -0
  11. package/dist/{component-parser2.js → commands/pull-v3/component-parser.js} +16 -3
  12. package/dist/commands/pull-v3/component-updater.js +710 -0
  13. package/dist/commands/pull-v3/components/agent-generator.js +241 -0
  14. package/dist/commands/pull-v3/components/artifact-component-generator.js +127 -0
  15. package/dist/commands/pull-v3/components/context-config-generator.js +190 -0
  16. package/dist/commands/pull-v3/components/credential-generator.js +89 -0
  17. package/dist/commands/pull-v3/components/data-component-generator.js +102 -0
  18. package/dist/commands/pull-v3/components/environment-generator.js +170 -0
  19. package/dist/commands/pull-v3/components/external-agent-generator.js +75 -0
  20. package/dist/commands/pull-v3/components/function-tool-generator.js +94 -0
  21. package/dist/commands/pull-v3/components/mcp-tool-generator.js +86 -0
  22. package/dist/commands/pull-v3/components/project-generator.js +145 -0
  23. package/dist/commands/pull-v3/components/status-component-generator.js +92 -0
  24. package/dist/commands/pull-v3/components/sub-agent-generator.js +285 -0
  25. package/dist/commands/pull-v3/index.js +510 -0
  26. package/dist/commands/pull-v3/introspect-generator.js +278 -0
  27. package/dist/commands/pull-v3/llm-content-merger.js +192 -0
  28. package/dist/{new-component-generator.js → commands/pull-v3/new-component-generator.js} +14 -3
  29. package/dist/commands/pull-v3/project-comparator.js +914 -0
  30. package/dist/{project-index-generator.js → commands/pull-v3/project-index-generator.js} +1 -2
  31. package/dist/{project-validator.js → commands/pull-v3/project-validator.js} +4 -4
  32. package/dist/commands/pull-v3/targeted-typescript-placeholders.js +173 -0
  33. package/dist/commands/pull-v3/utils/component-registry.js +369 -0
  34. package/dist/commands/pull-v3/utils/component-tracker.js +165 -0
  35. package/dist/commands/pull-v3/utils/generator-utils.js +146 -0
  36. package/dist/commands/pull-v3/utils/model-provider-detector.js +44 -0
  37. package/dist/commands/push.js +326 -0
  38. package/dist/commands/status.js +89 -0
  39. package/dist/commands/update.js +97 -0
  40. package/dist/commands/whoami.js +38 -0
  41. package/dist/config.js +0 -1
  42. package/dist/env.js +30 -0
  43. package/dist/exports.js +3 -0
  44. package/dist/index.js +28 -196514
  45. package/dist/instrumentation.js +47 -0
  46. package/dist/types/agent.js +1 -0
  47. package/dist/types/tsx.d.d.ts +1 -0
  48. package/dist/utils/background-version-check.js +19 -0
  49. package/dist/utils/ci-environment.js +87 -0
  50. package/dist/utils/cli-pipeline.js +158 -0
  51. package/dist/utils/config.js +290 -0
  52. package/dist/utils/credentials.js +132 -0
  53. package/dist/utils/environment-loader.js +28 -0
  54. package/dist/utils/file-finder.js +62 -0
  55. package/dist/utils/json-comparator.js +185 -0
  56. package/dist/utils/json-comparison.js +232 -0
  57. package/dist/utils/mcp-runner.js +120 -0
  58. package/dist/utils/model-config.js +182 -0
  59. package/dist/utils/package-manager.js +58 -0
  60. package/dist/utils/profile-config.js +85 -0
  61. package/dist/utils/profiles/index.js +4 -0
  62. package/dist/utils/profiles/profile-manager.js +219 -0
  63. package/dist/utils/profiles/types.js +62 -0
  64. package/dist/utils/project-directory.js +33 -0
  65. package/dist/utils/project-loader.js +29 -0
  66. package/dist/utils/schema-introspection.js +44 -0
  67. package/dist/utils/templates.js +198 -0
  68. package/dist/utils/tsx-loader.js +27 -0
  69. package/dist/utils/url.js +26 -0
  70. package/dist/utils/version-check.js +79 -0
  71. package/package.json +4 -19
  72. package/dist/component-parser.js +0 -4
  73. package/dist/component-updater.js +0 -4
  74. package/dist/config2.js +0 -4
  75. package/dist/credential-stores.js +0 -4
  76. package/dist/environment-generator.js +0 -4
  77. package/dist/nodefs.js +0 -27
  78. package/dist/opfs-ahp.js +0 -368
  79. package/dist/project-loader.js +0 -4
  80. package/dist/tsx-loader.js +0 -4
@@ -0,0 +1,7 @@
1
+ import { createRequire } from "node:module";
2
+
3
+ //#region rolldown:runtime
4
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
+
6
+ //#endregion
7
+ export { __require };
package/dist/api.js ADDED
@@ -0,0 +1,185 @@
1
+ import { OPENAI_MODELS, apiFetch } from "@inkeep/agents-core";
2
+
3
+ //#region src/api.ts
4
+ var BaseApiClient = class {
5
+ apiUrl;
6
+ tenantId;
7
+ projectId;
8
+ apiKey;
9
+ isCI;
10
+ constructor(apiUrl, tenantId, projectId, apiKey, isCI = false) {
11
+ this.apiUrl = apiUrl;
12
+ this.tenantId = tenantId;
13
+ this.projectId = projectId;
14
+ this.apiKey = apiKey;
15
+ this.isCI = isCI;
16
+ }
17
+ checkTenantId() {
18
+ if (!this.tenantId) throw new Error("No tenant ID configured. Please run: inkeep init");
19
+ return this.tenantId;
20
+ }
21
+ /**
22
+ * Wrapper around fetch that automatically includes auth header
23
+ * Uses X-API-Key for CI mode, Authorization Bearer for interactive mode
24
+ */
25
+ async authenticatedFetch(url, options = {}) {
26
+ const headers = { ...options.headers || {} };
27
+ if (this.apiKey) if (this.isCI) headers["X-API-Key"] = this.apiKey;
28
+ else headers.Authorization = `Bearer ${this.apiKey}`;
29
+ return apiFetch(url, {
30
+ ...options,
31
+ headers
32
+ });
33
+ }
34
+ getTenantId() {
35
+ return this.tenantId;
36
+ }
37
+ getProjectId() {
38
+ return this.projectId;
39
+ }
40
+ getApiUrl() {
41
+ return this.apiUrl;
42
+ }
43
+ getIsCI() {
44
+ return this.isCI;
45
+ }
46
+ };
47
+ var ManagementApiClient = class ManagementApiClient extends BaseApiClient {
48
+ constructor(apiUrl, tenantId, projectId, apiKey, isCI = false) {
49
+ super(apiUrl, tenantId, projectId, apiKey, isCI);
50
+ }
51
+ static async create(apiUrl, configPath, tenantIdOverride, projectIdOverride, isCI, apiKeyOverride) {
52
+ const { validateConfiguration } = await import("./utils/config.js");
53
+ const config = await validateConfiguration(configPath);
54
+ return new ManagementApiClient(apiUrl || config.agentsManageApiUrl, tenantIdOverride || config.tenantId, projectIdOverride || "", apiKeyOverride || config.agentsManageApiKey, isCI ?? false);
55
+ }
56
+ async listAgents() {
57
+ const tenantId = this.checkTenantId();
58
+ const projectId = this.getProjectId();
59
+ const response = await this.authenticatedFetch(`${this.apiUrl}/tenants/${tenantId}/projects/${projectId}/agents?limit=100`, { method: "GET" });
60
+ if (!response.ok) throw new Error(`Failed to list agents: ${response.statusText}. ${this.apiUrl}/tenants/${tenantId}/projects/${projectId}/agents`);
61
+ return (await response.json()).data || [];
62
+ }
63
+ async getAgent(agentId) {
64
+ return (await this.listAgents()).find((g) => g.id === agentId) || null;
65
+ }
66
+ async pushAgent(agentDefinition) {
67
+ const tenantId = this.checkTenantId();
68
+ const projectId = this.getProjectId();
69
+ const agentId = agentDefinition.id;
70
+ if (!agentId) throw new Error("Agent must have an id property");
71
+ const response = await this.authenticatedFetch(`${this.apiUrl}/tenants/${tenantId}/projects/${projectId}/agents/${agentId}`, {
72
+ method: "PUT",
73
+ body: JSON.stringify({
74
+ ...agentDefinition,
75
+ tenantId
76
+ })
77
+ });
78
+ if (!response.ok) {
79
+ const errorText = await response.text();
80
+ throw new Error(`Failed to push agent: ${response.statusText}\n${errorText}`);
81
+ }
82
+ return (await response.json()).data;
83
+ }
84
+ async getFullProject(projectId) {
85
+ const tenantId = this.checkTenantId();
86
+ const response = await this.authenticatedFetch(`${this.apiUrl}/tenants/${tenantId}/project-full/${projectId}`, { method: "GET" });
87
+ if (!response.ok) {
88
+ if (response.status === 404) throw new Error(`Project "${projectId}" not found`);
89
+ if (response.status === 401 || response.status === 403) {
90
+ const errorText$1 = await response.text().catch(() => "");
91
+ let errorMessage = "Authentication failed - check your API key configuration\n\n";
92
+ errorMessage += "Common issues:\n";
93
+ errorMessage += " • Missing or invalid API key in inkeep.config.ts\n";
94
+ errorMessage += " • API key does not have access to this tenant/project\n";
95
+ errorMessage += " • For local development, ensure INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET is set\n";
96
+ if (errorText$1) errorMessage += `\nServer response: ${errorText$1}`;
97
+ throw new Error(errorMessage);
98
+ }
99
+ const errorText = await response.text().catch(() => "");
100
+ throw new Error(`Failed to fetch project: ${response.statusText}${errorText ? `\n${errorText}` : ""}`);
101
+ }
102
+ return (await response.json()).data;
103
+ }
104
+ /**
105
+ * List all projects for the current tenant
106
+ * @param page - Page number (1-based)
107
+ * @param limit - Number of results per page (max 100)
108
+ * @returns List of projects with pagination info
109
+ */
110
+ async listProjects(page = 1, limit = 100) {
111
+ const tenantId = this.checkTenantId();
112
+ const response = await this.authenticatedFetch(`${this.apiUrl}/tenants/${tenantId}/projects?page=${page}&limit=${limit}`, { method: "GET" });
113
+ if (!response.ok) {
114
+ if (response.status === 401 || response.status === 403) {
115
+ const errorText$1 = await response.text().catch(() => "");
116
+ let errorMessage = "Authentication failed - check your API key configuration\n\n";
117
+ errorMessage += "Common issues:\n";
118
+ errorMessage += " • Missing or invalid API key in inkeep.config.ts\n";
119
+ errorMessage += " • API key does not have access to this tenant\n";
120
+ if (errorText$1) errorMessage += `\nServer response: ${errorText$1}`;
121
+ throw new Error(errorMessage);
122
+ }
123
+ const errorText = await response.text().catch(() => "");
124
+ throw new Error(`Failed to list projects: ${response.statusText}${errorText ? `\n${errorText}` : ""}`);
125
+ }
126
+ return await response.json();
127
+ }
128
+ /**
129
+ * List all projects for the current tenant (fetches all pages)
130
+ * @returns Array of all projects
131
+ */
132
+ async listAllProjects() {
133
+ const allProjects = [];
134
+ let page = 1;
135
+ const limit = 100;
136
+ while (true) {
137
+ const result = await this.listProjects(page, limit);
138
+ allProjects.push(...result.data);
139
+ if (result.data.length < limit || allProjects.length >= result.pagination.total) break;
140
+ page++;
141
+ }
142
+ return allProjects;
143
+ }
144
+ };
145
+ var ExecutionApiClient = class ExecutionApiClient extends BaseApiClient {
146
+ constructor(apiUrl, tenantId, projectId, apiKey, isCI = false) {
147
+ super(apiUrl, tenantId, projectId, apiKey, isCI);
148
+ }
149
+ static async create(apiUrl, configPath, tenantIdOverride, projectIdOverride, isCI) {
150
+ const { validateConfiguration } = await import("./utils/config.js");
151
+ const config = await validateConfiguration(configPath);
152
+ return new ExecutionApiClient(apiUrl || config.agentsRunApiUrl, tenantIdOverride || config.tenantId, projectIdOverride || "", config.agentsRunApiKey, isCI ?? false);
153
+ }
154
+ async chatCompletion(agentId, messages, conversationId, emitOperations) {
155
+ const response = await this.authenticatedFetch(`${this.apiUrl}/v1/chat/completions`, {
156
+ method: "POST",
157
+ headers: {
158
+ Accept: "text/event-stream",
159
+ "x-inkeep-tenant-id": this.tenantId || "test-tenant-id",
160
+ "x-inkeep-project-id": this.projectId,
161
+ "x-inkeep-agent-id": agentId,
162
+ ...emitOperations && { "x-emit-operations": "true" }
163
+ },
164
+ body: JSON.stringify({
165
+ model: OPENAI_MODELS.GPT_4_1_MINI,
166
+ messages,
167
+ conversationId,
168
+ stream: true
169
+ })
170
+ });
171
+ if (!response.ok) {
172
+ const errorText = await response.text();
173
+ throw new Error(`Chat request failed: ${response.statusText}\n${errorText}`);
174
+ }
175
+ if (response.headers.get("content-type")?.includes("text/event-stream")) {
176
+ if (!response.body) throw new Error("No response body for streaming request");
177
+ return response.body;
178
+ }
179
+ const data = await response.json();
180
+ return data.choices?.[0]?.message?.content || data.result || "";
181
+ }
182
+ };
183
+
184
+ //#endregion
185
+ export { ExecutionApiClient, ManagementApiClient };
@@ -0,0 +1,139 @@
1
+ import { cloneTemplate, cloneTemplateLocal, getAvailableTemplates } from "../utils/templates.js";
2
+ import { ANTHROPIC_MODELS, GOOGLE_MODELS, OPENAI_MODELS } from "@inkeep/agents-core";
3
+ import path from "node:path";
4
+ import * as p from "@clack/prompts";
5
+ import chalk from "chalk";
6
+ import { findUp } from "find-up";
7
+ import fs from "fs-extra";
8
+
9
+ //#region src/commands/add.ts
10
+ const defaultGoogleModelConfigurations = {
11
+ base: { model: GOOGLE_MODELS.GEMINI_2_5_FLASH },
12
+ structuredOutput: { model: GOOGLE_MODELS.GEMINI_2_5_FLASH_LITE },
13
+ summarizer: { model: GOOGLE_MODELS.GEMINI_2_5_FLASH_LITE }
14
+ };
15
+ const defaultOpenaiModelConfigurations = {
16
+ base: { model: OPENAI_MODELS.GPT_5_2 },
17
+ structuredOutput: { model: OPENAI_MODELS.GPT_4_1_MINI },
18
+ summarizer: { model: OPENAI_MODELS.GPT_4_1_NANO }
19
+ };
20
+ const defaultAnthropicModelConfigurations = {
21
+ base: { model: ANTHROPIC_MODELS.CLAUDE_SONNET_4_5 },
22
+ structuredOutput: { model: ANTHROPIC_MODELS.CLAUDE_SONNET_4_5 },
23
+ summarizer: { model: ANTHROPIC_MODELS.CLAUDE_SONNET_4_5 }
24
+ };
25
+ async function addCommand(options) {
26
+ const projectTemplates = await getAvailableTemplates("template-projects", options.localPrefix);
27
+ const mcpTemplates = await getAvailableTemplates("template-mcps", options.localPrefix);
28
+ if (!options.project && !options.mcp) {
29
+ console.log(chalk.cyan("\nUsage:\n"));
30
+ console.log(chalk.white("Add a project with: inkeep add --project <project-template>"));
31
+ console.log(chalk.gray(" Example: inkeep add --project docs-assistant\n"));
32
+ console.log(chalk.yellow("Available project templates:"));
33
+ for (const template of projectTemplates) console.log(chalk.gray(` • ${template}`));
34
+ console.log(chalk.white("\nAdd an MCP server with: inkeep add --mcp <mcp-template>"));
35
+ console.log(chalk.gray(" Example: inkeep add --mcp slack\n"));
36
+ console.log(chalk.yellow("Available MCP templates:"));
37
+ for (const template of mcpTemplates) console.log(chalk.gray(` • ${template}`));
38
+ process.exit(0);
39
+ } else {
40
+ if (options.project && !projectTemplates.includes(options.project)) {
41
+ console.error(`❌ Project template "${options.project}" not found`);
42
+ process.exit(1);
43
+ }
44
+ if (options.mcp && !mcpTemplates.includes(options.mcp)) {
45
+ console.error(`❌ MCP template "${options.mcp}" not found`);
46
+ process.exit(1);
47
+ }
48
+ if (options.project) await addProjectTemplate(projectTemplates, options.project, options.targetPath, options.localPrefix);
49
+ if (options.mcp) await addMcpTemplate(mcpTemplates, options.mcp, options.targetPath, options.localPrefix);
50
+ return;
51
+ }
52
+ }
53
+ async function checkTemplateDir(templateDir, commandType) {
54
+ const s = p.spinner();
55
+ if (await fs.pathExists(templateDir)) {
56
+ if (!await p.confirm({ message: `Directory "${templateDir}" already exists. Do you want to overwrite it?` })) {
57
+ p.cancel(`You can specify a different target path like: \`inkeep add --${commandType} <${commandType}-template> --target-path <path>\``);
58
+ process.exit(0);
59
+ }
60
+ s.start("Cleaning existing directory...");
61
+ await fs.emptyDir(templateDir);
62
+ s.stop();
63
+ }
64
+ }
65
+ function buildTemplateUrl(templateType, templateName) {
66
+ return `https://github.com/inkeep/agents/agents-cookbook/${templateType}/${templateName}`;
67
+ }
68
+ async function addProjectTemplate(availableTemplates, template, targetPath, localPrefix) {
69
+ if (!template) {
70
+ console.log(chalk.yellow("Available templates:"));
71
+ for (const template$1 of availableTemplates) console.log(chalk.gray(` • ${template$1}`));
72
+ process.exit(0);
73
+ } else {
74
+ if (!availableTemplates.includes(template)) {
75
+ console.error(`❌ Template "${template}" not found`);
76
+ process.exit(1);
77
+ }
78
+ const s = p.spinner();
79
+ const anthropicKey = process.env.ANTHROPIC_API_KEY;
80
+ const openAiKey = process.env.OPENAI_API_KEY;
81
+ const googleKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY;
82
+ let defaultModelSettings = {};
83
+ if (anthropicKey) defaultModelSettings = defaultAnthropicModelConfigurations;
84
+ else if (openAiKey) defaultModelSettings = defaultOpenaiModelConfigurations;
85
+ else if (googleKey) defaultModelSettings = defaultGoogleModelConfigurations;
86
+ const contentReplacements = [{
87
+ filePath: "index.ts",
88
+ replacements: { models: defaultModelSettings }
89
+ }];
90
+ if (Object.keys(defaultModelSettings).length === 0) console.error("❌ No AI provider key found in environment variables. Please set one of: ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_GENERATIVE_AI_API_KEY");
91
+ const projectDirectory = await findAppDirectory("project");
92
+ const baseDir = targetPath || projectDirectory;
93
+ const templateDir = path.join(baseDir, template);
94
+ await checkTemplateDir(templateDir, "project");
95
+ if (targetPath && !await fs.pathExists(baseDir)) try {
96
+ await fs.mkdir(baseDir, { recursive: true });
97
+ } catch (error) {
98
+ console.error(`❌ Failed to create target directory "${baseDir}": ${error instanceof Error ? error.message : "Unknown error"}`);
99
+ process.exit(1);
100
+ }
101
+ s.start("Adding template...");
102
+ if (localPrefix && localPrefix.length > 0) await cloneTemplateLocal(path.join(localPrefix, "template-projects", template), templateDir, contentReplacements);
103
+ else await cloneTemplate(buildTemplateUrl("template-projects", template), templateDir, contentReplacements);
104
+ s.stop(`Template "${template}" added to ${templateDir}`);
105
+ return;
106
+ }
107
+ }
108
+ async function addMcpTemplate(availableTemplates, template, targetPath, localPrefix) {
109
+ if (!template) {
110
+ console.log(chalk.yellow("Available templates:"));
111
+ for (const template$1 of availableTemplates) console.log(chalk.gray(` • ${template$1}`));
112
+ process.exit(0);
113
+ }
114
+ if (!targetPath) {
115
+ const foundPath = await findAppDirectory("mcp");
116
+ targetPath = path.join(foundPath, template);
117
+ }
118
+ await checkTemplateDir(targetPath, "mcp");
119
+ const s = p.spinner();
120
+ s.start("Adding template...");
121
+ if (localPrefix && localPrefix.length > 0) await cloneTemplateLocal(path.join(localPrefix, "template-mcps", template), targetPath);
122
+ else await cloneTemplate(buildTemplateUrl("template-mcps", template), targetPath);
123
+ s.stop(`MCP template "${template}" added to ${targetPath}`);
124
+ }
125
+ async function findAppDirectory(type) {
126
+ const searchPath = type === "project" ? "src/projects" : "apps/mcp/app";
127
+ const directory = await findUp(searchPath, { type: "directory" });
128
+ if (!directory || !directory.includes(searchPath)) {
129
+ console.log(chalk.yellow(`⚠️ No ${type} directory found.`));
130
+ if (!await p.confirm({ message: `Do you want to add to ${process.cwd()} instead?` })) {
131
+ p.cancel("Operation cancelled");
132
+ process.exit(0);
133
+ }
134
+ }
135
+ return directory || process.cwd();
136
+ }
137
+
138
+ //#endregion
139
+ export { addCommand, addMcpTemplate, addProjectTemplate, checkTemplateDir, defaultAnthropicModelConfigurations, defaultGoogleModelConfigurations, defaultOpenaiModelConfigurations, findAppDirectory };
@@ -0,0 +1,86 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import chalk from "chalk";
4
+
5
+ //#region src/commands/config.ts
6
+ async function configGetCommand(key, options) {
7
+ const configPath = options?.config || options?.configFilePath || join(process.cwd(), "inkeep.config.ts");
8
+ if (!existsSync(configPath)) {
9
+ console.error(chalk.red("No configuration file found."));
10
+ console.log(chalk.gray("Run \"inkeep init\" to create one, or specify a config file with --config"));
11
+ process.exit(1);
12
+ }
13
+ try {
14
+ const content = readFileSync(configPath, "utf-8");
15
+ const tenantIdMatch = content.match(/tenantId:\s*['"]([^'"]+)['"]/);
16
+ const apiUrlMatch = content.match(/apiUrl:\s*['"]([^'"]+)['"]/);
17
+ const config = {
18
+ tenantId: tenantIdMatch ? tenantIdMatch[1] : void 0,
19
+ apiUrl: apiUrlMatch ? apiUrlMatch[1] : void 0
20
+ };
21
+ if (key) {
22
+ const value = config[key];
23
+ if (value !== void 0) console.log(value);
24
+ else {
25
+ console.error(chalk.red(`Unknown configuration key: ${key}`));
26
+ console.log(chalk.gray("Available keys: tenantId, apiUrl"));
27
+ process.exit(1);
28
+ }
29
+ } else {
30
+ console.log(chalk.cyan("Current configuration:"));
31
+ console.log(chalk.gray(" Config file:"), configPath);
32
+ console.log(chalk.gray(" Tenant ID:"), config.tenantId || chalk.yellow("(not set)"));
33
+ console.log(chalk.gray(" API URL:"), config.apiUrl || chalk.yellow("(not set)"));
34
+ }
35
+ } catch (error) {
36
+ console.error(chalk.red("Failed to read configuration:"), error);
37
+ process.exit(1);
38
+ }
39
+ }
40
+ async function configSetCommand(key, value, options) {
41
+ const configPath = options?.config || options?.configFilePath || join(process.cwd(), "inkeep.config.ts");
42
+ if (!["tenantId", "apiUrl"].includes(key)) {
43
+ console.error(chalk.red(`Invalid configuration key: ${key}`));
44
+ console.log(chalk.gray("Available keys: tenantId, apiUrl"));
45
+ process.exit(1);
46
+ }
47
+ if (key === "apiUrl") try {
48
+ new URL(value);
49
+ } catch {
50
+ console.error(chalk.red("Invalid URL format"));
51
+ process.exit(1);
52
+ }
53
+ if (!existsSync(configPath)) {
54
+ const configContent = `import { defineConfig } from '@inkeep/agents-cli';
55
+
56
+ export default defineConfig({
57
+ tenantId: '${key === "tenantId" ? value : ""}',
58
+ apiUrl: '${key === "apiUrl" ? value : "http://localhost:3002"}',
59
+ });
60
+ `;
61
+ try {
62
+ writeFileSync(configPath, configContent);
63
+ console.log(chalk.green("✓"), `Created config file and set ${key} to:`, chalk.cyan(value));
64
+ } catch (error) {
65
+ console.error(chalk.red("Failed to create config file:"), error);
66
+ process.exit(1);
67
+ }
68
+ } else try {
69
+ let content = readFileSync(configPath, "utf-8");
70
+ if (key === "tenantId") if (content.includes("tenantId:")) content = content.replace(/tenantId:\s*['"][^'"]*['"]/, `tenantId: '${value}'`);
71
+ else content = content.replace(/defineConfig\s*\(\s*{/, `defineConfig({\n tenantId: '${value}',`);
72
+ else if (key === "apiUrl") if (content.includes("apiUrl:")) content = content.replace(/apiUrl:\s*['"][^'"]*['"]/, `apiUrl: '${value}'`);
73
+ else content = content.replace(/defineConfig\s*\(\s*{/, `defineConfig({\n apiUrl: '${value}',`);
74
+ writeFileSync(configPath, content);
75
+ console.log(chalk.green("✓"), `Updated ${key} to:`, chalk.cyan(value));
76
+ } catch (error) {
77
+ console.error(chalk.red("Failed to update config file:"), error);
78
+ process.exit(1);
79
+ }
80
+ }
81
+ async function configListCommand(options) {
82
+ await configGetCommand(void 0, options);
83
+ }
84
+
85
+ //#endregion
86
+ export { configGetCommand, configListCommand, configSetCommand };
@@ -0,0 +1,259 @@
1
+ import { createRequire } from "node:module";
2
+ import { existsSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import * as p from "@clack/prompts";
5
+ import chalk from "chalk";
6
+ import fs from "fs-extra";
7
+ import { fork } from "node:child_process";
8
+ import open from "open";
9
+
10
+ //#region src/commands/dev.ts
11
+ const require = createRequire(import.meta.url);
12
+ async function waitForServer(host, port, maxAttempts = 60) {
13
+ for (let i = 0; i < maxAttempts; i++) {
14
+ try {
15
+ const response = await fetch(`http://${host}:${port}`, {
16
+ method: "GET",
17
+ signal: AbortSignal.timeout(2e3)
18
+ });
19
+ if (response.ok) {
20
+ const text = await response.text();
21
+ if (text && text.length > 0 && text.includes("<!DOCTYPE html>")) {
22
+ await new Promise((resolve$1) => setTimeout(resolve$1, 1e3));
23
+ return true;
24
+ }
25
+ }
26
+ } catch {}
27
+ await new Promise((resolve$1) => setTimeout(resolve$1, 500));
28
+ }
29
+ return false;
30
+ }
31
+ function resolveWebRuntime(isRoot = false) {
32
+ try {
33
+ const root = dirname(require.resolve("@inkeep/agents-manage-ui/package.json"));
34
+ if (isRoot) return root;
35
+ return join(root, ".next/standalone/agents-manage-ui");
36
+ } catch (err) {
37
+ throw new Error(`Could not find @inkeep/agents-manage-ui package. Please install it first:
38
+
39
+ npm install @inkeep/agents-manage-ui
40
+ # or
41
+ pnpm add @inkeep/agents-manage-ui
42
+
43
+ Error: ${err instanceof Error ? err.message : "Unknown error"}`);
44
+ }
45
+ }
46
+ async function startWebApp({ port, host, openBrowser }) {
47
+ console.log("");
48
+ const s = p.spinner();
49
+ s.start("Starting dashboard server...");
50
+ try {
51
+ const rt = resolveWebRuntime();
52
+ const entry = join(rt, "server.js");
53
+ if (!existsSync(entry)) {
54
+ s.stop("Dashboard server not found");
55
+ console.error(chalk.red("The dashboard server has not been built yet."));
56
+ process.exit(1);
57
+ }
58
+ s.stop("Starting dashboard server...");
59
+ console.log("");
60
+ const child = fork(entry, [], {
61
+ cwd: rt,
62
+ env: {
63
+ ...process.env,
64
+ NODE_ENV: "production",
65
+ PORT: String(port),
66
+ HOSTNAME: host
67
+ },
68
+ stdio: "inherit"
69
+ });
70
+ console.log(chalk.green(`🚀 Dashboard server started at http://${host}:${port}`));
71
+ console.log("");
72
+ console.log(chalk.gray("Press Ctrl+C to stop the server"));
73
+ console.log("");
74
+ if (openBrowser) {
75
+ console.log(chalk.gray("Waiting for server to be ready..."));
76
+ if (await waitForServer(host, port)) await open(`http://${host}:${port}`);
77
+ else {
78
+ console.log(chalk.yellow("⚠️ Server did not respond in time, skipping browser open"));
79
+ console.log(chalk.gray(` You can manually open: http://${host}:${port}`));
80
+ }
81
+ }
82
+ process.on("SIGINT", () => {
83
+ console.log("");
84
+ console.log(chalk.yellow("\n🛑 Stopping dashboard server..."));
85
+ child.kill("SIGINT");
86
+ process.exit(0);
87
+ });
88
+ process.on("SIGTERM", () => {
89
+ child.kill("SIGTERM");
90
+ process.exit(0);
91
+ });
92
+ return child;
93
+ } catch (error) {
94
+ s.stop("Failed to start dashboard server");
95
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
96
+ process.exit(1);
97
+ }
98
+ }
99
+ async function buildNextApp({ outputDir }) {
100
+ console.log("");
101
+ const s = p.spinner();
102
+ s.start("Building Standalone build...");
103
+ try {
104
+ const standalonePath = join(dirname(require.resolve("@inkeep/agents-manage-ui/package.json")), ".next/standalone/agents-manage-ui");
105
+ if (!existsSync(standalonePath)) {
106
+ s.stop("Standalone build not found");
107
+ console.error(chalk.red("The standalone build has not been created yet."));
108
+ console.error(chalk.yellow("Please build the dashboard first:"));
109
+ console.error(chalk.gray(" cd node_modules/@inkeep/agents-manage-ui"));
110
+ console.error(chalk.gray(" npm run build"));
111
+ process.exit(1);
112
+ }
113
+ if (existsSync(outputDir)) await fs.remove(outputDir);
114
+ if (existsSync(outputDir)) await fs.remove(outputDir);
115
+ await fs.ensureDir(outputDir);
116
+ await fs.copy(standalonePath, outputDir);
117
+ await fs.writeJson(join(outputDir, "package.json"), {
118
+ name: "inkeep-dashboard",
119
+ version: "1.0.0",
120
+ scripts: { start: "node server.js" },
121
+ dependencies: {}
122
+ }, { spaces: 2 });
123
+ await fs.writeFile(join(outputDir, "README.md"), `
124
+ ## Environment Variables
125
+
126
+ Make sure to set these in your Vercel project settings:
127
+ - INKEEP_API_URL
128
+ - INKEEP_TENANT_ID
129
+ - Any other variables from your .env file
130
+ `);
131
+ s.stop(`Build created at ${outputDir}/`);
132
+ console.log("");
133
+ console.log(chalk.green("✅ Build completed successfully!"));
134
+ console.log("");
135
+ console.log(chalk.blue("📁 To run your dashboard:"));
136
+ console.log(chalk.gray(" cd"), chalk.white(outputDir));
137
+ console.log(chalk.gray(" npm start"));
138
+ console.log("");
139
+ console.log(chalk.blue("🌐 Or with pnpm:"));
140
+ console.log(chalk.gray(" cd"), chalk.white(outputDir));
141
+ console.log(chalk.gray(" pnpm start"));
142
+ console.log("");
143
+ console.log(chalk.yellow("📖 See README.md for deployment instructions"));
144
+ console.log("");
145
+ } catch (error) {
146
+ s.stop("Failed to build dashboard");
147
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
148
+ throw error;
149
+ }
150
+ }
151
+ async function exportNextApp({ outputDir }) {
152
+ console.log("");
153
+ const s = p.spinner();
154
+ s.start("Exporting Next.js project...");
155
+ try {
156
+ const root = dirname(require.resolve("@inkeep/agents-manage-ui/package.json"));
157
+ if (!existsSync(root)) {
158
+ s.stop("Source project not found");
159
+ console.error(chalk.red("The @inkeep/agents-manage-ui package was not found."));
160
+ console.error(chalk.yellow("Please install it first:"));
161
+ console.error(chalk.gray(" npm install @inkeep/agents-manage-ui"));
162
+ console.error(chalk.gray(" # or"));
163
+ console.error(chalk.gray(" pnpm add @inkeep/agents-manage-ui"));
164
+ process.exit(1);
165
+ }
166
+ if (existsSync(outputDir)) await fs.remove(outputDir);
167
+ await fs.ensureDir(outputDir);
168
+ const items = await fs.readdir(root);
169
+ for (const item of items) {
170
+ const srcPath = join(root, item);
171
+ const destPath = join(outputDir, item);
172
+ if (item === ".next" || item === "node_modules" || item === "dist") continue;
173
+ if ((await fs.stat(srcPath)).isDirectory()) await fs.copy(srcPath, destPath);
174
+ else await fs.copy(srcPath, destPath);
175
+ }
176
+ await fs.writeFile(join(outputDir, "README.md"), `# Inkeep Dashboard
177
+
178
+ This is an exported copy of the Inkeep Dashboard UI.
179
+
180
+ ## Getting Started
181
+
182
+ 1. Install dependencies:
183
+ \`\`\`bash
184
+ npm install
185
+ # or
186
+ pnpm install
187
+ \`\`\`
188
+
189
+ 2. Start the development server:
190
+ \`\`\`bash
191
+ npm run dev
192
+ # or
193
+ pnpm dev
194
+ \`\`\`
195
+
196
+ 3. Build for production:
197
+ \`\`\`bash
198
+ npm run build
199
+ # or
200
+ pnpm build
201
+ \`\`\`
202
+
203
+ ## Environment Variables
204
+
205
+ Make sure to set these environment variables:
206
+ - \`INKEEP_API_URL\` - Your Inkeep API URL
207
+ - \`INKEEP_TENANT_ID\` - Your tenant ID
208
+ - Any other variables from your .env file
209
+
210
+ ## Deployment
211
+
212
+ This project can be deployed to any platform that supports Next.js:
213
+ - Vercel
214
+ - Netlify
215
+ - AWS Amplify
216
+ - Railway
217
+ - And more...
218
+ `);
219
+ s.stop(`Project exported to ${outputDir}/`);
220
+ console.log("");
221
+ console.log(chalk.green("✅ Export completed successfully!"));
222
+ console.log("");
223
+ console.log(chalk.blue("📁 To get started:"));
224
+ console.log(chalk.gray(" cd"), chalk.white(outputDir));
225
+ console.log(chalk.gray(" npm install"));
226
+ console.log(chalk.gray(" npm run dev"));
227
+ console.log("");
228
+ console.log(chalk.yellow("📖 See README.md for more instructions"));
229
+ console.log("");
230
+ } catch (error) {
231
+ s.stop("Failed to export project");
232
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
233
+ throw error;
234
+ }
235
+ }
236
+ async function devCommand(options) {
237
+ const { port, host, build, outputDir, path: path$1, export: exportFlag, openBrowser } = options;
238
+ if (path$1) {
239
+ const rt = resolveWebRuntime(true);
240
+ console.log(rt);
241
+ return;
242
+ }
243
+ if (exportFlag) {
244
+ await exportNextApp({ outputDir });
245
+ return;
246
+ }
247
+ if (build) {
248
+ await buildNextApp({ outputDir });
249
+ return;
250
+ }
251
+ await startWebApp({
252
+ port,
253
+ host,
254
+ openBrowser
255
+ });
256
+ }
257
+
258
+ //#endregion
259
+ export { devCommand };