@miphamai/cli 0.31.1 → 0.32.1
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/bin/daemon.ts +40 -0
- package/bin/mipham.ts +796 -1
- package/package.json +1 -1
- package/src/agent/background-registry.ts +80 -1
- package/src/daemon/agent-manager.ts +110 -0
- package/src/daemon/attach-protocol.ts +74 -0
- package/src/daemon/auth.ts +104 -0
- package/src/daemon/cors.ts +68 -0
- package/src/daemon/database.ts +449 -0
- package/src/daemon/goal-manager.ts +53 -0
- package/src/daemon/index.ts +307 -0
- package/src/daemon/message-bus.ts +84 -0
- package/src/daemon/rate-limiter.ts +70 -0
- package/src/daemon/remote-engine.ts +434 -0
- package/src/daemon/schedule-manager.ts +159 -0
- package/src/daemon/server.ts +632 -0
- package/src/daemon/session-manager.ts +45 -0
- package/src/daemon/session-worker.ts +418 -0
- package/src/daemon/types.ts +98 -0
- package/src/daemon/worker-pool.ts +174 -0
- package/src/index.tsx +44 -4
- package/src/ui/app.tsx +6 -2
- package/src/ui/chat.tsx +39 -33
package/src/index.tsx
CHANGED
|
@@ -47,6 +47,13 @@ import enUS from './i18n-core/locales/en-US.json' with { type: 'json' }
|
|
|
47
47
|
import zhCN from './i18n-core/locales/zh-CN.json' with { type: 'json' }
|
|
48
48
|
import type { TranslationMap } from './i18n-core/types'
|
|
49
49
|
|
|
50
|
+
// Locale bundles — defined at module level so the remote-attach branch can
|
|
51
|
+
// reference them before the full bootstrap path runs.
|
|
52
|
+
const localeBundles: Record<string, TranslationMap> = {
|
|
53
|
+
'en-US': enUS as TranslationMap,
|
|
54
|
+
'zh-CN': zhCN as TranslationMap,
|
|
55
|
+
}
|
|
56
|
+
|
|
50
57
|
interface RunOptions {
|
|
51
58
|
model?: string
|
|
52
59
|
provider?: string
|
|
@@ -54,6 +61,8 @@ interface RunOptions {
|
|
|
54
61
|
permission?: string
|
|
55
62
|
resume?: string
|
|
56
63
|
version?: string
|
|
64
|
+
/** When set, launch TUI in remote mode connected to a daemon session. */
|
|
65
|
+
remoteSession?: { sessionId: string; port: number; token: string }
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
/**
|
|
@@ -183,6 +192,41 @@ export async function runApp(options: RunOptions): Promise<void> {
|
|
|
183
192
|
// ── Workspace Trust Check ──
|
|
184
193
|
await checkWorkspaceTrust()
|
|
185
194
|
|
|
195
|
+
// ── Remote attach mode ──────────────────────────────────────────────────
|
|
196
|
+
// When the TUI is launched via `mipham attach`, skip the entire local
|
|
197
|
+
// engine bootstrap and connect to the daemon via WebSocket instead.
|
|
198
|
+
if (options.remoteSession) {
|
|
199
|
+
const { RemoteEngine } = await import('./daemon/remote-engine')
|
|
200
|
+
const engine = new RemoteEngine(options.remoteSession)
|
|
201
|
+
|
|
202
|
+
// Detect locale and create translation function (same as local path)
|
|
203
|
+
const locale = detectLocale({ lang: options.lang })
|
|
204
|
+
const t = createT(localeBundles[locale] || enUS, enUS)
|
|
205
|
+
|
|
206
|
+
// Set terminal window title
|
|
207
|
+
process.stdout.write('\x1b]0;Mipham Code\x07')
|
|
208
|
+
|
|
209
|
+
const { waitUntilExit } = render(
|
|
210
|
+
React.createElement(I18nProvider, {
|
|
211
|
+
locale,
|
|
212
|
+
t,
|
|
213
|
+
children: React.createElement(App, {
|
|
214
|
+
engine,
|
|
215
|
+
config: {} as MiphamConfig,
|
|
216
|
+
initialProvider: 'remote',
|
|
217
|
+
initialModel: 'remote',
|
|
218
|
+
version: options.version,
|
|
219
|
+
sessionId: options.remoteSession.sessionId,
|
|
220
|
+
}),
|
|
221
|
+
}),
|
|
222
|
+
)
|
|
223
|
+
await waitUntilExit()
|
|
224
|
+
|
|
225
|
+
// Close the WebSocket connection on the way out
|
|
226
|
+
engine.close()
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
|
|
186
230
|
// Handle `mipham agents` subcommand — launch standalone dashboard
|
|
187
231
|
const args = process.argv.slice(2)
|
|
188
232
|
if (args[0] === 'agents') {
|
|
@@ -208,10 +252,6 @@ export async function runApp(options: RunOptions): Promise<void> {
|
|
|
208
252
|
|
|
209
253
|
// Detect locale and create translation function
|
|
210
254
|
const locale = detectLocale({ lang: options.lang })
|
|
211
|
-
const localeBundles: Record<string, TranslationMap> = {
|
|
212
|
-
'en-US': enUS as TranslationMap,
|
|
213
|
-
'zh-CN': zhCN as TranslationMap,
|
|
214
|
-
}
|
|
215
255
|
const t = createT(localeBundles[locale] || enUS, enUS)
|
|
216
256
|
|
|
217
257
|
// Bootstrap providers
|
package/src/ui/app.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { Box, Text, useInput } from 'ink'
|
|
|
3
3
|
import TextInput from 'ink-text-input'
|
|
4
4
|
import { readFileSync } from 'node:fs'
|
|
5
5
|
import type { QueryEngine } from '../core/engine'
|
|
6
|
+
import type { RemoteEngine } from '../daemon/remote-engine'
|
|
6
7
|
import type { MiphamConfig } from '../shared/index.ts'
|
|
7
8
|
import type { SkillsLoader } from '../skills/loader'
|
|
8
9
|
import type { PluginManager } from '../plugin/plugin-manager'
|
|
@@ -29,7 +30,7 @@ import type { PermissionMode } from '../shared/index.ts'
|
|
|
29
30
|
import { sanitizeForDisplay } from '../shared/sanitize.ts'
|
|
30
31
|
|
|
31
32
|
interface AppProps {
|
|
32
|
-
engine: QueryEngine
|
|
33
|
+
engine: QueryEngine | RemoteEngine
|
|
33
34
|
config: MiphamConfig
|
|
34
35
|
initialProvider?: string
|
|
35
36
|
initialModel?: string
|
|
@@ -265,7 +266,10 @@ export function App({
|
|
|
265
266
|
|
|
266
267
|
const mkCtx = useCallback(
|
|
267
268
|
(): CommandContext => ({
|
|
268
|
-
|
|
269
|
+
// In remote attach mode, RemoteEngine provides stub methods for all the
|
|
270
|
+
// QueryEngine APIs that slash commands need. The cast is safe because
|
|
271
|
+
// stubs cover every method that CommandContext consumers call.
|
|
272
|
+
engine: engine as QueryEngine,
|
|
269
273
|
config,
|
|
270
274
|
providerId,
|
|
271
275
|
modelId,
|
package/src/ui/chat.tsx
CHANGED
|
@@ -157,52 +157,58 @@ export function ChatPanel({ messages, focusMode }: ChatPanelProps) {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
160
|
+
* Pair each tool_use message with its following tool_result message(s),
|
|
161
|
+
* showing every tool individually with its name, detail, and output.
|
|
162
|
+
*
|
|
163
|
+
* Claude Code parity: each tool renders as:
|
|
164
|
+
* ⏺ ToolName (detail) ← colored per tool type
|
|
165
|
+
* ⎿ output summary ← dimmed below
|
|
166
|
+
*
|
|
167
|
+
* Only true duplicates (same tool name + same detail) are collapsed
|
|
168
|
+
* into a single entry with a count.
|
|
163
169
|
*/
|
|
164
170
|
function compactToolGroups(messages: ChatMessage[]): ChatMessage[] {
|
|
165
171
|
const result: ChatMessage[] = []
|
|
166
|
-
let
|
|
172
|
+
let current: ChatMessage | null = null
|
|
167
173
|
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
if (
|
|
171
|
-
result.push(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
.filter((t) => t.toolMeta?.name && !t.toolMeta.name.startsWith('tools ('))
|
|
175
|
-
.map((t) => t.toolMeta!.name)
|
|
176
|
-
const uniqueNames = [...new Set(names)]
|
|
177
|
-
result.push({
|
|
178
|
-
role: 'system',
|
|
179
|
-
content: `⏺ ${uniqueNames.join(' · ')} (${names.length} tools)`,
|
|
180
|
-
toolMeta: {
|
|
181
|
-
name: `tools (${names.length})`,
|
|
182
|
-
input: uniqueNames.join(', '),
|
|
183
|
-
collapsed: true,
|
|
184
|
-
},
|
|
185
|
-
})
|
|
174
|
+
for (const msg of messages) {
|
|
175
|
+
// Tool call with a name → flush previous, start a new one
|
|
176
|
+
if (msg.toolMeta?.name && !msg.toolMeta.name.startsWith('tools (')) {
|
|
177
|
+
if (current) result.push(current)
|
|
178
|
+
current = { ...msg }
|
|
179
|
+
continue
|
|
186
180
|
}
|
|
187
|
-
toolGroup = []
|
|
188
|
-
}
|
|
189
181
|
|
|
190
|
-
|
|
191
|
-
// Tool results (no name, has output) get folded into the preceding tool group
|
|
182
|
+
// Tool result (no name, has output) → merge output into current tool
|
|
192
183
|
if (msg.toolMeta && !msg.toolMeta.name && msg.toolMeta.output) {
|
|
193
|
-
|
|
184
|
+
if (current) {
|
|
185
|
+
current = {
|
|
186
|
+
...current,
|
|
187
|
+
content: msg.content,
|
|
188
|
+
toolMeta: {
|
|
189
|
+
...current.toolMeta!,
|
|
190
|
+
output: msg.toolMeta.output,
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Orphan tool result (no preceding tool_use) → skip
|
|
194
195
|
continue
|
|
195
196
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
197
|
+
|
|
198
|
+
// Already-grouped message (from a previous compact cycle) or non-tool
|
|
199
|
+
if (current) {
|
|
200
|
+
result.push(current)
|
|
201
|
+
current = null
|
|
202
|
+
}
|
|
203
|
+
// Skip orphan tool-result messages without a preceding tool_use
|
|
204
|
+
if (msg.toolMeta && !msg.toolMeta.name && msg.toolMeta.output) {
|
|
199
205
|
continue
|
|
200
206
|
}
|
|
201
|
-
// Already-grouped or non-tool message
|
|
202
|
-
flush()
|
|
203
207
|
result.push(msg)
|
|
204
208
|
}
|
|
205
|
-
|
|
209
|
+
|
|
210
|
+
// Flush any remaining tool
|
|
211
|
+
if (current) result.push(current)
|
|
206
212
|
return result
|
|
207
213
|
}
|
|
208
214
|
|