summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-03-23 13:11:38 -0400
committerGitHub <[email protected]>2026-03-23 13:11:38 -0400
commit3a0bf2f39f784d1b1f54e9fbe6c4df32a2ab67a7 (patch)
treec8af356112b2ec8b6511527d7af3df5db8780708 /packages
parentb556979634b038fae2c319811196b58af0faf36b (diff)
downloadopencode-3a0bf2f39f784d1b1f54e9fbe6c4df32a2ab67a7.tar.gz
opencode-3a0bf2f39f784d1b1f54e9fbe6c4df32a2ab67a7.zip
fix console account URL handling (#18809)
Diffstat (limited to 'packages')
-rw-r--r--packages/opencode/src/cli/cmd/account.ts56
-rw-r--r--packages/opencode/test/cli/account.test.ts24
2 files changed, 70 insertions, 10 deletions
diff --git a/packages/opencode/src/cli/cmd/account.ts b/packages/opencode/src/cli/cmd/account.ts
index fe8747bce..c09294edd 100644
--- a/packages/opencode/src/cli/cmd/account.ts
+++ b/packages/opencode/src/cli/cmd/account.ts
@@ -10,6 +10,26 @@ const openBrowser = (url: string) => Effect.promise(() => open(url).catch(() =>
const println = (msg: string) => Effect.sync(() => UI.println(msg))
+const dim = (value: string) => UI.Style.TEXT_DIM + value + UI.Style.TEXT_NORMAL
+
+const activeSuffix = (isActive: boolean) => (isActive ? dim(" (active)") : "")
+
+export const formatAccountLabel = (account: { email: string; url: string }, isActive: boolean) =>
+ `${account.email} ${dim(account.url)}${activeSuffix(isActive)}`
+
+const formatOrgChoiceLabel = (account: { email: string }, org: { name: string }, isActive: boolean) =>
+ `${org.name} (${account.email})${activeSuffix(isActive)}`
+
+export const formatOrgLine = (
+ account: { email: string; url: string },
+ org: { id: string; name: string },
+ isActive: boolean,
+) => {
+ const dot = isActive ? UI.Style.TEXT_SUCCESS + "●" + UI.Style.TEXT_NORMAL : " "
+ const name = isActive ? UI.Style.TEXT_HIGHLIGHT_BOLD + org.name + UI.Style.TEXT_NORMAL : org.name
+ return ` ${dot} ${name} ${dim(account.email)} ${dim(account.url)} ${dim(org.id)}`
+}
+
const isActiveOrgChoice = (
active: Option.Option<{ id: AccountID; active_org_id: OrgID | null }>,
choice: { accountID: AccountID; orgID: OrgID },
@@ -76,10 +96,9 @@ const logoutEffect = Effect.fn("logout")(function* (email?: string) {
const opts = accounts.map((a) => {
const isActive = Option.isSome(activeID) && activeID.value === a.id
- const server = UI.Style.TEXT_DIM + a.url + UI.Style.TEXT_NORMAL
return {
value: a,
- label: isActive ? `${a.email} ${server}` + UI.Style.TEXT_DIM + " (active)" : `${a.email} ${server}`,
+ label: formatAccountLabel(a, isActive),
}
})
@@ -109,9 +128,7 @@ const switchEffect = Effect.fn("switch")(function* () {
const isActive = isActiveOrgChoice(active, { accountID: group.account.id, orgID: org.id })
return {
value: { orgID: org.id, accountID: group.account.id, label: org.name },
- label: isActive
- ? `${org.name} (${group.account.email})` + UI.Style.TEXT_DIM + " (active)"
- : `${org.name} (${group.account.email})`,
+ label: formatOrgChoiceLabel(group.account, org, isActive),
}
}),
)
@@ -139,15 +156,21 @@ const orgsEffect = Effect.fn("orgs")(function* () {
for (const group of groups) {
for (const org of group.orgs) {
const isActive = isActiveOrgChoice(active, { accountID: group.account.id, orgID: org.id })
- const dot = isActive ? UI.Style.TEXT_SUCCESS + "●" + UI.Style.TEXT_NORMAL : " "
- const name = isActive ? UI.Style.TEXT_HIGHLIGHT_BOLD + org.name + UI.Style.TEXT_NORMAL : org.name
- const email = UI.Style.TEXT_DIM + group.account.email + UI.Style.TEXT_NORMAL
- const id = UI.Style.TEXT_DIM + org.id + UI.Style.TEXT_NORMAL
- yield* println(` ${dot} ${name} ${email} ${id}`)
+ yield* println(formatOrgLine(group.account, org, isActive))
}
}
})
+const openEffect = Effect.fn("open")(function* () {
+ const service = yield* Account.Service
+ const active = yield* service.active()
+ if (Option.isNone(active)) return yield* println("No active account")
+
+ const url = active.value.url
+ yield* openBrowser(url)
+ yield* Prompt.outro("Opened " + url)
+})
+
export const LoginCommand = cmd({
command: "login <url>",
describe: false,
@@ -195,6 +218,15 @@ export const OrgsCommand = cmd({
},
})
+export const OpenCommand = cmd({
+ command: "open",
+ describe: false,
+ async handler() {
+ UI.empty()
+ await Account.runPromise((_svc) => openEffect())
+ },
+})
+
export const ConsoleCommand = cmd({
command: "console",
describe: false,
@@ -216,6 +248,10 @@ export const ConsoleCommand = cmd({
...OrgsCommand,
describe: "list orgs",
})
+ .command({
+ ...OpenCommand,
+ describe: "open active console account",
+ })
.demandCommand(),
async handler() {},
})
diff --git a/packages/opencode/test/cli/account.test.ts b/packages/opencode/test/cli/account.test.ts
new file mode 100644
index 000000000..3f8196887
--- /dev/null
+++ b/packages/opencode/test/cli/account.test.ts
@@ -0,0 +1,24 @@
+import { describe, expect, test } from "bun:test"
+import stripAnsi from "strip-ansi"
+
+import { formatAccountLabel, formatOrgLine } from "../../src/cli/cmd/account"
+
+describe("console account display", () => {
+ test("includes the account url in account labels", () => {
+ expect(stripAnsi(formatAccountLabel({ email: "[email protected]", url: "https://one.example.com" }, false))).toBe(
+ "[email protected] https://one.example.com",
+ )
+ })
+
+ test("includes the active marker in account labels", () => {
+ expect(stripAnsi(formatAccountLabel({ email: "[email protected]", url: "https://one.example.com" }, true))).toBe(
+ "[email protected] https://one.example.com (active)",
+ )
+ })
+
+ test("includes the account url in org rows", () => {
+ expect(
+ stripAnsi(formatOrgLine({ email: "[email protected]", url: "https://one.example.com" }, { id: "org-1", name: "One" }, true)),
+ ).toBe(" ● One [email protected] https://one.example.com org-1")
+ })
+})