@openhands/extensions 0.5.0 → 0.7.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/.github/workflows/pypi-publish.yml +104 -0
- package/.github/workflows/tests.yml +6 -0
- package/.release-please-manifest.json +1 -1
- package/AGENTS.md +1 -1
- package/README.md +34 -4
- package/integrations/README.md +25 -9
- package/integrations/catalog/airtable.json +16 -10
- package/integrations/catalog/apify.json +0 -2
- package/integrations/catalog/asana.json +33 -0
- package/integrations/catalog/atlassian.json +5 -8
- package/integrations/catalog/bitbucket.json +34 -0
- package/integrations/catalog/box.json +34 -0
- package/integrations/catalog/brave-search.json +1 -2
- package/integrations/catalog/browser-mcp.json +0 -3
- package/integrations/catalog/canva.json +31 -0
- package/integrations/catalog/clickhouse.json +1 -2
- package/integrations/catalog/cloudflare-bindings.json +5 -8
- package/integrations/catalog/cloudflare-browser-rendering.json +5 -8
- package/integrations/catalog/cloudflare-builds.json +6 -9
- package/integrations/catalog/cloudflare-docs.json +4 -7
- package/integrations/catalog/cloudflare-observability.json +5 -8
- package/integrations/catalog/confluence.json +33 -0
- package/integrations/catalog/datadog.json +34 -0
- package/integrations/catalog/deepwiki.json +3 -7
- package/integrations/catalog/discord.json +34 -0
- package/integrations/catalog/elevenlabs.json +27 -28
- package/integrations/catalog/everything.json +0 -2
- package/integrations/catalog/exa.json +0 -2
- package/integrations/catalog/fetch.json +0 -2
- package/integrations/catalog/figma.json +35 -9
- package/integrations/catalog/filesystem.json +0 -3
- package/integrations/catalog/firecrawl.json +0 -2
- package/integrations/catalog/git.json +0 -3
- package/integrations/catalog/github.json +34 -10
- package/integrations/catalog/gmail.json +33 -0
- package/integrations/catalog/google-calendar.json +33 -0
- package/integrations/catalog/google-docs.json +33 -0
- package/integrations/catalog/google-drive.json +33 -0
- package/integrations/catalog/google-sheets.json +33 -0
- package/integrations/catalog/hubspot.json +34 -0
- package/integrations/catalog/huggingface.json +1 -2
- package/integrations/catalog/intercom.json +34 -0
- package/integrations/catalog/jira.json +33 -0
- package/integrations/catalog/kagi.json +2 -3
- package/integrations/catalog/linear.json +24 -16
- package/integrations/catalog/memory.json +0 -2
- package/integrations/catalog/microsoft-outlook.json +33 -0
- package/integrations/catalog/microsoft-teams.json +33 -0
- package/integrations/catalog/miro.json +42 -0
- package/integrations/catalog/monday.json +42 -0
- package/integrations/catalog/mongodb.json +1 -2
- package/integrations/catalog/neon.json +0 -2
- package/integrations/catalog/netlify.json +33 -0
- package/integrations/catalog/notion.json +30 -10
- package/integrations/catalog/obsidian.json +1 -3
- package/integrations/catalog/okta.json +33 -0
- package/integrations/catalog/onedrive.json +33 -0
- package/integrations/catalog/ordinal.json +33 -0
- package/integrations/catalog/paypal.json +5 -8
- package/integrations/catalog/playwright.json +0 -2
- package/integrations/catalog/posthog.json +38 -0
- package/integrations/catalog/quickbooks.json +86 -0
- package/integrations/catalog/redis.json +1 -3
- package/integrations/catalog/resend.json +1 -2
- package/integrations/catalog/salesforce.json +44 -0
- package/integrations/catalog/sentry.json +47 -14
- package/integrations/catalog/sequential-thinking.json +0 -2
- package/integrations/catalog/sharepoint.json +33 -0
- package/integrations/catalog/slack.json +49 -10
- package/integrations/catalog/stripe.json +39 -14
- package/integrations/catalog/supabase.json +31 -9
- package/integrations/catalog/tavily.json +0 -2
- package/integrations/catalog/time.json +0 -2
- package/integrations/catalog/trello.json +34 -0
- package/integrations/catalog/vercel.json +33 -0
- package/integrations/catalog/webflow.json +34 -0
- package/integrations/catalog/xero.json +33 -0
- package/integrations/catalog/zoom.json +33 -0
- package/integrations/catalog-index.js +151 -0
- package/integrations/catalog.schema.json +255 -0
- package/integrations/index.d.ts +49 -69
- package/integrations/index.js +36 -206
- package/marketplaces/openhands-extensions.json +40 -2
- package/package.json +4 -7
- package/plugins/cobol-modernization/README.md +3 -3
- package/plugins/cobol-modernization/skills/{cobol-modernization-overview → cobol-modernization}/SKILL.md +1 -1
- package/plugins/magic-test/skills/magic-word/SKILL.md +1 -0
- package/plugins/migration-scoring/README.md +1 -1
- package/plugins/pr-review/scripts/prompt.py +1 -1
- package/pyproject.toml +18 -3
- package/python/openhands_extensions/__init__.py +24 -0
- package/python/openhands_extensions/_version.py +25 -0
- package/python/openhands_extensions/integrations.py +92 -0
- package/release-please-config.json +2 -1
- package/scripts/build-integration-catalog.mjs +45 -0
- package/skills/agent-canvas-environment/.plugin/plugin.json +19 -0
- package/skills/agent-canvas-environment/README.md +5 -0
- package/skills/agent-canvas-environment/SKILL.md +202 -0
- package/skills/bitbucket/README.md +23 -40
- package/skills/bitbucket/SKILL.md +43 -31
- package/skills/bitbucket-cloud/.plugin/plugin.json +18 -0
- package/skills/bitbucket-cloud/README.md +15 -0
- package/skills/bitbucket-cloud/SKILL.md +46 -0
- package/skills/bitbucket-data-center/.plugin/plugin.json +19 -0
- package/skills/bitbucket-data-center/README.md +20 -0
- package/skills/bitbucket-data-center/SKILL.md +53 -0
- package/skills/code-review/SKILL.md +1 -1
- package/skills/code-review/references/supply-chain-security.md +2 -2
- package/skills/github-pr-reviewer/references/state-schema.md +18 -4
- package/skills/github-pr-reviewer/scripts/main.py +66 -12
- package/skills/github-repo-monitor/references/state-schema.md +11 -4
- package/skills/github-repo-monitor/scripts/main.py +67 -19
- package/skills/github-repo-monitor/tests/test_main.py +67 -8
- package/skills/index.js +34 -8
- package/skills/openhands-api/README.md +5 -1
- package/skills/openhands-api/SKILL.md +74 -5
- package/skills/openhands-automation/SKILL.md +3 -1
- package/skills/openhands-automation/references/custom-automation.md +166 -0
- package/skills/openhands-sdk/SKILL.md +8 -3
- package/skills/slack-channel-monitor/README.md +3 -3
- package/skills/slack-channel-monitor/SKILL.md +3 -2
- package/skills/slack-channel-monitor/references/state-schema.md +11 -4
- package/skills/slack-channel-monitor/scripts/main.py +111 -41
- package/tests/test_catalog_schema.py +47 -0
- package/tests/test_catalogs.py +47 -3
- package/tests/test_integration_catalog_in_sync.py +252 -0
- package/tests/test_slack_channel_monitor.py +142 -0
- package/tests/test_version_alignment.py +64 -0
- package/integrations/logos.d.ts +0 -5
- package/integrations/logos.js +0 -94
- package/integrations/oauth-provider-catalog.js +0 -553
- package/integrations/oauth-provider-registration-defaults.js +0 -610
- /package/plugins/cobol-modernization/skills/{mainfraime-removal → mainframe-removal}/SKILL.md +0 -0
- /package/plugins/cobol-modernization/skills/{mainfraime-removal → mainframe-removal}/references/cics-transformation-examples.md +0 -0
- /package/plugins/migration-scoring/skills/{migration-scoring-overview → migration-scoring}/SKILL.md +0 -0
package/integrations/index.js
CHANGED
|
@@ -1,212 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
import resend from "./catalog/resend.json" with { type: "json" };
|
|
34
|
-
import sentry from "./catalog/sentry.json" with { type: "json" };
|
|
35
|
-
import sequential_thinking from "./catalog/sequential-thinking.json" with { type: "json" };
|
|
36
|
-
import slack from "./catalog/slack.json" with { type: "json" };
|
|
37
|
-
import stripe from "./catalog/stripe.json" with { type: "json" };
|
|
38
|
-
import supabase from "./catalog/supabase.json" with { type: "json" };
|
|
39
|
-
import tavily from "./catalog/tavily.json" with { type: "json" };
|
|
40
|
-
import time from "./catalog/time.json" with { type: "json" };
|
|
41
|
-
import { listOAuthProviderCatalog } from "./oauth-provider-catalog.js";
|
|
42
|
-
export { listOAuthProviderCatalog } from "./oauth-provider-catalog.js";
|
|
43
|
-
export {
|
|
44
|
-
getOAuthProviderRegistrationDefaults,
|
|
45
|
-
hubspotMcpAuthorizationUrl,
|
|
46
|
-
hubspotMcpServerUrl,
|
|
47
|
-
hubspotMcpTokenUrl,
|
|
48
|
-
hubspotOptionalScopes,
|
|
49
|
-
hubspotRequiredScopes,
|
|
50
|
-
} from "./oauth-provider-registration-defaults.js";
|
|
51
|
-
|
|
52
|
-
const DIRECT_INTEGRATIONS = [
|
|
53
|
-
airtable,
|
|
54
|
-
apify,
|
|
55
|
-
atlassian,
|
|
56
|
-
brave_search,
|
|
57
|
-
browser_mcp,
|
|
58
|
-
clickhouse,
|
|
59
|
-
cloudflare_bindings,
|
|
60
|
-
cloudflare_browser_rendering,
|
|
61
|
-
cloudflare_builds,
|
|
62
|
-
cloudflare_docs,
|
|
63
|
-
cloudflare_observability,
|
|
64
|
-
deepwiki,
|
|
65
|
-
elevenlabs,
|
|
66
|
-
everything,
|
|
67
|
-
exa,
|
|
68
|
-
fetch,
|
|
69
|
-
figma,
|
|
70
|
-
filesystem,
|
|
71
|
-
firecrawl,
|
|
72
|
-
git,
|
|
73
|
-
github,
|
|
74
|
-
huggingface,
|
|
75
|
-
kagi,
|
|
76
|
-
linear,
|
|
77
|
-
memory,
|
|
78
|
-
mongodb,
|
|
79
|
-
neon,
|
|
80
|
-
notion,
|
|
81
|
-
obsidian,
|
|
82
|
-
paypal,
|
|
83
|
-
playwright,
|
|
84
|
-
redis,
|
|
85
|
-
resend,
|
|
86
|
-
sentry,
|
|
87
|
-
sequential_thinking,
|
|
88
|
-
slack,
|
|
89
|
-
stripe,
|
|
90
|
-
supabase,
|
|
91
|
-
tavily,
|
|
92
|
-
time,
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
const optionIdForDefaults = (defaults) => {
|
|
96
|
-
const strategy = defaults?.authStrategy ?? (defaults?.authorizationUrl || defaults?.tokenUrl ? "oauth2" : "oauth2");
|
|
97
|
-
if (strategy === "oauth2") return "oauth";
|
|
98
|
-
if (strategy === "none") return "none";
|
|
99
|
-
return "api";
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const providerConnectionOption = (provider) => {
|
|
103
|
-
const defaults = provider.registrationDefaults;
|
|
104
|
-
if (!defaults) return null;
|
|
105
|
-
const option = {
|
|
106
|
-
id: optionIdForDefaults(defaults),
|
|
107
|
-
provider: defaults.provider ?? (defaults.serverUrl ? "mcp" : "http"),
|
|
108
|
-
auth: {
|
|
109
|
-
strategy: defaults.authStrategy ?? provider.authStrategy ?? "oauth2",
|
|
110
|
-
authModes: defaults.authModes,
|
|
111
|
-
credentialLabel: defaults.credentialLabel,
|
|
112
|
-
credentialPlaceholder: defaults.credentialPlaceholder,
|
|
113
|
-
credentialHelp: defaults.credentialHelp,
|
|
114
|
-
apiKeyHeaderName: defaults.apiKeyHeaderName,
|
|
115
|
-
oauth: defaults.authorizationUrl || defaults.tokenUrl ? {
|
|
116
|
-
authorizationUrl: defaults.authorizationUrl,
|
|
117
|
-
tokenUrl: defaults.tokenUrl,
|
|
118
|
-
scopes: defaults.scopes ?? [],
|
|
119
|
-
optionalScopes: defaults.optionalScopes,
|
|
120
|
-
toolScopes: defaults.toolScopes,
|
|
121
|
-
scopeSeparator: defaults.scopeSeparator,
|
|
122
|
-
pkce: defaults.pkce,
|
|
123
|
-
clientAuthentication: defaults.clientAuthentication,
|
|
124
|
-
registrationUrl: defaults.registrationUrl,
|
|
125
|
-
additionalAuthorizationParams: defaults.additionalAuthorizationParams,
|
|
126
|
-
additionalTokenParams: defaults.additionalTokenParams,
|
|
127
|
-
} : undefined,
|
|
128
|
-
},
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
if (option.provider === "mcp") {
|
|
132
|
-
option.transport = { kind: "shttp", url: defaults.serverUrl };
|
|
133
|
-
} else {
|
|
134
|
-
option.http = {
|
|
135
|
-
apiBaseUrl: defaults.apiBaseUrl,
|
|
136
|
-
openApiUrl: defaults.openApiUrl,
|
|
137
|
-
defaultTool: defaults.toolName ? {
|
|
138
|
-
name: defaults.toolName,
|
|
139
|
-
description: defaults.toolDescription,
|
|
140
|
-
method: defaults.requestMethod,
|
|
141
|
-
path: defaults.requestPath,
|
|
142
|
-
scopes: defaults.toolScopes,
|
|
143
|
-
} : undefined,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return option;
|
|
1
|
+
/**
|
|
2
|
+
* Runtime integration catalog.
|
|
3
|
+
*
|
|
4
|
+
* The source of truth is the hand-authored `integrations/catalog/<id>.json`
|
|
5
|
+
* directory. `catalog-index.js` is generated from that directory so the JS
|
|
6
|
+
* package can statically import each JSON file without an aggregate JSON asset.
|
|
7
|
+
*/
|
|
8
|
+
import { INTEGRATION_CATALOG_ENTRIES } from "./catalog-index.js";
|
|
9
|
+
|
|
10
|
+
const clone = (value) => JSON.parse(JSON.stringify(value));
|
|
11
|
+
const INTEGRATIONS = INTEGRATION_CATALOG_ENTRIES;
|
|
12
|
+
const INTEGRATION_BY_ID = new Map(INTEGRATIONS.map((entry) => [entry.id, entry]));
|
|
13
|
+
|
|
14
|
+
const entrySupportsMcp = (entry) =>
|
|
15
|
+
entry.connectionOptions.some((option) => option.provider === "mcp");
|
|
16
|
+
|
|
17
|
+
const entrySupportsOauth = (entry) =>
|
|
18
|
+
entry.connectionOptions.some((option) => option.auth?.strategy === "oauth2");
|
|
19
|
+
|
|
20
|
+
export const listIntegrationCatalog = (filter) => {
|
|
21
|
+
const entries =
|
|
22
|
+
!filter || (filter.mcp === undefined && filter.oauth === undefined)
|
|
23
|
+
? INTEGRATIONS
|
|
24
|
+
: INTEGRATIONS.filter((entry) => {
|
|
25
|
+
const mcpOk =
|
|
26
|
+
filter.mcp === undefined || entrySupportsMcp(entry) === filter.mcp;
|
|
27
|
+
const oauthOk =
|
|
28
|
+
filter.oauth === undefined ||
|
|
29
|
+
entrySupportsOauth(entry) === filter.oauth;
|
|
30
|
+
return mcpOk && oauthOk;
|
|
31
|
+
});
|
|
32
|
+
return clone(entries);
|
|
148
33
|
};
|
|
149
34
|
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
return
|
|
153
|
-
id: provider.slug,
|
|
154
|
-
kind: option?.provider ?? "http",
|
|
155
|
-
name: provider.name,
|
|
156
|
-
description: provider.description,
|
|
157
|
-
categories: provider.categories,
|
|
158
|
-
appUrl: provider.appUrl,
|
|
159
|
-
docsUrl: provider.docsUrl,
|
|
160
|
-
notes: provider.notes,
|
|
161
|
-
catalogStatus: provider.availability,
|
|
162
|
-
managedConnectorSlug: provider.managedConnectorSlug,
|
|
163
|
-
authStrategy: provider.authStrategy,
|
|
164
|
-
popularityRank: provider.popularityRank,
|
|
165
|
-
registrationDefaults: provider.registrationDefaults,
|
|
166
|
-
...(option ? { defaultConnectionOptionId: option.id, connectionOptions: [option] } : { connectionOptions: [] }),
|
|
167
|
-
};
|
|
35
|
+
export const getIntegrationCatalogEntry = (id) => {
|
|
36
|
+
const entry = INTEGRATION_BY_ID.get(id);
|
|
37
|
+
return entry ? clone(entry) : undefined;
|
|
168
38
|
};
|
|
169
39
|
|
|
170
|
-
const
|
|
171
|
-
const options = new Map();
|
|
172
|
-
for (const option of left) options.set(option.id, option);
|
|
173
|
-
for (const option of right) {
|
|
174
|
-
if (!options.has(option.id)) options.set(option.id, option);
|
|
175
|
-
}
|
|
176
|
-
return [...options.values()];
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const mergeIntegration = (base, override) => {
|
|
180
|
-
const connectionOptions = mergeOptions(base.connectionOptions, override.connectionOptions);
|
|
181
|
-
return {
|
|
182
|
-
...base,
|
|
183
|
-
...override,
|
|
184
|
-
catalogStatus: base.catalogStatus ?? override.catalogStatus,
|
|
185
|
-
managedConnectorSlug: base.managedConnectorSlug ?? override.managedConnectorSlug,
|
|
186
|
-
authStrategy: base.authStrategy ?? override.authStrategy,
|
|
187
|
-
registrationDefaults: base.registrationDefaults ?? override.registrationDefaults,
|
|
188
|
-
connectionOptions,
|
|
189
|
-
defaultConnectionOptionId:
|
|
190
|
-
base.defaultConnectionOptionId ?? override.defaultConnectionOptionId ?? connectionOptions[0]?.id,
|
|
191
|
-
};
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
const entriesById = new Map();
|
|
195
|
-
for (const provider of listOAuthProviderCatalog().map(providerIntegration)) {
|
|
196
|
-
entriesById.set(provider.id, provider);
|
|
197
|
-
}
|
|
198
|
-
for (const direct of DIRECT_INTEGRATIONS) {
|
|
199
|
-
entriesById.set(
|
|
200
|
-
direct.id,
|
|
201
|
-
entriesById.has(direct.id)
|
|
202
|
-
? mergeIntegration(entriesById.get(direct.id), direct)
|
|
203
|
-
: direct,
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export const INTEGRATION_CATALOG = [...entriesById.values()].sort((a, b) => {
|
|
208
|
-
const rankDelta = (b.popularityRank ?? 0) - (a.popularityRank ?? 0);
|
|
209
|
-
return rankDelta || a.name.localeCompare(b.name);
|
|
210
|
-
});
|
|
40
|
+
export const INTEGRATION_CATALOG = clone(INTEGRATIONS);
|
|
211
41
|
|
|
212
42
|
export default INTEGRATION_CATALOG;
|
|
@@ -88,6 +88,19 @@
|
|
|
88
88
|
"openhands"
|
|
89
89
|
]
|
|
90
90
|
},
|
|
91
|
+
{
|
|
92
|
+
"name": "agent-canvas-environment",
|
|
93
|
+
"source": "./skills/agent-canvas-environment",
|
|
94
|
+
"description": "Work effectively inside a local Agent Canvas environment, including local agent-server auth, safe workspace hygiene, and local conversation delegation.",
|
|
95
|
+
"category": "development",
|
|
96
|
+
"keywords": [
|
|
97
|
+
"agent-canvas",
|
|
98
|
+
"openhands",
|
|
99
|
+
"local",
|
|
100
|
+
"conversation",
|
|
101
|
+
"delegation"
|
|
102
|
+
]
|
|
103
|
+
},
|
|
91
104
|
{
|
|
92
105
|
"name": "openhands-sdk",
|
|
93
106
|
"source": "./skills/openhands-sdk",
|
|
@@ -117,10 +130,35 @@
|
|
|
117
130
|
{
|
|
118
131
|
"name": "bitbucket",
|
|
119
132
|
"source": "./skills/bitbucket",
|
|
120
|
-
"description": "
|
|
133
|
+
"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.",
|
|
134
|
+
"category": "integration",
|
|
135
|
+
"keywords": [
|
|
136
|
+
"bitbucket",
|
|
137
|
+
"git",
|
|
138
|
+
"pull-request"
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"name": "bitbucket-cloud",
|
|
143
|
+
"source": "./skills/bitbucket-cloud",
|
|
144
|
+
"description": "Bitbucket Cloud (bitbucket.org) specifics — authenticate with BITBUCKET_TOKEN, use the REST API v2, workspace/repo_slug repositories, and the create_bitbucket_pr tool. Loaded on demand by the bitbucket skill once a Cloud environment is detected.",
|
|
145
|
+
"category": "integration",
|
|
146
|
+
"keywords": [
|
|
147
|
+
"bitbucket",
|
|
148
|
+
"bitbucket-cloud",
|
|
149
|
+
"git",
|
|
150
|
+
"pull-request"
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"name": "bitbucket-data-center",
|
|
155
|
+
"source": "./skills/bitbucket-data-center",
|
|
156
|
+
"description": "Bitbucket Data Center (self-hosted Bitbucket Server) specifics — authenticate with BITBUCKET_DATA_CENTER_TOKEN, use the REST API 1.0, PROJECT/repo_slug repositories, scm/ git remotes, and the create_bitbucket_data_center_pr tool. Loaded on demand by the bitbucket skill once a Data Center environment is detected.",
|
|
121
157
|
"category": "integration",
|
|
122
158
|
"keywords": [
|
|
123
159
|
"bitbucket",
|
|
160
|
+
"bitbucket-data-center",
|
|
161
|
+
"bitbucket-server",
|
|
124
162
|
"git",
|
|
125
163
|
"pull-request"
|
|
126
164
|
]
|
|
@@ -404,7 +442,7 @@
|
|
|
404
442
|
{
|
|
405
443
|
"name": "openhands-api",
|
|
406
444
|
"source": "./skills/openhands-api",
|
|
407
|
-
"description": "Use the OpenHands Cloud REST API (V1) to create and manage
|
|
445
|
+
"description": "Use the OpenHands Cloud REST API (V1) and agent-server APIs to create and manage Cloud or local backend conversations, including multi-conversation delegation workflows. Includes minimal Python and TypeScript clients under scripts/.",
|
|
408
446
|
"category": "development",
|
|
409
447
|
"keywords": [
|
|
410
448
|
"openhands",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openhands/extensions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Public OpenHands extension catalogs for skills, plugins, integrations, and automation templates.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"url": "https://github.com/OpenHands/extensions"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build:skills": "node scripts/build-skills-catalog.mjs"
|
|
12
|
+
"build:skills": "node scripts/build-skills-catalog.mjs",
|
|
13
|
+
"build:integrations": "node scripts/build-integration-catalog.mjs",
|
|
14
|
+
"build": "npm run build:skills && npm run build:integrations"
|
|
13
15
|
},
|
|
14
16
|
"engines": {
|
|
15
17
|
"node": ">=18.20.0"
|
|
@@ -39,11 +41,6 @@
|
|
|
39
41
|
"import": "./integrations/index.js",
|
|
40
42
|
"default": "./integrations/index.js"
|
|
41
43
|
},
|
|
42
|
-
"./integrations/logos": {
|
|
43
|
-
"types": "./integrations/logos.d.ts",
|
|
44
|
-
"import": "./integrations/logos.js",
|
|
45
|
-
"default": "./integrations/logos.js"
|
|
46
|
-
},
|
|
47
44
|
"./integrations/catalog/*.json": "./integrations/catalog/*.json",
|
|
48
45
|
"./skills": {
|
|
49
46
|
"types": "./skills/index.d.ts",
|
|
@@ -36,11 +36,11 @@ plugins/cobol-modernization/
|
|
|
36
36
|
└── skills/ # Workflow phase skills
|
|
37
37
|
├── build-setup/ # Phase 1: Build environment
|
|
38
38
|
│ └── SKILL.md
|
|
39
|
-
├── cobol-modernization
|
|
39
|
+
├── cobol-modernization/ # Plugin overview
|
|
40
40
|
│ └── SKILL.md
|
|
41
41
|
├── mainframe-planning/ # Phase 2: Transformation planning
|
|
42
42
|
│ └── SKILL.md
|
|
43
|
-
├──
|
|
43
|
+
├── mainframe-removal/ # Phase 3: Mainframe dependency removal
|
|
44
44
|
│ ├── SKILL.md
|
|
45
45
|
│ └── references/
|
|
46
46
|
│ └── cics-transformation-examples.md
|
|
@@ -139,7 +139,7 @@ You can also run individual phases by using the specific skill definitions:
|
|
|
139
139
|
|
|
140
140
|
- See [skills/build-setup/SKILL.md](skills/build-setup/SKILL.md) for build setup only
|
|
141
141
|
- See [skills/mainframe-planning/SKILL.md](skills/mainframe-planning/SKILL.md) for planning
|
|
142
|
-
- See [skills/
|
|
142
|
+
- See [skills/mainframe-removal/SKILL.md](skills/mainframe-removal/SKILL.md) for mainframe removal
|
|
143
143
|
- See [skills/to-java-migration/SKILL.md](skills/to-java-migration/SKILL.md) for Java translation
|
|
144
144
|
|
|
145
145
|
## Troubleshooting
|
|
@@ -67,7 +67,7 @@ Creates a transformation guide without modifying code:
|
|
|
67
67
|
|
|
68
68
|
### Phase 3: Mainframe Removal
|
|
69
69
|
|
|
70
|
-
See [../
|
|
70
|
+
See [../mainframe-removal/SKILL.md](../mainframe-removal/SKILL.md)
|
|
71
71
|
|
|
72
72
|
Applies the transformation guide:
|
|
73
73
|
- Replaces EXEC CICS commands with file I/O
|
|
@@ -34,7 +34,7 @@ uv run python -m lc_sdk_examples.migration_scoring \
|
|
|
34
34
|
plugins/migration-scoring/
|
|
35
35
|
├── README.md # This file
|
|
36
36
|
└── skills/ # Workflow phase skills
|
|
37
|
-
├── migration-scoring
|
|
37
|
+
├── migration-scoring/ # Plugin overview
|
|
38
38
|
│ └── SKILL.md
|
|
39
39
|
├── migration-mapping/ # Phase 1: Source-to-target mapping
|
|
40
40
|
│ └── SKILL.md
|
|
@@ -76,7 +76,7 @@ PROMPT = """{skill_trigger}
|
|
|
76
76
|
|
|
77
77
|
When posting a review, keep the review body brief unless your active review instructions require a longer structured format.
|
|
78
78
|
|
|
79
|
-
For dependency update PRs, do **NOT** approve a target version that was published less than 7 days ago.
|
|
79
|
+
For dependency update PRs, do **NOT** approve a target version that was published less than 7 days ago. First-party packages maintained by the same organization as the reviewed repository are intentionally excluded from this 7-day waiting rule, but still scrutinize them for supply-chain risk.
|
|
80
80
|
|
|
81
81
|
Review the PR changes below and identify issues that need to be addressed.
|
|
82
82
|
|
package/pyproject.toml
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
[project]
|
|
2
|
-
name = "extensions"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "OpenHands extensions, plugins, and skills"
|
|
2
|
+
name = "openhands-extensions"
|
|
3
|
+
version = "0.7.0"
|
|
4
|
+
description = "OpenHands extensions, plugins, and skills (Python bindings for the integration catalog)"
|
|
5
5
|
requires-python = ">=3.12"
|
|
6
6
|
|
|
7
|
+
[build-system]
|
|
8
|
+
requires = ["hatchling>=1.21"]
|
|
9
|
+
build-backend = "hatchling.build"
|
|
10
|
+
|
|
11
|
+
[tool.hatch.build.targets.wheel]
|
|
12
|
+
packages = ["python/openhands_extensions"]
|
|
13
|
+
|
|
14
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
15
|
+
"integrations/catalog" = "openhands_extensions/catalog"
|
|
16
|
+
|
|
7
17
|
[dependency-groups]
|
|
8
18
|
test = [
|
|
9
19
|
"pytest>=8.0",
|
|
10
20
|
"requests>=2.31",
|
|
11
21
|
"openhands-sdk>=0.3",
|
|
22
|
+
"jsonschema>=4.23",
|
|
12
23
|
]
|
|
24
|
+
|
|
25
|
+
[tool.pytest.ini_options]
|
|
26
|
+
pythonpath = ["python"]
|
|
27
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Python bindings for the OpenHands extensions catalogs.
|
|
2
|
+
|
|
3
|
+
Mirrors the JavaScript package (``@openhands/extensions``): both read the same
|
|
4
|
+
hand-authored ``integrations/catalog/<id>.json`` files. The JavaScript package
|
|
5
|
+
uses a generated static import index; the Python package loads the packaged
|
|
6
|
+
individual JSON files directly. See ``integrations.py`` for the catalog API,
|
|
7
|
+
including filtering by connector type (mcp / oauth).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from ._version import __version__
|
|
13
|
+
from .integrations import (
|
|
14
|
+
INTEGRATION_CATALOG_SNAPSHOT,
|
|
15
|
+
get_integration_catalog_entry,
|
|
16
|
+
list_integration_catalog,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"INTEGRATION_CATALOG_SNAPSHOT",
|
|
21
|
+
"__version__",
|
|
22
|
+
"get_integration_catalog_entry",
|
|
23
|
+
"list_integration_catalog",
|
|
24
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Package version, derived from installed package metadata.
|
|
2
|
+
|
|
3
|
+
The single source of truth is ``pyproject.toml``'s ``[project].version``
|
|
4
|
+
(which ``release-please`` bumps together with ``package.json`` via
|
|
5
|
+
``release-please-config.json`` -> ``extra-files``). At runtime we read the
|
|
6
|
+
installed distribution metadata via ``importlib.metadata`` so there is no
|
|
7
|
+
second hand-maintained version string. ``_FALLBACK_VERSION`` is only used
|
|
8
|
+
when the package is imported without being installed (e.g. straight off a
|
|
9
|
+
source checkout with no build), and ``tests/test_version_alignment.py``
|
|
10
|
+
asserts it stays in lock-step with ``pyproject.toml`` / ``package.json``.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
16
|
+
|
|
17
|
+
#: Fallback used only when the package is not installed (no dist metadata).
|
|
18
|
+
#: Kept in lock-step with pyproject.toml/package.json by the version test.
|
|
19
|
+
# x-release-please-update-version
|
|
20
|
+
_FALLBACK_VERSION = "0.7.0"
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
__version__: str = version("openhands-extensions")
|
|
24
|
+
except PackageNotFoundError: # not installed (e.g. raw source checkout)
|
|
25
|
+
__version__ = _FALLBACK_VERSION
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Python bindings for the OpenHands extensions integration catalog.
|
|
2
|
+
|
|
3
|
+
The source of truth is the hand-authored ``integrations/catalog/<id>.json``
|
|
4
|
+
directory. Wheels include those individual JSON files directly; no aggregate
|
|
5
|
+
catalog JSON is authored or packaged.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import copy
|
|
11
|
+
import json
|
|
12
|
+
from functools import lru_cache
|
|
13
|
+
from importlib import resources
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Iterable
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"INTEGRATION_CATALOG_SNAPSHOT",
|
|
19
|
+
"get_integration_catalog_entry",
|
|
20
|
+
"list_integration_catalog",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _repo_catalog_dir() -> Path:
|
|
25
|
+
return Path(__file__).resolve().parents[2] / "integrations" / "catalog"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _catalog_files() -> Iterable[Any]:
|
|
29
|
+
packaged = resources.files(__package__).joinpath("catalog")
|
|
30
|
+
if packaged.is_dir():
|
|
31
|
+
return sorted(
|
|
32
|
+
(path for path in packaged.iterdir() if path.name.endswith(".json")),
|
|
33
|
+
key=lambda path: path.name,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
repo_catalog = _repo_catalog_dir()
|
|
37
|
+
return sorted(repo_catalog.glob("*.json"), key=lambda path: path.name)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _read_json(path: Any) -> dict[str, Any]:
|
|
41
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@lru_cache(maxsize=1)
|
|
45
|
+
def _integrations() -> tuple[dict[str, Any], ...]:
|
|
46
|
+
entries = [_read_json(path) for path in _catalog_files()]
|
|
47
|
+
entries.sort(
|
|
48
|
+
key=lambda entry: (-(entry.get("popularityRank") if entry.get("popularityRank") is not None else -1), entry["id"]),
|
|
49
|
+
)
|
|
50
|
+
return tuple(entries)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@lru_cache(maxsize=1)
|
|
54
|
+
def _integration_by_id() -> dict[str, dict[str, Any]]:
|
|
55
|
+
return {entry["id"]: entry for entry in _integrations()}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _entry_supports_mcp(entry: dict[str, Any]) -> bool:
|
|
59
|
+
return any(option.get("provider") == "mcp" for option in entry.get("connectionOptions", []))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _entry_supports_oauth(entry: dict[str, Any]) -> bool:
|
|
63
|
+
return any(
|
|
64
|
+
option.get("auth", {}).get("strategy") == "oauth2"
|
|
65
|
+
for option in entry.get("connectionOptions", [])
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def list_integration_catalog(
|
|
70
|
+
mcp: bool | None = None,
|
|
71
|
+
oauth: bool | None = None,
|
|
72
|
+
) -> list[dict[str, Any]]:
|
|
73
|
+
"""Return the integration catalog, optionally filtered by connector type."""
|
|
74
|
+
result = []
|
|
75
|
+
for entry in _integrations():
|
|
76
|
+
if mcp is not None and _entry_supports_mcp(entry) != mcp:
|
|
77
|
+
continue
|
|
78
|
+
if oauth is not None and _entry_supports_oauth(entry) != oauth:
|
|
79
|
+
continue
|
|
80
|
+
result.append(copy.deepcopy(entry))
|
|
81
|
+
return result
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_integration_catalog_entry(id: str) -> dict[str, Any] | None:
|
|
85
|
+
"""Return one integration catalog entry by id, or ``None``."""
|
|
86
|
+
entry = _integration_by_id().get(id)
|
|
87
|
+
return copy.deepcopy(entry) if entry is not None else None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
INTEGRATION_CATALOG_SNAPSHOT: dict[str, Any] = {
|
|
91
|
+
"integrations": copy.deepcopy(list(_integrations()))
|
|
92
|
+
}
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"include-component-in-tag": false,
|
|
10
10
|
"skip-changelog": true,
|
|
11
11
|
"extra-files": [
|
|
12
|
-
{ "type": "toml", "path": "pyproject.toml", "jsonpath": "$.project.version" }
|
|
12
|
+
{ "type": "toml", "path": "pyproject.toml", "jsonpath": "$.project.version" },
|
|
13
|
+
"python/openhands_extensions/_version.py"
|
|
13
14
|
]
|
|
14
15
|
}
|
|
15
16
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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, "integrations", "catalog");
|
|
6
|
+
const outputPath = path.join(root, "integrations", "catalog-index.js");
|
|
7
|
+
|
|
8
|
+
const files = (await readdir(catalogDir))
|
|
9
|
+
.filter((file) => file.endsWith(".json"))
|
|
10
|
+
.sort((left, right) => left.localeCompare(right));
|
|
11
|
+
|
|
12
|
+
const integrations = await Promise.all(
|
|
13
|
+
files.map(async (file) => ({
|
|
14
|
+
file,
|
|
15
|
+
entry: JSON.parse(await readFile(path.join(catalogDir, file), "utf8")),
|
|
16
|
+
})),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
integrations.sort((left, right) => {
|
|
20
|
+
const rank = (right.entry.popularityRank ?? -1) - (left.entry.popularityRank ?? -1);
|
|
21
|
+
return rank || left.entry.id.localeCompare(right.entry.id);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const indexedIntegrations = integrations.map((integration, index) => ({
|
|
25
|
+
...integration,
|
|
26
|
+
importName: `entry${index}`,
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
const imports = indexedIntegrations
|
|
30
|
+
.map(({ file, importName }) => `import ${importName} from "./catalog/${file}" with { type: "json" };`)
|
|
31
|
+
.join("\n");
|
|
32
|
+
const entries = indexedIntegrations.map(({ importName }) => ` ${importName},`).join("\n");
|
|
33
|
+
|
|
34
|
+
const header = `// This file is auto-generated by scripts/build-integration-catalog.mjs.
|
|
35
|
+
// Do not edit it manually. To update it after changing integrations/catalog/*.json,
|
|
36
|
+
// run: npm run build:integrations
|
|
37
|
+
|
|
38
|
+
`;
|
|
39
|
+
const body = `${header}${imports}
|
|
40
|
+
|
|
41
|
+
export const INTEGRATION_CATALOG_ENTRIES = [
|
|
42
|
+
${entries}
|
|
43
|
+
];
|
|
44
|
+
`;
|
|
45
|
+
await writeFile(outputPath, body);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-canvas-environment",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Work effectively inside a local Agent Canvas environment, including local agent-server auth, frontend/backend port discovery, safe workspace hygiene, and local conversation delegation.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "OpenHands",
|
|
7
|
+
"email": "contact@all-hands.dev"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/OpenHands/extensions",
|
|
10
|
+
"repository": "https://github.com/OpenHands/extensions",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"agent-canvas",
|
|
14
|
+
"openhands",
|
|
15
|
+
"local",
|
|
16
|
+
"conversation",
|
|
17
|
+
"delegation"
|
|
18
|
+
]
|
|
19
|
+
}
|