Skip to content

Commit b1fc511

Browse files
committed
Merge remote-tracking branch 'upstream/dev' into context-command
2 parents 3fd3826 + 213b823 commit b1fc511

File tree

160 files changed

+9187
-1388
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

160 files changed

+9187
-1388
lines changed

.github/workflows/test.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,14 @@ jobs:
134134
VITE_OPENCODE_SERVER_PORT: "4096"
135135
OPENCODE_CLIENT: "app"
136136
timeout-minutes: 30
137+
138+
- name: Upload Playwright artifacts
139+
if: failure()
140+
uses: actions/upload-artifact@v4
141+
with:
142+
name: playwright-${{ matrix.settings.name }}-${{ github.run_attempt }}
143+
if-no-files-found: ignore
144+
retention-days: 7
145+
path: |
146+
packages/app/e2e/test-results
147+
packages/app/e2e/playwright-report

.opencode/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
plans/
2+
bun.lock
3+
package.json

STATS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,4 @@
207207
| 2026-01-19 | 4,861,108 (+233,485) | 1,863,112 (+23,941) | 6,724,220 (+257,426) |
208208
| 2026-01-20 | 5,128,999 (+267,891) | 1,903,665 (+40,553) | 7,032,664 (+308,444) |
209209
| 2026-01-21 | 5,444,842 (+315,843) | 1,962,531 (+58,866) | 7,407,373 (+374,709) |
210+
| 2026-01-22 | 5,766,340 (+321,498) | 2,029,487 (+66,956) | 7,795,827 (+388,454) |

bun.lock

Lines changed: 19 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

