@doguyilmaz/konvoy 0.3.1 → 0.3.2
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 +1 -1
- package/src/cli.ts +18 -21
- package/src/commands/repl.ts +58 -4
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { cmdRename } from './commands/rename'
|
|
|
22
22
|
import { cmdUsage } from './commands/usage'
|
|
23
23
|
import { cmdDashboard } from './commands/dashboard'
|
|
24
24
|
import { formatCommandList, resolveCommandName, type CommandName } from './commands/table'
|
|
25
|
-
import { runRepl, startSession, type ReplIo } from './commands/repl'
|
|
25
|
+
import { runRepl, startSession, terminalIo, type ReplIo } from './commands/repl'
|
|
26
26
|
import type { AgentId } from './types'
|
|
27
27
|
import pkg from '../package.json'
|
|
28
28
|
|
|
@@ -115,15 +115,7 @@ const handlers: Record<CommandName, Handler> = {
|
|
|
115
115
|
}),
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
lines: console as unknown as AsyncIterable<string>,
|
|
120
|
-
write: (text) => {
|
|
121
|
-
process.stdout.write(text)
|
|
122
|
-
},
|
|
123
|
-
tty: Boolean(process.stdin.isTTY),
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
export async function main(argv: string[], io: ReplIo = stdio()): Promise<number> {
|
|
118
|
+
export async function main(argv: string[], io?: ReplIo): Promise<number> {
|
|
127
119
|
const args = parseArgs(argv)
|
|
128
120
|
const [command, ...rest] = args._
|
|
129
121
|
const cwd = process.cwd()
|
|
@@ -172,7 +164,7 @@ async function dispatch(command: string, rest: string[], cwd: string, slug: stri
|
|
|
172
164
|
return handlers[name]({ db, cfg, cwd, args, slug }, rest)
|
|
173
165
|
}
|
|
174
166
|
|
|
175
|
-
async function interactive(
|
|
167
|
+
async function interactive(given: ReplIo | undefined, cwd: string, slug: string | undefined): Promise<number> {
|
|
176
168
|
const db = openDb(dbPath())
|
|
177
169
|
const cfg = await loadConfig({ cwd: (slug ? getSessionBySlug(db, slug)?.cwd : undefined) ?? cwd })
|
|
178
170
|
const session = await startSession(db, cfg, cwd, slug)
|
|
@@ -180,16 +172,21 @@ async function interactive(io: ReplIo, cwd: string, slug: string | undefined): P
|
|
|
180
172
|
console.error(slug ? `no konvoy session named "${slug}"` : 'could not create a session here')
|
|
181
173
|
return 2
|
|
182
174
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
175
|
+
const io = given ?? terminalIo()
|
|
176
|
+
try {
|
|
177
|
+
return await runRepl(io, db, cfg, cwd, session, (tokens, current) => {
|
|
178
|
+
const a = parseArgs(tokens)
|
|
179
|
+
const [cmd = '', ...r] = a._
|
|
180
|
+
const name = resolveCommandName(cmd)
|
|
181
|
+
if (!name) {
|
|
182
|
+
console.error(`unknown command "/${cmd}" - /help lists them`)
|
|
183
|
+
return 2
|
|
184
|
+
}
|
|
185
|
+
return handlers[name]({ db, cfg, cwd, args: a, slug: current }, r)
|
|
186
|
+
})
|
|
187
|
+
} finally {
|
|
188
|
+
io.pause()
|
|
189
|
+
}
|
|
193
190
|
}
|
|
194
191
|
|
|
195
192
|
if (import.meta.main) {
|
package/src/commands/repl.ts
CHANGED
|
@@ -12,6 +12,51 @@ export interface ReplIo {
|
|
|
12
12
|
lines: AsyncIterable<string>
|
|
13
13
|
write: (text: string) => void
|
|
14
14
|
tty: boolean
|
|
15
|
+
/** stop reading stdin while a command runs, so a child that inherits the terminal gets every keystroke */
|
|
16
|
+
pause: () => void
|
|
17
|
+
resume: () => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function terminalIo(): ReplIo {
|
|
21
|
+
const queue: string[] = []
|
|
22
|
+
const decoder = new TextDecoder()
|
|
23
|
+
let buffered = ''
|
|
24
|
+
let ended = false
|
|
25
|
+
let wake: (() => void) | undefined
|
|
26
|
+
const flush = (): void => {
|
|
27
|
+
wake?.()
|
|
28
|
+
wake = undefined
|
|
29
|
+
}
|
|
30
|
+
process.stdin.on('data', (chunk: Uint8Array) => {
|
|
31
|
+
buffered += decoder.decode(chunk, { stream: true })
|
|
32
|
+
let at: number
|
|
33
|
+
while ((at = buffered.indexOf('\n')) >= 0) {
|
|
34
|
+
queue.push(buffered.slice(0, at))
|
|
35
|
+
buffered = buffered.slice(at + 1)
|
|
36
|
+
}
|
|
37
|
+
flush()
|
|
38
|
+
})
|
|
39
|
+
process.stdin.on('end', () => {
|
|
40
|
+
if (buffered) queue.push(buffered)
|
|
41
|
+
ended = true
|
|
42
|
+
flush()
|
|
43
|
+
})
|
|
44
|
+
async function* lines(): AsyncGenerator<string> {
|
|
45
|
+
for (;;) {
|
|
46
|
+
if (queue.length > 0) yield queue.shift()!
|
|
47
|
+
else if (ended) return
|
|
48
|
+
else await new Promise<void>((resolve) => (wake = resolve))
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
lines: lines(),
|
|
53
|
+
write: (text) => {
|
|
54
|
+
process.stdout.write(text)
|
|
55
|
+
},
|
|
56
|
+
tty: Boolean(process.stdin.isTTY),
|
|
57
|
+
pause: () => process.stdin.pause(),
|
|
58
|
+
resume: () => process.stdin.resume(),
|
|
59
|
+
}
|
|
15
60
|
}
|
|
16
61
|
|
|
17
62
|
/** runs one command line through the command table, as `konvoy <tokens>` would, for the given session */
|
|
@@ -52,6 +97,15 @@ export async function runRepl(
|
|
|
52
97
|
return fresh !== undefined
|
|
53
98
|
}
|
|
54
99
|
|
|
100
|
+
const exec = async (tokens: string[]): Promise<void> => {
|
|
101
|
+
io.pause()
|
|
102
|
+
try {
|
|
103
|
+
await run(tokens, session.slug)
|
|
104
|
+
} finally {
|
|
105
|
+
io.resume()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
55
109
|
prompt()
|
|
56
110
|
for await (const raw of io.lines) {
|
|
57
111
|
const line = raw.trim()
|
|
@@ -60,7 +114,7 @@ export async function runRepl(
|
|
|
60
114
|
continue
|
|
61
115
|
}
|
|
62
116
|
if (!line.startsWith('/')) {
|
|
63
|
-
await
|
|
117
|
+
await exec(['send', agent, line])
|
|
64
118
|
// failover never falls back, so the agent that answered is the one to keep talking to
|
|
65
119
|
const moved = lastTurnAgent(db, session.id)
|
|
66
120
|
if (moved && moved !== agent) agent = moved
|
|
@@ -94,13 +148,13 @@ export async function runRepl(
|
|
|
94
148
|
} else if (cmd === 'rename') {
|
|
95
149
|
if (rest.length === 0) console.error('usage: /rename <new-name>')
|
|
96
150
|
else {
|
|
97
|
-
await
|
|
151
|
+
await exec(['rename', session.slug, rest.join(' ')])
|
|
98
152
|
refresh()
|
|
99
153
|
}
|
|
100
154
|
} else if (cmd === 'attach') {
|
|
101
|
-
await
|
|
155
|
+
await exec(['attach', rest[0] ?? agent, ...rest.slice(1)])
|
|
102
156
|
} else {
|
|
103
|
-
await
|
|
157
|
+
await exec([cmd, ...rest])
|
|
104
158
|
if (cmd === 'resume' && rest[0]) {
|
|
105
159
|
const target = getSessionBySlug(db, rest[0])
|
|
106
160
|
if (target) {
|