@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.
Files changed (49) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/AGENTS.md +2 -1
  3. package/MIGRATION.md +31 -0
  4. package/README.md +9 -7
  5. package/automations/README.md +300 -8
  6. package/automations/catalog/github-pr-reviewer/manifest.json +83 -0
  7. package/automations/catalog/github-repo-monitor/manifest.json +83 -0
  8. package/automations/catalog/incident-retrospective-drafter/manifest.json +100 -0
  9. package/automations/catalog/{jira-issue-to-pr.json → jira-issue-to-pr/manifest.json} +10 -5
  10. package/automations/catalog/{linear-triage-assistant.json → linear-triage-assistant/manifest.json} +8 -4
  11. package/automations/catalog/{research-brief-writer.json → research-brief-writer/manifest.json} +11 -5
  12. package/automations/catalog/{slack-channel-monitor.json → slack-channel-monitor/manifest.json} +7 -2
  13. package/automations/catalog/{slack-standup-digest.json → slack-standup-digest/manifest.json} +8 -5
  14. package/automations/catalog-index.js +23 -0
  15. package/automations/catalog.schema.json +272 -0
  16. package/automations/index.d.ts +232 -3
  17. package/automations/index.js +28 -18
  18. package/automations/interface.json +70 -0
  19. package/automations/interface.schema.json +243 -0
  20. package/index.d.ts +29 -2
  21. package/index.js +1 -1
  22. package/integrations/catalog/datadog.json +3 -3
  23. package/integrations/index.d.ts +1 -1
  24. package/package.json +8 -3
  25. package/pyproject.toml +1 -1
  26. package/python/openhands_extensions/__init__.py +0 -4
  27. package/python/openhands_extensions/_version.py +3 -2
  28. package/python/openhands_extensions/integrations.py +8 -30
  29. package/scripts/build-automation-catalog.mjs +51 -0
  30. package/skills/bitbucket/SKILL.md +0 -1
  31. package/skills/github/SKILL.md +0 -1
  32. package/skills/gitlab/SKILL.md +0 -1
  33. package/skills/index.js +5 -8
  34. package/skills/openhands-automation/SKILL.md +11 -0
  35. package/skills/openhands-automation/references/custom-automation.md +2 -0
  36. package/skills/openhands-automation/references/security.md +84 -0
  37. package/skills/openhands-sdk/SKILL.md +2 -0
  38. package/tests/fixtures/automations/capabilities.json +92 -0
  39. package/tests/fixtures/automations/github-pr-reviewer.json +251 -0
  40. package/tests/fixtures/automations/github-repo-monitor.json +180 -0
  41. package/tests/fixtures/automations/incident-retrospective-drafter.json +91 -0
  42. package/tests/test_automation_setup.py +526 -0
  43. package/tests/test_catalogs.py +37 -12
  44. package/tests/test_deprecations.py +39 -4
  45. package/tests/test_integration_catalog_in_sync.py +50 -60
  46. package/tests/test_interface_manifest.py +107 -0
  47. package/automations/catalog/github-pr-reviewer.json +0 -13
  48. package/automations/catalog/github-repo-monitor.json +0 -11
  49. package/automations/catalog/incident-retrospective-drafter.json +0 -15
@@ -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
- * `DD-API-KEY` / `DD-APPLICATION-KEY`). Values are sent verbatim as
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.11.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": "npm run build:skills && npm run build:integrations"
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/*.json": "./automations/catalog/*.json",
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openhands-extensions"
3
- version = "0.11.0"
3
+ version = "0.13.0"
4
4
  description = "OpenHands extensions, plugins, and skills (Python bindings for the integration catalog)"
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
@@ -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
- # x-release-please-version
20
- _FALLBACK_VERSION = "0.11.0"
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
- @deprecated(
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
- ``list_integration_catalog`` remains available for callers that need the
115
- original JSON-compatible dictionaries. New Python consumers should prefer
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 list_integration_catalog(mcp=mcp, oauth=oauth)
107
+ for entry in _list_catalog_entries(mcp=mcp, oauth=oauth)
121
108
  ]
122
109
 
123
110
 
124
- @deprecated(
125
- deprecated_in="0.10.0",
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 = get_integration_catalog_entry(id)
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
@@ -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
@@ -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