chore: generate
This commit is contained in:
parent
c06ad7c881
commit
537666149b
24 changed files with 826 additions and 854 deletions
|
|
@ -227,9 +227,7 @@ export const layer = Layer.effect(
|
||||||
yield* events.publish(Event.Refreshed, {})
|
yield* events.publish(Event.Refreshed, {})
|
||||||
}),
|
}),
|
||||||
).pipe(
|
).pipe(
|
||||||
Effect.tapCause((cause) =>
|
Effect.tapCause((cause) => Effect.logError("Failed to fetch models.dev", { cause: cause })),
|
||||||
Effect.logError("Failed to fetch models.dev", { cause: cause }),
|
|
||||||
),
|
|
||||||
Effect.ignore,
|
Effect.ignore,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,11 @@ function formatter(id: string = runID) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function flatten(input: Record<string, unknown>, prefix = "", seen = new WeakSet<object>()): Array<readonly [string, unknown]> {
|
function flatten(
|
||||||
|
input: Record<string, unknown>,
|
||||||
|
prefix = "",
|
||||||
|
seen = new WeakSet<object>(),
|
||||||
|
): Array<readonly [string, unknown]> {
|
||||||
if (seen.has(input)) return [[prefix, "[Circular]"]]
|
if (seen.has(input)) return [[prefix, "[Circular]"]]
|
||||||
seen.add(input)
|
seen.add(input)
|
||||||
const entries = Object.entries(input)
|
const entries = Object.entries(input)
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,11 @@ export const layer = Layer.effect(
|
||||||
(materializer) =>
|
(materializer) =>
|
||||||
materializer.run.pipe(
|
materializer.run.pipe(
|
||||||
Effect.catchCause((cause) =>
|
Effect.catchCause((cause) =>
|
||||||
Effect.logWarning("failed to materialize project reference", { name: materializer.name, repository: materializer.repository, cause }),
|
Effect.logWarning("failed to materialize project reference", {
|
||||||
|
name: materializer.name,
|
||||||
|
repository: materializer.repository,
|
||||||
|
cause,
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
{ concurrency: 4, discard: true },
|
{ concurrency: 4, discard: true },
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,9 @@ export const layer = Layer.effect(
|
||||||
contextLimitLoader.providers(input.directory).pipe(
|
contextLimitLoader.providers(input.directory).pipe(
|
||||||
Effect.map((providers) => findContextLimit(providers, input.providerID, input.modelID)),
|
Effect.map((providers) => findContextLimit(providers, input.providerID, input.modelID)),
|
||||||
Effect.catch((error) =>
|
Effect.catch((error) =>
|
||||||
Effect.logError("failed to get providers for usage context limit", { error: error }).pipe(Effect.as(undefined)),
|
Effect.logError("failed to get providers for usage context limit", { error: error }).pipe(
|
||||||
|
Effect.as(undefined),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -176,11 +178,13 @@ export const layer = Layer.effect(
|
||||||
readonly sessionID: string
|
readonly sessionID: string
|
||||||
readonly directory: string
|
readonly directory: string
|
||||||
}) {
|
}) {
|
||||||
const messages = yield* messageLoader.messages({ sessionID: input.sessionID, directory: input.directory }).pipe(
|
const messages = yield* messageLoader
|
||||||
Effect.catch((error) =>
|
.messages({ sessionID: input.sessionID, directory: input.directory })
|
||||||
Effect.logError("failed to fetch messages for usage update", { error: error }).pipe(Effect.as(undefined)),
|
.pipe(
|
||||||
),
|
Effect.catch((error) =>
|
||||||
)
|
Effect.logError("failed to fetch messages for usage update", { error: error }).pipe(Effect.as(undefined)),
|
||||||
|
),
|
||||||
|
)
|
||||||
if (!messages) return
|
if (!messages) return
|
||||||
|
|
||||||
const message = latestAssistantMessage(messages)
|
const message = latestAssistantMessage(messages)
|
||||||
|
|
|
||||||
|
|
@ -174,233 +174,233 @@ function queueSplash(
|
||||||
// scrollback commits and footer repaints happen in the same frame. After
|
// scrollback commits and footer repaints happen in the same frame. After
|
||||||
// the entry splash, RunFooter takes over the footer region.
|
// the entry splash, RunFooter takes over the footer region.
|
||||||
export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lifecycle> {
|
export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lifecycle> {
|
||||||
const source = resolveInteractiveStdin()
|
const source = resolveInteractiveStdin()
|
||||||
let unregisterKeymap: (() => void) | undefined
|
let unregisterKeymap: (() => void) | undefined
|
||||||
|
|
||||||
|
try {
|
||||||
|
const renderer = await createCliRenderer({
|
||||||
|
stdin: source.stdin,
|
||||||
|
targetFps: 30,
|
||||||
|
maxFps: 60,
|
||||||
|
useMouse: false,
|
||||||
|
autoFocus: false,
|
||||||
|
openConsoleOnError: false,
|
||||||
|
exitOnCtrlC: false,
|
||||||
|
useKittyKeyboard: { events: process.platform === "win32" },
|
||||||
|
screenMode: "split-footer",
|
||||||
|
footerHeight: FOOTER_HEIGHT,
|
||||||
|
externalOutputMode: "capture-stdout",
|
||||||
|
consoleMode: "disabled",
|
||||||
|
clearOnShutdown: false,
|
||||||
|
})
|
||||||
|
const theme = await resolveRunTheme(renderer)
|
||||||
|
renderer.setBackgroundColor(theme.background)
|
||||||
|
const keymap = createDefaultOpenTuiKeymap(renderer)
|
||||||
|
unregisterKeymap = registerOpencodeKeymap(keymap, renderer, input.tuiConfig)
|
||||||
|
const state: SplashState = {
|
||||||
|
entry: false,
|
||||||
|
exit: false,
|
||||||
|
}
|
||||||
|
const splash = splashInfo(input.sessionTitle, input.history)
|
||||||
|
const meta = splashMeta({
|
||||||
|
title: splash.title,
|
||||||
|
session_id: input.sessionID,
|
||||||
|
})
|
||||||
|
const labels = footerLabels({
|
||||||
|
agent: input.agent,
|
||||||
|
model: input.model,
|
||||||
|
variant: input.variant,
|
||||||
|
})
|
||||||
|
const footerTask = import("./footer")
|
||||||
|
const wrote = queueSplash(
|
||||||
|
renderer,
|
||||||
|
state,
|
||||||
|
"entry",
|
||||||
|
entrySplash({
|
||||||
|
...meta,
|
||||||
|
theme: theme.splash,
|
||||||
|
showSession: splash.showSession,
|
||||||
|
detail: directoryLabel(input.directory),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
await renderer.idle().catch(() => {})
|
||||||
|
|
||||||
|
const { RunFooter } = await footerTask
|
||||||
|
let closed = false
|
||||||
|
let sigintRegistered = false
|
||||||
|
|
||||||
|
const footer = new RunFooter(renderer, {
|
||||||
|
directory: input.directory,
|
||||||
|
findFiles: input.findFiles,
|
||||||
|
agents: input.agents,
|
||||||
|
resources: input.resources,
|
||||||
|
sessionID: input.getSessionID ?? (() => input.sessionID),
|
||||||
|
...labels,
|
||||||
|
model: input.model,
|
||||||
|
variant: input.variant,
|
||||||
|
first: input.first,
|
||||||
|
history: input.history,
|
||||||
|
theme,
|
||||||
|
wrote,
|
||||||
|
keymap,
|
||||||
|
tuiConfig: input.tuiConfig,
|
||||||
|
backgroundSubagents: input.backgroundSubagents,
|
||||||
|
diffStyle: input.tuiConfig.diff_style ?? "auto",
|
||||||
|
onPermissionReply: input.onPermissionReply,
|
||||||
|
onQuestionReply: input.onQuestionReply,
|
||||||
|
onQuestionReject: input.onQuestionReject,
|
||||||
|
onCycleVariant: input.onCycleVariant,
|
||||||
|
onModelSelect: input.onModelSelect,
|
||||||
|
onVariantSelect: input.onVariantSelect,
|
||||||
|
onInterrupt: input.onInterrupt,
|
||||||
|
onBackground: input.onBackground,
|
||||||
|
onEditorOpen: async ({ value }) => {
|
||||||
|
if (closed || renderer.isDestroyed) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await renderer.idle().catch(() => {})
|
||||||
|
const ignore = () => {}
|
||||||
|
detachSigint()
|
||||||
|
process.on("SIGINT", ignore)
|
||||||
|
try {
|
||||||
|
return await openEditor({
|
||||||
|
value,
|
||||||
|
cwd: input.directory,
|
||||||
|
renderer,
|
||||||
|
stdin: source.stdin,
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
process.off("SIGINT", ignore)
|
||||||
|
attachSigint()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSubagentSelect: input.onSubagentSelect,
|
||||||
|
})
|
||||||
|
|
||||||
|
const sigint = () => {
|
||||||
|
footer.requestExit()
|
||||||
|
}
|
||||||
|
|
||||||
|
const attachSigint = () => {
|
||||||
|
if (closed || sigintRegistered) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
process.on("SIGINT", sigint)
|
||||||
|
sigintRegistered = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const detachSigint = () => {
|
||||||
|
if (!sigintRegistered) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
process.off("SIGINT", sigint)
|
||||||
|
sigintRegistered = false
|
||||||
|
}
|
||||||
|
|
||||||
|
attachSigint()
|
||||||
|
|
||||||
|
const close = async (next: {
|
||||||
|
showExit: boolean
|
||||||
|
sessionTitle?: string
|
||||||
|
sessionID?: string
|
||||||
|
history?: RunPrompt[]
|
||||||
|
}) => {
|
||||||
|
if (closed) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
closed = true
|
||||||
|
detachSigint()
|
||||||
|
let wroteExit = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const renderer = await createCliRenderer({
|
await footer.idle().catch(() => {})
|
||||||
stdin: source.stdin,
|
|
||||||
targetFps: 30,
|
const show = renderer.isDestroyed ? false : next.showExit
|
||||||
maxFps: 60,
|
if (!renderer.isDestroyed && show) {
|
||||||
useMouse: false,
|
const sessionID = next.sessionID || input.getSessionID?.() || input.sessionID
|
||||||
autoFocus: false,
|
const splash = splashInfo(next.sessionTitle ?? input.sessionTitle, next.history ?? input.history)
|
||||||
openConsoleOnError: false,
|
wroteExit = queueSplash(
|
||||||
exitOnCtrlC: false,
|
renderer,
|
||||||
useKittyKeyboard: { events: process.platform === "win32" },
|
state,
|
||||||
screenMode: "split-footer",
|
"exit",
|
||||||
footerHeight: FOOTER_HEIGHT,
|
exitSplash({
|
||||||
externalOutputMode: "capture-stdout",
|
...splashMeta({
|
||||||
consoleMode: "disabled",
|
title: splash.title,
|
||||||
clearOnShutdown: false,
|
session_id: sessionID,
|
||||||
})
|
}),
|
||||||
const theme = await resolveRunTheme(renderer)
|
theme: footer.currentTheme().splash,
|
||||||
renderer.setBackgroundColor(theme.background)
|
}),
|
||||||
const keymap = createDefaultOpenTuiKeymap(renderer)
|
)
|
||||||
unregisterKeymap = registerOpencodeKeymap(keymap, renderer, input.tuiConfig)
|
await renderer.idle().catch(() => {})
|
||||||
const state: SplashState = {
|
|
||||||
entry: false,
|
|
||||||
exit: false,
|
|
||||||
}
|
}
|
||||||
const splash = splashInfo(input.sessionTitle, input.history)
|
} finally {
|
||||||
const meta = splashMeta({
|
footer.close()
|
||||||
title: splash.title,
|
await footer.idle().catch(() => {})
|
||||||
session_id: input.sessionID,
|
footer.destroy()
|
||||||
})
|
unregisterKeymap?.()
|
||||||
const labels = footerLabels({
|
shutdown(renderer)
|
||||||
agent: input.agent,
|
if (!wroteExit) {
|
||||||
model: input.model,
|
process.stdout.write("\n")
|
||||||
variant: input.variant,
|
}
|
||||||
})
|
source.cleanup?.()
|
||||||
const footerTask = import("./footer")
|
}
|
||||||
const wrote = queueSplash(
|
}
|
||||||
renderer,
|
|
||||||
state,
|
return {
|
||||||
"entry",
|
footer,
|
||||||
|
refreshTheme() {
|
||||||
|
footer.refreshTheme()
|
||||||
|
},
|
||||||
|
onResize(fn) {
|
||||||
|
let width = renderer.terminalWidth
|
||||||
|
let height = renderer.terminalHeight
|
||||||
|
const resize = () => {
|
||||||
|
if (width === renderer.terminalWidth && height === renderer.terminalHeight) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
width = renderer.terminalWidth
|
||||||
|
height = renderer.terminalHeight
|
||||||
|
fn()
|
||||||
|
}
|
||||||
|
renderer.on(CliRenderEvents.RESIZE, resize)
|
||||||
|
return () => renderer.off(CliRenderEvents.RESIZE, resize)
|
||||||
|
},
|
||||||
|
async resetForReplay(next) {
|
||||||
|
if (closed || renderer.isDestroyed || footer.isClosed) {
|
||||||
|
throw new Error("runtime closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
await footer.idle()
|
||||||
|
if (closed || renderer.isDestroyed || footer.isClosed) {
|
||||||
|
throw new Error("runtime closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.resetForReplay(true)
|
||||||
|
renderer.resetSplitFooterForReplay({ clearSavedLines: true })
|
||||||
|
const splash = splashInfo(next.sessionTitle ?? input.sessionTitle, next.history)
|
||||||
|
renderer.writeToScrollback(
|
||||||
entrySplash({
|
entrySplash({
|
||||||
...meta,
|
...splashMeta({
|
||||||
theme: theme.splash,
|
title: splash.title,
|
||||||
|
session_id: next.sessionID ?? input.getSessionID?.() ?? input.sessionID,
|
||||||
|
}),
|
||||||
|
theme: footer.currentTheme().splash,
|
||||||
showSession: splash.showSession,
|
showSession: splash.showSession,
|
||||||
detail: directoryLabel(input.directory),
|
detail: directoryLabel(input.directory),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
await renderer.idle().catch(() => {})
|
renderer.requestRender()
|
||||||
|
},
|
||||||
const { RunFooter } = await footerTask
|
close,
|
||||||
let closed = false
|
}
|
||||||
let sigintRegistered = false
|
} catch (error) {
|
||||||
|
unregisterKeymap?.()
|
||||||
const footer = new RunFooter(renderer, {
|
source.cleanup?.()
|
||||||
directory: input.directory,
|
throw error
|
||||||
findFiles: input.findFiles,
|
}
|
||||||
agents: input.agents,
|
|
||||||
resources: input.resources,
|
|
||||||
sessionID: input.getSessionID ?? (() => input.sessionID),
|
|
||||||
...labels,
|
|
||||||
model: input.model,
|
|
||||||
variant: input.variant,
|
|
||||||
first: input.first,
|
|
||||||
history: input.history,
|
|
||||||
theme,
|
|
||||||
wrote,
|
|
||||||
keymap,
|
|
||||||
tuiConfig: input.tuiConfig,
|
|
||||||
backgroundSubagents: input.backgroundSubagents,
|
|
||||||
diffStyle: input.tuiConfig.diff_style ?? "auto",
|
|
||||||
onPermissionReply: input.onPermissionReply,
|
|
||||||
onQuestionReply: input.onQuestionReply,
|
|
||||||
onQuestionReject: input.onQuestionReject,
|
|
||||||
onCycleVariant: input.onCycleVariant,
|
|
||||||
onModelSelect: input.onModelSelect,
|
|
||||||
onVariantSelect: input.onVariantSelect,
|
|
||||||
onInterrupt: input.onInterrupt,
|
|
||||||
onBackground: input.onBackground,
|
|
||||||
onEditorOpen: async ({ value }) => {
|
|
||||||
if (closed || renderer.isDestroyed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await renderer.idle().catch(() => {})
|
|
||||||
const ignore = () => {}
|
|
||||||
detachSigint()
|
|
||||||
process.on("SIGINT", ignore)
|
|
||||||
try {
|
|
||||||
return await openEditor({
|
|
||||||
value,
|
|
||||||
cwd: input.directory,
|
|
||||||
renderer,
|
|
||||||
stdin: source.stdin,
|
|
||||||
})
|
|
||||||
} finally {
|
|
||||||
process.off("SIGINT", ignore)
|
|
||||||
attachSigint()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSubagentSelect: input.onSubagentSelect,
|
|
||||||
})
|
|
||||||
|
|
||||||
const sigint = () => {
|
|
||||||
footer.requestExit()
|
|
||||||
}
|
|
||||||
|
|
||||||
const attachSigint = () => {
|
|
||||||
if (closed || sigintRegistered) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
process.on("SIGINT", sigint)
|
|
||||||
sigintRegistered = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const detachSigint = () => {
|
|
||||||
if (!sigintRegistered) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
process.off("SIGINT", sigint)
|
|
||||||
sigintRegistered = false
|
|
||||||
}
|
|
||||||
|
|
||||||
attachSigint()
|
|
||||||
|
|
||||||
const close = async (next: {
|
|
||||||
showExit: boolean
|
|
||||||
sessionTitle?: string
|
|
||||||
sessionID?: string
|
|
||||||
history?: RunPrompt[]
|
|
||||||
}) => {
|
|
||||||
if (closed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
closed = true
|
|
||||||
detachSigint()
|
|
||||||
let wroteExit = false
|
|
||||||
|
|
||||||
try {
|
|
||||||
await footer.idle().catch(() => {})
|
|
||||||
|
|
||||||
const show = renderer.isDestroyed ? false : next.showExit
|
|
||||||
if (!renderer.isDestroyed && show) {
|
|
||||||
const sessionID = next.sessionID || input.getSessionID?.() || input.sessionID
|
|
||||||
const splash = splashInfo(next.sessionTitle ?? input.sessionTitle, next.history ?? input.history)
|
|
||||||
wroteExit = queueSplash(
|
|
||||||
renderer,
|
|
||||||
state,
|
|
||||||
"exit",
|
|
||||||
exitSplash({
|
|
||||||
...splashMeta({
|
|
||||||
title: splash.title,
|
|
||||||
session_id: sessionID,
|
|
||||||
}),
|
|
||||||
theme: footer.currentTheme().splash,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
await renderer.idle().catch(() => {})
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
footer.close()
|
|
||||||
await footer.idle().catch(() => {})
|
|
||||||
footer.destroy()
|
|
||||||
unregisterKeymap?.()
|
|
||||||
shutdown(renderer)
|
|
||||||
if (!wroteExit) {
|
|
||||||
process.stdout.write("\n")
|
|
||||||
}
|
|
||||||
source.cleanup?.()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
footer,
|
|
||||||
refreshTheme() {
|
|
||||||
footer.refreshTheme()
|
|
||||||
},
|
|
||||||
onResize(fn) {
|
|
||||||
let width = renderer.terminalWidth
|
|
||||||
let height = renderer.terminalHeight
|
|
||||||
const resize = () => {
|
|
||||||
if (width === renderer.terminalWidth && height === renderer.terminalHeight) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
width = renderer.terminalWidth
|
|
||||||
height = renderer.terminalHeight
|
|
||||||
fn()
|
|
||||||
}
|
|
||||||
renderer.on(CliRenderEvents.RESIZE, resize)
|
|
||||||
return () => renderer.off(CliRenderEvents.RESIZE, resize)
|
|
||||||
},
|
|
||||||
async resetForReplay(next) {
|
|
||||||
if (closed || renderer.isDestroyed || footer.isClosed) {
|
|
||||||
throw new Error("runtime closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
await footer.idle()
|
|
||||||
if (closed || renderer.isDestroyed || footer.isClosed) {
|
|
||||||
throw new Error("runtime closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
footer.resetForReplay(true)
|
|
||||||
renderer.resetSplitFooterForReplay({ clearSavedLines: true })
|
|
||||||
const splash = splashInfo(next.sessionTitle ?? input.sessionTitle, next.history)
|
|
||||||
renderer.writeToScrollback(
|
|
||||||
entrySplash({
|
|
||||||
...splashMeta({
|
|
||||||
title: splash.title,
|
|
||||||
session_id: next.sessionID ?? input.getSessionID?.() ?? input.sessionID,
|
|
||||||
}),
|
|
||||||
theme: footer.currentTheme().splash,
|
|
||||||
showSession: splash.showSession,
|
|
||||||
detail: directoryLabel(input.directory),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
renderer.requestRender()
|
|
||||||
},
|
|
||||||
close,
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
unregisterKeymap?.()
|
|
||||||
source.cleanup?.()
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -73,7 +73,6 @@ function fromRow(row: typeof WorkspaceTable.$inferSelect): Info {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const CreateInput = Schema.Struct({
|
export const CreateInput = Schema.Struct({
|
||||||
id: Schema.optional(WorkspaceV2.ID),
|
id: Schema.optional(WorkspaceV2.ID),
|
||||||
type: Info.fields.type,
|
type: Info.fields.type,
|
||||||
|
|
@ -341,7 +340,6 @@ export const layer = Layer.effect(
|
||||||
)
|
)
|
||||||
: {}
|
: {}
|
||||||
|
|
||||||
|
|
||||||
const response = yield* http.execute(
|
const response = yield* http.execute(
|
||||||
HttpClientRequest.post(route(url, "/sync/history"), {
|
HttpClientRequest.post(route(url, "/sync/history"), {
|
||||||
headers: new Headers(headers),
|
headers: new Headers(headers),
|
||||||
|
|
@ -360,7 +358,6 @@ export const layer = Layer.effect(
|
||||||
|
|
||||||
const history = (yield* response.json) as HistoryEvent[]
|
const history = (yield* response.json) as HistoryEvent[]
|
||||||
|
|
||||||
|
|
||||||
yield* Effect.forEach(
|
yield* Effect.forEach(
|
||||||
history,
|
history,
|
||||||
(event) =>
|
(event) =>
|
||||||
|
|
@ -575,7 +572,6 @@ export const layer = Layer.effect(
|
||||||
|
|
||||||
const sessionWarp = Effect.fn("Workspace.sessionWarp")(function* (input: SessionWarpInput) {
|
const sessionWarp = Effect.fn("Workspace.sessionWarp")(function* (input: SessionWarpInput) {
|
||||||
return yield* Effect.gen(function* () {
|
return yield* Effect.gen(function* () {
|
||||||
|
|
||||||
const current = yield* db
|
const current = yield* db
|
||||||
.select({ workspaceID: SessionTable.workspace_id })
|
.select({ workspaceID: SessionTable.workspace_id })
|
||||||
.from(SessionTable)
|
.from(SessionTable)
|
||||||
|
|
@ -590,10 +586,7 @@ export const layer = Layer.effect(
|
||||||
|
|
||||||
if (target.type === "remote") {
|
if (target.type === "remote") {
|
||||||
yield* syncHistory(previous, target.url, target.headers).pipe(
|
yield* syncHistory(previous, target.url, target.headers).pipe(
|
||||||
Effect.catch((error) =>
|
Effect.catch((error) => Effect.sync(() => {})),
|
||||||
Effect.sync(() => {
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
yield* prompt.cancel(input.sessionID)
|
yield* prompt.cancel(input.sessionID)
|
||||||
|
|
@ -679,7 +672,6 @@ export const layer = Layer.effect(
|
||||||
const batches = Iterable.chunksOf(rows, 10)
|
const batches = Iterable.chunksOf(rows, 10)
|
||||||
const total = Iterable.size(batches)
|
const total = Iterable.size(batches)
|
||||||
|
|
||||||
|
|
||||||
yield* Effect.forEach(
|
yield* Effect.forEach(
|
||||||
batches,
|
batches,
|
||||||
(events, i) =>
|
(events, i) =>
|
||||||
|
|
@ -704,7 +696,6 @@ export const layer = Layer.effect(
|
||||||
body,
|
body,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}),
|
}),
|
||||||
{ discard: true },
|
{ discard: true },
|
||||||
)
|
)
|
||||||
|
|
@ -727,7 +718,6 @@ export const layer = Layer.effect(
|
||||||
}
|
}
|
||||||
|
|
||||||
yield* session.setWorkspace({ sessionID: input.sessionID, workspaceID: input.workspaceID })
|
yield* session.setWorkspace({ sessionID: input.sessionID, workspaceID: input.workspaceID })
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -827,9 +817,7 @@ export const layer = Layer.effect(
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
yield* WorkspaceAdapterRuntime.remove(info)
|
yield* WorkspaceAdapterRuntime.remove(info)
|
||||||
}),
|
}),
|
||||||
() =>
|
() => Effect.sync(() => {}),
|
||||||
Effect.sync(() => {
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
yield* db.delete(WorkspaceTable).where(eq(WorkspaceTable.id, id)).run().pipe(Effect.orDie)
|
yield* db.delete(WorkspaceTable).where(eq(WorkspaceTable.id, id)).run().pipe(Effect.orDie)
|
||||||
|
|
|
||||||
|
|
@ -94,12 +94,12 @@ export const layer = Layer.effect(
|
||||||
.pipe(
|
.pipe(
|
||||||
Effect.catch((error) =>
|
Effect.catch((error) =>
|
||||||
Effect.logError("failed to format file", {
|
Effect.logError("failed to format file", {
|
||||||
error: "spawn failed",
|
error: "spawn failed",
|
||||||
command: cmd,
|
command: cmd,
|
||||||
...item.environment,
|
...item.environment,
|
||||||
file: filepath,
|
file: filepath,
|
||||||
cause: errorMessage(error.cause ?? error),
|
cause: errorMessage(error.cause ?? error),
|
||||||
}).pipe(Effect.as(undefined)),
|
}).pipe(Effect.as(undefined)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if (result && result.exitCode !== 0) {
|
if (result && result.exitCode !== 0) {
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,6 @@ const cli = yargs(args)
|
||||||
process.env.AGENT = "1"
|
process.env.AGENT = "1"
|
||||||
process.env.OPENCODE = "1"
|
process.env.OPENCODE = "1"
|
||||||
process.env.OPENCODE_PID = String(process.pid)
|
process.env.OPENCODE_PID = String(process.pid)
|
||||||
|
|
||||||
})
|
})
|
||||||
.usage("")
|
.usage("")
|
||||||
.completion("completion", "generate shell completion script")
|
.completion("completion", "generate shell completion script")
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,6 @@ export const ESLint: Info = {
|
||||||
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm"
|
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm"
|
||||||
await Process.run([npmCmd, "install"], { cwd: finalPath })
|
await Process.run([npmCmd, "install"], { cwd: finalPath })
|
||||||
await Process.run([npmCmd, "run", "compile"], { cwd: finalPath })
|
await Process.run([npmCmd, "run", "compile"], { cwd: finalPath })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const proc = spawn("node", [serverPath, "--stdio"], {
|
const proc = spawn("node", [serverPath, "--stdio"], {
|
||||||
|
|
@ -572,7 +571,6 @@ export const ElixirLS: Info = {
|
||||||
await Process.run(["mix", "deps.get"], { cwd, env })
|
await Process.run(["mix", "deps.get"], { cwd, env })
|
||||||
await Process.run(["mix", "compile"], { cwd, env })
|
await Process.run(["mix", "compile"], { cwd, env })
|
||||||
await Process.run(["mix", "elixir_ls.release2", "-o", "release"], { cwd, env })
|
await Process.run(["mix", "elixir_ls.release2", "-o", "release"], { cwd, env })
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -676,7 +674,6 @@ export const Zls: Info = {
|
||||||
if (platform !== "win32") {
|
if (platform !== "win32") {
|
||||||
await fs.chmod(bin, 0o755).catch(() => {})
|
await fs.chmod(bin, 0o755).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -775,7 +772,6 @@ async function installRoslynLanguageServer(disableLspDownload: boolean) {
|
||||||
if (global) {
|
if (global) {
|
||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function roslynLanguageServerGlobalPath() {
|
async function roslynLanguageServerGlobalPath() {
|
||||||
|
|
@ -1062,7 +1058,6 @@ export const Clangd: Info = {
|
||||||
await fs.unlink(path.join(Global.Path.bin, "clangd")).catch(() => {})
|
await fs.unlink(path.join(Global.Path.bin, "clangd")).catch(() => {})
|
||||||
await fs.symlink(bin, path.join(Global.Path.bin, "clangd")).catch(() => {})
|
await fs.symlink(bin, path.join(Global.Path.bin, "clangd")).catch(() => {})
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
process: spawn(bin, args, {
|
process: spawn(bin, args, {
|
||||||
cwd: root,
|
cwd: root,
|
||||||
|
|
@ -1507,7 +1502,6 @@ export const LuaLS: Info = {
|
||||||
})
|
})
|
||||||
if (!ok) return
|
if (!ok) return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -1682,7 +1676,6 @@ export const TerraformLS: Info = {
|
||||||
if (platform !== "win32") {
|
if (platform !== "win32") {
|
||||||
await fs.chmod(bin, 0o755).catch(() => {})
|
await fs.chmod(bin, 0o755).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -1768,7 +1761,6 @@ export const TexLab: Info = {
|
||||||
if (platform !== "win32") {
|
if (platform !== "win32") {
|
||||||
await fs.chmod(bin, 0o755).catch(() => {})
|
await fs.chmod(bin, 0o755).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -1948,7 +1940,6 @@ export const Tinymist: Info = {
|
||||||
if (platform !== "win32") {
|
if (platform !== "win32") {
|
||||||
await fs.chmod(bin, 0o755).catch(() => {})
|
await fs.chmod(bin, 0o755).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -439,7 +439,6 @@ export const layer = Layer.effect(
|
||||||
return DISABLED_RESULT
|
return DISABLED_RESULT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const { client: mcpClient, status } =
|
const { client: mcpClient, status } =
|
||||||
mcp.type === "remote"
|
mcp.type === "remote"
|
||||||
? yield* connectRemote(key, mcp as ConfigMCPV1.Info & { type: "remote" })
|
? yield* connectRemote(key, mcp as ConfigMCPV1.Info & { type: "remote" })
|
||||||
|
|
@ -844,7 +843,6 @@ export const layer = Layer.effect(
|
||||||
return yield* storeClient(s, mcpName, client, listed, mcpConfig.timeout)
|
return yield* storeClient(s, mcpName, client, listed, mcpConfig.timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const callbackPromise = McpOAuthCallback.waitForCallback(result.oauthState, mcpName)
|
const callbackPromise = McpOAuthCallback.waitForCallback(result.oauthState, mcpName)
|
||||||
|
|
||||||
yield* Effect.tryPromise(() => open(result.authorizationUrl)).pipe(
|
yield* Effect.tryPromise(() => open(result.authorizationUrl)).pipe(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { createConnection } from "net"
|
||||||
import { createServer } from "http"
|
import { createServer } from "http"
|
||||||
import { OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH, parseRedirectUri } from "./oauth-provider"
|
import { OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH, parseRedirectUri } from "./oauth-provider"
|
||||||
|
|
||||||
|
|
||||||
// Current callback server configuration (may differ from defaults if custom redirectUri is used)
|
// Current callback server configuration (may differ from defaults if custom redirectUri is used)
|
||||||
let currentPort = OAUTH_CALLBACK_PORT
|
let currentPort = OAUTH_CALLBACK_PORT
|
||||||
let currentPath = OAUTH_CALLBACK_PATH
|
let currentPath = OAUTH_CALLBACK_PATH
|
||||||
|
|
@ -85,7 +84,6 @@ function handleRequest(req: import("http").IncomingMessage, res: import("http").
|
||||||
const error = url.searchParams.get("error")
|
const error = url.searchParams.get("error")
|
||||||
const errorDescription = url.searchParams.get("error_description")
|
const errorDescription = url.searchParams.get("error_description")
|
||||||
|
|
||||||
|
|
||||||
// Enforce state parameter presence
|
// Enforce state parameter presence
|
||||||
if (!state) {
|
if (!state) {
|
||||||
const errorMsg = "Missing required state parameter - potential CSRF attack"
|
const errorMsg = "Missing required state parameter - potential CSRF attack"
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import type {
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { McpAuth } from "./auth"
|
import { McpAuth } from "./auth"
|
||||||
|
|
||||||
|
|
||||||
const OAUTH_CALLBACK_PORT = 19876
|
const OAUTH_CALLBACK_PORT = 19876
|
||||||
const OAUTH_CALLBACK_PATH = "/mcp/oauth/callback"
|
const OAUTH_CALLBACK_PATH = "/mcp/oauth/callback"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||||
import { createServer } from "http"
|
import { createServer } from "http"
|
||||||
import open from "open"
|
import open from "open"
|
||||||
|
|
||||||
|
|
||||||
const DO_OAUTH_CLIENT_ID = "b1a6c5158156caac821fd1b30253ca8acb52454a48fa744420e41889cb589f82"
|
const DO_OAUTH_CLIENT_ID = "b1a6c5158156caac821fd1b30253ca8acb52454a48fa744420e41889cb589f82"
|
||||||
const DO_AUTHORIZE_URL = "https://cloud.digitalocean.com/v1/oauth/authorize"
|
const DO_AUTHORIZE_URL = "https://cloud.digitalocean.com/v1/oauth/authorize"
|
||||||
const DO_API_BASE = "https://api.digitalocean.com"
|
const DO_API_BASE = "https://api.digitalocean.com"
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { setTimeout as sleep } from "node:timers/promises"
|
||||||
import { CopilotModels } from "./models"
|
import { CopilotModels } from "./models"
|
||||||
import { MessageV2 } from "@/session/message-v2"
|
import { MessageV2 } from "@/session/message-v2"
|
||||||
|
|
||||||
|
|
||||||
const CLIENT_ID = "Ov23li8tweQw6odWQebz"
|
const CLIENT_ID = "Ov23li8tweQw6odWQebz"
|
||||||
const API_VERSION = "2026-06-01"
|
const API_VERSION = "2026-06-01"
|
||||||
const UTILITY_MODELS = ["gpt-5.4-nano", "gpt-4.1", "gpt-4o", "gpt-4o-mini"]
|
const UTILITY_MODELS = ["gpt-5.4-nano", "gpt-4.1", "gpt-4o", "gpt-4o-mini"]
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||||
import { EventV2Bridge } from "@/event-v2-bridge"
|
import { EventV2Bridge } from "@/event-v2-bridge"
|
||||||
import { InstallationChannel } from "@opencode-ai/core/installation/version"
|
import { InstallationChannel } from "@opencode-ai/core/installation/version"
|
||||||
|
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
hooks: Hooks[]
|
hooks: Hooks[]
|
||||||
}
|
}
|
||||||
|
|
@ -163,8 +162,7 @@ export const layer = Layer.effect(
|
||||||
for (const plugin of flags.disableDefaultPlugins ? [] : internalPlugins(flags)) {
|
for (const plugin of flags.disableDefaultPlugins ? [] : internalPlugins(flags)) {
|
||||||
const init = yield* Effect.tryPromise({
|
const init = yield* Effect.tryPromise({
|
||||||
try: () => plugin(input),
|
try: () => plugin(input),
|
||||||
catch: (err) => {
|
catch: (err) => {},
|
||||||
},
|
|
||||||
}).pipe(Effect.option)
|
}).pipe(Effect.option)
|
||||||
if (init._tag === "Some") hooks.push(init.value)
|
if (init._tag === "Some") hooks.push(init.value)
|
||||||
}
|
}
|
||||||
|
|
@ -179,10 +177,8 @@ export const layer = Layer.effect(
|
||||||
items: plugins,
|
items: plugins,
|
||||||
kind: "server",
|
kind: "server",
|
||||||
report: {
|
report: {
|
||||||
start(candidate) {
|
start(candidate) {},
|
||||||
},
|
missing(candidate, _retry, message) {},
|
||||||
missing(candidate, _retry, message) {
|
|
||||||
},
|
|
||||||
error(candidate, _retry, stage, error, resolved) {
|
error(candidate, _retry, stage, error, resolved) {
|
||||||
const spec = candidate.plan.spec
|
const spec = candidate.plan.spec
|
||||||
const cause = error instanceof Error ? (error.cause ?? error) : error
|
const cause = error instanceof Error ? (error.cause ?? error) : error
|
||||||
|
|
@ -260,8 +256,7 @@ export const layer = Layer.effect(
|
||||||
(hook) =>
|
(hook) =>
|
||||||
Effect.tryPromise({
|
Effect.tryPromise({
|
||||||
try: () => Promise.resolve(hook.dispose?.()),
|
try: () => Promise.resolve(hook.dispose?.()),
|
||||||
catch: (error) => {
|
catch: (error) => {},
|
||||||
},
|
|
||||||
}).pipe(Effect.ignore),
|
}).pipe(Effect.ignore),
|
||||||
{ discard: true },
|
{ discard: true },
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { setTimeout as sleep } from "node:timers/promises"
|
||||||
import { createServer } from "http"
|
import { createServer } from "http"
|
||||||
import { OpenAIWebSocketPool } from "./ws-pool"
|
import { OpenAIWebSocketPool } from "./ws-pool"
|
||||||
|
|
||||||
|
|
||||||
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"
|
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"
|
||||||
const ISSUER = "https://auth.openai.com"
|
const ISSUER = "https://auth.openai.com"
|
||||||
const CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses"
|
const CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses"
|
||||||
|
|
@ -314,8 +313,7 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
|
||||||
|
|
||||||
function stopOAuthServer() {
|
function stopOAuthServer() {
|
||||||
if (oauthServer) {
|
if (oauthServer) {
|
||||||
oauthServer.close(() => {
|
oauthServer.close(() => {})
|
||||||
})
|
|
||||||
oauthServer = undefined
|
oauthServer = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { OpenAIWebSocket } from "./ws"
|
||||||
|
|
||||||
export const TITLE_HEADER = "x-opencode-title"
|
export const TITLE_HEADER = "x-opencode-title"
|
||||||
|
|
||||||
|
|
||||||
export interface CreateWebSocketFetchOptions {
|
export interface CreateWebSocketFetchOptions {
|
||||||
httpFetch?: typeof globalThis.fetch
|
httpFetch?: typeof globalThis.fetch
|
||||||
url?: string
|
url?: string
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { OAUTH_DUMMY_KEY } from "../auth"
|
||||||
import { createServer } from "http"
|
import { createServer } from "http"
|
||||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||||
|
|
||||||
|
|
||||||
// Public Grok-CLI OAuth client. xAI's auth server rejects loopback OAuth from
|
// Public Grok-CLI OAuth client. xAI's auth server rejects loopback OAuth from
|
||||||
// non-allowlisted clients, so we reuse the Grok-CLI client_id that xAI ships
|
// non-allowlisted clients, so we reuse the Grok-CLI client_id that xAI ships
|
||||||
// for desktop OAuth flows. Source of truth: hermes-agent PR #26534.
|
// for desktop OAuth flows. Source of truth: hermes-agent PR #26534.
|
||||||
|
|
|
||||||
|
|
@ -1306,7 +1306,6 @@ export const layer = Layer.effect(
|
||||||
get: (key: string) => env.get(key),
|
get: (key: string) => env.get(key),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function mergeProvider(providerID: ProviderV2.ID, provider: Partial<Info>) {
|
function mergeProvider(providerID: ProviderV2.ID, provider: Partial<Info>) {
|
||||||
const existing = providers[providerID]
|
const existing = providers[providerID]
|
||||||
if (existing) {
|
if (existing) {
|
||||||
|
|
@ -1540,8 +1539,7 @@ export const layer = Layer.effect(
|
||||||
providers[gitlab].models[modelID] = model
|
providers[gitlab].models[modelID] = model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1592,7 +1590,6 @@ export const layer = Layer.effect(
|
||||||
delete providers[providerID]
|
delete providers[providerID]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -138,9 +138,7 @@ export const layer = Layer.effect(
|
||||||
s.queue.set(sessionID, next)
|
s.queue.set(sessionID, next)
|
||||||
yield* flush(sessionID).pipe(
|
yield* flush(sessionID).pipe(
|
||||||
Effect.delay(1000),
|
Effect.delay(1000),
|
||||||
Effect.catchCause((cause) =>
|
Effect.catchCause((cause) => Effect.logError("share flush failed", { sessionID: sessionID, cause: cause })),
|
||||||
Effect.logError("share flush failed", { sessionID: sessionID, cause: cause }),
|
|
||||||
),
|
|
||||||
Effect.forkIn(s.scope),
|
Effect.forkIn(s.scope),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
@ -263,7 +261,11 @@ export const layer = Layer.effect(
|
||||||
)
|
)
|
||||||
|
|
||||||
if (res.status >= 400) {
|
if (res.status >= 400) {
|
||||||
yield* Effect.logWarning("failed to sync share", { sessionID: sessionID, shareID: share.id, status: res.status })
|
yield* Effect.logWarning("failed to sync share", {
|
||||||
|
sessionID: sessionID,
|
||||||
|
shareID: share.id,
|
||||||
|
status: res.status,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -325,9 +327,7 @@ export const layer = Layer.effect(
|
||||||
const s = yield* InstanceState.get(state)
|
const s = yield* InstanceState.get(state)
|
||||||
s.shared.set(sessionID, result)
|
s.shared.set(sessionID, result)
|
||||||
yield* full(sessionID).pipe(
|
yield* full(sessionID).pipe(
|
||||||
Effect.catchCause((cause) =>
|
Effect.catchCause((cause) => Effect.logError("share full sync failed", { sessionID: sessionID, cause: cause })),
|
||||||
Effect.logError("share full sync failed", { sessionID: sessionID, cause: cause }),
|
|
||||||
),
|
|
||||||
Effect.forkIn(s.scope),
|
Effect.forkIn(s.scope),
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,7 @@ export const layer: Layer.Layer<Service, never, FSUtil.Service | Path.Path | Htt
|
||||||
Effect.flatMap((res) => res.arrayBuffer),
|
Effect.flatMap((res) => res.arrayBuffer),
|
||||||
Effect.flatMap((body) => fs.writeWithDirs(dest, new Uint8Array(body))),
|
Effect.flatMap((body) => fs.writeWithDirs(dest, new Uint8Array(body))),
|
||||||
Effect.as(true),
|
Effect.as(true),
|
||||||
Effect.catch((err) =>
|
Effect.catch((err) => Effect.logError("failed to download", { url: url, error: err }).pipe(Effect.as(false))),
|
||||||
Effect.logError("failed to download", { url: url, error: err }).pipe(Effect.as(false)),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -66,8 +64,7 @@ export const layer: Layer.Layer<Service, never, FSUtil.Service | Path.Path | Htt
|
||||||
const missing = data.skills.filter((skill) => !skill.files.includes("SKILL.md"))
|
const missing = data.skills.filter((skill) => !skill.files.includes("SKILL.md"))
|
||||||
yield* Effect.forEach(
|
yield* Effect.forEach(
|
||||||
missing,
|
missing,
|
||||||
(skill) =>
|
(skill) => Effect.logWarning("skill entry missing SKILL.md", { url: index, skill: skill.name }),
|
||||||
Effect.logWarning("skill entry missing SKILL.md", { url: index, skill: skill.name }),
|
|
||||||
{ discard: true },
|
{ discard: true },
|
||||||
)
|
)
|
||||||
const list = data.skills.filter((skill) => skill.files.includes("SKILL.md"))
|
const list = data.skills.filter((skill) => skill.files.includes("SKILL.md"))
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,11 @@ const add = Effect.fnUntraced(function* (state: State, match: string, events: Ev
|
||||||
if (!isSkillFrontmatter(md.data)) return
|
if (!isSkillFrontmatter(md.data)) return
|
||||||
|
|
||||||
if (state.skills[md.data.name]) {
|
if (state.skills[md.data.name]) {
|
||||||
yield* Effect.logWarning("duplicate skill name", { name: md.data.name, existing: state.skills[md.data.name].location, duplicate: match })
|
yield* Effect.logWarning("duplicate skill name", {
|
||||||
|
name: md.data.name,
|
||||||
|
existing: state.skills[md.data.name].location,
|
||||||
|
duplicate: match,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
state.dirs.add(path.dirname(match))
|
state.dirs.add(path.dirname(match))
|
||||||
|
|
@ -153,7 +157,9 @@ const scan = Effect.fnUntraced(function* (
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Effect.catch((error) => {
|
Effect.catch((error) => {
|
||||||
if (!opts?.scope) return Effect.die(error)
|
if (!opts?.scope) return Effect.die(error)
|
||||||
return Effect.logError(`failed to scan ${opts.scope} skills`, { dir: root, error: error }).pipe(Effect.as([] as string[]))
|
return Effect.logError(`failed to scan ${opts.scope} skills`, { dir: root, error: error }).pipe(
|
||||||
|
Effect.as([] as string[]),
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -564,8 +564,8 @@ export const layer: Layer.Layer<Service, never, FSUtil.Service | AppProcess.Serv
|
||||||
yield* Effect.logInfo(
|
yield* Effect.logInfo(
|
||||||
"git cat-file --batch failed during snapshot diff, falling back to per-file git show",
|
"git cat-file --batch failed during snapshot diff, falling back to per-file git show",
|
||||||
{
|
{
|
||||||
stderr: batch.stderr.toString("utf8"),
|
stderr: batch.stderr.toString("utf8"),
|
||||||
refs: refs.length,
|
refs: refs.length,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue