agentnet 0.0.3 → 0.0.5

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.
@@ -1,4 +1,4 @@
1
- import { AgentLoaderJSON, Message, Bindings, PostgresStore } from "../../src/index.js"
1
+ import { AgentLoaderJSON, Message, Bindings, MemoryStore } from "../../src/index.js"
2
2
 
3
3
  const agentDefinition = {
4
4
  "apiVersion": "agentnet/v1alpha1",
@@ -9,25 +9,18 @@ const agentDefinition = {
9
9
  },
10
10
  "spec": {
11
11
  "store": {
12
- "type": "Postgres",
12
+ "type": "Memory",
13
13
  },
14
14
  "llm": {
15
- "provider": "Gemini",
16
- "model": "gemini-2.0-flash",
17
- "systemInstruction": "You are a highly advanced accomodation manager agent. \nPrioritize clarity and helpfulness.\nUse tools effectively to gather information.\n",
18
- "config": {
19
- "temperature": 0.5,
20
- "toolConfig": {
21
- "functionCallingConfig": {
22
- "mode": "auto"
23
- }
24
- }
25
- }
15
+ "provider": "GPT",
16
+ "model": "gpt-4o-mini",
17
+ "instructions": "You are a highly advanced accomodation manager agent. \nPrioritize clarity and helpfulness.\nUse tools effectively to gather information."
26
18
  },
27
19
  "tools": [
28
20
  {
29
- "name": "getRoomsListTool",
21
+ "name": "get_rooms_list_tool",
30
22
  "description": "Retrieves a list of available rooms based on criteria.",
23
+ "type": "function",
31
24
  "parameters": {
32
25
  "type": "object",
33
26
  "properties": {
@@ -51,8 +44,9 @@ const agentDefinition = {
51
44
  }
52
45
  },
53
46
  {
54
- "name": "getRoomDetailTool",
47
+ "name": "get_room_detail_tool",
55
48
  "description": "Retrieves detailed information about a specific room.",
49
+ "type": "function",
56
50
  "parameters": {
57
51
  "type": "object",
58
52
  "properties": {
@@ -73,15 +67,15 @@ const agentDefinition = {
73
67
  // Load the agent definition
74
68
  const agents = await AgentLoaderJSON(agentDefinition, {
75
69
  bindings: {
76
- [Bindings.Postgres]: PostgresStore()
70
+ [Bindings.Memory]: MemoryStore()
77
71
  }
78
72
  })
79
73
 
80
74
  // Add the binding tools to the agent
81
- agents.accomodationAgent.tools.getRoomsListTool.bind(async (state, input) => {
75
+ agents.accomodationAgent.tools.get_rooms_list_tool.bind(async (state, input) => {
82
76
  return { answer: "We have Double room with a view of the sea and a single room with a view of the pool, and a suite with a view of the city." }
83
77
  })
84
- agents.accomodationAgent.tools.getRoomDetailTool.bind(async (state, input) => {
78
+ agents.accomodationAgent.tools.get_room_detail_tool.bind(async (state, input) => {
85
79
  return { answer: "The Double room with a view of the sea has a king size bed, a private balcony, and a view of the sea." }
86
80
  })
87
81
 
@@ -1,42 +1,37 @@
1
1
  ---
2
- apiVersion: smartagent.io/v1alpha1
2
+ apiVersion: agentnet/v1alpha1
3
3
  kind: AgentDefinition
4
4
  metadata:
5
5
  name: entrypoint
6
- namespace: smartchat
6
+ namespace: smartexample
7
7
  spec:
8
8
  io:
9
9
  - type: NatsIO
10
10
  bindings:
11
- discoveryTopic: smartness.discovery
11
+ discoveryTopic: smartexample.discovery
12
12
  acceptedNetworks:
13
- - "smartchat.*"
13
+ - "smartexample.*"
14
14
  store:
15
- type: Memory
15
+ type: Postgres
16
16
  llm:
17
- provider: Gemini
18
- model: gemini-2.0-flash
19
- systemInstruction: |
17
+ provider: GPT
18
+ model: gpt-4.1-2025-04-14
19
+ instructions: |
20
20
  You are a highly advanced triage agent.
21
21
  Prioritize clarity and helpfulness.
22
22
  Use tools effectively to gather information.
23
- config:
24
- temperature: 0.5
25
- toolConfig:
26
- functionCallingConfig:
27
- mode: 'auto'
28
23
 
29
24
  ---
30
- apiVersion: smartagent.io/v1alpha1
25
+ apiVersion: agentnet/v1alpha1
31
26
  kind: AgentDefinition
32
27
  metadata:
33
28
  name: accomodation
34
- namespace: smartchat
29
+ namespace: smartexample
35
30
  spec:
36
31
  io:
37
32
  - type: NatsIO
38
33
  bindings:
39
- discoveryTopic: smartness.discovery
34
+ discoveryTopic: smartexample.discovery
40
35
  store:
41
36
  type: Postgres
42
37
 
@@ -96,16 +91,16 @@ spec:
96
91
  - question
97
92
 
98
93
  ---
99
- apiVersion: smartagent.io/v1alpha1
94
+ apiVersion: agentnet/v1alpha1
100
95
  kind: AgentDefinition
101
96
  metadata:
102
97
  name: review
103
- namespace: smartchat
98
+ namespace: smartexample
104
99
  spec:
105
100
  io:
106
101
  - type: NatsIO
107
102
  bindings:
108
- discoveryTopic: smartness.discovery
103
+ discoveryTopic: smartexample.discovery
109
104
  store:
110
105
  type: Postgres
111
106
 
@@ -147,16 +142,16 @@ spec:
147
142
  - hotelName
148
143
 
149
144
  ---
150
- apiVersion: smartagent.io/v1alpha1
145
+ apiVersion: agentnet/v1alpha1
151
146
  kind: AgentDefinition
152
147
  metadata:
153
148
  name: booking
154
- namespace: smartchat
149
+ namespace: smartexample
155
150
  spec:
156
151
  io:
157
152
  - type: NatsIO
158
153
  bindings:
159
- discoveryTopic: smartness.discovery
154
+ discoveryTopic: smartexample.discovery
160
155
  store:
161
156
  type: Postgres
162
157
 
@@ -217,16 +212,16 @@ spec:
217
212
  - roomName
218
213
 
219
214
  ---
220
- apiVersion: smartagent.io/v1alpha1
215
+ apiVersion: agentnet/v1alpha1
221
216
  kind: AgentDefinition
222
217
  metadata:
223
218
  name: pricing
224
- namespace: smartchat
219
+ namespace: smartexample
225
220
  spec:
226
221
  io:
227
222
  - type: NatsIO
228
223
  bindings:
229
- discoveryTopic: smartness.discovery
224
+ discoveryTopic: smartexample.discovery
230
225
  store:
231
226
  type: Postgres
232
227
 
@@ -57,11 +57,19 @@ const agentClient = AgentClient()
57
57
  const message = new Message({
58
58
  content: "What rooms do you have from 2025-05-25 to 2025-05-30 for 3 guests For the hotel Flora? Give me the review of the hotel Flora",
59
59
  session: {
60
- id: "67a71e42-a7d8-1db2-ad17-64e1c8546b21"
60
+ id: "67a71e42-a7d8-1db2-ad17-64e1c8546b20"
61
61
  }
62
62
  })
63
63
  const res = await agentClient.queryIo(io, 'entrypoint', message)
64
64
  console.log("=======\n", res.getContent())
65
65
  console.log("=======\n", res.getSession())
66
- //const res2 = await agentClient.queryIo(io, 'smartnessAgent', "Quanto costa la camera doppia del Flora per il 10-05-2025 per due persone? Prenotala se costa meno di 100€ la camera double con vista mare per il 10-05-2025 al hotel Flora")
67
- //console.log("=======\n", res2)
66
+
67
+ const message2 = new Message({
68
+ content: "Quanto costa la camera doppia del Flora per il 10-05-2025 per due persone? Prenotala se costa meno di 100€ la camera double con vista mare per il 10-05-2025 al hotel Flora",
69
+ session: {
70
+ id: "67a71e42-a7d8-1db2-ad17-64e1c8546b20"
71
+ }
72
+ })
73
+ const res2 = await agentClient.queryIo(io, 'entrypoint', message2)
74
+ console.log("=======\n", res2.getContent())
75
+ console.log("=======\n", res2.getSession())
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "agentnet",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "description": "Agent library used by Smartness",
6
- "main": "index.js",
6
+ "main": "src/index.js",
7
7
  "scripts": {
8
8
  "test": "node --experimental-vm-modules ./node_modules/.bin/jest --rootDir=src/tests"
9
9
  },
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs'
2
2
  import { parse } from 'yaml'
3
3
  import { Gemini } from "../index.js"
4
+ import { GPT } from "../index.js"
4
5
  import { Agent } from "./agent.js"
5
6
  import { logger } from "../utils/logger.js"
6
7
  import { ConfigurationError } from "../errors/index.js"
@@ -10,8 +11,7 @@ import { validateApiVersion, DEFAULT_API_VERSION, API_VERSIONS } from '../utils/
10
11
  * Version handlers for different API versions
11
12
  */
12
13
  const VERSION_HANDLERS = {
13
- 'smartagent.io/v1alpha1': processV1Alpha1Definition,
14
- 'agentnet.io/v1alpha1': processV1Alpha1Definition
14
+ 'agentnet/v1alpha1': processV1Alpha1Definition
15
15
  // Additional version handlers can be added here as the API evolves
16
16
  };
17
17
 
@@ -139,6 +139,9 @@ async function loadLlmProvider(providerName) {
139
139
  if (providerName === 'Gemini') {
140
140
  return Gemini;
141
141
  }
142
+ if (providerName === 'GPT') {
143
+ return GPT;
144
+ }
142
145
 
143
146
  try {
144
147
  return global[providerName] || await import(providerName);
@@ -209,11 +212,14 @@ async function configureLLM(agentBuilder, llmSpec) {
209
212
 
210
213
  const llmProviderInstance = await loadLlmProvider(llmSpec.provider);
211
214
 
212
- return agentBuilder.withLLM(llmProviderInstance, {
213
- model: llmSpec.model,
214
- systemInstruction: llmSpec.systemInstruction,
215
- config: llmSpec.config
216
- });
215
+ const config = {}
216
+ for (const key in llmSpec) {
217
+ if (key !== 'provider') {
218
+ config[key] = llmSpec[key];
219
+ }
220
+ }
221
+
222
+ return agentBuilder.withLLM(llmProviderInstance, config);
217
223
  }
218
224
 
219
225
  /**
@@ -34,7 +34,7 @@ async function emit(hooks, event, data) {
34
34
  * @param {Array} tools - Tool definitions
35
35
  * @param {Array} handoffs - Handoff definitions
36
36
  */
37
- export function makeToolsAndHandoffsMap(toolsAndHandoffsMap, tools, handoffs) {
37
+ export function makeToolsAndHandoffsMap(llmType, toolsAndHandoffsMap, tools, handoffs) {
38
38
  if (!tools) {
39
39
  return;
40
40
  }
@@ -71,6 +71,11 @@ export function makeToolsAndHandoffsMap(toolsAndHandoffsMap, tools, handoffs) {
71
71
  logger.warn('Skipping invalid handoff definition', { handoff });
72
72
  continue;
73
73
  }
74
+
75
+ // TODO: Remove this once we have a better way to handle handoffs
76
+ if (llmType === 'openai') {
77
+ handoff.schema.type = 'function'
78
+ }
74
79
 
75
80
  // Add handoff schema to tools list
76
81
  toolsAndHandoffsMap.tools.push(handoff.schema);
@@ -295,12 +300,6 @@ export async function build(
295
300
  error: error
296
301
  });
297
302
 
298
- // Add error message to conversation
299
- contents.push({
300
- role: 'system',
301
- content: `Error: ${error.message}`
302
- });
303
-
304
303
  // If we haven't hit max runs, try again
305
304
  if (run < maxRuns - 1) {
306
305
  logger.info(`Continuing after error in agent ${agentName}`);
@@ -45,6 +45,7 @@ export async function AgentRuntime(agentConfig) {
45
45
  try {
46
46
  // Update tools and handoffs map with discovered agents
47
47
  makeToolsAndHandoffsMap(
48
+ llmApi.type,
48
49
  toolsAndHandoffsMap,
49
50
  Object.values(tools),
50
51
  [handoffs, Object.values(discoveredAgents)]
package/src/llm/base.js CHANGED
@@ -82,12 +82,12 @@ export class BaseLLM {
82
82
  * @param {Object} toolsAndHandoffsMap - Map of available tools
83
83
  * @returns {Promise<any>} Result of the tool execution
84
84
  */
85
- async executeToolCall(toolCall, name, args, state, conversation, toolsAndHandoffsMap) {
85
+ async executeToolCall(toolCall, name, args, state, toolsAndHandoffsMap) {
86
86
  logger.debug(`Executing tool from ${this.type}`, {
87
87
  toolName: name,
88
88
  argsPreview: JSON.stringify(args).substring(0, 100)
89
89
  });
90
-
90
+
91
91
  try {
92
92
  if (!toolsAndHandoffsMap[name] || !toolsAndHandoffsMap[name].function) {
93
93
  throw new Error(`Tool "${name}" not found or has no function implementation`);
@@ -95,7 +95,7 @@ export class BaseLLM {
95
95
 
96
96
  let result = null;
97
97
  if (toolsAndHandoffsMap[name].type === 'handoff') {
98
- result = await toolsAndHandoffsMap[name].function(conversation, state, args);
98
+ result = await toolsAndHandoffsMap[name].function(state, args);
99
99
  // Process handoff results if needed
100
100
  this.processHandoffResult(result, state);
101
101
  } else {
@@ -106,7 +106,7 @@ export class BaseLLM {
106
106
  return result;
107
107
 
108
108
  } catch (error) {
109
- logger.error(`Error executing tool "${name}"`, { error });
109
+ logger.error(`Error executing tool "${name}"`, error.message);
110
110
  throw error;
111
111
  }
112
112
  }
package/src/llm/gemini.js CHANGED
@@ -65,6 +65,7 @@ class GeminiLLM extends BaseLLM {
65
65
  });
66
66
  return res;
67
67
  } catch (error) {
68
+ console.log(error)
68
69
  logger.error('Gemini API error', {
69
70
  error,
70
71
  modelName: input.model
@@ -93,7 +94,7 @@ class GeminiLLM extends BaseLLM {
93
94
  const name = toolCall.name;
94
95
 
95
96
  try {
96
- const result = await super.executeToolCall(toolCall, name, args, state, conversation, toolsAndHandoffsMap);
97
+ const result = await super.executeToolCall(toolCall, name, args, state, toolsAndHandoffsMap);
97
98
 
98
99
  // Add function call and response to conversation in Gemini-specific format
99
100
  const function_response_part = {
@@ -128,7 +129,6 @@ class GeminiLLM extends BaseLLM {
128
129
  // Handle simple text response
129
130
  if (response.text !== undefined) {
130
131
  logger.debug('Gemini response contains text, returning directly');
131
- conversation.push({ role: 'model', parts: [{ text: response.text }] });
132
132
  return response.text;
133
133
  }
134
134
 
package/src/llm/gpt.js CHANGED
@@ -50,14 +50,14 @@ class OpenAILLM extends BaseLLM {
50
50
  conversationLength: conversation.length,
51
51
  toolsCount: toolsAndHandoffsMap.tools.length
52
52
  });
53
-
53
+ //console.log(JSON.stringify(input, null, 2))
54
54
  try {
55
55
  const response = await client.responses.create(input);
56
56
  logger.debug('OpenAI response received');
57
57
  return response;
58
58
  } catch (error) {
59
59
  logger.error('OpenAI API error', {
60
- error,
60
+ error: error.message,
61
61
  modelName: input.model
62
62
  });
63
63
 
@@ -90,7 +90,7 @@ class OpenAILLM extends BaseLLM {
90
90
  callId: toolCall.call_id
91
91
  });
92
92
 
93
- const result = await super.executeToolCall(toolCall, name, args, state, conversation, toolsAndHandoffsMap);
93
+ const result = await super.executeToolCall(toolCall, name, args, state, toolsAndHandoffsMap);
94
94
  conversation.push(toolCall);
95
95
 
96
96
  const resultString = typeof result === 'string' ? result : JSON.stringify(result);
@@ -129,7 +129,6 @@ class OpenAILLM extends BaseLLM {
129
129
  async onResponse(state, conversation, toolsAndHandoffsMap, response) {
130
130
  if (response.output_text !== undefined && response.output_text.length > 0) {
131
131
  logger.debug('OpenAI response contains text, returning directly');
132
- conversation.push({ role: 'model', parts: [{ text: response.output_text }] });
133
132
  return response.output_text;
134
133
  }
135
134
 
@@ -191,10 +191,12 @@ export function memoryStore () {
191
191
  connect: async function () {},
192
192
  disconnect: async function () {},
193
193
  set: async function (key, value) {
194
+ console.log('Setting state', key, value)
194
195
  state[key] = value
195
196
  return state[key]
196
197
  },
197
198
  get: async function (key) {
199
+ console.log('Getting state', key)
198
200
  return state[key] || null
199
201
  }
200
202
  }
@@ -15,7 +15,6 @@ import {
15
15
  // Constants
16
16
  const HEARTBEAT_INTERVAL = 1000;
17
17
  const TIMEOUT_TASK_REQUEST = 60000;
18
- const MAX_RECONNECT_ATTEMPTS = 5;
19
18
 
20
19
  /**
21
20
  * NATS implementation of the Transport interface
@@ -225,13 +224,14 @@ export class NatsTransport extends Transport {
225
224
  continue;
226
225
  }
227
226
  }
227
+
228
228
 
229
229
  // Skip if not accepted
230
230
  if (!isAccepted) {
231
231
  logger.warn(`Agent ${agentName} does not accept network ${network}`);
232
232
  nonAcceptedNetworks[network] = true;
233
233
  continue;
234
- }
234
+ }
235
235
 
236
236
  // Process the schemas from the discovery message
237
237
  for (const schema of discoveryMessage.schemas) {
@@ -242,11 +242,10 @@ export class NatsTransport extends Transport {
242
242
  }
243
243
 
244
244
  const agentKey = `${network}-${schema.name}`;
245
-
246
245
  if (discoveryMessage.agentName !== agentName && !discoveredAgents[agentKey]) {
247
246
  logger.info(`${agentName} discovered agent capability: ${discoveryMessage.agentName} with capability ${schema.name}`);
248
247
 
249
- const handoffFunction = async (conversation, state, input) => {
248
+ const handoffFunction = async (state, input) => {
250
249
  try {
251
250
  // Use withTimeout to ensure handoffs don't hang
252
251
  return await withTimeout(
@@ -13,7 +13,7 @@ export const LogLevel = {
13
13
  * Default logger configuration
14
14
  */
15
15
  const DEFAULT_CONFIG = {
16
- level: LogLevel.INFO,
16
+ level: process.env.AGENTNET_LOG_LEVEL || LogLevel.INFO,
17
17
  enableTimestamps: true,
18
18
  enableColors: true,
19
19
  redactSensitiveData: true,
@@ -5,18 +5,10 @@ import { ConfigurationError } from '../errors/index.js';
5
5
  * Map of API versions with their compatibility information
6
6
  */
7
7
  export const API_VERSIONS = {
8
- // Smartagent API versions
9
- 'smartagent.io/v1alpha1': {
8
+ 'agentnet/v1alpha1': {
10
9
  isSupported: true,
11
- isStable: false,
12
- minEngineVersion: '0.5.0',
13
- features: ['basic', 'discovery', 'handoffs']
14
- },
15
- // Agentnet API versions
16
- 'agentnet.io/v1alpha1': {
17
- isSupported: true,
18
- isStable: false,
19
- minEngineVersion: '0.8.0',
10
+ isStable: true,
11
+ minEngineVersion: '0.0.4',
20
12
  features: ['basic', 'discovery', 'handoffs', 'advanced-routing']
21
13
  }
22
14
  };
@@ -24,19 +16,19 @@ export const API_VERSIONS = {
24
16
  /**
25
17
  * Default API version to use when none is specified
26
18
  */
27
- export const DEFAULT_API_VERSION = 'smartagent.io/v1alpha1';
19
+ export const DEFAULT_API_VERSION = 'agentnet/v1alpha1';
28
20
 
29
21
  /**
30
22
  * Latest stable API version
31
23
  */
32
- export const LATEST_STABLE_VERSION = 'agentnet.io/v1alpha1';
24
+ export const LATEST_STABLE_VERSION = 'agentnet/v1alpha1';
33
25
 
34
26
  /**
35
27
  * Version upgrade paths map
36
28
  * Defines valid upgrade paths between versions
37
29
  */
38
30
  export const VERSION_UPGRADE_PATHS = {
39
- 'smartagent.io/v1alpha1': ['agentnet.io/v1alpha1']
31
+ 'agentnet/v1alpha1': ['agentnet/v1alpha1']
40
32
  };
41
33
 
42
34
  /**
@@ -154,18 +146,18 @@ export function getUpgradeOptions(fromVersion) {
154
146
  */
155
147
  const VERSION_MIGRATIONS = {
156
148
  // Migration from smartagent.io/v1alpha1 to agentnet.io/v1alpha1
157
- 'smartagent.io/v1alpha1->agentnet.io/v1alpha1': (definition) => {
149
+ 'smartagent.io/v1alpha1->agentnet.io/v1alpha1': (definition) => {
158
150
  // Deep clone the definition to avoid modifying the original
159
151
  const newDef = JSON.parse(JSON.stringify(definition));
160
152
 
161
153
  // Update the apiVersion
162
- newDef.apiVersion = 'agentnet.io/v1alpha1';
154
+ newDef.apiVersion = 'agentnet/v1alpha1';
163
155
 
164
156
  // Handle specific field migrations
165
157
  // Example: rename or restructure fields as needed
166
158
 
167
159
  // Log the migration
168
- logger.info(`Migrated agent definition from smartagent.io/v1alpha1 to agentnet.io/v1alpha1`, {
160
+ logger.info(`Migrated agent definition from smartagent.io/v1alpha1 to agentnet/v1alpha1`, {
169
161
  agentName: newDef.metadata?.name
170
162
  });
171
163
 
@@ -1,250 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from 'fs';
4
- import path from 'path';
5
- import { parse, stringify } from 'yaml';
6
- import { migrateDefinition, validateApiVersion, API_VERSIONS, LATEST_STABLE_VERSION } from '../utils/version.js';
7
- import { logger } from '../utils/logger.js';
8
-
9
- /**
10
- * Print command usage instructions
11
- */
12
- function printUsage() {
13
- console.log(`
14
- Agent Definition Version Migration Utility
15
-
16
- Usage:
17
- node migrate-version.js <input-file> [options]
18
-
19
- Options:
20
- --output <file> Output file (default: adds '-migrated' to input filename)
21
- --version <ver> Target API version (default: ${LATEST_STABLE_VERSION})
22
- --check Only check if migration is needed, don't perform it
23
- --quiet Suppress informational output
24
- --help Show this help message
25
-
26
- Examples:
27
- node migrate-version.js ./agents.yaml
28
- node migrate-version.js ./agents.yaml --version agentnet.io/v1alpha1
29
- node migrate-version.js ./agents.yaml --output ./agents-new.yaml
30
- node migrate-version.js ./agents.yaml --check
31
- `);
32
- }
33
-
34
- /**
35
- * Parse command line arguments
36
- * @returns {Object} Parsed arguments
37
- */
38
- function parseArgs() {
39
- const args = process.argv.slice(2);
40
- const result = {
41
- inputFile: null,
42
- outputFile: null,
43
- targetVersion: LATEST_STABLE_VERSION,
44
- checkOnly: false,
45
- quiet: false,
46
- help: false
47
- };
48
-
49
- for (let i = 0; i < args.length; i++) {
50
- const arg = args[i];
51
-
52
- if (arg === '--help' || arg === '-h') {
53
- result.help = true;
54
- } else if (arg === '--output' || arg === '-o') {
55
- result.outputFile = args[++i];
56
- } else if (arg === '--version' || arg === '-v') {
57
- result.targetVersion = args[++i];
58
- } else if (arg === '--check' || arg === '-c') {
59
- result.checkOnly = true;
60
- } else if (arg === '--quiet' || arg === '-q') {
61
- result.quiet = true;
62
- } else if (!result.inputFile) {
63
- result.inputFile = arg;
64
- }
65
- }
66
-
67
- return result;
68
- }
69
-
70
- /**
71
- * Get default output filename based on input filename
72
- * @param {string} inputFile - Input file path
73
- * @param {string} targetVersion - Target API version
74
- * @returns {string} Default output file path
75
- */
76
- function getDefaultOutputFile(inputFile, targetVersion) {
77
- const parsedPath = path.parse(inputFile);
78
- const versionSuffix = targetVersion.replace(/\//g, '-');
79
- return path.join(
80
- parsedPath.dir,
81
- `${parsedPath.name}-${versionSuffix}${parsedPath.ext}`
82
- );
83
- }
84
-
85
- /**
86
- * Log a message if not in quiet mode
87
- * @param {string} message - Message to log
88
- * @param {boolean} isError - Whether this is an error message
89
- * @param {boolean} quiet - Whether quiet mode is enabled
90
- */
91
- function log(message, isError = false, quiet = false) {
92
- if (isError || !quiet) {
93
- console.log(message);
94
- }
95
- }
96
-
97
- /**
98
- * Process and migrate a YAML file containing agent definitions
99
- * @param {string} content - YAML content
100
- * @param {string} targetVersion - Target API version
101
- * @param {boolean} checkOnly - Only check, don't modify
102
- * @returns {Object} Migration results including modified content
103
- */
104
- function processYamlFile(content, targetVersion, checkOnly) {
105
- // Split the YAML content by document separator
106
- const documents = content.split(/^---$/m)
107
- .map(s => s.trim())
108
- .filter(s => s);
109
-
110
- const result = {
111
- migrated: false,
112
- migratedCount: 0,
113
- alreadyUpToDateCount: 0,
114
- failedCount: 0,
115
- needsMigration: false,
116
- updatedContent: null,
117
- failures: []
118
- };
119
-
120
- // Process each document
121
- const processedDocs = [];
122
-
123
- for (let i = 0; i < documents.length; i++) {
124
- try {
125
- const docContent = documents[i];
126
- const doc = parse(docContent);
127
-
128
- // Skip non-agent definitions
129
- if (!doc || doc.kind !== 'AgentDefinition') {
130
- processedDocs.push(docContent);
131
- continue;
132
- }
133
-
134
- // Check if migration is needed
135
- const currentVersion = doc.apiVersion || 'smartagent.io/v1alpha1';
136
- const agentName = doc.metadata?.name || `[Document ${i+1}]`;
137
-
138
- if (currentVersion === targetVersion) {
139
- result.alreadyUpToDateCount++;
140
- log(`Agent "${agentName}" is already at version ${targetVersion}`);
141
- processedDocs.push(docContent);
142
- continue;
143
- }
144
-
145
- result.needsMigration = true;
146
-
147
- // If only checking, skip migration
148
- if (checkOnly) {
149
- processedDocs.push(docContent);
150
- continue;
151
- }
152
-
153
- // Perform migration
154
- const migratedDoc = migrateDefinition(doc, targetVersion);
155
- result.migratedCount++;
156
- result.migrated = true;
157
-
158
- // Convert back to YAML and add to processed docs
159
- processedDocs.push(stringify(migratedDoc));
160
-
161
- log(`Successfully migrated agent "${agentName}" from ${currentVersion} to ${targetVersion}`);
162
-
163
- } catch (error) {
164
- result.failedCount++;
165
- result.failures.push({
166
- documentIndex: i,
167
- error: error.message
168
- });
169
-
170
- // Keep original document on failure
171
- processedDocs.push(documents[i]);
172
-
173
- log(`Error processing document ${i+1}: ${error.message}`, true);
174
- }
175
- }
176
-
177
- // Combine the processed documents back into a single YAML file
178
- result.updatedContent = processedDocs.join('\n---\n');
179
-
180
- return result;
181
- }
182
-
183
- /**
184
- * Main function
185
- */
186
- async function main() {
187
- const args = parseArgs();
188
-
189
- // Show help if requested or no input file
190
- if (args.help || !args.inputFile) {
191
- printUsage();
192
- process.exit(args.help ? 0 : 1);
193
- }
194
-
195
- // Validate target version
196
- if (!API_VERSIONS[args.targetVersion]) {
197
- log(`Error: Unsupported target version: ${args.targetVersion}`, true);
198
- log(`Supported versions: ${Object.keys(API_VERSIONS).join(', ')}`, true);
199
- process.exit(1);
200
- }
201
-
202
- try {
203
- // Set default output file if not specified
204
- if (!args.outputFile && !args.checkOnly) {
205
- args.outputFile = getDefaultOutputFile(args.inputFile, args.targetVersion);
206
- }
207
-
208
- // Read input file
209
- const content = fs.readFileSync(args.inputFile, 'utf8');
210
-
211
- // Process the file
212
- const result = processYamlFile(content, args.targetVersion, args.checkOnly);
213
-
214
- // Output results
215
- if (result.needsMigration) {
216
- if (args.checkOnly) {
217
- log(`Migration needed: ${result.alreadyUpToDateCount} up-to-date, ${result.failedCount + documents.length - result.alreadyUpToDateCount} need migration`);
218
- process.exit(10); // Special exit code indicating migration needed
219
- } else if (result.migrated) {
220
- // Write the output file
221
- fs.writeFileSync(args.outputFile, result.updatedContent);
222
-
223
- log(`
224
- Migration completed:
225
- - ${result.migratedCount} agent definitions migrated
226
- - ${result.alreadyUpToDateCount} already up-to-date
227
- - ${result.failedCount} failures
228
- - Output written to: ${args.outputFile}
229
- `);
230
- }
231
- } else {
232
- log(`No migration needed. All agent definitions are already at version ${args.targetVersion}`);
233
- }
234
-
235
- // Exit with error if any migrations failed
236
- if (result.failedCount > 0) {
237
- process.exit(1);
238
- }
239
-
240
- } catch (error) {
241
- log(`Error: ${error.message}`, true);
242
- process.exit(1);
243
- }
244
- }
245
-
246
- // Run the main function
247
- main().catch(error => {
248
- console.error('Unhandled error:', error);
249
- process.exit(1);
250
- });