codeblog-app 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "codeblog-app",
4
- "version": "2.4.0",
4
+ "version": "2.5.0",
5
5
  "description": "CLI client for CodeBlog — the forum where AI writes the posts",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -58,11 +58,11 @@
58
58
  "typescript": "5.8.2"
59
59
  },
60
60
  "optionalDependencies": {
61
- "codeblog-app-darwin-arm64": "2.4.0",
62
- "codeblog-app-darwin-x64": "2.4.0",
63
- "codeblog-app-linux-arm64": "2.4.0",
64
- "codeblog-app-linux-x64": "2.4.0",
65
- "codeblog-app-windows-x64": "2.4.0"
61
+ "codeblog-app-darwin-arm64": "2.5.0",
62
+ "codeblog-app-darwin-x64": "2.5.0",
63
+ "codeblog-app-linux-arm64": "2.5.0",
64
+ "codeblog-app-linux-x64": "2.5.0",
65
+ "codeblog-app-windows-x64": "2.5.0"
66
66
  },
67
67
  "dependencies": {
68
68
  "@ai-sdk/anthropic": "^3.0.44",
@@ -73,7 +73,7 @@
73
73
  "@opentui/core": "^0.1.79",
74
74
  "@opentui/solid": "^0.1.79",
75
75
  "ai": "^6.0.86",
76
- "codeblog-mcp": "2.4.0",
76
+ "codeblog-mcp": "2.5.0",
77
77
  "drizzle-orm": "1.0.0-beta.12-a5629fb",
78
78
  "fuzzysort": "^3.1.0",
79
79
  "hono": "4.10.7",
package/src/auth/oauth.ts CHANGED
@@ -8,6 +8,8 @@ const log = Log.create({ service: "oauth" })
8
8
 
9
9
  /** Set after a successful login — indicates whether the user already has agents. */
10
10
  export let lastAuthHasAgents: boolean | undefined = undefined
11
+ /** Set after a successful login — number of agents the user has. */
12
+ export let lastAuthAgentsCount: number | undefined = undefined
11
13
 
12
14
  export namespace OAuth {
13
15
  export async function login(options?: { onUrl?: (url: string) => void }) {
@@ -20,6 +22,8 @@ export namespace OAuth {
20
22
  const username = params.get("username") || undefined
21
23
  const hasAgentsParam = params.get("has_agents")
22
24
  lastAuthHasAgents = hasAgentsParam === "true" ? true : hasAgentsParam === "false" ? false : undefined
25
+ const agentsCountParam = params.get("agents_count")
26
+ lastAuthAgentsCount = agentsCountParam ? parseInt(agentsCountParam, 10) : undefined
23
27
 
24
28
  if (key) {
25
29
  let ownerMismatch = ""
@@ -1,6 +1,6 @@
1
1
  import type { CommandModule } from "yargs"
2
2
  import { Auth } from "../../auth"
3
- import { OAuth, lastAuthHasAgents } from "../../auth/oauth"
3
+ import { OAuth, lastAuthHasAgents, lastAuthAgentsCount } from "../../auth/oauth"
4
4
  import { McpBridge } from "../../mcp/client"
5
5
  import { UI } from "../ui"
6
6
  import { Config } from "../../config"
@@ -670,6 +670,61 @@ async function createAgentViaAPI(opts: {
670
670
  }
671
671
  }
672
672
 
673
+ async function agentSelectionPrompt(): Promise<void> {
674
+ await UI.typeText("You have multiple agents. Let's make sure the right one is active.", { charDelay: 10 })
675
+ console.log("")
676
+
677
+ const auth = await Auth.get()
678
+ if (!auth?.value) return
679
+
680
+ const base = await Config.url()
681
+ let agents: Array<{ id: string; name: string; source_type: string; posts_count: number }> = []
682
+
683
+ try {
684
+ const res = await fetch(`${base}/api/v1/agents/list`, {
685
+ headers: { Authorization: `Bearer ${auth.value}` },
686
+ })
687
+ if (res.ok) {
688
+ const data = await res.json() as { agents?: Array<{ id: string; name: string; source_type: string; posts_count: number; activated: boolean }> }
689
+ agents = (data.agents || []).filter((a) => a.activated)
690
+ }
691
+ } catch {}
692
+
693
+ if (agents.length <= 1) return
694
+
695
+ const options = agents.map((a) => `${a.name} (${a.source_type}, ${a.posts_count} posts)`)
696
+ const idx = await UI.select(" Which agent should be active?", options)
697
+
698
+ if (idx >= 0 && idx < agents.length) {
699
+ const chosen = agents[idx]!
700
+
701
+ // Switch to the chosen agent via the switch endpoint (returns api_key)
702
+ try {
703
+ const switchRes = await fetch(`${base}/api/v1/agents/switch`, {
704
+ method: "POST",
705
+ headers: { Authorization: `Bearer ${auth.value}`, "Content-Type": "application/json" },
706
+ body: JSON.stringify({ agent_id: chosen.id }),
707
+ })
708
+ if (switchRes.ok) {
709
+ const switchData = await switchRes.json() as { agent: { api_key: string; name: string } }
710
+ await Auth.set({ type: "apikey", value: switchData.agent.api_key, username: auth.username })
711
+ await Config.saveActiveAgent(switchData.agent.name, auth.username)
712
+
713
+ // Sync to MCP config
714
+ try {
715
+ await McpBridge.callTool("codeblog_setup", { api_key: switchData.agent.api_key })
716
+ } catch {}
717
+
718
+ UI.success(`Active agent: ${switchData.agent.name}`)
719
+ } else {
720
+ UI.error("Failed to switch agent. You can switch later with: codeblog agent switch")
721
+ }
722
+ } catch {
723
+ UI.error("Failed to switch agent. You can switch later with: codeblog agent switch")
724
+ }
725
+ }
726
+ }
727
+
673
728
  async function agentCreationWizard(): Promise<void> {
674
729
  await UI.typeText("Now let's create your AI Agent!", { charDelay: 10 })
675
730
  await UI.typeText("Your agent is your coding persona on CodeBlog — it represents you and your coding style.", { charDelay: 10 })
@@ -814,11 +869,15 @@ export const SetupCommand: CommandModule = {
814
869
  console.log("")
815
870
  await runAISetupWizard("setup")
816
871
 
817
- // Phase 3.5: Agent creation (if the user has no agents yet)
872
+ // Phase 3.5: Agent creation or selection
818
873
  const needsAgent = lastAuthHasAgents === false || (lastAuthHasAgents === undefined && !(await Auth.get())?.type?.startsWith("apikey"))
819
874
  if (needsAgent) {
820
875
  UI.divider()
821
876
  await agentCreationWizard()
877
+ } else if (lastAuthAgentsCount !== undefined && lastAuthAgentsCount > 1) {
878
+ // User has multiple agents — offer selection
879
+ UI.divider()
880
+ await agentSelectionPrompt()
822
881
  }
823
882
 
824
883
  // Phase 4: Interactive scan & publish
package/src/tui/app.tsx CHANGED
@@ -71,6 +71,7 @@ function App() {
71
71
  const [loggedIn, setLoggedIn] = createSignal(false)
72
72
  const [username, setUsername] = createSignal("")
73
73
  const [activeAgent, setActiveAgent] = createSignal("")
74
+ const [agentCount, setAgentCount] = createSignal(0)
74
75
  const [hasAI, setHasAI] = createSignal(false)
75
76
  const [aiProvider, setAiProvider] = createSignal("")
76
77
  const [modelName, setModelName] = createSignal("")
@@ -118,6 +119,21 @@ function App() {
118
119
  }
119
120
  setActiveAgent(name)
120
121
  await Config.saveActiveAgent(name, username || undefined)
122
+ // Fetch agent count for multi-agent display
123
+ try {
124
+ const listRes = await fetch(`${base}/api/v1/agents/list`, {
125
+ headers: { Authorization: `Bearer ${token.value}` },
126
+ })
127
+ if (listRes.ok) {
128
+ const listData = await listRes.json() as { agents?: Array<{ activated: boolean }> }
129
+ const activated = listData.agents?.filter((a) => a.activated)?.length || 0
130
+ setAgentCount(activated)
131
+ } else {
132
+ setAgentCount(0)
133
+ }
134
+ } catch {
135
+ setAgentCount(0)
136
+ }
121
137
  } catch {
122
138
  if (!cached) setActiveAgent("")
123
139
  }
@@ -171,6 +187,7 @@ function App() {
171
187
  loggedIn={loggedIn()}
172
188
  username={username()}
173
189
  activeAgent={activeAgent()}
190
+ agentCount={agentCount()}
174
191
  hasAI={hasAI()}
175
192
  aiProvider={aiProvider()}
176
193
  modelName={modelName()}
@@ -57,6 +57,7 @@ export function Home(props: {
57
57
  loggedIn: boolean
58
58
  username: string
59
59
  activeAgent: string
60
+ agentCount: number
60
61
  hasAI: boolean
61
62
  aiProvider: string
62
63
  modelName: string
@@ -823,7 +824,7 @@ export function Home(props: {
823
824
  {props.loggedIn ? props.username : "Not logged in"}
824
825
  </text>
825
826
  <Show when={props.loggedIn && props.activeAgent}>
826
- <text fg={theme.colors.textMuted}> / {props.activeAgent}</text>
827
+ <text fg={theme.colors.textMuted}> / {props.activeAgent}{props.agentCount > 1 ? ` (${props.agentCount} agents)` : ""}</text>
827
828
  </Show>
828
829
  <Show when={!props.loggedIn}>
829
830
  <text fg={theme.colors.textMuted}> — type /login</text>