Compare commits

...
10 changed files with 74 additions and 38 deletions

No files matched your search

+2
View File
@@ -475,6 +475,8 @@ const Root = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCODE_CLI_NAME
Spec.make("restart", { description: "Restart the background server" }),
Spec.make("status", { description: "Show background server status" }),
Spec.make("stop", { description: "Stop the background server" }),
Spec.make("disable", { description: "Stop the background server and use private servers by default" }),
Spec.make("enable", { description: "Use the background server by default" }),
Spec.make("get", {
description: "Get service configuration",
params: {
+1 -1
View File
@@ -12,7 +12,7 @@ export default Runtime.handler(
Effect.fn("cli.pair")(function* (input: Runtime.Input<typeof Commands.commands.pair>) {
if ((yield* ServiceConfig.read()).disabled === true)
return yield* Effect.fail(
new Error("Pairing requires the background service; run `opencode service unset disabled` first"),
new Error("Pairing requires the background service; run `opencode service enable` first"),
)
const endpoint = yield* Service.ensure(yield* ServiceConfig.options())
const client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) })
@@ -0,0 +1,13 @@
import { Effect } from "effect"
import { Commands } from "../../commands"
import { Runtime } from "../../../framework/runtime"
import { ServiceConfig } from "../../../services/service-config"
import { ServerConnection } from "../../../services/server-connection"
export default Runtime.handler(
Commands.commands.service.commands.disable,
Effect.fn("cli.service.disable")(function* () {
yield* ServerConnection.shutdownPersistentPty(yield* ServiceConfig.options()).pipe(Effect.ignore)
yield* ServiceConfig.disable()
}),
)
@@ -0,0 +1,11 @@
import { Effect } from "effect"
import { Commands } from "../../commands"
import { Runtime } from "../../../framework/runtime"
import { ServiceConfig } from "../../../services/service-config"
export default Runtime.handler(
Commands.commands.service.commands.enable,
Effect.fn("cli.service.enable")(function* () {
yield* ServiceConfig.enable()
}),
)
@@ -10,6 +10,7 @@ export default Runtime.handler(
Effect.fn("cli.service.status")(function* () {
const options = yield* ServiceConfig.options()
const found = yield* Service.discover({ ...options, version: undefined })
process.stdout.write((found?.url ?? "stopped") + EOL)
if (found) return process.stdout.write(found.url + EOL)
process.stdout.write(((yield* ServiceConfig.read()).disabled === true ? "disabled" : "stopped") + EOL)
}),
)
+2
View File
@@ -71,6 +71,8 @@ const Handlers = Runtime.handlers(Commands, {
restart: () => import("./commands/handlers/service/restart"),
status: () => import("./commands/handlers/service/status"),
stop: () => import("./commands/handlers/service/stop"),
disable: () => import("./commands/handlers/service/disable"),
enable: () => import("./commands/handlers/service/enable"),
get: () => import("./commands/handlers/service/get"),
set: () => import("./commands/handlers/service/set"),
unset: () => import("./commands/handlers/service/unset"),
+12 -24
View File
@@ -21,7 +21,7 @@ export const Info = Schema.Struct({
})
export type Info = typeof Info.Type
const keys = ["disabled", "hostname", "port", "password", "cors", "env"] as const
const keys = ["hostname", "port", "password", "cors", "env"] as const
type Key = (typeof keys)[number]
const decodeInfo = Schema.decodeUnknownEffect(Schema.fromJsonString(Info))
@@ -79,15 +79,7 @@ export const migrateConfig = Effect.fnUntraced(function* (legacy: string, file:
})
function configKey(key: string): Key {
if (
key === "disabled" ||
key === "hostname" ||
key === "port" ||
key === "password" ||
key === "cors" ||
key === "env"
)
return key
if (key === "hostname" || key === "port" || key === "password" || key === "cors" || key === "env") return key
throw new Error(`Unknown service config key: ${key}`)
}
@@ -152,6 +144,16 @@ export const password = Effect.fn("cli.service-config.password")(function* (valu
return next
})
export const disable = Effect.fn("cli.service-config.disable")(function* () {
yield* Service.stop(yield* options())
yield* write({ ...(yield* read()), disabled: true })
})
export const enable = Effect.fn("cli.service-config.enable")(function* () {
const { disabled: _disabled, ...next } = yield* read()
yield* write(next)
})
export const get = Effect.fn("cli.service-config.get")(function* (key?: string, name?: string) {
if (key === undefined) {
const { password: _password, ...safe } = yield* read()
@@ -160,9 +162,6 @@ export const get = Effect.fn("cli.service-config.get")(function* (key?: string,
const selected = configKey(key)
if (selected !== "env" && name !== undefined) throw new Error(`Usage: opencode service get ${selected}`)
switch (selected) {
case "disabled": {
return String((yield* read()).disabled ?? false)
}
case "hostname": {
return (yield* read()).hostname ?? ""
}
@@ -189,12 +188,6 @@ export const set = Effect.fn("cli.service-config.set")(function* (key: string, v
if (selected !== "env" && nestedValue !== undefined)
throw new Error(`Usage: opencode service set ${selected} <value>`)
switch (selected) {
case "disabled": {
if (value !== "true" && value !== "false") throw new Error("Disabled must be true or false")
if (value === "true") yield* Service.stop(yield* options())
yield* write({ ...(yield* read()), disabled: value === "true" })
return
}
case "hostname": {
yield* Service.stop(yield* options())
yield* write({ ...(yield* read()), hostname: value })
@@ -239,11 +232,6 @@ export const unset = Effect.fn("cli.service-config.unset")(function* (key: strin
const selected = configKey(key)
if (selected !== "env" && name !== undefined) throw new Error(`Usage: opencode service unset ${selected}`)
switch (selected) {
case "disabled": {
const { disabled: _disabled, ...next } = yield* read()
yield* write(next)
return
}
case "hostname": {
yield* Service.stop(yield* options())
const { hostname: _hostname, ...next } = yield* read()
+7 -8
View File
@@ -21,19 +21,18 @@ test("managed service ports are stable per installation channel", () => {
expect(ServiceConfig.defaultPort("preview-a")).not.toBe(ServiceConfig.defaultPort("preview-b"))
})
test("service disabled accepts only booleans without changing configuration on invalid input", async () => {
test("service disable and enable toggle the disabled flag", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "opencode-service-disabled-config-"))
const layer = Global.layerWith({ config: path.join(root, "config"), state: path.join(root, "state") })
const run = <A, E>(effect: Effect.Effect<A, E, Global.Service | FileSystem.FileSystem>) =>
Effect.runPromise(effect.pipe(Effect.provide(layer), Effect.provide(NodeFileSystem.layer)))
try {
expect(await run(ServiceConfig.get("disabled"))).toBe("false")
await expect(run(ServiceConfig.set("disabled", "yes"))).rejects.toThrow("Disabled must be true or false")
expect(await run(ServiceConfig.read())).toEqual({})
await run(ServiceConfig.set("disabled", "true"))
expect(await run(ServiceConfig.read())).toEqual({ disabled: true })
await run(ServiceConfig.unset("disabled"))
expect(await run(ServiceConfig.read())).toEqual({})
await run(ServiceConfig.set("hostname", "127.0.0.2"))
await run(ServiceConfig.disable())
expect(await run(ServiceConfig.read())).toEqual({ hostname: "127.0.0.2", disabled: true })
await expect(run(ServiceConfig.set("disabled", "true"))).rejects.toThrow("Unknown service config key: disabled")
await run(ServiceConfig.enable())
expect(await run(ServiceConfig.read())).toEqual({ hostname: "127.0.0.2" })
} finally {
await fs.rm(root, { recursive: true, force: true })
}
@@ -407,6 +407,18 @@ Stop it.
$ opencode service stop
```
Stop it and use private servers by default. See [Background service](/cli#background-service).
```bash
$ opencode service disable
```
Use it by default again.
```bash
$ opencode service enable
```
Read a setting.
```bash
+12 -4
View File
@@ -58,13 +58,21 @@ opencode --server http://localhost:4096
To make private servers the default for CLI commands instead of starting the shared service, disable it:
```bash
opencode service set disabled true
opencode service disable
opencode
```
This stops the running background service. `--server` still connects to an explicitly selected server; `opencode service start`
can still start the shared service explicitly. To return to automatic service use, run `opencode service unset disabled`.
Pairing requires the shared service and is unavailable while it is disabled.
This stops the running background service. While it is disabled:
- `--server` still connects to an explicitly selected server.
- `opencode service start` still starts the shared service explicitly.
- Pairing is unavailable, because it requires the shared service.
To return to automatic service use, enable it again:
```bash
opencode service enable
```
See [Troubleshooting](/troubleshooting) for shared service diagnostics and the [API reference](/api) for server endpoints.