xworkmate-app/lib/runtime/codex_runtime.dart
2026-06-04 22:38:09 +08:00

287 lines
6.6 KiB
Dart

/// Codex sandbox mode for controlling file system access.
enum CodexSandboxMode {
readOnly('read-only'),
workspaceWrite('workspace-write'),
dangerFullAccess('danger-full-access');
final String value;
const CodexSandboxMode(this.value);
}
/// Codex approval policy for controlling automatic execution.
enum CodexApprovalPolicy {
suggest('suggest'),
autoEdit('auto-edit'),
fullAuto('full-auto');
final String value;
const CodexApprovalPolicy(this.value);
}
/// Codex authentication mode.
enum CodexAuthMode {
apiKey('api-key'),
chatgpt('chatgpt'),
chatgptAuthTokens('chatgptAuthTokens');
final String value;
const CodexAuthMode(this.value);
}
/// Codex thread information.
class CodexThread {
final String id;
final String? path;
final bool ephemeral;
final DateTime? createdAt;
const CodexThread({
required this.id,
this.path,
this.ephemeral = false,
this.createdAt,
});
factory CodexThread.fromJson(Map<String, dynamic> json) {
return CodexThread(
id: json['id'] as String,
path: json['path'] as String?,
ephemeral: json['ephemeral'] as bool? ?? false,
createdAt: json['createdAt'] != null
? DateTime.tryParse(json['createdAt'] as String)
: null,
);
}
Map<String, dynamic> toJson() => {
'id': id,
if (path != null) 'path': path,
'ephemeral': ephemeral,
if (createdAt != null) 'createdAt': createdAt!.toIso8601String(),
};
}
/// Codex turn information.
class CodexTurn {
final String id;
final String threadId;
final String status;
final DateTime? startedAt;
final DateTime? completedAt;
const CodexTurn({
required this.id,
required this.threadId,
required this.status,
this.startedAt,
this.completedAt,
});
factory CodexTurn.fromJson(Map<String, dynamic> json) {
return CodexTurn(
id: json['id'] as String,
threadId: json['threadId'] as String,
status: json['status'] as String,
startedAt: json['startedAt'] != null
? DateTime.tryParse(json['startedAt'] as String)
: null,
completedAt: json['completedAt'] != null
? DateTime.tryParse(json['completedAt'] as String)
: null,
);
}
}
/// Codex account information.
class CodexAccount {
final String? email;
final String? plan;
final bool hasCredits;
final double? creditsBalance;
final List<CodexRateLimit> rateLimits;
const CodexAccount({
this.email,
this.plan,
this.hasCredits = false,
this.creditsBalance,
this.rateLimits = const [],
});
factory CodexAccount.fromJson(Map<String, dynamic> json) {
return CodexAccount(
email: json['email'] as String?,
plan: json['plan'] as String?,
hasCredits: json['hasCredits'] as bool? ?? false,
creditsBalance: (json['creditsBalance'] as num?)?.toDouble(),
rateLimits:
(json['rateLimits'] as List?)
?.map((e) => CodexRateLimit.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
);
}
}
/// Codex rate limit information.
class CodexRateLimit {
final String type;
final int percentRemaining;
final DateTime? resetsAt;
const CodexRateLimit({
required this.type,
required this.percentRemaining,
this.resetsAt,
});
factory CodexRateLimit.fromJson(Map<String, dynamic> json) {
return CodexRateLimit(
type: json['type'] as String,
percentRemaining: json['percentRemaining'] as int? ?? 0,
resetsAt: json['resetsAt'] != null
? DateTime.tryParse(json['resetsAt'] as String)
: null,
);
}
}
/// Codex user input for turn/start.
class CodexUserInput {
final String type;
final String content;
final List<CodexAttachment>? attachments;
const CodexUserInput({
this.type = 'message',
required this.content,
this.attachments,
});
Map<String, dynamic> toJson() => {
'type': type,
'content': content,
if (attachments != null && attachments!.isNotEmpty)
'attachments': attachments!.map((a) => a.toJson()).toList(),
};
}
/// Codex file attachment.
class CodexAttachment {
final String path;
final String? name;
const CodexAttachment({required this.path, this.name});
Map<String, dynamic> toJson() => {
'path': path,
if (name != null) 'name': name,
};
}
/// Base class for Codex events.
sealed class CodexEvent {
const CodexEvent();
}
/// Log event from Codex.
class CodexLogEvent extends CodexEvent {
final String level;
final String message;
final DateTime timestamp;
const CodexLogEvent({
required this.level,
required this.message,
required this.timestamp,
});
}
/// Notification event from Codex App Server.
class CodexNotificationEvent extends CodexEvent {
final String method;
final Map<String, dynamic> params;
const CodexNotificationEvent({required this.method, required this.params});
}
/// Turn event (item/started, item/completed, etc.).
class CodexTurnEvent extends CodexEvent {
final String type;
final String? threadId;
final String? turnId;
final String? itemId;
final Map<String, dynamic> data;
const CodexTurnEvent({
required this.type,
this.threadId,
this.turnId,
this.itemId,
required this.data,
});
factory CodexTurnEvent.fromNotification(CodexNotificationEvent notification) {
final params = notification.params;
return CodexTurnEvent(
type: notification.method,
threadId: params['threadId'] as String?,
turnId: params['turnId'] as String?,
itemId: params['itemId'] as String?,
data: params,
);
}
/// Check if this is a text delta event.
bool get isTextDelta => type == 'item/agentMessage/delta';
/// Get text delta content.
String? get textDelta => data['delta'] as String?;
}
/// Error from Codex RPC.
class CodexRpcError implements Exception {
final int code;
final String message;
final dynamic data;
const CodexRpcError({required this.code, required this.message, this.data});
factory CodexRpcError.fromJson(Map<String, dynamic> json) {
return CodexRpcError(
code: json['code'] as int? ?? -1,
message: json['message'] as String? ?? 'Unknown error',
data: json['data'],
);
}
@override
String toString() => 'CodexRpcError($code): $message';
}
/// Connection state for CodexRuntime.
enum CodexConnectionState {
disconnected,
connecting,
connected,
initializing,
ready,
error,
}
/// Codex App Server RPC client.
class CodexRuntime {}
class CodexLaunchConfiguration {
const CodexLaunchConfiguration({
required this.executable,
required this.arguments,
this.runInShell = false,
});
final String executable;
final List<String> arguments;
final bool runInShell;
}