Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fae7e084e | ||
|
|
fa3d0da80b | ||
|
|
f48794c2cc |
@@ -6,18 +6,15 @@ OpenCode V2 plugin providing a `coordinate` agent and fast/good/deep worker pair
|
|||||||
|
|
||||||
Requires **OpenCode V2** (`opencode2`), **Bun 1.3.14+**, Bash, curl, jq, and `sha256sum` (Linux/WSL). The native plugin API is pinned to `@opencode/plugin@0.0.0-beta-19378`; use the matching OpenCode beta.
|
Requires **OpenCode V2** (`opencode2`), **Bun 1.3.14+**, Bash, curl, jq, and `sha256sum` (Linux/WSL). The native plugin API is pinned to `@opencode/plugin@0.0.0-beta-19378`; use the matching OpenCode beta.
|
||||||
|
|
||||||
Download and inspect [install.sh](install.sh), then run it without sudo:
|
Run the installer without sudo:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl --fail --show-error --location \
|
curl -fsSL https://git.krtss.de/StanPonomarev/stanp-opencode/raw/branch/main/install.sh | bash
|
||||||
https://git.krtss.de/StanPonomarev/stanp-opencode/raw/branch/main/install.sh \
|
|
||||||
--output install.sh
|
|
||||||
bash install.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
From a checkout, run `bash install.sh` directly. Rerun the same script to upgrade to the latest published, non-prerelease Gitea release.
|
From a checkout, run `bash install.sh` directly. Rerun the same script to upgrade to the latest published, non-prerelease Gitea release.
|
||||||
|
|
||||||
The installer downloads `stanp-opencode.tgz` and its SHA-256 checksum, verifies the archive, and installs its dependencies with lifecycle scripts disabled. It activates the plugin only after importing and checking the native V2 export. Downloads, dependency installation, and validation failures leave the previous active plugin untouched. The checksum detects corruption; it is not a signature independent of the release server.
|
The installer downloads `stanp-opencode.tgz` and its SHA-256 checksum, verifies and extracts the self-contained archive, then activates the plugin only after importing and checking the native V2 export. Download, extraction, and validation failures leave the previous active plugin untouched. The checksum detects corruption; it is not a signature independent of the release server.
|
||||||
|
|
||||||
Installation defaults to `~/.config/opencode` and respects `XDG_CONFIG_HOME`. Override it explicitly when needed:
|
Installation defaults to `~/.config/opencode` and respects `XDG_CONFIG_HOME`. Override it explicitly when needed:
|
||||||
|
|
||||||
@@ -25,7 +22,7 @@ Installation defaults to `~/.config/opencode` and respects `XDG_CONFIG_HOME`. Ov
|
|||||||
OPENCODE_CONFIG_DIR=/path/to/opencode-config bash install.sh
|
OPENCODE_CONFIG_DIR=/path/to/opencode-config bash install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
The installer atomically updates the `plugins/stanp-opencode` symlink for [V2 automatic plugin discovery](https://opencode.ai/v2/docs/plugins), with dependencies in a private `stanp-opencode/install.*` directory beneath the same config directory. It does not edit `opencode.json` or `opencode.jsonc`, install OpenCode itself, or configure provider credentials. Older private installation directories are retained so running sessions can finish; remove unused ones after restarting OpenCode.
|
The installer atomically replaces `plugins/stanp-opencode.js` for [V2 automatic plugin discovery](https://opencode.ai/v2/docs/plugins). It does not create symlinks, install package dependencies, edit `opencode.json` or `opencode.jsonc`, install OpenCode itself, or configure provider credentials.
|
||||||
|
|
||||||
Restart `opencode2` after installation (or `opencode2 service restart` for an existing service). Connect your provider, then select `coordinate` in the agent picker. The default configuration uses OpenAI GPT models; agents are only enabled when the required models are available.
|
Restart `opencode2` after installation (or `opencode2 service restart` for an existing service). Connect your provider, then select `coordinate` in the agent picker. The default configuration uses OpenAI GPT models; agents are only enabled when the required models are available.
|
||||||
|
|
||||||
@@ -37,7 +34,7 @@ See [GPT configuration](examples/gpt.opencode.jsonc) and [custom model assignmen
|
|||||||
{
|
{
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"package": "./plugins/stanp-opencode",
|
"package": "./plugins/stanp-opencode.js",
|
||||||
"options": {
|
"options": {
|
||||||
"mode": "gpt",
|
"mode": "gpt",
|
||||||
"provider": "openai",
|
"provider": "openai",
|
||||||
@@ -54,4 +51,4 @@ The plugin supplies session-local `todoread` and `todowrite` tools when no other
|
|||||||
|
|
||||||
## Uninstall
|
## Uninstall
|
||||||
|
|
||||||
Remove the `plugins/stanp-opencode` symlink from the selected config directory and remove any explicit configuration entry for it, then restart OpenCode. Once no running session uses it, the private `stanp-opencode` installation directory can also be removed.
|
Remove `plugins/stanp-opencode.js` from the selected config directory and remove any explicit configuration entry for it, then restart OpenCode.
|
||||||
|
|||||||
+17
-12
@@ -1,15 +1,15 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
for command in bun curl jq sha256sum; do
|
for command in bun curl jq sha256sum tar; do
|
||||||
command -v "$command" >/dev/null || { printf 'Required command not found: %s\n' "$command" >&2; exit 1; }
|
command -v "$command" >/dev/null || { printf 'Required command not found: %s\n' "$command" >&2; exit 1; }
|
||||||
done
|
done
|
||||||
|
|
||||||
config="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}"
|
config="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}"
|
||||||
mkdir -p "$config"
|
mkdir -p "$config"
|
||||||
config=$(cd "$config" && pwd -P)
|
config=$(cd "$config" && pwd -P)
|
||||||
mkdir -p "$config/stanp-opencode" "$config/plugins"
|
mkdir -p "$config/plugins"
|
||||||
install=$(mktemp -d "$config/stanp-opencode/install.XXXXXXXX")
|
install=$(mktemp -d "$config/install.stanp-opencode.XXXXXXXX")
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if [[ -n "$install" ]]; then rm -rf "$install"; fi
|
if [[ -n "$install" ]]; then rm -rf "$install"; fi
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ trap cleanup EXIT
|
|||||||
api='https://git.krtss.de/api/v1/repos/StanPonomarev/stanp-opencode/releases/latest'
|
api='https://git.krtss.de/api/v1/repos/StanPonomarev/stanp-opencode/releases/latest'
|
||||||
curl --fail-with-body --silent --show-error --location --proto '=https' --proto-redir '=https' "$api" -o "$install/release.json"
|
curl --fail-with-body --silent --show-error --location --proto '=https' --proto-redir '=https' "$api" -o "$install/release.json"
|
||||||
for asset in stanp-opencode.tgz stanp-opencode.tgz.sha256; do
|
for asset in stanp-opencode.tgz stanp-opencode.tgz.sha256; do
|
||||||
url=$(jq -er --arg name "$asset" '[.assets[] | select(.name == $name) | .browser_download_url] | if length == 1 then .[0] | select(type == "string" and startswith("https://")) else error("Missing or duplicate release asset: " + $name) end' "$install/release.json")
|
url=$(jq -er --arg name "$asset" '[.assets[] | select(.name == $name)] | if length == 1 then .[0] | select((.id | type) == "number" and (.browser_download_url | type) == "string" and (.browser_download_url | startswith("https://"))) | "\(.browser_download_url)?asset_id=\(.id)" else error("Missing or duplicate release asset: " + $name) end' "$install/release.json")
|
||||||
curl --fail-with-body --silent --show-error --location --proto '=https' --proto-redir '=https' "$url" -o "$install/$asset"
|
curl --fail-with-body --silent --show-error --location --proto '=https' --proto-redir '=https' "$url" -o "$install/$asset"
|
||||||
done
|
done
|
||||||
read -r checksum _ < "$install/stanp-opencode.tgz.sha256"
|
read -r checksum _ < "$install/stanp-opencode.tgz.sha256"
|
||||||
@@ -26,13 +26,14 @@ if [[ ! "$checksum" =~ ^[a-fA-F0-9]{64}$ ]]; then
|
|||||||
printf 'Invalid release checksum\n' >&2
|
printf 'Invalid release checksum\n' >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! (cd "$install" && printf '%s stanp-opencode.tgz\n' "$checksum" | sha256sum --check --status); then
|
hash_output=$(sha256sum "$install/stanp-opencode.tgz")
|
||||||
|
read -r actual _ <<< "$hash_output"
|
||||||
|
if [[ ! "$actual" =~ ^[a-fA-F0-9]{64}$ || "$actual" != "$checksum" ]]; then
|
||||||
printf 'Release checksum mismatch; installation unchanged\n' >&2
|
printf 'Release checksum mismatch; installation unchanged\n' >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '{"private":true,"type":"module"}\n' > "$install/package.json"
|
tar -xzf "$install/stanp-opencode.tgz" -C "$install"
|
||||||
(cd "$install" && bun add --production --ignore-scripts --no-cache ./stanp-opencode.tgz)
|
|
||||||
bun -e '
|
bun -e '
|
||||||
if (!await Bun.file(process.argv[1]).exists()) {
|
if (!await Bun.file(process.argv[1]).exists()) {
|
||||||
throw new Error("Latest release lacks the OpenCode V2 directory entrypoint. Publish a V2-compatible release first.");
|
throw new Error("Latest release lacks the OpenCode V2 directory entrypoint. Publish a V2-compatible release first.");
|
||||||
@@ -41,10 +42,14 @@ bun -e '
|
|||||||
if (!plugin || plugin.id !== "stanp-opencode" || typeof plugin.setup !== "function") {
|
if (!plugin || plugin.id !== "stanp-opencode" || typeof plugin.setup !== "function") {
|
||||||
throw new Error("Latest release is not a native OpenCode V2 plugin. Publish a V2-compatible release first.");
|
throw new Error("Latest release is not a native OpenCode V2 plugin. Publish a V2-compatible release first.");
|
||||||
}
|
}
|
||||||
' "$install/node_modules/stanp-opencode/index.js"
|
' "$install/package/dist/index.js"
|
||||||
|
|
||||||
tag=$(jq -r '.tag_name' "$install/release.json")
|
tag=$(jq -r '.tag_name' "$install/release.json")
|
||||||
ln -s "../stanp-opencode/$(basename "$install")/node_modules/stanp-opencode" "$install/plugin"
|
if ! mv -f "$install/package/dist/index.js" "$config/plugins/stanp-opencode.js"; then
|
||||||
mv -Tf "$install/plugin" "$config/plugins/stanp-opencode"
|
printf 'Could not activate plugin; installation unchanged\n' >&2
|
||||||
install=""
|
exit 1
|
||||||
printf 'Installed stanp-opencode %s into %s/plugins/stanp-opencode\nRestart opencode2 to load it.\n' "$tag" "$config"
|
fi
|
||||||
|
if [[ -L "$config/plugins/stanp-opencode" ]]; then
|
||||||
|
rm -f "$config/plugins/stanp-opencode"
|
||||||
|
fi
|
||||||
|
printf 'Installed stanp-opencode %s into %s/plugins/stanp-opencode.js\nRestart opencode2 to load it.\n' "$tag" "$config"
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@
|
|||||||
"format": "oxfmt --write .",
|
"format": "oxfmt --write .",
|
||||||
"format:check": "oxfmt --check .",
|
"format:check": "oxfmt --check .",
|
||||||
"test": "bun test",
|
"test": "bun test",
|
||||||
"build": "bun build src/index.ts --outdir dist --target bun --format esm --packages external && tsc --project tsconfig.build.json --emitDeclarationOnly"
|
"build": "bun build src/index.ts --outdir dist --target bun --format esm && tsc --project tsconfig.build.json --emitDeclarationOnly"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@opencode/client": "0.0.0-beta-19378",
|
"@opencode/client": "0.0.0-beta-19378",
|
||||||
|
|||||||
+19
-5
@@ -114,7 +114,8 @@ async function install(mode: "native" | "checksum" | "legacy") {
|
|||||||
join(directory, "release.json"),
|
join(directory, "release.json"),
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
tag_name: "v1.0.0",
|
tag_name: "v1.0.0",
|
||||||
assets: ["stanp-opencode.tgz", "stanp-opencode.tgz.sha256"].map((name) => ({
|
assets: ["stanp-opencode.tgz", "stanp-opencode.tgz.sha256"].map((name, id) => ({
|
||||||
|
id: id + 1,
|
||||||
name,
|
name,
|
||||||
browser_download_url: `https://fixtures.invalid/${name}`,
|
browser_download_url: `https://fixtures.invalid/${name}`,
|
||||||
})),
|
})),
|
||||||
@@ -135,8 +136,8 @@ while (($#)); do
|
|||||||
done
|
done
|
||||||
case "$url" in
|
case "$url" in
|
||||||
https://git.krtss.de/api/v1/repos/StanPonomarev/stanp-opencode/releases/latest) source=release.json ;;
|
https://git.krtss.de/api/v1/repos/StanPonomarev/stanp-opencode/releases/latest) source=release.json ;;
|
||||||
https://fixtures.invalid/stanp-opencode.tgz) source=stanp-opencode.tgz ;;
|
https://fixtures.invalid/stanp-opencode.tgz?asset_id=1) source=stanp-opencode.tgz ;;
|
||||||
https://fixtures.invalid/stanp-opencode.tgz.sha256) source=stanp-opencode.tgz.sha256 ;;
|
https://fixtures.invalid/stanp-opencode.tgz.sha256?asset_id=2) source=stanp-opencode.tgz.sha256 ;;
|
||||||
*) printf 'Unexpected fixture URL: %s\\n' "$url" >&2; exit 1 ;;
|
*) printf 'Unexpected fixture URL: %s\\n' "$url" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
if [[ -n "$output" ]]; then
|
if [[ -n "$output" ]]; then
|
||||||
@@ -144,10 +145,22 @@ if [[ -n "$output" ]]; then
|
|||||||
else
|
else
|
||||||
cat "$FIXTURE_DIR/$source"
|
cat "$FIXTURE_DIR/$source"
|
||||||
fi
|
fi
|
||||||
|
`,
|
||||||
|
),
|
||||||
|
writeFile(
|
||||||
|
join(bin, "sha256sum"),
|
||||||
|
`#!/usr/bin/env bash
|
||||||
|
if [[ "\${1:-}" == -* ]]; then
|
||||||
|
printf 'usage: sha256sum [-bctwz] [files ...]\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
PATH="\${PATH#*:}"
|
||||||
|
export PATH
|
||||||
|
exec sha256sum "$@"
|
||||||
`,
|
`,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
await chmod(join(bin, "curl"), 0o755);
|
await Promise.all([chmod(join(bin, "curl"), 0o755), chmod(join(bin, "sha256sum"), 0o755)]);
|
||||||
const result = await run(["bash", installer], directory, env);
|
const result = await run(["bash", installer], directory, env);
|
||||||
return { directory, config, env, result };
|
return { directory, config, env, result };
|
||||||
}
|
}
|
||||||
@@ -161,7 +174,7 @@ test("installs an importable native plugin under XDG without changing user confi
|
|||||||
process.execPath,
|
process.execPath,
|
||||||
"-e",
|
"-e",
|
||||||
"const { default: plugin } = await import(process.argv[1]); console.log(JSON.stringify([plugin.id, plugin.setup()]));",
|
"const { default: plugin } = await import(process.argv[1]); console.log(JSON.stringify([plugin.id, plugin.setup()]));",
|
||||||
join(config, "plugins", "stanp-opencode"),
|
join(config, "plugins", "stanp-opencode.js"),
|
||||||
],
|
],
|
||||||
directory,
|
directory,
|
||||||
env,
|
env,
|
||||||
@@ -171,6 +184,7 @@ test("installs an importable native plugin under XDG without changing user confi
|
|||||||
stdout: '["stanp-opencode","native-ready"]\n',
|
stdout: '["stanp-opencode","native-ready"]\n',
|
||||||
});
|
});
|
||||||
expect(await readFile(join(config, "opencode.jsonc"), "utf8")).toBe(configContents);
|
expect(await readFile(join(config, "opencode.jsonc"), "utf8")).toBe(configContents);
|
||||||
|
expect(await Bun.file(join(config, "plugins", "stanp-opencode")).exists()).toBe(false);
|
||||||
expect(
|
expect(
|
||||||
await readFile(join(config, "stanp-opencode", "install.previous", "index.js"), "utf8"),
|
await readFile(join(config, "stanp-opencode", "install.previous", "index.js"), "utf8"),
|
||||||
).toBe(previousPlugin);
|
).toBe(previousPlugin);
|
||||||
|
|||||||
Reference in New Issue
Block a user