Files
flutter_opencode_client/lib/data/model/opencode/models.dart
root 0f4fe33003 Add Opencode (opencode.ai) integration via SSH tunnel
Features:
- Opencode AI chat interface
- SSH tunnel for secure API communication
- Auto-install Opencode on remote servers
- API key management with server-side storage
- Session history and management
- Integration with flutter_server_box server list
2026-04-03 00:41:32 +08:00

249 lines
6.3 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dartssh2/dartssh2.dart';
import 'package:flutter/foundation.dart';
/// Opencode API 响应模型
class OpencodeResponse {
final String id;
final String? content;
final String? error;
final Map<String, dynamic>? toolCalls;
final bool isComplete;
final DateTime timestamp;
OpencodeResponse({
required this.id,
this.content,
this.error,
this.toolCalls,
this.isComplete = false,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
factory OpencodeResponse.fromJson(Map<String, dynamic> json) {
return OpencodeResponse(
id: json['id'] ?? '',
content: json['content'],
error: json['error'],
toolCalls: json['tool_calls'],
isComplete: json['is_complete'] ?? false,
timestamp: json['timestamp'] != null
? DateTime.parse(json['timestamp'])
: DateTime.now(),
);
}
Map<String, dynamic> toJson() => {
'id': id,
'content': content,
'error': error,
'tool_calls': toolCalls,
'is_complete': isComplete,
'timestamp': timestamp.toIso8601String(),
};
}
/// Opencode 消息模型
class OpencodeMessage {
final String id;
final String role; // 'user', 'assistant', 'system'
final String content;
final DateTime timestamp;
final List<Map<String, dynamic>>? toolCalls;
final Map<String, dynamic>? toolResults;
OpencodeMessage({
required this.id,
required this.role,
required this.content,
DateTime? timestamp,
this.toolCalls,
this.toolResults,
}) : timestamp = timestamp ?? DateTime.now();
factory OpencodeMessage.user(String content) {
return OpencodeMessage(
id: _generateId(),
role: 'user',
content: content,
);
}
factory OpencodeMessage.assistant(String content) {
return OpencodeMessage(
id: _generateId(),
role: 'assistant',
content: content,
);
}
factory OpencodeMessage.system(String content) {
return OpencodeMessage(
id: _generateId(),
role: 'system',
content: content,
);
}
factory OpencodeMessage.fromJson(Map<String, dynamic> json) {
return OpencodeMessage(
id: json['id'] ?? _generateId(),
role: json['role'] ?? 'user',
content: json['content'] ?? '',
timestamp: json['timestamp'] != null
? DateTime.parse(json['timestamp'])
: DateTime.now(),
toolCalls: json['tool_calls'] != null
? List<Map<String, dynamic>>.from(json['tool_calls'])
: null,
toolResults: json['tool_results'],
);
}
Map<String, dynamic> toJson() => {
'id': id,
'role': role,
'content': content,
'timestamp': timestamp.toIso8601String(),
'tool_calls': toolCalls,
'tool_results': toolResults,
};
static String _generateId() {
return DateTime.now().millisecondsSinceEpoch.toString();
}
}
/// Opencode 会话配置
class OpencodeSession {
final String id;
final String serverId;
final String serverName;
final List<OpencodeMessage> messages;
final DateTime createdAt;
DateTime updatedAt;
String? title;
OpencodeSession({
required this.id,
required this.serverId,
required this.serverName,
this.messages = const [],
DateTime? createdAt,
DateTime? updatedAt,
this.title,
}) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
factory OpencodeSession.create({
required String serverId,
required String serverName,
String? title,
}) {
return OpencodeSession(
id: _generateId(),
serverId: serverId,
serverName: serverName,
title: title,
messages: [
OpencodeMessage.system(
'You are Opencode, a helpful AI assistant running inside an SSH session. '
'You have access to the server filesystem and can execute commands via tools.',
),
],
);
}
void addMessage(OpencodeMessage message) {
messages.add(message);
updatedAt = DateTime.now();
}
Map<String, dynamic> toJson() => {
'id': id,
'server_id': serverId,
'server_name': serverName,
'messages': messages.map((m) => m.toJson()).toList(),
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
'title': title,
};
factory OpencodeSession.fromJson(Map<String, dynamic> json) {
return OpencodeSession(
id: json['id'] ?? _generateId(),
serverId: json['server_id'] ?? '',
serverName: json['server_name'] ?? '',
messages: (json['messages'] as List?)
?.map((m) => OpencodeMessage.fromJson(m))
.toList() ??
[],
createdAt: json['created_at'] != null
? DateTime.parse(json['created_at'])
: DateTime.now(),
updatedAt: json['updated_at'] != null
? DateTime.parse(json['updated_at'])
: DateTime.now(),
title: json['title'],
);
}
static String _generateId() {
return DateTime.now().millisecondsSinceEpoch.toString();
}
}
/// Opencode 安装状态
enum OpencodeInstallStatus {
notInstalled,
installing,
installed,
error,
}
/// Opencode 服务器配置
class OpencodeServerConfig {
final String serverId;
String opencodePath;
String configPath;
String apiPort;
bool autoStart;
Map<String, String> envVars;
String? apiKey; // Opencode API Key
OpencodeServerConfig({
required this.serverId,
this.opencodePath = '/usr/local/bin/opencode',
this.configPath = '~/.config/opencode',
this.apiPort = '8080',
this.autoStart = true,
this.envVars = const {},
this.apiKey,
});
Map<String, dynamic> toJson() => {
'server_id': serverId,
'opencode_path': opencodePath,
'config_path': configPath,
'api_port': apiPort,
'auto_start': autoStart,
'env_vars': envVars,
'api_key': apiKey,
};
factory OpencodeServerConfig.fromJson(Map<String, dynamic> json) {
return OpencodeServerConfig(
serverId: json['server_id'] ?? '',
opencodePath: json['opencode_path'] ?? '/usr/local/bin/opencode',
configPath: json['config_path'] ?? '~/.config/opencode',
apiPort: json['api_port'] ?? '8080',
autoStart: json['auto_start'] ?? true,
envVars: Map<String, String>.from(json['env_vars'] ?? {}),
apiKey: json['api_key'],
);
}
}