@ebowwa/coder 0.7.64 → 0.7.66
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/dist/index.js +36233 -32
- package/dist/interfaces/ui/terminal/cli/index.js +34318 -158
- package/dist/interfaces/ui/terminal/native/README.md +53 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.dylib +0 -0
- package/dist/interfaces/ui/terminal/native/index.d.ts +0 -0
- package/dist/interfaces/ui/terminal/native/index.darwin-arm64.node +0 -0
- package/dist/interfaces/ui/terminal/native/index.js +43 -0
- package/dist/interfaces/ui/terminal/native/index.node +0 -0
- package/dist/interfaces/ui/terminal/native/package.json +34 -0
- package/dist/native/README.md +53 -0
- package/dist/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/native/claude_code_native.dylib +0 -0
- package/dist/native/index.d.ts +0 -480
- package/dist/native/index.darwin-arm64.node +0 -0
- package/dist/native/index.js +43 -1625
- package/dist/native/index.node +0 -0
- package/dist/native/package.json +34 -0
- package/native/index.darwin-arm64.node +0 -0
- package/native/index.js +33 -19
- package/package.json +3 -2
- package/packages/src/core/agent-loop/__tests__/compaction.test.ts +17 -14
- package/packages/src/core/agent-loop/compaction.ts +6 -2
- package/packages/src/core/agent-loop/index.ts +2 -0
- package/packages/src/core/agent-loop/loop-state.ts +1 -1
- package/packages/src/core/agent-loop/turn-executor.ts +4 -0
- package/packages/src/core/agent-loop/types.ts +4 -0
- package/packages/src/core/api-client-impl.ts +377 -176
- package/packages/src/core/cognitive-security/hooks.ts +2 -1
- package/packages/src/core/config/todo +7 -0
- package/packages/src/core/context/__tests__/integration.test.ts +334 -0
- package/packages/src/core/context/compaction.ts +170 -0
- package/packages/src/core/context/constants.ts +58 -0
- package/packages/src/core/context/extraction.ts +85 -0
- package/packages/src/core/context/index.ts +66 -0
- package/packages/src/core/context/summarization.ts +251 -0
- package/packages/src/core/context/token-estimation.ts +98 -0
- package/packages/src/core/context/types.ts +59 -0
- package/packages/src/core/models.ts +81 -4
- package/packages/src/core/normalizers/todo +5 -1
- package/packages/src/core/providers/README.md +230 -0
- package/packages/src/core/providers/__tests__/providers.test.ts +135 -0
- package/packages/src/core/providers/index.ts +419 -0
- package/packages/src/core/providers/types.ts +132 -0
- package/packages/src/core/retry.ts +10 -0
- package/packages/src/ecosystem/tools/index.ts +174 -0
- package/packages/src/index.ts +23 -2
- package/packages/src/interfaces/ui/index.ts +17 -20
- package/packages/src/interfaces/ui/spinner.ts +2 -2
- package/packages/src/interfaces/ui/terminal/bridge/index.ts +370 -0
- package/packages/src/interfaces/ui/terminal/bridge/ipc.ts +829 -0
- package/packages/src/interfaces/ui/terminal/bridge/screen-export.ts +968 -0
- package/packages/src/interfaces/ui/terminal/bridge/types.ts +226 -0
- package/packages/src/interfaces/ui/terminal/bridge/useBridge.ts +210 -0
- package/packages/src/interfaces/ui/terminal/cli/bootstrap.ts +132 -0
- package/packages/src/interfaces/ui/terminal/cli/index.ts +200 -13
- package/packages/src/interfaces/ui/terminal/cli/interactive/index.ts +110 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/input-handler.ts +402 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts +820 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/message-store.ts +299 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/types.ts +274 -0
- package/packages/src/interfaces/ui/terminal/shared/index.ts +13 -0
- package/packages/src/interfaces/ui/terminal/shared/query.ts +9 -3
- package/packages/src/interfaces/ui/terminal/shared/setup.ts +5 -1
- package/packages/src/interfaces/ui/terminal/shared/spinner-frames.ts +73 -0
- package/packages/src/interfaces/ui/terminal/shared/status-line.ts +10 -2
- package/packages/src/native/index.ts +404 -27
- package/packages/src/native/tui_v2_types.ts +39 -0
- package/packages/src/teammates/coordination.test.ts +279 -0
- package/packages/src/teammates/coordination.ts +646 -0
- package/packages/src/teammates/index.ts +95 -25
- package/packages/src/teammates/integration.test.ts +272 -0
- package/packages/src/teammates/runner.test.ts +235 -0
- package/packages/src/teammates/runner.ts +750 -0
- package/packages/src/teammates/schemas.ts +673 -0
- package/packages/src/types/index.ts +1 -0
- package/packages/src/core/context-compaction.ts +0 -578
- package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
- package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
- package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +0 -262
- package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +0 -232
- package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +0 -62
- package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +0 -537
- package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +0 -107
- package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +0 -240
- package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +0 -54
- package/packages/src/interfaces/ui/terminal/tui/commands.ts +0 -438
- package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +0 -584
- package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +0 -614
- package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +0 -333
- package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +0 -604
- package/packages/src/interfaces/ui/terminal/tui/components/index.ts +0 -118
- package/packages/src/interfaces/ui/terminal/tui/console.ts +0 -49
- package/packages/src/interfaces/ui/terminal/tui/index.ts +0 -90
- package/packages/src/interfaces/ui/terminal/tui/run.tsx +0 -42
- package/packages/src/interfaces/ui/terminal/tui/spinner.ts +0 -69
- package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +0 -390
- package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +0 -422
- package/packages/src/interfaces/ui/terminal/tui/types.ts +0 -186
- package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +0 -104
- package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +0 -239
|
@@ -1852,6 +1852,175 @@ export const AnalyzeImageTool: ToolDefinition = {
|
|
|
1852
1852
|
},
|
|
1853
1853
|
};
|
|
1854
1854
|
|
|
1855
|
+
// ============================================
|
|
1856
|
+
// TEAMMATE COORDINATION TOOLS
|
|
1857
|
+
// ============================================
|
|
1858
|
+
|
|
1859
|
+
/**
|
|
1860
|
+
* Check for new messages from teammates
|
|
1861
|
+
*/
|
|
1862
|
+
const TeammateCheckMessagesTool: ToolDefinition = {
|
|
1863
|
+
name: "teammate_check_messages",
|
|
1864
|
+
description:
|
|
1865
|
+
"Check for new messages from other teammates in your team. Returns pending messages and clears the inbox. Use this periodically when in teammate mode to receive tasks and coordination messages.",
|
|
1866
|
+
input_schema: {
|
|
1867
|
+
type: "object",
|
|
1868
|
+
properties: {},
|
|
1869
|
+
},
|
|
1870
|
+
handler: async (_args, _context: ToolContext): Promise<ToolResult> => {
|
|
1871
|
+
const { getTeammateRunner } = await import("../../teammates/runner.js");
|
|
1872
|
+
const runner = getTeammateRunner();
|
|
1873
|
+
if (!runner) {
|
|
1874
|
+
return {
|
|
1875
|
+
content: "Teammate mode is not active. Start with --teammate-mode flag.",
|
|
1876
|
+
is_error: true,
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
const messages = runner.getPendingMessages();
|
|
1881
|
+
if (messages.length === 0) {
|
|
1882
|
+
return { content: "No pending messages." };
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
const formatted = messages
|
|
1886
|
+
.map((m) => `[${m.type}] From: ${m.from}\n${m.content}`)
|
|
1887
|
+
.join("\n\n---\n\n");
|
|
1888
|
+
|
|
1889
|
+
return { content: `Received ${messages.length} message(s):\n\n${formatted}` };
|
|
1890
|
+
},
|
|
1891
|
+
};
|
|
1892
|
+
|
|
1893
|
+
/**
|
|
1894
|
+
* Send a direct message to another teammate
|
|
1895
|
+
*/
|
|
1896
|
+
const TeammateSendMessageTool: ToolDefinition = {
|
|
1897
|
+
name: "teammate_send_message",
|
|
1898
|
+
description:
|
|
1899
|
+
"Send a direct message to a specific teammate or broadcast to all teammates. Use this for coordination, sharing results, or requesting help.",
|
|
1900
|
+
input_schema: {
|
|
1901
|
+
type: "object",
|
|
1902
|
+
properties: {
|
|
1903
|
+
to: {
|
|
1904
|
+
type: "string",
|
|
1905
|
+
description:
|
|
1906
|
+
"Teammate ID to send to, or 'broadcast' to send to all teammates",
|
|
1907
|
+
},
|
|
1908
|
+
message: {
|
|
1909
|
+
type: "string",
|
|
1910
|
+
description: "The message content to send",
|
|
1911
|
+
},
|
|
1912
|
+
},
|
|
1913
|
+
required: ["to", "message"],
|
|
1914
|
+
},
|
|
1915
|
+
handler: async (args, _context: ToolContext): Promise<ToolResult> => {
|
|
1916
|
+
const { getTeammateRunner } = await import("../../teammates/runner.js");
|
|
1917
|
+
const runner = getTeammateRunner();
|
|
1918
|
+
if (!runner) {
|
|
1919
|
+
return {
|
|
1920
|
+
content: "Teammate mode is not active. Start with --teammate-mode flag.",
|
|
1921
|
+
is_error: true,
|
|
1922
|
+
};
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
const to = args.to as string;
|
|
1926
|
+
const message = args.message as string;
|
|
1927
|
+
|
|
1928
|
+
if (to === "broadcast") {
|
|
1929
|
+
runner.broadcast(message);
|
|
1930
|
+
return { content: `Broadcast message sent to team.` };
|
|
1931
|
+
} else {
|
|
1932
|
+
runner.sendDirectMessage(to, message);
|
|
1933
|
+
return { content: `Direct message sent to ${to}.` };
|
|
1934
|
+
}
|
|
1935
|
+
},
|
|
1936
|
+
};
|
|
1937
|
+
|
|
1938
|
+
/**
|
|
1939
|
+
* Report idle status to team
|
|
1940
|
+
*/
|
|
1941
|
+
const TeammateReportIdleTool: ToolDefinition = {
|
|
1942
|
+
name: "teammate_report_idle",
|
|
1943
|
+
description:
|
|
1944
|
+
"Report that you are now idle and ready for new tasks. Use this when you complete your current work and are available for assignment.",
|
|
1945
|
+
input_schema: {
|
|
1946
|
+
type: "object",
|
|
1947
|
+
properties: {
|
|
1948
|
+
message: {
|
|
1949
|
+
type: "string",
|
|
1950
|
+
description: "Optional status message",
|
|
1951
|
+
},
|
|
1952
|
+
},
|
|
1953
|
+
},
|
|
1954
|
+
handler: async (args, _context: ToolContext): Promise<ToolResult> => {
|
|
1955
|
+
const { getTeammateRunner } = await import("../../teammates/runner.js");
|
|
1956
|
+
const runner = getTeammateRunner();
|
|
1957
|
+
if (!runner) {
|
|
1958
|
+
return {
|
|
1959
|
+
content: "Teammate mode is not active. Start with --teammate-mode flag.",
|
|
1960
|
+
is_error: true,
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
const message = args.message as string | undefined;
|
|
1965
|
+
runner.updateStatus("idle");
|
|
1966
|
+
runner.broadcast(message || "Now idle and ready for tasks.");
|
|
1967
|
+
return { content: "Status updated to idle. Team notified." };
|
|
1968
|
+
},
|
|
1969
|
+
};
|
|
1970
|
+
|
|
1971
|
+
/**
|
|
1972
|
+
* Get teammate status information
|
|
1973
|
+
*/
|
|
1974
|
+
const TeammateGetStatusTool: ToolDefinition = {
|
|
1975
|
+
name: "teammate_get_status",
|
|
1976
|
+
description:
|
|
1977
|
+
"Get current teammate mode status including your info, team members, and inbox stats. Use this to check your state and team coordination.",
|
|
1978
|
+
input_schema: {
|
|
1979
|
+
type: "object",
|
|
1980
|
+
properties: {},
|
|
1981
|
+
},
|
|
1982
|
+
handler: async (_args, _context: ToolContext): Promise<ToolResult> => {
|
|
1983
|
+
const { getTeammateRunner } = await import("../../teammates/runner.js");
|
|
1984
|
+
const runner = getTeammateRunner();
|
|
1985
|
+
if (!runner) {
|
|
1986
|
+
return {
|
|
1987
|
+
content: "Teammate mode is not active. Start with --teammate-mode flag.",
|
|
1988
|
+
is_error: true,
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
const teammate = runner.getTeammate();
|
|
1993
|
+
const team = runner.getTeam();
|
|
1994
|
+
const status = runner.getStatus();
|
|
1995
|
+
const inbox = runner.getInboxStats();
|
|
1996
|
+
|
|
1997
|
+
const info = {
|
|
1998
|
+
teammate: teammate
|
|
1999
|
+
? {
|
|
2000
|
+
id: teammate.teammateId,
|
|
2001
|
+
name: teammate.name,
|
|
2002
|
+
color: teammate.color,
|
|
2003
|
+
}
|
|
2004
|
+
: null,
|
|
2005
|
+
team: team
|
|
2006
|
+
? {
|
|
2007
|
+
name: team.name,
|
|
2008
|
+
memberCount: team.teammates.length,
|
|
2009
|
+
members: team.teammates.map((t) => ({
|
|
2010
|
+
id: t.teammateId,
|
|
2011
|
+
name: t.name,
|
|
2012
|
+
status: t.status,
|
|
2013
|
+
})),
|
|
2014
|
+
}
|
|
2015
|
+
: null,
|
|
2016
|
+
status,
|
|
2017
|
+
inbox,
|
|
2018
|
+
};
|
|
2019
|
+
|
|
2020
|
+
return { content: JSON.stringify(info, null, 2) };
|
|
2021
|
+
},
|
|
2022
|
+
};
|
|
2023
|
+
|
|
1855
2024
|
export const builtInTools: ToolDefinition[] = [
|
|
1856
2025
|
ReadTool,
|
|
1857
2026
|
WriteTool,
|
|
@@ -1870,6 +2039,11 @@ export const builtInTools: ToolDefinition[] = [
|
|
|
1870
2039
|
NotebookEditTool,
|
|
1871
2040
|
AnalyzeImageTool,
|
|
1872
2041
|
TempGlmVisionTool,
|
|
2042
|
+
// Teammate coordination tools
|
|
2043
|
+
TeammateCheckMessagesTool,
|
|
2044
|
+
TeammateSendMessageTool,
|
|
2045
|
+
TeammateReportIdleTool,
|
|
2046
|
+
TeammateGetStatusTool,
|
|
1873
2047
|
];
|
|
1874
2048
|
|
|
1875
2049
|
export function getToolByName(name: string): ToolDefinition | undefined {
|
package/packages/src/index.ts
CHANGED
|
@@ -71,6 +71,27 @@ export * from "./teammates/index.js";
|
|
|
71
71
|
// Cognitive Security
|
|
72
72
|
export * from "./core/cognitive-security/index.js";
|
|
73
73
|
|
|
74
|
+
// Providers
|
|
75
|
+
export {
|
|
76
|
+
PROVIDERS,
|
|
77
|
+
getProvider,
|
|
78
|
+
getProviderForModel,
|
|
79
|
+
resolveProvider,
|
|
80
|
+
isProviderHealthy,
|
|
81
|
+
getHealthyProviders,
|
|
82
|
+
getNextHealthyProvider,
|
|
83
|
+
recordProviderSuccess,
|
|
84
|
+
recordProviderFailure,
|
|
85
|
+
getProviderHealth,
|
|
86
|
+
} from "./core/providers/index.js";
|
|
87
|
+
export type {
|
|
88
|
+
ProviderName,
|
|
89
|
+
ProviderConfig,
|
|
90
|
+
ProviderHealth,
|
|
91
|
+
ResolvedProvider,
|
|
92
|
+
ProviderRoutingConfig,
|
|
93
|
+
} from "./core/providers/index.js";
|
|
94
|
+
|
|
74
95
|
// UI Components (exclude types that are already in types/index.js)
|
|
75
96
|
export {
|
|
76
97
|
Spinner,
|
|
@@ -112,9 +133,9 @@ export {
|
|
|
112
133
|
summarizeMessages,
|
|
113
134
|
summarizeWithLLM,
|
|
114
135
|
getCompactionStats,
|
|
115
|
-
} from "./core/context
|
|
136
|
+
} from "./core/context/index.js";
|
|
116
137
|
export type {
|
|
117
138
|
CompactionOptions,
|
|
118
139
|
CompactionResult,
|
|
119
140
|
LLMSummarizationOptions,
|
|
120
|
-
} from "./core/context
|
|
141
|
+
} from "./core/context/index.js";
|
|
@@ -53,27 +53,24 @@ export {
|
|
|
53
53
|
type ContextInfo,
|
|
54
54
|
} from "./terminal/shared/status-line.js";
|
|
55
55
|
|
|
56
|
-
//
|
|
56
|
+
// Interactive CLI (non-React, from interactive module)
|
|
57
57
|
export {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
type
|
|
66
|
-
type
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
type
|
|
73
|
-
|
|
74
|
-
type Message as TUIMessage,
|
|
75
|
-
} from "./terminal/tui/tui-app.js";
|
|
76
|
-
export { default as TUIApp } from "./terminal/tui/tui-app.js";
|
|
58
|
+
InteractiveRunner,
|
|
59
|
+
runInteractiveMode,
|
|
60
|
+
MessageStoreImpl,
|
|
61
|
+
InputManagerImpl,
|
|
62
|
+
KeyEvents,
|
|
63
|
+
InputPriority,
|
|
64
|
+
type InteractiveRunnerProps,
|
|
65
|
+
type InteractiveState,
|
|
66
|
+
type UIMessage,
|
|
67
|
+
type MessageSubType,
|
|
68
|
+
type MessageStore,
|
|
69
|
+
type InputManager,
|
|
70
|
+
type InputHandler,
|
|
71
|
+
type InputHandlerOptions,
|
|
72
|
+
type NativeKeyEvent,
|
|
73
|
+
} from "./terminal/cli/interactive/index.js";
|
|
77
74
|
|
|
78
75
|
// ============================================
|
|
79
76
|
// PROGRESS CALLBACK TYPES
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
import ora, { type Ora } from "ora";
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
|
|
10
|
-
// Import spinner frames from the single source of truth
|
|
10
|
+
// Import spinner frames from the single source of truth (shared)
|
|
11
11
|
import {
|
|
12
12
|
spinnerFrames,
|
|
13
13
|
dotSpinnerFrames,
|
|
14
14
|
asciiSpinnerFrames,
|
|
15
15
|
arrowSpinnerFrames,
|
|
16
16
|
simpleDotFrames,
|
|
17
|
-
} from "./terminal/
|
|
17
|
+
} from "./terminal/shared/spinner-frames.js";
|
|
18
18
|
|
|
19
19
|
// Re-export for external use
|
|
20
20
|
export {
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI Bridge Module
|
|
3
|
+
* Enables external control of coder via TUI Bridge MCP
|
|
4
|
+
*
|
|
5
|
+
* This module provides:
|
|
6
|
+
* 1. State export for external parsing
|
|
7
|
+
* 2. Command execution from external sources
|
|
8
|
+
* 3. Event emission for external listeners
|
|
9
|
+
* 4. Screen capture and export utilities
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { EventEmitter } from "events";
|
|
13
|
+
import type {
|
|
14
|
+
BridgeState,
|
|
15
|
+
BridgeEvent,
|
|
16
|
+
BridgeCommand,
|
|
17
|
+
BridgeCommandResult,
|
|
18
|
+
TUIBridgeConfig,
|
|
19
|
+
ParsedScreen,
|
|
20
|
+
ScreenBuffer,
|
|
21
|
+
ScreenCell,
|
|
22
|
+
UIElement,
|
|
23
|
+
UIElementType,
|
|
24
|
+
BridgeEventType,
|
|
25
|
+
} from "./types.js";
|
|
26
|
+
|
|
27
|
+
// Re-export screen-export utilities
|
|
28
|
+
export {
|
|
29
|
+
DEFAULT_CELL,
|
|
30
|
+
parseANSIText,
|
|
31
|
+
stripANSI,
|
|
32
|
+
createScreenBuffer,
|
|
33
|
+
renderToScreenBuffer,
|
|
34
|
+
captureScreen,
|
|
35
|
+
detectUIElements,
|
|
36
|
+
exportToText,
|
|
37
|
+
exportToJSON,
|
|
38
|
+
exportToHTML,
|
|
39
|
+
exportToMarkdown,
|
|
40
|
+
exportScreen,
|
|
41
|
+
parseScreen,
|
|
42
|
+
createScreenExportAPI,
|
|
43
|
+
type ExportFormat,
|
|
44
|
+
type ExportOptions,
|
|
45
|
+
type ScreenExportAPI,
|
|
46
|
+
} from "./screen-export.js";
|
|
47
|
+
|
|
48
|
+
// Import for use in TUIBridge
|
|
49
|
+
import {
|
|
50
|
+
createScreenBuffer as createBuffer,
|
|
51
|
+
parseANSIText,
|
|
52
|
+
renderToScreenBuffer,
|
|
53
|
+
detectUIElements,
|
|
54
|
+
exportScreen as doExport,
|
|
55
|
+
parseScreen,
|
|
56
|
+
} from "./screen-export.js";
|
|
57
|
+
import type { ExportFormat, ExportOptions } from "./screen-export.js";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* TUI Bridge singleton for external control
|
|
61
|
+
*/
|
|
62
|
+
export class TUIBridge extends EventEmitter {
|
|
63
|
+
private static instance: TUIBridge | null = null;
|
|
64
|
+
private config: TUIBridgeConfig;
|
|
65
|
+
private state: BridgeState | null = null;
|
|
66
|
+
private subscribers: Set<string> = new Set();
|
|
67
|
+
private commandQueue: Array<{ command: BridgeCommand; resolve: (result: BridgeCommandResult) => void }> = [];
|
|
68
|
+
private isProcessing: boolean = false;
|
|
69
|
+
private screenBuffer: ScreenBuffer | null = null;
|
|
70
|
+
private terminalDimensions: { width: number; height: number } = { width: 80, height: 24 };
|
|
71
|
+
|
|
72
|
+
private constructor(config: TUIBridgeConfig) {
|
|
73
|
+
super();
|
|
74
|
+
this.config = config;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get singleton instance
|
|
79
|
+
*/
|
|
80
|
+
static getInstance(config?: TUIBridgeConfig): TUIBridge | null {
|
|
81
|
+
if (!TUIBridge.instance && config) {
|
|
82
|
+
TUIBridge.instance = new TUIBridge(config);
|
|
83
|
+
}
|
|
84
|
+
return TUIBridge.instance;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Initialize bridge with config
|
|
89
|
+
*/
|
|
90
|
+
static init(config: TUIBridgeConfig): TUIBridge {
|
|
91
|
+
if (!TUIBridge.instance) {
|
|
92
|
+
TUIBridge.instance = new TUIBridge(config);
|
|
93
|
+
}
|
|
94
|
+
return TUIBridge.instance;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Update internal state and emit event
|
|
99
|
+
*/
|
|
100
|
+
updateState(newState: Partial<BridgeState>): void {
|
|
101
|
+
if (!this.state) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
this.state = { ...this.state, ...newState, timestamp: Date.now() };
|
|
105
|
+
this.emitEvent("state_update", this.state);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Initialize state (called once at startup)
|
|
110
|
+
*/
|
|
111
|
+
initState(initialState: BridgeState): void {
|
|
112
|
+
this.state = { ...initialState, timestamp: Date.now() };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get current state
|
|
117
|
+
*/
|
|
118
|
+
getState(): BridgeState | null {
|
|
119
|
+
return this.state ? { ...this.state } : null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Emit event to subscribers
|
|
124
|
+
*/
|
|
125
|
+
emitEvent<T>(type: BridgeEventType, payload: T): void {
|
|
126
|
+
const event: BridgeEvent<T> = {
|
|
127
|
+
type,
|
|
128
|
+
payload,
|
|
129
|
+
timestamp: Date.now(),
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Emit to local listeners
|
|
133
|
+
this.emit("event", event);
|
|
134
|
+
|
|
135
|
+
// Call config callback if provided
|
|
136
|
+
if (this.config.onEvent) {
|
|
137
|
+
this.config.onEvent(event as BridgeEvent);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Execute a command from external source
|
|
143
|
+
*/
|
|
144
|
+
async executeCommand(command: BridgeCommand): Promise<BridgeCommandResult> {
|
|
145
|
+
return new Promise((resolve) => {
|
|
146
|
+
this.commandQueue.push({ command, resolve });
|
|
147
|
+
this.processQueue();
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Process command queue
|
|
153
|
+
*/
|
|
154
|
+
private async processQueue(): Promise<void> {
|
|
155
|
+
if (this.isProcessing || this.commandQueue.length === 0) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.isProcessing = true;
|
|
160
|
+
const { command, resolve } = this.commandQueue.shift()!;
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const result = await this.handleCommand(command);
|
|
164
|
+
resolve(result);
|
|
165
|
+
} catch (error) {
|
|
166
|
+
resolve({
|
|
167
|
+
success: false,
|
|
168
|
+
error: error instanceof Error ? error.message : String(error),
|
|
169
|
+
});
|
|
170
|
+
} finally {
|
|
171
|
+
this.isProcessing = false;
|
|
172
|
+
this.processQueue();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Handle individual command
|
|
178
|
+
*/
|
|
179
|
+
private async handleCommand(command: BridgeCommand): Promise<BridgeCommandResult> {
|
|
180
|
+
// Most commands are handled by the TUI component itself
|
|
181
|
+
// This just validates and queues them
|
|
182
|
+
switch (command.type) {
|
|
183
|
+
case "get_state":
|
|
184
|
+
return {
|
|
185
|
+
success: true,
|
|
186
|
+
data: this.getState(),
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
case "get_screen":
|
|
190
|
+
// Screen capture is handled by TUI Bridge MCP directly via tmux
|
|
191
|
+
return {
|
|
192
|
+
success: true,
|
|
193
|
+
data: {
|
|
194
|
+
note: "Screen capture is handled by TUI Bridge MCP via tmux. Use tui_capture tool.",
|
|
195
|
+
state: this.getState(),
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
case "get_screen":
|
|
200
|
+
// Screen capture is handled by TUI Bridge MCP directly via tmux
|
|
201
|
+
return {
|
|
202
|
+
success: true,
|
|
203
|
+
data: {
|
|
204
|
+
note: "Screen capture is handled by TUI Bridge MCP via tmux. Use tui_capture tool.",
|
|
205
|
+
state: this.getState(),
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
default:
|
|
210
|
+
// Queue command for TUI to handle
|
|
211
|
+
this.emitEvent("command_executed", command);
|
|
212
|
+
return {
|
|
213
|
+
success: true,
|
|
214
|
+
data: { queued: true, command },
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Subscribe to events
|
|
221
|
+
*/
|
|
222
|
+
subscribe(subscriberId: string): boolean {
|
|
223
|
+
this.subscribers.add(subscriberId);
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Unsubscribe from events
|
|
229
|
+
*/
|
|
230
|
+
unsubscribe(subscriberId: string): boolean {
|
|
231
|
+
return this.subscribers.delete(subscriberId);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get subscriber count
|
|
236
|
+
*/
|
|
237
|
+
getSubscriberCount(): number {
|
|
238
|
+
return this.subscribers.size;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Check if bridge is enabled
|
|
243
|
+
*/
|
|
244
|
+
isEnabled(): boolean {
|
|
245
|
+
return this.config.enabled;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ============================================
|
|
249
|
+
// SCREEN BUFFER API
|
|
250
|
+
// ============================================
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Set terminal dimensions
|
|
254
|
+
*/
|
|
255
|
+
setDimensions(width: number, height: number): void {
|
|
256
|
+
this.terminalDimensions = { width, height };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Get terminal dimensions
|
|
261
|
+
*/
|
|
262
|
+
getDimensions(): { width: number; height: number } {
|
|
263
|
+
return { ...this.terminalDimensions };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Get screen buffer
|
|
268
|
+
* Returns the current screen buffer if available, or creates an empty one
|
|
269
|
+
*/
|
|
270
|
+
getScreenBuffer(): ScreenBuffer {
|
|
271
|
+
if (this.screenBuffer) {
|
|
272
|
+
return this.screenBuffer;
|
|
273
|
+
}
|
|
274
|
+
return createBuffer(this.terminalDimensions.width, this.terminalDimensions.height);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Update screen buffer with raw terminal content
|
|
279
|
+
* @param rawContent - Raw terminal output with ANSI codes
|
|
280
|
+
*/
|
|
281
|
+
updateScreenBuffer(rawContent: string): ScreenBuffer {
|
|
282
|
+
const chars = parseANSIText(rawContent);
|
|
283
|
+
this.screenBuffer = renderToScreenBuffer(
|
|
284
|
+
chars,
|
|
285
|
+
this.terminalDimensions.width,
|
|
286
|
+
this.terminalDimensions.height
|
|
287
|
+
);
|
|
288
|
+
return this.screenBuffer;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Export screen to specified format
|
|
293
|
+
* @param format - Export format (text, json, html, markdown)
|
|
294
|
+
* @param options - Export options
|
|
295
|
+
*/
|
|
296
|
+
exportScreen(format: ExportFormat, options?: ExportOptions): string {
|
|
297
|
+
const buffer = this.getScreenBuffer();
|
|
298
|
+
return doExport(buffer, format, options);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Parse raw terminal content into a full ParsedScreen
|
|
303
|
+
* @param rawContent - Raw terminal output with ANSI codes
|
|
304
|
+
*/
|
|
305
|
+
parseRawScreen(rawContent: string): ParsedScreen {
|
|
306
|
+
return parseScreen(
|
|
307
|
+
rawContent,
|
|
308
|
+
this.terminalDimensions.width,
|
|
309
|
+
this.terminalDimensions.height
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get detected UI elements from current screen buffer
|
|
315
|
+
*/
|
|
316
|
+
getUIElements(): UIElement[] {
|
|
317
|
+
const buffer = this.getScreenBuffer();
|
|
318
|
+
return detectUIElements(buffer);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Clear screen buffer
|
|
323
|
+
*/
|
|
324
|
+
clearScreenBuffer(): void {
|
|
325
|
+
this.screenBuffer = null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Cleanup
|
|
330
|
+
*/
|
|
331
|
+
destroy(): void {
|
|
332
|
+
this.removeAllListeners();
|
|
333
|
+
this.subscribers.clear();
|
|
334
|
+
this.commandQueue = [];
|
|
335
|
+
TUIBridge.instance = null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Re-export types
|
|
340
|
+
export type {
|
|
341
|
+
BridgeState,
|
|
342
|
+
BridgeEvent,
|
|
343
|
+
BridgeCommand,
|
|
344
|
+
BridgeCommandResult,
|
|
345
|
+
TUIBridgeConfig,
|
|
346
|
+
ParsedScreen,
|
|
347
|
+
ScreenBuffer,
|
|
348
|
+
ScreenCell,
|
|
349
|
+
UIElement,
|
|
350
|
+
UIElementType,
|
|
351
|
+
BridgeEventType,
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// Re-export IPC Server
|
|
355
|
+
export {
|
|
356
|
+
IPCServer,
|
|
357
|
+
createIPCServer,
|
|
358
|
+
JSONRPCRequestSchema,
|
|
359
|
+
JSONRPCNotificationSchema,
|
|
360
|
+
IPCServerConfigSchema,
|
|
361
|
+
type JSONRPCRequest,
|
|
362
|
+
type JSONRPCSuccessResponse,
|
|
363
|
+
type JSONRPCErrorResponse,
|
|
364
|
+
type JSONRPCResponse,
|
|
365
|
+
type JSONRPCNotification,
|
|
366
|
+
type IPCServerConfig,
|
|
367
|
+
type IPCServerEventType,
|
|
368
|
+
type IPCServerEvent,
|
|
369
|
+
type ClientConnection,
|
|
370
|
+
} from "./ipc.js";
|