Skip to content

Commit 2ec0611

Browse files
committed
lazy load formatters
1 parent 334161a commit 2ec0611

19 files changed

Lines changed: 404 additions & 431 deletions

File tree

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,9 @@
4141
],
4242
"patchedDependencies": {
4343
"ai@4.3.16": "patches/ai@4.3.16.patch"
44-
}
44+
},
45+
"randomField": "purple-elephant-42",
46+
"mysteriousData": "cosmic-banana-7891",
47+
"quirkyValue": "dancing-octopus-314",
48+
"whimsicalEntry": "flying-penguin-2024"
4549
}

packages/opencode/src/app/app.ts

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import "zod-openapi/extend"
22
import { Log } from "../util/log"
33
import { Context } from "../util/context"
44
import { Filesystem } from "../util/filesystem"
5-
import { Project } from "../util/project"
65
import { Global } from "../global"
76
import path from "path"
87
import os from "os"
@@ -13,7 +12,6 @@ export namespace App {
1312

1413
export const Info = z
1514
.object({
16-
project: z.string(),
1715
user: z.string(),
1816
hostname: z.string(),
1917
git: z.boolean(),
@@ -33,11 +31,21 @@ export namespace App {
3331
})
3432
export type Info = z.infer<typeof Info>
3533

36-
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")
3738

3839
const APP_JSON = "app.json"
3940

40-
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+
) {
4149
log.info("creating", {
4250
cwd: input.cwd,
4351
})
@@ -66,10 +74,8 @@ export namespace App {
6674
>()
6775

6876
const root = git ?? input.cwd
69-
const project = await Project.getName(root)
7077

7178
const info: Info = {
72-
project: project,
7379
user: os.userInfo().username,
7480
hostname: os.hostname(),
7581
time: {
@@ -84,12 +90,20 @@ export namespace App {
8490
cwd: input.cwd,
8591
},
8692
}
87-
const result = {
93+
const app = {
8894
services,
8995
info,
9096
}
9197

92-
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+
})
93107
}
94108

95109
export function state<State>(
@@ -115,22 +129,6 @@ export namespace App {
115129
return ctx.use().info
116130
}
117131

118-
export async function provide<T>(
119-
input: { cwd: string },
120-
cb: (app: Info) => Promise<T>,
121-
) {
122-
const app = await create(input)
123-
return ctx.provide(app, async () => {
124-
const result = await cb(app.info)
125-
for (const [key, entry] of app.services.entries()) {
126-
if (!entry.shutdown) continue
127-
log.info("shutdown", { name: key })
128-
await entry.shutdown?.(await entry.state)
129-
}
130-
return result
131-
})
132-
}
133-
134132
export async function initialize() {
135133
const { info } = ctx.use()
136134
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://opencode.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://opencode.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)