Skip to content

Commit 60dc187

Browse files
committed
fix: correct stale-dev-branch reset that broke typecheck
Previous cleanup commit (6cdc4d7) reset files against local 'dev' branch, which was stale at June 2 — a month behind fork/local's actual merge base. That reset plugin/index.ts, cli/error.ts, session-data.ts, and session/prompt.ts to pre-LayerNode-refactor versions, stripping the 'node' export that current tests require. Fixed by fetching upstream/dev (current as of Jul 10) and re-resetting against that instead. Also: - Removed stray duplicate packages/opencode/src/cli/cmd/tui/worker.ts (leftover from the bad reset) — real file lives at packages/opencode/src/cli/tui/worker.ts (upstream path, unrelated to the dead patch-aft-lsp architecture change) - Restored script/patch-aft-lsp.sh — it IS still referenced by the real (upstream-path) worker.ts at startup; my earlier 'dead code' classification was wrong, it was only dead in the stale duplicate Verified: bun run typecheck passes clean (30/30 packages).
1 parent a7f32ba commit 60dc187

5 files changed

Lines changed: 320 additions & 504 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Patches the @cortexkit/aft-opencode plugin to work around an npm v11+
4+
# regression where `npm install --no-save` into a bare directory (no
5+
# package.json) silently installs nothing.
6+
#
7+
# The fix: ensure a minimal package.json exists in the LSP cache dir
8+
# before npm runs.
9+
#
10+
# Run this after any opencode version upgrade or plugin update:
11+
# ./packages/opencode/script/patch-aft-lsp.sh
12+
#
13+
# Safe to re-run (idempotent).
14+
15+
set -euo pipefail
16+
17+
PLUGIN_DIR="${HOME}/.cache/opencode/packages/@cortexkit/aft-opencode@latest"
18+
DIST_FILE="${PLUGIN_DIR}/node_modules/@cortexkit/aft-opencode/dist/index.js"
19+
20+
if [[ ! -f "${DIST_FILE}" ]]; then
21+
echo "[patch-aft-lsp] AFT plugin not found at ${DIST_FILE} — skipping."
22+
exit 0
23+
fi
24+
25+
# Already patched?
26+
if grep -q 'aft-lsp' "${DIST_FILE}" 2>/dev/null; then
27+
echo "[patch-aft-lsp] Already patched — skipping."
28+
exit 0
29+
fi
30+
31+
# Find the runInstall function and inject the package.json stub creation.
32+
# The patch adds 6 lines after the "installing X to Y" log line.
33+
PATCHED=$(awk '
34+
/function runInstall\(spec, version2, cwd, signal\) \{/ {
35+
print
36+
found = 1
37+
next
38+
}
39+
found && /log2\(`\[lsp\] installing/ {
40+
print
41+
# Inject the package.json stub creation
42+
print " try {"
43+
print " const pkgPath = join13(cwd, \"package.json\");"
44+
print " if (!existsSync5(pkgPath)) {"
45+
print " writeFileSync5(pkgPath, JSON.stringify({ name: \"aft-lsp\", private: true }));"
46+
print " }"
47+
print " } catch {}"
48+
found = 0
49+
next
50+
}
51+
{ print }
52+
' "${DIST_FILE}")
53+
54+
if echo "${PATCHED}" | grep -q 'aft-lsp'; then
55+
echo "${PATCHED}" > "${DIST_FILE}"
56+
echo "[patch-aft-lsp] Patched ${DIST_FILE} successfully."
57+
else
58+
echo "[patch-aft-lsp] WARNING: Could not locate injection point — patch not applied."
59+
echo " The plugin structure may have changed. Check manually."
60+
exit 1
61+
fi

packages/opencode/src/cli/cmd/tui/worker.ts

Lines changed: 0 additions & 99 deletions
This file was deleted.

packages/opencode/src/cli/error.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ export function FormatError(input: unknown): string | undefined {
9494
return stringField(configFrontmatter, "message") ?? ""
9595
}
9696

97+
// ConfigRemoteAuthError: { url: string, remote: string }
98+
const remoteAuth = configData(input, "ConfigRemoteAuthError")
99+
if (remoteAuth) {
100+
const url = stringField(remoteAuth, "url")
101+
const remote = stringField(remoteAuth, "remote")
102+
return [
103+
`Failed to load remote config${remote ? ` from ${remote}` : ""}: the server returned a login page instead of JSON.`,
104+
`Authentication is missing or has expired (the endpoint is likely behind an SSO or identity-aware proxy).`,
105+
...(url ? [`Run \`opencode auth login ${url}\` to re-authenticate.`] : []),
106+
].join("\n")
107+
}
108+
97109
// ConfigInvalidError: { path?: string, message?: string, issues?: Array<{ message: string, path: string[] }> }
98110
const configInvalid = configData(input, "ConfigInvalidError")
99111
if (configInvalid) {

packages/opencode/src/plugin/index.ts

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
12
import type {
23
Hooks,
34
PluginInput,
@@ -6,7 +7,6 @@ import type {
67
WorkspaceAdapter as PluginWorkspaceAdapter,
78
} from "@opencode-ai/plugin"
89
import { Config } from "@/config/config"
9-
import * as Log from "@opencode-ai/core/util/log"
1010
import { createOpencodeClient } from "@opencode-ai/sdk"
1111
import { ServerAuth } from "@/server/auth"
1212
import { CodexAuthPlugin } from "./openai/codex"
@@ -19,6 +19,7 @@ import { CloudflareAIGatewayAuthPlugin, CloudflareWorkersAuthPlugin } from "./cl
1919
import { AzureAuthPlugin } from "./azure"
2020
import { DigitalOceanAuthPlugin } from "./digitalocean"
2121
import { XaiAuthPlugin } from "./xai"
22+
import { SnowflakeCortexAuthPlugin } from "./snowflake-cortex"
2223
import { Effect, Layer, Context } from "effect"
2324
import { EffectBridge } from "@/effect/bridge"
2425
import { InstanceState } from "@/effect/instance-state"
@@ -31,8 +32,6 @@ import { RuntimeFlags } from "@/effect/runtime-flags"
3132
import { EventV2Bridge } from "@/event-v2-bridge"
3233
import { InstallationChannel } from "@opencode-ai/core/installation/version"
3334

34-
const log = Log.create({ service: "plugin" })
35-
3635
type State = {
3736
hooks: Hooks[]
3837
}
@@ -77,6 +76,7 @@ function internalPlugins(flags: RuntimeFlags.Info): PluginInstance[] {
7776
CloudflareAIGatewayAuthPlugin,
7877
AzureAuthPlugin,
7978
DigitalOceanAuthPlugin,
79+
SnowflakeCortexAuthPlugin,
8080
XaiAuthPlugin,
8181
]
8282
}
@@ -120,7 +120,7 @@ async function applyPlugin(load: PluginLoader.Loaded, input: PluginInput, hooks:
120120
}
121121
}
122122

