refactor(commands): move the command service to Remote
`CommandService.list` and `execute` carry the wire contract directly through `@Remote`, and the Client assembly mounts the generated commands contribution. The legacy API Proxy route, its schemas, the map rows, the generated client methods and the fixture's command domain are removed, so the catalog and the admission call have one owner again. `Session.command()` keeps a result-shaped public face for parity with the prompt, cancel and attachment neighbours it sits beside, and reads the generated namespace through one `SessionRemotes` parameter. The Session cluster declares that face against the owning business package rather than the generated contribution: the Host compiler aggregate builds this package, and it runs before any contribution is emitted. Migrated calls lose the `title-invalid` class of protocol-only error codes and report `internal`; no production caller branched on them.
This commit is contained in:
71 files changed
+648
-1129
No files matched your search
@@ -5,13 +5,10 @@
|
||||
* / epoch-guard behavior of the original global cache; the session-key axis
|
||||
* is the only extra dimension.
|
||||
*/
|
||||
import type { IApiClient, SessionId } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type { CommandDescriptor } from '@deepseek-ai/dsh-commands/types'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
|
||||
/** command.list success value, derived so the wire type authority stays in apiproxy. */
|
||||
type ListValue = Extract<Awaited<ReturnType<IApiClient['commands']['list']>>['result'], { ok: true }>['value']
|
||||
|
||||
/** One host command descriptor as served to the client. */
|
||||
export type CommandDescriptor = ListValue['commands'][number]
|
||||
export type { CommandDescriptor } from '@deepseek-ai/dsh-commands/types'
|
||||
|
||||
/**
|
||||
* cold = never pulled; pending = pull in flight with nothing servable;
|
||||
|
||||
@@ -44,8 +44,8 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
/** Dictionary namespace owned by this plugin. */
|
||||
const NS = 'command'
|
||||
|
||||
/** Required services: the '/' source registry plus the scope + wire faces the service reads, and the copy's locale registry. */
|
||||
export const inject = ['slash', 'sessions', 'connection', 'locale', 'remote']
|
||||
/** Required services: the '/' source registry, session scopes, commands Remote, and locale registry. */
|
||||
export const inject = ['slash', 'sessions', 'remote', 'remote.commands', 'locale']
|
||||
|
||||
/**
|
||||
* Client plugin body: mount the service, then register the popupSelect shell
|
||||
|
||||
@@ -9,11 +9,10 @@
|
||||
*/
|
||||
import { Service } from '@deepseek-ai/cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { ConnectionHandle, SessionId } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type { ClientContext, ISessions } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
// Type-only: pulls the ctx.remote merge and the forwarded-event key face
|
||||
// (`commands/change` rides the allowlist) into this program.
|
||||
import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
||||
import type { ClientContext, ISessions, SessionId } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {
|
||||
CandidateRequest, ClientSessionContext, CommandClaim, PickOutcome, SlashCandidate, SlashPick,
|
||||
SubmitOutcome,
|
||||
@@ -96,7 +95,7 @@ function fuzzyCandidates(candidates: readonly SlashCandidate[], rawQuery: string
|
||||
|
||||
/** Command surface: session-keyed directory + '/' source + contribution registry + per-session popups. */
|
||||
export class CommandService extends Service implements CommandServiceContract {
|
||||
static inject = ['slash', 'sessions', 'connection', 'remote']
|
||||
static inject = ['slash', 'sessions', 'remote', 'remote.commands']
|
||||
|
||||
private readonly directory: CommandDirectory
|
||||
private readonly live: LiveState = { contributions: new Map(), decorations: new Map(), popups: new Map() }
|
||||
@@ -107,13 +106,11 @@ export class CommandService extends Service implements CommandServiceContract {
|
||||
*/
|
||||
constructor(ctx: Context) {
|
||||
super(ctx, 'command')
|
||||
const connection = ctx.get('connection') as ConnectionHandle | undefined
|
||||
if (connection === undefined) throw new Error('ui-command: connection service unavailable')
|
||||
this.directory = new CommandDirectory(async (sessionId) => {
|
||||
if (this.sessions().subagentAddress(sessionId) !== undefined) return []
|
||||
const { result } = await connection.api.commands.list({ sessionId })
|
||||
const result = await ctx.remote.commands.list(sessionId)
|
||||
if (!result.ok) throw new Error(`command.list failed: ${result.error.code}: ${result.error.message}`)
|
||||
return result.value.commands
|
||||
return result.value
|
||||
})
|
||||
const slash = ctx.get('slash')
|
||||
if (slash === undefined) throw new Error('ui-command: slash service unavailable')
|
||||
@@ -351,10 +348,9 @@ export class CommandService extends Service implements CommandServiceContract {
|
||||
session: ClientSessionContext,
|
||||
line: string,
|
||||
): Promise<SubmitOutcome> {
|
||||
const connection = this.ctx.get('connection') as ConnectionHandle
|
||||
const { result } = await connection.api.commands.execute({ sessionId: session.sessionId, line })
|
||||
const result = await this.ctx.remote.commands.execute(session.sessionId, line)
|
||||
if (!result.ok) throw new Error(`command.execute failed: ${result.error.code}: ${result.error.message}`)
|
||||
if (!result.value.matched) return { kind: 'error', text: `unknown or malformed command: ${line}` }
|
||||
if (result.value === undefined) return { kind: 'error', text: `unknown or malformed command: ${line}` }
|
||||
return { kind: 'success' }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user