@inkeep/agents-cli 0.39.5 → 0.41.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 +143 -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 +9 -24
  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,120 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { homedir } from "node:os";
5
+ import http from "node:http";
6
+
7
+ //#region src/utils/mcp-runner.ts
8
+ /**
9
+ * MCP Runner - Loads and starts MCP servers from an agent file
10
+ * This is executed as a subprocess by the CLI
11
+ */
12
+ const MCP_DIR = join(homedir(), ".inkeep", "mcp");
13
+ const REGISTRY_FILE = join(MCP_DIR, "servers.json");
14
+ if (!existsSync(MCP_DIR)) mkdirSync(MCP_DIR, { recursive: true });
15
+ async function startServers(agentPath$1) {
16
+ try {
17
+ const module = await import(agentPath$1);
18
+ const servers = module.servers || module.tools || [];
19
+ let agentId = "unknown";
20
+ if (module.agent && typeof module.agent.getId === "function") agentId = module.agent.getId();
21
+ const registeredServers = [];
22
+ let nextPort = 3100;
23
+ for (const server of servers) {
24
+ if (!server) continue;
25
+ const name = server.name || "unnamed";
26
+ const id = server.id || name;
27
+ const description = server.description || "";
28
+ if (typeof server.execute === "function" || typeof server.init === "function" || !server.serverUrl) {
29
+ const port = server.port || nextPort++;
30
+ if (typeof server.init === "function") await server.init();
31
+ if (typeof server.execute === "function") http.createServer(async (req, res) => {
32
+ if (req.method === "POST" && req.url === "/mcp") {
33
+ let body = "";
34
+ req.on("data", (chunk) => body += chunk);
35
+ req.on("end", async () => {
36
+ try {
37
+ const params = JSON.parse(body);
38
+ const result = await server.execute(params);
39
+ res.writeHead(200, { "Content-Type": "application/json" });
40
+ res.end(JSON.stringify({ result }));
41
+ } catch (error) {
42
+ res.writeHead(500, { "Content-Type": "application/json" });
43
+ res.end(JSON.stringify({ error: error.message }));
44
+ }
45
+ });
46
+ } else {
47
+ res.writeHead(404);
48
+ res.end("Not Found");
49
+ }
50
+ }).listen(port, () => {
51
+ console.log(JSON.stringify({
52
+ type: "server_started",
53
+ name,
54
+ port,
55
+ deployment: "local"
56
+ }));
57
+ });
58
+ registeredServers.push({
59
+ pid: process.pid,
60
+ agentId,
61
+ toolId: id,
62
+ name,
63
+ port,
64
+ deployment: "local",
65
+ transport: "http",
66
+ command: agentPath$1,
67
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
68
+ description
69
+ });
70
+ } else {
71
+ registeredServers.push({
72
+ pid: process.pid,
73
+ agentId,
74
+ toolId: id,
75
+ name,
76
+ serverUrl: server.serverUrl || server.getServerUrl?.(),
77
+ deployment: "remote",
78
+ transport: server.transport || "http",
79
+ command: agentPath$1,
80
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
81
+ description
82
+ });
83
+ console.log(JSON.stringify({
84
+ type: "server_registered",
85
+ name,
86
+ serverUrl: server.serverUrl,
87
+ deployment: "remote"
88
+ }));
89
+ }
90
+ }
91
+ writeFileSync(REGISTRY_FILE, JSON.stringify({ servers: registeredServers }, null, 2));
92
+ console.log(JSON.stringify({
93
+ type: "all_started",
94
+ count: registeredServers.length
95
+ }));
96
+ process.stdin.resume();
97
+ process.on("SIGINT", () => {
98
+ console.log(JSON.stringify({ type: "shutting_down" }));
99
+ process.exit(0);
100
+ });
101
+ } catch (error) {
102
+ console.error(JSON.stringify({
103
+ type: "error",
104
+ message: error.message
105
+ }));
106
+ process.exit(1);
107
+ }
108
+ }
109
+ const agentPath = process.argv[2];
110
+ if (!agentPath) {
111
+ console.error(JSON.stringify({
112
+ type: "error",
113
+ message: "Agent path is required"
114
+ }));
115
+ process.exit(1);
116
+ }
117
+ startServers(agentPath);
118
+
119
+ //#endregion
120
+ export { };
@@ -0,0 +1,182 @@
1
+ import { ANTHROPIC_MODELS, GOOGLE_MODELS, OPENAI_MODELS } from "@inkeep/agents-core";
2
+ import * as p from "@clack/prompts";
3
+
4
+ //#region src/utils/model-config.ts
5
+ const defaultGeminiModelConfigurations = {
6
+ base: { model: GOOGLE_MODELS.GEMINI_2_5_FLASH },
7
+ structuredOutput: { model: GOOGLE_MODELS.GEMINI_2_5_FLASH_LITE },
8
+ summarizer: { model: GOOGLE_MODELS.GEMINI_2_5_FLASH_LITE }
9
+ };
10
+ const defaultOpenaiModelConfigurations = {
11
+ base: { model: OPENAI_MODELS.GPT_5_2 },
12
+ structuredOutput: { model: OPENAI_MODELS.GPT_4_1_MINI },
13
+ summarizer: { model: OPENAI_MODELS.GPT_4_1_NANO }
14
+ };
15
+ const defaultAnthropicModelConfigurations = {
16
+ base: { model: ANTHROPIC_MODELS.CLAUDE_SONNET_4_5 },
17
+ structuredOutput: { model: ANTHROPIC_MODELS.CLAUDE_SONNET_4_5 },
18
+ summarizer: { model: ANTHROPIC_MODELS.CLAUDE_SONNET_4_5 }
19
+ };
20
+ /**
21
+ * Prompt user for model configuration (providers and model selection)
22
+ * This is shared between init and push commands
23
+ */
24
+ async function promptForModelConfiguration() {
25
+ const providers = await p.multiselect({
26
+ message: "Which AI providers would you like to configure?",
27
+ options: [
28
+ {
29
+ value: "anthropic",
30
+ label: "Anthropic (Claude)"
31
+ },
32
+ {
33
+ value: "openai",
34
+ label: "OpenAI (GPT)"
35
+ },
36
+ {
37
+ value: "google",
38
+ label: "Google (Gemini)"
39
+ }
40
+ ],
41
+ required: true
42
+ });
43
+ if (p.isCancel(providers)) {
44
+ p.cancel("Operation cancelled");
45
+ process.exit(0);
46
+ }
47
+ const anthropicModels = [
48
+ {
49
+ label: "Claude Opus 4.5",
50
+ value: ANTHROPIC_MODELS.CLAUDE_OPUS_4_5
51
+ },
52
+ {
53
+ label: "Claude Opus 4.1",
54
+ value: ANTHROPIC_MODELS.CLAUDE_OPUS_4_1
55
+ },
56
+ {
57
+ label: "Claude Sonnet 4.5",
58
+ value: ANTHROPIC_MODELS.CLAUDE_SONNET_4_5
59
+ },
60
+ {
61
+ label: "Claude Sonnet 4",
62
+ value: ANTHROPIC_MODELS.CLAUDE_SONNET_4
63
+ },
64
+ {
65
+ label: "Claude Haiku 4.5",
66
+ value: ANTHROPIC_MODELS.CLAUDE_HAIKU_4_5
67
+ },
68
+ {
69
+ label: "Claude Haiku 3.5",
70
+ value: ANTHROPIC_MODELS.CLAUDE_3_5_HAIKU
71
+ }
72
+ ];
73
+ const openaiModels = [
74
+ {
75
+ label: "GPT-5.2",
76
+ value: OPENAI_MODELS.GPT_5_2
77
+ },
78
+ {
79
+ label: "GPT-5.1",
80
+ value: OPENAI_MODELS.GPT_5_1
81
+ },
82
+ {
83
+ label: "GPT-4.1",
84
+ value: OPENAI_MODELS.GPT_4_1
85
+ },
86
+ {
87
+ label: "GPT-4.1 Mini",
88
+ value: OPENAI_MODELS.GPT_4_1_MINI
89
+ },
90
+ {
91
+ label: "GPT-4.1 Nano",
92
+ value: OPENAI_MODELS.GPT_4_1_NANO
93
+ },
94
+ {
95
+ label: "GPT-5",
96
+ value: OPENAI_MODELS.GPT_5
97
+ },
98
+ {
99
+ label: "GPT-5 Mini",
100
+ value: OPENAI_MODELS.GPT_5_MINI
101
+ },
102
+ {
103
+ label: "GPT-5 Nano",
104
+ value: OPENAI_MODELS.GPT_5_NANO
105
+ }
106
+ ];
107
+ const googleModels = [
108
+ {
109
+ label: "Gemini 3 Pro Preview",
110
+ value: GOOGLE_MODELS.GEMINI_3_PRO_PREVIEW
111
+ },
112
+ {
113
+ label: "Gemini 3 Flash Preview",
114
+ value: GOOGLE_MODELS.GEMINI_3_FLASH_PREVIEW
115
+ },
116
+ {
117
+ label: "Gemini 2.5 Pro",
118
+ value: GOOGLE_MODELS.GEMINI_2_5_PRO
119
+ },
120
+ {
121
+ label: "Gemini 2.5 Flash",
122
+ value: GOOGLE_MODELS.GEMINI_2_5_FLASH
123
+ },
124
+ {
125
+ label: "Gemini 2.5 Flash Lite",
126
+ value: GOOGLE_MODELS.GEMINI_2_5_FLASH_LITE
127
+ }
128
+ ];
129
+ const availableModels = [];
130
+ if (providers.includes("anthropic")) availableModels.push(...anthropicModels);
131
+ if (providers.includes("openai")) availableModels.push(...openaiModels);
132
+ if (providers.includes("google")) availableModels.push(...googleModels);
133
+ const baseModel = await p.select({
134
+ message: "Select your default model for general tasks (required):",
135
+ options: availableModels
136
+ });
137
+ if (p.isCancel(baseModel)) {
138
+ p.cancel("Operation cancelled");
139
+ process.exit(0);
140
+ }
141
+ const configureOptionalModels = await p.confirm({
142
+ message: "Would you like to configure optional models for structured output and summaries?",
143
+ initialValue: false
144
+ });
145
+ if (p.isCancel(configureOptionalModels)) {
146
+ p.cancel("Operation cancelled");
147
+ process.exit(0);
148
+ }
149
+ let structuredOutputModel = null;
150
+ let summarizerModel = null;
151
+ if (configureOptionalModels) {
152
+ const optionalChoices = [...availableModels, {
153
+ label: "Use base model",
154
+ value: null
155
+ }];
156
+ const structuredOutputResponse = await p.select({
157
+ message: "Select your model for structured output tasks (or use base model):",
158
+ options: optionalChoices
159
+ });
160
+ if (p.isCancel(structuredOutputResponse)) {
161
+ p.cancel("Operation cancelled");
162
+ process.exit(0);
163
+ }
164
+ structuredOutputModel = structuredOutputResponse;
165
+ const summarizerResponse = await p.select({
166
+ message: "Select your model for summaries and quick tasks (or use base model):",
167
+ options: optionalChoices
168
+ });
169
+ if (p.isCancel(summarizerResponse)) {
170
+ p.cancel("Operation cancelled");
171
+ process.exit(0);
172
+ }
173
+ summarizerModel = summarizerResponse;
174
+ }
175
+ const modelSettings = { base: { model: baseModel } };
176
+ if (structuredOutputModel) modelSettings.structuredOutput = { model: structuredOutputModel };
177
+ if (summarizerModel) modelSettings.summarizer = { model: summarizerModel };
178
+ return { modelSettings };
179
+ }
180
+
181
+ //#endregion
182
+ export { defaultAnthropicModelConfigurations, defaultGeminiModelConfigurations, defaultOpenaiModelConfigurations, promptForModelConfiguration };
@@ -0,0 +1,58 @@
1
+ import { DEFAULT_PACKAGE_NAME } from "./version-check.js";
2
+ import { exec } from "node:child_process";
3
+ import { promisify } from "node:util";
4
+
5
+ //#region src/utils/package-manager.ts
6
+ const execAsync = promisify(exec);
7
+ /**
8
+ * Detect which package manager was used to install the CLI globally
9
+ */
10
+ async function detectPackageManager() {
11
+ for (const manager of [
12
+ "pnpm",
13
+ "bun",
14
+ "npm",
15
+ "yarn"
16
+ ]) try {
17
+ if (manager === "npm") {
18
+ const { stdout } = await execAsync(`npm list -g ${DEFAULT_PACKAGE_NAME} --depth=0`);
19
+ if (stdout.includes(DEFAULT_PACKAGE_NAME)) return "npm";
20
+ } else if (manager === "pnpm") {
21
+ const { stdout } = await execAsync(`pnpm list -g ${DEFAULT_PACKAGE_NAME} --depth=0`);
22
+ if (stdout.includes(DEFAULT_PACKAGE_NAME)) return "pnpm";
23
+ } else if (manager === "bun") {
24
+ const { stdout } = await execAsync("bun pm ls -g");
25
+ if (stdout.includes(DEFAULT_PACKAGE_NAME)) return "bun";
26
+ } else if (manager === "yarn") {
27
+ const { stdout } = await execAsync("yarn global list");
28
+ if (stdout.includes(DEFAULT_PACKAGE_NAME)) return "yarn";
29
+ }
30
+ } catch {}
31
+ return null;
32
+ }
33
+ /**
34
+ * Get the update command for a specific package manager
35
+ */
36
+ function getUpdateCommand(manager, packageName = DEFAULT_PACKAGE_NAME) {
37
+ switch (manager) {
38
+ case "npm": return `npm install -g ${packageName}@latest`;
39
+ case "pnpm": return `pnpm add -g ${packageName}@latest`;
40
+ case "bun": return `bun add -g ${packageName}@latest`;
41
+ case "yarn": return `yarn global add ${packageName}@latest`;
42
+ }
43
+ }
44
+ /**
45
+ * Execute update command
46
+ */
47
+ async function executeUpdate(manager) {
48
+ if (![
49
+ "npm",
50
+ "pnpm",
51
+ "bun",
52
+ "yarn"
53
+ ].includes(manager)) throw new Error(`Unsupported package manager: ${manager}`);
54
+ await execAsync(getUpdateCommand(manager));
55
+ }
56
+
57
+ //#endregion
58
+ export { detectPackageManager, executeUpdate, getUpdateCommand };
@@ -0,0 +1,85 @@
1
+ import { getCredentialExpiryInfo, loadCredentials } from "./credentials.js";
2
+ import { ProfileManager } from "./profiles/profile-manager.js";
3
+ import "./profiles/index.js";
4
+ import { existsSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import chalk from "chalk";
7
+ import dotenv from "dotenv";
8
+
9
+ //#region src/utils/profile-config.ts
10
+ async function resolveProfileConfig(options = {}) {
11
+ const profileManager = new ProfileManager();
12
+ let profile;
13
+ let profileName;
14
+ try {
15
+ if (options.profileName) {
16
+ const foundProfile = profileManager.getProfile(options.profileName);
17
+ if (!foundProfile) {
18
+ console.error(chalk.red(`Profile '${options.profileName}' not found.`));
19
+ console.log(chalk.gray("Run \"inkeep profile list\" to see available profiles."));
20
+ process.exit(1);
21
+ }
22
+ profile = foundProfile;
23
+ profileName = options.profileName;
24
+ } else {
25
+ profile = profileManager.getActiveProfile();
26
+ profileName = profile.name;
27
+ }
28
+ } catch {
29
+ return {
30
+ profileName: "default",
31
+ agentsManageApiUrl: "http://localhost:3002",
32
+ agentsRunApiUrl: "http://localhost:3003",
33
+ manageUiUrl: "http://localhost:3000",
34
+ environment: "development",
35
+ credentialKey: "auth-credentials",
36
+ isAuthenticated: false
37
+ };
38
+ }
39
+ const credentials = await loadCredentials(profile.credential);
40
+ let accessToken;
41
+ let isAuthenticated = false;
42
+ let authExpiry;
43
+ if (credentials) {
44
+ const expiryInfo = getCredentialExpiryInfo(credentials);
45
+ if (!expiryInfo.isExpired) {
46
+ accessToken = credentials.accessToken;
47
+ isAuthenticated = true;
48
+ authExpiry = expiryInfo.expiresIn;
49
+ }
50
+ }
51
+ if (options.projectDir && profile.environment) {
52
+ const envFile = join(options.projectDir, `.env.${profile.environment}`);
53
+ if (existsSync(envFile)) dotenv.config({ path: envFile });
54
+ }
55
+ return {
56
+ profileName,
57
+ tenantId: credentials?.organizationId,
58
+ agentsManageApiUrl: profile.remote.manageApi,
59
+ agentsRunApiUrl: profile.remote.runApi,
60
+ manageUiUrl: profile.remote.manageUi,
61
+ environment: profile.environment,
62
+ credentialKey: profile.credential,
63
+ accessToken,
64
+ isAuthenticated,
65
+ authExpiry
66
+ };
67
+ }
68
+ function logProfileConfig(config, quiet = false) {
69
+ if (quiet) return;
70
+ console.log(chalk.gray(`Using profile: ${chalk.cyan(config.profileName)}`));
71
+ console.log(chalk.gray(` Remote: ${config.agentsManageApiUrl}`));
72
+ console.log(chalk.gray(` Environment: ${config.environment}`));
73
+ if (config.isAuthenticated) {
74
+ const expiryText = config.authExpiry ? ` (expires in ${config.authExpiry})` : "";
75
+ console.log(chalk.gray(` Auth: ${chalk.green("authenticated")}${expiryText}`));
76
+ } else console.log(chalk.gray(` Auth: ${chalk.yellow("not authenticated")}`));
77
+ }
78
+ function getAuthHeaders(config) {
79
+ const headers = {};
80
+ if (config.accessToken) headers["Authorization"] = `Bearer ${config.accessToken}`;
81
+ return headers;
82
+ }
83
+
84
+ //#endregion
85
+ export { getAuthHeaders, logProfileConfig, resolveProfileConfig };
@@ -0,0 +1,4 @@
1
+ import { CLOUD_REMOTE, DEFAULT_CLOUD_PROFILE, DEFAULT_PROFILES_CONFIG, explicitRemoteSchema, profileNameSchema, profileSchema, profilesConfigSchema, remoteSchema } from "./types.js";
2
+ import { ProfileError, ProfileManager, profileManager } from "./profile-manager.js";
3
+
4
+ export { CLOUD_REMOTE, DEFAULT_CLOUD_PROFILE, DEFAULT_PROFILES_CONFIG, ProfileError, ProfileManager, explicitRemoteSchema, profileManager, profileNameSchema, profileSchema, profilesConfigSchema, remoteSchema };
@@ -0,0 +1,219 @@
1
+ import { __require } from "../../_virtual/rolldown_runtime.js";
2
+ import { CLOUD_REMOTE, DEFAULT_PROFILES_CONFIG, profileNameSchema, profilesConfigSchema } from "./types.js";
3
+ import { getLogger } from "@inkeep/agents-core";
4
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
5
+ import { dirname, join } from "node:path";
6
+ import { homedir, tmpdir } from "node:os";
7
+ import * as yaml from "yaml";
8
+
9
+ //#region src/utils/profiles/profile-manager.ts
10
+ const logger = getLogger("profile-manager");
11
+ /**
12
+ * Error thrown when profile operations fail
13
+ */
14
+ var ProfileError = class extends Error {
15
+ constructor(message, code) {
16
+ super(message);
17
+ this.code = code;
18
+ this.name = "ProfileError";
19
+ }
20
+ };
21
+ /**
22
+ * Format Zod errors into a human-readable message
23
+ * Compatible with both Zod v3 (errors) and Zod v4 (issues)
24
+ */
25
+ function formatZodErrors(error) {
26
+ const issues = error.issues ?? error.errors ?? [];
27
+ if (!Array.isArray(issues) || issues.length === 0) return error.message || "Validation failed";
28
+ return issues.map((err) => {
29
+ return `${err.path && err.path.length > 0 ? `at '${err.path.join(".")}'` : ""}: ${err.message || "Invalid value"}`.trim();
30
+ }).join("\n ");
31
+ }
32
+ /**
33
+ * ProfileManager - handles loading, saving, and managing CLI profiles
34
+ *
35
+ * Profiles are stored in ~/.inkeep/profiles.yaml and allow users to switch
36
+ * between different Inkeep deployments (cloud, local, self-hosted).
37
+ */
38
+ var ProfileManager = class {
39
+ profilesDir;
40
+ profilesPath;
41
+ constructor(options) {
42
+ this.profilesDir = options?.profilesDir ?? join(homedir(), ".inkeep");
43
+ this.profilesPath = join(this.profilesDir, "profiles.yaml");
44
+ }
45
+ /**
46
+ * Get the path to the profiles.yaml file
47
+ * Creates the ~/.inkeep directory if it doesn't exist
48
+ */
49
+ getProfilePath() {
50
+ if (!existsSync(this.profilesDir)) {
51
+ logger.info({ dir: this.profilesDir }, "Creating profiles directory");
52
+ mkdirSync(this.profilesDir, { recursive: true });
53
+ }
54
+ return this.profilesPath;
55
+ }
56
+ /**
57
+ * Check if the profiles file exists
58
+ */
59
+ profilesFileExists() {
60
+ return existsSync(this.profilesPath);
61
+ }
62
+ /**
63
+ * Load and validate profiles from YAML file
64
+ * Creates default config if file doesn't exist
65
+ */
66
+ loadProfiles() {
67
+ const profilePath = this.getProfilePath();
68
+ if (!existsSync(profilePath)) {
69
+ logger.info({}, "Profiles file not found, creating default");
70
+ this.saveProfiles(DEFAULT_PROFILES_CONFIG);
71
+ return DEFAULT_PROFILES_CONFIG;
72
+ }
73
+ let content;
74
+ try {
75
+ content = readFileSync(profilePath, "utf-8");
76
+ } catch (error) {
77
+ throw new ProfileError(`Failed to read profiles file: ${profilePath}\n${error instanceof Error ? error.message : String(error)}`, "FILE_NOT_FOUND");
78
+ }
79
+ let parsed;
80
+ try {
81
+ parsed = yaml.parse(content);
82
+ } catch (error) {
83
+ throw new ProfileError(`Failed to parse profiles.yaml: ${error instanceof Error ? error.message : String(error)}`, "PARSE_ERROR");
84
+ }
85
+ const result = profilesConfigSchema.safeParse(parsed);
86
+ if (!result.success) throw new ProfileError(`Invalid profiles.yaml:\n ${formatZodErrors(result.error)}`, "VALIDATION_ERROR");
87
+ const config = result.data;
88
+ if (!config.profiles[config.activeProfile]) throw new ProfileError(`Active profile '${config.activeProfile}' does not exist in profiles`, "VALIDATION_ERROR");
89
+ logger.info({ activeProfile: config.activeProfile }, "Profiles loaded");
90
+ return config;
91
+ }
92
+ /**
93
+ * Save profiles to YAML file atomically (write to temp, then rename)
94
+ */
95
+ saveProfiles(profiles) {
96
+ const result = profilesConfigSchema.safeParse(profiles);
97
+ if (!result.success) throw new ProfileError(`Invalid profiles configuration:\n ${formatZodErrors(result.error)}`, "VALIDATION_ERROR");
98
+ if (!profiles.profiles[profiles.activeProfile]) throw new ProfileError(`Active profile '${profiles.activeProfile}' does not exist in profiles`, "VALIDATION_ERROR");
99
+ const profilePath = this.getProfilePath();
100
+ const tempPath = join(tmpdir(), `inkeep-profiles-${Date.now()}.yaml`);
101
+ const yamlContent = yaml.stringify(profiles, {
102
+ indent: 2,
103
+ lineWidth: 0
104
+ });
105
+ try {
106
+ writeFileSync(tempPath, yamlContent, "utf-8");
107
+ renameSync(tempPath, profilePath);
108
+ logger.info({ path: profilePath }, "Profiles saved");
109
+ } catch (error) {
110
+ try {
111
+ if (existsSync(tempPath)) __require("node:fs").unlinkSync(tempPath);
112
+ } catch {}
113
+ throw new ProfileError(`Failed to save profiles: ${error instanceof Error ? error.message : String(error)}`, "WRITE_ERROR");
114
+ }
115
+ }
116
+ /**
117
+ * Get the currently active profile configuration
118
+ */
119
+ getActiveProfile() {
120
+ const config = this.loadProfiles();
121
+ const profileName = config.activeProfile;
122
+ const profile = config.profiles[profileName];
123
+ if (!profile) throw new ProfileError(`Active profile '${profileName}' not found in profiles`, "PROFILE_NOT_FOUND");
124
+ return {
125
+ name: profileName,
126
+ remote: this.resolveRemoteUrls(profile),
127
+ credential: profile.credential,
128
+ environment: profile.environment
129
+ };
130
+ }
131
+ /**
132
+ * Resolve remote URLs from profile configuration
133
+ * If remote is 'cloud', returns baked-in cloud URLs
134
+ * Otherwise returns the explicit URLs
135
+ */
136
+ resolveRemoteUrls(profile) {
137
+ if (profile.remote === "cloud") return { ...CLOUD_REMOTE };
138
+ return { ...profile.remote };
139
+ }
140
+ /**
141
+ * Get a specific profile by name
142
+ */
143
+ getProfile(name) {
144
+ const profile = this.loadProfiles().profiles[name];
145
+ if (!profile) return null;
146
+ return {
147
+ name,
148
+ remote: this.resolveRemoteUrls(profile),
149
+ credential: profile.credential,
150
+ environment: profile.environment
151
+ };
152
+ }
153
+ /**
154
+ * List all profiles with their resolved URLs
155
+ */
156
+ listProfiles() {
157
+ const config = this.loadProfiles();
158
+ return {
159
+ profiles: Object.entries(config.profiles).map(([name, profile]) => ({
160
+ name,
161
+ remote: this.resolveRemoteUrls(profile),
162
+ credential: profile.credential,
163
+ environment: profile.environment
164
+ })),
165
+ activeProfile: config.activeProfile
166
+ };
167
+ }
168
+ /**
169
+ * Add a new profile
170
+ */
171
+ addProfile(name, profile) {
172
+ const nameResult = profileNameSchema.safeParse(name);
173
+ if (!nameResult.success) throw new ProfileError(`Invalid profile name: ${formatZodErrors(nameResult.error)}`, "VALIDATION_ERROR");
174
+ const config = this.loadProfiles();
175
+ if (config.profiles[name]) throw new ProfileError(`Profile '${name}' already exists`, "PROFILE_EXISTS");
176
+ config.profiles[name] = profile;
177
+ this.saveProfiles(config);
178
+ logger.info({ name }, "Profile added");
179
+ }
180
+ /**
181
+ * Set the active profile
182
+ */
183
+ setActiveProfile(name) {
184
+ const config = this.loadProfiles();
185
+ if (!config.profiles[name]) throw new ProfileError(`Profile '${name}' does not exist`, "PROFILE_NOT_FOUND");
186
+ config.activeProfile = name;
187
+ this.saveProfiles(config);
188
+ logger.info({ name }, "Active profile set");
189
+ }
190
+ /**
191
+ * Remove a profile
192
+ * Cannot remove the currently active profile
193
+ */
194
+ removeProfile(name) {
195
+ const config = this.loadProfiles();
196
+ if (!config.profiles[name]) throw new ProfileError(`Profile '${name}' does not exist`, "PROFILE_NOT_FOUND");
197
+ if (config.activeProfile === name) throw new ProfileError(`Cannot remove active profile '${name}'. Switch to a different profile first.`, "ACTIVE_PROFILE_DELETE");
198
+ delete config.profiles[name];
199
+ this.saveProfiles(config);
200
+ logger.info({ name }, "Profile removed");
201
+ }
202
+ /**
203
+ * Check if a credential reference exists in the keychain
204
+ * Returns true if it exists, false otherwise
205
+ * This is a warning check - missing credentials don't block operations
206
+ */
207
+ async checkCredentialExists(credentialRef) {
208
+ try {
209
+ const { KeyChainStore } = await import("@inkeep/agents-core/credential-stores");
210
+ return await new KeyChainStore("auth", "inkeep-cli").get(credentialRef) !== null;
211
+ } catch {
212
+ return false;
213
+ }
214
+ }
215
+ };
216
+ const profileManager = new ProfileManager();
217
+
218
+ //#endregion
219
+ export { ProfileError, ProfileManager, profileManager };