feat(tui): wire /pair command execution with PairingManager
This commit is contained in:
@@ -62,6 +62,10 @@ export function registerTuiCommand(program: Command): void {
|
|||||||
// local_providers, retry config, and per-tier fallback logic.
|
// local_providers, retry config, and per-tier fallback logic.
|
||||||
const modelRouter = createModelRouter(config);
|
const modelRouter = createModelRouter(config);
|
||||||
|
|
||||||
|
const { initPairingManager } = await import('../daemon/services.js');
|
||||||
|
const pairingStore = config.pairing.enabled ? sessionStore.getPairingStore() : undefined;
|
||||||
|
const pairingManager = initPairingManager(config, pairingStore);
|
||||||
|
|
||||||
const systemPrompt = loadSystemPrompt();
|
const systemPrompt = loadSystemPrompt();
|
||||||
|
|
||||||
const hookEngine = new HookEngine(config.hooks);
|
const hookEngine = new HookEngine(config.hooks);
|
||||||
@@ -145,6 +149,7 @@ export function registerTuiCommand(program: Command): void {
|
|||||||
modelRouter,
|
modelRouter,
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
agent,
|
agent,
|
||||||
|
pairingManager,
|
||||||
localProviders: config.models.local_providers,
|
localProviders: config.models.local_providers,
|
||||||
currentLocalProvider: config.models.local?.provider,
|
currentLocalProvider: config.models.local?.provider,
|
||||||
onTransfer: (target) => {
|
onTransfer: (target) => {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import type { ModelConfig } from '../../config/schema.js';
|
|||||||
import { OllamaClient, LlamaCppClient } from '../../models/index.js';
|
import { OllamaClient, LlamaCppClient } from '../../models/index.js';
|
||||||
import { createClientFromConfig } from '../../daemon/index.js';
|
import { createClientFromConfig } from '../../daemon/index.js';
|
||||||
import { loginGitHub } from '../../auth/index.js';
|
import { loginGitHub } from '../../auth/index.js';
|
||||||
|
import type { PairingManager } from '../../channels/pairing.js';
|
||||||
|
|
||||||
export { parseCommand, type Command };
|
export { parseCommand, type Command };
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ export interface MinimalTuiConfig {
|
|||||||
onTransfer?: (target: string) => void;
|
onTransfer?: (target: string) => void;
|
||||||
localProviders?: Record<string, ModelConfig>;
|
localProviders?: Record<string, ModelConfig>;
|
||||||
currentLocalProvider?: string;
|
currentLocalProvider?: string;
|
||||||
|
pairingManager?: PairingManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MinimalTui {
|
export class MinimalTui {
|
||||||
@@ -196,6 +198,10 @@ export class MinimalTui {
|
|||||||
await this.handleLoginCommand(command.provider);
|
await this.handleLoginCommand(command.provider);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'pair':
|
||||||
|
this.handlePairCommand(command.action, command.args);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'transfer':
|
case 'transfer':
|
||||||
this.config.onTransfer?.(command.target);
|
this.config.onTransfer?.(command.target);
|
||||||
break;
|
break;
|
||||||
@@ -321,6 +327,74 @@ export class MinimalTui {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handlePairCommand(action?: 'generate' | 'list' | 'revoke', args?: string): void {
|
||||||
|
const pm = this.config.pairingManager;
|
||||||
|
if (!pm) {
|
||||||
|
console.log(`${colors.gray}Pairing not enabled. Set pairing.enabled: true in config.${colors.reset}\n`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'generate': {
|
||||||
|
const code = pm.generateCode(args);
|
||||||
|
const pending = pm.listPendingCodes().find(p => p.code === code);
|
||||||
|
const expiresIn = pending ? Math.round((pending.expiresAt - Date.now()) / 1000) : '?';
|
||||||
|
console.log(`${colors.bold}Pairing code: ${code}${colors.reset}`);
|
||||||
|
console.log(`${colors.gray}Expires in ${expiresIn}s${args ? ` (label: ${args})` : ''}${colors.reset}\n`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'revoke': {
|
||||||
|
if (!args) {
|
||||||
|
console.log(`${colors.gray}Usage: /pair revoke <channel> <senderId>${colors.reset}\n`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const parts = args.split(/\s+/);
|
||||||
|
if (parts.length < 2) {
|
||||||
|
console.log(`${colors.gray}Usage: /pair revoke <channel> <senderId>${colors.reset}\n`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [channel, senderId] = parts;
|
||||||
|
const revoked = pm.revokeApproval(channel, senderId);
|
||||||
|
if (revoked) {
|
||||||
|
console.log(`${colors.bold}Revoked approval for ${channel}:${senderId}${colors.reset}\n`);
|
||||||
|
} else {
|
||||||
|
console.log(`${colors.gray}No approval found for ${channel}:${senderId}${colors.reset}\n`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'list':
|
||||||
|
default: {
|
||||||
|
const pending = pm.listPendingCodes();
|
||||||
|
const approved = pm.listApproved();
|
||||||
|
|
||||||
|
if (pending.length === 0 && approved.length === 0) {
|
||||||
|
console.log(`${colors.gray}No pending codes or approved senders.${colors.reset}\n`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pending.length > 0) {
|
||||||
|
console.log(`${colors.bold}Pending codes:${colors.reset}`);
|
||||||
|
for (const p of pending) {
|
||||||
|
const expiresIn = Math.round((p.expiresAt - Date.now()) / 1000);
|
||||||
|
console.log(` ${p.code} expires in ${expiresIn}s${p.label ? ` (${p.label})` : ''}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (approved.length > 0) {
|
||||||
|
console.log(`${colors.bold}Approved senders:${colors.reset}`);
|
||||||
|
for (const a of approved) {
|
||||||
|
const date = new Date(a.approvedAt).toISOString().slice(0, 16).replace('T', ' ');
|
||||||
|
console.log(` ${a.channel}:${a.senderId} since ${date} (code: ${a.codeUsed})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getAvailableBackends(): string[] {
|
private getAvailableBackends(): string[] {
|
||||||
const backends: string[] = [];
|
const backends: string[] = [];
|
||||||
if (this.config.currentLocalProvider) {
|
if (this.config.currentLocalProvider) {
|
||||||
|
|||||||
Reference in New Issue
Block a user