Skip to content

Commit 91d53c3

Browse files
committed
Merge branch 'sst/dev' into dev
2 parents 83c8bfb + 5773d9d commit 91d53c3

File tree

24 files changed

+913
-501
lines changed

24 files changed

+913
-501
lines changed

packages/opencode/script/publish.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ if (!snapshot) {
142142
// "# Maintainer: dax",
143143
// "# Maintainer: adam",
144144
// "",
145-
// "pkgname='opencode-bin'",
145+
// "pkgname='${pkg}'",
146146
// `pkgver=${version.split("-")[0]}`,
147147
// "options=('!debug' '!strip')",
148148
// "pkgrel=1",
@@ -166,14 +166,17 @@ if (!snapshot) {
166166
// "",
167167
// ].join("\n")
168168

169-
// await $`rm -rf ./dist/aur-opencode-bin`
170-
171-
// await $`git clone ssh://aur@aur.archlinux.org/opencode-bin.git ./dist/aur-opencode-bin`
172-
// await Bun.file("./dist/aur-opencode-bin/PKGBUILD").write(pkgbuild)
173-
// await $`cd ./dist/aur-opencode-bin && makepkg --printsrcinfo > .SRCINFO`
174-
// await $`cd ./dist/aur-opencode-bin && git add PKGBUILD .SRCINFO`
175-
// await $`cd ./dist/aur-opencode-bin && git commit -m "Update to v${version}"`
176-
// if (!dry) await $`cd ./dist/aur-opencode-bin && git push`
169+
// for (const pkg of ["opencode", "opencode-bin"]) {
170+
// await $`rm -rf ./dist/aur-${pkg}`
171+
// await $`git clone ssh://aur@aur.archlinux.org/opencode-bin.git ./dist/aur-${pkg}`
172+
// await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(
173+
// pkgbuild.replace("${pkg}", pkg),
174+
// )
175+
// await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO`
176+
// await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO`
177+
// await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${version}"`
178+
// if (!dry) await $`cd ./dist/aur-${pkg} && git push`
179+
// }
177180

178181
// Homebrew formula
179182
// const homebrewFormula = [

packages/opencode/src/app/app.ts

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export namespace App {
1313
export const Info = z
1414
.object({
1515
user: z.string(),
16+
hostname: z.string(),
1617
git: z.boolean(),
1718
path: z.object({
1819
config: z.string(),
@@ -30,11 +31,21 @@ export namespace App {
3031
})
3132
export type Info = z.infer<typeof Info>
3233

33-
const ctx = Context.create<Awaited<ReturnType<typeof create>>>("app")
34+
const ctx = Context.create<{
35+
info: Info
36+
services: Map<any, { state: any; shutdown?: (input: any) => Promise<void> }>
37+
}>("app")
3438

3539
const APP_JSON = "app.json"
3640

37-
async function create(input: { cwd: string }) {
41+
export type Input = {
42+
cwd: string
43+
}
44+
45+
export async function provide<T>(
46+
input: Input,
47+
cb: (app: App.Info) => Promise<T>,
48+
) {
3849
log.info("creating", {
3950
cwd: input.cwd,
4051
})
@@ -62,8 +73,11 @@ export namespace App {
6273
}
6374
>()
6475

76+
const root = git ?? input.cwd
77+
6578
const info: Info = {
6679
user: os.userInfo().username,
80+
hostname: os.hostname(),
6781
time: {
6882
initialized: state.initialized,
6983
},
@@ -72,16 +86,24 @@ export namespace App {
7286
config: Global.Path.config,
7387
state: Global.Path.state,
7488
data,
75-
root: git ?? input.cwd,
89+
root,
7690
cwd: input.cwd,
7791
},
7892
}
79-
const result = {
93+
const app = {
8094
services,
8195
info,
8296
}
8397

84-
return result
98+
return ctx.provide(app, async () => {
99+
const result = await cb(app.info)
100+
for (const [key, entry] of app.services.entries()) {
101+
if (!entry.shutdown) continue
102+
log.info("shutdown", { name: key })
103+
await entry.shutdown?.(await entry.state)
104+
}
105+
return result
106+
})
85107
}
86108

87109
export function state<State>(
@@ -107,22 +129,6 @@ export namespace App {
107129
return ctx.use().info
108130
}
109131

110-
export async function provide<T>(
111-
input: { cwd: string },
112-
cb: (app: Info) => Promise<T>,
113-
) {
114-
const app = await create(input)
115-
return ctx.provide(app, async () => {
116-
const result = await cb(app.info)
117-
for (const [key, entry] of app.services.entries()) {
118-
if (!entry.shutdown) continue
119-
log.info("shutdown", { name: key })
120-
await entry.shutdown?.(await entry.state)
121-
}
122-
return result
123-
})
124-
}
125-
126132
export async function initialize() {
127133
const { info } = ctx.use()
128134
info.time.initialized = Date.now()

packages/opencode/src/bus/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export namespace Bus {
4949
)
5050
}
5151

52-
export function publish<Definition extends EventDefinition>(
52+
export async function publish<Definition extends EventDefinition>(
5353
def: Definition,
5454
properties: z.output<Definition["properties"]>,
5555
) {
@@ -60,12 +60,14 @@ export namespace Bus {
6060
log.info("publishing", {
6161
type: def.type,
6262
})
63+
const pending = []
6364
for (const key of [def.type, "*"]) {
6465
const match = state().subscriptions.get(key)
6566
for (const sub of match ?? []) {
66-
sub(payload)
67+
pending.push(sub(payload))
6768
}
6869
}
70+
return Promise.all(pending)
6971
}
7072

7173
export function subscribe<Definition extends EventDefinition>(
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { App } from "../app/app"
2+
import { ConfigHooks } from "../config/hooks"
3+
import { Format } from "../format"
4+
import { Share } from "../share/share"
5+
6+
export async function bootstrap<T>(
7+
input: App.Input,
8+
cb: (app: App.Info) => Promise<T>,
9+
) {
10+
return App.provide(input, async (app) => {
11+
Share.init()
12+
Format.init()
13+
ConfigHooks.init()
14+
15+
return cb(app)
16+
})
17+
}
Lines changed: 90 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import type { Argv } from "yargs"
2-
import { App } from "../../app/app"
32
import { Bus } from "../../bus"
43
import { Provider } from "../../provider/provider"
54
import { Session } from "../../session"
6-
import { Share } from "../../share/share"
75
import { Message } from "../../session/message"
86
import { UI } from "../ui"
97
import { cmd } from "./cmd"
108
import { Flag } from "../../flag/flag"
119
import { Config } from "../../config/config"
10+
import { bootstrap } from "../bootstrap"
1211

1312
const TOOL: Record<string, [string, string]> = {
1413
todowrite: ["Todo", UI.Style.TEXT_WARNING_BOLD],
@@ -56,118 +55,109 @@ export const RunCommand = cmd({
5655
},
5756
handler: async (args) => {
5857
const message = args.message.join(" ")
59-
await App.provide(
60-
{
61-
cwd: process.cwd(),
62-
},
63-
async () => {
64-
await Share.init()
65-
const session = await (async () => {
66-
if (args.continue) {
67-
const first = await Session.list().next()
68-
if (first.done) return
69-
return first.value
70-
}
71-
72-
if (args.session) return Session.get(args.session)
58+
await bootstrap({ cwd: process.cwd() }, async () => {
59+
const session = await (async () => {
60+
if (args.continue) {
61+
const first = await Session.list().next()
62+
if (first.done) return
63+
return first.value
64+
}
7365

74-
return Session.create()
75-
})()
66+
if (args.session) return Session.get(args.session)
7667

77-
if (!session) {
78-
UI.error("Session not found")
79-
return
80-
}
68+
return Session.create()
69+
})()
8170

82-
const isPiped = !process.stdout.isTTY
71+
if (!session) {
72+
UI.error("Session not found")
73+
return
74+
}
8375

84-
UI.empty()
85-
UI.println(UI.logo())
86-
UI.empty()
87-
UI.println(UI.Style.TEXT_NORMAL_BOLD + "> ", message)
88-
UI.empty()
76+
const isPiped = !process.stdout.isTTY
8977

90-
const cfg = await Config.get()
91-
if (cfg.autoshare || Flag.OPENCODE_AUTO_SHARE || args.share) {
92-
await Session.share(session.id)
93-
UI.println(
94-
UI.Style.TEXT_INFO_BOLD +
95-
"~ https://opencoder.aryalabs.ai/s/" +
96-
session.id.slice(-8),
97-
)
98-
}
99-
UI.empty()
78+
UI.empty()
79+
UI.println(UI.logo())
80+
UI.empty()
81+
UI.println(UI.Style.TEXT_NORMAL_BOLD + "> ", message)
82+
UI.empty()
10083

101-
const { providerID, modelID } = args.model
102-
? Provider.parseModel(args.model)
103-
: await Provider.defaultModel()
84+
const cfg = await Config.get()
85+
if (cfg.autoshare || Flag.OPENCODE_AUTO_SHARE || args.share) {
86+
await Session.share(session.id)
10487
UI.println(
105-
UI.Style.TEXT_NORMAL_BOLD + "@ ",
106-
UI.Style.TEXT_NORMAL + `${providerID}/${modelID}`,
88+
UI.Style.TEXT_INFO_BOLD +
89+
"~ https://opencoder.aryalabs.ai/s/" +
90+
session.id.slice(-8),
10791
)
108-
UI.empty()
92+
}
93+
UI.empty()
10994

110-
function printEvent(color: string, type: string, title: string) {
111-
UI.println(
112-
color + `|`,
113-
UI.Style.TEXT_NORMAL +
114-
UI.Style.TEXT_DIM +
115-
` ${type.padEnd(7, " ")}`,
116-
"",
117-
UI.Style.TEXT_NORMAL + title,
118-
)
119-
}
95+
const { providerID, modelID } = args.model
96+
? Provider.parseModel(args.model)
97+
: await Provider.defaultModel()
98+
UI.println(
99+
UI.Style.TEXT_NORMAL_BOLD + "@ ",
100+
UI.Style.TEXT_NORMAL + `${providerID}/${modelID}`,
101+
)
102+
UI.empty()
103+
104+
function printEvent(color: string, type: string, title: string) {
105+
UI.println(
106+
color + `|`,
107+
UI.Style.TEXT_NORMAL + UI.Style.TEXT_DIM + ` ${type.padEnd(7, " ")}`,
108+
"",
109+
UI.Style.TEXT_NORMAL + title,
110+
)
111+
}
120112

121-
Bus.subscribe(Message.Event.PartUpdated, async (evt) => {
122-
if (evt.properties.sessionID !== session.id) return
123-
const part = evt.properties.part
124-
const message = await Session.getMessage(
125-
evt.properties.sessionID,
126-
evt.properties.messageID,
127-
)
113+
Bus.subscribe(Message.Event.PartUpdated, async (evt) => {
114+
if (evt.properties.sessionID !== session.id) return
115+
const part = evt.properties.part
116+
const message = await Session.getMessage(
117+
evt.properties.sessionID,
118+
evt.properties.messageID,
119+
)
128120

129-
if (
130-
part.type === "tool-invocation" &&
131-
part.toolInvocation.state === "result"
132-
) {
133-
const metadata =
134-
message.metadata.tool[part.toolInvocation.toolCallId]
135-
const [tool, color] = TOOL[part.toolInvocation.toolName] ?? [
136-
part.toolInvocation.toolName,
137-
UI.Style.TEXT_INFO_BOLD,
138-
]
139-
printEvent(color, tool, metadata?.title || "Unknown")
140-
}
121+
if (
122+
part.type === "tool-invocation" &&
123+
part.toolInvocation.state === "result"
124+
) {
125+
const metadata = message.metadata.tool[part.toolInvocation.toolCallId]
126+
const [tool, color] = TOOL[part.toolInvocation.toolName] ?? [
127+
part.toolInvocation.toolName,
128+
UI.Style.TEXT_INFO_BOLD,
129+
]
130+
printEvent(color, tool, metadata?.title || "Unknown")
131+
}
141132

142-
if (part.type === "text") {
143-
if (part.text.includes("\n")) {
144-
UI.empty()
145-
UI.println(part.text)
146-
UI.empty()
147-
return
148-
}
149-
printEvent(UI.Style.TEXT_NORMAL_BOLD, "Text", part.text)
133+
if (part.type === "text") {
134+
if (part.text.includes("\n")) {
135+
UI.empty()
136+
UI.println(part.text)
137+
UI.empty()
138+
return
150139
}
151-
})
140+
printEvent(UI.Style.TEXT_NORMAL_BOLD, "Text", part.text)
141+
}
142+
})
152143

153-
const result = await Session.chat({
154-
sessionID: session.id,
155-
providerID,
156-
modelID,
157-
parts: [
158-
{
159-
type: "text",
160-
text: message,
161-
},
162-
],
163-
})
144+
const result = await Session.chat({
145+
sessionID: session.id,
146+
providerID,
147+
modelID,
148+
parts: [
149+
{
150+
type: "text",
151+
text: message,
152+
},
153+
],
154+
})
164155

165-
if (isPiped) {
166-
const match = result.parts.findLast((x) => x.type === "text")
167-
if (match) process.stdout.write(match.text)
168-
}
169-
UI.empty()
170-
},
171-
)
156+
if (isPiped) {
157+
const match = result.parts.findLast((x) => x.type === "text")
158+
if (match) process.stdout.write(match.text)
159+
}
160+
UI.empty()
161+
})
172162
},
173163
})

0 commit comments

Comments
 (0)