|
|
@@ -300,288 +300,310 @@ export const RunCommand = effectCmd({
|
|
|
}),
|
|
|
handler: Effect.fn("Cli.run")(function* (args) {
|
|
|
yield* Effect.promise(async () => {
|
|
|
- let message = [...args.message, ...(args["--"] || [])]
|
|
|
- .map((arg) => (arg.includes(" ") ? `"${arg.replace(/"/g, '\\"')}"` : arg))
|
|
|
- .join(" ")
|
|
|
-
|
|
|
- const directory = (() => {
|
|
|
- if (!args.dir) return undefined
|
|
|
- if (args.attach) return args.dir
|
|
|
- try {
|
|
|
- process.chdir(args.dir)
|
|
|
- return process.cwd()
|
|
|
- } catch {
|
|
|
- UI.error("Failed to change directory to " + args.dir)
|
|
|
- process.exit(1)
|
|
|
- }
|
|
|
- })()
|
|
|
-
|
|
|
- const files: { type: "file"; url: string; filename: string; mime: string }[] = []
|
|
|
- if (args.file) {
|
|
|
- const list = Array.isArray(args.file) ? args.file : [args.file]
|
|
|
+ let message = [...args.message, ...(args["--"] || [])]
|
|
|
+ .map((arg) => (arg.includes(" ") ? `"${arg.replace(/"/g, '\\"')}"` : arg))
|
|
|
+ .join(" ")
|
|
|
|
|
|
- for (const filePath of list) {
|
|
|
- const resolvedPath = path.resolve(process.cwd(), filePath)
|
|
|
- if (!(await Filesystem.exists(resolvedPath))) {
|
|
|
- UI.error(`File not found: ${filePath}`)
|
|
|
+ const directory = (() => {
|
|
|
+ if (!args.dir) return undefined
|
|
|
+ if (args.attach) return args.dir
|
|
|
+ try {
|
|
|
+ process.chdir(args.dir)
|
|
|
+ return process.cwd()
|
|
|
+ } catch {
|
|
|
+ UI.error("Failed to change directory to " + args.dir)
|
|
|
process.exit(1)
|
|
|
}
|
|
|
+ })()
|
|
|
|
|
|
- const mime = (await Filesystem.isDir(resolvedPath)) ? "application/x-directory" : "text/plain"
|
|
|
+ const files: { type: "file"; url: string; filename: string; mime: string }[] = []
|
|
|
+ if (args.file) {
|
|
|
+ const list = Array.isArray(args.file) ? args.file : [args.file]
|
|
|
|
|
|
- files.push({
|
|
|
- type: "file",
|
|
|
- url: pathToFileURL(resolvedPath).href,
|
|
|
- filename: path.basename(resolvedPath),
|
|
|
- mime,
|
|
|
- })
|
|
|
+ for (const filePath of list) {
|
|
|
+ const resolvedPath = path.resolve(process.cwd(), filePath)
|
|
|
+ if (!(await Filesystem.exists(resolvedPath))) {
|
|
|
+ UI.error(`File not found: ${filePath}`)
|
|
|
+ process.exit(1)
|
|
|
+ }
|
|
|
+
|
|
|
+ const mime = (await Filesystem.isDir(resolvedPath)) ? "application/x-directory" : "text/plain"
|
|
|
+
|
|
|
+ files.push({
|
|
|
+ type: "file",
|
|
|
+ url: pathToFileURL(resolvedPath).href,
|
|
|
+ filename: path.basename(resolvedPath),
|
|
|
+ mime,
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- if (!process.stdin.isTTY) message += "\n" + (await Bun.stdin.text())
|
|
|
-
|
|
|
- if (message.trim().length === 0 && !args.command) {
|
|
|
- UI.error("You must provide a message or a command")
|
|
|
- process.exit(1)
|
|
|
- }
|
|
|
-
|
|
|
- if (args.fork && !args.continue && !args.session) {
|
|
|
- UI.error("--fork requires --continue or --session")
|
|
|
- process.exit(1)
|
|
|
- }
|
|
|
-
|
|
|
- const rules: Permission.Ruleset = [
|
|
|
- {
|
|
|
- permission: "question",
|
|
|
- action: "deny",
|
|
|
- pattern: "*",
|
|
|
- },
|
|
|
- {
|
|
|
- permission: "plan_enter",
|
|
|
- action: "deny",
|
|
|
- pattern: "*",
|
|
|
- },
|
|
|
- {
|
|
|
- permission: "plan_exit",
|
|
|
- action: "deny",
|
|
|
- pattern: "*",
|
|
|
- },
|
|
|
- ]
|
|
|
-
|
|
|
- function title() {
|
|
|
- if (args.title === undefined) return
|
|
|
- if (args.title !== "") return args.title
|
|
|
- return message.slice(0, 50) + (message.length > 50 ? "..." : "")
|
|
|
- }
|
|
|
-
|
|
|
- async function session(sdk: OpencodeClient) {
|
|
|
- const baseID = args.continue ? (await sdk.session.list()).data?.find((s) => !s.parentID)?.id : args.session
|
|
|
-
|
|
|
- if (baseID && args.fork) {
|
|
|
- const forked = await sdk.session.fork({ sessionID: baseID })
|
|
|
- return forked.data?.id
|
|
|
+
|
|
|
+ if (!process.stdin.isTTY) message += "\n" + (await Bun.stdin.text())
|
|
|
+
|
|
|
+ if (message.trim().length === 0 && !args.command) {
|
|
|
+ UI.error("You must provide a message or a command")
|
|
|
+ process.exit(1)
|
|
|
}
|
|
|
|
|
|
- if (baseID) return baseID
|
|
|
+ if (args.fork && !args.continue && !args.session) {
|
|
|
+ UI.error("--fork requires --continue or --session")
|
|
|
+ process.exit(1)
|
|
|
+ }
|
|
|
|
|
|
- const name = title()
|
|
|
- const result = await sdk.session.create({ title: name, permission: rules })
|
|
|
- return result.data?.id
|
|
|
- }
|
|
|
+ const rules: Permission.Ruleset = [
|
|
|
+ {
|
|
|
+ permission: "question",
|
|
|
+ action: "deny",
|
|
|
+ pattern: "*",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ permission: "plan_enter",
|
|
|
+ action: "deny",
|
|
|
+ pattern: "*",
|
|
|
+ },
|
|
|
+ {
|
|
|
+ permission: "plan_exit",
|
|
|
+ action: "deny",
|
|
|
+ pattern: "*",
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ function title() {
|
|
|
+ if (args.title === undefined) return
|
|
|
+ if (args.title !== "") return args.title
|
|
|
+ return message.slice(0, 50) + (message.length > 50 ? "..." : "")
|
|
|
+ }
|
|
|
|
|
|
- async function share(sdk: OpencodeClient, sessionID: string) {
|
|
|
- const cfg = await sdk.config.get()
|
|
|
- if (!cfg.data) return
|
|
|
- if (cfg.data.share !== "auto" && !Flag.OPENCODE_AUTO_SHARE && !args.share) return
|
|
|
- const res = await sdk.session.share({ sessionID }).catch((error) => {
|
|
|
- if (error instanceof Error && error.message.includes("disabled")) {
|
|
|
- UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message)
|
|
|
+ async function session(sdk: OpencodeClient) {
|
|
|
+ const baseID = args.continue ? (await sdk.session.list()).data?.find((s) => !s.parentID)?.id : args.session
|
|
|
+
|
|
|
+ if (baseID && args.fork) {
|
|
|
+ const forked = await sdk.session.fork({ sessionID: baseID })
|
|
|
+ return forked.data?.id
|
|
|
}
|
|
|
- return { error }
|
|
|
- })
|
|
|
- if (!res.error && "data" in res && res.data?.share?.url) {
|
|
|
- UI.println(UI.Style.TEXT_INFO_BOLD + "~ " + res.data.share.url)
|
|
|
+
|
|
|
+ if (baseID) return baseID
|
|
|
+
|
|
|
+ const name = title()
|
|
|
+ const result = await sdk.session.create({ title: name, permission: rules })
|
|
|
+ return result.data?.id
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- async function execute(sdk: OpencodeClient) {
|
|
|
- function tool(part: ToolPart) {
|
|
|
- try {
|
|
|
- if (part.tool === ShellID.ToolID) return shell(props<typeof ShellTool>(part))
|
|
|
- if (part.tool === "glob") return glob(props<typeof GlobTool>(part))
|
|
|
- if (part.tool === "grep") return grep(props<typeof GrepTool>(part))
|
|
|
- if (part.tool === "read") return read(props<typeof ReadTool>(part))
|
|
|
- if (part.tool === "write") return write(props<typeof WriteTool>(part))
|
|
|
- if (part.tool === "webfetch") return webfetch(props<typeof WebFetchTool>(part))
|
|
|
- if (part.tool === "edit") return edit(props<typeof EditTool>(part))
|
|
|
- if (part.tool === "websearch") return websearch(props<typeof WebSearchTool>(part))
|
|
|
- if (part.tool === "task") return task(props<typeof TaskTool>(part))
|
|
|
- if (part.tool === "todowrite") return todo(props<typeof TodoWriteTool>(part))
|
|
|
- if (part.tool === "skill") return skill(props<typeof SkillTool>(part))
|
|
|
- return fallback(part)
|
|
|
- } catch {
|
|
|
- return fallback(part)
|
|
|
+ async function share(sdk: OpencodeClient, sessionID: string) {
|
|
|
+ const cfg = await sdk.config.get()
|
|
|
+ if (!cfg.data) return
|
|
|
+ if (cfg.data.share !== "auto" && !Flag.OPENCODE_AUTO_SHARE && !args.share) return
|
|
|
+ const res = await sdk.session.share({ sessionID }).catch((error) => {
|
|
|
+ if (error instanceof Error && error.message.includes("disabled")) {
|
|
|
+ UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message)
|
|
|
+ }
|
|
|
+ return { error }
|
|
|
+ })
|
|
|
+ if (!res.error && "data" in res && res.data?.share?.url) {
|
|
|
+ UI.println(UI.Style.TEXT_INFO_BOLD + "~ " + res.data.share.url)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- function emit(type: string, data: Record<string, unknown>) {
|
|
|
- if (args.format === "json") {
|
|
|
- process.stdout.write(JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL)
|
|
|
- return true
|
|
|
+ async function execute(sdk: OpencodeClient) {
|
|
|
+ function tool(part: ToolPart) {
|
|
|
+ try {
|
|
|
+ if (part.tool === ShellID.ToolID) return shell(props<typeof ShellTool>(part))
|
|
|
+ if (part.tool === "glob") return glob(props<typeof GlobTool>(part))
|
|
|
+ if (part.tool === "grep") return grep(props<typeof GrepTool>(part))
|
|
|
+ if (part.tool === "read") return read(props<typeof ReadTool>(part))
|
|
|
+ if (part.tool === "write") return write(props<typeof WriteTool>(part))
|
|
|
+ if (part.tool === "webfetch") return webfetch(props<typeof WebFetchTool>(part))
|
|
|
+ if (part.tool === "edit") return edit(props<typeof EditTool>(part))
|
|
|
+ if (part.tool === "websearch") return websearch(props<typeof WebSearchTool>(part))
|
|
|
+ if (part.tool === "task") return task(props<typeof TaskTool>(part))
|
|
|
+ if (part.tool === "todowrite") return todo(props<typeof TodoWriteTool>(part))
|
|
|
+ if (part.tool === "skill") return skill(props<typeof SkillTool>(part))
|
|
|
+ return fallback(part)
|
|
|
+ } catch {
|
|
|
+ return fallback(part)
|
|
|
+ }
|
|
|
}
|
|
|
- return false
|
|
|
- }
|
|
|
|
|
|
- const events = await sdk.event.subscribe()
|
|
|
- let error: string | undefined
|
|
|
-
|
|
|
- async function loop() {
|
|
|
- const toggles = new Map<string, boolean>()
|
|
|
-
|
|
|
- for await (const event of events.stream) {
|
|
|
- if (
|
|
|
- event.type === "message.updated" &&
|
|
|
- event.properties.info.role === "assistant" &&
|
|
|
- args.format !== "json" &&
|
|
|
- toggles.get("start") !== true
|
|
|
- ) {
|
|
|
- UI.empty()
|
|
|
- UI.println(`> ${event.properties.info.agent} · ${event.properties.info.modelID}`)
|
|
|
- UI.empty()
|
|
|
- toggles.set("start", true)
|
|
|
+ function emit(type: string, data: Record<string, unknown>) {
|
|
|
+ if (args.format === "json") {
|
|
|
+ process.stdout.write(JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL)
|
|
|
+ return true
|
|
|
}
|
|
|
+ return false
|
|
|
+ }
|
|
|
|
|
|
- if (event.type === "message.part.updated") {
|
|
|
- const part = event.properties.part
|
|
|
- if (part.sessionID !== sessionID) continue
|
|
|
+ const events = await sdk.event.subscribe()
|
|
|
+ let error: string | undefined
|
|
|
|
|
|
- if (part.type === "tool" && (part.state.status === "completed" || part.state.status === "error")) {
|
|
|
- if (emit("tool_use", { part })) continue
|
|
|
- if (part.state.status === "completed") {
|
|
|
- tool(part)
|
|
|
- continue
|
|
|
- }
|
|
|
- inline({
|
|
|
- icon: "✗",
|
|
|
- title: `${part.tool} failed`,
|
|
|
- })
|
|
|
- UI.error(part.state.error)
|
|
|
- }
|
|
|
+ async function loop() {
|
|
|
+ const toggles = new Map<string, boolean>()
|
|
|
|
|
|
+ for await (const event of events.stream) {
|
|
|
if (
|
|
|
- part.type === "tool" &&
|
|
|
- part.tool === "task" &&
|
|
|
- part.state.status === "running" &&
|
|
|
- args.format !== "json"
|
|
|
+ event.type === "message.updated" &&
|
|
|
+ event.properties.info.role === "assistant" &&
|
|
|
+ args.format !== "json" &&
|
|
|
+ toggles.get("start") !== true
|
|
|
) {
|
|
|
- if (toggles.get(part.id) === true) continue
|
|
|
- task(props<typeof TaskTool>(part))
|
|
|
- toggles.set(part.id, true)
|
|
|
+ UI.empty()
|
|
|
+ UI.println(`> ${event.properties.info.agent} · ${event.properties.info.modelID}`)
|
|
|
+ UI.empty()
|
|
|
+ toggles.set("start", true)
|
|
|
}
|
|
|
|
|
|
- if (part.type === "step-start") {
|
|
|
- if (emit("step_start", { part })) continue
|
|
|
- }
|
|
|
+ if (event.type === "message.part.updated") {
|
|
|
+ const part = event.properties.part
|
|
|
+ if (part.sessionID !== sessionID) continue
|
|
|
+
|
|
|
+ if (part.type === "tool" && (part.state.status === "completed" || part.state.status === "error")) {
|
|
|
+ if (emit("tool_use", { part })) continue
|
|
|
+ if (part.state.status === "completed") {
|
|
|
+ tool(part)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ inline({
|
|
|
+ icon: "✗",
|
|
|
+ title: `${part.tool} failed`,
|
|
|
+ })
|
|
|
+ UI.error(part.state.error)
|
|
|
+ }
|
|
|
|
|
|
- if (part.type === "step-finish") {
|
|
|
- if (emit("step_finish", { part })) continue
|
|
|
- }
|
|
|
+ if (
|
|
|
+ part.type === "tool" &&
|
|
|
+ part.tool === "task" &&
|
|
|
+ part.state.status === "running" &&
|
|
|
+ args.format !== "json"
|
|
|
+ ) {
|
|
|
+ if (toggles.get(part.id) === true) continue
|
|
|
+ task(props<typeof TaskTool>(part))
|
|
|
+ toggles.set(part.id, true)
|
|
|
+ }
|
|
|
|
|
|
- if (part.type === "text" && part.time?.end) {
|
|
|
- if (emit("text", { part })) continue
|
|
|
- const text = part.text.trim()
|
|
|
- if (!text) continue
|
|
|
- if (!process.stdout.isTTY) {
|
|
|
- process.stdout.write(text + EOL)
|
|
|
- continue
|
|
|
+ if (part.type === "step-start") {
|
|
|
+ if (emit("step_start", { part })) continue
|
|
|
}
|
|
|
- UI.empty()
|
|
|
- UI.println(text)
|
|
|
- UI.empty()
|
|
|
- }
|
|
|
|
|
|
- if (part.type === "reasoning" && part.time?.end && args.thinking) {
|
|
|
- if (emit("reasoning", { part })) continue
|
|
|
- const text = part.text.trim()
|
|
|
- if (!text) continue
|
|
|
- const line = `Thinking: ${text}`
|
|
|
- if (process.stdout.isTTY) {
|
|
|
+ if (part.type === "step-finish") {
|
|
|
+ if (emit("step_finish", { part })) continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if (part.type === "text" && part.time?.end) {
|
|
|
+ if (emit("text", { part })) continue
|
|
|
+ const text = part.text.trim()
|
|
|
+ if (!text) continue
|
|
|
+ if (!process.stdout.isTTY) {
|
|
|
+ process.stdout.write(text + EOL)
|
|
|
+ continue
|
|
|
+ }
|
|
|
UI.empty()
|
|
|
- UI.println(`${UI.Style.TEXT_DIM}\u001b[3m${line}\u001b[0m${UI.Style.TEXT_NORMAL}`)
|
|
|
+ UI.println(text)
|
|
|
UI.empty()
|
|
|
- continue
|
|
|
}
|
|
|
- process.stdout.write(line + EOL)
|
|
|
+
|
|
|
+ if (part.type === "reasoning" && part.time?.end && args.thinking) {
|
|
|
+ if (emit("reasoning", { part })) continue
|
|
|
+ const text = part.text.trim()
|
|
|
+ if (!text) continue
|
|
|
+ const line = `Thinking: ${text}`
|
|
|
+ if (process.stdout.isTTY) {
|
|
|
+ UI.empty()
|
|
|
+ UI.println(`${UI.Style.TEXT_DIM}\u001b[3m${line}\u001b[0m${UI.Style.TEXT_NORMAL}`)
|
|
|
+ UI.empty()
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ process.stdout.write(line + EOL)
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if (event.type === "session.error") {
|
|
|
- const props = event.properties
|
|
|
- if (props.sessionID !== sessionID || !props.error) continue
|
|
|
- let err = String(props.error.name)
|
|
|
- if ("data" in props.error && props.error.data && "message" in props.error.data) {
|
|
|
- err = String(props.error.data.message)
|
|
|
+ if (event.type === "session.error") {
|
|
|
+ const props = event.properties
|
|
|
+ if (props.sessionID !== sessionID || !props.error) continue
|
|
|
+ let err = String(props.error.name)
|
|
|
+ if ("data" in props.error && props.error.data && "message" in props.error.data) {
|
|
|
+ err = String(props.error.data.message)
|
|
|
+ }
|
|
|
+ error = error ? error + EOL + err : err
|
|
|
+ if (emit("error", { error: props.error })) continue
|
|
|
+ UI.error(err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if (
|
|
|
+ event.type === "session.status" &&
|
|
|
+ event.properties.sessionID === sessionID &&
|
|
|
+ event.properties.status.type === "idle"
|
|
|
+ ) {
|
|
|
+ break
|
|
|
}
|
|
|
- error = error ? error + EOL + err : err
|
|
|
- if (emit("error", { error: props.error })) continue
|
|
|
- UI.error(err)
|
|
|
- }
|
|
|
|
|
|
- if (
|
|
|
- event.type === "session.status" &&
|
|
|
- event.properties.sessionID === sessionID &&
|
|
|
- event.properties.status.type === "idle"
|
|
|
- ) {
|
|
|
- break
|
|
|
+ if (event.type === "permission.asked") {
|
|
|
+ const permission = event.properties
|
|
|
+ if (permission.sessionID !== sessionID) continue
|
|
|
+
|
|
|
+ if (args["dangerously-skip-permissions"]) {
|
|
|
+ await sdk.permission.reply({
|
|
|
+ requestID: permission.id,
|
|
|
+ reply: "once",
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ UI.println(
|
|
|
+ UI.Style.TEXT_WARNING_BOLD + "!",
|
|
|
+ UI.Style.TEXT_NORMAL +
|
|
|
+ `permission requested: ${permission.permission} (${permission.patterns.join(", ")}); auto-rejecting`,
|
|
|
+ )
|
|
|
+ await sdk.permission.reply({
|
|
|
+ requestID: permission.id,
|
|
|
+ reply: "reject",
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (event.type === "permission.asked") {
|
|
|
- const permission = event.properties
|
|
|
- if (permission.sessionID !== sessionID) continue
|
|
|
+ // Validate agent if specified
|
|
|
+ const agent = await (async () => {
|
|
|
+ if (!args.agent) return undefined
|
|
|
+ const name = args.agent
|
|
|
|
|
|
- if (args["dangerously-skip-permissions"]) {
|
|
|
- await sdk.permission.reply({
|
|
|
- requestID: permission.id,
|
|
|
- reply: "once",
|
|
|
- })
|
|
|
- } else {
|
|
|
+ // When attaching, validate against the running server instead of local Instance state.
|
|
|
+ if (args.attach) {
|
|
|
+ const modes = await sdk.app
|
|
|
+ .agents(undefined, { throwOnError: true })
|
|
|
+ .then((x) => x.data ?? [])
|
|
|
+ .catch(() => undefined)
|
|
|
+
|
|
|
+ if (!modes) {
|
|
|
UI.println(
|
|
|
UI.Style.TEXT_WARNING_BOLD + "!",
|
|
|
- UI.Style.TEXT_NORMAL +
|
|
|
- `permission requested: ${permission.permission} (${permission.patterns.join(", ")}); auto-rejecting`,
|
|
|
+ UI.Style.TEXT_NORMAL,
|
|
|
+ `failed to list agents from ${args.attach}. Falling back to default agent`,
|
|
|
)
|
|
|
- await sdk.permission.reply({
|
|
|
- requestID: permission.id,
|
|
|
- reply: "reject",
|
|
|
- })
|
|
|
+ return undefined
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- // Validate agent if specified
|
|
|
- const agent = await (async () => {
|
|
|
- if (!args.agent) return undefined
|
|
|
- const name = args.agent
|
|
|
+ const agent = modes.find((a) => a.name === name)
|
|
|
+ if (!agent) {
|
|
|
+ UI.println(
|
|
|
+ UI.Style.TEXT_WARNING_BOLD + "!",
|
|
|
+ UI.Style.TEXT_NORMAL,
|
|
|
+ `agent "${name}" not found. Falling back to default agent`,
|
|
|
+ )
|
|
|
+ return undefined
|
|
|
+ }
|
|
|
|
|
|
- // When attaching, validate against the running server instead of local Instance state.
|
|
|
- if (args.attach) {
|
|
|
- const modes = await sdk.app
|
|
|
- .agents(undefined, { throwOnError: true })
|
|
|
- .then((x) => x.data ?? [])
|
|
|
- .catch(() => undefined)
|
|
|
+ if (agent.mode === "subagent") {
|
|
|
+ UI.println(
|
|
|
+ UI.Style.TEXT_WARNING_BOLD + "!",
|
|
|
+ UI.Style.TEXT_NORMAL,
|
|
|
+ `agent "${name}" is a subagent, not a primary agent. Falling back to default agent`,
|
|
|
+ )
|
|
|
+ return undefined
|
|
|
+ }
|
|
|
|
|
|
- if (!modes) {
|
|
|
- UI.println(
|
|
|
- UI.Style.TEXT_WARNING_BOLD + "!",
|
|
|
- UI.Style.TEXT_NORMAL,
|
|
|
- `failed to list agents from ${args.attach}. Falling back to default agent`,
|
|
|
- )
|
|
|
- return undefined
|
|
|
+ return name
|
|
|
}
|
|
|
|
|
|
- const agent = modes.find((a) => a.name === name)
|
|
|
- if (!agent) {
|
|
|
+ const entry = await AppRuntime.runPromise(Agent.Service.use((svc) => svc.get(name)))
|
|
|
+ if (!entry) {
|
|
|
UI.println(
|
|
|
UI.Style.TEXT_WARNING_BOLD + "!",
|
|
|
UI.Style.TEXT_NORMAL,
|
|
|
@@ -589,8 +611,7 @@ export const RunCommand = effectCmd({
|
|
|
)
|
|
|
return undefined
|
|
|
}
|
|
|
-
|
|
|
- if (agent.mode === "subagent") {
|
|
|
+ if (entry.mode === "subagent") {
|
|
|
UI.println(
|
|
|
UI.Style.TEXT_WARNING_BOLD + "!",
|
|
|
UI.Style.TEXT_NORMAL,
|
|
|
@@ -598,81 +619,60 @@ export const RunCommand = effectCmd({
|
|
|
)
|
|
|
return undefined
|
|
|
}
|
|
|
-
|
|
|
return name
|
|
|
- }
|
|
|
+ })()
|
|
|
|
|
|
- const entry = await AppRuntime.runPromise(Agent.Service.use((svc) => svc.get(name)))
|
|
|
- if (!entry) {
|
|
|
- UI.println(
|
|
|
- UI.Style.TEXT_WARNING_BOLD + "!",
|
|
|
- UI.Style.TEXT_NORMAL,
|
|
|
- `agent "${name}" not found. Falling back to default agent`,
|
|
|
- )
|
|
|
- return undefined
|
|
|
- }
|
|
|
- if (entry.mode === "subagent") {
|
|
|
- UI.println(
|
|
|
- UI.Style.TEXT_WARNING_BOLD + "!",
|
|
|
- UI.Style.TEXT_NORMAL,
|
|
|
- `agent "${name}" is a subagent, not a primary agent. Falling back to default agent`,
|
|
|
- )
|
|
|
- return undefined
|
|
|
+ const sessionID = await session(sdk)
|
|
|
+ if (!sessionID) {
|
|
|
+ UI.error("Session not found")
|
|
|
+ process.exit(1)
|
|
|
}
|
|
|
- return name
|
|
|
- })()
|
|
|
+ await share(sdk, sessionID)
|
|
|
|
|
|
- const sessionID = await session(sdk)
|
|
|
- if (!sessionID) {
|
|
|
- UI.error("Session not found")
|
|
|
- process.exit(1)
|
|
|
- }
|
|
|
- await share(sdk, sessionID)
|
|
|
+ loop().catch((e) => {
|
|
|
+ console.error(e)
|
|
|
+ process.exit(1)
|
|
|
+ })
|
|
|
|
|
|
- loop().catch((e) => {
|
|
|
- console.error(e)
|
|
|
- process.exit(1)
|
|
|
- })
|
|
|
+ if (args.command) {
|
|
|
+ await sdk.session.command({
|
|
|
+ sessionID,
|
|
|
+ agent,
|
|
|
+ model: args.model,
|
|
|
+ command: args.command,
|
|
|
+ arguments: message,
|
|
|
+ variant: args.variant,
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ const model = args.model ? Provider.parseModel(args.model) : undefined
|
|
|
+ await sdk.session.prompt({
|
|
|
+ sessionID,
|
|
|
+ agent,
|
|
|
+ model,
|
|
|
+ variant: args.variant,
|
|
|
+ parts: [...files, { type: "text", text: message }],
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if (args.command) {
|
|
|
- await sdk.session.command({
|
|
|
- sessionID,
|
|
|
- agent,
|
|
|
- model: args.model,
|
|
|
- command: args.command,
|
|
|
- arguments: message,
|
|
|
- variant: args.variant,
|
|
|
- })
|
|
|
- } else {
|
|
|
- const model = args.model ? Provider.parseModel(args.model) : undefined
|
|
|
- await sdk.session.prompt({
|
|
|
- sessionID,
|
|
|
- agent,
|
|
|
- model,
|
|
|
- variant: args.variant,
|
|
|
- parts: [...files, { type: "text", text: message }],
|
|
|
- })
|
|
|
+ if (args.attach) {
|
|
|
+ const headers = (() => {
|
|
|
+ const password = args.password ?? process.env.OPENCODE_SERVER_PASSWORD
|
|
|
+ if (!password) return undefined
|
|
|
+ const username = process.env.OPENCODE_SERVER_USERNAME ?? "opencode"
|
|
|
+ const auth = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`
|
|
|
+ return { Authorization: auth }
|
|
|
+ })()
|
|
|
+ const sdk = createOpencodeClient({ baseUrl: args.attach, directory, headers })
|
|
|
+ return await execute(sdk)
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- if (args.attach) {
|
|
|
- const headers = (() => {
|
|
|
- const password = args.password ?? process.env.OPENCODE_SERVER_PASSWORD
|
|
|
- if (!password) return undefined
|
|
|
- const username = process.env.OPENCODE_SERVER_USERNAME ?? "opencode"
|
|
|
- const auth = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`
|
|
|
- return { Authorization: auth }
|
|
|
- })()
|
|
|
- const sdk = createOpencodeClient({ baseUrl: args.attach, directory, headers })
|
|
|
- return await execute(sdk)
|
|
|
- }
|
|
|
-
|
|
|
- const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
|
- const request = new Request(input, init)
|
|
|
- return Server.Default().app.fetch(request)
|
|
|
- }) as typeof globalThis.fetch
|
|
|
- const sdk = createOpencodeClient({ baseUrl: "http://opencode.internal", fetch: fetchFn })
|
|
|
- await execute(sdk)
|
|
|
+
|
|
|
+ const fetchFn = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
|
+ const request = new Request(input, init)
|
|
|
+ return Server.Default().app.fetch(request)
|
|
|
+ }) as typeof globalThis.fetch
|
|
|
+ const sdk = createOpencodeClient({ baseUrl: "http://opencode.internal", fetch: fetchFn })
|
|
|
+ await execute(sdk)
|
|
|
})
|
|
|
}),
|
|
|
})
|