refactor(opencode): improve startup time by 38% (#30453)
Co-authored-by: starptech <starptech@starptechs-MBP.fritz.box>
This commit is contained in:
@@ -52,6 +52,7 @@ const { a, b } = obj
|
|||||||
- Never alias imports. Do not use `import { foo as bar } from "..."` or renamed imports like `resolve as pathResolve`.
|
- Never alias imports. Do not use `import { foo as bar } from "..."` or renamed imports like `resolve as pathResolve`.
|
||||||
- Never use star imports. Do not use `import * as Foo from "..."` or `import type * as Foo from "..."`.
|
- Never use star imports. Do not use `import * as Foo from "..."` or `import type * as Foo from "..."`.
|
||||||
- If a namespace-style value is needed, import the module's own exported namespace by name, for example `import { Project } from "@opencode-ai/core/project"`, then reference `Project.ID`.
|
- If a namespace-style value is needed, import the module's own exported namespace by name, for example `import { Project } from "@opencode-ai/core/project"`, then reference `Project.ID`.
|
||||||
|
- Prefer dynamic imports for heavy modules that are only needed in selected code paths, especially in startup-sensitive entrypoints. Destructure dynamic import bindings near the top of the narrowest scope that needs them so they read like normal imports. Avoid inline chains such as `await import("./module").then((mod) => mod.value())` or `(await import("./module")).value()`. Keep branch-specific imports inside the branch that needs them to preserve lazy loading.
|
||||||
|
|
||||||
### Variables
|
### Variables
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import * as Log from "@opencode-ai/core/util/log"
|
|||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { effectCmd } from "../effect-cmd"
|
import { effectCmd } from "../effect-cmd"
|
||||||
import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk"
|
import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk"
|
||||||
import { ACP } from "@/acp/agent"
|
|
||||||
import { Server } from "@/server/server"
|
|
||||||
import { ServerAuth } from "@/server/auth"
|
import { ServerAuth } from "@/server/auth"
|
||||||
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
|
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
|
||||||
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
||||||
@@ -22,6 +20,8 @@ export const AcpCommand = effectCmd({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
handler: Effect.fn("Cli.acp")(function* (args) {
|
handler: Effect.fn("Cli.acp")(function* (args) {
|
||||||
|
const { Server } = yield* Effect.promise(() => import("@/server/server"))
|
||||||
|
const { ACP } = yield* Effect.promise(() => import("@/acp/agent"))
|
||||||
ACPProfile.mark("cli.acp.handler")
|
ACPProfile.mark("cli.acp.handler")
|
||||||
process.env.OPENCODE_CLIENT = "acp"
|
process.env.OPENCODE_CLIENT = "acp"
|
||||||
const opts = yield* resolveNetworkOptions(args)
|
const opts = yield* resolveNetworkOptions(args)
|
||||||
|
|||||||
@@ -2,13 +2,10 @@ import { cmd } from "./cmd"
|
|||||||
import * as prompts from "@clack/prompts"
|
import * as prompts from "@clack/prompts"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
import { Global } from "@opencode-ai/core/global"
|
import { Global } from "@opencode-ai/core/global"
|
||||||
import { Agent } from "../../agent/agent"
|
|
||||||
import { Provider } from "@/provider/provider"
|
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import fs from "fs/promises"
|
import fs from "fs/promises"
|
||||||
import { Filesystem } from "@/util/filesystem"
|
import { Filesystem } from "@/util/filesystem"
|
||||||
import matter from "gray-matter"
|
import matter from "gray-matter"
|
||||||
import { InstanceRef } from "@/effect/instance-ref"
|
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import type { Argv } from "yargs"
|
import type { Argv } from "yargs"
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
@@ -62,6 +59,9 @@ const AgentCreateCommand = effectCmd({
|
|||||||
describe: "model to use in the format of provider/model",
|
describe: "model to use in the format of provider/model",
|
||||||
}),
|
}),
|
||||||
handler: Effect.fn("Cli.agent.create")(function* (args) {
|
handler: Effect.fn("Cli.agent.create")(function* (args) {
|
||||||
|
const { InstanceRef } = yield* Effect.promise(() => import("@/effect/instance-ref"))
|
||||||
|
const { Agent } = yield* Effect.promise(() => import("../../agent/agent"))
|
||||||
|
const { Provider } = yield* Effect.promise(() => import("@/provider/provider"))
|
||||||
const maybeCtx = yield* InstanceRef
|
const maybeCtx = yield* InstanceRef
|
||||||
if (!maybeCtx) return yield* Effect.die("InstanceRef not provided")
|
if (!maybeCtx) return yield* Effect.die("InstanceRef not provided")
|
||||||
const ctx = maybeCtx
|
const ctx = maybeCtx
|
||||||
@@ -235,6 +235,7 @@ const AgentListCommand = effectCmd({
|
|||||||
command: "list",
|
command: "list",
|
||||||
describe: "list all available agents",
|
describe: "list all available agents",
|
||||||
handler: Effect.fn("Cli.agent.list")(function* () {
|
handler: Effect.fn("Cli.agent.list")(function* () {
|
||||||
|
const { Agent } = yield* Effect.promise(() => import("../../agent/agent"))
|
||||||
const agents = yield* Agent.Service.use((svc) => svc.list())
|
const agents = yield* Agent.Service.use((svc) => svc.list())
|
||||||
const sortedAgents = agents.sort((a, b) => {
|
const sortedAgents = agents.sort((a, b) => {
|
||||||
if (a.native !== b.native) {
|
if (a.native !== b.native) {
|
||||||
|
|||||||
@@ -0,0 +1,193 @@
|
|||||||
|
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||||
|
import { EOL } from "os"
|
||||||
|
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
|
||||||
|
import { basename } from "path"
|
||||||
|
import { Cause, Effect } from "effect"
|
||||||
|
import { Agent } from "../../../agent/agent"
|
||||||
|
import { Provider } from "@/provider/provider"
|
||||||
|
import { Session } from "@/session/session"
|
||||||
|
import type { MessageV2 } from "../../../session/message-v2"
|
||||||
|
import { MessageID, PartID } from "../../../session/schema"
|
||||||
|
import { ToolRegistry } from "@/tool/registry"
|
||||||
|
import { Permission } from "../../../permission"
|
||||||
|
import { iife } from "../../../util/iife"
|
||||||
|
import { fail } from "../../effect-cmd"
|
||||||
|
import { InstanceRef } from "@/effect/instance-ref"
|
||||||
|
import type { InstanceContext } from "@/project/instance-context"
|
||||||
|
|
||||||
|
export const debugAgent = Effect.fn("Cli.debug.agent")(function* (args: {
|
||||||
|
name: string
|
||||||
|
tool?: string
|
||||||
|
params?: string
|
||||||
|
}) {
|
||||||
|
const ctx = yield* InstanceRef
|
||||||
|
if (!ctx) return
|
||||||
|
return yield* run(args, ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
const run = Effect.fn("Cli.debug.agent.body")(function* (
|
||||||
|
args: { name: string; tool?: string; params?: string },
|
||||||
|
ctx: InstanceContext,
|
||||||
|
) {
|
||||||
|
const agentName = args.name
|
||||||
|
const agent = yield* Agent.Service.use((svc) => svc.get(agentName))
|
||||||
|
if (!agent) {
|
||||||
|
process.stderr.write(
|
||||||
|
`Agent ${agentName} not found, run '${basename(process.execPath)} agent list' to get an agent list` + EOL,
|
||||||
|
)
|
||||||
|
return yield* fail("", 1)
|
||||||
|
}
|
||||||
|
const availableTools = yield* getAvailableTools(agent)
|
||||||
|
const resolvedTools = resolveTools(agent, availableTools)
|
||||||
|
const toolID = args.tool
|
||||||
|
if (toolID) {
|
||||||
|
const tool = availableTools.find((item) => item.id === toolID)
|
||||||
|
if (!tool) {
|
||||||
|
process.stderr.write(`Tool ${toolID} not found for agent ${agentName}` + EOL)
|
||||||
|
return yield* fail("", 1)
|
||||||
|
}
|
||||||
|
if (resolvedTools[toolID] === false) {
|
||||||
|
process.stderr.write(`Tool ${toolID} is disabled for agent ${agentName}` + EOL)
|
||||||
|
return yield* fail("", 1)
|
||||||
|
}
|
||||||
|
const params = parseToolParams(args.params)
|
||||||
|
const toolCtx = yield* createToolContext(agent, ctx)
|
||||||
|
const result = yield* tool.execute(params, toolCtx)
|
||||||
|
process.stdout.write(JSON.stringify({ tool: toolID, input: params, result }, null, 2) + EOL)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const output = {
|
||||||
|
...agent,
|
||||||
|
tools: resolvedTools,
|
||||||
|
}
|
||||||
|
process.stdout.write(JSON.stringify(output, null, 2) + EOL)
|
||||||
|
})
|
||||||
|
|
||||||
|
const getAvailableTools = Effect.fn("Cli.debug.agent.getAvailableTools")(function* (agent: Agent.Info) {
|
||||||
|
const provider = yield* Provider.Service
|
||||||
|
const registry = yield* ToolRegistry.Service
|
||||||
|
const model =
|
||||||
|
agent.model ??
|
||||||
|
(yield* provider.defaultModel().pipe(
|
||||||
|
Effect.matchCauseEffect({
|
||||||
|
onSuccess: Effect.succeed,
|
||||||
|
onFailure: (cause) => {
|
||||||
|
const error = Cause.squash(cause) as Provider.DefaultModelError
|
||||||
|
if (error instanceof Provider.ModelNotFoundError) {
|
||||||
|
return fail(`Model not found: ${error.providerID}/${error.modelID}`)
|
||||||
|
}
|
||||||
|
if (error instanceof Provider.NoModelsError) return fail(`No models found for provider ${error.providerID}`)
|
||||||
|
return fail("No providers found")
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
return yield* registry.tools({ ...model, agent })
|
||||||
|
})
|
||||||
|
|
||||||
|
function resolveTools(agent: Agent.Info, availableTools: { id: string }[]) {
|
||||||
|
const disabled = Permission.disabled(
|
||||||
|
availableTools.map((tool) => tool.id),
|
||||||
|
agent.permission,
|
||||||
|
)
|
||||||
|
const resolved: Record<string, boolean> = {}
|
||||||
|
for (const tool of availableTools) {
|
||||||
|
resolved[tool.id] = !disabled.has(tool.id)
|
||||||
|
}
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseToolParams(input?: string) {
|
||||||
|
if (!input) return {}
|
||||||
|
const trimmed = input.trim()
|
||||||
|
if (trimmed.length === 0) return {}
|
||||||
|
|
||||||
|
const parsed = iife(() => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(trimmed)
|
||||||
|
} catch (jsonError) {
|
||||||
|
try {
|
||||||
|
return new Function(`return (${trimmed})`)()
|
||||||
|
} catch (evalError) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to parse --params. Use JSON or a JS object literal. JSON error: ${jsonError}. Eval error: ${evalError}.`,
|
||||||
|
{ cause: evalError },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
||||||
|
throw new Error("Tool params must be an object.")
|
||||||
|
}
|
||||||
|
return parsed as Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
const createToolContext = Effect.fn("Cli.debug.agent.createToolContext")(function* (
|
||||||
|
agent: Agent.Info,
|
||||||
|
ctx: InstanceContext,
|
||||||
|
) {
|
||||||
|
const sessionSvc = yield* Session.Service
|
||||||
|
const session = yield* sessionSvc.create({ title: `Debug tool run (${agent.name})` })
|
||||||
|
const messageID = MessageID.ascending()
|
||||||
|
const model = agent.model
|
||||||
|
? agent.model
|
||||||
|
: yield* Effect.gen(function* () {
|
||||||
|
const provider = yield* Provider.Service
|
||||||
|
return yield* provider.defaultModel().pipe(
|
||||||
|
Effect.matchCauseEffect({
|
||||||
|
onSuccess: Effect.succeed,
|
||||||
|
onFailure: (cause) => {
|
||||||
|
const error = Cause.squash(cause) as Provider.DefaultModelError
|
||||||
|
if (error instanceof Provider.ModelNotFoundError) {
|
||||||
|
return fail(`Model not found: ${error.providerID}/${error.modelID}`)
|
||||||
|
}
|
||||||
|
if (error instanceof Provider.NoModelsError)
|
||||||
|
return fail(`No models found for provider ${error.providerID}`)
|
||||||
|
return fail("No providers found")
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
const now = Date.now()
|
||||||
|
const message: SessionLegacy.Assistant = {
|
||||||
|
id: messageID,
|
||||||
|
sessionID: session.id,
|
||||||
|
role: "assistant",
|
||||||
|
time: { created: now },
|
||||||
|
parentID: messageID,
|
||||||
|
modelID: model.modelID,
|
||||||
|
providerID: model.providerID,
|
||||||
|
mode: "debug",
|
||||||
|
agent: agent.name,
|
||||||
|
path: {
|
||||||
|
cwd: ctx.directory,
|
||||||
|
root: ctx.worktree,
|
||||||
|
},
|
||||||
|
cost: 0,
|
||||||
|
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||||
|
}
|
||||||
|
yield* sessionSvc.updateMessage(message)
|
||||||
|
|
||||||
|
const ruleset = Permission.merge(agent.permission, session.permission ?? [])
|
||||||
|
|
||||||
|
return {
|
||||||
|
sessionID: session.id,
|
||||||
|
messageID,
|
||||||
|
callID: PartID.ascending(),
|
||||||
|
agent: agent.name,
|
||||||
|
abort: new AbortController().signal,
|
||||||
|
messages: [],
|
||||||
|
metadata: () => Effect.void,
|
||||||
|
ask(req: Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">) {
|
||||||
|
return Effect.sync(() => {
|
||||||
|
for (const pattern of req.patterns) {
|
||||||
|
const rule = Permission.evaluate(req.permission, pattern, ruleset)
|
||||||
|
if (rule.action === "deny") {
|
||||||
|
throw new PermissionLegacy.DeniedError({ ruleset })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,19 +1,5 @@
|
|||||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
import { Effect } from "effect"
|
||||||
import { EOL } from "os"
|
import { effectCmd } from "../../effect-cmd"
|
||||||
import { SessionLegacy } from "@opencode-ai/core/session/legacy"
|
|
||||||
import { basename } from "path"
|
|
||||||
import { Cause, Effect } from "effect"
|
|
||||||
import { Agent } from "../../../agent/agent"
|
|
||||||
import { Provider } from "@/provider/provider"
|
|
||||||
import { Session } from "@/session/session"
|
|
||||||
import type { MessageV2 } from "../../../session/message-v2"
|
|
||||||
import { MessageID, PartID } from "../../../session/schema"
|
|
||||||
import { ToolRegistry } from "@/tool/registry"
|
|
||||||
import { Permission } from "../../../permission"
|
|
||||||
import { iife } from "../../../util/iife"
|
|
||||||
import { effectCmd, fail } from "../../effect-cmd"
|
|
||||||
import { InstanceRef } from "@/effect/instance-ref"
|
|
||||||
import type { InstanceContext } from "@/project/instance-context"
|
|
||||||
|
|
||||||
export const AgentCommand = effectCmd({
|
export const AgentCommand = effectCmd({
|
||||||
command: "agent <name>",
|
command: "agent <name>",
|
||||||
@@ -33,176 +19,9 @@ export const AgentCommand = effectCmd({
|
|||||||
type: "string",
|
type: "string",
|
||||||
description: "Tool params as JSON or a JS object literal",
|
description: "Tool params as JSON or a JS object literal",
|
||||||
}),
|
}),
|
||||||
handler: Effect.fn("Cli.debug.agent")(function* (args) {
|
handler: (args) =>
|
||||||
const ctx = yield* InstanceRef
|
Effect.gen(function* () {
|
||||||
if (!ctx) return
|
const { debugAgent } = yield* Effect.promise(() => import("./agent.handler"))
|
||||||
return yield* run(args, ctx)
|
return yield* debugAgent(args)
|
||||||
}),
|
}),
|
||||||
})
|
|
||||||
|
|
||||||
const run = Effect.fn("Cli.debug.agent.body")(function* (
|
|
||||||
args: { name: string; tool?: string; params?: string },
|
|
||||||
ctx: InstanceContext,
|
|
||||||
) {
|
|
||||||
const agentName = args.name
|
|
||||||
const agent = yield* Agent.Service.use((svc) => svc.get(agentName))
|
|
||||||
if (!agent) {
|
|
||||||
process.stderr.write(
|
|
||||||
`Agent ${agentName} not found, run '${basename(process.execPath)} agent list' to get an agent list` + EOL,
|
|
||||||
)
|
|
||||||
return yield* fail("", 1)
|
|
||||||
}
|
|
||||||
const availableTools = yield* getAvailableTools(agent)
|
|
||||||
const resolvedTools = resolveTools(agent, availableTools)
|
|
||||||
const toolID = args.tool
|
|
||||||
if (toolID) {
|
|
||||||
const tool = availableTools.find((item) => item.id === toolID)
|
|
||||||
if (!tool) {
|
|
||||||
process.stderr.write(`Tool ${toolID} not found for agent ${agentName}` + EOL)
|
|
||||||
return yield* fail("", 1)
|
|
||||||
}
|
|
||||||
if (resolvedTools[toolID] === false) {
|
|
||||||
process.stderr.write(`Tool ${toolID} is disabled for agent ${agentName}` + EOL)
|
|
||||||
return yield* fail("", 1)
|
|
||||||
}
|
|
||||||
const params = parseToolParams(args.params)
|
|
||||||
const toolCtx = yield* createToolContext(agent, ctx)
|
|
||||||
const result = yield* tool.execute(params, toolCtx)
|
|
||||||
process.stdout.write(JSON.stringify({ tool: toolID, input: params, result }, null, 2) + EOL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = {
|
|
||||||
...agent,
|
|
||||||
tools: resolvedTools,
|
|
||||||
}
|
|
||||||
process.stdout.write(JSON.stringify(output, null, 2) + EOL)
|
|
||||||
})
|
|
||||||
|
|
||||||
const getAvailableTools = Effect.fn("Cli.debug.agent.getAvailableTools")(function* (agent: Agent.Info) {
|
|
||||||
const provider = yield* Provider.Service
|
|
||||||
const registry = yield* ToolRegistry.Service
|
|
||||||
const model =
|
|
||||||
agent.model ??
|
|
||||||
(yield* provider.defaultModel().pipe(
|
|
||||||
Effect.matchCauseEffect({
|
|
||||||
onSuccess: Effect.succeed,
|
|
||||||
onFailure: (cause) => {
|
|
||||||
const error = Cause.squash(cause) as Provider.DefaultModelError
|
|
||||||
if (error instanceof Provider.ModelNotFoundError) {
|
|
||||||
return fail(`Model not found: ${error.providerID}/${error.modelID}`)
|
|
||||||
}
|
|
||||||
if (error instanceof Provider.NoModelsError) return fail(`No models found for provider ${error.providerID}`)
|
|
||||||
return fail("No providers found")
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
return yield* registry.tools({ ...model, agent })
|
|
||||||
})
|
|
||||||
|
|
||||||
function resolveTools(agent: Agent.Info, availableTools: { id: string }[]) {
|
|
||||||
const disabled = Permission.disabled(
|
|
||||||
availableTools.map((tool) => tool.id),
|
|
||||||
agent.permission,
|
|
||||||
)
|
|
||||||
const resolved: Record<string, boolean> = {}
|
|
||||||
for (const tool of availableTools) {
|
|
||||||
resolved[tool.id] = !disabled.has(tool.id)
|
|
||||||
}
|
|
||||||
return resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseToolParams(input?: string) {
|
|
||||||
if (!input) return {}
|
|
||||||
const trimmed = input.trim()
|
|
||||||
if (trimmed.length === 0) return {}
|
|
||||||
|
|
||||||
const parsed = iife(() => {
|
|
||||||
try {
|
|
||||||
return JSON.parse(trimmed)
|
|
||||||
} catch (jsonError) {
|
|
||||||
try {
|
|
||||||
return new Function(`return (${trimmed})`)()
|
|
||||||
} catch (evalError) {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to parse --params. Use JSON or a JS object literal. JSON error: ${jsonError}. Eval error: ${evalError}.`,
|
|
||||||
{ cause: evalError },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
||||||
throw new Error("Tool params must be an object.")
|
|
||||||
}
|
|
||||||
return parsed as Record<string, unknown>
|
|
||||||
}
|
|
||||||
|
|
||||||
const createToolContext = Effect.fn("Cli.debug.agent.createToolContext")(function* (
|
|
||||||
agent: Agent.Info,
|
|
||||||
ctx: InstanceContext,
|
|
||||||
) {
|
|
||||||
const sessionSvc = yield* Session.Service
|
|
||||||
const session = yield* sessionSvc.create({ title: `Debug tool run (${agent.name})` })
|
|
||||||
const messageID = MessageID.ascending()
|
|
||||||
const model = agent.model
|
|
||||||
? agent.model
|
|
||||||
: yield* Effect.gen(function* () {
|
|
||||||
const provider = yield* Provider.Service
|
|
||||||
return yield* provider.defaultModel().pipe(
|
|
||||||
Effect.matchCauseEffect({
|
|
||||||
onSuccess: Effect.succeed,
|
|
||||||
onFailure: (cause) => {
|
|
||||||
const error = Cause.squash(cause) as Provider.DefaultModelError
|
|
||||||
if (error instanceof Provider.ModelNotFoundError) {
|
|
||||||
return fail(`Model not found: ${error.providerID}/${error.modelID}`)
|
|
||||||
}
|
|
||||||
if (error instanceof Provider.NoModelsError)
|
|
||||||
return fail(`No models found for provider ${error.providerID}`)
|
|
||||||
return fail("No providers found")
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
const now = Date.now()
|
|
||||||
const message: SessionLegacy.Assistant = {
|
|
||||||
id: messageID,
|
|
||||||
sessionID: session.id,
|
|
||||||
role: "assistant",
|
|
||||||
time: { created: now },
|
|
||||||
parentID: messageID,
|
|
||||||
modelID: model.modelID,
|
|
||||||
providerID: model.providerID,
|
|
||||||
mode: "debug",
|
|
||||||
agent: agent.name,
|
|
||||||
path: {
|
|
||||||
cwd: ctx.directory,
|
|
||||||
root: ctx.worktree,
|
|
||||||
},
|
|
||||||
cost: 0,
|
|
||||||
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
||||||
}
|
|
||||||
yield* sessionSvc.updateMessage(message)
|
|
||||||
|
|
||||||
const ruleset = Permission.merge(agent.permission, session.permission ?? [])
|
|
||||||
|
|
||||||
return {
|
|
||||||
sessionID: session.id,
|
|
||||||
messageID,
|
|
||||||
callID: PartID.ascending(),
|
|
||||||
agent: agent.name,
|
|
||||||
abort: new AbortController().signal,
|
|
||||||
messages: [],
|
|
||||||
metadata: () => Effect.void,
|
|
||||||
ask(req: Omit<PermissionLegacy.Request, "id" | "sessionID" | "tool">) {
|
|
||||||
return Effect.sync(() => {
|
|
||||||
for (const pattern of req.patterns) {
|
|
||||||
const rule = Permission.evaluate(req.permission, pattern, ruleset)
|
|
||||||
if (rule.action === "deny") {
|
|
||||||
throw new PermissionLegacy.DeniedError({ ruleset })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { Config } from "@/config/config"
|
|
||||||
import { effectCmd } from "../../effect-cmd"
|
import { effectCmd } from "../../effect-cmd"
|
||||||
|
|
||||||
export const ConfigCommand = effectCmd({
|
export const ConfigCommand = effectCmd({
|
||||||
@@ -8,6 +7,7 @@ export const ConfigCommand = effectCmd({
|
|||||||
describe: "show resolved configuration",
|
describe: "show resolved configuration",
|
||||||
builder: (yargs) => yargs,
|
builder: (yargs) => yargs,
|
||||||
handler: Effect.fn("Cli.debug.config")(function* () {
|
handler: Effect.fn("Cli.debug.config")(function* () {
|
||||||
|
const { Config } = yield* Effect.promise(() => import("@/config/config"))
|
||||||
const config = yield* Config.Service.use((cfg) => cfg.get())
|
const config = yield* Config.Service.use((cfg) => cfg.get())
|
||||||
process.stdout.write(JSON.stringify(config, null, 2) + EOL)
|
process.stdout.write(JSON.stringify(config, null, 2) + EOL)
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
|||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||||
import os from "os"
|
import os from "os"
|
||||||
import { Duration, Effect } from "effect"
|
import { Duration, Effect } from "effect"
|
||||||
import { Config } from "@/config/config"
|
|
||||||
import { ConfigPlugin } from "@/config/plugin"
|
|
||||||
import { effectCmd } from "../../effect-cmd"
|
import { effectCmd } from "../../effect-cmd"
|
||||||
import { cmd } from "../cmd"
|
import { cmd } from "../cmd"
|
||||||
import { ConfigCommand } from "./config"
|
import { ConfigCommand } from "./config"
|
||||||
@@ -52,6 +50,8 @@ const InfoCommand = effectCmd({
|
|||||||
command: "info",
|
command: "info",
|
||||||
describe: "show debug information",
|
describe: "show debug information",
|
||||||
handler: Effect.fn("Cli.debug.info")(function* () {
|
handler: Effect.fn("Cli.debug.info")(function* () {
|
||||||
|
const { Config } = yield* Effect.promise(() => import("@/config/config"))
|
||||||
|
const { ConfigPlugin } = yield* Effect.promise(() => import("@/config/plugin"))
|
||||||
const config = yield* Config.Service.use((cfg) => cfg.get())
|
const config = yield* Config.Service.use((cfg) => cfg.get())
|
||||||
const termProgram = process.env.TERM_PROGRAM
|
const termProgram = process.env.TERM_PROGRAM
|
||||||
? `${process.env.TERM_PROGRAM}${process.env.TERM_PROGRAM_VERSION ? ` ${process.env.TERM_PROGRAM_VERSION}` : ""}`
|
? `${process.env.TERM_PROGRAM}${process.env.TERM_PROGRAM_VERSION ? ` ${process.env.TERM_PROGRAM_VERSION}` : ""}`
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import { Project } from "@/project/project"
|
|
||||||
import * as Log from "@opencode-ai/core/util/log"
|
import * as Log from "@opencode-ai/core/util/log"
|
||||||
import { makeRuntime } from "@opencode-ai/core/effect/runtime"
|
|
||||||
import { cmd } from "../cmd"
|
import { cmd } from "../cmd"
|
||||||
|
|
||||||
const runtime = makeRuntime(Project.Service, Project.defaultLayer)
|
|
||||||
|
|
||||||
export const ScrapCommand = cmd({
|
export const ScrapCommand = cmd({
|
||||||
command: "scrap",
|
command: "scrap",
|
||||||
describe: "list all known projects",
|
describe: "list all known projects",
|
||||||
builder: (yargs) => yargs,
|
builder: (yargs) => yargs,
|
||||||
async handler() {
|
async handler() {
|
||||||
|
const { Project } = await import("@/project/project")
|
||||||
|
const { makeRuntime } = await import("@opencode-ai/core/effect/runtime")
|
||||||
|
const runtime = makeRuntime(Project.Service, Project.defaultLayer)
|
||||||
const timer = Log.Default.time("scrap")
|
const timer = Log.Default.time("scrap")
|
||||||
const list = await runtime.runPromise((project) => project.list())
|
const list = await runtime.runPromise((project) => project.list())
|
||||||
process.stdout.write(JSON.stringify(list, null, 2) + EOL)
|
process.stdout.write(JSON.stringify(list, null, 2) + EOL)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Server } from "../../server/server"
|
|
||||||
import type { CommandModule } from "yargs"
|
import type { CommandModule } from "yargs"
|
||||||
|
|
||||||
type Args = {}
|
type Args = {}
|
||||||
@@ -7,7 +6,10 @@ export const GenerateCommand = {
|
|||||||
command: "generate",
|
command: "generate",
|
||||||
builder: (yargs) => yargs,
|
builder: (yargs) => yargs,
|
||||||
handler: async () => {
|
handler: async () => {
|
||||||
const specs = (await Server.openapi()) as { paths: Record<string, Record<string, any>> }
|
const { Server } = await import("../../server/server")
|
||||||
|
const specs = (await Server.openapi()) as {
|
||||||
|
paths: Record<string, Record<string, any>>
|
||||||
|
}
|
||||||
for (const item of Object.values(specs.paths)) {
|
for (const item of Object.values(specs.paths)) {
|
||||||
for (const method of ["get", "post", "put", "delete", "patch"] as const) {
|
for (const method of ["get", "post", "put", "delete", "patch"] as const) {
|
||||||
const operation = item[method]
|
const operation = item[method]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
|||||||
|
import type { SessionLegacy } from "@opencode-ai/core/session/legacy"
|
||||||
|
|
||||||
|
export { parseGitHubRemote } from "@/util/repository"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts displayable text from assistant response parts.
|
||||||
|
* Returns null for non-text responses (signals summary needed).
|
||||||
|
* Throws only for truly empty responses.
|
||||||
|
*/
|
||||||
|
export function extractResponseText(parts: SessionLegacy.Part[]): string | null {
|
||||||
|
const textPart = parts.findLast((p) => p.type === "text")
|
||||||
|
if (textPart) return textPart.text
|
||||||
|
|
||||||
|
// Non-text parts (tools, reasoning, step-start/step-finish, etc.) - signal summary needed
|
||||||
|
if (parts.length > 0) return null
|
||||||
|
|
||||||
|
throw new Error("Failed to parse response: no parts returned")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a PROMPT_TOO_LARGE error message with details about files in the prompt.
|
||||||
|
* Content is base64 encoded, so we calculate original size by multiplying by 0.75.
|
||||||
|
*/
|
||||||
|
export function formatPromptTooLargeError(files: { filename: string; content: string }[]): string {
|
||||||
|
const fileDetails =
|
||||||
|
files.length > 0
|
||||||
|
? `\n\nFiles in prompt:\n${files.map((f) => ` - ${f.filename} (${((f.content.length * 0.75) / 1024).toFixed(0)} KB)`).join("\n")}`
|
||||||
|
: ""
|
||||||
|
return `PROMPT_TOO_LARGE: The prompt exceeds the model's context limit.${fileDetails}`
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,5 @@
|
|||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { Provider } from "@/provider/provider"
|
|
||||||
|
|
||||||
import { ModelsDev } from "@opencode-ai/core/models-dev"
|
import { ModelsDev } from "@opencode-ai/core/models-dev"
|
||||||
import { effectCmd, fail } from "../effect-cmd"
|
import { effectCmd, fail } from "../effect-cmd"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
@@ -26,6 +24,7 @@ export const ModelsCommand = effectCmd({
|
|||||||
type: "boolean",
|
type: "boolean",
|
||||||
}),
|
}),
|
||||||
handler: Effect.fn("Cli.models")(function* (args) {
|
handler: Effect.fn("Cli.models")(function* (args) {
|
||||||
|
const { Provider } = yield* Effect.promise(() => import("@/provider/provider"))
|
||||||
if (args.refresh) {
|
if (args.refresh) {
|
||||||
yield* ModelsDev.Service.use((s) => s.refresh(true))
|
yield* ModelsDev.Service.use((s) => s.refresh(true))
|
||||||
UI.println(UI.Style.TEXT_SUCCESS_BOLD + "Models cache refreshed" + UI.Style.TEXT_NORMAL)
|
UI.println(UI.Style.TEXT_SUCCESS_BOLD + "Models cache refreshed" + UI.Style.TEXT_NORMAL)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
import type { PermissionLegacy } from "@opencode-ai/core/permission/legacy"
|
||||||
// CLI entry point for `opencode run`.
|
// CLI entry point for `opencode run`.
|
||||||
//
|
//
|
||||||
// Handles three modes:
|
// Handles three modes:
|
||||||
@@ -18,18 +18,12 @@ import { pathToFileURL } from "url"
|
|||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
import { effectCmd } from "../effect-cmd"
|
import { effectCmd } from "../effect-cmd"
|
||||||
import { ServerAuth } from "@/server/auth"
|
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
import { Filesystem } from "@/util/filesystem"
|
import { Filesystem } from "@/util/filesystem"
|
||||||
import { createOpencodeClient, type OpencodeClient, type ToolPart } from "@opencode-ai/sdk/v2"
|
import { createOpencodeClient, type OpencodeClient, type ToolPart } from "@opencode-ai/sdk/v2"
|
||||||
import { Agent } from "@/agent/agent"
|
|
||||||
import { Permission } from "@/permission"
|
|
||||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
|
||||||
import { InstanceRef } from "@/effect/instance-ref"
|
|
||||||
import { FormatError, FormatUnknownError } from "../error"
|
import { FormatError, FormatUnknownError } from "../error"
|
||||||
import { INTERACTIVE_INPUT_ERROR, resolveInteractiveStdin } from "./run/runtime.stdin"
|
import { INTERACTIVE_INPUT_ERROR, resolveInteractiveStdin } from "./run/runtime.stdin"
|
||||||
|
|
||||||
const runtimeTask = import("./run/runtime")
|
|
||||||
type ModelInput = Parameters<OpencodeClient["session"]["prompt"]>[0]["model"]
|
type ModelInput = Parameters<OpencodeClient["session"]["prompt"]>[0]["model"]
|
||||||
|
|
||||||
function pick(value: string | undefined): ModelInput | undefined {
|
function pick(value: string | undefined): ModelInput | undefined {
|
||||||
@@ -245,6 +239,10 @@ export const RunCommand = effectCmd({
|
|||||||
describe: "enable direct interactive demo slash commands; pass one as the message to run it immediately",
|
describe: "enable direct interactive demo slash commands; pass one as the message to run it immediately",
|
||||||
}),
|
}),
|
||||||
handler: Effect.fn("Cli.run")(function* (args) {
|
handler: Effect.fn("Cli.run")(function* (args) {
|
||||||
|
const { Agent } = yield* Effect.promise(() => import("@/agent/agent"))
|
||||||
|
const { RuntimeFlags } = yield* Effect.promise(() => import("@/effect/runtime-flags"))
|
||||||
|
const { InstanceRef } = yield* Effect.promise(() => import("@/effect/instance-ref"))
|
||||||
|
const { ServerAuth } = yield* Effect.promise(() => import("@/server/auth"))
|
||||||
const agentSvc = yield* Agent.Service
|
const agentSvc = yield* Agent.Service
|
||||||
const flags = yield* RuntimeFlags.Service
|
const flags = yield* RuntimeFlags.Service
|
||||||
const localInstance = yield* InstanceRef
|
const localInstance = yield* InstanceRef
|
||||||
@@ -805,7 +803,7 @@ export const RunCommand = effectCmd({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const model = pick(args.model)
|
const model = pick(args.model)
|
||||||
const { runInteractiveMode } = await runtimeTask
|
const { runInteractiveMode } = await import("./run/runtime")
|
||||||
try {
|
try {
|
||||||
await runInteractiveMode({
|
await runInteractiveMode({
|
||||||
sdk: client,
|
sdk: client,
|
||||||
@@ -832,7 +830,7 @@ export const RunCommand = effectCmd({
|
|||||||
|
|
||||||
if (args.interactive && !args.attach && !args.session && !args.continue) {
|
if (args.interactive && !args.attach && !args.session && !args.continue) {
|
||||||
const model = pick(args.model)
|
const model = pick(args.model)
|
||||||
const { runInteractiveLocalMode } = await runtimeTask
|
const { runInteractiveLocalMode } = await import("./run/runtime")
|
||||||
const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||||
const { Server } = await import("@/server/server")
|
const { Server } = await import("@/server/server")
|
||||||
const request = new Request(input, init)
|
const request = new Request(input, init)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { Server } from "../../server/server"
|
|
||||||
import { effectCmd } from "../effect-cmd"
|
import { effectCmd } from "../effect-cmd"
|
||||||
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||||
@@ -12,6 +11,7 @@ export const ServeCommand = effectCmd({
|
|||||||
// need for an ambient project InstanceContext at startup.
|
// need for an ambient project InstanceContext at startup.
|
||||||
instance: false,
|
instance: false,
|
||||||
handler: Effect.fn("Cli.serve")(function* (args) {
|
handler: Effect.fn("Cli.serve")(function* (args) {
|
||||||
|
const { Server } = yield* Effect.promise(() => import("../../server/server"))
|
||||||
if (!Flag.OPENCODE_SERVER_PASSWORD) {
|
if (!Flag.OPENCODE_SERVER_PASSWORD) {
|
||||||
console.log("Warning: OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
|
console.log("Warning: OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { cmd } from "../cmd"
|
import { cmd } from "../cmd"
|
||||||
import { UI } from "@/cli/ui"
|
import { UI } from "@/cli/ui"
|
||||||
import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32"
|
import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32"
|
||||||
import { TuiConfig } from "@/cli/cmd/tui/config/tui"
|
|
||||||
import { errorMessage } from "@/util/error"
|
import { errorMessage } from "@/util/error"
|
||||||
import { validateSession } from "./validate-session"
|
import { validateSession } from "./validate-session"
|
||||||
import { ServerAuth } from "@/server/auth"
|
import { ServerAuth } from "@/server/auth"
|
||||||
@@ -45,6 +44,7 @@ export const AttachCommand = cmd({
|
|||||||
describe: "basic auth username (defaults to OPENCODE_SERVER_USERNAME or 'opencode')",
|
describe: "basic auth username (defaults to OPENCODE_SERVER_USERNAME or 'opencode')",
|
||||||
}),
|
}),
|
||||||
handler: async (args) => {
|
handler: async (args) => {
|
||||||
|
const { TuiConfig } = await import("@/cli/cmd/tui/config/tui")
|
||||||
const unguard = win32InstallCtrlCGuard()
|
const unguard = win32InstallCtrlCGuard()
|
||||||
try {
|
try {
|
||||||
win32DisableProcessedInput()
|
win32DisableProcessedInput()
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import type { GlobalEvent } from "@opencode-ai/sdk/v2"
|
|||||||
import type { EventSource } from "./context/sdk"
|
import type { EventSource } from "./context/sdk"
|
||||||
import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32"
|
import { win32DisableProcessedInput, win32InstallCtrlCGuard } from "./win32"
|
||||||
import { writeHeapSnapshot } from "v8"
|
import { writeHeapSnapshot } from "v8"
|
||||||
import { TuiConfig } from "./config/tui"
|
|
||||||
import {
|
import {
|
||||||
OPENCODE_PROCESS_ROLE,
|
OPENCODE_PROCESS_ROLE,
|
||||||
OPENCODE_RUN_ID,
|
OPENCODE_RUN_ID,
|
||||||
@@ -113,6 +112,7 @@ export const TuiThreadCommand = cmd({
|
|||||||
describe: "agent to use",
|
describe: "agent to use",
|
||||||
}),
|
}),
|
||||||
handler: async (args) => {
|
handler: async (args) => {
|
||||||
|
const { TuiConfig } = await import("./config/tui")
|
||||||
// Keep ENABLE_PROCESSED_INPUT cleared even if other code flips it.
|
// Keep ENABLE_PROCESSED_INPUT cleared even if other code flips it.
|
||||||
// (Important when running under `bun run` wrappers on Windows.)
|
// (Important when running under `bun run` wrappers on Windows.)
|
||||||
const unguard = win32InstallCtrlCGuard()
|
const unguard = win32InstallCtrlCGuard()
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { Server } from "../../server/server"
|
|
||||||
import { UI } from "../ui"
|
import { UI } from "../ui"
|
||||||
import { effectCmd } from "../effect-cmd"
|
import { effectCmd } from "../effect-cmd"
|
||||||
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
import { withNetworkOptions, resolveNetworkOptions } from "../network"
|
||||||
@@ -37,6 +36,7 @@ export const WebCommand = effectCmd({
|
|||||||
// ambient project InstanceContext needed at startup.
|
// ambient project InstanceContext needed at startup.
|
||||||
instance: false,
|
instance: false,
|
||||||
handler: Effect.fn("Cli.web")(function* (args) {
|
handler: Effect.fn("Cli.web")(function* (args) {
|
||||||
|
const { Server } = yield* Effect.promise(() => import("../../server/server"))
|
||||||
if (!Flag.OPENCODE_SERVER_PASSWORD) {
|
if (!Flag.OPENCODE_SERVER_PASSWORD) {
|
||||||
UI.println(UI.Style.TEXT_WARNING_BOLD + "! OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
|
UI.println(UI.Style.TEXT_WARNING_BOLD + "! OPENCODE_SERVER_PASSWORD is not set; server is unsecured.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import type { Argv } from "yargs"
|
import type { Argv } from "yargs"
|
||||||
import { Effect, Schema } from "effect"
|
import { Effect, Schema } from "effect"
|
||||||
import { AppRuntime, type AppServices } from "@/effect/app-runtime"
|
import type { AppServices } from "@/effect/app-runtime"
|
||||||
import { InstanceStore } from "@/project/instance-store"
|
import type { InstanceStore } from "@/project/instance-store"
|
||||||
import { InstanceRef } from "@/effect/instance-ref"
|
|
||||||
import { cmd, type WithDoubleDash } from "./cmd/cmd"
|
import { cmd, type WithDoubleDash } from "./cmd/cmd"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,6 +73,7 @@ export const effectCmd = <Args, A>(opts: EffectCmdOpts<Args, A>) =>
|
|||||||
describe: opts.describe,
|
describe: opts.describe,
|
||||||
builder: opts.builder as never,
|
builder: opts.builder as never,
|
||||||
async handler(rawArgs) {
|
async handler(rawArgs) {
|
||||||
|
const { AppRuntime } = await import("@/effect/app-runtime")
|
||||||
// yargs typing wraps Args in ArgumentsCamelCase<WithDoubleDash<...>>; cast at the boundary.
|
// yargs typing wraps Args in ArgumentsCamelCase<WithDoubleDash<...>>; cast at the boundary.
|
||||||
const args = rawArgs as unknown as WithDoubleDash<Args>
|
const args = rawArgs as unknown as WithDoubleDash<Args>
|
||||||
const useInstance = typeof opts.instance === "function" ? opts.instance(args) : opts.instance !== false
|
const useInstance = typeof opts.instance === "function" ? opts.instance(args) : opts.instance !== false
|
||||||
@@ -81,6 +81,8 @@ export const effectCmd = <Args, A>(opts: EffectCmdOpts<Args, A>) =>
|
|||||||
await AppRuntime.runPromise(opts.handler(args))
|
await AppRuntime.runPromise(opts.handler(args))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const { InstanceStore } = await import("@/project/instance-store")
|
||||||
|
const { InstanceRef } = await import("@/effect/instance-ref")
|
||||||
const directory = opts.directory?.(args) ?? process.cwd()
|
const directory = opts.directory?.(args) ?? process.cwd()
|
||||||
const { store, ctx } = await AppRuntime.runPromise(
|
const { store, ctx } = await AppRuntime.runPromise(
|
||||||
InstanceStore.Service.use((store) => store.load({ directory }).pipe(Effect.map((ctx) => ({ store, ctx })))),
|
InstanceStore.Service.use((store) => store.load({ directory }).pipe(Effect.map((ctx) => ({ store, ctx })))),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Argv, InferredOptionTypes } from "yargs"
|
import type { Argv, InferredOptionTypes } from "yargs"
|
||||||
import { Config } from "@/config/config"
|
import type { Config } from "@/config/config"
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
@@ -37,6 +37,7 @@ export function withNetworkOptions<T>(yargs: Argv<T>) {
|
|||||||
return yargs.options(options)
|
return yargs.options(options)
|
||||||
}
|
}
|
||||||
export const resolveNetworkOptions = Effect.fn("Cli.resolveNetworkOptions")(function* (args: NetworkOptions) {
|
export const resolveNetworkOptions = Effect.fn("Cli.resolveNetworkOptions")(function* (args: NetworkOptions) {
|
||||||
|
const { Config } = yield* Effect.promise(() => import("@/config/config"))
|
||||||
const config = yield* Config.Service.use((cfg) => cfg.getGlobal())
|
const config = yield* Config.Service.use((cfg) => cfg.getGlobal())
|
||||||
return resolveNetworkOptionsNoConfig(args, config)
|
return resolveNetworkOptionsNoConfig(args, config)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user