@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,132 @@
1
+ import { KeyChainStore } from "@inkeep/agents-core/credential-stores";
2
+
3
+ //#region src/utils/credentials.ts
4
+ const CLI_SERVICE_PREFIX = "inkeep-cli";
5
+ const DEFAULT_CREDENTIALS_KEY = "auth-credentials";
6
+ let keychainStore = null;
7
+ function getKeychainStore() {
8
+ if (!keychainStore) keychainStore = new KeyChainStore("auth", CLI_SERVICE_PREFIX);
9
+ return keychainStore;
10
+ }
11
+ /**
12
+ * Save CLI credentials to the system keychain
13
+ * @param credentials - The credentials to store
14
+ * @param credentialKey - Optional key to store under (from profile's credential field). Defaults to 'auth-credentials'
15
+ */
16
+ async function saveCredentials(credentials, credentialKey) {
17
+ const store = getKeychainStore();
18
+ const { available, reason } = await store.checkAvailability();
19
+ if (!available) throw new Error(getKeychainUnavailableMessage(reason));
20
+ const key = credentialKey || DEFAULT_CREDENTIALS_KEY;
21
+ const credentialsJson = JSON.stringify(credentials);
22
+ await store.set(key, credentialsJson);
23
+ }
24
+ /**
25
+ * Load CLI credentials from the system keychain
26
+ * @param credentialKey - Optional key to load from (from profile's credential field). Defaults to 'auth-credentials'
27
+ */
28
+ async function loadCredentials(credentialKey) {
29
+ const store = getKeychainStore();
30
+ const key = credentialKey || DEFAULT_CREDENTIALS_KEY;
31
+ const credentialsJson = await store.get(key);
32
+ if (!credentialsJson) return null;
33
+ try {
34
+ const credentials = JSON.parse(credentialsJson);
35
+ if (!credentials.accessToken || !credentials.userId || !credentials.userEmail) return null;
36
+ return credentials;
37
+ } catch {
38
+ await store.delete(key);
39
+ return null;
40
+ }
41
+ }
42
+ /**
43
+ * Clear CLI credentials from the system keychain
44
+ * @param credentialKey - Optional key to clear (from profile's credential field). Defaults to 'auth-credentials'
45
+ */
46
+ async function clearCredentials(credentialKey) {
47
+ const store = getKeychainStore();
48
+ const key = credentialKey || DEFAULT_CREDENTIALS_KEY;
49
+ return store.delete(key);
50
+ }
51
+ /**
52
+ * Check if valid credentials exist in the keychain
53
+ * @param credentialKey - Optional key to check (from profile's credential field). Defaults to 'auth-credentials'
54
+ */
55
+ async function hasValidCredentials(credentialKey) {
56
+ const credentials = await loadCredentials(credentialKey);
57
+ if (!credentials) return false;
58
+ if (credentials.expiresAt) {
59
+ if (new Date(credentials.expiresAt) < /* @__PURE__ */ new Date()) return false;
60
+ }
61
+ return true;
62
+ }
63
+ /**
64
+ * Check if credentials are expired
65
+ */
66
+ function isCredentialExpired(credentials) {
67
+ if (!credentials.expiresAt) return false;
68
+ return new Date(credentials.expiresAt) < /* @__PURE__ */ new Date();
69
+ }
70
+ /**
71
+ * Get time until credentials expire
72
+ */
73
+ function getCredentialExpiryInfo(credentials) {
74
+ if (!credentials.expiresAt) return { isExpired: false };
75
+ const expiresAt = new Date(credentials.expiresAt);
76
+ const now = /* @__PURE__ */ new Date();
77
+ if (expiresAt < now) return {
78
+ isExpired: true,
79
+ expiresAt
80
+ };
81
+ const diffMs = expiresAt.getTime() - now.getTime();
82
+ const hours = Math.floor(diffMs / (1e3 * 60 * 60));
83
+ const minutes = Math.floor(diffMs % (1e3 * 60 * 60) / (1e3 * 60));
84
+ let expiresIn;
85
+ if (hours > 24) expiresIn = `${Math.floor(hours / 24)}d`;
86
+ else if (hours > 0) expiresIn = `${hours}h`;
87
+ else expiresIn = `${minutes}m`;
88
+ return {
89
+ isExpired: false,
90
+ expiresIn,
91
+ expiresAt
92
+ };
93
+ }
94
+ /**
95
+ * Check if the keychain is available for storing credentials
96
+ */
97
+ async function checkKeychainAvailability() {
98
+ return getKeychainStore().checkAvailability();
99
+ }
100
+ /**
101
+ * Get a helpful error message when keychain is unavailable
102
+ */
103
+ function getKeychainUnavailableMessage(reason) {
104
+ const platform = process.platform;
105
+ let message = "Unable to store credentials securely.\n\n";
106
+ if (reason) message += `Reason: ${reason}\n\n`;
107
+ message += "The Inkeep CLI requires access to the system keychain to store your login credentials securely.\n\n";
108
+ switch (platform) {
109
+ case "darwin":
110
+ message += "On macOS:\n";
111
+ message += " 1. Ensure you have Keychain Access available\n";
112
+ message += " 2. If prompted, click \"Allow\" or \"Always Allow\" to grant access\n";
113
+ message += " 3. Check System Preferences > Security & Privacy if access was denied\n";
114
+ break;
115
+ case "win32":
116
+ message += "On Windows:\n";
117
+ message += " 1. Ensure Windows Credential Manager is available\n";
118
+ message += " 2. Try running the CLI as administrator if access is denied\n";
119
+ break;
120
+ case "linux":
121
+ message += "On Linux:\n";
122
+ message += " 1. Ensure libsecret is installed (e.g., `sudo apt install libsecret-1-dev`)\n";
123
+ message += " 2. Ensure a keyring service is running (GNOME Keyring, KWallet, etc.)\n";
124
+ message += " 3. For headless servers, consider using an API key instead of `inkeep login`\n";
125
+ break;
126
+ default: message += "Please ensure your system has a supported keychain/credential manager available.\n";
127
+ }
128
+ return message;
129
+ }
130
+
131
+ //#endregion
132
+ export { checkKeychainAvailability, clearCredentials, getCredentialExpiryInfo, getKeychainUnavailableMessage, hasValidCredentials, isCredentialExpired, loadCredentials, saveCredentials };
@@ -0,0 +1,28 @@
1
+ import { importWithTypeScriptSupport } from "./tsx-loader.js";
2
+ import { existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ //#region src/utils/environment-loader.ts
6
+ /**
7
+ * Load environment credentials from the environments directory
8
+ * @param projectDir - Path to the project directory
9
+ * @param env - Environment name (e.g., 'development', 'production')
10
+ * @returns Object containing credentials or null if not found
11
+ */
12
+ async function loadEnvironmentCredentials(projectDir, env) {
13
+ const envFilePath = join(join(projectDir, "environments"), `${env}.env.ts`);
14
+ if (!existsSync(envFilePath)) throw new Error(`Environment file not found: ${envFilePath}\nMake sure you have a ${env}.env.ts file in the environments directory.`);
15
+ try {
16
+ const envModule = await importWithTypeScriptSupport(envFilePath);
17
+ const exports = Object.keys(envModule);
18
+ if (exports.length === 0) throw new Error(`No exports found in environment file: ${envFilePath}`);
19
+ const envSettings = envModule[exports[0]];
20
+ if (!envSettings || typeof envSettings !== "object") throw new Error(`Invalid environment settings in ${envFilePath}. Expected an object with credentials.`);
21
+ return envSettings.credentials || {};
22
+ } catch (error) {
23
+ throw new Error(`Failed to load environment file ${envFilePath}: ${error.message}`);
24
+ }
25
+ }
26
+
27
+ //#endregion
28
+ export { loadEnvironmentCredentials };
@@ -0,0 +1,62 @@
1
+ import { existsSync, readdirSync, statSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+
4
+ //#region src/utils/file-finder.ts
5
+ /**
6
+ * Recursively find all TypeScript files in a directory, excluding specified directories
7
+ * @param rootDir - Root directory to search
8
+ * @param excludeDirs - Array of directory names to exclude (relative to root)
9
+ * @returns Array of file paths
10
+ */
11
+ function findAllTypeScriptFiles(rootDir, excludeDirs = []) {
12
+ const tsFiles = [];
13
+ function scanDirectory(dir) {
14
+ if (!existsSync(dir)) return;
15
+ const items = readdirSync(dir);
16
+ for (const item of items) {
17
+ const fullPath = join(dir, item);
18
+ const relativePath = relative(rootDir, fullPath);
19
+ if (excludeDirs.some((excludeDir) => relativePath === excludeDir || relativePath.startsWith(`${excludeDir}/`))) continue;
20
+ const stat = statSync(fullPath);
21
+ if (stat.isDirectory()) scanDirectory(fullPath);
22
+ else if (stat.isFile() && item.endsWith(".ts")) tsFiles.push(fullPath);
23
+ }
24
+ }
25
+ scanDirectory(rootDir);
26
+ return tsFiles.sort();
27
+ }
28
+ /**
29
+ * Categorize TypeScript files by their likely purpose based on filename and location
30
+ * @param files - Array of file paths
31
+ * @param rootDir - Root directory for relative path calculation
32
+ * @returns Object with categorized files
33
+ */
34
+ function categorizeTypeScriptFiles(files, rootDir) {
35
+ const indexFile = files.find((file) => file.endsWith("/index.ts") || file === join(rootDir, "index.ts")) || null;
36
+ const configFiles = [];
37
+ const agentFiles = [];
38
+ const subAgentFiles = [];
39
+ const toolFiles = [];
40
+ const otherFiles = [];
41
+ for (const file of files) {
42
+ const fileName = file.split("/").pop() || "";
43
+ const relativePath = relative(rootDir, file);
44
+ if (file === indexFile) continue;
45
+ if (fileName.includes(".config.") || fileName.includes(".env.") || fileName === "inkeep.config.ts") configFiles.push(file);
46
+ else if (fileName.includes(".agent.") || relativePath.includes("agent/")) agentFiles.push(file);
47
+ else if (fileName.includes("agent") || fileName.includes("Agent") || relativePath.includes("agents/")) agentFiles.push(file);
48
+ else if (fileName.includes("tool") || fileName.includes("Tool") || relativePath.includes("tools/")) toolFiles.push(file);
49
+ else otherFiles.push(file);
50
+ }
51
+ return {
52
+ indexFile,
53
+ configFiles,
54
+ agentFiles,
55
+ subAgentFiles,
56
+ toolFiles,
57
+ otherFiles
58
+ };
59
+ }
60
+
61
+ //#endregion
62
+ export { categorizeTypeScriptFiles, findAllTypeScriptFiles };
@@ -0,0 +1,185 @@
1
+ //#region src/utils/json-comparator.ts
2
+ /**
3
+ * Compare two JSON objects for structural equivalence
4
+ * Handles arrays with different ordering and nested objects
5
+ */
6
+ function compareJsonObjects(obj1, obj2, options = {}) {
7
+ const { ignoreArrayOrder = true, ignoreCase = false, ignoreWhitespace = false, ignorePaths = [] } = options;
8
+ const differences = [];
9
+ const stats = {
10
+ totalKeys: 0,
11
+ differentKeys: 0,
12
+ missingKeys: 0,
13
+ extraKeys: 0
14
+ };
15
+ function normalizeValue(value) {
16
+ if (typeof value === "string") {
17
+ let normalized = value;
18
+ if (ignoreCase) normalized = normalized.toLowerCase();
19
+ if (ignoreWhitespace) normalized = normalized.trim().replace(/\s+/g, " ");
20
+ return normalized;
21
+ }
22
+ return value;
23
+ }
24
+ function shouldIgnorePath(path) {
25
+ return ignorePaths.some((ignorePath) => {
26
+ if (ignorePath.endsWith("*")) return path.startsWith(ignorePath.slice(0, -1));
27
+ return path === ignorePath;
28
+ });
29
+ }
30
+ function compareValues(value1, value2, path = "") {
31
+ if (shouldIgnorePath(path)) return true;
32
+ if (value1 === null || value1 === void 0) {
33
+ if (value2 === null || value2 === void 0) return true;
34
+ differences.push({
35
+ path,
36
+ type: "different",
37
+ value1,
38
+ value2,
39
+ description: `Null/undefined mismatch: ${value1} vs ${value2}`
40
+ });
41
+ return false;
42
+ }
43
+ if (value2 === null || value2 === void 0) {
44
+ differences.push({
45
+ path,
46
+ type: "different",
47
+ value1,
48
+ value2,
49
+ description: `Null/undefined mismatch: ${value1} vs ${value2}`
50
+ });
51
+ return false;
52
+ }
53
+ if (typeof value1 !== typeof value2) {
54
+ differences.push({
55
+ path,
56
+ type: "type_mismatch",
57
+ value1,
58
+ value2,
59
+ description: `Type mismatch: ${typeof value1} vs ${typeof value2}`
60
+ });
61
+ return false;
62
+ }
63
+ if (typeof value1 !== "object") {
64
+ if (normalizeValue(value1) !== normalizeValue(value2)) {
65
+ differences.push({
66
+ path,
67
+ type: "different",
68
+ value1,
69
+ value2,
70
+ description: `Value mismatch: ${value1} vs ${value2}`
71
+ });
72
+ return false;
73
+ }
74
+ return true;
75
+ }
76
+ if (Array.isArray(value1) && Array.isArray(value2)) {
77
+ if (value1.length !== value2.length) {
78
+ differences.push({
79
+ path,
80
+ type: "different",
81
+ value1: value1.length,
82
+ value2: value2.length,
83
+ description: `Array length mismatch: ${value1.length} vs ${value2.length}`
84
+ });
85
+ return false;
86
+ }
87
+ if (ignoreArrayOrder) {
88
+ const sorted1 = [...value1].sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
89
+ const sorted2 = [...value2].sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
90
+ for (let i = 0; i < sorted1.length; i++) compareValues(sorted1[i], sorted2[i], `${path}[${i}]`);
91
+ } else for (let i = 0; i < value1.length; i++) compareValues(value1[i], value2[i], `${path}[${i}]`);
92
+ return true;
93
+ }
94
+ if (typeof value1 === "object" && typeof value2 === "object") {
95
+ const keys1 = Object.keys(value1);
96
+ const keys2 = Object.keys(value2);
97
+ const allKeys = new Set([...keys1, ...keys2]);
98
+ stats.totalKeys += allKeys.size;
99
+ for (const key of allKeys) {
100
+ const currentPath = path ? `${path}.${key}` : key;
101
+ if (!keys1.includes(key)) {
102
+ differences.push({
103
+ path: currentPath,
104
+ type: "missing",
105
+ value2: value2[key],
106
+ description: `Missing key in first object: ${key}`
107
+ });
108
+ stats.missingKeys++;
109
+ continue;
110
+ }
111
+ if (!keys2.includes(key)) {
112
+ differences.push({
113
+ path: currentPath,
114
+ type: "extra",
115
+ value1: value1[key],
116
+ description: `Extra key in first object: ${key}`
117
+ });
118
+ stats.extraKeys++;
119
+ continue;
120
+ }
121
+ if (!compareValues(value1[key], value2[key], currentPath)) stats.differentKeys++;
122
+ }
123
+ return true;
124
+ }
125
+ return true;
126
+ }
127
+ compareValues(obj1, obj2);
128
+ return {
129
+ isEqual: differences.length === 0,
130
+ differences,
131
+ stats
132
+ };
133
+ }
134
+ /**
135
+ * Create a normalized version of a JSON object for comparison
136
+ * This can be useful for creating a canonical representation
137
+ */
138
+ function normalizeJsonObject(obj, options = {}) {
139
+ const { ignoreArrayOrder = true, ignoreCase = false, ignoreWhitespace = false } = options;
140
+ function normalizeValue(value) {
141
+ if (typeof value === "string") {
142
+ let normalized = value;
143
+ if (ignoreCase) normalized = normalized.toLowerCase();
144
+ if (ignoreWhitespace) normalized = normalized.trim().replace(/\s+/g, " ");
145
+ return normalized;
146
+ }
147
+ if (Array.isArray(value)) {
148
+ const normalizedArray = value.map(normalizeValue);
149
+ if (ignoreArrayOrder) return normalizedArray.sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));
150
+ return normalizedArray;
151
+ }
152
+ if (typeof value === "object" && value !== null) {
153
+ const normalizedObj = {};
154
+ const sortedKeys = Object.keys(value).sort();
155
+ for (const key of sortedKeys) normalizedObj[key] = normalizeValue(value[key]);
156
+ return normalizedObj;
157
+ }
158
+ return value;
159
+ }
160
+ return normalizeValue(obj);
161
+ }
162
+ /**
163
+ * Get a summary of differences in a human-readable format
164
+ */
165
+ function getDifferenceSummary(result) {
166
+ if (result.isEqual) return "✅ Objects are equivalent";
167
+ const { differences, stats } = result;
168
+ const summary = [`❌ Objects differ (${differences.length} differences found)`];
169
+ summary.push(`📊 Statistics:`);
170
+ summary.push(` • Total keys: ${stats.totalKeys}`);
171
+ summary.push(` • Different values: ${stats.differentKeys}`);
172
+ summary.push(` • Missing keys: ${stats.missingKeys}`);
173
+ summary.push(` • Extra keys: ${stats.extraKeys}`);
174
+ if (differences.length > 0) {
175
+ summary.push(`\n🔍 Differences:`);
176
+ differences.slice(0, 10).forEach((diff, index) => {
177
+ summary.push(` ${index + 1}. ${diff.path}: ${diff.description}`);
178
+ });
179
+ if (differences.length > 10) summary.push(` ... and ${differences.length - 10} more differences`);
180
+ }
181
+ return summary.join("\n");
182
+ }
183
+
184
+ //#endregion
185
+ export { compareJsonObjects, getDifferenceSummary, normalizeJsonObject };
@@ -0,0 +1,232 @@
1
+ import chalk from "chalk";
2
+
3
+ //#region src/utils/json-comparison.ts
4
+ /**
5
+ * Deep compare two FullProjectDefinition objects
6
+ *
7
+ * Ignores timestamp fields (createdAt, updatedAt) and provides detailed
8
+ * error messages about specific differences.
9
+ *
10
+ * @param original - The original project definition from the backend
11
+ * @param generated - The generated project definition from loaded TypeScript
12
+ * @returns Comparison result with matches boolean and list of differences
13
+ */
14
+ function compareProjectDefinitions(original, generated) {
15
+ const differences = [];
16
+ const warnings = [];
17
+ const dbGeneratedFields = ["agentToolRelationId"];
18
+ const sdkGeneratedFields = ["type"];
19
+ const contextFields = [
20
+ "tenantId",
21
+ "projectId",
22
+ "agentId"
23
+ ];
24
+ const cosmenticFields = ["imageUrl"];
25
+ const allIgnoredFields = [
26
+ ...dbGeneratedFields,
27
+ ...sdkGeneratedFields,
28
+ ...contextFields,
29
+ ...cosmenticFields
30
+ ];
31
+ const isEmpty = (value) => {
32
+ if (value === void 0 || value === null) return true;
33
+ if (Array.isArray(value) && value.length === 0) return true;
34
+ if (typeof value === "object" && Object.keys(value).length === 0) return true;
35
+ return false;
36
+ };
37
+ const isSchemaField = (path) => {
38
+ return path.endsWith(".props") || path.endsWith(".schema") || path.endsWith("Schema") || path.includes(".props.") || path.includes(".schema.");
39
+ };
40
+ const hasContent = (value) => {
41
+ if (!value || typeof value !== "object") return false;
42
+ if (Array.isArray(value)) return value.length > 0;
43
+ return Object.keys(value).length > 0;
44
+ };
45
+ const comparePrimitive = (path, a, b) => {
46
+ if (a === b) return true;
47
+ if (isSchemaField(path)) {
48
+ const aHasContent = hasContent(a);
49
+ const bHasContent = hasContent(b);
50
+ if (aHasContent !== bHasContent) return false;
51
+ if (!aHasContent && !bHasContent) return true;
52
+ } else if (isEmpty(a) && isEmpty(b)) return true;
53
+ if (path.includes("credential") || path.endsWith("ReferenceId")) {
54
+ if (typeof a === "string" && typeof b === "object" && b !== null && "id" in b) return a === b.id;
55
+ if (typeof b === "string" && typeof a === "object" && a !== null && "id" in a) return b === a.id;
56
+ }
57
+ if (typeof a !== typeof b) {
58
+ differences.push(`Type mismatch at ${path}: ${typeof a} vs ${typeof b}`);
59
+ return false;
60
+ }
61
+ if (a !== b) {
62
+ differences.push(`Value mismatch at ${path}: "${a}" vs "${b}"`);
63
+ return false;
64
+ }
65
+ return true;
66
+ };
67
+ const compareArrays = (path, a, b) => {
68
+ if (a.length !== b.length) {
69
+ differences.push(`Array length mismatch at ${path}: ${a.length} vs ${b.length}`);
70
+ return false;
71
+ }
72
+ if ([
73
+ "canDelegateTo",
74
+ "canTransferTo",
75
+ "canUse",
76
+ "tools",
77
+ "functionTools",
78
+ "dataComponents",
79
+ "artifactComponents"
80
+ ].some((pattern) => path.includes(pattern))) {
81
+ const allIgnoredFields$1 = [
82
+ ...dbGeneratedFields,
83
+ ...sdkGeneratedFields,
84
+ ...contextFields
85
+ ];
86
+ const filterIgnoredFields = (item) => {
87
+ if (typeof item === "object" && item !== null) {
88
+ const filtered = { ...item };
89
+ allIgnoredFields$1.forEach((field) => delete filtered[field]);
90
+ return JSON.stringify(filtered);
91
+ }
92
+ return item;
93
+ };
94
+ const aSet = new Set(a.map(filterIgnoredFields));
95
+ const bSet = new Set(b.map(filterIgnoredFields));
96
+ if (aSet.size !== bSet.size) {
97
+ differences.push(`Array content mismatch at ${path}: different unique elements`);
98
+ return false;
99
+ }
100
+ for (const item of aSet) if (!bSet.has(item)) {
101
+ differences.push(`Array content mismatch at ${path}: missing element ${item}`);
102
+ return false;
103
+ }
104
+ return true;
105
+ }
106
+ let allMatch = true;
107
+ for (let i = 0; i < a.length; i++) if (!compareValues(`${path}[${i}]`, a[i], b[i])) allMatch = false;
108
+ return allMatch;
109
+ };
110
+ const compareObjects = (path, a, b) => {
111
+ const ignoredFields = ["createdAt", "updatedAt"];
112
+ const aKeys = Object.keys(a || {}).filter((k) => !ignoredFields.includes(k));
113
+ const bKeys = Object.keys(b || {}).filter((k) => !ignoredFields.includes(k));
114
+ const missingInB = aKeys.filter((k) => !bKeys.includes(k) && a[k] !== null && !(Array.isArray(a[k]) && a[k].length === 0) && !(typeof a[k] === "object" && a[k] !== null && Object.keys(a[k]).length === 0) && !allIgnoredFields.includes(k) && k !== "$schema" && !(k === "properties" && path.includes(".schema")) && !(k === "required" && path.includes(".schema")) && !(k === "additionalProperties" && path.includes(".schema")));
115
+ const extraInB = bKeys.filter((k) => !aKeys.includes(k) && b[k] !== null && !(Array.isArray(b[k]) && b[k].length === 0) && !(typeof b[k] === "object" && b[k] !== null && Object.keys(b[k]).length === 0) && !allIgnoredFields.includes(k) && k !== "$schema" && !(k === "additionalProperties" && path.includes(".props")));
116
+ if (missingInB.length > 0) differences.push(`Missing keys in generated at ${path}: ${missingInB.join(", ")}`);
117
+ if (extraInB.length > 0) {
118
+ const meaningfulExtraKeys = [];
119
+ const emptyExtraKeys = [];
120
+ for (const key of extraInB) {
121
+ const value = b[key];
122
+ if (value === null || value === void 0 || value === "" || Array.isArray(value) && value.length === 0 || typeof value === "object" && value !== null && Object.keys(value).length === 0) emptyExtraKeys.push(key);
123
+ else meaningfulExtraKeys.push(key);
124
+ }
125
+ if (meaningfulExtraKeys.length > 0) differences.push(`Extra keys in generated at ${path}: ${meaningfulExtraKeys.join(", ")}`);
126
+ if (emptyExtraKeys.length > 0) warnings.push(`Extra keys in generated at ${path}: ${emptyExtraKeys.join(", ")}`);
127
+ }
128
+ let allMatch = true;
129
+ for (const key of aKeys) if (bKeys.includes(key)) {
130
+ if (!compareValues(`${path}.${key}`, a[key], b[key])) allMatch = false;
131
+ }
132
+ return allMatch && missingInB.length === 0;
133
+ };
134
+ const compareValues = (path, a, b) => {
135
+ if ((path.match(/\./g) || []).length > 50) {
136
+ warnings.push(`Max comparison depth reached at ${path}`);
137
+ return true;
138
+ }
139
+ if (a === null && b === null) return true;
140
+ if (a === void 0 && b === void 0) return true;
141
+ if (a === null && b === void 0 || a === void 0 && b === null) return true;
142
+ if (Array.isArray(a) && a.length === 0 && b === void 0) return true;
143
+ if (a === void 0 && Array.isArray(b) && b.length === 0) return true;
144
+ if (typeof a === "object" && a !== null && Object.keys(a).length === 0 && b === void 0) return true;
145
+ if (a === void 0 && typeof b === "object" && b !== null && Object.keys(b).length === 0) return true;
146
+ if (path.includes(".models") && typeof a === "object" && a !== null && b === void 0) {
147
+ if (a.model || a.provider || typeof a === "object" && (a.base || a.fast || a.smart)) {
148
+ warnings.push(`Model inheritance at ${path}: API has explicit model config, generator uses inheritance (this is expected)`);
149
+ return true;
150
+ }
151
+ }
152
+ if (path.includes(".models") && a === void 0 && typeof b === "object" && b !== null) {
153
+ if (b.model || b.provider || typeof b === "object" && (b.base || b.fast || b.smart)) {
154
+ warnings.push(`Model inheritance at ${path}: generator has explicit model config, API uses inheritance (this is expected)`);
155
+ return true;
156
+ }
157
+ }
158
+ if (Array.isArray(a) && Array.isArray(b)) return compareArrays(path, a, b);
159
+ if (Array.isArray(a) !== Array.isArray(b)) {
160
+ differences.push(`Array type mismatch at ${path}`);
161
+ return false;
162
+ }
163
+ if (typeof a === "object" && typeof b === "object") return compareObjects(path, a, b);
164
+ return comparePrimitive(path, a, b);
165
+ };
166
+ comparePrimitive("id", original.id, generated.id);
167
+ comparePrimitive("name", original.name, generated.name);
168
+ if (original.description || generated.description) {
169
+ const origDesc = original.description || "";
170
+ const genDesc = generated.description || "";
171
+ if (origDesc !== genDesc) comparePrimitive("description", origDesc, genDesc);
172
+ }
173
+ if (original.models || generated.models) compareValues("models", original.models, generated.models);
174
+ if (original.stopWhen || generated.stopWhen) compareValues("stopWhen", original.stopWhen, generated.stopWhen);
175
+ const originalAgentIds = Object.keys(original.agents || {});
176
+ const generatedAgentIds = Object.keys(generated.agents || {});
177
+ if (originalAgentIds.length !== generatedAgentIds.length) differences.push(`Agent count mismatch: ${originalAgentIds.length} vs ${generatedAgentIds.length}`);
178
+ for (const agentId of originalAgentIds) if (!generatedAgentIds.includes(agentId)) differences.push(`Missing agent in generated: ${agentId}`);
179
+ else compareValues(`agents.${agentId}`, original.agents?.[agentId], generated.agents?.[agentId]);
180
+ for (const agentId of generatedAgentIds) if (!originalAgentIds.includes(agentId)) warnings.push(`Extra agent in generated: ${agentId}`);
181
+ const originalToolIds = Object.keys(original.tools || {});
182
+ const generatedToolIds = Object.keys(generated.tools || {});
183
+ if (originalToolIds.length !== generatedToolIds.length) differences.push(`Tool count mismatch: ${originalToolIds.length} vs ${generatedToolIds.length}`);
184
+ for (const toolId of originalToolIds) if (!generatedToolIds.includes(toolId)) differences.push(`Missing tool in generated: ${toolId}`);
185
+ else compareValues(`tools.${toolId}`, original.tools?.[toolId], generated.tools?.[toolId]);
186
+ if (original.functions || generated.functions) {
187
+ const originalFunctionIds = Object.keys(original.functions || {});
188
+ const generatedFunctionIds = Object.keys(generated.functions || {});
189
+ for (const functionId of originalFunctionIds) if (!generatedFunctionIds.includes(functionId)) differences.push(`Missing function in generated: ${functionId}`);
190
+ else compareValues(`functions.${functionId}`, original.functions?.[functionId], generated.functions?.[functionId]);
191
+ }
192
+ if (original.functionTools || generated.functionTools) {
193
+ const originalFunctionToolIds = Object.keys(original.functionTools || {});
194
+ const generatedFunctionToolIds = Object.keys(generated.functionTools || {});
195
+ if (originalFunctionToolIds.length !== generatedFunctionToolIds.length) differences.push(`Function tool count mismatch: ${originalFunctionToolIds.length} vs ${generatedFunctionToolIds.length}`);
196
+ for (const functionToolId of originalFunctionToolIds) if (!generatedFunctionToolIds.includes(functionToolId)) differences.push(`Missing function tool in generated: ${functionToolId}`);
197
+ else compareValues(`functionTools.${functionToolId}`, original.functionTools?.[functionToolId], generated.functionTools?.[functionToolId]);
198
+ for (const functionToolId of generatedFunctionToolIds) if (!originalFunctionToolIds.includes(functionToolId)) warnings.push(`Extra function tool in generated: ${functionToolId}`);
199
+ }
200
+ if (original.dataComponents || generated.dataComponents) {
201
+ const originalComponentIds = Object.keys(original.dataComponents || {});
202
+ const generatedComponentIds = Object.keys(generated.dataComponents || {});
203
+ for (const componentId of originalComponentIds) if (!generatedComponentIds.includes(componentId)) differences.push(`Missing data component in generated: ${componentId}`);
204
+ else compareValues(`dataComponents.${componentId}`, original.dataComponents?.[componentId], generated.dataComponents?.[componentId]);
205
+ }
206
+ if (original.artifactComponents || generated.artifactComponents) {
207
+ const originalArtifactIds = Object.keys(original.artifactComponents || {});
208
+ const generatedArtifactIds = Object.keys(generated.artifactComponents || {});
209
+ for (const artifactId of originalArtifactIds) if (!generatedArtifactIds.includes(artifactId)) differences.push(`Missing artifact component in generated: ${artifactId}`);
210
+ else compareValues(`artifactComponents.${artifactId}`, original.artifactComponents?.[artifactId], generated.artifactComponents?.[artifactId]);
211
+ }
212
+ if (original.credentialReferences || generated.credentialReferences) {
213
+ const originalCredIds = Object.keys(original.credentialReferences || {});
214
+ const generatedCredIds = Object.keys(generated.credentialReferences || {});
215
+ for (const credId of originalCredIds) if (!generatedCredIds.includes(credId)) differences.push(`Missing credential reference in generated: ${credId}`);
216
+ else {
217
+ const origCred = { ...original.credentialReferences?.[credId] || {} };
218
+ const genCred = { ...generated.credentialReferences?.[credId] || {} };
219
+ delete origCred.usedBy;
220
+ delete genCred.usedBy;
221
+ compareValues(`credentialReferences.${credId}`, origCred, genCred);
222
+ }
223
+ }
224
+ return {
225
+ matches: differences.length === 0,
226
+ differences,
227
+ warnings
228
+ };
229
+ }
230
+
231
+ //#endregion
232
+ export { compareProjectDefinitions };