Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/sync-upstream.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Sync Fork with Upstream

on:
schedule:
- cron: "0 6 * * *"
workflow_dispatch:

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Rebase on upstream
id: rebase
continue-on-error: true
run: |
git remote add upstream https://github.com/anomalyco/opencode.git || true
git fetch upstream
git checkout dev
git rebase upstream/dev
git push origin dev --force-with-lease

- name: Create PR on conflict
if: steps.rebase.outcome == 'failure'
run: |
git rebase --abort
git checkout -b sync/conflict-$(date +%Y-%m-%d) upstream/dev
git merge origin/dev --no-edit || true
git add -A && git commit -m "sync: conflicts $(date +%Y-%m-%d)" --no-verify
git push origin sync/conflict-$(date +%Y-%m-%d)
gh pr create --base dev --head sync/conflict-$(date +%Y-%m-%d) \
--title "Sync upstream - conflicts $(date +%Y-%m-%d)" \
--body "Rebase failed due to conflicts. Please resolve manually."
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion packages/opencode/script/build.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bun

import solidPlugin from "../node_modules/@opentui/solid/scripts/solid-plugin"
import solidPlugin from "@opentui/solid/bun-plugin"
import path from "path"
import fs from "fs"
import { $ } from "bun"
Expand Down
18 changes: 17 additions & 1 deletion packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ import { PermissionPrompt } from "./permission"
import { QuestionPrompt } from "./question"
import { DialogExportOptions } from "../../ui/dialog-export-options"
import { formatTranscript } from "../../util/transcript"
import { getSecurityIndicator } from "../../util/provider"
import { UI } from "@/cli/ui.ts"

addDefaultParsers(parsers.parsers)
Expand Down Expand Up @@ -1300,7 +1301,22 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
{" "}
</span>{" "}
<span style={{ fg: theme.text }}>{Locale.titlecase(props.message.mode)}</span>
<span style={{ fg: theme.textMuted }}> · {props.message.modelID}</span>
{(() => {
const indicator = getSecurityIndicator(props.message.providerID, !!props.message.error)
return (
<span style={{
fg: indicator.status === "error"
? theme.error
: indicator.status === "secure"
? theme.success
: theme.textMuted
}}>
{" · "}
{indicator.label}
{props.message.modelID}
</span>
)
})()}
<Show when={duration()}>
<span style={{ fg: theme.textMuted }}> · {Locale.duration(duration())}</span>
</Show>
Expand Down
16 changes: 16 additions & 0 deletions packages/opencode/src/cli/cmd/tui/util/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Returns a security indicator label and status for the given provider.
*
* When the provider is "concrete-security", the channel is expected to be
* encrypted end-to-end. If an error occurred during the exchange the
* indicator signals an unsecure state; otherwise it confirms the channel
* is secure. For any other provider, no indicator is shown.
*/
export function getSecurityIndicator(providerID: string, hasError: boolean) {
if (providerID === "concrete-security") {
return hasError
? { label: "⚠ Unsecure ", status: "error" as const }
: { label: "🔐 Secure ", status: "secure" as const }
}
return { label: "", status: "none" as const }
}
31 changes: 31 additions & 0 deletions packages/opencode/test/cli/tui/provider.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Tests for the security indicator displayed in the session header.
*
* The "concrete-security" provider uses an encrypted channel, so the UI
* shows a visual cue reflecting whether the exchange succeeded (secure)
* or failed (unsecure). Other providers should produce no indicator.
*/
import { describe, expect, test } from "bun:test"
import { getSecurityIndicator } from "../../../src/cli/cmd/tui/util/provider"

describe("getSecurityIndicator", () => {
// When the provider is "concrete-security" and no error occurred,
// the channel is considered secure.
test("concrete-security without error returns secure", () => {
const result = getSecurityIndicator("concrete-security", false)
expect(result).toEqual({ label: "🔐 Secure ", status: "secure" })
})

// When the provider is "concrete-security" but an error occurred,
// the channel is flagged as unsecure.
test("concrete-security with error returns unsecure", () => {
const result = getSecurityIndicator("concrete-security", true)
expect(result).toEqual({ label: "⚠ Unsecure ", status: "error" })
})

// Non-regression: any other provider should produce no indicator at all.
test("other provider returns empty label", () => {
const result = getSecurityIndicator("anthropic", false)
expect(result).toEqual({ label: "", status: "none" })
})
})
20 changes: 20 additions & 0 deletions user_secure_configuration_example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://opencode.ai/config.json",
"model": "concrete-security/secure-gpt-oss-120b",
"provider": {
"opencode": { "options": {} },
"concrete-security": {
"npm": "file:///Users/kcelia/Concrete-Security/ratls/ai-provider/dist/index.js",
"api": "https://vllm.concrete-security.com/v1",
"options": { "sdk": "@ai-sdk/anthropic", "apiKey": "dummy-key" },
"models": {
"secure-gpt-oss-120b": {
"id": "openai/gpt-oss-120b",
"name": "Secure gpt-oss-120b"
}
}
}
},
"mcp": {},
"tools": { "github-triage": false, "github-pr-search": false }
}
Loading