kanna-code 0.14.0 → 0.15.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.
@@ -8,6 +8,7 @@ import { CLI_SUPPRESS_OPEN_ONCE_ENV_VAR } from "./restart"
8
8
 
9
9
  export interface CliOptions {
10
10
  port: number
11
+ host: string
11
12
  openBrowser: boolean
12
13
  strictPort: boolean
13
14
  }
@@ -69,15 +70,18 @@ Usage:
69
70
  ${CLI_COMMAND} [options]
70
71
 
71
72
  Options:
72
- --port <number> Port to listen on (default: ${PROD_SERVER_PORT})
73
- --strict-port Fail instead of trying another port
74
- --no-open Don't open browser automatically
75
- --version Print version and exit
76
- --help Show this help message`)
73
+ --port <number> Port to listen on (default: ${PROD_SERVER_PORT})
74
+ --host <host> Bind to a specific host or IP
75
+ --remote Shortcut for --host 0.0.0.0
76
+ --strict-port Fail instead of trying another port
77
+ --no-open Don't open browser automatically
78
+ --version Print version and exit
79
+ --help Show this help message`)
77
80
  }
78
81
 
79
82
  export function parseArgs(argv: string[]): ParsedArgs {
80
83
  let port = PROD_SERVER_PORT
84
+ let host = "127.0.0.1"
81
85
  let openBrowser = true
82
86
  let strictPort = false
83
87
 
@@ -96,6 +100,17 @@ export function parseArgs(argv: string[]): ParsedArgs {
96
100
  index += 1
97
101
  continue
98
102
  }
103
+ if (arg === "--host") {
104
+ const next = argv[index + 1]
105
+ if (!next || next.startsWith("-")) throw new Error("Missing value for --host")
106
+ host = next
107
+ index += 1
108
+ continue
109
+ }
110
+ if (arg === "--remote") {
111
+ host = "0.0.0.0"
112
+ continue
113
+ }
99
114
  if (arg === "--no-open") {
100
115
  openBrowser = false
101
116
  continue
@@ -111,6 +126,7 @@ export function parseArgs(argv: string[]): ParsedArgs {
111
126
  kind: "run",
112
127
  options: {
113
128
  port,
129
+ host,
114
130
  openBrowser,
115
131
  strictPort,
116
132
  },
@@ -210,10 +226,11 @@ export async function runCli(argv: string[], deps: CliRuntimeDeps): Promise<CliR
210
226
  command: CLI_COMMAND,
211
227
  },
212
228
  })
213
- const url = `http://localhost:${port}`
214
- const launchUrl = url
229
+ const bindHost = parsedArgs.options.host
230
+ const displayHost = bindHost === "127.0.0.1" || bindHost === "0.0.0.0" ? "localhost" : bindHost
231
+ const launchUrl = `http://${displayHost}:${port}`
215
232
 
216
- deps.log(`${LOG_PREFIX} listening on ${url}`)
233
+ deps.log(`${LOG_PREFIX} listening on http://${bindHost}:${port}`)
217
234
  deps.log(`${LOG_PREFIX} data dir: ${getDataDirDisplay()}`)
218
235
 
219
236
  const suppressOpenBrowser = process.env[CLI_SUPPRESS_OPEN_ONCE_ENV_VAR] === "1"
@@ -1,7 +1,5 @@
1
1
  import { QuickResponseAdapter } from "./quick-response"
2
2
 
