@openhands/extensions 0.11.0 → 0.13.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 +2 -1
- package/MIGRATION.md +31 -0
- package/README.md +9 -7
- package/automations/README.md +300 -8
- package/automations/catalog/github-pr-reviewer/manifest.json +83 -0
- package/automations/catalog/github-repo-monitor/manifest.json +83 -0
- package/automations/catalog/incident-retrospective-drafter/manifest.json +100 -0
- package/automations/catalog/{jira-issue-to-pr.json → jira-issue-to-pr/manifest.json} +10 -5
- package/automations/catalog/{linear-triage-assistant.json → linear-triage-assistant/manifest.json} +8 -4
- package/automations/catalog/{research-brief-writer.json → research-brief-writer/manifest.json} +11 -5
- package/automations/catalog/{slack-channel-monitor.json → slack-channel-monitor/manifest.json} +7 -2
- package/automations/catalog/{slack-standup-digest.json → slack-standup-digest/manifest.json} +8 -5
- package/automations/catalog-index.js +23 -0
- package/automations/catalog.schema.json +272 -0
- package/automations/index.d.ts +232 -3
- package/automations/index.js +28 -18
- package/automations/interface.json +70 -0
- package/automations/interface.schema.json +243 -0
- package/index.d.ts +29 -2
- package/index.js +1 -1
- package/integrations/catalog/datadog.json +3 -3
- package/integrations/index.d.ts +1 -1
- package/package.json +8 -3
- package/pyproject.toml +1 -1
- package/python/openhands_extensions/__init__.py +0 -4
- package/python/openhands_extensions/_version.py +3 -2
- package/python/openhands_extensions/integrations.py +8 -30
- package/scripts/build-automation-catalog.mjs +51 -0
- package/skills/bitbucket/SKILL.md +0 -1
- package/skills/github/SKILL.md +0 -1
- package/skills/gitlab/SKILL.md +0 -1
- package/skills/index.js +5 -8
- package/skills/openhands-automation/SKILL.md +11 -0
- package/skills/openhands-automation/references/custom-automation.md +2 -0
- package/skills/openhands-automation/references/security.md +84 -0
- package/skills/openhands-sdk/SKILL.md +2 -0
- package/tests/fixtures/automations/capabilities.json +92 -0
- package/tests/fixtures/automations/github-pr-reviewer.json +251 -0
- package/tests/fixtures/automations/github-repo-monitor.json +180 -0
- package/tests/fixtures/automations/incident-retrospective-drafter.json +91 -0
- package/tests/test_automation_setup.py +526 -0
- package/tests/test_catalogs.py +37 -12
- package/tests/test_deprecations.py +39 -4
- package/tests/test_integration_catalog_in_sync.py +50 -60
- package/tests/test_interface_manifest.py +107 -0
- package/automations/catalog/github-pr-reviewer.json +0 -13
- package/automations/catalog/github-repo-monitor.json +0 -11
- package/automations/catalog/incident-retrospective-drafter.json +0 -15
package/integrations/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type IntegrationTransport =
|
|
|
17
17
|
apiKeyOptional?: boolean;
|
|
18
18
|
/**
|
|
19
19
|
* Named request headers the user must supply (e.g. Datadog's
|
|
20
|
-
* `
|
|
20
|
+
* `DD_API_KEY` / `DD_APPLICATION_KEY`). Values are sent verbatim as
|
|
21
21
|
* headers on every MCP request. The direct analog of stdio's
|
|
22
22
|
* `envFields`: each entry renders one input in the install modal,
|
|
23
23
|
* `type: "password"` entries are secret-saved by default, and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openhands/extensions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Public OpenHands extension catalogs for skills, plugins, integrations, and automation templates.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build:skills": "node scripts/build-skills-catalog.mjs",
|
|
13
13
|
"build:integrations": "node scripts/build-integration-catalog.mjs",
|
|
14
|
-
"build": "
|
|
14
|
+
"build:automations": "node scripts/build-automation-catalog.mjs",
|
|
15
|
+
"build": "npm run build:skills && npm run build:integrations && npm run build:automations"
|
|
15
16
|
},
|
|
16
17
|
"engines": {
|
|
17
18
|
"node": ">=18.20.0"
|
|
@@ -35,7 +36,10 @@
|
|
|
35
36
|
"import": "./automations/index.js",
|
|
36
37
|
"default": "./automations/index.js"
|
|
37
38
|
},
|
|
38
|
-
"./automations/catalog
|
|
39
|
+
"./automations/catalog/*": "./automations/catalog/*",
|
|
40
|
+
"./automations/catalog.schema.json": "./automations/catalog.schema.json",
|
|
41
|
+
"./automations/interface.json": "./automations/interface.json",
|
|
42
|
+
"./automations/interface.schema.json": "./automations/interface.schema.json",
|
|
39
43
|
"./integrations": {
|
|
40
44
|
"types": "./integrations/index.d.ts",
|
|
41
45
|
"import": "./integrations/index.js",
|
|
@@ -50,6 +54,7 @@
|
|
|
50
54
|
"./skills/*": "./skills/*",
|
|
51
55
|
"./plugins/*": "./plugins/*",
|
|
52
56
|
"./marketplaces/*.json": "./marketplaces/*.json",
|
|
57
|
+
"./testing/automations/*.json": "./tests/fixtures/automations/*.json",
|
|
53
58
|
"./package.json": "./package.json"
|
|
54
59
|
}
|
|
55
60
|
}
|
package/pyproject.toml
CHANGED
|
@@ -12,9 +12,7 @@ from __future__ import annotations
|
|
|
12
12
|
from ._version import __version__
|
|
13
13
|
from .integrations import (
|
|
14
14
|
INTEGRATION_CATALOG_SNAPSHOT,
|
|
15
|
-
get_integration_catalog_entry,
|
|
16
15
|
get_integration_catalog_entry_model,
|
|
17
|
-
list_integration_catalog,
|
|
18
16
|
list_integration_catalog_models,
|
|
19
17
|
)
|
|
20
18
|
from .integration_models import (
|
|
@@ -55,8 +53,6 @@ __all__ = [
|
|
|
55
53
|
"StdioTransport",
|
|
56
54
|
"StreamableHttpTransport",
|
|
57
55
|
"__version__",
|
|
58
|
-
"get_integration_catalog_entry",
|
|
59
56
|
"get_integration_catalog_entry_model",
|
|
60
|
-
"list_integration_catalog",
|
|
61
57
|
"list_integration_catalog_models",
|
|
62
58
|
]
|
|
@@ -16,8 +16,9 @@ from importlib.metadata import PackageNotFoundError, version
|
|
|
16
16
|
|
|
17
17
|
#: Fallback used only when the package is not installed (no dist metadata).
|
|
18
18
|
#: Kept in lock-step with pyproject.toml/package.json by the version test.
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
#: release-please only rewrites a semver found on the annotated line itself, so
|
|
20
|
+
#: the annotation must stay on the assignment; on its own line it is a no-op.
|
|
21
|
+
_FALLBACK_VERSION = "0.13.0" # x-release-please-version
|
|
21
22
|
|
|
22
23
|
try:
|
|
23
24
|
__version__: str = version("openhands-extensions")
|
|
@@ -14,15 +14,12 @@ from importlib import resources
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from typing import Any, Iterable
|
|
16
16
|
|
|
17
|
-
from .deprecation import deprecated
|
|
18
17
|
from .integration_models import IntegrationCatalogEntry
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
__all__ = [
|
|
22
21
|
"INTEGRATION_CATALOG_SNAPSHOT",
|
|
23
|
-
"get_integration_catalog_entry",
|
|
24
22
|
"get_integration_catalog_entry_model",
|
|
25
|
-
"list_integration_catalog",
|
|
26
23
|
"list_integration_catalog_models",
|
|
27
24
|
]
|
|
28
25
|
|
|
@@ -81,20 +78,11 @@ def _entry_supports_oauth(entry: dict[str, Any]) -> bool:
|
|
|
81
78
|
)
|
|
82
79
|
|
|
83
80
|
|
|
84
|
-
|
|
85
|
-
deprecated_in="0.10.0",
|
|
86
|
-
removed_in="0.12.0",
|
|
87
|
-
details="Use list_integration_catalog_models() for validated Pydantic models.",
|
|
88
|
-
)
|
|
89
|
-
def list_integration_catalog(
|
|
81
|
+
def _list_catalog_entries(
|
|
90
82
|
mcp: bool | None = None,
|
|
91
83
|
oauth: bool | None = None,
|
|
92
84
|
) -> list[dict[str, Any]]:
|
|
93
|
-
"""Return raw catalog dictionaries, optionally filtered by connector type.
|
|
94
|
-
|
|
95
|
-
Deprecated since v0.10.0 and scheduled for removal in v0.12.0.
|
|
96
|
-
Use :func:`list_integration_catalog_models` instead.
|
|
97
|
-
"""
|
|
85
|
+
"""Return raw catalog dictionaries, optionally filtered by connector type."""
|
|
98
86
|
result = []
|
|
99
87
|
for entry in _integrations():
|
|
100
88
|
if mcp is not None and _entry_supports_mcp(entry) != mcp:
|
|
@@ -111,34 +99,24 @@ def list_integration_catalog_models(
|
|
|
111
99
|
) -> list[IntegrationCatalogEntry]:
|
|
112
100
|
"""Return typed, validated integration catalog entries.
|
|
113
101
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
this function for a stable, validated contract.
|
|
102
|
+
This is the supported Python contract for reading the catalog. The raw
|
|
103
|
+
dictionary accessors it replaced were removed in v0.12.0.
|
|
117
104
|
"""
|
|
118
105
|
return [
|
|
119
106
|
IntegrationCatalogEntry.model_validate(entry)
|
|
120
|
-
for entry in
|
|
107
|
+
for entry in _list_catalog_entries(mcp=mcp, oauth=oauth)
|
|
121
108
|
]
|
|
122
109
|
|
|
123
110
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
removed_in="0.12.0",
|
|
127
|
-
details="Use get_integration_catalog_entry_model() for a validated Pydantic model.",
|
|
128
|
-
)
|
|
129
|
-
def get_integration_catalog_entry(id: str) -> dict[str, Any] | None:
|
|
130
|
-
"""Return one raw catalog dictionary by id, or ``None``.
|
|
131
|
-
|
|
132
|
-
Deprecated since v0.10.0 and scheduled for removal in v0.12.0.
|
|
133
|
-
Use :func:`get_integration_catalog_entry_model` instead.
|
|
134
|
-
"""
|
|
111
|
+
def _get_catalog_entry(id: str) -> dict[str, Any] | None:
|
|
112
|
+
"""Return one raw catalog dictionary by id, or ``None``."""
|
|
135
113
|
entry = _integration_by_id().get(id)
|
|
136
114
|
return copy.deepcopy(entry) if entry is not None else None
|
|
137
115
|
|
|
138
116
|
|
|
139
117
|
def get_integration_catalog_entry_model(id: str) -> IntegrationCatalogEntry | None:
|
|
140
118
|
"""Return one typed catalog entry, or ``None`` when it does not exist."""
|
|
141
|
-
entry =
|
|
119
|
+
entry = _get_catalog_entry(id)
|
|
142
120
|
return IntegrationCatalogEntry.model_validate(entry) if entry is not None else None
|
|
143
121
|
|
|
144
122
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const root = process.cwd();
|
|
5
|
+
const catalogDir = path.join(root, "automations", "catalog");
|
|
6
|
+
const outputPath = path.join(root, "automations", "catalog-index.js");
|
|
7
|
+
|
|
8
|
+
const directories = (await readdir(catalogDir, { withFileTypes: true }))
|
|
9
|
+
.filter((entry) => entry.isDirectory())
|
|
10
|
+
.map((entry) => entry.name)
|
|
11
|
+
.sort((left, right) => left.localeCompare(right));
|
|
12
|
+
|
|
13
|
+
const automations = await Promise.all(
|
|
14
|
+
directories.map(async (directory) => ({
|
|
15
|
+
directory,
|
|
16
|
+
entry: JSON.parse(
|
|
17
|
+
await readFile(path.join(catalogDir, directory, "manifest.json"), "utf8"),
|
|
18
|
+
),
|
|
19
|
+
})),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
automations.sort((left, right) => {
|
|
23
|
+
const rank = (right.entry.popularityRank ?? -1) - (left.entry.popularityRank ?? -1);
|
|
24
|
+
return rank || left.entry.id.localeCompare(right.entry.id);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const indexedAutomations = automations.map((automation, index) => ({
|
|
28
|
+
...automation,
|
|
29
|
+
importName: `entry${index}`,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
const imports = indexedAutomations
|
|
33
|
+
.map(
|
|
34
|
+
({ directory, importName }) =>
|
|
35
|
+
`import ${importName} from "./catalog/${directory}/manifest.json" with { type: "json" };`,
|
|
36
|
+
)
|
|
37
|
+
.join("\n");
|
|
38
|
+
const entries = indexedAutomations.map(({ importName }) => ` ${importName},`).join("\n");
|
|
39
|
+
|
|
40
|
+
const header = `// This file is auto-generated by scripts/build-automation-catalog.mjs.
|
|
41
|
+
// Do not edit it manually. To update it after changing automations/catalog/*/manifest.json,
|
|
42
|
+
// run: npm run build:automations
|
|
43
|
+
|
|
44
|
+
`;
|
|
45
|
+
const body = `${header}${imports}
|
|
46
|
+
|
|
47
|
+
export const AUTOMATION_CATALOG_ENTRIES = [
|
|
48
|
+
${entries}
|
|
49
|
+
];
|
|
50
|
+
`;
|
|
51
|
+
await writeFile(outputPath, body);
|
|
@@ -3,7 +3,6 @@ name: bitbucket
|
|
|
3
3
|
description: Bitbucket integration hub. Detects whether the repository is on Bitbucket Cloud or Bitbucket Data Center and directs you to the matching detailed skill (bitbucket-cloud or bitbucket-data-center). Use for any Bitbucket repository or pull request task.
|
|
4
4
|
triggers:
|
|
5
5
|
- bitbucket
|
|
6
|
-
- git
|
|
7
6
|
---
|
|
8
7
|
|
|
9
8
|
You are working with **Bitbucket**, which ships as two distinct products that behave
|
package/skills/github/SKILL.md
CHANGED
|
@@ -3,7 +3,6 @@ name: github
|
|
|
3
3
|
description: Interact with GitHub repositories, pull requests, issues, and workflows using the GITHUB_TOKEN environment variable and GitHub CLI. Use when working with code hosted on GitHub or managing GitHub resources.
|
|
4
4
|
triggers:
|
|
5
5
|
- github
|
|
6
|
-
- git
|
|
7
6
|
---
|
|
8
7
|
|
|
9
8
|
You have access to an environment variable, `GITHUB_TOKEN`, which allows you to interact with
|
package/skills/gitlab/SKILL.md
CHANGED
|
@@ -3,7 +3,6 @@ name: gitlab
|
|
|
3
3
|
description: Interact with GitLab repositories, merge requests, and APIs using the GITLAB_TOKEN environment variable. Use when working with code hosted on GitLab or managing GitLab resources.
|
|
4
4
|
triggers:
|
|
5
5
|
- gitlab
|
|
6
|
-
- git
|
|
7
6
|
---
|
|
8
7
|
|
|
9
8
|
You have access to an environment variable, `GITLAB_TOKEN`, which allows you to interact with
|