Merge remote-tracking branch 'origin/v2' into client-service
# Conflicts: # packages/core/src/session/generate-node.ts # packages/core/src/session/title.ts # packages/util/src/observability.ts
This commit is contained in:
commit
689e17738c
41 changed files with 238 additions and 93 deletions
1
bun.lock
1
bun.lock
|
|
@ -968,7 +968,6 @@
|
|||
"@effect/platform-node": "catalog:",
|
||||
"@npmcli/arborist": "catalog:",
|
||||
"@npmcli/config": "10.8.1",
|
||||
"@opencode-ai/ai": "workspace:*",
|
||||
"@opentelemetry/api": "1.9.0",
|
||||
"@opentelemetry/context-async-hooks": "2.6.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "0.214.0",
|
||||
|
|
|
|||
13
packages/core/src/effect/app-node-platform.ts
Normal file
13
packages/core/src/effect/app-node-platform.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { LLMClient, RequestExecutor } from "@opencode-ai/ai/route"
|
||||
import { makeGlobalNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { httpClient } from "@opencode-ai/util/effect/app-node-platform"
|
||||
|
||||
export const requestExecutor = makeGlobalNode({
|
||||
service: RequestExecutor.Service,
|
||||
layer: RequestExecutor.layer,
|
||||
deps: [httpClient],
|
||||
})
|
||||
|
||||
export const llmClient = makeGlobalNode({ service: LLMClient.Service, layer: LLMClient.layer, deps: [requestExecutor] })
|
||||
|
||||
export * as LayerNodePlatform from "./app-node-platform"
|
||||
|
|
@ -4,7 +4,7 @@ import { LLM, LLMClient, LLMError } from "@opencode-ai/ai"
|
|||
import { Context, Effect, Layer, Schema } from "effect"
|
||||
import { Catalog } from "./catalog"
|
||||
import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { llmClient } from "./effect/app-node-platform"
|
||||
import { Integration } from "./integration"
|
||||
import { ModelV2 } from "./model"
|
||||
import { SessionRunnerModel } from "./session/runner/model"
|
||||
|
|
|
|||
|
|
@ -649,7 +649,7 @@ export const layer = (options?: Options) =>
|
|||
|
||||
if (fetch && !process.argv.includes("--get-yargs-completions")) {
|
||||
// Schedule.spaced runs the effect once, then waits between completions.
|
||||
yield* Effect.forkScoped(refresh().pipe(Effect.repeat(Schedule.spaced("60 minutes")), Effect.ignore))
|
||||
yield* Effect.forkScoped(refresh().pipe(Effect.repeat(Schedule.spaced(ttl)), Effect.ignore))
|
||||
}
|
||||
|
||||
return Service.of({ get, refresh })
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Context, Effect, Layer, Stream } from "effect"
|
|||
import { Config } from "../config"
|
||||
import { EventV2 } from "../event"
|
||||
import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { llmClient } from "../effect/app-node-platform"
|
||||
import { SessionEvent } from "./event"
|
||||
import type { SessionMessage } from "./message"
|
||||
import { SessionModelHeaders } from "./model-headers"
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { LLM, LLMClient, Message, SystemPart } from "@opencode-ai/ai"
|
|||
import { Effect, Layer } from "effect"
|
||||
import { Database } from "../database/database"
|
||||
import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { Client } from "@opencode-ai/util/client"
|
||||
import { llmClient } from "../effect/app-node-platform"
|
||||
import { PluginHooks } from "../plugin/hooks"
|
||||
import { SessionContext } from "./context"
|
||||
import { SessionGenerate } from "./generate"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import { Service } from "./index"
|
|||
import { createLLMEventPublisher } from "./publish-llm-event"
|
||||
import { Snapshot } from "../../snapshot"
|
||||
import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { llmClient } from "../../effect/app-node-platform"
|
||||
import { StepFailedError } from "../error"
|
||||
import { toSessionError } from "../to-session-error"
|
||||
import { SessionRunnerRetry } from "./retry"
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import { AgentV2 } from "../agent"
|
|||
import { Database } from "../database/database"
|
||||
import { EventV2 } from "../event"
|
||||
import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { Client } from "@opencode-ai/util/client"
|
||||
import { llmClient } from "../effect/app-node-platform"
|
||||
import { SessionEvent } from "./event"
|
||||
import { SessionHistory } from "./history"
|
||||
import { SessionModelHeaders } from "./model-headers"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { OpenAIChat } from "@opencode-ai/ai/protocols"
|
|||
import { Config } from "@opencode-ai/core/config"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { llmClient } from "@opencode-ai/core/effect/app-node-platform"
|
||||
import { LayerNode } from "@opencode-ai/util/effect/layer-node"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { EventTable } from "@opencode-ai/core/event/sql"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { OpenAIChat } from "@opencode-ai/ai/protocols"
|
|||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { llmClient } from "@opencode-ai/core/effect/app-node-platform"
|
||||
import { LayerNode } from "@opencode-ai/util/effect/layer-node"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { EventTable } from "@opencode-ai/core/event/sql"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Auth, LLMClient, RequestExecutor } from "@opencode-ai/ai/route"
|
|||
import { Catalog } from "@opencode-ai/core/catalog"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { LayerNodePlatform } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
|
||||
import { LayerNode } from "@opencode-ai/util/effect/layer-node"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { EventTable } from "@opencode-ai/core/event/sql"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
|||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { LayerNodePlatform } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
|
||||
import { LayerNode } from "@opencode-ai/util/effect/layer-node"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { InstallationVersion } from "@opencode-ai/util/installation/version"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { OpenAIChat } from "@opencode-ai/ai/protocols"
|
|||
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||
import { Database } from "@opencode-ai/core/database/database"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { llmClient } from "@opencode-ai/util/effect/app-node-platform"
|
||||
import { llmClient } from "@opencode-ai/core/effect/app-node-platform"
|
||||
import { LayerNode } from "@opencode-ai/util/effect/layer-node"
|
||||
import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { SessionEvent } from "@opencode-ai/core/session/event"
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import { RunFormBody } from "./footer.form"
|
|||
import { createFormBodyState, type FormBodyState } from "./form.shared"
|
||||
import { footerWidthPolicy } from "./footer.width"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { modelInfo } from "./variant.shared"
|
||||
|
||||
import type {
|
||||
FooterPromptRoute,
|
||||
|
|
@ -157,6 +158,10 @@ export function RunFooterView(props: RunFooterViewProps) {
|
|||
return tabs().findIndex((item) => item.sessionID === sessionID) + 1
|
||||
})
|
||||
const foregroundSubagents = createMemo(() => activeTabs().some((item) => !item.background))
|
||||
const model = createMemo(() => {
|
||||
const current = props.currentModel()
|
||||
return current ? modelInfo(props.providers(), current).model : undefined
|
||||
})
|
||||
const detail = createMemo(() => {
|
||||
const current = route()
|
||||
return current.type === "subagent" ? subagent().details[current.sessionID] : undefined
|
||||
|
|
@ -364,6 +369,14 @@ export function RunFooterView(props: RunFooterViewProps) {
|
|||
|
||||
return usage()
|
||||
})
|
||||
const modelStatus = createMemo(() => {
|
||||
const current = model()
|
||||
if (!prompt() || !responsive().statusline.showModel || !current) return
|
||||
return {
|
||||
model: current,
|
||||
variant: responsive().statusline.showModelVariant ? props.currentVariant() : undefined,
|
||||
}
|
||||
})
|
||||
const statusColor = createMemo(() => {
|
||||
if (exiting()) {
|
||||
return theme().error
|
||||
|
|
@ -381,6 +394,7 @@ export function RunFooterView(props: RunFooterViewProps) {
|
|||
})
|
||||
const statuslineBackground = createMemo(() => theme().status)
|
||||
const hasActivityMeta = createMemo(() => activityMeta().length > 0)
|
||||
const hasModelStatus = createMemo(() => Boolean(modelStatus()))
|
||||
const contextHints = createMemo(() => {
|
||||
if (!prompt() || shell() || !responsive().statusline.showContextHints) {
|
||||
return []
|
||||
|
|
@ -819,11 +833,29 @@ export function RunFooterView(props: RunFooterViewProps) {
|
|||
</box>
|
||||
</Show>
|
||||
|
||||
<Show when={modelStatus()}>
|
||||
{(info) => (
|
||||
<box
|
||||
minWidth={8}
|
||||
paddingRight={1}
|
||||
backgroundColor="transparent"
|
||||
flexShrink={1}
|
||||
>
|
||||
<text fg={theme().text} wrapMode="none" truncate>
|
||||
{info().model}
|
||||
<Show when={info().variant}>
|
||||
{(variant) => <span style={{ fg: theme().warning, bold: true }}> {variant()}</span>}
|
||||
</Show>
|
||||
</text>
|
||||
</box>
|
||||
)}
|
||||
</Show>
|
||||
|
||||
<For each={contextHints()}>
|
||||
{(hint, index) => (
|
||||
<box paddingRight={1} backgroundColor="transparent" flexShrink={0} maxWidth={24}>
|
||||
<text fg={theme().text} wrapMode="none" truncate>
|
||||
<Show when={index() > 0 || (hasActivityMeta() && index() === 0)}>
|
||||
<Show when={index() > 0 || ((hasActivityMeta() || hasModelStatus()) && index() === 0)}>
|
||||
{sectionSeparator()}
|
||||
</Show>
|
||||
<span style={{ fg: theme().text }}>{hint.key}</span>{" "}
|
||||
|
|
@ -837,7 +869,7 @@ export function RunFooterView(props: RunFooterViewProps) {
|
|||
{(hint) => (
|
||||
<box paddingRight={1} backgroundColor="transparent" flexShrink={0} maxWidth={18}>
|
||||
<text fg={theme().text} wrapMode="none" truncate>
|
||||
<Show when={hasActivityMeta() || hasContextHints()}>
|
||||
<Show when={hasActivityMeta() || hasModelStatus() || hasContextHints()}>
|
||||
{sectionSeparator()}
|
||||
</Show>
|
||||
<span style={{ fg: theme().text }}>{hint().key}</span>{" "}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
// Shared responsive width policy
|
||||
|
||||
const FOOTER_WIDTH_BREAKPOINTS = {
|
||||
commandHint: 24,
|
||||
model: 32,
|
||||
modelVariant: 40,
|
||||
compact: 80,
|
||||
commandHint: 66,
|
||||
context: 120,
|
||||
spacious: 150,
|
||||
} as const
|
||||
|
|
@ -19,6 +21,8 @@ export function footerWidthPolicy(width: number) {
|
|||
statusline: {
|
||||
showActivityMeta: compact,
|
||||
showCommandHint: width >= FOOTER_WIDTH_BREAKPOINTS.commandHint,
|
||||
showModel: width >= FOOTER_WIDTH_BREAKPOINTS.model,
|
||||
showModelVariant: width >= FOOTER_WIDTH_BREAKPOINTS.modelVariant,
|
||||
showContextHints: compact,
|
||||
contextHintLimit: !compact ? 0 : spacious ? undefined : context ? 2 : 1,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1074,8 +1074,8 @@ test("direct footer shows authoritative pending work while running", async () =>
|
|||
const statusItems = statusline.getChildren().filter((item): item is BoxRenderable => item instanceof BoxRenderable)
|
||||
const main = statusItems[0]
|
||||
const spinner = main.getChildren()[0]
|
||||
const background = statusItems[1]
|
||||
const queued = statusItems[2]
|
||||
const background = statusItems[2]
|
||||
const queued = statusItems[3]
|
||||
const hint = statusItems.at(-1)!
|
||||
|
||||
expect(spinner).toBeDefined()
|
||||
|
|
@ -1098,11 +1098,36 @@ test("direct footer shows authoritative pending work while running", async () =>
|
|||
}
|
||||
})
|
||||
|
||||
test("direct footer progressively adds model details after the command hint", async () => {
|
||||
for (const expected of [
|
||||
{ width: 24, model: false, variant: false },
|
||||
{ width: 32, model: true, variant: false },
|
||||
{ width: 40, model: true, variant: true },
|
||||
]) {
|
||||
const app = await renderFooter({
|
||||
providers: [provider()],
|
||||
currentModel: { providerID: "opencode", modelID: "gpt-5" },
|
||||
currentVariant: "xhigh",
|
||||
width: expected.width,
|
||||
})
|
||||
|
||||
try {
|
||||
await app.renderOnce()
|
||||
const frame = app.captureCharFrame()
|
||||
expect({
|
||||
width: expected.width,
|
||||
command: frame.includes("ctrl+p cmd"),
|
||||
model: frame.includes("GPT-5"),
|
||||
variant: frame.includes("xhigh"),
|
||||
}).toEqual({ ...expected, command: true })
|
||||
} finally {
|
||||
app.cleanup()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test("direct footer always offers backgrounding for a foreground subagent", async () => {
|
||||
const app = await renderFooter({
|
||||
providers: [provider()],
|
||||
currentModel: { providerID: "opencode", modelID: "gpt-5" },
|
||||
currentVariant: "xhigh",
|
||||
subagents: {
|
||||
tabs: [subagent({ sessionID: "s-1", label: "Explore", description: "Inspect auth flow" })],
|
||||
details: {},
|
||||
|
|
@ -1125,9 +1150,6 @@ test("direct footer always offers backgrounding for a foreground subagent", asyn
|
|||
|
||||
test("direct footer hides the subagent hint when only completed subagents remain", async () => {
|
||||
const app = await renderFooter({
|
||||
providers: [provider()],
|
||||
currentModel: { providerID: "opencode", modelID: "gpt-5" },
|
||||
currentVariant: "xhigh",
|
||||
subagents: {
|
||||
tabs: [subagent({ sessionID: "s-1", label: "Explore", description: "Inspect auth flow", status: "completed" })],
|
||||
details: {},
|
||||
|
|
|
|||
|
|
@ -3,19 +3,16 @@ import { footerWidthPolicy } from "../../src/mini/footer.width"
|
|||
|
||||
describe("run footer width", () => {
|
||||
test("preserves shared dialog and statusline breakpoints", () => {
|
||||
expect([23, 24].map((width) => footerWidthPolicy(width).statusline.showCommandHint)).toEqual([false, true])
|
||||
expect([31, 32].map((width) => footerWidthPolicy(width).statusline.showModel)).toEqual([false, true])
|
||||
expect([39, 40].map((width) => footerWidthPolicy(width).statusline.showModelVariant)).toEqual([false, true])
|
||||
|
||||
const narrow = footerWidthPolicy(79)
|
||||
expect(narrow.dialog.narrow).toBe(true)
|
||||
expect(narrow.statusline.showActivityMeta).toBe(false)
|
||||
expect(narrow.statusline.showCommandHint).toBe(true)
|
||||
expect(narrow.statusline.showContextHints).toBe(false)
|
||||
expect(narrow.statusline.contextHintLimit).toBe(0)
|
||||
|
||||
const command = footerWidthPolicy(65)
|
||||
expect(command.statusline.showCommandHint).toBe(false)
|
||||
|
||||
const commandHint = footerWidthPolicy(66)
|
||||
expect(commandHint.statusline.showCommandHint).toBe(true)
|
||||
|
||||
const compact = footerWidthPolicy(80)
|
||||
expect(compact.dialog.narrow).toBe(false)
|
||||
expect(compact.statusline.showActivityMeta).toBe(true)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,19 @@
|
|||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.18.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/anomalyco/opencode.git",
|
||||
"directory": "packages/util"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
"./effect/layer-node": "./src/effect/layer-node.ts",
|
||||
"./*": "./src/*.ts"
|
||||
|
|
@ -17,6 +27,7 @@
|
|||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "bun run script/build.ts",
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -24,7 +35,6 @@
|
|||
"@effect/platform-node": "catalog:",
|
||||
"@npmcli/arborist": "catalog:",
|
||||
"@npmcli/config": "10.8.1",
|
||||
"@opencode-ai/ai": "workspace:*",
|
||||
"@opentelemetry/api": "1.9.0",
|
||||
"@opentelemetry/context-async-hooks": "2.6.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "0.214.0",
|
||||
|
|
|
|||
9
packages/util/script/build.ts
Normal file
9
packages/util/script/build.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
import { $ } from "bun"
|
||||
import { fileURLToPath } from "node:url"
|
||||
|
||||
process.chdir(fileURLToPath(new URL("..", import.meta.url)))
|
||||
|
||||
await $`rm -rf dist`
|
||||
await $`bun tsc -p tsconfig.build.json`
|
||||
49
packages/util/script/publish.ts
Normal file
49
packages/util/script/publish.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
import { Script } from "@opencode-ai/script"
|
||||
import { $ } from "bun"
|
||||
import { rm } from "node:fs/promises"
|
||||
import { fileURLToPath } from "node:url"
|
||||
|
||||
process.chdir(fileURLToPath(new URL("..", import.meta.url)))
|
||||
|
||||
const originalText = await Bun.file("package.json").text()
|
||||
const pkg = JSON.parse(originalText) as {
|
||||
name: string
|
||||
version: string
|
||||
exports: Record<string, string | { import: string; types: string }>
|
||||
imports: Record<string, Record<string, string>>
|
||||
}
|
||||
const tarball = `${pkg.name.replace("@", "").replace("/", "-")}-${pkg.version}.tgz`
|
||||
|
||||
if ((await $`npm view ${pkg.name}@${pkg.version} version`.nothrow()).exitCode === 0) {
|
||||
console.log(`already published ${pkg.name}@${pkg.version}`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const output = (value: string, extension: ".js" | ".d.ts") =>
|
||||
value.replace("./src/", "./dist/").replace(/\.ts$/, extension)
|
||||
|
||||
try {
|
||||
await $`bun run typecheck`
|
||||
await $`bun run build`
|
||||
pkg.exports = Object.fromEntries(
|
||||
Object.entries(pkg.exports).map(([key, value]) => {
|
||||
if (typeof value !== "string") return [key, value]
|
||||
return [key, { import: output(value, ".js"), types: output(value, ".d.ts") }]
|
||||
}),
|
||||
)
|
||||
pkg.imports = Object.fromEntries(
|
||||
Object.entries(pkg.imports).map(([key, conditions]) => [
|
||||
key,
|
||||
Object.fromEntries(Object.entries(conditions).map(([condition, value]) => [condition, output(value, ".js")])),
|
||||
]),
|
||||
)
|
||||
await Bun.write("package.json", JSON.stringify(pkg, null, 2) + "\n")
|
||||
await rm(tarball, { force: true })
|
||||
await $`bun pm pack`
|
||||
await $`npm publish ${tarball} --tag ${Script.channel} --access public`
|
||||
} finally {
|
||||
await Bun.write("package.json", originalText)
|
||||
await rm(tarball, { force: true })
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
export * as Client from "./client"
|
||||
export * as Client from "./client.js"
|
||||
|
||||
import { Context, Layer } from "effect"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { makeGlobalNode } from "./effect/app-node.js"
|
||||
|
||||
export const Name = Context.Reference<string>("@opencode/Client/Name", {
|
||||
defaultValue: () => "cli",
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ import {
|
|||
import * as NodeChildProcess from "node:child_process"
|
||||
import { PassThrough } from "node:stream"
|
||||
import launch from "cross-spawn"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { filesystem, path } from "./effect/app-node-platform"
|
||||
import { makeGlobalNode } from "./effect/app-node.js"
|
||||
import { filesystem, path } from "./effect/app-node-platform.js"
|
||||
|
||||
const toError = (err: unknown): Error => (err instanceof globalThis.Error ? err : new globalThis.Error(String(err)))
|
||||
|
||||
|
|
@ -495,4 +495,4 @@ const layer: Layer.Layer<ChildProcessSpawner, never, FileSystem.FileSystem | Pat
|
|||
|
||||
export const node = makeGlobalNode({ service: ChildProcessSpawner, layer, deps: [filesystem, path] })
|
||||
|
||||
export * as CrossSpawnSpawner from "./cross-spawn-spawner"
|
||||
export * as CrossSpawnSpawner from "./cross-spawn-spawner.js"
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import { randomUUID } from "crypto"
|
|||
import { Context, Effect, Function, Layer, Option, Schedule, Schema } from "effect"
|
||||
import type { FileSystem, Scope } from "effect"
|
||||
import type { PlatformError } from "effect/PlatformError"
|
||||
import { FSUtil } from "./fs-util"
|
||||
import { Global } from "./global"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { Hash } from "./hash"
|
||||
import { FSUtil } from "./fs-util.js"
|
||||
import { Global } from "./global.js"
|
||||
import { makeGlobalNode } from "./effect/app-node.js"
|
||||
import { Hash } from "./hash.js"
|
||||
|
||||
export namespace EffectFlock {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1,17 +1,10 @@
|
|||
import { NodeFileSystem, NodePath } from "@effect/platform-node"
|
||||
import { LLMClient, RequestExecutor } from "@opencode-ai/ai/route"
|
||||
import { FileSystem, Path } from "effect"
|
||||
import { FetchHttpClient, HttpClient } from "effect/unstable/http"
|
||||
import { makeGlobalNode } from "./app-node"
|
||||
import { makeGlobalNode } from "./app-node.js"
|
||||
|
||||
export const filesystem = makeGlobalNode({ service: FileSystem.FileSystem, layer: NodeFileSystem.layer, deps: [] })
|
||||
export const path = makeGlobalNode({ service: Path.Path, layer: NodePath.layer, deps: [] })
|
||||
export const httpClient = makeGlobalNode({ service: HttpClient.HttpClient, layer: FetchHttpClient.layer, deps: [] })
|
||||
export const requestExecutor = makeGlobalNode({
|
||||
service: RequestExecutor.Service,
|
||||
layer: RequestExecutor.layer,
|
||||
deps: [httpClient],
|
||||
})
|
||||
export const llmClient = makeGlobalNode({ service: LLMClient.Service, layer: LLMClient.layer, deps: [requestExecutor] })
|
||||
|
||||
export * as LayerNodePlatform from "./app-node-platform"
|
||||
export * as LayerNodePlatform from "./app-node-platform.js"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { LayerNode } from "./layer-node"
|
||||
import { LayerNode } from "./layer-node.js"
|
||||
|
||||
export const tags = LayerNode.tags({
|
||||
location: ["global"],
|
||||
|
|
@ -11,4 +11,4 @@ export type LocationNode<A, E = never> = LayerNode.Node<A, E, (typeof tags.value
|
|||
export const makeGlobalNode = tags.make("global")
|
||||
export const makeLocationNode = tags.make("location")
|
||||
|
||||
export * as Node from "./app-node"
|
||||
export * as Node from "./app-node.js"
|
||||
|
|
|
|||
|
|
@ -330,4 +330,4 @@ function flatten(node: AnyNode): readonly AnyNode[] {
|
|||
return node.kind === "group" ? node.dependencies.flatMap(flatten) : [node]
|
||||
}
|
||||
|
||||
export * as LayerNode from "./layer-node"
|
||||
export * as LayerNode from "./layer-node.js"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Layer, type Context, ManagedRuntime, type Effect } from "effect"
|
||||
import { memoMap } from "./memo-map"
|
||||
import { Observability } from "../observability"
|
||||
import { memoMap } from "./memo-map.js"
|
||||
import { Observability } from "../observability.js"
|
||||
|
||||
export function makeRuntime<I, S, E>(service: Context.Service<I, S>, layer: Layer.Layer<I, E>) {
|
||||
let rt: ManagedRuntime.ManagedRuntime<I, E> | undefined
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import path from "path"
|
|||
import os from "os"
|
||||
import { randomBytes, randomUUID } from "crypto"
|
||||
import { mkdir, readFile, rm, stat, utimes, writeFile } from "fs/promises"
|
||||
import { Hash } from "./hash"
|
||||
import { Hash } from "./hash.js"
|
||||
import { Effect } from "effect"
|
||||
|
||||
export type FlockGlobal = {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import { readdir } from "fs/promises"
|
|||
import { lookup } from "mime-types"
|
||||
import { Context, Effect, FileSystem, Layer, Schema } from "effect"
|
||||
import type { PlatformError } from "effect/PlatformError"
|
||||
import { Glob } from "./glob"
|
||||
import { serviceUse } from "./effect/service-use"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { filesystem } from "./effect/app-node-platform"
|
||||
import { Glob } from "./glob.js"
|
||||
import { serviceUse } from "./effect/service-use.js"
|
||||
import { makeGlobalNode } from "./effect/app-node.js"
|
||||
import { filesystem } from "./effect/app-node-platform.js"
|
||||
|
||||
export namespace FSUtil {
|
||||
export class FileSystemError extends Schema.TaggedErrorClass<FileSystemError>()("FileSystemError", {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import fs from "fs/promises"
|
|||
import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
|
||||
import os from "os"
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { Flock } from "./flock"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { Flock } from "./flock.js"
|
||||
import { makeGlobalNode } from "./effect/app-node.js"
|
||||
|
||||
const app = "opencode"
|
||||
const data = path.join(xdgData!, app)
|
||||
|
|
@ -83,4 +83,4 @@ export const layerWith = (input: Partial<Interface>) =>
|
|||
Effect.sync(() => Service.of(make(input))),
|
||||
)
|
||||
|
||||
export * as Global from "./global"
|
||||
export * as Global from "./global.js"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
declare global {
|
||||
const OPENCODE_VERSION: string
|
||||
const OPENCODE_CHANNEL: string
|
||||
}
|
||||
declare const OPENCODE_VERSION: string
|
||||
declare const OPENCODE_CHANNEL: string
|
||||
|
||||
export const InstallationVersion = typeof OPENCODE_VERSION === "string" ? OPENCODE_VERSION : "local"
|
||||
export const InstallationChannel = typeof OPENCODE_CHANNEL === "string" ? OPENCODE_CHANNEL : "local"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export * as NpmConfig from "./npm-config"
|
||||
export * as NpmConfig from "./npm-config.js"
|
||||
|
||||
import { fileURLToPath } from "url"
|
||||
// @ts-expect-error npm does not publish types for this internal config API.
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
export * as Npm from "./npm"
|
||||
export * as Npm from "./npm.js"
|
||||
|
||||
import path from "path"
|
||||
import npa from "npm-package-arg"
|
||||
import { Effect, Schema, Context, Layer, Option, FileSystem } from "effect"
|
||||
import { NodeFileSystem } from "@effect/platform-node"
|
||||
import { FSUtil } from "./fs-util"
|
||||
import { Global } from "./global"
|
||||
import { EffectFlock } from "./effect-flock"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { filesystem } from "./effect/app-node-platform"
|
||||
import { LayerNode } from "./effect/layer-node"
|
||||
import { makeRuntime } from "./effect/runtime"
|
||||
import { NpmConfig } from "./npm-config"
|
||||
import { FSUtil } from "./fs-util.js"
|
||||
import { Global } from "./global.js"
|
||||
import { EffectFlock } from "./effect-flock.js"
|
||||
import { makeGlobalNode } from "./effect/app-node.js"
|
||||
import { filesystem } from "./effect/app-node-platform.js"
|
||||
import { LayerNode } from "./effect/layer-node.js"
|
||||
import { makeRuntime } from "./effect/runtime.js"
|
||||
import { NpmConfig } from "./npm-config.js"
|
||||
import { resolveModule } from "#runtime-import"
|
||||
|
||||
export class InstallFailedError extends Schema.TaggedErrorClass<InstallFailedError>()("NpmInstallFailedError", {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
export * as Observability from "./observability"
|
||||
export * as Observability from "./observability.js"
|
||||
|
||||
import { NodeFileSystem } from "@effect/platform-node"
|
||||
import { LayerNode } from "./effect/layer-node"
|
||||
import { LayerNode } from "./effect/layer-node.js"
|
||||
import { Effect, Layer, Logger, References, Schema } from "effect"
|
||||
import { FetchHttpClient } from "effect/unstable/http"
|
||||
import { OtlpSerialization } from "effect/unstable/observability"
|
||||
import { Logging } from "./observability/logging"
|
||||
import { Otlp } from "./observability/otlp"
|
||||
import { Client } from "./client"
|
||||
import { Logging } from "./observability/logging.js"
|
||||
import { Otlp } from "./observability/otlp.js"
|
||||
import { Client } from "./client.js"
|
||||
|
||||
export const Options = Schema.Struct({
|
||||
endpoint: Schema.optional(Schema.String),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Formatter, Logger, type LogLevel } from "effect"
|
||||
import path from "path"
|
||||
import { Global } from "../global"
|
||||
import { InstallationChannel, InstallationLocal } from "../installation/version"
|
||||
import { runID } from "./shared"
|
||||
import { Global } from "../global.js"
|
||||
import { InstallationChannel, InstallationLocal } from "../installation/version.js"
|
||||
import { runID } from "./shared.js"
|
||||
|
||||
function formatter(id: string = runID) {
|
||||
return Logger.map(Logger.formatStructured, (output) => {
|
||||
|
|
@ -74,4 +74,4 @@ export function loggers() {
|
|||
return process.env.OPENCODE_PRINT_LOGS === "1" ? [fileLogger(), stderrLogger] : [fileLogger()]
|
||||
}
|
||||
|
||||
export * as Logging from "./logging"
|
||||
export * as Logging from "./logging.js"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Layer } from "effect"
|
||||
import { OtlpLogger } from "effect/unstable/observability"
|
||||
import { InstallationChannel, InstallationVersion } from "../installation/version"
|
||||
import { runID } from "./shared"
|
||||
import { InstallationChannel, InstallationVersion } from "../installation/version.js"
|
||||
import { runID } from "./shared.js"
|
||||
|
||||
export interface Options {
|
||||
readonly endpoint?: string
|
||||
|
|
@ -90,4 +90,4 @@ export async function tracingLayer(options: Options | undefined, client: string)
|
|||
}))
|
||||
}
|
||||
|
||||
export * as Otlp from "./otlp"
|
||||
export * as Otlp from "./otlp.js"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export * as Patch from "./patch"
|
||||
export * as Patch from "./patch.js"
|
||||
|
||||
import { Result, Schema } from "effect"
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { Context, Duration, Effect, Fiber, Layer, Schema, Stream } from "effect"
|
|||
import type { PlatformError } from "effect/PlatformError"
|
||||
import { ChildProcess } from "effect/unstable/process"
|
||||
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
|
||||
import { CrossSpawnSpawner } from "./cross-spawn-spawner"
|
||||
import { makeGlobalNode } from "./effect/app-node"
|
||||
import { CrossSpawnSpawner } from "./cross-spawn-spawner.js"
|
||||
import { makeGlobalNode } from "./effect/app-node.js"
|
||||
|
||||
export class AppProcessError extends Schema.TaggedErrorClass<AppProcessError>()("AppProcessError", {
|
||||
command: Schema.String,
|
||||
|
|
@ -258,4 +258,4 @@ const layer = Layer.effect(
|
|||
|
||||
export const node = makeGlobalNode({ service: Service, layer: layer, deps: [CrossSpawnSpawner.node] })
|
||||
|
||||
export * as AppProcess from "./process"
|
||||
export * as AppProcess from "./process.js"
|
||||
|
|
|
|||
11
packages/util/tsconfig.build.json
Normal file
11
packages/util/tsconfig.build.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"noEmit": false,
|
||||
"declaration": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|
@ -2,6 +2,11 @@
|
|||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@tsconfig/bun/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"allowImportingTsExtensions": false,
|
||||
"allowJs": false,
|
||||
"noUncheckedIndexedAccess": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ await $`bun ./packages/schema/script/publish.ts`
|
|||
console.log("\n=== ai ===\n")
|
||||
await $`bun ./packages/ai/script/publish.ts`
|
||||
|
||||
console.log("\n=== util ===\n")
|
||||
await $`bun ./packages/util/script/publish.ts`
|
||||
|
||||
console.log("\n=== protocol ===\n")
|
||||
await $`bun ./packages/protocol/script/publish.ts`
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue