lua-cli 2.3.0 → 2.3.2

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.
@@ -62,9 +62,11 @@ export async function createLegacyDeploymentData(tools, luaDir, indexFile) {
62
62
  const skillsArray = buildSkillsArray(skillsMetadata, skillToTools, tools);
63
63
  // Ensure all skills exist in YAML config and have valid IDs
64
64
  const updatedSkillsArray = await ensureSkillsExistInYaml(skillsArray, config);
65
+ // Override versions from YAML config if they exist
66
+ const finalSkillsArray = overrideVersionsFromConfig(updatedSkillsArray, config);
65
67
  // Write deployment data
66
68
  const deployData = {
67
- skills: updatedSkillsArray
69
+ skills: finalSkillsArray
68
70
  };
69
71
  fs.writeFileSync(path.join(luaDir, COMPILE_FILES.DEPLOY_JSON), JSON.stringify(deployData, null, JSON_FORMAT.INDENT));
70
72
  // Write individual tool files (uncompressed for debugging)
@@ -144,6 +146,36 @@ function buildSkillsArray(skillsMetadata, skillToTools, tools) {
144
146
  };
145
147
  });
146
148
  }
149
+ /**
150
+ * Overrides skill versions from YAML config.
151
+ * This ensures that any version updates in the YAML take precedence over code versions.
152
+ *
153
+ * @param skillsArray - Array of skills from code
154
+ * @param config - Skill configuration from YAML
155
+ * @returns Skills array with versions overridden from config
156
+ */
157
+ function overrideVersionsFromConfig(skillsArray, config) {
158
+ // Get version map from YAML config
159
+ const configVersionMap = new Map();
160
+ if (config.skills && Array.isArray(config.skills)) {
161
+ config.skills.forEach((skill) => {
162
+ if (skill.name && skill.version) {
163
+ configVersionMap.set(skill.name, skill.version);
164
+ }
165
+ });
166
+ }
167
+ // Override versions in skills array
168
+ return skillsArray.map(skill => {
169
+ const configVersion = configVersionMap.get(skill.name);
170
+ if (configVersion) {
171
+ return {
172
+ ...skill,
173
+ version: configVersion
174
+ };
175
+ }
176
+ return skill;
177
+ });
178
+ }
147
179
  /**
148
180
  * Writes individual tool files to the .lua directory for debugging.
149
181
  * These are uncompressed versions of the tool code.
@@ -12,6 +12,11 @@ import { executeTool, createBroadcastConsole, loadEnvironmentVariables } from '.
12
12
  import { getSandboxSkillId } from './sandbox-storage.js';
13
13
  import { sendChatMessage } from './dev-api.js';
14
14
  import { CORS_HEADERS, HTTP_STATUS, DEV_DEFAULTS } from '../config/dev.constants.js';
15
+ import { BASE_URLS } from '../config/constants.js';
16
+ import ProductApi from '../api/products.api.service.js';
17
+ import BasketApi from '../api/basket.api.service.js';
18
+ import OrderApi from '../api/order.api.service.js';
19
+ import CustomDataApi from '../api/custom.data.api.service.js';
15
20
  const __filename = fileURLToPath(import.meta.url);
16
21
  const __dirname = path.dirname(__filename);
17
22
  /**
@@ -86,6 +91,26 @@ export function createChatServer(apiKey, agentId, skillId, sandboxId, port) {
86
91
  handleToolTestEndpoint(req, res, apiKey, agentId, wss);
87
92
  return;
88
93
  }
94
+ // Products routes
95
+ if (req.url?.startsWith('/api/products')) {
96
+ handleProductsEndpoint(req, res, apiKey, agentId);
97
+ return;
98
+ }
99
+ // Baskets routes
100
+ if (req.url?.startsWith('/api/baskets')) {
101
+ handleBasketsEndpoint(req, res, apiKey, agentId);
102
+ return;
103
+ }
104
+ // Orders routes
105
+ if (req.url?.startsWith('/api/orders')) {
106
+ handleOrdersEndpoint(req, res, apiKey, agentId);
107
+ return;
108
+ }
109
+ // Custom Data routes
110
+ if (req.url?.startsWith('/api/custom-data')) {
111
+ handleCustomDataEndpoint(req, res, apiKey, agentId);
112
+ return;
113
+ }
89
114
  if (req.method === 'GET' && req.url === '/') {
90
115
  handleIndexEndpoint(req, res, apiKey, agentId, skillId, sandboxId);
91
116
  return;
@@ -553,3 +578,233 @@ function handleStaticFile(req, res, filename, contentType) {
553
578
  res.end(`${filename} not found`);
554
579
  }
555
580
  }
581
+ /**
582
+ * Handler for products API using ProductApi service
583
+ */
584
+ async function handleProductsEndpoint(req, res, apiKey, agentId) {
585
+ const productApi = new ProductApi(BASE_URLS.API, apiKey, agentId);
586
+ try {
587
+ let result;
588
+ if (req.method === 'GET') {
589
+ if (req.url.includes('/search')) {
590
+ const searchQuery = new URL(req.url, 'http://localhost').searchParams.get('searchQuery') || '';
591
+ result = await productApi.search(searchQuery);
592
+ }
593
+ else if (req.url.match(/\/api\/products\/[^/]+$/)) {
594
+ const productId = req.url.split('/').pop();
595
+ result = await productApi.getById(productId);
596
+ }
597
+ else {
598
+ const url = new URL(req.url, 'http://localhost');
599
+ const page = parseInt(url.searchParams.get('page') || '1');
600
+ const limit = parseInt(url.searchParams.get('limit') || '20');
601
+ result = await productApi.get(page, limit);
602
+ }
603
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
604
+ res.end(JSON.stringify({ success: true, data: result }));
605
+ }
606
+ else if (req.method === 'POST') {
607
+ const body = await new Promise((resolve) => {
608
+ let data = '';
609
+ req.on('data', (chunk) => { data += chunk; });
610
+ req.on('end', () => resolve(data));
611
+ });
612
+ const productData = JSON.parse(body);
613
+ result = await productApi.create(productData);
614
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
615
+ res.end(JSON.stringify({ success: true, data: { product: result } }));
616
+ }
617
+ else if (req.method === 'PUT') {
618
+ const body = await new Promise((resolve) => {
619
+ let data = '';
620
+ req.on('data', (chunk) => { data += chunk; });
621
+ req.on('end', () => resolve(data));
622
+ });
623
+ const productData = JSON.parse(body);
624
+ result = await productApi.update(productData, productData.id);
625
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
626
+ res.end(JSON.stringify({ success: true, data: result }));
627
+ }
628
+ else if (req.method === 'DELETE') {
629
+ const productId = req.url.split('/').pop();
630
+ result = await productApi.delete(productId);
631
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
632
+ res.end(JSON.stringify({ success: true, data: result }));
633
+ }
634
+ }
635
+ catch (error) {
636
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
637
+ res.end(JSON.stringify({ success: false, error: error.message || 'API error' }));
638
+ }
639
+ }
640
+ /**
641
+ * Handler for baskets API using BasketApi service
642
+ */
643
+ async function handleBasketsEndpoint(req, res, apiKey, agentId) {
644
+ const basketApi = new BasketApi(BASE_URLS.API, apiKey, agentId);
645
+ try {
646
+ let result;
647
+ if (req.method === 'GET') {
648
+ if (req.url.match(/\/api\/baskets\/[^/]+$/)) {
649
+ const basketId = req.url.split('/').pop();
650
+ result = await basketApi.getById(basketId);
651
+ }
652
+ else {
653
+ const url = new URL(req.url, 'http://localhost');
654
+ const status = url.searchParams.get('status');
655
+ result = await basketApi.get(status);
656
+ }
657
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
658
+ res.end(JSON.stringify({ success: true, data: result }));
659
+ }
660
+ else if (req.method === 'POST') {
661
+ const body = await new Promise((resolve) => {
662
+ let data = '';
663
+ req.on('data', (chunk) => { data += chunk; });
664
+ req.on('end', () => resolve(data));
665
+ });
666
+ const basketData = JSON.parse(body);
667
+ result = await basketApi.create(basketData);
668
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
669
+ res.end(JSON.stringify({ success: true, data: result }));
670
+ }
671
+ else if (req.method === 'DELETE') {
672
+ const basketId = req.url.split('/').pop();
673
+ // Baskets don't have a direct delete in the API, use clear instead
674
+ result = await basketApi.clear(basketId);
675
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
676
+ res.end(JSON.stringify({ success: true, data: result }));
677
+ }
678
+ }
679
+ catch (error) {
680
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
681
+ res.end(JSON.stringify({ success: false, error: error.message || 'API error' }));
682
+ }
683
+ }
684
+ /**
685
+ * Handler for orders API using OrderApi service
686
+ */
687
+ async function handleOrdersEndpoint(req, res, apiKey, agentId) {
688
+ const orderApi = new OrderApi(BASE_URLS.API, apiKey, agentId);
689
+ try {
690
+ let result;
691
+ if (req.method === 'GET') {
692
+ if (req.url.match(/\/api\/orders\/[^/]+$/)) {
693
+ const orderId = req.url.split('/').pop();
694
+ result = await orderApi.getById(orderId);
695
+ }
696
+ else {
697
+ const url = new URL(req.url, 'http://localhost');
698
+ const status = url.searchParams.get('status');
699
+ result = await orderApi.get(status);
700
+ }
701
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
702
+ res.end(JSON.stringify({ success: true, data: result }));
703
+ }
704
+ else if (req.method === 'POST') {
705
+ const body = await new Promise((resolve) => {
706
+ let data = '';
707
+ req.on('data', (chunk) => { data += chunk; });
708
+ req.on('end', () => resolve(data));
709
+ });
710
+ const orderData = JSON.parse(body);
711
+ result = await orderApi.create(orderData);
712
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
713
+ res.end(JSON.stringify({ success: true, data: result }));
714
+ }
715
+ else if (req.method === 'PUT') {
716
+ if (req.url.includes('/status')) {
717
+ const orderId = req.url.split('/')[3];
718
+ const body = await new Promise((resolve) => {
719
+ let data = '';
720
+ req.on('data', (chunk) => { data += chunk; });
721
+ req.on('end', () => resolve(data));
722
+ });
723
+ const { status } = JSON.parse(body);
724
+ result = await orderApi.updateStatus(status, orderId);
725
+ }
726
+ else {
727
+ const body = await new Promise((resolve) => {
728
+ let data = '';
729
+ req.on('data', (chunk) => { data += chunk; });
730
+ req.on('end', () => resolve(data));
731
+ });
732
+ const orderData = JSON.parse(body);
733
+ const orderId = req.url.split('/').pop();
734
+ result = await orderApi.updateData(orderData, orderId);
735
+ }
736
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
737
+ res.end(JSON.stringify({ success: true, data: result }));
738
+ }
739
+ }
740
+ catch (error) {
741
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
742
+ res.end(JSON.stringify({ success: false, error: error.message || 'API error' }));
743
+ }
744
+ }
745
+ /**
746
+ * Handler for custom data API using CustomDataApi service
747
+ */
748
+ async function handleCustomDataEndpoint(req, res, apiKey, agentId) {
749
+ const customDataApi = new CustomDataApi(BASE_URLS.API, apiKey, agentId);
750
+ try {
751
+ let result;
752
+ const pathParts = req.url.split('/');
753
+ const collectionName = pathParts[3]; // /api/custom-data/{collection}
754
+ if (req.method === 'GET') {
755
+ if (req.url.includes('/search')) {
756
+ const url = new URL(req.url, 'http://localhost');
757
+ const searchText = url.searchParams.get('searchText') || '';
758
+ const limit = parseInt(url.searchParams.get('limit') || '10');
759
+ const scoreThreshold = parseFloat(url.searchParams.get('scoreThreshold') || '0.7');
760
+ result = await customDataApi.search(collectionName, searchText, limit, scoreThreshold);
761
+ }
762
+ else if (pathParts.length > 4) {
763
+ const entryId = pathParts[4];
764
+ result = await customDataApi.getEntry(collectionName, entryId);
765
+ }
766
+ else {
767
+ const url = new URL(req.url, 'http://localhost');
768
+ const filter = url.searchParams.get('filter') ? JSON.parse(url.searchParams.get('filter')) : undefined;
769
+ const page = parseInt(url.searchParams.get('page') || '1');
770
+ const limit = parseInt(url.searchParams.get('limit') || '10');
771
+ result = await customDataApi.get(collectionName, filter, page, limit);
772
+ }
773
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
774
+ res.end(JSON.stringify({ success: true, data: result }));
775
+ }
776
+ else if (req.method === 'POST') {
777
+ const body = await new Promise((resolve) => {
778
+ let data = '';
779
+ req.on('data', (chunk) => { data += chunk; });
780
+ req.on('end', () => resolve(data));
781
+ });
782
+ const { data, searchText } = JSON.parse(body);
783
+ result = await customDataApi.create(collectionName, data, searchText);
784
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
785
+ res.end(JSON.stringify({ success: true, data: result }));
786
+ }
787
+ else if (req.method === 'PUT') {
788
+ const entryId = pathParts[4];
789
+ const body = await new Promise((resolve) => {
790
+ let data = '';
791
+ req.on('data', (chunk) => { data += chunk; });
792
+ req.on('end', () => resolve(data));
793
+ });
794
+ const updateData = JSON.parse(body);
795
+ result = await customDataApi.update(collectionName, entryId, updateData);
796
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
797
+ res.end(JSON.stringify({ success: true, data: result }));
798
+ }
799
+ else if (req.method === 'DELETE') {
800
+ const entryId = pathParts[4];
801
+ result = await customDataApi.delete(collectionName, entryId);
802
+ res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json' });
803
+ res.end(JSON.stringify({ success: true, data: result }));
804
+ }
805
+ }
806
+ catch (error) {
807
+ res.writeHead(HTTP_STATUS.SERVER_ERROR, { 'Content-Type': 'application/json' });
808
+ res.end(JSON.stringify({ success: false, error: error.message || 'API error' }));
809
+ }
810
+ }
@@ -80,9 +80,9 @@ export async function createNewAgent(apiKey, agentType, businessConfig, metadata
80
80
  // Wait for agent to be ready
81
81
  await waitForAgentReady();
82
82
  // Validate required fields from API response
83
- const agentId = createAgentResult.data?.data?.agentId;
84
- const agentName = createAgentResult.data?.data?.name;
85
- const orgId = createAgentResult.data?.data?.org?.id;
83
+ const agentId = createAgentResult.data?.agentId;
84
+ const agentName = createAgentResult.data?.name;
85
+ const orgId = createAgentResult.data?.org?.id;
86
86
  if (!agentId || !agentName || !orgId) {
87
87
  throw new Error("Failed to create agent: Missing required fields in API response");
88
88
  }
@@ -2,6 +2,28 @@
2
2
  * Push Helper Utilities
3
3
  * Helper functions for the push command
4
4
  */
5
+ /**
6
+ * Selected skill for pushing.
7
+ */
8
+ export interface SelectedSkill {
9
+ name: string;
10
+ version: string;
11
+ skillId: string;
12
+ }
13
+ /**
14
+ * Prompts user to select a skill from available skills.
15
+ *
16
+ * @param skills - Array of available skills
17
+ * @returns Selected skill
18
+ */
19
+ export declare function promptSkillSelection(skills: SelectedSkill[]): Promise<SelectedSkill>;
20
+ /**
21
+ * Prompts user to confirm or update the version.
22
+ *
23
+ * @param currentVersion - Current version of the skill
24
+ * @returns Confirmed or updated version
25
+ */
26
+ export declare function promptVersionConfirmOrUpdate(currentVersion: string): Promise<string>;
5
27
  /**
6
28
  * Reads the version from skill configuration.
7
29
  *
@@ -29,10 +51,32 @@ export declare function confirmVersionPush(version: string): Promise<boolean>;
29
51
  */
30
52
  export declare function validatePushConfig(config: any): void;
31
53
  /**
32
- * Validates that deploy data matches configuration.
54
+ * Gets available skills from configuration.
55
+ *
56
+ * @param config - Skill configuration
57
+ * @returns Array of available skills
58
+ */
59
+ export declare function getAvailableSkills(config: any): SelectedSkill[];
60
+ /**
61
+ * Updates a skill's version in the YAML configuration file.
62
+ *
63
+ * @param skillName - Name of the skill to update
64
+ * @param newVersion - New version to set
65
+ */
66
+ export declare function updateSkillVersionInYaml(skillName: string, newVersion: string): void;
67
+ /**
68
+ * Validates that deploy data matches configuration for a specific skill.
33
69
  *
34
70
  * @param deployData - Deploy data from deploy.json
35
- * @param configVersion - Version from configuration
71
+ * @param selectedSkill - The selected skill to validate
36
72
  * @throws Error if deploy data is invalid or version mismatch
37
73
  */
38
- export declare function validateDeployData(deployData: any, configVersion: string): void;
74
+ export declare function validateDeployData(deployData: any, selectedSkill: SelectedSkill): void;
75
+ /**
76
+ * Extracts the deploy data for a specific skill from the deploy.json.
77
+ *
78
+ * @param deployData - Deploy data from deploy.json
79
+ * @param skillName - Name of the skill to extract
80
+ * @returns The deploy data for the specific skill
81
+ */
82
+ export declare function getSkillDeployData(deployData: any, skillName: string): any;
@@ -7,6 +7,72 @@ import path from 'path';
7
7
  import inquirer from 'inquirer';
8
8
  import { readSkillConfig } from './files.js';
9
9
  import { clearPromptLines } from './cli.js';
10
+ /**
11
+ * Prompts user to select a skill from available skills.
12
+ *
13
+ * @param skills - Array of available skills
14
+ * @returns Selected skill
15
+ */
16
+ export async function promptSkillSelection(skills) {
17
+ const skillChoices = skills.map((skill) => ({
18
+ name: `${skill.name} (v${skill.version})`,
19
+ value: skill
20
+ }));
21
+ const { selectedSkill } = await inquirer.prompt([
22
+ {
23
+ type: "list",
24
+ name: "selectedSkill",
25
+ message: "Select a skill to push:",
26
+ choices: skillChoices
27
+ }
28
+ ]);
29
+ clearPromptLines(2);
30
+ return selectedSkill;
31
+ }
32
+ /**
33
+ * Prompts user to confirm or update the version.
34
+ *
35
+ * @param currentVersion - Current version of the skill
36
+ * @returns Confirmed or updated version
37
+ */
38
+ export async function promptVersionConfirmOrUpdate(currentVersion) {
39
+ const { versionAction } = await inquirer.prompt([
40
+ {
41
+ type: "list",
42
+ name: "versionAction",
43
+ message: `Current version is ${currentVersion}. What would you like to do?`,
44
+ choices: [
45
+ { name: `Keep current version (${currentVersion})`, value: "keep" },
46
+ { name: "Update version", value: "update" }
47
+ ]
48
+ }
49
+ ]);
50
+ clearPromptLines(2);
51
+ if (versionAction === "update") {
52
+ const { newVersion } = await inquirer.prompt([
53
+ {
54
+ type: "input",
55
+ name: "newVersion",
56
+ message: "Enter new version:",
57
+ default: currentVersion,
58
+ validate: (input) => {
59
+ if (!input || input.trim() === "") {
60
+ return "Version cannot be empty";
61
+ }
62
+ // Basic semver validation
63
+ const semverRegex = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/;
64
+ if (!semverRegex.test(input.trim())) {
65
+ return "Version must follow semver format (e.g., 1.0.0, 1.0.0-alpha.1)";
66
+ }
67
+ return true;
68
+ }
69
+ }
70
+ ]);
71
+ clearPromptLines(2);
72
+ return newVersion.trim();
73
+ }
74
+ return currentVersion;
75
+ }
10
76
  /**
11
77
  * Reads the version from skill configuration.
12
78
  *
@@ -57,28 +123,114 @@ export function validatePushConfig(config) {
57
123
  if (!config) {
58
124
  throw new Error("No lua.skill.yaml found. Please run this command from a skill directory.");
59
125
  }
60
- if (!config.skill?.version) {
61
- throw new Error("No version found in skill configuration");
62
- }
63
126
  if (!config.agent?.agentId) {
64
127
  throw new Error("Missing agentId in skill configuration");
65
128
  }
66
- if (!config.skill?.skillId) {
67
- throw new Error("Missing skillId in skill configuration");
129
+ // Check for new multi-skill format
130
+ if (config.skills && Array.isArray(config.skills) && config.skills.length > 0) {
131
+ return; // Valid multi-skill config
132
+ }
133
+ // Check for legacy single-skill format
134
+ if (config.skill?.version && config.skill?.skillId) {
135
+ return; // Valid legacy config
68
136
  }
137
+ throw new Error("No skills found in configuration. Please compile your skill first.");
69
138
  }
70
139
  /**
71
- * Validates that deploy data matches configuration.
140
+ * Gets available skills from configuration.
141
+ *
142
+ * @param config - Skill configuration
143
+ * @returns Array of available skills
144
+ */
145
+ export function getAvailableSkills(config) {
146
+ // Check for new multi-skill format
147
+ if (config.skills && Array.isArray(config.skills) && config.skills.length > 0) {
148
+ return config.skills.map((skill) => ({
149
+ name: skill.name,
150
+ version: skill.version,
151
+ skillId: skill.skillId
152
+ }));
153
+ }
154
+ // Fallback to legacy single-skill format
155
+ if (config.skill) {
156
+ return [{
157
+ name: config.skill.name || 'unnamed-skill',
158
+ version: config.skill.version,
159
+ skillId: config.skill.skillId
160
+ }];
161
+ }
162
+ return [];
163
+ }
164
+ /**
165
+ * Updates a skill's version in the YAML configuration file.
166
+ *
167
+ * @param skillName - Name of the skill to update
168
+ * @param newVersion - New version to set
169
+ */
170
+ export function updateSkillVersionInYaml(skillName, newVersion) {
171
+ const yamlPath = path.join(process.cwd(), 'lua.skill.yaml');
172
+ if (!fs.existsSync(yamlPath)) {
173
+ throw new Error("lua.skill.yaml not found");
174
+ }
175
+ const yamlContent = fs.readFileSync(yamlPath, 'utf8');
176
+ // Simple regex-based replacement to preserve formatting
177
+ // Match the skill entry and update its version
178
+ const skillPattern = new RegExp(`(- name: ${skillName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s+version: )[^\\n]+`, 'g');
179
+ const updatedContent = yamlContent.replace(skillPattern, `$1${newVersion}`);
180
+ if (updatedContent === yamlContent) {
181
+ // Try legacy format
182
+ const legacyPattern = /(\s+version: )[^\n]+/;
183
+ const legacyUpdated = yamlContent.replace(legacyPattern, `$1${newVersion}`);
184
+ fs.writeFileSync(yamlPath, legacyUpdated, 'utf8');
185
+ }
186
+ else {
187
+ fs.writeFileSync(yamlPath, updatedContent, 'utf8');
188
+ }
189
+ }
190
+ /**
191
+ * Validates that deploy data matches configuration for a specific skill.
72
192
  *
73
193
  * @param deployData - Deploy data from deploy.json
74
- * @param configVersion - Version from configuration
194
+ * @param selectedSkill - The selected skill to validate
75
195
  * @throws Error if deploy data is invalid or version mismatch
76
196
  */
77
- export function validateDeployData(deployData, configVersion) {
197
+ export function validateDeployData(deployData, selectedSkill) {
78
198
  if (!deployData) {
79
199
  throw new Error("No deploy.json found. Compilation may have failed.");
80
200
  }
81
- if (deployData.version !== configVersion) {
82
- throw new Error(`Version mismatch: config has ${configVersion}, deploy.json has ${deployData.version}`);
201
+ // Handle multi-skill format
202
+ if (deployData.skills && Array.isArray(deployData.skills)) {
203
+ const skillData = deployData.skills.find((s) => s.name === selectedSkill.name);
204
+ if (!skillData) {
205
+ throw new Error(`Skill "${selectedSkill.name}" not found in deploy.json. Compilation may have failed.`);
206
+ }
207
+ if (skillData.version !== selectedSkill.version) {
208
+ throw new Error(`Version mismatch for "${selectedSkill.name}": ` +
209
+ `config has ${selectedSkill.version}, deploy.json has ${skillData.version || 'undefined'}`);
210
+ }
211
+ return;
212
+ }
213
+ // Handle legacy single-skill format
214
+ if (deployData.version !== selectedSkill.version) {
215
+ throw new Error(`Version mismatch: config has ${selectedSkill.version}, deploy.json has ${deployData.version || 'undefined'}`);
216
+ }
217
+ }
218
+ /**
219
+ * Extracts the deploy data for a specific skill from the deploy.json.
220
+ *
221
+ * @param deployData - Deploy data from deploy.json
222
+ * @param skillName - Name of the skill to extract
223
+ * @returns The deploy data for the specific skill
224
+ */
225
+ export function getSkillDeployData(deployData, skillName) {
226
+ // Handle multi-skill format
227
+ if (deployData.skills && Array.isArray(deployData.skills)) {
228
+ const skillData = deployData.skills.find((s) => s.name === skillName);
229
+ if (!skillData) {
230
+ throw new Error(`Skill "${skillName}" not found in deploy.json`);
231
+ }
232
+ return skillData;
83
233
  }
234
+ // Handle legacy single-skill format
235
+ return deployData;
84
236
  }
@@ -131,6 +131,19 @@ export function createSandbox(options) {
131
131
  // };
132
132
  // Create console object (use custom console if provided, otherwise default)
133
133
  const consoleObj = customConsole || console;
134
+ // Create API service instances
135
+ const userService = new UserDataApiService(BASE_URLS.API, apiKey, agentId);
136
+ const productService = new ProductApiService(BASE_URLS.API, apiKey, agentId);
137
+ const dataService = new CustomDataApiService(BASE_URLS.API, apiKey, agentId);
138
+ const basketsService = new BasketApiService(BASE_URLS.API, apiKey, agentId);
139
+ const orderService = new OrderApiService(BASE_URLS.API, apiKey, agentId);
140
+ // Override User service methods if needed
141
+ // Example: Override the update method
142
+ // const originalUpdate = userService.update.bind(userService);
143
+ // userService.update = async (data: any) => {
144
+ // console.log('Custom update logic here');
145
+ // return await originalUpdate(data);
146
+ // };
134
147
  // Create comprehensive polyfills for browser/Node.js APIs
135
148
  const polyfills = {
136
149
  // AbortController polyfill
@@ -257,11 +270,11 @@ export function createSandbox(options) {
257
270
  null: null,
258
271
  Infinity: Infinity,
259
272
  NaN: NaN,
260
- User: new UserDataApiService(BASE_URLS.API, apiKey, agentId),
261
- Product: new ProductApiService(BASE_URLS.API, apiKey, agentId),
262
- CustomData: new CustomDataApiService(BASE_URLS.API, apiKey, agentId),
263
- Basket: new BasketApiService(BASE_URLS.API, apiKey, agentId),
264
- Order: new OrderApiService(BASE_URLS.API, apiKey, agentId),
273
+ User: userService,
274
+ Product: productService,
275
+ Data: dataService,
276
+ Baskets: basketsService,
277
+ Order: orderService,
265
278
  // Environment variables function
266
279
  env: (key) => envVars[key]
267
280
  };