2034fabc7d
commit 7b2ad6a1abf88e0731f15bbf6e281b29a610dd76
Merge: 74c85391 847a63e1
Author: Frank <frank@sst.dev>
Date: Mon Aug 18 15:31:54 2025 +0800
Merge branch 'dev' into github
commit 74c85391b576d01df298f6c30e3399b281b5c997
Author: Frank <frank@sst.dev>
Date: Mon Aug 18 15:30:14 2025 +0800
sync
commit 0d27f8e490f1aa242e1a3fcd1f21eb077f852207
Author: Frank <frank@sst.dev>
Date: Mon Aug 18 14:30:57 2025 +0800
sync
commit 0cf7e6c89f173b053f37cc0d316011b3e9d5fcc4
Author: Frank <frank@sst.dev>
Date: Mon Aug 18 11:54:57 2025 +0800
sync
commit a782cb7a268bf98916c3850083eaf44ebc38de05
Author: Frank <frank@sst.dev>
Date: Mon Aug 18 11:53:25 2025 +0800
sync
commit aa557014584abaf462656ba9b1de7c8bd6e9b9d8
Author: Frank <frank@sst.dev>
Date: Mon Aug 18 11:48:10 2025 +0800
sync
commit 73c8150479bd3c965087c634102df047a36b40ab
Author: Frank <frank@sst.dev>
Date: Mon Aug 18 01:29:29 2025 +0800
sync
commit c5325134e80ce3f9e2cb88e5a51893e4ffd880c2
Author: Frank <frank@sst.dev>
Date: Mon Aug 18 01:07:48 2025 +0800
sync
commit c5b646aa88760731ac9cd221f677bd400c31224b
Author: Frank <frank@sst.dev>
Date: Mon Aug 18 01:02:02 2025 +0800
sync
commit 27f7cc86ab4713a26d316ae71d2aa5978aaa2007
Author: Frank <frank@sst.dev>
Date: Mon Aug 18 00:59:22 2025 +0800
sync
commit 0a6152a0e0c2bb0e5b7cafbcb92b908433dd6c5b
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 18:11:31 2025 +0800
fix /opencode trigger
commit f1089103c607ac11251cac5e032e62c8b4667b30
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 17:55:14 2025 +0800
sync
commit 3ad18240248301380a68880315bfa83c18e9652d
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 17:44:11 2025 +0800
sync
commit 24f0f81773762a38ba0a26e599b718495e2f4b54
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 17:18:22 2025 +0800
sync
commit bc199d32bed9679d2f80ade527fa57a91e0883ca
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 16:59:03 2025 +0800
sync
commit 6cf860be843e94401166a6de83e36d6bdd8ca6d7
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 16:54:48 2025 +0800
sync
commit f5f753ff38498062b2e3de38a1be94158fce1463
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 14:43:12 2025 +0800
sync
commit 26d2e23a3ee99141a5951a153e444a1be25548dc
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 14:33:40 2025 +0800
sync
commit c5b3f54a0ae6064ff51c11ade41e21b594939715
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 14:16:10 2025 +0800
sync
commit 1c74e9a7ad35551eea53d0e51dcd28e6ae30a944
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 08:17:53 2025 +0800
sync
commit 89052dc9aaf7e4f02b7ca869ef6017322ee21c94
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 08:12:43 2025 +0800
sync
commit 42931d4d2a942eedef44f5570a57bf84df26ecfa
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 08:08:37 2025 +0800
sync
commit f22e97dd051ae3f592f4258a8d0270ca7fd60338
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 08:01:57 2025 +0800
sync
commit 2dda422ef85d2308b459cebe7f202b7fb782e75e
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 07:55:38 2025 +0800
sync
commit b8be1d0e9e89732bd60185c724cda72b8de5f145
Author: Frank <frank@sst.dev>
Date: Sun Aug 17 07:48:18 2025 +0800
sync
commit 78c84b96a3c8aa78e0ffa089a2a72ad80348fe72
Author: Frank <frank@sst.dev>
Date: Sat Aug 16 20:49:26 2025 +0800
sync
commit dd9c0c83090ea6c5da963303227a1e09a8434994
Author: Frank <frank@sst.dev>
Date: Sat Aug 16 20:47:25 2025 +0800
sync
commit 5eb917abba182712d1581376e95de45a092bbb24
Author: Frank <frank@sst.dev>
Date: Sat Aug 16 20:35:48 2025 +0800
sync
commit 43cf83e7ccbc99484602b06cbb6aafdbc63bf11c
Author: Frank <frank@sst.dev>
Date: Sat Aug 16 20:32:49 2025 +0800
sync
commit 10673ca3d2e1572e15c944ddd7d7af8175971f74
Author: Frank <frank@sst.dev>
Date: Sat Aug 16 19:55:53 2025 +0800
sync
commit c45ae8a233ed64c49a08b98f3ad01e0348b2df22
Author: Frank <frank@sst.dev>
Date: Sat Aug 16 19:53:52 2025 +0800
sync
commit 3c329dee05ecda95f5d249552aafc885997f07f2
Author: Frank <frank@sst.dev>
Date: Sat Aug 16 19:49:56 2025 +0800
sync
commit 5797048db864142f15d73c854131a77a31a421ee
Author: Frank <frank@sst.dev>
Date: Sat Aug 16 18:00:04 2025 +0800
sync
commit 2741338e8a27e57d9d023cf9c0a6a05276b82f41
Author: Frank <frank@sst.dev>
Date: Sat Aug 16 17:54:42 2025 +0800
sync
commit a51a8ca6d094bd5f98330c730d335285688c6ed8
Author: Frank <frank@sst.dev>
Date: Fri Aug 15 18:59:29 2025 +0800
sync
commit f4eeeb612dfa6f1714a954dd167519ade0c36a2d
Author: Frank <frank@sst.dev>
Date: Fri Aug 15 18:56:35 2025 +0800
sync
commit 1d0509c5630904a5a9e89ce0de09fbebb6f711be
Author: Frank <frank@sst.dev>
Date: Fri Aug 15 18:54:21 2025 +0800
sync
commit 339807d1b88d2439e9543b5da4ca2538a49f4ab8
Author: Frank <frank@sst.dev>
Date: Fri Aug 15 18:49:22 2025 +0800
sync
commit 70b4b78922fe80424d8922bb999ed84d28dff005
Author: Frank <frank@sst.dev>
Date: Fri Aug 15 18:04:57 2025 +0800
sync
238 lines
7.1 KiB
TypeScript
238 lines
7.1 KiB
TypeScript
import path from "path"
|
|
import { $ } from "bun"
|
|
import { exec } from "child_process"
|
|
import * as prompts from "@clack/prompts"
|
|
import { map, pipe, sortBy, values } from "remeda"
|
|
import { UI } from "../ui"
|
|
import { cmd } from "./cmd"
|
|
import { ModelsDev } from "../../provider/models"
|
|
import { App } from "../../app/app"
|
|
|
|
const WORKFLOW_FILE = ".github/workflows/opencode.yml"
|
|
|
|
export const GithubCommand = cmd({
|
|
command: "github",
|
|
describe: "manage GitHub agent",
|
|
builder: (yargs) => yargs.command(GithubInstallCommand).demandCommand(),
|
|
async handler() {},
|
|
})
|
|
|
|
export const GithubInstallCommand = cmd({
|
|
command: "install",
|
|
describe: "install the GitHub agent",
|
|
async handler() {
|
|
await App.provide({ cwd: process.cwd() }, async () => {
|
|
UI.empty()
|
|
prompts.intro("Install GitHub agent")
|
|
const app = await getAppInfo()
|
|
await installGitHubApp()
|
|
|
|
const providers = await ModelsDev.get()
|
|
const provider = await promptProvider()
|
|
const model = await promptModel()
|
|
//const key = await promptKey()
|
|
|
|
await addWorkflowFiles()
|
|
printNextSteps()
|
|
|
|
function printNextSteps() {
|
|
let step2
|
|
if (provider === "amazon-bedrock") {
|
|
step2 =
|
|
"Configure OIDC in AWS - https://docs.github.com/en/actions/how-tos/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services"
|
|
} else {
|
|
step2 = [
|
|
` 2. Add the following secrets in org or repo (${app.owner}/${app.repo}) settings`,
|
|
"",
|
|
...providers[provider].env.map((e) => ` - ${e}`),
|
|
].join("\n")
|
|
}
|
|
|
|
prompts.outro(
|
|
[
|
|
"Next steps:",
|
|
"",
|
|
` 1. Commit the \`${WORKFLOW_FILE}\` file and push`,
|
|
step2,
|
|
"",
|
|
" 3. Go to a GitHub issue and comment `/oc summarize` to see the agent in action",
|
|
"",
|
|
" Learn more about the GitHub agent - https://opencode.ai/docs/github/#usage-examples",
|
|
].join("\n"),
|
|
)
|
|
}
|
|
|
|
async function getAppInfo() {
|
|
const app = App.info()
|
|
if (!app.git) {
|
|
prompts.log.error(`Could not find git repository. Please run this command from a git repository.`)
|
|
throw new UI.CancelledError()
|
|
}
|
|
|
|
// Get repo info
|
|
const info = await $`git remote get-url origin`
|
|
.quiet()
|
|
.nothrow()
|
|
.text()
|
|
.then((text) => text.trim())
|
|
// match https or git pattern
|
|
// ie. https://github.com/sst/opencode.git
|
|
// ie. https://github.com/sst/opencode
|
|
// ie. git@github.com:sst/opencode.git
|
|
// ie. git@github.com:sst/opencode
|
|
// ie. ssh://git@github.com/sst/opencode.git
|
|
// ie. ssh://git@github.com/sst/opencode
|
|
const parsed = info.match(/^(?:(?:https?|ssh):\/\/)?(?:git@)?github\.com[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/)
|
|
if (!parsed) {
|
|
prompts.log.error(`Could not find git repository. Please run this command from a git repository.`)
|
|
throw new UI.CancelledError()
|
|
}
|
|
const [, owner, repo] = parsed
|
|
return { owner, repo, root: app.path.root }
|
|
}
|
|
|
|
async function promptProvider() {
|
|
const priority: Record<string, number> = {
|
|
anthropic: 0,
|
|
"github-copilot": 1,
|
|
openai: 2,
|
|
google: 3,
|
|
}
|
|
let provider = await prompts.select({
|
|
message: "Select provider",
|
|
maxItems: 8,
|
|
options: pipe(
|
|
providers,
|
|
values(),
|
|
sortBy(
|
|
(x) => priority[x.id] ?? 99,
|
|
(x) => x.name ?? x.id,
|
|
),
|
|
map((x) => ({
|
|
label: x.name,
|
|
value: x.id,
|
|
hint: priority[x.id] === 0 ? "recommended" : undefined,
|
|
})),
|
|
),
|
|
})
|
|
|
|
if (prompts.isCancel(provider)) throw new UI.CancelledError()
|
|
|
|
return provider
|
|
}
|
|
|
|
async function promptModel() {
|
|
const providerData = providers[provider]!
|
|
|
|
const model = await prompts.select({
|
|
message: "Select model",
|
|
maxItems: 8,
|
|
options: pipe(
|
|
providerData.models,
|
|
values(),
|
|
sortBy((x) => x.name ?? x.id),
|
|
map((x) => ({
|
|
label: x.name ?? x.id,
|
|
value: x.id,
|
|
})),
|
|
),
|
|
})
|
|
|
|
if (prompts.isCancel(model)) throw new UI.CancelledError()
|
|
return model
|
|
}
|
|
|
|
async function installGitHubApp() {
|
|
const s = prompts.spinner()
|
|
s.start("Installing GitHub app")
|
|
|
|
// Get installation
|
|
const installation = await getInstallation()
|
|
if (installation) return s.stop("GitHub app already installed")
|
|
|
|
// Open browser
|
|
const url = "https://github.com/apps/opencode-agent"
|
|
const command =
|
|
process.platform === "darwin"
|
|
? `open "${url}"`
|
|
: process.platform === "win32"
|
|
? `start "${url}"`
|
|
: `xdg-open "${url}"`
|
|
|
|
exec(command, (error) => {
|
|
if (error) {
|
|
prompts.log.warn(`Could not open browser. Please visit: ${url}`)
|
|
}
|
|
})
|
|
|
|
// Wait for installation
|
|
s.message("Waiting for GitHub app to be installed")
|
|
const MAX_RETRIES = 120
|
|
let retries = 0
|
|
do {
|
|
const installation = await getInstallation()
|
|
if (installation) break
|
|
|
|
if (retries > MAX_RETRIES) {
|
|
s.stop(
|
|
`Failed to detect GitHub app installation. Make sure to install the app for the \`${app.owner}/${app.repo}\` repository.`,
|
|
)
|
|
throw new UI.CancelledError()
|
|
}
|
|
|
|
retries++
|
|
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
} while (true)
|
|
|
|
s.stop("Installed GitHub app")
|
|
|
|
async function getInstallation() {
|
|
return await fetch(`https://api.opencode.ai/get_github_app_installation?owner=${app.owner}&repo=${app.repo}`)
|
|
.then((res) => res.json())
|
|
.then((data) => data.installation)
|
|
}
|
|
}
|
|
|
|
async function addWorkflowFiles() {
|
|
const envStr =
|
|
provider === "amazon-bedrock"
|
|
? ""
|
|
: `\n env:${providers[provider].env.map((e) => `\n ${e}: \${{ secrets.${e} }}`).join("")}`
|
|
|
|
await Bun.write(
|
|
path.join(app.root, WORKFLOW_FILE),
|
|
`
|
|
name: opencode
|
|
|
|
on:
|
|
issue_comment:
|
|
types: [created]
|
|
|
|
jobs:
|
|
opencode:
|
|
if: |
|
|
contains(github.event.comment.body, ' /oc') ||
|
|
startsWith(github.event.comment.body, '/oc') ||
|
|
contains(github.event.comment.body, ' /opencode') ||
|
|
startsWith(github.event.comment.body, '/opencode')
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
id-token: write
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Run opencode
|
|
uses: sst/opencode/github@latest${envStr}
|
|
with:
|
|
model: ${provider}/${model}
|
|
`.trim(),
|
|
)
|
|
|
|
prompts.log.success(`Added workflow file: "${WORKFLOW_FILE}"`)
|
|
}
|
|
})
|
|
},
|
|
})
|