123-
export const layer = Layer.effect(
123+
const layer = Layer.effect(
124124
Service,
125125
Effect.gen(function* () {
126126
const events = yield* EventV2Bridge.Service
@@ -138,11 +138,12 @@ export const layer = Layer.effect(
138138

139139
const { Server } = yield* Effect.promise(() => import("../server/server"))
140140

141+
const serverUrl = Server.url
141142
const client = createOpencodeClient({
142-
baseUrl: "http://localhost:4096",
143+
baseUrl: serverUrl?.toString() ?? "http://localhost:4096",
143144
directory: ctx.directory,
144145
headers: ServerAuth.headers(),
145-
fetch: async (...args) => Server.Default().app.fetch(...args),
146+
...(serverUrl ? {} : { fetch: async (...args) => Server.Default().app.fetch(...args) }),
146147
})
147148
const cfg = yield* config.get()
148149
const input: PluginInput = {
@@ -163,19 +164,18 @@ export const layer = Layer.effect(
163164
}
164165

165166
for (const plugin of flags.disableDefaultPlugins ? [] : internalPlugins(flags)) {
166-
log.info("loading internal plugin", { name: plugin.name })
167167
const init = yield* Effect.tryPromise({
168168
try: () => plugin(input),
169-
catch: (err) => {
170-
log.error("failed to load internal plugin", { name: plugin.name, error: err })
171-
},
172-
}).pipe(Effect.option)
169+
catch: errorMessage,
170+
}).pipe(
171+
Effect.tapError((error) => Effect.logError("failed to load internal plugin", { name: plugin.name, error })),
172+
Effect.option,
173+
)
173174
if (init._tag === "Some") hooks.push(init.value)
174175
}
175176

176177
const plugins = flags.pure ? [] : (cfg.plugin_origins ?? [])
177178
if (flags.pure && cfg.plugin_origins?.length) {
178-
log.info("skipping external plugins in pure mode", { count: cfg.plugin_origins.length })
179179
}
180180
if (plugins.length) yield* config.waitForDependencies()
181181

@@ -184,37 +184,29 @@ export const layer = Layer.effect(
184184
items: plugins,
185185
kind: "server",
186186
report: {
187-
start(candidate) {
188-
log.info("loading plugin", { path: candidate.plan.spec })
189-
},
190-
missing(candidate, _retry, message) {
191-
log.warn("plugin has no server entrypoint", { path: candidate.plan.spec, message })
192-
},
187+
start(candidate) {},
188+
missing(candidate, _retry, message) {},
193189
error(candidate, _retry, stage, error, resolved) {
194190
const spec = candidate.plan.spec
195191
const cause = error instanceof Error ? (error.cause ?? error) : error
196192
const message = stage === "load" ? errorMessage(error) : errorMessage(cause)
197193

198194
if (stage === "install") {
199195
const parsed = parsePluginSpecifier(spec)
200-
log.error("failed to install plugin", { pkg: parsed.pkg, version: parsed.version, error: message })
201196
publishPluginError(`Failed to install plugin ${parsed.pkg}@${parsed.version}: ${message}`)
202197
return
203198
}
204199

205200
if (stage === "compatibility") {
206-
log.warn("plugin incompatible", { path: spec, error: message })
207201
publishPluginError(`Plugin ${spec} skipped: ${message}`)
208202
return
209203
}
210204

211205
if (stage === "entry") {
212-
log.error("failed to resolve plugin server entry", { path: spec, error: message })
213206
publishPluginError(`Failed to load plugin ${spec}: ${message}`)
214207
return
215208
}
216209

217-
log.error("failed to load plugin", { path: spec, target: resolved?.entry, error: message })
218210
publishPluginError(`Failed to load plugin ${spec}: ${message}`)
219211
},
220212
},
@@ -229,10 +221,10 @@ export const layer = Layer.effect(
229221
try: () => applyPlugin(load, input, hooks),
230222
catch: (err) => {
231223
const message = errorMessage(err)
232-
log.error("failed to load plugin", { path: load.spec, error: message })
233224
return message
234225
},
235226
}).pipe(
227+
Effect.tapError((error) => Effect.logError("failed to load plugin", { path: load.spec, error })),
236228
Effect.catch(() => {
237229
// TODO: make proper events for this
238230
// events.publish(Session.Event.Error, {
@@ -249,10 +241,11 @@ export const layer = Layer.effect(
249241
for (const hook of hooks) {
250242
yield* Effect.tryPromise({
251243
try: () => Promise.resolve((hook as any).config?.(cfg)),
252-
catch: (err) => {
253-
log.error("plugin config hook failed", { error: err })
254-
},
255-
}).pipe(Effect.ignore)
244+
catch: errorMessage,
245+
}).pipe(
246+
Effect.tapError((error) => Effect.logError("plugin config hook failed", { error })),
247+
Effect.ignore,
248+
)
256249
}
257250

258251
const unsubscribe = yield* events.listen((event) => {
@@ -271,10 +264,11 @@ export const layer = Layer.effect(
271264
(hook) =>
272265
Effect.tryPromise({
273266
try: () => Promise.resolve(hook.dispose?.()),
274-
catch: (error) => {
275-
log.error("plugin dispose hook failed", { error })
276-
},
277-
}).pipe(Effect.ignore),
267+
catch: errorMessage,
268+
}).pipe(
269+
Effect.tapError((error) => Effect.logError("plugin dispose hook failed", { error })),
270+
Effect.ignore,
271+
),
278272
{ discard: true },
279273
),
280274
)
@@ -311,10 +305,10 @@ export const layer = Layer.effect(
311305
}),
312306
)
313307

314-
export const defaultLayer = layer.pipe(
315-
Layer.provide(EventV2Bridge.defaultLayer),
316-
Layer.provide(Config.defaultLayer),
317-
Layer.provide(RuntimeFlags.defaultLayer),
318-
)
308+
export const node = LayerNode.make({
309+
service: Service,
310+
layer: layer,
311+
deps: [EventV2Bridge.node, Config.node, RuntimeFlags.node],
312+
})
319313

320314
export * as Plugin from "."

0 commit comments

Comments
 (0)