mirror of
https://fastgit.cc/https://github.com/anomalyco/opencode
synced 2026-05-02 23:04:07 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8eaa77bf1 | ||
|
|
a07f37073b | ||
|
|
4d760a1984 | ||
|
|
6b7058fe1c | ||
|
|
1149b984d9 | ||
|
|
81fb1b313e |
2
.github/workflows/publish-github-action.yml
vendored
2
.github/workflows/publish-github-action.yml
vendored
@@ -27,4 +27,4 @@ jobs:
|
||||
git config --global user.email "opencode@sst.dev"
|
||||
git config --global user.name "opencode"
|
||||
./script/publish
|
||||
working-directory: ./sdks/github
|
||||
working-directory: ./github
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
}
|
||||
},
|
||||
"mcp": {
|
||||
"context7": {
|
||||
"type": "remote",
|
||||
"url": "https://mcp.context7.com/sse"
|
||||
},
|
||||
"weather": {
|
||||
"type": "local",
|
||||
"command": ["opencode", "x", "@h1deya/mcp-server-weather"]
|
||||
|
||||
@@ -386,7 +386,6 @@ export const GithubRunCommand = cmd({
|
||||
type PromptFiles = Awaited<ReturnType<typeof getUserPrompt>>["promptFiles"]
|
||||
|
||||
try {
|
||||
const { userPrompt, promptFiles } = await getUserPrompt()
|
||||
const actionToken = isMock ? args.token! : await getOidcToken()
|
||||
appToken = await exchangeForAppToken(actionToken)
|
||||
octoRest = new Octokit({ auth: appToken })
|
||||
@@ -394,6 +393,7 @@ export const GithubRunCommand = cmd({
|
||||
headers: { authorization: `token ${appToken}` },
|
||||
})
|
||||
|
||||
const { userPrompt, promptFiles } = await getUserPrompt()
|
||||
await configureGit(appToken)
|
||||
await assertPermissions()
|
||||
|
||||
@@ -425,7 +425,7 @@ export const GithubRunCommand = cmd({
|
||||
const response = await chat(`${userPrompt}\n\n${dataPrompt}`, promptFiles)
|
||||
if (await branchIsDirty()) {
|
||||
const summary = await summarize(response)
|
||||
await pushToCurrentBranch(summary)
|
||||
await pushToLocalBranch(summary)
|
||||
}
|
||||
const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`))
|
||||
await updateComment(`${response}${footer({ image: !hasShared })}`)
|
||||
@@ -451,7 +451,7 @@ export const GithubRunCommand = cmd({
|
||||
const response = await chat(`${userPrompt}\n\n${dataPrompt}`, promptFiles)
|
||||
if (await branchIsDirty()) {
|
||||
const summary = await summarize(response)
|
||||
await pushToCurrentBranch(summary)
|
||||
await pushToNewBranch(summary, branch)
|
||||
const pr = await createPR(
|
||||
repoData.data.default_branch,
|
||||
branch,
|
||||
@@ -531,19 +531,28 @@ export const GithubRunCommand = cmd({
|
||||
}[] = []
|
||||
|
||||
// Search for files
|
||||
// ie. <img alt="Image" src="https://github.com/user-attachments/assets/xxxx" />
|
||||
// ie. [api.json](https://github.com/user-attachments/files/21433810/api.json)
|
||||
// ie. 
|
||||
const imgTags = prompt.matchAll(/!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi)
|
||||
const mdMatches = prompt.matchAll(/!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi)
|
||||
const tagMatches = prompt.matchAll(/<img .*?src="(https:\/\/github\.com\/user-attachments\/[^"]+)" \/>/gi)
|
||||
const matches = [...mdMatches, ...tagMatches].sort((a, b) => a.index - b.index)
|
||||
console.log("Images", JSON.stringify(matches, null, 2))
|
||||
|
||||
let offset = 0
|
||||
for (const imgTag of imgTags) {
|
||||
const tag = imgTag[0]
|
||||
const url = imgTag[1]
|
||||
const start = imgTag.index
|
||||
for (const m of matches) {
|
||||
const tag = m[0]
|
||||
const url = m[1]
|
||||
const start = m.index
|
||||
const filename = path.basename(url)
|
||||
|
||||
// Download image
|
||||
const res = await fetch(url)
|
||||
const res = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${appToken}`,
|
||||
Accept: "application/vnd.github.v3+json",
|
||||
},
|
||||
})
|
||||
if (!res.ok) {
|
||||
console.error(`Failed to download image: ${url}`)
|
||||
continue
|
||||
@@ -777,8 +786,17 @@ export const GithubRunCommand = cmd({
|
||||
return `opencode/${type}${issueId}-${timestamp}`
|
||||
}
|
||||
|
||||
async function pushToCurrentBranch(summary: string) {
|
||||
console.log("Pushing to current branch...")
|
||||
async function pushToNewBranch(summary: string, branch: string) {
|
||||
console.log("Pushing to new branch...")
|
||||
await $`git add .`
|
||||
await $`git commit -m "${summary}
|
||||
|
||||
Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"`
|
||||
await $`git push -u origin ${branch}`
|
||||
}
|
||||
|
||||
async function pushToLocalBranch(summary: string) {
|
||||
console.log("Pushing to local branch...")
|
||||
await $`git add .`
|
||||
await $`git commit -m "${summary}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { experimental_createMCPClient, type Tool } from "ai"
|
||||
import { Experimental_StdioMCPTransport } from "ai/mcp-stdio"
|
||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
|
||||
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
|
||||
import { App } from "../app/app"
|
||||
import { Config } from "../config/config"
|
||||
import { Log } from "../util/log"
|
||||
@@ -32,15 +34,28 @@ export namespace MCP {
|
||||
}
|
||||
log.info("found", { key, type: mcp.type })
|
||||
if (mcp.type === "remote") {
|
||||
const client = await experimental_createMCPClient({
|
||||
name: key,
|
||||
transport: {
|
||||
type: "sse",
|
||||
url: mcp.url,
|
||||
headers: mcp.headers,
|
||||
},
|
||||
}).catch(() => {})
|
||||
if (!client) {
|
||||
const transports = [
|
||||
new StreamableHTTPClientTransport(new URL(mcp.url), {
|
||||
requestInit: {
|
||||
headers: mcp.headers,
|
||||
},
|
||||
}),
|
||||
new SSEClientTransport(new URL(mcp.url), {
|
||||
requestInit: {
|
||||
headers: mcp.headers,
|
||||
},
|
||||
}),
|
||||
]
|
||||
for (const transport of transports) {
|
||||
const client = await experimental_createMCPClient({
|
||||
name: key,
|
||||
transport,
|
||||
}).catch(() => {})
|
||||
if (!client) continue
|
||||
clients[key] = client
|
||||
break
|
||||
}
|
||||
if (!clients[key])
|
||||
Bus.publish(Session.Event.Error, {
|
||||
error: {
|
||||
name: "UnknownError",
|
||||
@@ -49,16 +64,13 @@ export namespace MCP {
|
||||
},
|
||||
},
|
||||
})
|
||||
continue
|
||||
}
|
||||
clients[key] = client
|
||||
}
|
||||
|
||||
if (mcp.type === "local") {
|
||||
const [cmd, ...args] = mcp.command
|
||||
const client = await experimental_createMCPClient({
|
||||
name: key,
|
||||
transport: new Experimental_StdioMCPTransport({
|
||||
transport: new StdioClientTransport({
|
||||
stderr: "ignore",
|
||||
command: cmd,
|
||||
args,
|
||||
|
||||
@@ -23,8 +23,9 @@ export namespace ProviderTransform {
|
||||
}
|
||||
|
||||
for (const msg of unique([...system, ...final])) {
|
||||
const shouldUseContentOptions = providerID !== "anthropic" && Array.isArray(msg.content) && msg.content.length > 0
|
||||
|
||||
const shouldUseContentOptions =
|
||||
providerID !== "anthropic" && Array.isArray(msg.content) && msg.content.length > 0
|
||||
|
||||
if (shouldUseContentOptions) {
|
||||
const lastContent = msg.content[msg.content.length - 1]
|
||||
if (lastContent && typeof lastContent === "object") {
|
||||
@@ -35,7 +36,7 @@ export namespace ProviderTransform {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
msg.providerOptions = {
|
||||
...msg.providerOptions,
|
||||
...providerOptions,
|
||||
@@ -46,7 +47,7 @@ export namespace ProviderTransform {
|
||||
}
|
||||
|
||||
export function temperature(_providerID: string, modelID: string) {
|
||||
if (modelID.includes("qwen")) return 0.55
|
||||
if (modelID.toLowerCase().includes("qwen")) return 0.55
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,12 +64,12 @@ export namespace ToolRegistry {
|
||||
}
|
||||
|
||||
export function enabled(_providerID: string, modelID: string): Record<string, boolean> {
|
||||
if (modelID.includes("claude")) {
|
||||
if (modelID.toLowerCase().includes("claude")) {
|
||||
return {
|
||||
patch: false,
|
||||
}
|
||||
}
|
||||
if (modelID.includes("qwen")) {
|
||||
if (modelID.toLowerCase().includes("qwen")) {
|
||||
return {
|
||||
patch: false,
|
||||
todowrite: false,
|
||||
|
||||
@@ -18,7 +18,7 @@ You can define MCP servers in your opencode config under `mcp`.
|
||||
|
||||
### Local
|
||||
|
||||
Add local MCP servers under `mcp` with `"type": "local"`.
|
||||
Add local MCP servers using `"type": "local"` within the MCP object. Multiple MCP servers can be added. The key string for each server can be any arbitrary name.
|
||||
|
||||
```json title="opencode.json"
|
||||
{
|
||||
@@ -31,7 +31,7 @@ Add local MCP servers under `mcp` with `"type": "local"`.
|
||||
"environment": {
|
||||
"MY_ENV_VAR": "my_env_var_value"
|
||||
}
|
||||
}, {
|
||||
},
|
||||
"my-different-local-mcp-server": {
|
||||
"type": "local",
|
||||
"command": ["bun", "x", "my-other-mcp-command"],
|
||||
@@ -62,3 +62,28 @@ Add remote MCP servers under `mcp` with `"type": "remote"`.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Local and remote servers can be used together within the same `mcp` config object.
|
||||
|
||||
```json title="opencode.json"
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"mcp": {
|
||||
"my-local-mcp-server": {
|
||||
"type": "local",
|
||||
"command": ["bun", "x", "my-mcp-command"],
|
||||
"enabled": true,
|
||||
"environment": {
|
||||
"MY_ENV_VAR": "my_env_var_value"
|
||||
}
|
||||
},
|
||||
"my-remote-mcp": {
|
||||
"type": "remote",
|
||||
"url": "https://my-mcp-server.com",
|
||||
"enabled": true,
|
||||
"headers": {
|
||||
"Authorization": "Bearer MY_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user