infra/console.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,26 @@ export const stripeWebhook = new stripe.WebhookEndpoint("StripeWebhookEndpoint",
101101
const zenProduct = new stripe.Product("ZenBlack", {
102102
name: "OpenCode Black",
103103
})
104-
const zenPrice = new stripe.Price("ZenBlackPrice", {
104+
const zenPriceProps = {
105105
product: zenProduct.id,
106-
unitAmount: 20000,
107106
currency: "usd",
108107
recurring: {
109108
interval: "month",
110109
intervalCount: 1,
111110
},
111+
}
112+
const zenPrice200 = new stripe.Price("ZenBlackPrice", { ...zenPriceProps, unitAmount: 20000 })
113+
const zenPrice100 = new stripe.Price("ZenBlack100Price", { ...zenPriceProps, unitAmount: 10000 })
114+
const zenPrice20 = new stripe.Price("ZenBlack20Price", { ...zenPriceProps, unitAmount: 2000 })
115+
const ZEN_BLACK_PRICE = new sst.Linkable("ZEN_BLACK_PRICE", {
116+
properties: {
117+
product: zenProduct.id,
118+
plan200: zenPrice200.id,
119+
plan100: zenPrice100.id,
120+
plan20: zenPrice20.id,
121+
},
112122
})
123+
const ZEN_BLACK_LIMITS = new sst.Secret("ZEN_BLACK_LIMITS")
113124

114125
const ZEN_MODELS = [
115126
new sst.Secret("ZEN_MODELS1"),
@@ -121,7 +132,6 @@ const ZEN_MODELS = [
121132
new sst.Secret("ZEN_MODELS7"),
122133
new sst.Secret("ZEN_MODELS8"),
123134
]
124-
const ZEN_BLACK = new sst.Secret("ZEN_BLACK")
125135
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
126136
const STRIPE_PUBLISHABLE_KEY = new sst.Secret("STRIPE_PUBLISHABLE_KEY")
127137
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
@@ -164,7 +174,8 @@ new sst.cloudflare.x.SolidStart("Console", {
164174
EMAILOCTOPUS_API_KEY,
165175
AWS_SES_ACCESS_KEY_ID,
166176
AWS_SES_SECRET_ACCESS_KEY,
167-
ZEN_BLACK,
177+
ZEN_BLACK_PRICE,
178+
ZEN_BLACK_LIMITS,
168179
new sst.Secret("ZEN_SESSION_SECRET"),
169180
...ZEN_MODELS,
170181
...($dev

nix/hashes.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"nodeModules": {
3-
"x86_64-linux": "sha256-sH6zUk9G4vC6btPZIR9aiSHX0F4aGyUZB7fKbpDUcpE=",
4-
"aarch64-linux": "sha256-CVpdXnFns34hmGwwlRrrI6Uk6B/jZUxfnH4HC2NanEo=",
5-
"aarch64-darwin": "sha256-khP27Iiq+FAZRlzUy7rGXc2MviZjirFH1ShRyd7q1bY=",
6-
"x86_64-darwin": "sha256-nE2p62Tld64sQVMq7j0YNT5Zwjqp22H997+K8xfi1ag="
3+
"x86_64-linux": "sha256-wSkJcUnS0ODOYkbkjRnxnjfWYKQOVXwkDNB8qrikuLA=",
4+
"aarch64-linux": "sha256-4BlpH/oIXRJEjkQydXDv1oi1Yx7li3k1dKHUy2/Gb10=",
5+
"aarch64-darwin": "sha256-awW0ooZo/QfB2xmRdZ9XLNzQ9sP/mbN+rTg215id6nc=",
6+
"x86_64-darwin": "sha256-CHrE2z+LqY2WXTQeGWG5LNMF1AY4UGSwViJAy4IwIVw="
77
}
88
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { test, expect } from "./fixtures"
2+
import { modKey } from "./utils"
3+
4+
test("smoke file viewer renders real file content", async ({ page, gotoSession }) => {
5+
await gotoSession()
6+
7+
const sep = process.platform === "win32" ? "\\" : "/"
8+
const file = ["packages", "app", "package.json"].join(sep)
9+
10+
await page.keyboard.press(`${modKey}+P`)
11+
12+
const dialog = page.getByRole("dialog")
13+
await expect(dialog).toBeVisible()
14+
15+
const input = dialog.getByRole("textbox").first()
16+
await input.fill(file)
17+
18+
const fileItem = dialog
19+
.locator(
20+
'[data-slot="list-item"][data-key^="file:"][data-key*="packages"][data-key*="app"][data-key$="package.json"]',
21+
)
22+
.first()
23+
await expect(fileItem).toBeVisible()
24+
await fileItem.click()
25+
26+
await expect(dialog).toHaveCount(0)
27+
28+
const tab = page.getByRole("tab", { name: "package.json" })
29+
await expect(tab).toBeVisible()
30+
await tab.click()
31+
32+
const code = page.locator('[data-component="code"]').first()
33+
await expect(code).toBeVisible()
34+
await expect(code.getByText("@opencode-ai/app")).toBeVisible()
35+
})
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { test, expect } from "./fixtures"
2+
import { promptSelector } from "./utils"
3+
4+
test("smoke model selection updates prompt footer", async ({ page, gotoSession }) => {
5+
await gotoSession()
6+
7+
await page.locator(promptSelector).click()
8+
await page.keyboard.type("/model")
9+
10+
const command = page.locator('[data-slash-id="model.choose"]')
11+
await expect(command).toBeVisible()
12+
await command.hover()
13+
14+
await page.keyboard.press("Enter")
15+
16+
const dialog = page.getByRole("dialog")
17+
await expect(dialog).toBeVisible()
18+
19+
const input = dialog.getByRole("textbox").first()
20+
21+
const selected = dialog.locator('[data-slot="list-item"][data-selected="true"]').first()
22+
await expect(selected).toBeVisible()
23+
24+
const other = dialog.locator('[data-slot="list-item"]:not([data-selected="true"])').first()
25+
const target = (await other.count()) > 0 ? other : selected
26+
27+
const key = await target.getAttribute("data-key")
28+
if (!key) throw new Error("Failed to resolve model key from list item")
29+
30+
const name = (await target.locator("span").first().innerText()).trim()
31+
const model = key.split(":").slice(1).join(":")
32+
33+
await input.fill(model)
34+
35+
const item = dialog.locator(`[data-slot="list-item"][data-key="${key}"]`)
36+
await expect(item).toBeVisible()
37+
await item.click()
38+
39+
await expect(dialog).toHaveCount(0)
40+
41+
const form = page.locator(promptSelector).locator("xpath=ancestor::form[1]")
42+
await expect(form.locator('[data-component="button"]').filter({ hasText: name }).first()).toBeVisible()
43+
})
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { test, expect } from "./fixtures"
2+
import { promptSelector } from "./utils"
3+
4+
test("smoke @mention inserts file pill token", async ({ page, gotoSession }) => {
5+
await gotoSession()
6+
7+
await page.locator(promptSelector).click()
8+
const sep = process.platform === "win32" ? "\\" : "/"
9+
const file = ["packages", "app", "package.json"].join(sep)
10+
const filePattern = /packages[\\/]+app[\\/]+\s*package\.json/
11+
12+
await page.keyboard.type(`@${file}`)
13+
14+
const suggestion = page.getByRole("button", { name: filePattern }).first()
15+
await expect(suggestion).toBeVisible()
16+
await suggestion.hover()
17+
18+
await page.keyboard.press("Tab")
19+
20+
const pill = page.locator(`${promptSelector} [data-type="file"]`).first()
21+
await expect(pill).toBeVisible()
22+
await expect(pill).toHaveAttribute("data-path", filePattern)
23+
24+
await page.keyboard.type(" ok")
25+
await expect(page.locator(promptSelector)).toContainText("ok")
26+
})
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { test, expect } from "./fixtures"
2+
import { promptSelector } from "./utils"
3+
4+
test("smoke /open opens file picker dialog", async ({ page, gotoSession }) => {
5+
await gotoSession()
6+
7+
await page.locator(promptSelector).click()
8+
await page.keyboard.type("/open")
9+
10+
const command = page.locator('[data-slash-id="file.open"]')
11+
await expect(command).toBeVisible()
12+
await command.hover()
13+
14+
await page.keyboard.press("Enter")
15+
16+
const dialog = page.getByRole("dialog")
17+
await expect(dialog).toBeVisible()
18+
await expect(dialog.getByRole("textbox").first()).toBeVisible()
19+
20+
await page.keyboard.press("Escape")
21+
await expect(dialog).toHaveCount(0)
22+
})

0 commit comments

Comments
 (0)