3
- const LOG_PREFIX = "[kanna:title]"
4
-
5
3
  const TITLE_SCHEMA = {
6
4
  type: "object",
7
5
  properties: {
@@ -23,11 +21,6 @@ export async function generateTitleForChat(
23
21
  cwd: string,
24
22
  adapter = new QuickResponseAdapter()
25
23
  ): Promise<string | null> {
26
- console.log(`${LOG_PREFIX} generating title`, {
27
- cwd,
28
- messagePreview: messageContent.replace(/\s+/g, " ").trim().slice(0, 120),
29
- })
30
-
31
24
  const result = await adapter.generateStructured<string>({
32
25
  cwd,
33
26
  task: "conversation title generation",
@@ -39,11 +32,5 @@ export async function generateTitleForChat(
39
32
  },
40
33
  })
41
34
 
42
- if (result) {
43
- console.log(`${LOG_PREFIX} generated title`, { title: result })
44
- } else {
45
- console.warn(`${LOG_PREFIX} no title generated`)
46
- }
47
-
48
35
  return result
49
36
  }
@@ -1,7 +1,6 @@
1
1
  import { query } from "@anthropic-ai/claude-agent-sdk"
2
2
  import { CodexAppServerManager } from "./codex-app-server"
3
3
 
4
- const LOG_PREFIX = "[kanna:title]"
5
4
  const CLAUDE_STRUCTURED_TIMEOUT_MS = 5_000
6
5
 
7
6
  type JsonSchema = {
@@ -82,7 +81,6 @@ async function runClaudeStructured(args: Omit<StructuredQuickResponseArgs<unknow
82
81
 
83
82
  return result
84
83
  } catch (error) {
85
- console.warn(`${LOG_PREFIX} claude structured query failed before fallback:`, error)
86
84
  return null
87
85
  } finally {
88
86
  try {
@@ -116,7 +114,6 @@ export class QuickResponseAdapter {
116
114
  this.runCodexStructured = args.runCodexStructured ?? ((structuredArgs) =>
117
115
  runCodexStructured(this.codexManager, structuredArgs))
118
116
  }
119
-
120
117
  async generateStructured<T>(args: StructuredQuickResponseArgs<T>): Promise<T | null> {
121
118
  const request = {
122
119
  cwd: args.cwd,
@@ -125,11 +122,9 @@ export class QuickResponseAdapter {
125
122
  schema: args.schema,
126
123
  }
127
124
 
128
- console.log(`${LOG_PREFIX} starting ${args.task} via claude`, { cwd: args.cwd })
129
125
  const claudeResult = await this.tryProvider("claude", args.task, args.parse, () => this.runClaudeStructured(request))
130
126
  if (claudeResult !== null) return claudeResult
131
127
 
132
- console.warn(`${LOG_PREFIX} claude returned no usable result for ${args.task}, falling back to codex`)
133
128
  return await this.tryProvider("codex", args.task, args.parse, () => this.runCodexStructured(request))
134
129
  }
135
130
 
@@ -142,20 +137,16 @@ export class QuickResponseAdapter {
142
137
  try {
143
138
  const result = await run()
144
139
  if (result === null) {
145
- console.warn(`${LOG_PREFIX} ${provider} returned no structured output for ${task}`)
146
140
  return null
147
141
  }
148
142
 
149
143
  const parsed = parse(result)
150
144
  if (parsed === null) {
151
- console.warn(`${LOG_PREFIX} ${provider} returned unparseable structured output for ${task}`, { result })
152
145
  return null
153
146
  }
154
147
 
155
- console.log(`${LOG_PREFIX} ${provider} produced structured output for ${task}`)
156
148
  return parsed
157
149
  } catch (error) {
158
- console.warn(`${LOG_PREFIX} ${provider} failed during ${task}:`, error)
159
150
  return null
160
151
  }
161
152
  }
@@ -12,6 +12,7 @@ import { createWsRouter, type ClientState } from "./ws-router"
12
12
 
13
13
  export interface StartKannaServerOptions {
14
14
  port?: number
15
+ host?: string
15
16
  strictPort?: boolean
16
17
  update?: {
17
18
  version: string
@@ -22,6 +23,7 @@ export interface StartKannaServerOptions {
22
23
 
23
24
  export async function startKannaServer(options: StartKannaServerOptions = {}) {
24
25
  const port = options.port ?? 3210
26
+ const hostname = options.host ?? "127.0.0.1"
25
27
  const strictPort = options.strictPort ?? false
26
28
  const store = new EventStore()
27
29
  const machineDisplayName = getMachineDisplayName()
@@ -74,6 +76,7 @@ export async function startKannaServer(options: StartKannaServerOptions = {}) {
74
76
  try {
75
77
  server = Bun.serve<ClientState>({
76
78
  port: actualPort,
79
+ hostname,
77
80
  fetch(req, serverInstance) {
78
81
  const url = new URL(req.url)
79
82
 
@@ -0,0 +1,52 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import {
3
+ DEFAULT_DEV_CLIENT_PORT,
4
+ getDefaultDevServerPort,
5
+ resolveDevPorts,
6
+ stripPortArg,
7
+ } from "./dev-ports"
8
+
9
+ describe("getDefaultDevServerPort", () => {
10
+ test("derives the default backend port from the client port", () => {
11
+ expect(getDefaultDevServerPort()).toBe(DEFAULT_DEV_CLIENT_PORT + 1)
12
+ expect(getDefaultDevServerPort(4000)).toBe(4001)
13
+ })
14
+ })
15
+
16
+ describe("resolveDevPorts", () => {
17
+ test("uses default dev ports when no port override is provided", () => {
18
+ expect(resolveDevPorts([])).toEqual({
19
+ clientPort: DEFAULT_DEV_CLIENT_PORT,
20
+ serverPort: DEFAULT_DEV_CLIENT_PORT + 1,
21
+ })
22
+ })
23
+
24
+ test("treats --port as the client port and derives the backend port", () => {
25
+ expect(resolveDevPorts(["--remote", "--port", "4000"])).toEqual({
26
+ clientPort: 4000,
27
+ serverPort: 4001,
28
+ })
29
+ })
30
+
31
+ test("uses the last provided --port value", () => {
32
+ expect(resolveDevPorts(["--port", "4000", "--port", "4100"])).toEqual({
33
+ clientPort: 4100,
34
+ serverPort: 4101,
35
+ })
36
+ })
37
+
38
+ test("throws when --port is missing a value", () => {
39
+ expect(() => resolveDevPorts(["--port"])).toThrow("Missing value for --port")
40
+ expect(() => resolveDevPorts(["--port", "--remote"])).toThrow("Missing value for --port")
41
+ })
42
+ })
43
+
44
+ describe("stripPortArg", () => {
45
+ test("removes --port and its value while preserving other args", () => {
46
+ expect(stripPortArg(["--remote", "--port", "4000", "--host", "dev-box"])).toEqual([
47
+ "--remote",
48
+ "--host",
49
+ "dev-box",
50
+ ])
51
+ })
52
+ })
@@ -0,0 +1,43 @@
1
+ export const DEFAULT_DEV_CLIENT_PORT = 5174
2
+
3
+ export function getDefaultDevServerPort(clientPort = DEFAULT_DEV_CLIENT_PORT) {
4
+ return clientPort + 1
5
+ }
6
+
7
+ export function resolveDevPorts(args: string[]) {
8
+ let clientPort = DEFAULT_DEV_CLIENT_PORT
9
+
10
+ for (let index = 0; index < args.length; index += 1) {
11
+ const arg = args[index]
12
+ if (arg !== "--port") continue
13
+
14
+ const next = args[index + 1]
15
+ if (!next || next.startsWith("-")) {
16
+ throw new Error("Missing value for --port")
17
+ }
18
+
19
+ clientPort = Number(next)
20
+ index += 1
21
+ }
22
+
23
+ return {
24
+ clientPort,
25
+ serverPort: getDefaultDevServerPort(clientPort),
26
+ }
27
+ }
28
+
29
+ export function stripPortArg(args: string[]) {
30
+ const stripped: string[] = []
31
+
32
+ for (let index = 0; index < args.length; index += 1) {
33
+ const arg = args[index]
34
+ if (arg === "--port") {
35
+ index += 1
36
+ continue
37
+ }
38
+
39
+ stripped.push(arg)
40
+ }
41
+
42
+ return stripped
43
+ }
@@ -1,3 +1,2 @@
1
1
  export const PROD_SERVER_PORT = 3210
2
- export const DEV_SERVER_PORT = 3211
3
2
  export const DEV_CLIENT_PORT = 5174
@@ -82,7 +82,7 @@ export const PROVIDERS: ProviderCatalogEntry[] = [
82
82
  {
83
83
  id: "claude",
84
84
  label: "Claude",
85
- defaultModel: "opus",
85
+ defaultModel: "sonnet",
86
86
  defaultEffort: "high",
87
87
  supportsPlanMode: true,
88
88
  models: [