busy-cli 0.1.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.
- package/README.md +129 -0
- package/dist/builders/context.d.ts +50 -0
- package/dist/builders/context.d.ts.map +1 -0
- package/dist/builders/context.js +190 -0
- package/dist/cache/index.d.ts +100 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +270 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +463 -0
- package/dist/commands/package.d.ts +96 -0
- package/dist/commands/package.d.ts.map +1 -0
- package/dist/commands/package.js +285 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/loader.d.ts +6 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +361 -0
- package/dist/merge.d.ts +16 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +102 -0
- package/dist/package/manifest.d.ts +59 -0
- package/dist/package/manifest.d.ts.map +1 -0
- package/dist/package/manifest.js +265 -0
- package/dist/parser.d.ts +28 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +220 -0
- package/dist/parsers/frontmatter.d.ts +14 -0
- package/dist/parsers/frontmatter.d.ts.map +1 -0
- package/dist/parsers/frontmatter.js +110 -0
- package/dist/parsers/imports.d.ts +48 -0
- package/dist/parsers/imports.d.ts.map +1 -0
- package/dist/parsers/imports.js +147 -0
- package/dist/parsers/links.d.ts +12 -0
- package/dist/parsers/links.d.ts.map +1 -0
- package/dist/parsers/links.js +79 -0
- package/dist/parsers/localdefs.d.ts +6 -0
- package/dist/parsers/localdefs.d.ts.map +1 -0
- package/dist/parsers/localdefs.js +132 -0
- package/dist/parsers/operations.d.ts +32 -0
- package/dist/parsers/operations.d.ts.map +1 -0
- package/dist/parsers/operations.js +313 -0
- package/dist/parsers/sections.d.ts +15 -0
- package/dist/parsers/sections.d.ts.map +1 -0
- package/dist/parsers/sections.js +173 -0
- package/dist/parsers/tools.d.ts +30 -0
- package/dist/parsers/tools.d.ts.map +1 -0
- package/dist/parsers/tools.js +178 -0
- package/dist/parsers/triggers.d.ts +35 -0
- package/dist/parsers/triggers.d.ts.map +1 -0
- package/dist/parsers/triggers.js +219 -0
- package/dist/providers/base.d.ts +60 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +34 -0
- package/dist/providers/github.d.ts +18 -0
- package/dist/providers/github.d.ts.map +1 -0
- package/dist/providers/github.js +109 -0
- package/dist/providers/gitlab.d.ts +18 -0
- package/dist/providers/gitlab.d.ts.map +1 -0
- package/dist/providers/gitlab.js +101 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +17 -0
- package/dist/providers/local.d.ts +31 -0
- package/dist/providers/local.d.ts.map +1 -0
- package/dist/providers/local.js +116 -0
- package/dist/providers/url.d.ts +16 -0
- package/dist/providers/url.d.ts.map +1 -0
- package/dist/providers/url.js +45 -0
- package/dist/registry/index.d.ts +99 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +320 -0
- package/dist/types/schema.d.ts +3259 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +258 -0
- package/dist/utils/logger.d.ts +19 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +23 -0
- package/dist/utils/slugify.d.ts +14 -0
- package/dist/utils/slugify.d.ts.map +1 -0
- package/dist/utils/slugify.js +28 -0
- package/package.json +61 -0
- package/src/__tests__/cache.test.ts +393 -0
- package/src/__tests__/cli-package.test.ts +667 -0
- package/src/__tests__/fixtures/automated-workflow.busy.md +84 -0
- package/src/__tests__/fixtures/concept.busy.md +30 -0
- package/src/__tests__/fixtures/document.busy.md +44 -0
- package/src/__tests__/fixtures/simple-operation.busy.md +45 -0
- package/src/__tests__/fixtures/tool-document.busy.md +71 -0
- package/src/__tests__/fixtures/tool.busy.md +54 -0
- package/src/__tests__/imports.test.ts +244 -0
- package/src/__tests__/integration.test.ts +432 -0
- package/src/__tests__/operations.test.ts +408 -0
- package/src/__tests__/package-manifest.test.ts +455 -0
- package/src/__tests__/providers.test.ts +672 -0
- package/src/__tests__/registry.test.ts +402 -0
- package/src/__tests__/schema.test.ts +467 -0
- package/src/__tests__/tools.test.ts +376 -0
- package/src/__tests__/triggers.test.ts +312 -0
- package/src/builders/context.ts +294 -0
- package/src/cache/index.ts +312 -0
- package/src/cli/index.ts +514 -0
- package/src/commands/package.ts +392 -0
- package/src/index.ts +46 -0
- package/src/loader.ts +474 -0
- package/src/merge.ts +126 -0
- package/src/package/manifest.ts +349 -0
- package/src/parser.ts +278 -0
- package/src/parsers/frontmatter.ts +135 -0
- package/src/parsers/imports.ts +196 -0
- package/src/parsers/links.ts +108 -0
- package/src/parsers/localdefs.ts +166 -0
- package/src/parsers/operations.ts +404 -0
- package/src/parsers/sections.ts +230 -0
- package/src/parsers/tools.ts +215 -0
- package/src/parsers/triggers.ts +252 -0
- package/src/providers/base.ts +77 -0
- package/src/providers/github.ts +129 -0
- package/src/providers/gitlab.ts +121 -0
- package/src/providers/index.ts +25 -0
- package/src/providers/local.ts +129 -0
- package/src/providers/url.ts +56 -0
- package/src/registry/index.ts +408 -0
- package/src/types/schema.ts +369 -0
- package/src/utils/logger.ts +25 -0
- package/src/utils/slugify.ts +31 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Parser - Matches busy-python Tool and ToolDocument models
|
|
3
|
+
*
|
|
4
|
+
* Tools have:
|
|
5
|
+
* - name: string
|
|
6
|
+
* - description: string
|
|
7
|
+
* - inputs: list[str]
|
|
8
|
+
* - outputs: list[str]
|
|
9
|
+
* - examples: Optional[list[str]]
|
|
10
|
+
* - providers: Optional[dict[str, dict[str, Any]]] (provider_name -> {action, parameters})
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Parse provider mappings from tool content
|
|
14
|
+
*
|
|
15
|
+
* @param content - Content of the tool section
|
|
16
|
+
* @returns Record of provider name to {action, parameters}
|
|
17
|
+
*/
|
|
18
|
+
export function parseToolProviders(content) {
|
|
19
|
+
const providers = {};
|
|
20
|
+
// Find Providers section - get everything after ### Providers or ### [Providers]
|
|
21
|
+
// Or if content starts with #### (direct provider definitions)
|
|
22
|
+
let providersContent;
|
|
23
|
+
const providersMatch = content.match(/###\s*\[?Providers\]?\s*\n([\s\S]*)$/i);
|
|
24
|
+
if (providersMatch) {
|
|
25
|
+
providersContent = providersMatch[1];
|
|
26
|
+
}
|
|
27
|
+
else if (content.match(/^####\s+/m)) {
|
|
28
|
+
// Content starts with provider definitions directly
|
|
29
|
+
providersContent = content;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return providers;
|
|
33
|
+
}
|
|
34
|
+
// Trim at next ### that's not #### (to stop at next H3 section)
|
|
35
|
+
const nextSectionMatch = providersContent.match(/\n###\s+[^\#]/);
|
|
36
|
+
if (nextSectionMatch) {
|
|
37
|
+
providersContent = providersContent.slice(0, nextSectionMatch.index);
|
|
38
|
+
}
|
|
39
|
+
// Split by #### headings (provider names)
|
|
40
|
+
const parts = providersContent.split(/(?=####\s+)/);
|
|
41
|
+
for (const part of parts) {
|
|
42
|
+
if (!part.trim())
|
|
43
|
+
continue;
|
|
44
|
+
// Match provider heading: #### providerName
|
|
45
|
+
const providerMatch = part.match(/^####\s+(\w+)\s*\n?([\s\S]*)/);
|
|
46
|
+
if (providerMatch) {
|
|
47
|
+
const providerName = providerMatch[1];
|
|
48
|
+
const providerContent = providerMatch[2] || '';
|
|
49
|
+
// Extract Action
|
|
50
|
+
const actionMatch = providerContent.match(/Action:\s*(.+)/i);
|
|
51
|
+
const action = actionMatch ? actionMatch[1].trim() : '';
|
|
52
|
+
// Extract Parameters
|
|
53
|
+
const paramsMatch = providerContent.match(/Parameters:\s*\n([\s\S]*?)(?=####|Action:|$)/i);
|
|
54
|
+
let parameters;
|
|
55
|
+
if (paramsMatch) {
|
|
56
|
+
parameters = parseYamlLikeParams(paramsMatch[1]);
|
|
57
|
+
}
|
|
58
|
+
if (action) {
|
|
59
|
+
providers[providerName] = {
|
|
60
|
+
action,
|
|
61
|
+
parameters: parameters && Object.keys(parameters).length > 0 ? parameters : undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return providers;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parse YAML-like indented parameters
|
|
70
|
+
*/
|
|
71
|
+
function parseYamlLikeParams(content) {
|
|
72
|
+
const params = {};
|
|
73
|
+
const lines = content.split('\n');
|
|
74
|
+
let currentKey = '';
|
|
75
|
+
let currentNested = null;
|
|
76
|
+
for (const line of lines) {
|
|
77
|
+
const trimmed = line.trim();
|
|
78
|
+
if (!trimmed)
|
|
79
|
+
continue;
|
|
80
|
+
// Check indentation level
|
|
81
|
+
const leadingSpaces = line.match(/^(\s*)/)?.[1].length || 0;
|
|
82
|
+
// Match key: value pair
|
|
83
|
+
const kvMatch = trimmed.match(/^(\w+):\s*(.*)$/);
|
|
84
|
+
if (kvMatch) {
|
|
85
|
+
const key = kvMatch[1];
|
|
86
|
+
const value = kvMatch[2].trim();
|
|
87
|
+
if (leadingSpaces <= 2) {
|
|
88
|
+
// Top-level key
|
|
89
|
+
if (value) {
|
|
90
|
+
params[key] = value;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// Nested object
|
|
94
|
+
currentKey = key;
|
|
95
|
+
currentNested = {};
|
|
96
|
+
params[key] = currentNested;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (currentNested && currentKey) {
|
|
100
|
+
// Nested key
|
|
101
|
+
currentNested[key] = value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return params;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Parse bullet list items from a section
|
|
109
|
+
*/
|
|
110
|
+
function parseBulletList(content, sectionName) {
|
|
111
|
+
const pattern = new RegExp(`###\\s*\\[?${sectionName}\\]?\\s*\\n([\\s\\S]*?)(?=\\n###|\\n##|$)`, 'i');
|
|
112
|
+
const match = content.match(pattern);
|
|
113
|
+
if (!match) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
const items = [];
|
|
117
|
+
const lines = match[1].split('\n');
|
|
118
|
+
for (const line of lines) {
|
|
119
|
+
const trimmed = line.trim();
|
|
120
|
+
const bulletMatch = trimmed.match(/^[-*]\s+(.+)$/);
|
|
121
|
+
if (bulletMatch) {
|
|
122
|
+
items.push(bulletMatch[1].trim());
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return items;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Parse tools from markdown content
|
|
129
|
+
*
|
|
130
|
+
* @param content - Full markdown document content
|
|
131
|
+
* @returns Array of Tool objects
|
|
132
|
+
*/
|
|
133
|
+
export function parseTools(content) {
|
|
134
|
+
const tools = [];
|
|
135
|
+
// Find Tools section - handle both with and without brackets
|
|
136
|
+
const toolsMatch = content.match(/^#\s*\[?Tools\]?\s*$/im);
|
|
137
|
+
if (!toolsMatch) {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
// Get content after Tools heading until next top-level section or end
|
|
141
|
+
const startIndex = toolsMatch.index + toolsMatch[0].length;
|
|
142
|
+
const restContent = content.slice(startIndex);
|
|
143
|
+
// Find next top-level heading (# not ##)
|
|
144
|
+
const nextH1Match = restContent.match(/\n#\s+[^\#]/);
|
|
145
|
+
const toolsContent = nextH1Match
|
|
146
|
+
? restContent.slice(0, nextH1Match.index)
|
|
147
|
+
: restContent;
|
|
148
|
+
// Split by ## headings to find individual tools
|
|
149
|
+
const parts = toolsContent.split(/\n(?=##\s+)/);
|
|
150
|
+
for (const part of parts) {
|
|
151
|
+
if (!part.trim())
|
|
152
|
+
continue;
|
|
153
|
+
// Match tool heading: ## tool_name
|
|
154
|
+
const headingMatch = part.match(/^##\s+(\S+)\s*\n([\s\S]*)/);
|
|
155
|
+
if (headingMatch) {
|
|
156
|
+
const name = headingMatch[1].trim();
|
|
157
|
+
const toolContent = headingMatch[2] || '';
|
|
158
|
+
// Extract description (first paragraph before any ### section)
|
|
159
|
+
const descMatch = toolContent.match(/^([^#\n][\s\S]*?)(?=\n###|$)/);
|
|
160
|
+
const description = descMatch ? descMatch[1].trim() : '';
|
|
161
|
+
// Parse sections
|
|
162
|
+
const inputs = parseBulletList(toolContent, 'Inputs');
|
|
163
|
+
const outputs = parseBulletList(toolContent, 'Outputs');
|
|
164
|
+
const examples = parseBulletList(toolContent, 'Examples');
|
|
165
|
+
const providers = parseToolProviders(toolContent);
|
|
166
|
+
tools.push({
|
|
167
|
+
name,
|
|
168
|
+
description,
|
|
169
|
+
inputs,
|
|
170
|
+
outputs,
|
|
171
|
+
examples: examples.length > 0 ? examples : undefined,
|
|
172
|
+
providers: Object.keys(providers).length > 0 ? providers : undefined,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return tools;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger Parser - Matches busy-python trigger parsing
|
|
3
|
+
*
|
|
4
|
+
* Supports two trigger formats:
|
|
5
|
+
* 1. Time-based (alarm): "Set alarm for <time> to run <Operation>"
|
|
6
|
+
* 2. Event-based: "When <event> [from <filter>], run <Operation>"
|
|
7
|
+
*
|
|
8
|
+
* Triggers can appear in:
|
|
9
|
+
* - # [Triggers] section as bullet points
|
|
10
|
+
* - Frontmatter as a Triggers array
|
|
11
|
+
*/
|
|
12
|
+
import { Trigger } from '../types/schema.js';
|
|
13
|
+
/**
|
|
14
|
+
* Parse a time specification into a cron expression
|
|
15
|
+
*
|
|
16
|
+
* @param timeSpec - Time string like "6am", "3pm each day", "9am on Monday"
|
|
17
|
+
* @returns Cron expression string
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseTimeSpec(timeSpec: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Parse a trigger declaration text into a Trigger object
|
|
22
|
+
*
|
|
23
|
+
* @param text - The trigger declaration text
|
|
24
|
+
* @returns Trigger object
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseTriggerDeclaration(text: string): Trigger;
|
|
27
|
+
/**
|
|
28
|
+
* Parse triggers from markdown content
|
|
29
|
+
* Combines triggers from both Triggers section and frontmatter
|
|
30
|
+
*
|
|
31
|
+
* @param content - Full markdown document content
|
|
32
|
+
* @returns Array of Trigger objects
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseTriggers(content: string): Trigger[];
|
|
35
|
+
//# sourceMappingURL=triggers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triggers.d.ts","sourceRoot":"","sources":["../../src/parsers/triggers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAqB7C;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAuBtD;AAqCD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAmD7D;AAyCD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CA+CxD"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger Parser - Matches busy-python trigger parsing
|
|
3
|
+
*
|
|
4
|
+
* Supports two trigger formats:
|
|
5
|
+
* 1. Time-based (alarm): "Set alarm for <time> to run <Operation>"
|
|
6
|
+
* 2. Event-based: "When <event> [from <filter>], run <Operation>"
|
|
7
|
+
*
|
|
8
|
+
* Triggers can appear in:
|
|
9
|
+
* - # [Triggers] section as bullet points
|
|
10
|
+
* - Frontmatter as a Triggers array
|
|
11
|
+
*/
|
|
12
|
+
import matter from 'gray-matter';
|
|
13
|
+
// Weekday mapping
|
|
14
|
+
const WEEKDAYS = {
|
|
15
|
+
sunday: 0,
|
|
16
|
+
monday: 1,
|
|
17
|
+
tuesday: 2,
|
|
18
|
+
wednesday: 3,
|
|
19
|
+
thursday: 4,
|
|
20
|
+
friday: 5,
|
|
21
|
+
saturday: 6,
|
|
22
|
+
sun: 0,
|
|
23
|
+
mon: 1,
|
|
24
|
+
tue: 2,
|
|
25
|
+
wed: 3,
|
|
26
|
+
thu: 4,
|
|
27
|
+
fri: 5,
|
|
28
|
+
sat: 6,
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Parse a time specification into a cron expression
|
|
32
|
+
*
|
|
33
|
+
* @param timeSpec - Time string like "6am", "3pm each day", "9am on Monday"
|
|
34
|
+
* @returns Cron expression string
|
|
35
|
+
*/
|
|
36
|
+
export function parseTimeSpec(timeSpec) {
|
|
37
|
+
const spec = timeSpec.toLowerCase().trim();
|
|
38
|
+
// Extract hour and AM/PM
|
|
39
|
+
const timeMatch = spec.match(/(\d{1,2})\s*(am|pm)/i);
|
|
40
|
+
if (!timeMatch) {
|
|
41
|
+
return '* * * * *'; // Default: every minute (invalid spec)
|
|
42
|
+
}
|
|
43
|
+
let hour = parseInt(timeMatch[1], 10);
|
|
44
|
+
const isPM = timeMatch[2].toLowerCase() === 'pm';
|
|
45
|
+
// Convert to 24-hour format
|
|
46
|
+
if (isPM && hour !== 12) {
|
|
47
|
+
hour += 12;
|
|
48
|
+
}
|
|
49
|
+
else if (!isPM && hour === 12) {
|
|
50
|
+
hour = 0;
|
|
51
|
+
}
|
|
52
|
+
// Check for day specifications
|
|
53
|
+
const dayOfWeek = parseDayOfWeek(spec);
|
|
54
|
+
return `0 ${hour} * * ${dayOfWeek}`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse day of week specification from time string
|
|
58
|
+
*/
|
|
59
|
+
function parseDayOfWeek(spec) {
|
|
60
|
+
// Check for "weekdays"
|
|
61
|
+
if (spec.includes('weekdays')) {
|
|
62
|
+
return '1-5';
|
|
63
|
+
}
|
|
64
|
+
// Check for "weekends"
|
|
65
|
+
if (spec.includes('weekends')) {
|
|
66
|
+
return '0,6';
|
|
67
|
+
}
|
|
68
|
+
// Check for specific days
|
|
69
|
+
const days = [];
|
|
70
|
+
for (const [dayName, dayNum] of Object.entries(WEEKDAYS)) {
|
|
71
|
+
// Match full day name or abbreviation
|
|
72
|
+
const pattern = new RegExp(`\\b${dayName}\\b`, 'i');
|
|
73
|
+
if (pattern.test(spec)) {
|
|
74
|
+
if (!days.includes(dayNum)) {
|
|
75
|
+
days.push(dayNum);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (days.length > 0) {
|
|
80
|
+
return days.sort((a, b) => a - b).join(',');
|
|
81
|
+
}
|
|
82
|
+
// Default: every day
|
|
83
|
+
return '*';
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Parse a trigger declaration text into a Trigger object
|
|
87
|
+
*
|
|
88
|
+
* @param text - The trigger declaration text
|
|
89
|
+
* @returns Trigger object
|
|
90
|
+
*/
|
|
91
|
+
export function parseTriggerDeclaration(text) {
|
|
92
|
+
const rawText = text.trim();
|
|
93
|
+
// Try to match alarm pattern: "Set alarm for <time> to run <Operation>"
|
|
94
|
+
const alarmMatch = rawText.match(/set\s+alarm\s+for\s+(.+?)\s+to\s+run\s+(\w+)/i);
|
|
95
|
+
if (alarmMatch) {
|
|
96
|
+
const timeSpec = alarmMatch[1];
|
|
97
|
+
const operation = alarmMatch[2];
|
|
98
|
+
const schedule = parseTimeSpec(timeSpec);
|
|
99
|
+
return {
|
|
100
|
+
rawText,
|
|
101
|
+
triggerType: 'alarm',
|
|
102
|
+
schedule,
|
|
103
|
+
operation,
|
|
104
|
+
queueWhenPaused: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
// Try to match event pattern: "When <event> [from <filter>], run <Operation>"
|
|
108
|
+
const eventMatch = rawText.match(/when\s+([\w.]+)(?:\s+from\s+(.+?))?,\s*run\s+(\w+)/i);
|
|
109
|
+
if (eventMatch) {
|
|
110
|
+
const eventType = eventMatch[1];
|
|
111
|
+
const filterSpec = eventMatch[2];
|
|
112
|
+
const operation = eventMatch[3];
|
|
113
|
+
let filter;
|
|
114
|
+
if (filterSpec) {
|
|
115
|
+
// Treat filter as email pattern by default
|
|
116
|
+
filter = { from: filterSpec.trim() };
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
rawText,
|
|
120
|
+
triggerType: 'event',
|
|
121
|
+
eventType,
|
|
122
|
+
filter,
|
|
123
|
+
operation,
|
|
124
|
+
queueWhenPaused: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Fallback: couldn't parse, return minimal trigger
|
|
128
|
+
return {
|
|
129
|
+
rawText,
|
|
130
|
+
triggerType: 'event',
|
|
131
|
+
operation: 'Unknown',
|
|
132
|
+
queueWhenPaused: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Parse triggers from frontmatter data
|
|
137
|
+
*/
|
|
138
|
+
function parseFrontmatterTriggers(data) {
|
|
139
|
+
const triggers = [];
|
|
140
|
+
if (!data?.Triggers || !Array.isArray(data.Triggers)) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
for (const item of data.Triggers) {
|
|
144
|
+
if (typeof item === 'object' && item !== null) {
|
|
145
|
+
// Handle structured trigger from frontmatter
|
|
146
|
+
if (item.schedule) {
|
|
147
|
+
// Alarm trigger with explicit cron
|
|
148
|
+
triggers.push({
|
|
149
|
+
rawText: JSON.stringify(item),
|
|
150
|
+
triggerType: 'alarm',
|
|
151
|
+
schedule: item.schedule,
|
|
152
|
+
operation: item.operation || 'Unknown',
|
|
153
|
+
queueWhenPaused: item.queue_when_paused ?? true,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else if (item.event_type) {
|
|
157
|
+
// Event trigger
|
|
158
|
+
triggers.push({
|
|
159
|
+
rawText: JSON.stringify(item),
|
|
160
|
+
triggerType: 'event',
|
|
161
|
+
eventType: item.event_type,
|
|
162
|
+
filter: item.filter,
|
|
163
|
+
operation: item.operation || 'Unknown',
|
|
164
|
+
queueWhenPaused: item.queue_when_paused ?? true,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return triggers;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Parse triggers from markdown content
|
|
173
|
+
* Combines triggers from both Triggers section and frontmatter
|
|
174
|
+
*
|
|
175
|
+
* @param content - Full markdown document content
|
|
176
|
+
* @returns Array of Trigger objects
|
|
177
|
+
*/
|
|
178
|
+
export function parseTriggers(content) {
|
|
179
|
+
const triggers = [];
|
|
180
|
+
// Extract only first frontmatter block to avoid "multiple documents" error
|
|
181
|
+
const trimmedContent = content.trimStart();
|
|
182
|
+
const frontmatterMatch = trimmedContent.match(/^---\n([\s\S]*?)\n---/);
|
|
183
|
+
if (frontmatterMatch) {
|
|
184
|
+
try {
|
|
185
|
+
const { data } = matter(frontmatterMatch[0]);
|
|
186
|
+
if (data && typeof data === 'object') {
|
|
187
|
+
const frontmatterTriggers = parseFrontmatterTriggers(data);
|
|
188
|
+
triggers.push(...frontmatterTriggers);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
// Frontmatter parsing error, continue without frontmatter triggers
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Find Triggers section
|
|
196
|
+
const triggersMatch = content.match(/^#\s*\[?Triggers\]?\s*$/im);
|
|
197
|
+
if (triggersMatch) {
|
|
198
|
+
// Get content after Triggers heading
|
|
199
|
+
const startIndex = triggersMatch.index + triggersMatch[0].length;
|
|
200
|
+
const restContent = content.slice(startIndex);
|
|
201
|
+
// Find next top-level heading
|
|
202
|
+
const nextH1Match = restContent.match(/\n#\s+[^\#]/);
|
|
203
|
+
const triggersContent = nextH1Match
|
|
204
|
+
? restContent.slice(0, nextH1Match.index)
|
|
205
|
+
: restContent;
|
|
206
|
+
// Parse bullet items
|
|
207
|
+
const lines = triggersContent.split('\n');
|
|
208
|
+
for (const line of lines) {
|
|
209
|
+
const trimmed = line.trim();
|
|
210
|
+
const bulletMatch = trimmed.match(/^[-*]\s+(.+)$/);
|
|
211
|
+
if (bulletMatch) {
|
|
212
|
+
const trigger = parseTriggerDeclaration(bulletMatch[1]);
|
|
213
|
+
triggers.push(trigger);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return triggers;
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=triggers.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider system for URL handling
|
|
3
|
+
*
|
|
4
|
+
* Providers handle fetching content from different URL sources (GitHub, GitLab, generic URLs).
|
|
5
|
+
*/
|
|
6
|
+
export interface ParsedURL {
|
|
7
|
+
provider: string;
|
|
8
|
+
org?: string;
|
|
9
|
+
repo?: string;
|
|
10
|
+
ref?: string;
|
|
11
|
+
path: string;
|
|
12
|
+
anchor?: string;
|
|
13
|
+
rawUrl?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface Provider {
|
|
16
|
+
name: string;
|
|
17
|
+
/**
|
|
18
|
+
* Check if this provider handles the given URL
|
|
19
|
+
*/
|
|
20
|
+
matches(url: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Parse URL into components
|
|
23
|
+
*/
|
|
24
|
+
parse(url: string): ParsedURL;
|
|
25
|
+
/**
|
|
26
|
+
* Convert parsed URL to raw content URL
|
|
27
|
+
*/
|
|
28
|
+
getRawUrl(parsed: ParsedURL): string;
|
|
29
|
+
/**
|
|
30
|
+
* Fetch content from URL
|
|
31
|
+
*/
|
|
32
|
+
fetch(url: string): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Get the latest version/tag for a repo (optional)
|
|
35
|
+
*/
|
|
36
|
+
getLatestVersion?(parsed: ParsedURL): Promise<string>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Provider registry - manages all URL providers
|
|
40
|
+
*/
|
|
41
|
+
export declare class ProviderRegistry {
|
|
42
|
+
private providers;
|
|
43
|
+
/**
|
|
44
|
+
* Register a provider
|
|
45
|
+
*/
|
|
46
|
+
register(provider: Provider): void;
|
|
47
|
+
/**
|
|
48
|
+
* Find provider that handles a URL
|
|
49
|
+
*/
|
|
50
|
+
findProvider(url: string): Provider | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Get all registered providers
|
|
53
|
+
*/
|
|
54
|
+
getProviders(): Provider[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Global provider registry instance
|
|
58
|
+
*/
|
|
59
|
+
export declare const providerRegistry: ProviderRegistry;
|
|
60
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/providers/base.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAE9B;;OAEG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IAE9B;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAAC;IAErC;;OAEG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEpC;;OAEG;IACH,gBAAgB,CAAC,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACvD;AAED;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAkB;IAEnC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAIlC;;OAEG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAI/C;;OAEG;IACH,YAAY,IAAI,QAAQ,EAAE;CAG3B;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider system for URL handling
|
|
3
|
+
*
|
|
4
|
+
* Providers handle fetching content from different URL sources (GitHub, GitLab, generic URLs).
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Provider registry - manages all URL providers
|
|
8
|
+
*/
|
|
9
|
+
export class ProviderRegistry {
|
|
10
|
+
providers = [];
|
|
11
|
+
/**
|
|
12
|
+
* Register a provider
|
|
13
|
+
*/
|
|
14
|
+
register(provider) {
|
|
15
|
+
this.providers.push(provider);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Find provider that handles a URL
|
|
19
|
+
*/
|
|
20
|
+
findProvider(url) {
|
|
21
|
+
return this.providers.find(p => p.matches(url));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get all registered providers
|
|
25
|
+
*/
|
|
26
|
+
getProviders() {
|
|
27
|
+
return [...this.providers];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Global provider registry instance
|
|
32
|
+
*/
|
|
33
|
+
export const providerRegistry = new ProviderRegistry();
|
|
34
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub URL provider
|
|
3
|
+
*
|
|
4
|
+
* Handles URLs like:
|
|
5
|
+
* - https://github.com/org/repo/blob/ref/path/to/file.md
|
|
6
|
+
* - https://github.com/org/repo/tree/ref/path/to/dir
|
|
7
|
+
*/
|
|
8
|
+
import { Provider, ParsedURL } from './base.js';
|
|
9
|
+
export declare class GitHubProvider implements Provider {
|
|
10
|
+
name: string;
|
|
11
|
+
matches(url: string): boolean;
|
|
12
|
+
parse(url: string): ParsedURL;
|
|
13
|
+
getRawUrl(parsed: ParsedURL): string;
|
|
14
|
+
fetch(url: string): Promise<string>;
|
|
15
|
+
getLatestVersion(parsed: ParsedURL): Promise<string>;
|
|
16
|
+
}
|
|
17
|
+
export declare const githubProvider: GitHubProvider;
|
|
18
|
+
//# sourceMappingURL=github.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/providers/github.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAoB,MAAM,WAAW,CAAC;AAWlE,qBAAa,cAAe,YAAW,QAAQ;IAC7C,IAAI,SAAY;IAEhB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAU7B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;IA+C7B,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM;IAO9B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYnC,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;CA0B3D;AAGD,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub URL provider
|
|
3
|
+
*
|
|
4
|
+
* Handles URLs like:
|
|
5
|
+
* - https://github.com/org/repo/blob/ref/path/to/file.md
|
|
6
|
+
* - https://github.com/org/repo/tree/ref/path/to/dir
|
|
7
|
+
*/
|
|
8
|
+
import { providerRegistry } from './base.js';
|
|
9
|
+
// Pattern for GitHub blob URLs (single file)
|
|
10
|
+
const BLOB_PATTERN = /github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)/;
|
|
11
|
+
// Pattern for GitHub tree URLs (directory)
|
|
12
|
+
const TREE_PATTERN = /github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)\/(.+)/;
|
|
13
|
+
// Pattern for raw GitHub URLs
|
|
14
|
+
const RAW_PATTERN = /raw\.githubusercontent\.com\/([^/]+)\/([^/]+)\/([^/]+)\/(.+)/;
|
|
15
|
+
export class GitHubProvider {
|
|
16
|
+
name = 'github';
|
|
17
|
+
matches(url) {
|
|
18
|
+
// Use proper URL parsing to check the hostname
|
|
19
|
+
try {
|
|
20
|
+
const parsed = new URL(url);
|
|
21
|
+
return parsed.hostname === 'github.com' || parsed.hostname === 'raw.githubusercontent.com';
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
parse(url) {
|
|
28
|
+
// Extract anchor if present
|
|
29
|
+
const [urlWithoutAnchor, anchor] = url.split('#');
|
|
30
|
+
// Try blob pattern first
|
|
31
|
+
let match = urlWithoutAnchor.match(BLOB_PATTERN);
|
|
32
|
+
if (match) {
|
|
33
|
+
return {
|
|
34
|
+
provider: 'github',
|
|
35
|
+
org: match[1],
|
|
36
|
+
repo: match[2],
|
|
37
|
+
ref: match[3],
|
|
38
|
+
path: match[4],
|
|
39
|
+
anchor,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Try tree pattern (directory)
|
|
43
|
+
match = urlWithoutAnchor.match(TREE_PATTERN);
|
|
44
|
+
if (match) {
|
|
45
|
+
return {
|
|
46
|
+
provider: 'github',
|
|
47
|
+
org: match[1],
|
|
48
|
+
repo: match[2],
|
|
49
|
+
ref: match[3],
|
|
50
|
+
path: match[4],
|
|
51
|
+
anchor,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Try raw URL pattern
|
|
55
|
+
match = urlWithoutAnchor.match(RAW_PATTERN);
|
|
56
|
+
if (match) {
|
|
57
|
+
return {
|
|
58
|
+
provider: 'github',
|
|
59
|
+
org: match[1],
|
|
60
|
+
repo: match[2],
|
|
61
|
+
ref: match[3],
|
|
62
|
+
path: match[4],
|
|
63
|
+
anchor,
|
|
64
|
+
rawUrl: urlWithoutAnchor,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`Cannot parse GitHub URL: ${url}`);
|
|
68
|
+
}
|
|
69
|
+
getRawUrl(parsed) {
|
|
70
|
+
if (parsed.rawUrl) {
|
|
71
|
+
return parsed.rawUrl;
|
|
72
|
+
}
|
|
73
|
+
return `https://raw.githubusercontent.com/${parsed.org}/${parsed.repo}/${parsed.ref}/${parsed.path}`;
|
|
74
|
+
}
|
|
75
|
+
async fetch(url) {
|
|
76
|
+
const parsed = this.parse(url);
|
|
77
|
+
const rawUrl = this.getRawUrl(parsed);
|
|
78
|
+
const response = await fetch(rawUrl);
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw new Error(`Failed to fetch ${rawUrl}: ${response.status} ${response.statusText}`);
|
|
81
|
+
}
|
|
82
|
+
return response.text();
|
|
83
|
+
}
|
|
84
|
+
async getLatestVersion(parsed) {
|
|
85
|
+
const apiUrl = `https://api.github.com/repos/${parsed.org}/${parsed.repo}/tags`;
|
|
86
|
+
const response = await fetch(apiUrl, {
|
|
87
|
+
headers: {
|
|
88
|
+
Accept: 'application/vnd.github.v3+json',
|
|
89
|
+
// Add User-Agent header as required by GitHub API
|
|
90
|
+
'User-Agent': 'busy-parser',
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
throw new Error(`Failed to fetch tags from GitHub API: ${response.status}`);
|
|
95
|
+
}
|
|
96
|
+
const tags = await response.json();
|
|
97
|
+
if (tags.length === 0) {
|
|
98
|
+
throw new Error(`No tags found for ${parsed.org}/${parsed.repo}`);
|
|
99
|
+
}
|
|
100
|
+
// Return the first (latest) tag
|
|
101
|
+
// Note: GitHub API returns tags in reverse chronological order for most repos
|
|
102
|
+
// For proper semver sorting, we'd need to parse and sort the tags
|
|
103
|
+
return tags[0].name;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Create and register the GitHub provider
|
|
107
|
+
export const githubProvider = new GitHubProvider();
|
|
108
|
+
providerRegistry.register(githubProvider);
|
|
109
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitLab URL provider
|
|
3
|
+
*
|
|
4
|
+
* Handles URLs like:
|
|
5
|
+
* - https://gitlab.com/org/repo/-/blob/ref/path/to/file.md
|
|
6
|
+
* - https://gitlab.com/org/repo/-/tree/ref/path/to/dir
|
|
7
|
+
*/
|
|
8
|
+
import { Provider, ParsedURL } from './base.js';
|
|
9
|
+
export declare class GitLabProvider implements Provider {
|
|
10
|
+
name: string;
|
|
11
|
+
matches(url: string): boolean;
|
|
12
|
+
parse(url: string): ParsedURL;
|
|
13
|
+
getRawUrl(parsed: ParsedURL): string;
|
|
14
|
+
fetch(url: string): Promise<string>;
|
|
15
|
+
getLatestVersion(parsed: ParsedURL): Promise<string>;
|
|
16
|
+
}
|
|
17
|
+
export declare const gitlabProvider: GitLabProvider;
|
|
18
|
+
//# sourceMappingURL=gitlab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitlab.d.ts","sourceRoot":"","sources":["../../src/providers/gitlab.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAoB,MAAM,WAAW,CAAC;AAWlE,qBAAa,cAAe,YAAW,QAAQ;IAC7C,IAAI,SAAY;IAEhB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAU7B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;IA+C7B,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM;IAO9B,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYnC,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC;CAkB3D;AAGD,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
|