@openhands/extensions 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-please-manifest.json +1 -1
- package/AGENTS.md +1 -0
- package/automations/README.md +97 -18
- package/automations/bundle-index.js +9 -0
- package/automations/catalog/github-pr-reviewer/manifest.json +23 -7
- package/automations/catalog/github-repo-monitor/manifest.json +28 -34
- package/automations/catalog/jira-issue-to-pr/manifest.json +3 -3
- package/automations/catalog.schema.json +102 -1
- package/automations/index.d.ts +174 -1
- package/automations/index.js +14 -0
- package/automations/interface.json +93 -6
- package/automations/interface.schema.json +273 -9
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/integrations/catalog/posthog.json +41 -3
- package/integrations/catalog/sonarqube.json +78 -0
- package/integrations/catalog-index.js +65 -63
- package/marketplaces/openhands-extensions.json +11 -0
- package/package.json +1 -1
- package/plugins/qa-changes/action.yml +1 -1
- package/pyproject.toml +1 -1
- package/python/openhands_extensions/_version.py +1 -1
- package/scripts/build-automation-catalog.mjs +30 -0
- package/scripts/build-skills-catalog.mjs +24 -7
- package/skills/add-skill/SKILL.md +1 -1
- package/skills/github-pr-reviewer/README.md +12 -6
- package/skills/github-pr-reviewer/SKILL.md +93 -31
- package/skills/github-pr-reviewer/commands/pr-reviewer-setup.md +1 -1
- package/skills/github-pr-reviewer/references/state-schema.md +68 -18
- package/skills/github-pr-reviewer/scripts/main.py +540 -103
- package/skills/github-pr-reviewer/tests/test_main.py +503 -0
- package/skills/index.d.ts +6 -0
- package/skills/index.js +29 -17
- package/skills/jira-issue-to-pr/scripts/main.py +4 -2
- package/skills/openhands-sdk/SKILL.md +2 -0
- package/skills/skill-creator/SKILL.md +1 -1
- package/skills/slack-channel-monitor/scripts/main.py +3 -2
- package/tests/fixtures/automations/capabilities.json +30 -0
- package/tests/fixtures/automations/github-pr-reviewer.json +319 -55
- package/tests/fixtures/automations/github-repo-monitor.json +153 -62
- package/tests/test_automation_setup.py +226 -31
- package/tests/test_catalogs.py +43 -0
- package/tests/test_interface_manifest.py +20 -0
- package/tests/test_skills_catalog.py +182 -36
- package/tests/test_slack_channel_monitor.py +1 -1
|
@@ -63,6 +63,10 @@
|
|
|
63
63
|
"description": "direct only. What the automation is told to do. The rest of the create request restates the form, so it is not written here: the automation's name comes from this entry, the repository from the repo-picker field, and the trigger from the key and fields under form.triggers.",
|
|
64
64
|
"$ref": "#/$defs/templateValue"
|
|
65
65
|
},
|
|
66
|
+
"bundle": {
|
|
67
|
+
"description": "direct only, and the alternative to prompt. The script tarball this entry ships: the automation is deterministic machinery rather than judgement, so it runs as its own code and the host creates it through the raw create endpoint instead of a preset. The rest of the create request still restates the form, so it is not written here either.",
|
|
68
|
+
"$ref": "#/$defs/bundle"
|
|
69
|
+
},
|
|
66
70
|
"filter": {
|
|
67
71
|
"description": "direct only, and only for an event trigger. The expression deciding whether a delivered event belongs to this automation. It composes form values into a JMESPath expression, which is the one part of an event trigger that cannot be read off the form.",
|
|
68
72
|
"$ref": "#/$defs/templateValue"
|
|
@@ -79,7 +83,10 @@
|
|
|
79
83
|
"required": ["mode"]
|
|
80
84
|
},
|
|
81
85
|
"then": {
|
|
82
|
-
"
|
|
86
|
+
"oneOf": [
|
|
87
|
+
{ "required": ["prompt"], "not": { "required": ["bundle"] } },
|
|
88
|
+
{ "required": ["bundle"], "not": { "required": ["prompt"] } }
|
|
89
|
+
]
|
|
83
90
|
}
|
|
84
91
|
},
|
|
85
92
|
{
|
|
@@ -91,6 +98,7 @@
|
|
|
91
98
|
"required": ["message"],
|
|
92
99
|
"allOf": [
|
|
93
100
|
{ "not": { "required": ["prompt"] } },
|
|
101
|
+
{ "not": { "required": ["bundle"] } },
|
|
94
102
|
{ "not": { "required": ["filter"] } }
|
|
95
103
|
]
|
|
96
104
|
}
|
|
@@ -108,6 +116,88 @@
|
|
|
108
116
|
"required": ["form"]
|
|
109
117
|
},
|
|
110
118
|
"then": { "not": { "required": ["filter"] } }
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"if": { "required": ["bundle"] },
|
|
122
|
+
"then": { "not": { "required": ["filter"] } }
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
"bundle": {
|
|
127
|
+
"description": "What the host packs into a .tar.gz and uploads before creating the automation. It names files inside this repository and the command that runs them - never a host, a URL, or code to evaluate.",
|
|
128
|
+
"type": "object",
|
|
129
|
+
"additionalProperties": false,
|
|
130
|
+
"required": ["version", "entrypoint", "files", "config"],
|
|
131
|
+
"properties": {
|
|
132
|
+
"version": {
|
|
133
|
+
"description": "The version of this bundle, recorded as the created automation's template provenance. Bump it when the shipped files or the config shape change: it is what tells an already-enabled deployment that what it installed is no longer current. Not the package version, which moves for reasons that have nothing to do with this entry.",
|
|
134
|
+
"type": "string",
|
|
135
|
+
"minLength": 1,
|
|
136
|
+
"maxLength": 50,
|
|
137
|
+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
|
|
138
|
+
},
|
|
139
|
+
"entrypoint": {
|
|
140
|
+
"description": "The command the automation service runs inside the extracted tarball, such as `python3 main.py`. No shell metacharacters: the service rejects them, and a bundle has no reason to need one.",
|
|
141
|
+
"type": "string",
|
|
142
|
+
"minLength": 1,
|
|
143
|
+
"maxLength": 500,
|
|
144
|
+
"pattern": "^[A-Za-z0-9 ._/-]+$"
|
|
145
|
+
},
|
|
146
|
+
"setupScript": {
|
|
147
|
+
"description": "Path inside the tarball to a script run once before the entrypoint, such as `setup.sh`. Absent when the bundle needs no installation step, which is the case for a standard-library script.",
|
|
148
|
+
"type": "string",
|
|
149
|
+
"minLength": 1,
|
|
150
|
+
"maxLength": 255,
|
|
151
|
+
"pattern": "^[A-Za-z0-9._-]+(/[A-Za-z0-9._-]+)*$",
|
|
152
|
+
"not": { "pattern": "(^|/)\\.\\.?(/|$)" }
|
|
153
|
+
},
|
|
154
|
+
"timeout": {
|
|
155
|
+
"description": "Seconds a single run may take, when this entry needs more than the service default. A poll that fetches an archive per queued item needs longer than one that only calls an API.",
|
|
156
|
+
"type": "integer",
|
|
157
|
+
"minimum": 1,
|
|
158
|
+
"maximum": 86400
|
|
159
|
+
},
|
|
160
|
+
"files": {
|
|
161
|
+
"description": "The tarball's contents, keyed by the path each file takes inside the archive and valued by where it lives in this repository. Stated rather than derived from the entry directory, so a script shipped by both a skill and the catalog has one copy rather than two.",
|
|
162
|
+
"type": "object",
|
|
163
|
+
"minProperties": 1,
|
|
164
|
+
"maxProperties": 32,
|
|
165
|
+
"propertyNames": {
|
|
166
|
+
"pattern": "^[A-Za-z0-9._-]+(/[A-Za-z0-9._-]+)*$",
|
|
167
|
+
"not": { "pattern": "(^|/)\\.\\.?(/|$)" },
|
|
168
|
+
"maxLength": 255
|
|
169
|
+
},
|
|
170
|
+
"additionalProperties": { "$ref": "#/$defs/bundleSource" }
|
|
171
|
+
},
|
|
172
|
+
"config": {
|
|
173
|
+
"description": "The config.json packed beside the entrypoint, rendered from the form. This is the bundle's analogue of prompt: the one thing that cannot be read off the form, because only the entry knows which key of its own script each field fills.",
|
|
174
|
+
"$ref": "#/$defs/bundleConfig"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"bundleSource": {
|
|
179
|
+
"description": "A file in this repository, as a repository-relative path under skills/ or automations/. No absolute path, no traversal, and no scheme, so a bundle cannot name anything outside the published package.",
|
|
180
|
+
"type": "string",
|
|
181
|
+
"minLength": 1,
|
|
182
|
+
"maxLength": 255,
|
|
183
|
+
"pattern": "^(skills|automations)/[A-Za-z0-9._-]+(/[A-Za-z0-9._-]+)*$",
|
|
184
|
+
"not": { "pattern": "(^|/)\\.\\.?(/|$)" }
|
|
185
|
+
},
|
|
186
|
+
"bundleConfig": {
|
|
187
|
+
"description": "The JSON document written to config.json. Its string leaves may embed the same placeholders every other value uses. It carries no markup rule, because it is written to a file rather than rendered.",
|
|
188
|
+
"type": "object",
|
|
189
|
+
"minProperties": 1,
|
|
190
|
+
"additionalProperties": { "$ref": "#/$defs/bundleConfigValue" }
|
|
191
|
+
},
|
|
192
|
+
"bundleConfigValue": {
|
|
193
|
+
"description": "A config leaf: a templated string, a number, a boolean, null, or an array or object of the same.",
|
|
194
|
+
"anyOf": [
|
|
195
|
+
{ "$ref": "#/$defs/templateValue" },
|
|
196
|
+
{ "type": ["number", "boolean", "null"] },
|
|
197
|
+
{ "type": "array", "items": { "$ref": "#/$defs/bundleConfigValue" } },
|
|
198
|
+
{
|
|
199
|
+
"type": "object",
|
|
200
|
+
"additionalProperties": { "$ref": "#/$defs/bundleConfigValue" }
|
|
111
201
|
}
|
|
112
202
|
]
|
|
113
203
|
},
|
|
@@ -224,6 +314,10 @@
|
|
|
224
314
|
"default": { "type": "string" },
|
|
225
315
|
"required": { "type": "boolean" },
|
|
226
316
|
"provider": { "enum": ["github", "gitlab", "bitbucket"] },
|
|
317
|
+
"multiple": {
|
|
318
|
+
"description": "repo-picker only. The field collects several repositories rather than one, and its value is a list. A whole-value placeholder resolves to that list, so a payload can state `\"repos\": \"{{form.repositories}}\"` and get an array.",
|
|
319
|
+
"const": true
|
|
320
|
+
},
|
|
227
321
|
"options": {
|
|
228
322
|
"type": "array",
|
|
229
323
|
"minItems": 1,
|
|
@@ -259,6 +353,13 @@
|
|
|
259
353
|
},
|
|
260
354
|
"then": { "required": ["provider"] }
|
|
261
355
|
},
|
|
356
|
+
{
|
|
357
|
+
"if": {
|
|
358
|
+
"properties": { "type": { "not": { "const": "repo-picker" } } },
|
|
359
|
+
"required": ["type"]
|
|
360
|
+
},
|
|
361
|
+
"then": { "not": { "required": ["multiple"] } }
|
|
362
|
+
},
|
|
262
363
|
{
|
|
263
364
|
"if": {
|
|
264
365
|
"properties": { "type": { "not": { "const": "select" } } },
|
package/automations/index.d.ts
CHANGED
|
@@ -63,6 +63,11 @@ export interface AutomationFormField {
|
|
|
63
63
|
default?: string;
|
|
64
64
|
required: boolean;
|
|
65
65
|
provider?: AutomationGitProvider;
|
|
66
|
+
/**
|
|
67
|
+
* repo-picker only. The field collects several repositories rather than one,
|
|
68
|
+
* and its value is a list. A whole-value placeholder resolves to that list.
|
|
69
|
+
*/
|
|
70
|
+
multiple?: true;
|
|
66
71
|
options?: AutomationFieldOption[];
|
|
67
72
|
constraints?: AutomationFieldConstraints;
|
|
68
73
|
}
|
|
@@ -96,12 +101,40 @@ export interface AutomationForm {
|
|
|
96
101
|
args: AutomationFormFields;
|
|
97
102
|
}
|
|
98
103
|
|
|
104
|
+
/** A config.json leaf: templated string, number, boolean, null, or a nesting of those. */
|
|
105
|
+
export type AutomationBundleConfigValue =
|
|
106
|
+
| string
|
|
107
|
+
| number
|
|
108
|
+
| boolean
|
|
109
|
+
| null
|
|
110
|
+
| AutomationBundleConfigValue[]
|
|
111
|
+
| { [key: string]: AutomationBundleConfigValue };
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The script tarball a direct entry may ship instead of a prompt, for an
|
|
115
|
+
* automation that is deterministic machinery rather than judgement.
|
|
116
|
+
*/
|
|
117
|
+
export interface AutomationBundle {
|
|
118
|
+
/** The command run inside the extracted tarball. */
|
|
119
|
+
entrypoint: string;
|
|
120
|
+
/** Script run once before the entrypoint. Absent when nothing to install. */
|
|
121
|
+
setupScript?: string;
|
|
122
|
+
/** Seconds a run may take, when the service default is not enough. */
|
|
123
|
+
timeout?: number;
|
|
124
|
+
/** Packed path -> the repository path the file is read from at build time. */
|
|
125
|
+
files: Record<string, string>;
|
|
126
|
+
/** Rendered from the form and packed as config.json beside the entrypoint. */
|
|
127
|
+
config: Record<string, AutomationBundleConfigValue>;
|
|
128
|
+
}
|
|
129
|
+
|
|
99
130
|
export interface AutomationSetup {
|
|
100
131
|
version: "1.0";
|
|
101
132
|
mode: AutomationSetupMode;
|
|
102
133
|
form: AutomationForm;
|
|
103
134
|
/** direct only. What the automation is told to do. */
|
|
104
135
|
prompt?: string;
|
|
136
|
+
/** direct only, and the alternative to `prompt`. Exactly one is present. */
|
|
137
|
+
bundle?: AutomationBundle;
|
|
105
138
|
/** direct only, event trigger only. Which delivered events belong to it. */
|
|
106
139
|
filter?: string;
|
|
107
140
|
/**
|
|
@@ -130,17 +163,142 @@ export interface AutomationInterfaceRoutes {
|
|
|
130
163
|
setup: string;
|
|
131
164
|
/** Carries the `:automationId` segment the host substitutes. */
|
|
132
165
|
detail: string;
|
|
166
|
+
/** The templates sub-page. Static: there is no parameter to substitute. */
|
|
167
|
+
templates: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** An icon name from the host's closed icon map. */
|
|
171
|
+
export type AutomationIconSlug =
|
|
172
|
+
| "layout-dashboard"
|
|
173
|
+
| "sparkles"
|
|
174
|
+
| "bot"
|
|
175
|
+
| "circle-alert"
|
|
176
|
+
| "activity"
|
|
177
|
+
| "timer";
|
|
178
|
+
|
|
179
|
+
/** The pages a sub-page navigation item may point at. */
|
|
180
|
+
export type AutomationSubPageId = "list" | "templates";
|
|
181
|
+
|
|
182
|
+
export interface AutomationSubPageNavItem {
|
|
183
|
+
/** The `pages` entry this item navigates to; its route comes from `routes`. */
|
|
184
|
+
page: AutomationSubPageId;
|
|
185
|
+
label: string;
|
|
186
|
+
icon: AutomationIconSlug;
|
|
133
187
|
}
|
|
134
188
|
|
|
135
189
|
export interface AutomationInterfaceNavigation {
|
|
136
190
|
sidebar: { label: string };
|
|
137
191
|
commandMenu: { title: string; description: string; keywords: string };
|
|
192
|
+
/** The ordered sub-page navigation of the Automation interface. */
|
|
193
|
+
subPages: AutomationSubPageNavItem[];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** A value the host computes; a tile picks and captions it, never defines it. */
|
|
197
|
+
export type AutomationOverviewMetric =
|
|
198
|
+
| "automations"
|
|
199
|
+
| "needs-attention"
|
|
200
|
+
| "total-runs"
|
|
201
|
+
| "average-duration";
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* One summary tile. `detail` captions the value; `zeroDetail`, when present,
|
|
205
|
+
* replaces it while the value is zero. Both are plain substitution over the
|
|
206
|
+
* metric's placeholder namespace: only the `automations` metric exposes
|
|
207
|
+
* `{{active}}`, so every other tile's copy is literal.
|
|
208
|
+
*/
|
|
209
|
+
export interface AutomationOverviewTile {
|
|
210
|
+
metric: AutomationOverviewMetric;
|
|
211
|
+
label: string;
|
|
212
|
+
detail: string;
|
|
213
|
+
zeroDetail?: string;
|
|
214
|
+
icon: AutomationIconSlug;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface AutomationOverview {
|
|
218
|
+
/** Names the tiles section for assistive technology. */
|
|
219
|
+
label: string;
|
|
220
|
+
tiles: AutomationOverviewTile[];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export type AutomationStatusFilterValue =
|
|
224
|
+
| "all"
|
|
225
|
+
| "active"
|
|
226
|
+
| "failing"
|
|
227
|
+
| "disabled";
|
|
228
|
+
export type AutomationTriggerFilterValue = "all" | "schedule" | "event";
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* A filter dropdown. Values name predicates the host implements; the manifest
|
|
232
|
+
* supplies which appear and their labels. `label` is the control's accessible
|
|
233
|
+
* name. The `all` option is the host's default and reset target.
|
|
234
|
+
*/
|
|
235
|
+
export interface AutomationStatusFilter {
|
|
236
|
+
id: "status";
|
|
237
|
+
label: string;
|
|
238
|
+
options: { value: AutomationStatusFilterValue; label: string }[];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export interface AutomationTriggerFilter {
|
|
242
|
+
id: "trigger";
|
|
243
|
+
label: string;
|
|
244
|
+
options: { value: AutomationTriggerFilterValue; label: string }[];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export type AutomationDashboardFilter =
|
|
248
|
+
| AutomationStatusFilter
|
|
249
|
+
| AutomationTriggerFilter;
|
|
250
|
+
|
|
251
|
+
/** A comparator the host implements, named from a closed set. */
|
|
252
|
+
export type AutomationSortValue = "last-run" | "runs" | "name";
|
|
253
|
+
|
|
254
|
+
export interface AutomationDashboardSort {
|
|
255
|
+
/** The control's accessible name. */
|
|
256
|
+
label: string;
|
|
257
|
+
options: { value: AutomationSortValue; label: string }[];
|
|
258
|
+
/** Must be one of the declared option values. */
|
|
259
|
+
default: AutomationSortValue;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Copy for the per-automation run insights on cards and rows. The states,
|
|
264
|
+
* precedence, sampling, and value formatting are the host's; the manifest
|
|
265
|
+
* names them.
|
|
266
|
+
*/
|
|
267
|
+
export interface AutomationListInsights {
|
|
268
|
+
health: {
|
|
269
|
+
healthy: string;
|
|
270
|
+
failing: string;
|
|
271
|
+
running: string;
|
|
272
|
+
disabled: string;
|
|
273
|
+
neverRun: string;
|
|
274
|
+
checking: string;
|
|
275
|
+
};
|
|
276
|
+
lastRun: { label: string; never: string; justNow: string };
|
|
277
|
+
stats: { runs: string; recentSuccess: string; averageDuration: string };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* The templates sub-page identity. Its body - the catalog cards and their
|
|
282
|
+
* launch behavior - is the host's existing catalog surface.
|
|
283
|
+
*/
|
|
284
|
+
export interface AutomationTemplatesPage {
|
|
285
|
+
title: string;
|
|
286
|
+
description: string;
|
|
138
287
|
}
|
|
139
288
|
|
|
140
289
|
export interface AutomationInterfacePages {
|
|
141
|
-
list: {
|
|
290
|
+
list: {
|
|
291
|
+
title: string;
|
|
292
|
+
subtitle: string;
|
|
293
|
+
overview: AutomationOverview;
|
|
294
|
+
/** The filter dropdowns of the list page, in render order. */
|
|
295
|
+
filters: AutomationDashboardFilter[];
|
|
296
|
+
sort: AutomationDashboardSort;
|
|
297
|
+
insights: AutomationListInsights;
|
|
298
|
+
};
|
|
142
299
|
detail: { backLabel: string };
|
|
143
300
|
edit: { title: string };
|
|
301
|
+
templates: AutomationTemplatesPage;
|
|
144
302
|
}
|
|
145
303
|
|
|
146
304
|
export type AutomationAttributeType =
|
|
@@ -212,6 +370,10 @@ export interface AutomationInterfaceEndpoints {
|
|
|
212
370
|
validate: string;
|
|
213
371
|
createPrompt: string;
|
|
214
372
|
createPlugin: string;
|
|
373
|
+
/** The raw create endpoint, which a bundle entry is created through. */
|
|
374
|
+
createBundle: string;
|
|
375
|
+
/** Where a bundle's tarball is uploaded before that create call. */
|
|
376
|
+
uploads: string;
|
|
215
377
|
}
|
|
216
378
|
|
|
217
379
|
export interface AutomationInterfaceManifest {
|
|
@@ -240,4 +402,15 @@ export function listAutomationCatalog(): RecommendedAutomation[];
|
|
|
240
402
|
export function getAutomationCatalogEntry(
|
|
241
403
|
id: string,
|
|
242
404
|
): RecommendedAutomation | undefined;
|
|
405
|
+
/**
|
|
406
|
+
* Return the files a bundle entry ships, keyed by the path each takes inside
|
|
407
|
+
* the tarball, as an independent copy. Undefined for an entry with no bundle.
|
|
408
|
+
*
|
|
409
|
+
* The contents are inlined at build time from the repository paths
|
|
410
|
+
* `setup.bundle.files` names, because a host packing the tarball has the
|
|
411
|
+
* published package but not the repository.
|
|
412
|
+
*/
|
|
413
|
+
export function getAutomationBundleFiles(
|
|
414
|
+
id: string,
|
|
415
|
+
): Record<string, string> | undefined;
|
|
243
416
|
export default AUTOMATION_CATALOG;
|
package/automations/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* directory. `catalog-index.js` is generated from that directory so the JS
|
|
6
6
|
* package can statically import each JSON file without an aggregate JSON asset.
|
|
7
7
|
*/
|
|
8
|
+
import { AUTOMATION_BUNDLE_FILES } from "./bundle-index.js";
|
|
8
9
|
import { AUTOMATION_CATALOG_ENTRIES } from "./catalog-index.js";
|
|
9
10
|
import interfaceManifest from "./interface.json" with { type: "json" };
|
|
10
11
|
|
|
@@ -21,6 +22,19 @@ export const getAutomationCatalogEntry = (id) => {
|
|
|
21
22
|
|
|
22
23
|
export const AUTOMATION_CATALOG = clone(AUTOMATIONS);
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* The files a bundle entry ships, keyed by entry id and then by the path each
|
|
27
|
+
* file takes inside the tarball. Generated from the repository paths the
|
|
28
|
+
* entry's `setup.bundle.files` names, because a host packing the tarball has
|
|
29
|
+
* the package but not the repository.
|
|
30
|
+
*
|
|
31
|
+
* Absent for every entry that does not ship a bundle.
|
|
32
|
+
*/
|
|
33
|
+
export const getAutomationBundleFiles = (id) => {
|
|
34
|
+
const files = AUTOMATION_BUNDLE_FILES[id];
|
|
35
|
+
return files ? clone(files) : undefined;
|
|
36
|
+
};
|
|
37
|
+
|
|
24
38
|
/**
|
|
25
39
|
* The production Automation interface manifest: the domain-level facts of the
|
|
26
40
|
* interface, hand-authored in `automations/interface.json`.
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
"routes": {
|
|
4
4
|
"list": "/automations",
|
|
5
5
|
"setup": "/automations/new/:automationId",
|
|
6
|
-
"detail": "/automations/:automationId"
|
|
6
|
+
"detail": "/automations/:automationId",
|
|
7
|
+
"templates": "/automations/templates"
|
|
7
8
|
},
|
|
8
9
|
"navigation": {
|
|
9
10
|
"sidebar": { "label": "Automate" },
|
|
@@ -11,15 +12,99 @@
|
|
|
11
12
|
"title": "Automations",
|
|
12
13
|
"description": "Review scheduled and webhook automations.",
|
|
13
14
|
"keywords": "automate cron schedule webhook jobs"
|
|
14
|
-
}
|
|
15
|
+
},
|
|
16
|
+
"subPages": [
|
|
17
|
+
{ "page": "list", "label": "Dashboard", "icon": "layout-dashboard" },
|
|
18
|
+
{ "page": "templates", "label": "Templates", "icon": "sparkles" }
|
|
19
|
+
]
|
|
15
20
|
},
|
|
16
21
|
"pages": {
|
|
17
22
|
"list": {
|
|
18
|
-
"title": "
|
|
19
|
-
"subtitle": "
|
|
23
|
+
"title": "Dashboard",
|
|
24
|
+
"subtitle": "Health, activity, and run performance across your automations.",
|
|
25
|
+
"overview": {
|
|
26
|
+
"label": "Automation overview",
|
|
27
|
+
"tiles": [
|
|
28
|
+
{
|
|
29
|
+
"metric": "automations",
|
|
30
|
+
"label": "Automations",
|
|
31
|
+
"detail": "{{active}} active",
|
|
32
|
+
"icon": "bot"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"metric": "needs-attention",
|
|
36
|
+
"label": "Needs attention",
|
|
37
|
+
"detail": "Latest run failed",
|
|
38
|
+
"zeroDetail": "No latest-run failures",
|
|
39
|
+
"icon": "circle-alert"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"metric": "total-runs",
|
|
43
|
+
"label": "Total runs",
|
|
44
|
+
"detail": "Across loaded automations",
|
|
45
|
+
"icon": "activity"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"metric": "average-duration",
|
|
49
|
+
"label": "Average duration",
|
|
50
|
+
"detail": "Recent completed runs",
|
|
51
|
+
"icon": "timer"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"filters": [
|
|
56
|
+
{
|
|
57
|
+
"id": "status",
|
|
58
|
+
"label": "Filter by status",
|
|
59
|
+
"options": [
|
|
60
|
+
{ "value": "all", "label": "All statuses" },
|
|
61
|
+
{ "value": "active", "label": "Active" },
|
|
62
|
+
{ "value": "failing", "label": "Needs attention" },
|
|
63
|
+
{ "value": "disabled", "label": "Disabled" }
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "trigger",
|
|
68
|
+
"label": "Filter by trigger",
|
|
69
|
+
"options": [
|
|
70
|
+
{ "value": "all", "label": "All triggers" },
|
|
71
|
+
{ "value": "schedule", "label": "Scheduled" },
|
|
72
|
+
{ "value": "event", "label": "Event-driven" }
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"sort": {
|
|
77
|
+
"label": "Sort automations",
|
|
78
|
+
"default": "last-run",
|
|
79
|
+
"options": [
|
|
80
|
+
{ "value": "last-run", "label": "Latest run" },
|
|
81
|
+
{ "value": "runs", "label": "Most runs" },
|
|
82
|
+
{ "value": "name", "label": "Name" }
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
"insights": {
|
|
86
|
+
"health": {
|
|
87
|
+
"healthy": "Healthy",
|
|
88
|
+
"failing": "Failing",
|
|
89
|
+
"running": "Running",
|
|
90
|
+
"disabled": "Disabled",
|
|
91
|
+
"neverRun": "Never run",
|
|
92
|
+
"checking": "Checking"
|
|
93
|
+
},
|
|
94
|
+
"lastRun": { "label": "Last run", "never": "Never", "justNow": "Just now" },
|
|
95
|
+
"stats": {
|
|
96
|
+
"runs": "Runs",
|
|
97
|
+
"recentSuccess": "Recent success",
|
|
98
|
+
"averageDuration": "Avg. duration"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
20
101
|
},
|
|
21
102
|
"detail": { "backLabel": "Back to Automations" },
|
|
22
|
-
"edit": { "title": "Edit automation" }
|
|
103
|
+
"edit": { "title": "Edit automation" },
|
|
104
|
+
"templates": {
|
|
105
|
+
"title": "Templates",
|
|
106
|
+
"description": "Browse proven automations and beta ideas, then launch one into a conversation to tailor it to your work."
|
|
107
|
+
}
|
|
23
108
|
},
|
|
24
109
|
"docsUrl": "https://docs.openhands.dev/openhands/usage/automations/overview",
|
|
25
110
|
"attributes": {
|
|
@@ -59,7 +144,9 @@
|
|
|
59
144
|
"capabilities": "/v1/capabilities",
|
|
60
145
|
"validate": "/v1/validate",
|
|
61
146
|
"createPrompt": "/v1/preset/prompt",
|
|
62
|
-
"createPlugin": "/v1/preset/plugin"
|
|
147
|
+
"createPlugin": "/v1/preset/plugin",
|
|
148
|
+
"createBundle": "/v1",
|
|
149
|
+
"uploads": "/v1/uploads"
|
|
63
150
|
},
|
|
64
151
|
"featuredAutomationIds": [
|
|
65
152
|
"github-pr-reviewer",
|