lua-cli 2.5.8 → 3.0.0-alpha.10

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 (121) hide show
  1. package/dist/api/chat.api.service.d.ts +8 -0
  2. package/dist/api/chat.api.service.js +50 -0
  3. package/dist/api/job.api.service.d.ts +219 -0
  4. package/dist/api/job.api.service.js +216 -0
  5. package/dist/api/lazy-instances.d.ts +24 -0
  6. package/dist/api/lazy-instances.js +48 -0
  7. package/dist/api/postprocessor.api.service.d.ts +158 -0
  8. package/dist/api/postprocessor.api.service.js +111 -0
  9. package/dist/api/preprocessor.api.service.d.ts +158 -0
  10. package/dist/api/preprocessor.api.service.js +111 -0
  11. package/dist/api/user.data.api.service.d.ts +13 -0
  12. package/dist/api/user.data.api.service.js +20 -0
  13. package/dist/api/webhook.api.service.d.ts +151 -0
  14. package/dist/api/webhook.api.service.js +134 -0
  15. package/dist/api-exports.d.ts +176 -41
  16. package/dist/api-exports.js +195 -21
  17. package/dist/cli/command-definitions.js +85 -8
  18. package/dist/commands/chat.js +73 -36
  19. package/dist/commands/compile.js +140 -7
  20. package/dist/commands/dev.js +23 -2
  21. package/dist/commands/index.d.ts +4 -0
  22. package/dist/commands/index.js +4 -0
  23. package/dist/commands/init.js +53 -7
  24. package/dist/commands/jobs.d.ts +20 -0
  25. package/dist/commands/jobs.js +533 -0
  26. package/dist/commands/logs.js +2 -5
  27. package/dist/commands/postprocessors.d.ts +8 -0
  28. package/dist/commands/postprocessors.js +431 -0
  29. package/dist/commands/preprocessors.d.ts +8 -0
  30. package/dist/commands/preprocessors.js +431 -0
  31. package/dist/commands/push.d.ts +3 -2
  32. package/dist/commands/push.js +1216 -7
  33. package/dist/commands/test.d.ts +9 -18
  34. package/dist/commands/test.js +574 -82
  35. package/dist/commands/webhooks.d.ts +18 -0
  36. package/dist/commands/webhooks.js +424 -0
  37. package/dist/common/job.instance.d.ts +80 -0
  38. package/dist/common/job.instance.js +116 -0
  39. package/dist/common/user.instance.d.ts +1 -0
  40. package/dist/common/user.instance.js +9 -0
  41. package/dist/config/constants.d.ts +4 -3
  42. package/dist/config/constants.js +10 -8
  43. package/dist/interfaces/agent.d.ts +2 -1
  44. package/dist/interfaces/chat.d.ts +52 -1
  45. package/dist/interfaces/index.d.ts +10 -0
  46. package/dist/interfaces/index.js +7 -0
  47. package/dist/interfaces/jobs.d.ts +193 -0
  48. package/dist/interfaces/jobs.js +5 -0
  49. package/dist/interfaces/postprocessors.d.ts +35 -0
  50. package/dist/interfaces/postprocessors.js +4 -0
  51. package/dist/interfaces/preprocessors.d.ts +35 -0
  52. package/dist/interfaces/preprocessors.js +4 -0
  53. package/dist/interfaces/webhooks.d.ts +104 -0
  54. package/dist/interfaces/webhooks.js +5 -0
  55. package/dist/services/auth.d.ts +8 -2
  56. package/dist/services/auth.js +35 -3
  57. package/dist/types/api-contracts.d.ts +5 -0
  58. package/dist/types/compile.types.d.ts +49 -0
  59. package/dist/types/index.d.ts +1 -1
  60. package/dist/types/index.js +1 -1
  61. package/dist/types/skill.d.ts +521 -0
  62. package/dist/types/skill.js +471 -0
  63. package/dist/utils/agent-management.d.ts +25 -0
  64. package/dist/utils/agent-management.js +67 -0
  65. package/dist/utils/bundling.d.ts +44 -5
  66. package/dist/utils/bundling.js +723 -23
  67. package/dist/utils/compile.d.ts +63 -0
  68. package/dist/utils/compile.js +712 -36
  69. package/dist/utils/deployment.d.ts +2 -1
  70. package/dist/utils/deployment.js +16 -2
  71. package/dist/utils/dev-api.d.ts +42 -2
  72. package/dist/utils/dev-api.js +177 -4
  73. package/dist/utils/dev-server.d.ts +1 -1
  74. package/dist/utils/dev-server.js +4 -4
  75. package/dist/utils/dynamic-job-bundler.d.ts +17 -0
  76. package/dist/utils/dynamic-job-bundler.js +143 -0
  77. package/dist/utils/init-agent.d.ts +3 -1
  78. package/dist/utils/init-agent.js +6 -4
  79. package/dist/utils/init-prompts.d.ts +2 -1
  80. package/dist/utils/init-prompts.js +14 -9
  81. package/dist/utils/job-management.d.ts +24 -0
  82. package/dist/utils/job-management.js +264 -0
  83. package/dist/utils/postprocessor-management.d.ts +9 -0
  84. package/dist/utils/postprocessor-management.js +118 -0
  85. package/dist/utils/pre-bundle-jobs.d.ts +26 -0
  86. package/dist/utils/pre-bundle-jobs.js +176 -0
  87. package/dist/utils/preprocessor-management.d.ts +9 -0
  88. package/dist/utils/preprocessor-management.js +118 -0
  89. package/dist/utils/sandbox-storage.d.ts +48 -0
  90. package/dist/utils/sandbox-storage.js +114 -0
  91. package/dist/utils/sandbox.d.ts +61 -1
  92. package/dist/utils/sandbox.js +299 -72
  93. package/dist/utils/tool-detection.d.ts +3 -2
  94. package/dist/utils/tool-detection.js +18 -4
  95. package/dist/utils/webhook-management.d.ts +24 -0
  96. package/dist/utils/webhook-management.js +256 -0
  97. package/package.json +1 -1
  98. package/template/README.md +30 -2
  99. package/template/env.example +5 -0
  100. package/template/lua.skill.yaml +47 -0
  101. package/template/package-lock.json +10505 -0
  102. package/template/package.json +2 -1
  103. package/template/src/index.ts +103 -2
  104. package/template/src/jobs/AbandonedBasketProcessorJob.ts +139 -0
  105. package/template/src/jobs/DailyCleanupJob.ts +100 -0
  106. package/template/src/jobs/DataMigrationJob.ts +133 -0
  107. package/template/src/jobs/HealthCheckJob.ts +87 -0
  108. package/template/src/tools/CreateInlineJob.ts +42 -0
  109. package/template/src/tools/GameScoreTrackerTool.ts +356 -0
  110. package/template/src/tools/SmartBasketTool.ts +188 -0
  111. package/template/src/webhooks/PaymentWebhook.ts +113 -0
  112. package/template/src/webhooks/UserEventWebhook.ts +77 -0
  113. package/API_REFERENCE.md +0 -1408
  114. package/CHANGELOG.md +0 -236
  115. package/CLI_REFERENCE.md +0 -908
  116. package/GETTING_STARTED.md +0 -1040
  117. package/INSTANCE_TYPES.md +0 -1158
  118. package/README.md +0 -865
  119. package/TEMPLATE_GUIDE.md +0 -1398
  120. package/USER_DATA_INSTANCE.md +0 -621
  121. package/template/TOOL_EXAMPLES.md +0 -655
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Job Management Utilities
3
+ * Handles job creation via API and YAML configuration management
4
+ */
5
+ import fs from "fs";
6
+ import path from "path";
7
+ import yaml from "js-yaml";
8
+ import JobApi from '../api/job.api.service.js';
9
+ import { BASE_URLS } from '../config/constants.js';
10
+ import { loadApiKey } from '../services/auth.js';
11
+ import { COMPILE_FILES, SKILL_DEFAULTS, YAML_FORMAT, } from '../config/compile.constants.js';
12
+ /**
13
+ * Ensures all detected jobs exist in the YAML config with valid job IDs.
14
+ * If a job doesn't exist or has no ID, creates it via the API.
15
+ *
16
+ * @param jobsArray - Array of jobs detected from source code
17
+ * @param config - The skill configuration from lua.skill.yaml
18
+ * @returns Updated jobs array with valid job IDs
19
+ */
20
+ export async function ensureJobsExistInYaml(jobsArray, config) {
21
+ const updatedJobsArray = [];
22
+ let yamlUpdated = false;
23
+ const existingJobs = config?.jobs || [];
24
+ const existingJobsMap = createJobsMap(existingJobs);
25
+ // Process each detected job
26
+ for (const job of jobsArray) {
27
+ const existingJob = existingJobsMap.get(job.name);
28
+ if (existingJob && existingJob.jobId && existingJob.jobId !== '') {
29
+ // Job exists with valid jobId - reuse it
30
+ updatedJobsArray.push({
31
+ ...job,
32
+ jobId: existingJob.jobId
33
+ });
34
+ }
35
+ else {
36
+ // Job doesn't exist or missing jobId - create via API
37
+ const createdJob = await createJobViaApi(job, config, existingJobs, existingJob);
38
+ updatedJobsArray.push(createdJob);
39
+ yamlUpdated = true;
40
+ }
41
+ }
42
+ // Update YAML file if any changes were made
43
+ if (yamlUpdated) {
44
+ await updateYamlWithJobs(config);
45
+ }
46
+ return updatedJobsArray;
47
+ }
48
+ /**
49
+ * Creates a map of existing jobs for quick lookup by name.
50
+ *
51
+ * @param existingJobs - Array of jobs from YAML config
52
+ * @returns Map of job names to job objects
53
+ */
54
+ function createJobsMap(existingJobs) {
55
+ const map = new Map();
56
+ existingJobs.forEach((job) => {
57
+ map.set(job.name, job);
58
+ });
59
+ return map;
60
+ }
61
+ /**
62
+ * Creates a new job via the Lua API and updates the config.
63
+ *
64
+ * @param job - The job to create
65
+ * @param config - The skill configuration
66
+ * @param existingJobs - Array of existing jobs (mutated)
67
+ * @param existingJob - The existing job entry if one exists (may be without ID)
68
+ * @returns The job with its new ID
69
+ */
70
+ async function createJobViaApi(job, config, existingJobs, existingJob) {
71
+ try {
72
+ // Validate prerequisites
73
+ const apiKey = await loadApiKey();
74
+ if (!apiKey) {
75
+ throw new Error("No API key found. Run 'lua auth configure' first.");
76
+ }
77
+ const agentId = config?.agent?.agentId;
78
+ if (!agentId) {
79
+ throw new Error("No agent ID found in lua.skill.yaml. Run 'lua init' first.");
80
+ }
81
+ // Create job via API
82
+ const jobPayload = {
83
+ name: job.name,
84
+ description: job.description || `A Lua job for ${job.name}`,
85
+ context: job.context || '',
86
+ schedule: job.schedule || { type: 'cron', expression: '0 0 * * *' },
87
+ timeout: job.timeout,
88
+ retry: job.retry,
89
+ metadata: job.metadata
90
+ };
91
+ const jobApi = new JobApi(BASE_URLS.API, apiKey, agentId);
92
+ const result = await jobApi.createJob(jobPayload);
93
+ if (result.success && result.data && result.data.id) {
94
+ const newJobId = result.data.id;
95
+ // Update YAML config with new job ID
96
+ if (!existingJob) {
97
+ existingJobs.push({
98
+ name: job.name || '',
99
+ version: job.version || SKILL_DEFAULTS.VERSION,
100
+ jobId: newJobId,
101
+ schedule: job.schedule
102
+ });
103
+ }
104
+ else {
105
+ existingJob.jobId = newJobId;
106
+ existingJob.schedule = job.schedule;
107
+ }
108
+ // Add jobs to config if not already present
109
+ if (!config.jobs) {
110
+ config.jobs = existingJobs;
111
+ }
112
+ return {
113
+ ...job,
114
+ jobId: newJobId
115
+ };
116
+ }
117
+ else {
118
+ console.error(`❌ Failed to create job ${job.name}:`, result.error);
119
+ throw new Error(result.error?.message || 'Failed to create job - no ID returned from API');
120
+ }
121
+ }
122
+ catch (error) {
123
+ console.error(`❌ Failed to create job ${job.name}:`, error);
124
+ throw error;
125
+ }
126
+ }
127
+ /**
128
+ * Updates the lua.skill.yaml file with the current jobs array.
129
+ * Ensures no undefined values are written to the YAML file.
130
+ *
131
+ * @param config - Current skill configuration to merge with
132
+ */
133
+ async function updateYamlWithJobs(config) {
134
+ const jobs = config?.jobs || [];
135
+ // Clean jobs array to ensure no undefined values
136
+ const cleanedJobs = jobs.map(job => ({
137
+ name: job.name || '',
138
+ version: job.version || SKILL_DEFAULTS.VERSION,
139
+ jobId: job.jobId || '',
140
+ schedule: job.schedule || { type: 'cron', expression: '0 0 * * *' }
141
+ }));
142
+ // Update config with cleaned jobs array
143
+ const updatedConfig = {
144
+ ...config,
145
+ jobs: cleanedJobs
146
+ };
147
+ // Write updated YAML with consistent formatting
148
+ const yamlPath = path.join(process.cwd(), COMPILE_FILES.LUA_SKILL_YAML);
149
+ const yamlContent = yaml.dump(updatedConfig, {
150
+ indent: YAML_FORMAT.INDENT,
151
+ lineWidth: YAML_FORMAT.LINE_WIDTH,
152
+ noRefs: YAML_FORMAT.NO_REFS,
153
+ replacer: (key, value) => {
154
+ // Replace undefined values with empty strings
155
+ return value === undefined ? '' : value;
156
+ }
157
+ });
158
+ fs.writeFileSync(yamlPath, yamlContent);
159
+ }
160
+ /**
161
+ * Syncs the server jobs with the YAML configuration.
162
+ * Performs a two-way sync:
163
+ * 1. Deletes/deactivates jobs from server that aren't in YAML
164
+ * 2. Updates YAML with active version numbers from server
165
+ *
166
+ * @param config - The skill configuration from lua.skill.yaml
167
+ * @returns Array of messages about sync operations
168
+ */
169
+ export async function syncServerJobsWithYaml(config) {
170
+ const messages = [];
171
+ let yamlNeedsUpdate = false;
172
+ try {
173
+ // Validate prerequisites
174
+ const apiKey = await loadApiKey();
175
+ if (!apiKey) {
176
+ console.warn("⚠️ No API key found. Skipping server job sync.");
177
+ return messages;
178
+ }
179
+ const agentId = config?.agent?.agentId;
180
+ if (!agentId) {
181
+ console.warn("⚠️ No agent ID found in lua.skill.yaml. Skipping server job sync.");
182
+ return messages;
183
+ }
184
+ // Get jobs from server
185
+ const jobApi = new JobApi(BASE_URLS.API, apiKey, agentId);
186
+ const serverJobsResponse = await jobApi.getJobs();
187
+ if (!serverJobsResponse.success || !serverJobsResponse.data?.jobs) {
188
+ console.warn("⚠️ Could not retrieve server jobs. Skipping server job sync.");
189
+ return messages;
190
+ }
191
+ const serverJobs = serverJobsResponse.data.jobs;
192
+ const yamlJobs = config?.jobs || [];
193
+ // Create maps for efficient lookup
194
+ const yamlJobsMap = new Map(yamlJobs
195
+ .filter((job) => job.jobId)
196
+ .map((job) => [job.jobId, job]));
197
+ const serverJobsMap = new Map(serverJobs.map(job => [job.id, job]));
198
+ // Part 1: Delete jobs from server that aren't in YAML
199
+ const jobsToDelete = serverJobs.filter(serverJob => !yamlJobsMap.has(serverJob.id));
200
+ for (const job of jobsToDelete) {
201
+ try {
202
+ const deleteResponse = await jobApi.deleteJob(job.id);
203
+ if (deleteResponse.success && deleteResponse.data) {
204
+ if (deleteResponse.data.deleted) {
205
+ const msg = `✅ Deleted job "${job.name}" from server`;
206
+ messages.push(msg);
207
+ console.log(msg);
208
+ }
209
+ else if (deleteResponse.data.deactivated) {
210
+ const msg = `⚠️ Job "${job.name}" has versions and cannot be deleted. It has been deactivated instead.`;
211
+ messages.push(msg);
212
+ console.warn(msg);
213
+ }
214
+ }
215
+ else {
216
+ const msg = `❌ Failed to delete job "${job.name}": ${deleteResponse.error?.message || 'Unknown error'}`;
217
+ messages.push(msg);
218
+ console.error(msg);
219
+ }
220
+ }
221
+ catch (error) {
222
+ const msg = `❌ Error deleting job "${job.name}": ${error}`;
223
+ messages.push(msg);
224
+ console.error(msg);
225
+ }
226
+ }
227
+ // Part 2: Sync version numbers from server to YAML
228
+ const updatedYamlJobs = yamlJobs.map((yamlJob) => {
229
+ const serverJob = serverJobsMap.get(yamlJob.jobId);
230
+ if (serverJob && serverJob.versions && serverJob.versions.length > 0) {
231
+ // Find the active version on the server
232
+ const activeVersion = serverJob.versions.find((v) => v.isActive);
233
+ if (activeVersion && activeVersion.version !== yamlJob.version) {
234
+ const msg = `📝 Updated "${yamlJob.name}" job version in YAML: ${yamlJob.version} → ${activeVersion.version}`;
235
+ messages.push(msg);
236
+ console.log(msg);
237
+ yamlNeedsUpdate = true;
238
+ return {
239
+ ...yamlJob,
240
+ version: activeVersion.version,
241
+ schedule: activeVersion.schedule || yamlJob.schedule
242
+ };
243
+ }
244
+ }
245
+ return yamlJob;
246
+ });
247
+ // Update YAML file if versions changed
248
+ if (yamlNeedsUpdate) {
249
+ const updatedConfig = {
250
+ ...config,
251
+ jobs: updatedYamlJobs
252
+ };
253
+ await updateYamlWithJobs(updatedConfig);
254
+ console.log("✅ YAML job versions synced with server");
255
+ }
256
+ if (jobsToDelete.length === 0 && !yamlNeedsUpdate) {
257
+ console.log("✅ Server jobs and YAML are fully in sync");
258
+ }
259
+ }
260
+ catch (error) {
261
+ console.error("❌ Error syncing server jobs:", error);
262
+ }
263
+ return messages;
264
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * PostProcessor Management Utilities
3
+ */
4
+ import { SkillConfig } from '../types/compile.types.js';
5
+ /**
6
+ * Ensures all detected postprocessors exist in the YAML config with valid IDs.
7
+ */
8
+ export declare function ensurePostProcessorsExistInYaml(postprocessorsArray: any[], config: SkillConfig): Promise<any[]>;
9
+ export declare function syncServerPostProcessorsWithYaml(config: SkillConfig): Promise<string[]>;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * PostProcessor Management Utilities
3
+ */
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import yaml from "js-yaml";
7
+ import PostProcessorApi from '../api/postprocessor.api.service.js';
8
+ import { BASE_URLS } from '../config/constants.js';
9
+ import { loadApiKey } from '../services/auth.js';
10
+ import { SKILL_DEFAULTS, YAML_FORMAT } from '../config/compile.constants.js';
11
+ /**
12
+ * Ensures all detected postprocessors exist in the YAML config with valid IDs.
13
+ */
14
+ export async function ensurePostProcessorsExistInYaml(postprocessorsArray, config) {
15
+ const updatedPostProcessorsArray = [];
16
+ let yamlUpdated = false;
17
+ const existingPostProcessors = config?.postprocessors || [];
18
+ const existingMap = new Map(existingPostProcessors.map((p) => [p.name, p]));
19
+ for (const postprocessor of postprocessorsArray) {
20
+ const existing = existingMap.get(postprocessor.name);
21
+ if (existing && existing.postprocessorId) {
22
+ updatedPostProcessorsArray.push({
23
+ ...postprocessor,
24
+ postprocessorId: existing.postprocessorId
25
+ });
26
+ }
27
+ else {
28
+ const created = await createPostProcessorViaApi(postprocessor, config, existingPostProcessors, existing);
29
+ updatedPostProcessorsArray.push(created);
30
+ yamlUpdated = true;
31
+ }
32
+ }
33
+ if (yamlUpdated) {
34
+ await updateYamlWithPostProcessors(config);
35
+ }
36
+ return updatedPostProcessorsArray;
37
+ }
38
+ async function createPostProcessorViaApi(postprocessor, config, existingPostProcessors, existingPostProcessor) {
39
+ try {
40
+ const apiKey = await loadApiKey();
41
+ if (!apiKey)
42
+ throw new Error("No API key found. Run 'lua auth configure' first.");
43
+ const agentId = config?.agent?.agentId;
44
+ if (!agentId)
45
+ throw new Error("No agent ID found in lua.skill.yaml. Run 'lua init' first.");
46
+ const payload = {
47
+ name: postprocessor.name,
48
+ description: postprocessor.description || `A Lua postprocessor for ${postprocessor.name}`,
49
+ context: postprocessor.context || ''
50
+ };
51
+ const api = new PostProcessorApi(BASE_URLS.API, apiKey, agentId);
52
+ const result = await api.createPostProcessor(payload);
53
+ if (result.success && result.data && result.data.id) {
54
+ const newId = result.data.id;
55
+ if (!existingPostProcessor) {
56
+ existingPostProcessors.push({
57
+ name: postprocessor.name || '',
58
+ version: postprocessor.version || SKILL_DEFAULTS.VERSION,
59
+ postprocessorId: newId
60
+ });
61
+ }
62
+ else {
63
+ existingPostProcessor.postprocessorId = newId;
64
+ }
65
+ if (!config.postprocessors) {
66
+ config.postprocessors = existingPostProcessors;
67
+ }
68
+ return {
69
+ ...postprocessor,
70
+ postprocessorId: newId
71
+ };
72
+ }
73
+ else {
74
+ throw new Error(result.error?.message || "Failed to create postprocessor");
75
+ }
76
+ }
77
+ catch (error) {
78
+ console.warn(`Warning: Could not create postprocessor ${postprocessor.name}:`, error);
79
+ return postprocessor;
80
+ }
81
+ }
82
+ async function updateYamlWithPostProcessors(config) {
83
+ const yamlPath = path.join(process.cwd(), 'lua.skill.yaml');
84
+ const yamlContent = yaml.dump(config, { ...YAML_FORMAT, sortKeys: (a, b) => {
85
+ const order = ['agent', 'skills', 'webhooks', 'jobs', 'preprocessors', 'postprocessors', 'skill'];
86
+ const aIndex = order.indexOf(a);
87
+ const bIndex = order.indexOf(b);
88
+ if (aIndex !== -1 && bIndex !== -1)
89
+ return aIndex - bIndex;
90
+ if (aIndex !== -1)
91
+ return -1;
92
+ if (bIndex !== -1)
93
+ return 1;
94
+ return a.localeCompare(b);
95
+ } });
96
+ fs.writeFileSync(yamlPath, yamlContent);
97
+ }
98
+ export async function syncServerPostProcessorsWithYaml(config) {
99
+ const messages = [];
100
+ try {
101
+ const apiKey = await loadApiKey();
102
+ if (!apiKey) {
103
+ console.warn("⚠️ No API key found. Skipping server postprocessor sync.");
104
+ return messages;
105
+ }
106
+ const agentId = config?.agent?.agentId;
107
+ if (!agentId) {
108
+ console.warn("⚠️ No agent ID found. Skipping server postprocessor sync.");
109
+ return messages;
110
+ }
111
+ // Server sync will be implemented when backend is ready
112
+ return messages;
113
+ }
114
+ catch (error) {
115
+ console.warn("⚠️ Error syncing postprocessors:", error);
116
+ return messages;
117
+ }
118
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Pre-Bundle Jobs - Extract and bundle Jobs.create() before tool bundling
3
+ *
4
+ * Flow:
5
+ * 1. Detect Jobs.create() in source
6
+ * 2. Extract execute function
7
+ * 3. Bundle execute with ALL dependencies
8
+ * 4. Replace execute with placeholder
9
+ * 5. Bundle tool/webhook/job
10
+ * 6. Replace placeholder with bundled job code
11
+ * 7. Compress
12
+ */
13
+ import { Project } from 'ts-morph';
14
+ /**
15
+ * Pre-processes source file to extract and bundle Jobs.create() execute functions
16
+ * Returns modified source code with placeholders
17
+ */
18
+ export declare function preBundleJobsInSource(sourceFilePath: string, project: Project, distDir: string): Promise<{
19
+ modifiedSource: string;
20
+ jobBundles: Map<string, string>;
21
+ }>;
22
+ /**
23
+ * Replaces placeholders in bundled code with actual job bundles
24
+ * The bundled job code is NOT compressed - it's JavaScript that will be part of the tool
25
+ */
26
+ export declare function replaceJobPlaceholders(bundledCode: string, jobBundles: Map<string, string>): string;
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Pre-Bundle Jobs - Extract and bundle Jobs.create() before tool bundling
3
+ *
4
+ * Flow:
5
+ * 1. Detect Jobs.create() in source
6
+ * 2. Extract execute function
7
+ * 3. Bundle execute with ALL dependencies
8
+ * 4. Replace execute with placeholder
9
+ * 5. Bundle tool/webhook/job
10
+ * 6. Replace placeholder with bundled job code
11
+ * 7. Compress
12
+ */
13
+ import { Node } from 'ts-morph';
14
+ import { build } from 'esbuild';
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+ import { sandboxGlobalsPlugin } from './bundling.js';
18
+ const JOB_PLACEHOLDER = '__BUNDLED_JOB_EXECUTE__';
19
+ /**
20
+ * Pre-processes source file to extract and bundle Jobs.create() execute functions
21
+ * Returns modified source code with placeholders
22
+ */
23
+ export async function preBundleJobsInSource(sourceFilePath, project, distDir) {
24
+ const sourceFile = project.getSourceFile(sourceFilePath);
25
+ if (!sourceFile) {
26
+ return { modifiedSource: fs.readFileSync(sourceFilePath, 'utf8'), jobBundles: new Map() };
27
+ }
28
+ const jobBundles = new Map();
29
+ const bundlingPromises = [];
30
+ let jobIndex = 0;
31
+ let modifiedSource = sourceFile.getFullText();
32
+ // Find all Jobs.create() calls
33
+ sourceFile.forEachDescendant((node) => {
34
+ if (Node.isCallExpression(node)) {
35
+ const expression = node.getExpression();
36
+ // Check if this is Jobs.create()
37
+ if (Node.isPropertyAccessExpression(expression)) {
38
+ const object = expression.getExpression();
39
+ const property = expression.getName();
40
+ if (object.getText() === 'Jobs' && property === 'create') {
41
+ jobIndex++;
42
+ const placeholder = `${JOB_PLACEHOLDER}_${jobIndex}`;
43
+ console.log(`[PreBundleJobs] Found Jobs.create() #${jobIndex}`);
44
+ // Get the config object argument
45
+ const args = node.getArguments();
46
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
47
+ const configObj = args[0];
48
+ // Find the execute property
49
+ const executeProperty = configObj.getProperty('execute');
50
+ if (executeProperty && Node.isPropertyAssignment(executeProperty)) {
51
+ const executeInitializer = executeProperty.getInitializer();
52
+ if (executeInitializer) {
53
+ const executeCode = executeInitializer.getText();
54
+ console.log(`[PreBundleJobs] Extracting execute function for bundling...`);
55
+ // Bundle the execute function with dependencies (async)
56
+ const bundlePromise = bundleJobExecuteFunction(executeCode, `job_${jobIndex}`, distDir, sourceFilePath).then(bundledCode => {
57
+ if (bundledCode) {
58
+ jobBundles.set(placeholder, bundledCode);
59
+ console.log(`[PreBundleJobs] ✅ Bundled job #${jobIndex} with dependencies`);
60
+ }
61
+ });
62
+ bundlingPromises.push(bundlePromise);
63
+ // Replace execute function with a function that references the bundled code
64
+ // Don't use string placeholder - use a reference
65
+ const originalText = executeProperty.getText();
66
+ const replacementText = `execute: ${placeholder}`;
67
+ modifiedSource = modifiedSource.replace(originalText, replacementText);
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ });
75
+ // Wait for all bundling to complete
76
+ if (bundlingPromises.length > 0) {
77
+ console.log(`[PreBundleJobs] Waiting for ${bundlingPromises.length} job(s) to bundle...`);
78
+ await Promise.all(bundlingPromises);
79
+ console.log(`[PreBundleJobs] ✅ All job bundles complete`);
80
+ }
81
+ return { modifiedSource, jobBundles };
82
+ }
83
+ /**
84
+ * Bundles a job execute function with all its dependencies
85
+ */
86
+ async function bundleJobExecuteFunction(executeCode, jobName, distDir, sourceFilePath) {
87
+ const tempDir = path.join(distDir, '.temp');
88
+ if (!fs.existsSync(tempDir)) {
89
+ fs.mkdirSync(tempDir, { recursive: true });
90
+ }
91
+ const tempOutput = path.join(tempDir, `${jobName}-bundled.js`);
92
+ try {
93
+ // Read the original source file to get its imports
94
+ const sourceContent = fs.readFileSync(sourceFilePath, 'utf8');
95
+ // Extract all import statements from the source
96
+ const importPattern = /^import\s+.+\s+from\s+['"][^'"]+['"];?$/gm;
97
+ const imports = sourceContent.match(importPattern) || [];
98
+ // Filter to only imports that might be used (exclude lua-cli internal classes and APIs)
99
+ const relevantImports = imports.filter(imp => !imp.includes('LuaTool') &&
100
+ !imp.includes('LuaWebhook') &&
101
+ !imp.includes('LuaJob') &&
102
+ !imp.includes('lua-cli') && // Exclude all lua-cli imports (they're available in sandbox)
103
+ !imp.includes('api-exports') && // Exclude api-exports
104
+ !imp.includes('../../../dist/api-exports') // Exclude direct api-exports imports
105
+ );
106
+ console.log(`[PreBundleJobs] Including ${relevantImports.length} import(s) in job bundle`);
107
+ // Create a TypeScript module with the execute function and all imports
108
+ const moduleCode = `
109
+ // Auto-generated job execute bundle
110
+ ${relevantImports.join('\n')}
111
+
112
+ // The execute function with all dependencies available
113
+ const executeFunc = ${executeCode};
114
+
115
+ export default executeFunc;
116
+ `;
117
+ // Bundle with esbuild using stdin to preserve import resolution
118
+ await build({
119
+ stdin: {
120
+ contents: moduleCode,
121
+ resolveDir: path.dirname(sourceFilePath),
122
+ sourcefile: sourceFilePath,
123
+ loader: 'ts',
124
+ },
125
+ bundle: true,
126
+ platform: 'node',
127
+ target: 'node16',
128
+ format: 'cjs',
129
+ minify: true,
130
+ outfile: tempOutput,
131
+ external: [],
132
+ plugins: [sandboxGlobalsPlugin],
133
+ });
134
+ // Read bundled code
135
+ const bundledCode = fs.readFileSync(tempOutput, 'utf8');
136
+ console.log(`[PreBundleJobs] Bundle size: ${(bundledCode.length / 1024).toFixed(2)}KB`);
137
+ // DON'T compress - just wrap for VM execution
138
+ // Compression happens later when the entire tool is bundled
139
+ const wrappedCode = `(async (job) => {
140
+ ${bundledCode}
141
+ const executeFunction = module.exports.default || module.exports;
142
+ return await executeFunction(job);
143
+ })`;
144
+ // Cleanup temp files
145
+ try {
146
+ fs.unlinkSync(tempOutput);
147
+ }
148
+ catch { }
149
+ // Return uncompressed bundled code
150
+ return wrappedCode;
151
+ }
152
+ catch (error) {
153
+ console.warn(`[PreBundleJobs] Error bundling job ${jobName}:`, error);
154
+ try {
155
+ if (fs.existsSync(tempOutput))
156
+ fs.unlinkSync(tempOutput);
157
+ }
158
+ catch { }
159
+ return '';
160
+ }
161
+ }
162
+ /**
163
+ * Replaces placeholders in bundled code with actual job bundles
164
+ * The bundled job code is NOT compressed - it's JavaScript that will be part of the tool
165
+ */
166
+ export function replaceJobPlaceholders(bundledCode, jobBundles) {
167
+ let result = bundledCode;
168
+ jobBundles.forEach((bundledJobCode, placeholder) => {
169
+ // The placeholder is used as an identifier (not a string), just replace it directly
170
+ // It appears as: execute: __BUNDLED_JOB_EXECUTE___1
171
+ const placeholderPattern = new RegExp(placeholder, 'g');
172
+ result = result.replace(placeholderPattern, bundledJobCode);
173
+ console.log(`[PreBundleJobs] ✅ Replaced ${placeholder} with bundled code (${(bundledJobCode.length / 1024).toFixed(1)}KB)`);
174
+ });
175
+ return result;
176
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * PreProcessor Management Utilities
3
+ */
4
+ import { SkillConfig } from '../types/compile.types.js';
5
+ /**
6
+ * Ensures all detected preprocessors exist in the YAML config with valid IDs.
7
+ */
8
+ export declare function ensurePreProcessorsExistInYaml(preprocessorsArray: any[], config: SkillConfig): Promise<any[]>;
9
+ export declare function syncServerPreProcessorsWithYaml(config: SkillConfig): Promise<string[]>;