@openhands/extensions 0.13.0 → 0.14.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/README.md +3 -2
- package/automations/catalog/upstream-fork-sync/manifest.json +85 -0
- package/automations/catalog-index.js +3 -1
- package/marketplaces/openhands-extensions.json +14 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/python/openhands_extensions/_version.py +1 -1
- package/skills/index.js +8 -0
- package/skills/upstream-fork-sync/.plugin/plugin.json +21 -0
- package/skills/upstream-fork-sync/README.md +38 -0
- package/skills/upstream-fork-sync/SKILL.md +122 -0
- package/skills/upstream-fork-sync/commands/upstream-fork-sync-setup.md +8 -0
- package/tests/fixtures/automations/upstream-fork-sync.json +223 -0
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ The JS and Python versions are kept in lock-step by `release-please` and guarded
|
|
|
89
89
|
## Extensions Catalog
|
|
90
90
|
|
|
91
91
|
<!-- BEGIN AUTO-GENERATED CATALOG -->
|
|
92
|
-
This repository contains **2 marketplace(s)** with **
|
|
92
|
+
This repository contains **2 marketplace(s)** with **64 extensions** (54 skills, 10 plugins).
|
|
93
93
|
|
|
94
94
|
### large-codebase
|
|
95
95
|
|
|
@@ -108,7 +108,7 @@ OpenHands skills for interacting, improving, and refactoring large codebases
|
|
|
108
108
|
|
|
109
109
|
Official skills and plugins for OpenHands — the open-source AI software engineer.
|
|
110
110
|
|
|
111
|
-
**
|
|
111
|
+
**60 extensions** (52 skills, 8 plugins)
|
|
112
112
|
|
|
113
113
|
| Name | Type | Description | Commands |
|
|
114
114
|
|------|------|-------------|----------|
|
|
@@ -168,6 +168,7 @@ Official skills and plugins for OpenHands — the open-source AI software engine
|
|
|
168
168
|
| swift-linux | skill | Install and configure Swift programming language on Debian Linux for server-side development. Use when building Swift... | — |
|
|
169
169
|
| technical-writing | skill | Write and revise technical explanations in flowing, direct, conversational prose that stays concise without becoming ... | — |
|
|
170
170
|
| theme-factory | skill | Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc.... | — |
|
|
171
|
+
| upstream-fork-sync | skill | Keep a long-lived fork in sync with its upstream. Creates a cron automation that fetches upstream changes, rebases lo... | `/upstream-fork-sync:setup` |
|
|
171
172
|
| uv | skill | Common project, dependency, and environment operations using uv. | — |
|
|
172
173
|
| vercel | skill | Deploy and manage applications on Vercel, including preview deployments and deployment protection. | — |
|
|
173
174
|
| vulnerability-remediation | plugin | Automated security vulnerability scanning and AI-powered remediation. Scans repositories, skips when no issues found,... | — |
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "upstream-fork-sync",
|
|
3
|
+
"name": "Upstream fork sync",
|
|
4
|
+
"category": "Developer tools",
|
|
5
|
+
"description": "Keep a long-lived fork current with its upstream by re-running a nightly job that fetches upstream changes, rebases local customizations on top, verifies the software still works, and replaces the running version when it does.",
|
|
6
|
+
"requires": {
|
|
7
|
+
"integrations": {
|
|
8
|
+
"github": {
|
|
9
|
+
"message": "Used to fetch upstream changes and push the rebased fork."
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"features": [
|
|
13
|
+
"repoClone",
|
|
14
|
+
"presetPrompt"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"popularityRank": 80,
|
|
18
|
+
"estimatedSetupMinutes": 4,
|
|
19
|
+
"exampleImplementation": "Trigger: cron, nightly (configurable)\nRequired secret: GITHUB_PERSONAL_ACCESS_TOKEN\n\n1. Clone the fork repository and fetch the latest from its upstream remote.\n2. Rebase every local customization commit on top of the newest upstream HEAD.\n3. Run the configured verification command to confirm the software works as intended.\n4. If verification passes, force-push the rebased branch and replace the currently deployed version with the freshly built one.\n5. If verification fails, leave the running version untouched and report the conflict.",
|
|
20
|
+
"setup": {
|
|
21
|
+
"version": "1.0",
|
|
22
|
+
"mode": "direct",
|
|
23
|
+
"form": {
|
|
24
|
+
"triggers": {
|
|
25
|
+
"cron": {
|
|
26
|
+
"schedule": {
|
|
27
|
+
"type": "cron",
|
|
28
|
+
"label": "Sync schedule",
|
|
29
|
+
"help": "How often to fetch upstream and rebase local changes. Nightly is the default.",
|
|
30
|
+
"default": "0 3 * * *",
|
|
31
|
+
"required": true
|
|
32
|
+
},
|
|
33
|
+
"timezone": {
|
|
34
|
+
"type": "timezone",
|
|
35
|
+
"label": "Timezone",
|
|
36
|
+
"help": "Timezone the schedule is interpreted in.",
|
|
37
|
+
"default": "UTC",
|
|
38
|
+
"required": true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"args": {
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "repo-picker",
|
|
45
|
+
"label": "Repository",
|
|
46
|
+
"help": "The long-lived fork to keep synchronized with upstream.",
|
|
47
|
+
"provider": "github",
|
|
48
|
+
"required": true
|
|
49
|
+
},
|
|
50
|
+
"upstreamRemote": {
|
|
51
|
+
"type": "text",
|
|
52
|
+
"label": "Upstream remote",
|
|
53
|
+
"help": "The remote (URL or owner/repo) the fork tracks. Defaults to the repository's parent.",
|
|
54
|
+
"default": "",
|
|
55
|
+
"required": false,
|
|
56
|
+
"constraints": {
|
|
57
|
+
"maxLength": 255
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"localChanges": {
|
|
61
|
+
"type": "text",
|
|
62
|
+
"label": "Local changes",
|
|
63
|
+
"help": "Plain-language description of the customizations to preserve across rebase.",
|
|
64
|
+
"default": "",
|
|
65
|
+
"required": false,
|
|
66
|
+
"constraints": {
|
|
67
|
+
"maxLength": 2000
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"verifyCommand": {
|
|
71
|
+
"type": "text",
|
|
72
|
+
"label": "Verify command",
|
|
73
|
+
"help": "The command that confirms the software works as intended (e.g. `make test`). If blank, the agent infers a sensible check.",
|
|
74
|
+
"default": "",
|
|
75
|
+
"required": false,
|
|
76
|
+
"constraints": {
|
|
77
|
+
"maxLength": 500
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"prompt": "Fetch the latest upstream changes for the fork {{form.repository}} and rebase all local changes on top of upstream. Local changes to preserve: {{form.localChanges}}. Check that the software works as intended; if it does, replace the current version, otherwise leave the running version untouched and report what failed.",
|
|
83
|
+
"message": "This deployment cannot run the scheduled fork-sync automation directly. Set it up in this conversation instead: confirm the fork repository, the upstream remote, the local changes to preserve, the verification command, and the nightly schedule, then create the automation."
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -9,7 +9,8 @@ import entry3 from "./catalog/slack-channel-monitor/manifest.json" with { type:
|
|
|
9
9
|
import entry4 from "./catalog/linear-triage-assistant/manifest.json" with { type: "json" };
|
|
10
10
|
import entry5 from "./catalog/jira-issue-to-pr/manifest.json" with { type: "json" };
|
|
11
11
|
import entry6 from "./catalog/research-brief-writer/manifest.json" with { type: "json" };
|
|
12
|
-
import entry7 from "./catalog/
|
|
12
|
+
import entry7 from "./catalog/upstream-fork-sync/manifest.json" with { type: "json" };
|
|
13
|
+
import entry8 from "./catalog/incident-retrospective-drafter/manifest.json" with { type: "json" };
|
|
13
14
|
|
|
14
15
|
export const AUTOMATION_CATALOG_ENTRIES = [
|
|
15
16
|
entry0,
|
|
@@ -20,4 +21,5 @@ export const AUTOMATION_CATALOG_ENTRIES = [
|
|
|
20
21
|
entry5,
|
|
21
22
|
entry6,
|
|
22
23
|
entry7,
|
|
24
|
+
entry8,
|
|
23
25
|
];
|
|
@@ -603,6 +603,20 @@
|
|
|
603
603
|
"venv"
|
|
604
604
|
]
|
|
605
605
|
},
|
|
606
|
+
{
|
|
607
|
+
"name": "upstream-fork-sync",
|
|
608
|
+
"source": "./skills/upstream-fork-sync",
|
|
609
|
+
"description": "Keep a long-lived fork in sync with its upstream. Creates a cron automation that fetches upstream changes, rebases local customizations on top, verifies the software works, and replaces the running version when the rebase is clean.",
|
|
610
|
+
"category": "integration",
|
|
611
|
+
"keywords": [
|
|
612
|
+
"fork",
|
|
613
|
+
"upstream",
|
|
614
|
+
"rebase",
|
|
615
|
+
"sync",
|
|
616
|
+
"automation",
|
|
617
|
+
"long-lived-fork"
|
|
618
|
+
]
|
|
619
|
+
},
|
|
606
620
|
{
|
|
607
621
|
"name": "vercel",
|
|
608
622
|
"source": "./skills/vercel",
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -18,7 +18,7 @@ from importlib.metadata import PackageNotFoundError, version
|
|
|
18
18
|
#: Kept in lock-step with pyproject.toml/package.json by the version test.
|
|
19
19
|
#: release-please only rewrites a semver found on the annotated line itself, so
|
|
20
20
|
#: the annotation must stay on the assignment; on its own line it is a no-op.
|
|
21
|
-
_FALLBACK_VERSION = "0.
|
|
21
|
+
_FALLBACK_VERSION = "0.14.0" # x-release-please-version
|
|
22
22
|
|
|
23
23
|
try:
|
|
24
24
|
__version__: str = version("openhands-extensions")
|
package/skills/index.js
CHANGED
|
@@ -499,6 +499,14 @@ export const SKILLS_CATALOG = [
|
|
|
499
499
|
"content": "# Theme Factory Skill\n\nThis skill provides a curated collection of professional font and color themes themes, each with carefully selected color palettes and font pairings. Once a theme is chosen, it can be applied to any artifact.\n\n## Purpose\n\nTo apply consistent, professional styling to presentation slide decks, use this skill. Each theme includes:\n- A cohesive color palette with hex codes\n- Complementary font pairings for headers and body text\n- A distinct visual identity suitable for different contexts and audiences\n\n## Usage Instructions\n\nTo apply styling to a slide deck or other artifact:\n\n1. **Show the theme showcase**: Display the `theme-showcase.pdf` file to allow users to see all available themes visually. Do not make any modifications to it; simply show the file for viewing.\n2. **Ask for their choice**: Ask which theme to apply to the deck\n3. **Wait for selection**: Get explicit confirmation about the chosen theme\n4. **Apply the theme**: Once a theme has been chosen, apply the selected theme's colors and fonts to the deck/artifact\n\n## Themes Available\n\nThe following 10 themes are available, each showcased in `theme-showcase.pdf`:\n\n1. **Ocean Depths** - Professional and calming maritime theme\n2. **Sunset Boulevard** - Warm and vibrant sunset colors\n3. **Forest Canopy** - Natural and grounded earth tones\n4. **Modern Minimalist** - Clean and contemporary grayscale\n5. **Golden Hour** - Rich and warm autumnal palette\n6. **Arctic Frost** - Cool and crisp winter-inspired theme\n7. **Desert Rose** - Soft and sophisticated dusty tones\n8. **Tech Innovation** - Bold and modern tech aesthetic\n9. **Botanical Garden** - Fresh and organic garden colors\n10. **Midnight Galaxy** - Dramatic and cosmic deep tones\n\n## Theme Details\n\nEach theme is defined in the `themes/` directory with complete specifications including:\n- Cohesive color palette with hex codes\n- Complementary font pairings for headers and body text\n- Distinct visual identity suitable for different contexts and audiences\n\n## Application Process\n\nAfter a preferred theme is selected:\n1. Read the corresponding theme file from the `themes/` directory\n2. Apply the specified colors and fonts consistently throughout the deck\n3. Ensure proper contrast and readability\n4. Maintain the theme's visual identity across all slides\n\n## Create your Own Theme\nTo handle cases where none of the existing themes work for an artifact, create a custom theme. Based on provided inputs, generate a new theme similar to the ones above. Give the theme a similar name describing what the font/color combinations represent. Use any basic description provided to choose appropriate colors/fonts. After generating the theme, show it for review and verification. Following that, apply the theme as described above.",
|
|
500
500
|
"license": "Complete terms in LICENSE.txt"
|
|
501
501
|
},
|
|
502
|
+
{
|
|
503
|
+
"name": "upstream-fork-sync",
|
|
504
|
+
"description": "This skill should be used when the user asks to \"keep a fork in sync\", \"rebase local changes on upstream\", \"sync my fork nightly\", \"long-lived fork\", or \"automate upstream rebases\". Guides the user through creating a cron automation that fetches upstream changes, rebases local customizations on top, verifies the software works, and replaces the running version when the rebase is clean.",
|
|
505
|
+
"triggers": [
|
|
506
|
+
"/upstream-fork-sync:setup"
|
|
507
|
+
],
|
|
508
|
+
"content": "# Upstream Fork Sync Automation\n\nCreate a cron automation that keeps a long-lived fork current with its\nupstream source. On every run it fetches the latest upstream changes,\nrebases the fork's local customizations on top, runs a verification check,\nand replaces the deployed version only when the software still works.\n\nThis implements the \"long-lived fork\" pattern: instead of repeatedly\nre-deriving a customization, the local changes are preserved across\nupstream releases and kept working automatically.\n\nWindows PowerShell equivalents for the setup, packaging, upload, and API-check\nshell snippets are in `references/windows.md`.\n\n---\n\n## Prerequisites\n\n### Required secret\n\nVerify that the following secret is set in **OpenHands Settings -> Secrets**:\n\n| Secret name | Token type | Minimum permissions |\n|---|---|---|\n| `GITHUB_PERSONAL_ACCESS_TOKEN` | Classic PAT | `repo` |\n| `GITHUB_PERSONAL_ACCESS_TOKEN` | Fine-grained PAT | Contents: Read and Write, Metadata: Read |\n\nCheck with:\n```bash\ncurl -s https://api.github.com/user \\\n -H \"Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN\" \\\n | python3 -c \"import json,sys; d=json.load(sys.stdin); print(d.get('login') or d.get('message'))\"\n```\n\nIf the token is missing or invalid, inform the user and stop.\n\n---\n\n## Setup Workflow\n\nFollow these steps in order.\n\n### Step 1 - Verify `GITHUB_PERSONAL_ACCESS_TOKEN`\n\nRun the `curl` check above.\n\n- If absent: *\"GITHUB_PERSONAL_ACCESS_TOKEN is not set. Please add it in\n OpenHands Settings -> Secrets.\"* Stop.\n- If the API returns `{\"message\": \"Bad credentials\"}`: tell the user the\n token is invalid and ask them to update it. Stop.\n\n### Step 2 - Collect configuration\n\nConfirm with the user:\n\n- **Repository** — the long-lived fork to keep synchronized (owner/repo).\n- **Upstream remote** (optional) — the remote the fork tracks. Defaults to\n the repository's GitHub parent.\n- **Local changes** (optional) — a plain-language description of the\n customizations to preserve across rebase.\n- **Verify command** (optional) — the command that confirms the software\n works (e.g. `make test`). If blank, infer a sensible check from the\n repository's build system.\n- **Sync schedule** — how often to run the sync. Default: nightly (`0 3 * * *`).\n\n### Step 3 - Create the automation\n\nCreate the automation via the prompt preset:\n\n```bash\ncurl -s -X POST \"$AUTOMATION_API_URL/v1/preset/prompt\" \\\n -H \"Authorization: Bearer $OPENHANDS_AUTOMATION_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"name\": \"Upstream fork sync - '\"$REPO\"'\",\n \"prompt\": \"Fetch the latest upstream changes for the fork '\"$REPO\"' and rebase all local changes on top of upstream. Local changes to preserve: '\"$LOCAL_CHANGES\"'. Check that the software works as intended; if it does, replace the current version, otherwise leave the running version untouched and report what failed.\",\n \"repos\": [{\"url\": \"'\"$REPO\"'\", \"provider\": \"github\"}],\n \"trigger\": {\"type\": \"cron\", \"schedule\": \"'\"$SCHEDULE\"'\", \"timezone\": \"'\"$TIMEZONE\"'\"}\n }'\n```\n\nConfirm the automation was created (HTTP 201) and report its ID to the user.\n\n---\n\n## Runtime behavior\n\nOn each scheduled run the automation:\n\n1. Clones the fork and fetches the latest from its upstream remote.\n2. Rebases every local customization commit on top of the newest upstream\n HEAD, resolving conflicts in favor of the local changes where the\n description indicates intent.\n3. Runs the verification command. If none was supplied, infers one from the\n repo's build system (e.g. `make test`, `npm test`, `pytest`).\n4. On success, force-pushes the rebased branch and replaces the currently\n deployed version with the freshly built one.\n5. On failure, leaves the running version untouched and reports the conflict\n or failing check so a human can intervene.\n\n---\n\n## Notes\n\n- The automation is idempotent: a clean upstream with no new commits is a\n no-op.\n- Force-push targets the fork's working branch only, never upstream.\n- If a rebase conflict cannot be resolved automatically, the run fails safe\n and the previously deployed version keeps running."
|
|
509
|
+
},
|
|
502
510
|
{
|
|
503
511
|
"name": "uv",
|
|
504
512
|
"description": "If the project uses uv, use this skill. Use this skill to create/manage Python projects and environments with `uv`, add/remove dependencies, sync a project from `uv.lock`, and run commands in the project environment.",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "upstream-fork-sync",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a cron automation that keeps a long-lived fork synchronized with its upstream. Fetches upstream changes, rebases local customizations on top, verifies the software works, and replaces the running version when the rebase is clean.",
|
|
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
|
+
"fork",
|
|
14
|
+
"upstream",
|
|
15
|
+
"rebase",
|
|
16
|
+
"sync",
|
|
17
|
+
"cron",
|
|
18
|
+
"automation",
|
|
19
|
+
"long-lived-fork"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Upstream Fork Sync Skill
|
|
2
|
+
|
|
3
|
+
An OpenHands skill that creates a cron automation to keep a long-lived
|
|
4
|
+
fork synchronized with its upstream source.
|
|
5
|
+
|
|
6
|
+
## What it does
|
|
7
|
+
|
|
8
|
+
1. **Fetches** the latest upstream changes for the fork on a schedule.
|
|
9
|
+
2. **Rebases** the fork's local customizations on top of the newest
|
|
10
|
+
upstream HEAD.
|
|
11
|
+
3. **Verifies** the software still works (using a configured command or an
|
|
12
|
+
inferred build check).
|
|
13
|
+
4. **Replaces** the deployed version when verification passes, or
|
|
14
|
+
**fails safe** and reports the problem when it does not.
|
|
15
|
+
|
|
16
|
+
## When to use
|
|
17
|
+
|
|
18
|
+
Use this skill when a user wants to maintain customizations on top of an
|
|
19
|
+
upstream project over time — the "long-lived fork" pattern — without
|
|
20
|
+
manually redoing the work after every upstream release.
|
|
21
|
+
|
|
22
|
+
## Files
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
upstream-fork-sync/
|
|
26
|
+
├── SKILL.md ← agent instructions (loaded automatically)
|
|
27
|
+
└── README.md ← this file
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
Just tell OpenHands:
|
|
33
|
+
|
|
34
|
+
> *"Set up an upstream fork sync for `owner/repo`"*
|
|
35
|
+
|
|
36
|
+
The skill will walk through token verification, upstream-remote selection,
|
|
37
|
+
local-changes description, verification command, and schedule, then create
|
|
38
|
+
the automation automatically.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upstream-fork-sync
|
|
3
|
+
description: >
|
|
4
|
+
This skill should be used when the user asks to "keep a fork in sync",
|
|
5
|
+
"rebase local changes on upstream", "sync my fork nightly",
|
|
6
|
+
"long-lived fork", or "automate upstream rebases". Guides the user through
|
|
7
|
+
creating a cron automation that fetches upstream changes, rebases local
|
|
8
|
+
customizations on top, verifies the software works, and replaces the
|
|
9
|
+
running version when the rebase is clean.
|
|
10
|
+
triggers:
|
|
11
|
+
- /upstream-fork-sync:setup
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Upstream Fork Sync Automation
|
|
15
|
+
|
|
16
|
+
Create a cron automation that keeps a long-lived fork current with its
|
|
17
|
+
upstream source. On every run it fetches the latest upstream changes,
|
|
18
|
+
rebases the fork's local customizations on top, runs a verification check,
|
|
19
|
+
and replaces the deployed version only when the software still works.
|
|
20
|
+
|
|
21
|
+
This implements the "long-lived fork" pattern: instead of repeatedly
|
|
22
|
+
re-deriving a customization, the local changes are preserved across
|
|
23
|
+
upstream releases and kept working automatically.
|
|
24
|
+
|
|
25
|
+
Windows PowerShell equivalents for the setup, packaging, upload, and API-check
|
|
26
|
+
shell snippets are in `references/windows.md`.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Prerequisites
|
|
31
|
+
|
|
32
|
+
### Required secret
|
|
33
|
+
|
|
34
|
+
Verify that the following secret is set in **OpenHands Settings -> Secrets**:
|
|
35
|
+
|
|
36
|
+
| Secret name | Token type | Minimum permissions |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `GITHUB_PERSONAL_ACCESS_TOKEN` | Classic PAT | `repo` |
|
|
39
|
+
| `GITHUB_PERSONAL_ACCESS_TOKEN` | Fine-grained PAT | Contents: Read and Write, Metadata: Read |
|
|
40
|
+
|
|
41
|
+
Check with:
|
|
42
|
+
```bash
|
|
43
|
+
curl -s https://api.github.com/user \
|
|
44
|
+
-H "Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN" \
|
|
45
|
+
| python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('login') or d.get('message'))"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If the token is missing or invalid, inform the user and stop.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Setup Workflow
|
|
53
|
+
|
|
54
|
+
Follow these steps in order.
|
|
55
|
+
|
|
56
|
+
### Step 1 - Verify `GITHUB_PERSONAL_ACCESS_TOKEN`
|
|
57
|
+
|
|
58
|
+
Run the `curl` check above.
|
|
59
|
+
|
|
60
|
+
- If absent: *"GITHUB_PERSONAL_ACCESS_TOKEN is not set. Please add it in
|
|
61
|
+
OpenHands Settings -> Secrets."* Stop.
|
|
62
|
+
- If the API returns `{"message": "Bad credentials"}`: tell the user the
|
|
63
|
+
token is invalid and ask them to update it. Stop.
|
|
64
|
+
|
|
65
|
+
### Step 2 - Collect configuration
|
|
66
|
+
|
|
67
|
+
Confirm with the user:
|
|
68
|
+
|
|
69
|
+
- **Repository** — the long-lived fork to keep synchronized (owner/repo).
|
|
70
|
+
- **Upstream remote** (optional) — the remote the fork tracks. Defaults to
|
|
71
|
+
the repository's GitHub parent.
|
|
72
|
+
- **Local changes** (optional) — a plain-language description of the
|
|
73
|
+
customizations to preserve across rebase.
|
|
74
|
+
- **Verify command** (optional) — the command that confirms the software
|
|
75
|
+
works (e.g. `make test`). If blank, infer a sensible check from the
|
|
76
|
+
repository's build system.
|
|
77
|
+
- **Sync schedule** — how often to run the sync. Default: nightly (`0 3 * * *`).
|
|
78
|
+
|
|
79
|
+
### Step 3 - Create the automation
|
|
80
|
+
|
|
81
|
+
Create the automation via the prompt preset:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
curl -s -X POST "$AUTOMATION_API_URL/v1/preset/prompt" \
|
|
85
|
+
-H "Authorization: Bearer $OPENHANDS_AUTOMATION_API_KEY" \
|
|
86
|
+
-H "Content-Type: application/json" \
|
|
87
|
+
-d '{
|
|
88
|
+
"name": "Upstream fork sync - '"$REPO"'",
|
|
89
|
+
"prompt": "Fetch the latest upstream changes for the fork '"$REPO"' and rebase all local changes on top of upstream. Local changes to preserve: '"$LOCAL_CHANGES"'. Check that the software works as intended; if it does, replace the current version, otherwise leave the running version untouched and report what failed.",
|
|
90
|
+
"repos": [{"url": "'"$REPO"'", "provider": "github"}],
|
|
91
|
+
"trigger": {"type": "cron", "schedule": "'"$SCHEDULE"'", "timezone": "'"$TIMEZONE"'"}
|
|
92
|
+
}'
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Confirm the automation was created (HTTP 201) and report its ID to the user.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Runtime behavior
|
|
100
|
+
|
|
101
|
+
On each scheduled run the automation:
|
|
102
|
+
|
|
103
|
+
1. Clones the fork and fetches the latest from its upstream remote.
|
|
104
|
+
2. Rebases every local customization commit on top of the newest upstream
|
|
105
|
+
HEAD, resolving conflicts in favor of the local changes where the
|
|
106
|
+
description indicates intent.
|
|
107
|
+
3. Runs the verification command. If none was supplied, infers one from the
|
|
108
|
+
repo's build system (e.g. `make test`, `npm test`, `pytest`).
|
|
109
|
+
4. On success, force-pushes the rebased branch and replaces the currently
|
|
110
|
+
deployed version with the freshly built one.
|
|
111
|
+
5. On failure, leaves the running version untouched and reports the conflict
|
|
112
|
+
or failing check so a human can intervene.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Notes
|
|
117
|
+
|
|
118
|
+
- The automation is idempotent: a clean upstream with no new commits is a
|
|
119
|
+
no-op.
|
|
120
|
+
- Force-push targets the fork's working branch only, never upstream.
|
|
121
|
+
- If a rebase conflict cannot be resolved automatically, the run fails safe
|
|
122
|
+
and the previously deployed version keeps running.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
# auto-generated by sync_extensions.py
|
|
3
|
+
description: This skill should be used when the user asks to "keep a fork in sync", "rebase local changes on upstream", "sync my fork nightly", "long-lived fork", or "automate upstream rebases". Guides the user through creating a cron automation that fetches upstream changes, rebases local customizations on top, verifies the software works, and replaces the running version when the rebase is clean.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Read and follow the complete instructions in the SKILL.md file located in this skill's directory.
|
|
7
|
+
|
|
8
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
{
|
|
2
|
+
"automationId": "upstream-fork-sync",
|
|
3
|
+
"description": "Direct scheduled setup. Every create request body below is verified against the live CreatePromptAutomationRequest model in OpenHands/automation (openhands/automation/preset_router.py).",
|
|
4
|
+
"capabilities": "supported",
|
|
5
|
+
"blockedBy": [
|
|
6
|
+
"notReady"
|
|
7
|
+
],
|
|
8
|
+
"scenarios": [
|
|
9
|
+
{
|
|
10
|
+
"id": "happy-path",
|
|
11
|
+
"description": "Defaults plus a repository, a description of the local changes, and a verify command. Preflight passes, the automation is created.",
|
|
12
|
+
"formValues": {
|
|
13
|
+
"repository": "OpenHands/agent-server-gui",
|
|
14
|
+
"upstreamRemote": "",
|
|
15
|
+
"localChanges": "high-contrast UI theme",
|
|
16
|
+
"verifyCommand": "make test",
|
|
17
|
+
"schedule": "0 3 * * *",
|
|
18
|
+
"timezone": "UTC"
|
|
19
|
+
},
|
|
20
|
+
"preflight": {
|
|
21
|
+
"request": {
|
|
22
|
+
"method": "POST",
|
|
23
|
+
"path": "/v1/validate",
|
|
24
|
+
"body": {
|
|
25
|
+
"automationId": "upstream-fork-sync",
|
|
26
|
+
"endpoint": "/v1/preset/prompt",
|
|
27
|
+
"draft": {
|
|
28
|
+
"name": "Upstream fork sync - OpenHands/agent-server-gui",
|
|
29
|
+
"prompt": "Fetch the latest upstream changes for the fork OpenHands/agent-server-gui and rebase all local changes on top of upstream. Local changes to preserve: high-contrast UI theme. Check that the software works as intended; if it does, replace the current version, otherwise leave the running version untouched and report what failed.",
|
|
30
|
+
"repos": [
|
|
31
|
+
{
|
|
32
|
+
"url": "OpenHands/agent-server-gui",
|
|
33
|
+
"provider": "github"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"trigger": {
|
|
37
|
+
"type": "cron",
|
|
38
|
+
"schedule": "0 3 * * *",
|
|
39
|
+
"timezone": "UTC"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"response": {
|
|
45
|
+
"status": 200,
|
|
46
|
+
"body": {
|
|
47
|
+
"valid": true,
|
|
48
|
+
"errors": []
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"create": {
|
|
53
|
+
"request": {
|
|
54
|
+
"method": "POST",
|
|
55
|
+
"path": "/v1/preset/prompt",
|
|
56
|
+
"body": {
|
|
57
|
+
"name": "Upstream fork sync - OpenHands/agent-server-gui",
|
|
58
|
+
"prompt": "Fetch the latest upstream changes for the fork OpenHands/agent-server-gui and rebase all local changes on top of upstream. Local changes to preserve: high-contrast UI theme. Check that the software works as intended; if it does, replace the current version, otherwise leave the running version untouched and report what failed.",
|
|
59
|
+
"repos": [
|
|
60
|
+
{
|
|
61
|
+
"url": "OpenHands/agent-server-gui",
|
|
62
|
+
"provider": "github"
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"trigger": {
|
|
66
|
+
"type": "cron",
|
|
67
|
+
"schedule": "0 3 * * *",
|
|
68
|
+
"timezone": "UTC"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"response": {
|
|
73
|
+
"status": 201,
|
|
74
|
+
"body": {
|
|
75
|
+
"id": "9a2c7b15-3c4d-4e5f-8a1b-4c9d0e1f2a33",
|
|
76
|
+
"user_id": "1a2b3c4d-0000-4000-8000-000000000001",
|
|
77
|
+
"org_id": "1a2b3c4d-0000-4000-8000-000000000002",
|
|
78
|
+
"model": null,
|
|
79
|
+
"name": "Upstream fork sync - OpenHands/agent-server-gui",
|
|
80
|
+
"prompt": "Fetch the latest upstream changes for the fork OpenHands/agent-server-gui and rebase all local changes on top of upstream. Local changes to preserve: high-contrast UI theme. Check that the software works as intended; if it does, replace the current version, otherwise leave the running version untouched and report what failed.",
|
|
81
|
+
"trigger": {
|
|
82
|
+
"type": "cron",
|
|
83
|
+
"schedule": "0 3 * * *",
|
|
84
|
+
"timezone": "UTC"
|
|
85
|
+
},
|
|
86
|
+
"tarball_path": "oh-internal://uploads/7e1b2c30-9f23-5b66-c2e3-7df1b4c5a990",
|
|
87
|
+
"setup_script_path": "setup.sh",
|
|
88
|
+
"entrypoint": ".venv/bin/python main.py",
|
|
89
|
+
"timeout": 600,
|
|
90
|
+
"keep_alive": null,
|
|
91
|
+
"enabled": true,
|
|
92
|
+
"last_triggered_at": null,
|
|
93
|
+
"created_at": "2026-08-03T12:00:00Z",
|
|
94
|
+
"updated_at": "2026-08-03T12:00:00Z"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "interval-below-deployment-minimum",
|
|
101
|
+
"description": "A one-minute schedule. The create model accepts it, so only the deployment can reject it - this is precisely why preflight exists and why local validation is not authoritative.",
|
|
102
|
+
"formValues": {
|
|
103
|
+
"repository": "OpenHands/agent-server-gui",
|
|
104
|
+
"upstreamRemote": "",
|
|
105
|
+
"localChanges": "high-contrast UI theme",
|
|
106
|
+
"verifyCommand": "make test",
|
|
107
|
+
"schedule": "*/1 * * * *",
|
|
108
|
+
"timezone": "UTC"
|
|
109
|
+
},
|
|
110
|
+
"preflight": {
|
|
111
|
+
"request": {
|
|
112
|
+
"method": "POST",
|
|
113
|
+
"path": "/v1/validate",
|
|
114
|
+
"body": {
|
|
115
|
+
"automationId": "upstream-fork-sync",
|
|
116
|
+
"endpoint": "/v1/preset/prompt",
|
|
117
|
+
"draft": {
|
|
118
|
+
"name": "Upstream fork sync - OpenHands/agent-server-gui",
|
|
119
|
+
"prompt": "Fetch the latest upstream changes for the fork OpenHands/agent-server-gui and rebase all local changes on top of upstream. Local changes to preserve: high-contrast UI theme. Check that the software works as intended; if it does, replace the current version, otherwise leave the running version untouched and report what failed.",
|
|
120
|
+
"repos": [
|
|
121
|
+
{
|
|
122
|
+
"url": "OpenHands/agent-server-gui",
|
|
123
|
+
"provider": "github"
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"trigger": {
|
|
127
|
+
"type": "cron",
|
|
128
|
+
"schedule": "*/1 * * * *",
|
|
129
|
+
"timezone": "UTC"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"response": {
|
|
135
|
+
"status": 200,
|
|
136
|
+
"body": {
|
|
137
|
+
"valid": false,
|
|
138
|
+
"errors": [
|
|
139
|
+
{
|
|
140
|
+
"field": "trigger.schedule",
|
|
141
|
+
"code": "interval_too_short",
|
|
142
|
+
"message": "Minimum interval for this deployment is 5 minutes."
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"expectedFieldErrors": {
|
|
149
|
+
"schedule": "Minimum interval for this deployment is 5 minutes."
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"id": "unfireable-cron-rejected-at-create",
|
|
154
|
+
"description": "31 February. Syntactically valid, so a local check passes, but the service rejects it. Shows the real 422 body: detail is a list and the loc path carries the trigger discriminator tag.",
|
|
155
|
+
"formValues": {
|
|
156
|
+
"repository": "OpenHands/agent-server-gui",
|
|
157
|
+
"upstreamRemote": "",
|
|
158
|
+
"localChanges": "high-contrast UI theme",
|
|
159
|
+
"verifyCommand": "make test",
|
|
160
|
+
"schedule": "0 0 31 2 *",
|
|
161
|
+
"timezone": "UTC"
|
|
162
|
+
},
|
|
163
|
+
"create": {
|
|
164
|
+
"request": {
|
|
165
|
+
"method": "POST",
|
|
166
|
+
"path": "/v1/preset/prompt",
|
|
167
|
+
"body": {
|
|
168
|
+
"name": "Upstream fork sync - OpenHands/agent-server-gui",
|
|
169
|
+
"prompt": "Fetch the latest upstream changes for the fork OpenHands/agent-server-gui and rebase all local changes on top of upstream. Local changes to preserve: high-contrast UI theme. Check that the software works as intended; if it does, replace the current version, otherwise leave the running version untouched and report what failed.",
|
|
170
|
+
"repos": [
|
|
171
|
+
{
|
|
172
|
+
"url": "OpenHands/agent-server-gui",
|
|
173
|
+
"provider": "github"
|
|
174
|
+
}
|
|
175
|
+
],
|
|
176
|
+
"trigger": {
|
|
177
|
+
"type": "cron",
|
|
178
|
+
"schedule": "0 0 31 2 *",
|
|
179
|
+
"timezone": "UTC"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"response": {
|
|
184
|
+
"status": 422,
|
|
185
|
+
"body": {
|
|
186
|
+
"detail": [
|
|
187
|
+
{
|
|
188
|
+
"type": "value_error",
|
|
189
|
+
"loc": [
|
|
190
|
+
"body",
|
|
191
|
+
"trigger",
|
|
192
|
+
"cron",
|
|
193
|
+
"schedule"
|
|
194
|
+
],
|
|
195
|
+
"msg": "Value error, Cron expression cannot produce any future fire times: 0 0 31 2 *"
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"expectedFieldErrors": {
|
|
202
|
+
"schedule": "Value error, Cron expression cannot produce any future fire times: 0 0 31 2 *"
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"id": "fallback-conversation-when-direct-unavailable",
|
|
207
|
+
"description": "A cron-only deployment cannot run a webhook trigger, but this entry is cron-only and so runs directly; the assisted message seeds the conversation when the deployment is not ready.",
|
|
208
|
+
"formValues": {
|
|
209
|
+
"repository": "OpenHands/agent-server-gui",
|
|
210
|
+
"upstreamRemote": "",
|
|
211
|
+
"localChanges": "high-contrast UI theme",
|
|
212
|
+
"verifyCommand": "make test",
|
|
213
|
+
"schedule": "0 3 * * *",
|
|
214
|
+
"timezone": "UTC"
|
|
215
|
+
},
|
|
216
|
+
"conversation": {
|
|
217
|
+
"request": {
|
|
218
|
+
"message": "This deployment cannot run the scheduled fork-sync automation directly. Set it up in this conversation instead: confirm the fork repository, the upstream remote, the local changes to preserve, the verification command, and the nightly schedule, then create the automation."
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
}
|