@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,165 @@
1
+ //#region src/commands/pull-v3/utils/component-tracker.ts
2
+ var ComponentTracker = class {
3
+ components = /* @__PURE__ */ new Map();
4
+ usedNames = /* @__PURE__ */ new Set();
5
+ /**
6
+ * Register a component in the tracker
7
+ */
8
+ register(info) {
9
+ const uniqueName = this.ensureUniqueName(info.name, info.type);
10
+ const uniqueExportName = this.ensureUniqueName(info.exportName, info.type);
11
+ const updatedInfo = {
12
+ ...info,
13
+ name: uniqueName,
14
+ exportName: uniqueExportName
15
+ };
16
+ this.components.set(info.id, updatedInfo);
17
+ this.usedNames.add(uniqueName);
18
+ this.usedNames.add(uniqueExportName);
19
+ }
20
+ /**
21
+ * Ensure a variable name is unique across the project
22
+ */
23
+ ensureUniqueName(baseName, type) {
24
+ let uniqueName = baseName;
25
+ let counter = 1;
26
+ while (this.usedNames.has(uniqueName)) {
27
+ if (counter === 1) uniqueName = `${this.getTypePrefix(type)}${baseName.charAt(0).toUpperCase() + baseName.slice(1)}`;
28
+ else uniqueName = `${baseName}${counter}`;
29
+ counter++;
30
+ if (counter > 100) {
31
+ uniqueName = `${baseName}_${Date.now()}`;
32
+ break;
33
+ }
34
+ }
35
+ return uniqueName;
36
+ }
37
+ /**
38
+ * Get type prefix for uniqueness resolution
39
+ */
40
+ getTypePrefix(type) {
41
+ switch (type) {
42
+ case "agent": return "agent";
43
+ case "subAgent": return "sub";
44
+ case "externalAgent": return "ext";
45
+ case "tool": return "tool";
46
+ case "functionTool": return "func";
47
+ case "dataComponent": return "data";
48
+ case "artifactComponent": return "artifact";
49
+ case "credential": return "cred";
50
+ case "statusComponent": return "status";
51
+ case "contextConfig": return "context";
52
+ default: return "comp";
53
+ }
54
+ }
55
+ /**
56
+ * Get component info by ID
57
+ */
58
+ get(id) {
59
+ return this.components.get(id);
60
+ }
61
+ /**
62
+ * Check if component exists
63
+ */
64
+ has(id) {
65
+ return this.components.has(id);
66
+ }
67
+ /**
68
+ * Get all components of a specific type
69
+ */
70
+ getByType(type) {
71
+ return Array.from(this.components.values()).filter((c) => c.type === type);
72
+ }
73
+ /**
74
+ * Extract IDs from mixed array (objects with id property or strings)
75
+ */
76
+ extractIds(references) {
77
+ if (!Array.isArray(references)) return [];
78
+ return references.map((ref) => {
79
+ if (typeof ref === "string") return ref;
80
+ if (typeof ref === "object" && ref) {
81
+ if (ref.id) return ref.id;
82
+ if (ref.type) return ref.type;
83
+ if (ref.name) return ref.name;
84
+ console.warn("ComponentTracker: Skipping reference without clear ID:", ref);
85
+ return null;
86
+ }
87
+ return null;
88
+ }).filter(Boolean);
89
+ }
90
+ /**
91
+ * Resolve references to import statements and variable names
92
+ */
93
+ resolveReferences(references, currentFilePath) {
94
+ const ids = this.extractIds(references);
95
+ const imports = [];
96
+ const variableNames = [];
97
+ for (const id of ids) {
98
+ const component = this.get(id);
99
+ if (component) {
100
+ const importPath = this.getRelativeImportPath(currentFilePath, component.filePath);
101
+ imports.push(`import { ${component.exportName} } from '${importPath}';`);
102
+ variableNames.push(component.name);
103
+ } else variableNames.push(id);
104
+ }
105
+ return {
106
+ imports,
107
+ variableNames
108
+ };
109
+ }
110
+ /**
111
+ * Generate formatted array of variable names for code generation
112
+ */
113
+ formatReferencesForCode(references, style, indentLevel) {
114
+ const ids = this.extractIds(references);
115
+ const variableNames = [];
116
+ for (const id of ids) {
117
+ const component = this.get(id);
118
+ if (component) variableNames.push(component.name);
119
+ else variableNames.push(id);
120
+ }
121
+ if (variableNames.length === 0) return "[]";
122
+ const { indentation } = style;
123
+ const indent = indentation.repeat(indentLevel);
124
+ if (variableNames.length === 1) return `[${variableNames[0]}]`;
125
+ const lines = ["["];
126
+ for (const name of variableNames) lines.push(`${indent}${name},`);
127
+ if (lines.length > 1 && lines[lines.length - 1].endsWith(",")) lines[lines.length - 1] = lines[lines.length - 1].slice(0, -1);
128
+ lines.push(`${indentation.repeat(indentLevel - 1)}]`);
129
+ return lines.join("\n");
130
+ }
131
+ /**
132
+ * Calculate relative import path between files
133
+ */
134
+ getRelativeImportPath(fromPath, toPath) {
135
+ const fromParts = fromPath.replace(".ts", "").split("/");
136
+ const toParts = toPath.replace(".ts", "").split("/");
137
+ fromParts.pop();
138
+ let relativePath = "";
139
+ for (let i = 0; i < fromParts.length; i++) relativePath += "../";
140
+ relativePath += toParts.join("/");
141
+ if (relativePath.startsWith("../")) return relativePath;
142
+ return "./" + relativePath;
143
+ }
144
+ /**
145
+ * Get all components as a list
146
+ */
147
+ getAllComponents() {
148
+ return Array.from(this.components.values());
149
+ }
150
+ /**
151
+ * Clear all registered components
152
+ */
153
+ clear() {
154
+ this.components.clear();
155
+ }
156
+ };
157
+ /**
158
+ * Convert kebab-case or snake_case to camelCase
159
+ */
160
+ function toCamelCase(str) {
161
+ return str.toLowerCase().replace(/[-_](.)/g, (_, char) => char.toUpperCase()).replace(/[^a-zA-Z0-9]/g, "").replace(/^[0-9]/, "_$&");
162
+ }
163
+
164
+ //#endregion
165
+ export { ComponentTracker, toCamelCase };
@@ -0,0 +1,146 @@
1
+ //#region src/commands/pull-v3/utils/generator-utils.ts
2
+ const DEFAULT_STYLE = {
3
+ quotes: "single",
4
+ semicolons: true,
5
+ indentation: " "
6
+ };
7
+ /**
8
+ * Convert kebab-case or snake_case to camelCase for variable names
9
+ */
10
+ function toCamelCase(str) {
11
+ const result = str.replace(/[-_](.)/g, (_, char) => char.toUpperCase()).replace(/[^a-zA-Z0-9]/g, "").replace(/^[0-9]/, "_$&");
12
+ return result.charAt(0).toLowerCase() + result.slice(1);
13
+ }
14
+ /**
15
+ * Format a string value with proper quoting and multiline handling
16
+ */
17
+ function formatString(str, quote = "'", multiline = false) {
18
+ if (!str && str !== "") return `${quote}${quote}`;
19
+ if (multiline && (str.includes("\n") || str.length > 80)) return `\`${str.replace(/`/g, "\\`")}\``;
20
+ return `${quote}${str.replace(new RegExp(quote, "g"), "\\" + quote)}${quote}`;
21
+ }
22
+ /**
23
+ * Check if a string contains template variables like {{user.name}}
24
+ */
25
+ function hasTemplateVariables(str) {
26
+ return /\{\{[^}]+\}\}/.test(str);
27
+ }
28
+ /**
29
+ * Determine if a variable path exists in headers schema
30
+ */
31
+ function isHeadersVariable(variablePath, contextConfigData) {
32
+ if (!contextConfigData?.headersSchema?.schema) return false;
33
+ const schemaStr = JSON.stringify(contextConfigData.headersSchema.schema);
34
+ return schemaStr.includes(`"${variablePath}"`) || schemaStr.includes(variablePath.split(".")[0]);
35
+ }
36
+ /**
37
+ * Determine if a variable path exists in context variables
38
+ */
39
+ function isContextVariable(variablePath, contextConfigData) {
40
+ if (!contextConfigData?.contextVariables) return false;
41
+ const topLevelVar = variablePath.split(".")[0];
42
+ return Object.keys(contextConfigData.contextVariables).includes(topLevelVar);
43
+ }
44
+ /**
45
+ * Format prompt strings with appropriate context.toTemplate() or headers.toTemplate() based on actual schema structure
46
+ */
47
+ function formatPromptWithContext(str, contextVarName, headersVarName, contextConfigData, quote = "'", multiline = false) {
48
+ if (!str && str !== "") return `${quote}${quote}`;
49
+ if (hasTemplateVariables(str)) return `\`${str.replace(/\{\{([^}]+)\}\}/g, (match, variablePath) => {
50
+ if (isContextVariable(variablePath, contextConfigData)) return `\${${contextVarName}.toTemplate("${variablePath}")}`;
51
+ if (isHeadersVariable(variablePath, contextConfigData)) return `\${${headersVarName}.toTemplate("${variablePath}")}`;
52
+ return `\${${contextVarName}.toTemplate("${variablePath}")}`;
53
+ }).replace(/`/g, "\\`")}\``;
54
+ return formatString(str, quote, multiline);
55
+ }
56
+ /**
57
+ * Format prompt strings with headers.toTemplate() only (for backwards compatibility)
58
+ */
59
+ function formatPromptWithHeaders(str, headersVarName, quote = "'", multiline = false) {
60
+ if (!str && str !== "") return `${quote}${quote}`;
61
+ if (hasTemplateVariables(str)) return `\`${str.replace(/\{\{([^}]+)\}\}/g, `\${${headersVarName}.toTemplate("$1")}`).replace(/`/g, "\\`")}\``;
62
+ return formatString(str, quote, multiline);
63
+ }
64
+ /**
65
+ * Format array of references (tools, agents, components) with proper indentation
66
+ */
67
+ function formatReferencesArray(references, style, indentLevel) {
68
+ let refArray;
69
+ if (Array.isArray(references)) refArray = references.map((item) => {
70
+ if (typeof item === "string") return toCamelCase(item);
71
+ if (typeof item === "object" && item && item.id) return toCamelCase(item.id);
72
+ return toCamelCase(String(item));
73
+ });
74
+ else if (references && typeof references === "object") refArray = Object.keys(references).map((key) => toCamelCase(key));
75
+ else return "[]";
76
+ if (!refArray || refArray.length === 0) return "[]";
77
+ const { indentation } = style;
78
+ const indent = indentation.repeat(indentLevel);
79
+ if (refArray.length === 1) return `[${refArray[0]}]`;
80
+ const lines = ["["];
81
+ for (const ref of refArray) lines.push(`${indent}${ref},`);
82
+ if (lines.length > 1 && lines[lines.length - 1].endsWith(",")) lines[lines.length - 1] = lines[lines.length - 1].slice(0, -1);
83
+ lines.push(`${indentation.repeat(indentLevel - 1)}]`);
84
+ return lines.join("\n");
85
+ }
86
+ /**
87
+ * Format object properties as JavaScript object literal
88
+ */
89
+ function formatObject(obj, style, indentLevel) {
90
+ if (!obj || typeof obj !== "object") return "{}";
91
+ const { quotes, indentation } = style;
92
+ const q = quotes === "single" ? "'" : "\"";
93
+ const indent = indentation.repeat(indentLevel);
94
+ const lines = ["{"];
95
+ for (const [key, value] of Object.entries(obj)) {
96
+ if (value === null || value === void 0) continue;
97
+ const formattedKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : formatString(key, q);
98
+ if (typeof value === "string") lines.push(`${indent}${formattedKey}: ${formatString(value, q)},`);
99
+ else if (typeof value === "object") lines.push(`${indent}${formattedKey}: ${formatObject(value, style, indentLevel + 1)},`);
100
+ else lines.push(`${indent}${formattedKey}: ${JSON.stringify(value)},`);
101
+ }
102
+ if (lines.length > 1 && lines[lines.length - 1].endsWith(",")) lines[lines.length - 1] = lines[lines.length - 1].slice(0, -1);
103
+ lines.push(`${indentation.repeat(indentLevel - 1)}}`);
104
+ return lines.join("\n");
105
+ }
106
+ /**
107
+ * Remove trailing comma from the last line in an array of lines
108
+ */
109
+ function removeTrailingComma(lines) {
110
+ if (lines.length > 0 && lines[lines.length - 1].endsWith(",")) lines[lines.length - 1] = lines[lines.length - 1].slice(0, -1);
111
+ }
112
+ /**
113
+ * Generate standard import statement with proper quoting and semicolons
114
+ */
115
+ function generateImport(imports, from, style) {
116
+ const { quotes, semicolons } = style;
117
+ const q = quotes === "single" ? "'" : "\"";
118
+ const semi = semicolons ? ";" : "";
119
+ if (imports.length === 1) return `import { ${imports[0]} } from ${q}${from}${q}${semi}`;
120
+ return `import { ${imports.join(", ")} } from ${q}${from}${q}${semi}`;
121
+ }
122
+ /**
123
+ * Check if a value is truthy and should be included in output
124
+ */
125
+ function shouldInclude(value) {
126
+ if (value === null || value === void 0) return false;
127
+ if (Array.isArray(value) && value.length === 0) return false;
128
+ if (typeof value === "object" && Object.keys(value).length === 0) return false;
129
+ return true;
130
+ }
131
+ /**
132
+ * Generate complete file content with imports and definitions
133
+ */
134
+ function generateFileContent(imports, definitions) {
135
+ const content = [];
136
+ if (imports.length > 0) content.push(imports.join("\n"));
137
+ if (definitions.length > 0) {
138
+ if (content.length > 0) content.push("");
139
+ content.push(definitions.join("\n\n"));
140
+ }
141
+ content.push("");
142
+ return content.join("\n");
143
+ }
144
+
145
+ //#endregion
146
+ export { DEFAULT_STYLE, formatObject, formatPromptWithContext, formatPromptWithHeaders, formatReferencesArray, formatString, generateFileContent, generateImport, hasTemplateVariables, removeTrailingComma, shouldInclude, toCamelCase };
@@ -0,0 +1,44 @@
1
+ import { anthropic } from "@ai-sdk/anthropic";
2
+ import { google } from "@ai-sdk/google";
3
+ import { openai } from "@ai-sdk/openai";
4
+
5
+ //#region src/commands/pull-v3/utils/model-provider-detector.ts
6
+ /**
7
+ * Model Provider Detector - Detect available API keys and select appropriate models
8
+ */
9
+ const PROVIDER_CONFIGS = [
10
+ {
11
+ name: "anthropic",
12
+ envVars: ["ANTHROPIC_API_KEY"],
13
+ model: "claude-sonnet-4-5"
14
+ },
15
+ {
16
+ name: "openai",
17
+ envVars: ["OPENAI_API_KEY"],
18
+ model: "gpt-5.1"
19
+ },
20
+ {
21
+ name: "google",
22
+ envVars: ["GOOGLE_GENERATIVE_AI_API_KEY"],
23
+ model: "gemini-2.5-flash"
24
+ }
25
+ ];
26
+ /**
27
+ * Get a model instance for LLM content merging
28
+ * Returns first available provider or throws if none available
29
+ */
30
+ function getAvailableModel() {
31
+ for (const config of PROVIDER_CONFIGS) if (config.envVars.some((envVar) => {
32
+ const value = process.env[envVar];
33
+ return value && value.trim() !== "";
34
+ })) switch (config.name) {
35
+ case "anthropic": return anthropic(config.model);
36
+ case "openai": return openai(config.model);
37
+ case "google": return google(config.model);
38
+ default: throw new Error(`Unknown provider: ${config.name}`);
39
+ }
40
+ throw new Error("No API keys detected. Please set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_GENERATIVE_AI_API_KEY");
41
+ }
42
+
43
+ //#endregion
44
+ export { getAvailableModel };
@@ -0,0 +1,326 @@
1
+ import { env } from "../env.js";
2
+ import { findAllConfigFiles, findConfigFile } from "../utils/config.js";
3
+ import { initializeCommand } from "../utils/cli-pipeline.js";
4
+ import { performBackgroundVersionCheck } from "../utils/background-version-check.js";
5
+ import { loadProject } from "../utils/project-loader.js";
6
+ import { loadEnvironmentCredentials } from "../utils/environment-loader.js";
7
+ import { existsSync, readdirSync, statSync } from "node:fs";
8
+ import { basename, dirname, join, resolve } from "node:path";
9
+ import * as p from "@clack/prompts";
10
+ import chalk from "chalk";
11
+
12
+ //#region src/commands/push.ts
13
+ async function pushCommand(options) {
14
+ performBackgroundVersionCheck();
15
+ if (options.all) {
16
+ await pushAllProjects(options);
17
+ return;
18
+ }
19
+ const { config, profile } = await initializeCommand({
20
+ configPath: options.config,
21
+ profileName: options.profile,
22
+ tag: options.tag,
23
+ showSpinner: true,
24
+ spinnerText: "Loading configuration...",
25
+ logConfig: true,
26
+ quiet: options.quiet
27
+ });
28
+ const s = p.spinner();
29
+ try {
30
+ s.start("Detecting project...");
31
+ let projectDir;
32
+ if (options.project) {
33
+ projectDir = resolve(process.cwd(), options.project);
34
+ if (!existsSync(join(projectDir, "index.ts"))) {
35
+ s.stop(`No index.ts found in specified project directory: ${projectDir}`);
36
+ process.exit(1);
37
+ }
38
+ } else {
39
+ const currentDir = process.cwd();
40
+ if (existsSync(join(currentDir, "index.ts"))) projectDir = currentDir;
41
+ else {
42
+ const configFile = findConfigFile(currentDir, options.tag);
43
+ if (configFile) {
44
+ const configDir = dirname(configFile);
45
+ if (existsSync(join(configDir, "index.ts"))) projectDir = configDir;
46
+ else {
47
+ s.stop("No index.ts found in config directory");
48
+ console.error(chalk.yellow("Please run this command from a directory containing index.ts or use --project <path>"));
49
+ process.exit(1);
50
+ }
51
+ } else {
52
+ s.stop("No index.ts found in current directory");
53
+ console.error(chalk.yellow("Please run this command from a directory containing index.ts or use --project <path>"));
54
+ process.exit(1);
55
+ }
56
+ }
57
+ }
58
+ s.stop(`Project found: ${projectDir}`);
59
+ if (options.env) {
60
+ process.env.INKEEP_ENV = options.env;
61
+ s.start(`Setting environment to '${options.env}'...`);
62
+ }
63
+ const originalTenantId = process.env.INKEEP_TENANT_ID;
64
+ const originalApiUrl = process.env.INKEEP_API_URL;
65
+ process.env.INKEEP_TENANT_ID = config.tenantId;
66
+ process.env.INKEEP_API_URL = config.agentsManageApiUrl;
67
+ s.start("Loading project from index.ts...");
68
+ const project = await loadProject(projectDir);
69
+ if (originalTenantId !== void 0) process.env.INKEEP_TENANT_ID = originalTenantId;
70
+ else delete process.env.INKEEP_TENANT_ID;
71
+ if (originalApiUrl !== void 0) process.env.INKEEP_API_URL = originalApiUrl;
72
+ else delete process.env.INKEEP_API_URL;
73
+ s.stop("Project loaded successfully");
74
+ if (typeof project.setConfig === "function") project.setConfig(config.tenantId, config.agentsManageApiUrl, void 0, config.agentsManageApiKey);
75
+ if (options.env && typeof project.setCredentials === "function") {
76
+ s.start(`Loading credentials for environment '${options.env}'...`);
77
+ try {
78
+ const credentials = await loadEnvironmentCredentials(projectDir, options.env);
79
+ project.setCredentials(credentials);
80
+ s.stop("Project loaded with credentials");
81
+ console.log(chalk.gray(` • Environment: ${options.env}`));
82
+ console.log(chalk.gray(` • Credentials loaded: ${Object.keys(credentials).length}`));
83
+ } catch (error) {
84
+ s.stop("Failed to load environment credentials");
85
+ console.error(chalk.red("Error:"), error.message);
86
+ process.exit(1);
87
+ }
88
+ }
89
+ if (options.json) {
90
+ s.start("Generating project data JSON...");
91
+ try {
92
+ const projectDefinition = await project.getFullDefinition();
93
+ const jsonFilePath = join(projectDir, `project.json`);
94
+ await (await import("node:fs/promises")).writeFile(jsonFilePath, JSON.stringify(projectDefinition, null, 2));
95
+ s.stop(`Project data saved to ${jsonFilePath}`);
96
+ console.log(chalk.gray(` • File: ${jsonFilePath}`));
97
+ console.log(chalk.gray(` • Size: ${JSON.stringify(projectDefinition).length} bytes`));
98
+ const agentCount = Object.keys(projectDefinition.agents || {}).length;
99
+ const toolCount = Object.keys(projectDefinition.tools || {}).length;
100
+ const subAgentCount = Object.values(projectDefinition.agents || {}).reduce((total, agent) => {
101
+ return total + Object.keys(agent.subAgents || {}).length;
102
+ }, 0);
103
+ console.log(chalk.cyan("\n📊 Project Data Summary:"));
104
+ console.log(chalk.gray(` • Agent: ${agentCount}`));
105
+ console.log(chalk.gray(` • Tools: ${toolCount}`));
106
+ console.log(chalk.gray(` • SubAgent: ${subAgentCount}`));
107
+ console.log(chalk.green("\n✨ JSON file generated successfully!"));
108
+ process.exit(0);
109
+ } catch (error) {
110
+ s.stop("Failed to generate JSON file");
111
+ console.error(chalk.red("Error:"), error.message);
112
+ process.exit(1);
113
+ }
114
+ }
115
+ s.start("Initializing project...");
116
+ await project.init();
117
+ const projectId = project.getId();
118
+ const projectName = project.getName();
119
+ const stats = project.getStats();
120
+ s.stop(`Project "${projectName}" (${projectId}) pushed successfully`);
121
+ console.log(chalk.cyan("\n📊 Project Summary:"));
122
+ console.log(chalk.gray(` • Project ID: ${projectId}`));
123
+ console.log(chalk.gray(` • Name: ${projectName}`));
124
+ console.log(chalk.gray(` • Agent: ${stats.agentCount}`));
125
+ console.log(chalk.gray(` • Tenant: ${stats.tenantId}`));
126
+ const agents = project.getAgents();
127
+ if (agents.length > 0) {
128
+ console.log(chalk.cyan("\n📊 Agent Details:"));
129
+ for (const agent of agents) {
130
+ const agentStats = agent.getStats();
131
+ console.log(chalk.gray(` • ${agent.getName()} (${agent.getId()}): ${agentStats.agentCount} agents`));
132
+ }
133
+ }
134
+ try {
135
+ const credentialTracking = await project.getCredentialTracking();
136
+ const credentialCount = Object.keys(credentialTracking.credentials).length;
137
+ if (credentialCount > 0) {
138
+ console.log(chalk.cyan("\n🔐 Credentials:"));
139
+ console.log(chalk.gray(` • Total credentials: ${credentialCount}`));
140
+ for (const [credId, credData] of Object.entries(credentialTracking.credentials)) {
141
+ const usageInfo = credentialTracking.usage[credId] || [];
142
+ const credType = credData.type || "unknown";
143
+ const storeId = credData.credentialStoreId || "unknown";
144
+ console.log(chalk.gray(` • ${credId} (${credType}, store: ${storeId})`));
145
+ if (usageInfo.length > 0) {
146
+ const usageByType = {};
147
+ for (const usage of usageInfo) usageByType[usage.type] = (usageByType[usage.type] || 0) + 1;
148
+ const usageSummary = Object.entries(usageByType).map(([type, count]) => `${count} ${type}${count > 1 ? "s" : ""}`).join(", ");
149
+ console.log(chalk.gray(` Used by: ${usageSummary}`));
150
+ }
151
+ }
152
+ }
153
+ } catch (_error) {
154
+ if (env.DEBUG) console.error(chalk.yellow("Could not retrieve credential tracking information"));
155
+ }
156
+ if (config.manageUiUrl) {
157
+ const projectUrl = `${config.manageUiUrl}/${config.tenantId}/projects/${projectId}`;
158
+ console.log(chalk.cyan("\n🔗 Project URL:"));
159
+ console.log(chalk.blue.underline(` ${projectUrl}`));
160
+ }
161
+ console.log(chalk.green("\n✨ Next steps:"));
162
+ console.log(chalk.gray(` • View all agents: inkeep list-agent`));
163
+ process.exit(0);
164
+ } catch (_error) {
165
+ s.stop("Failed to push project");
166
+ const error = _error;
167
+ console.error(chalk.red("Error:"), error.message);
168
+ if (error.stack && env.DEBUG) console.error(chalk.gray(error.stack));
169
+ process.exit(1);
170
+ }
171
+ }
172
+ /**
173
+ * Check if an index.ts file exports a project (has __type = 'project')
174
+ */
175
+ async function isProjectDirectory(dir) {
176
+ const indexPath = join(dir, "index.ts");
177
+ if (!existsSync(indexPath)) return false;
178
+ try {
179
+ const { importWithTypeScriptSupport } = await import("../utils/tsx-loader.js");
180
+ const module = await importWithTypeScriptSupport(indexPath);
181
+ for (const key of Object.keys(module)) {
182
+ const value = module[key];
183
+ if (value && typeof value === "object" && value.__type === "project") return true;
184
+ }
185
+ return false;
186
+ } catch {
187
+ return false;
188
+ }
189
+ }
190
+ /**
191
+ * Find all directories containing index.ts that export a project
192
+ */
193
+ async function findAllProjectDirs(rootDir, excludeDirs = [
194
+ "node_modules",
195
+ ".git",
196
+ "dist",
197
+ "build",
198
+ ".temp-validation"
199
+ ]) {
200
+ const projectDirs = [];
201
+ async function scanDirectory(dir) {
202
+ if (!existsSync(dir)) return;
203
+ let items;
204
+ try {
205
+ items = readdirSync(dir);
206
+ } catch {
207
+ return;
208
+ }
209
+ if (existsSync(join(dir, "index.ts"))) {
210
+ if (await isProjectDirectory(dir)) {
211
+ projectDirs.push(dir);
212
+ return;
213
+ }
214
+ }
215
+ for (const item of items) {
216
+ const fullPath = join(dir, item);
217
+ if (excludeDirs.includes(item)) continue;
218
+ try {
219
+ if (statSync(fullPath).isDirectory()) await scanDirectory(fullPath);
220
+ } catch {}
221
+ }
222
+ }
223
+ await scanDirectory(rootDir);
224
+ return projectDirs.sort();
225
+ }
226
+ /**
227
+ * Push all projects found in current directory tree
228
+ */
229
+ async function pushAllProjects(options) {
230
+ console.log(chalk.blue("\n🚀 Batch Push: Finding all projects...\n"));
231
+ const configFiles = findAllConfigFiles(process.cwd(), options.tag);
232
+ const projectDirsFromConfig = [];
233
+ for (const configFile of configFiles) {
234
+ const dir = dirname(configFile);
235
+ if (existsSync(join(dir, "index.ts"))) projectDirsFromConfig.push(dir);
236
+ }
237
+ const allIndexDirs = await findAllProjectDirs(process.cwd());
238
+ const projectDirsFromIndex = [];
239
+ for (const dir of allIndexDirs) {
240
+ if (projectDirsFromConfig.includes(dir)) continue;
241
+ if (findConfigFile(dir, options.tag)) projectDirsFromIndex.push(dir);
242
+ }
243
+ const projectDirs = [...projectDirsFromConfig, ...projectDirsFromIndex].sort();
244
+ if (projectDirs.length === 0) {
245
+ const configPattern = options.tag ? `${options.tag}.__inkeep.config.ts__` : "inkeep.config.ts";
246
+ console.error(chalk.red("No valid projects found."));
247
+ console.log(chalk.yellow("\nHint: Projects must have an index.ts file and access to an " + configPattern + " file"));
248
+ console.log(chalk.yellow(" (either in the same directory or in a parent directory)."));
249
+ process.exit(1);
250
+ }
251
+ console.log(chalk.gray(`Found ${projectDirs.length} project(s) to push:\n`));
252
+ for (const dir of projectDirs) {
253
+ const relativePath = dir === process.cwd() ? "." : dir.replace(process.cwd() + "/", "");
254
+ console.log(chalk.gray(` • ${relativePath}`));
255
+ }
256
+ console.log();
257
+ const results = [];
258
+ const total = projectDirs.length;
259
+ for (let i = 0; i < projectDirs.length; i++) {
260
+ const projectDir = projectDirs[i];
261
+ const relativePath = projectDir === process.cwd() ? "." : projectDir.replace(process.cwd() + "/", "");
262
+ const progress = `[${i + 1}/${total}]`;
263
+ console.log(chalk.cyan(`${progress} Pushing ${relativePath}...`));
264
+ const result = await pushSingleProject(projectDir, options);
265
+ results.push(result);
266
+ if (result.success) console.log(chalk.green(` ✓ ${result.projectName || result.projectId || basename(projectDir)}`));
267
+ else console.log(chalk.red(` ✗ ${basename(projectDir)}: ${result.error}`));
268
+ }
269
+ const succeeded = results.filter((r) => r.success).length;
270
+ const failed = results.filter((r) => !r.success).length;
271
+ console.log(chalk.cyan("\n📊 Batch Push Summary:"));
272
+ console.log(chalk.green(` ✓ Succeeded: ${succeeded}`));
273
+ if (failed > 0) {
274
+ console.log(chalk.red(` ✗ Failed: ${failed}`));
275
+ console.log(chalk.red("\nFailed projects:"));
276
+ for (const result of results) if (!result.success) {
277
+ const relativePath = result.projectDir === process.cwd() ? "." : result.projectDir.replace(process.cwd() + "/", "");
278
+ console.log(chalk.red(` • ${relativePath}: ${result.error}`));
279
+ }
280
+ }
281
+ process.exit(failed > 0 ? 1 : 0);
282
+ }
283
+ /**
284
+ * Push a single project (used by batch operations)
285
+ */
286
+ async function pushSingleProject(projectDir, options) {
287
+ try {
288
+ const { config } = await initializeCommand({
289
+ configPath: findConfigFile(projectDir, options.tag) || void 0,
290
+ profileName: options.profile,
291
+ tag: options.tag,
292
+ showSpinner: false,
293
+ logConfig: false
294
+ });
295
+ const originalTenantId = process.env.INKEEP_TENANT_ID;
296
+ const originalApiUrl = process.env.INKEEP_API_URL;
297
+ process.env.INKEEP_TENANT_ID = config.tenantId;
298
+ process.env.INKEEP_API_URL = config.agentsManageApiUrl;
299
+ const project = await loadProject(projectDir);
300
+ if (originalTenantId !== void 0) process.env.INKEEP_TENANT_ID = originalTenantId;
301
+ else delete process.env.INKEEP_TENANT_ID;
302
+ if (originalApiUrl !== void 0) process.env.INKEEP_API_URL = originalApiUrl;
303
+ else delete process.env.INKEEP_API_URL;
304
+ if (typeof project.setConfig === "function") project.setConfig(config.tenantId, config.agentsManageApiUrl, void 0, config.agentsManageApiKey);
305
+ if (options.env && typeof project.setCredentials === "function") {
306
+ const credentials = await loadEnvironmentCredentials(projectDir, options.env);
307
+ project.setCredentials(credentials);
308
+ }
309
+ await project.init();
310
+ return {
311
+ projectDir,
312
+ projectId: project.getId(),
313
+ projectName: project.getName(),
314
+ success: true
315
+ };
316
+ } catch (error) {
317
+ return {
318
+ projectDir,
319
+ success: false,
320
+ error: error instanceof Error ? error.message : String(error)
321
+ };
322
+ }
323
+ }
324
+
325
+ //#endregion
326
+ export { pushCommand };