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.
- package/README.md +18 -2
- package/dist/client/assets/{index-CVu2H5bX.js → index-C6R1P_HL.js} +114 -114
- package/dist/client/assets/{index-D5tYLJi-.css → index-CA-kJR8F.css} +1 -1
- package/dist/client/index.html +2 -2
- package/package.json +3 -2
- package/src/server/agent.ts +4 -34
- package/src/server/cli-runtime.test.ts +55 -0
- package/src/server/cli-runtime.ts +25 -8
- package/src/server/generate-title.ts +0 -13
- package/src/server/quick-response.ts +0 -9
- package/src/server/server.ts +3 -0
- package/src/shared/dev-ports.test.ts +52 -0
- package/src/shared/dev-ports.ts +43 -0
- package/src/shared/ports.ts +0 -1
- package/src/shared/types.ts +1 -1
|
@@ -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>
|
|
73
|
-
--
|
|
74
|
-
--
|
|
75
|
-
--
|
|
76
|
-
--
|
|
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
|
|
214
|
-
const
|
|
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
|
|
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
|
}
|
package/src/server/server.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/shared/ports.ts
CHANGED