@robota-sdk/agent-transport 3.0.0-beta.74 → 3.0.0-beta.76

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.
Files changed (197) hide show
  1. package/README.md +10 -10
  2. package/dist/node/headless/index.cjs +1 -1
  3. package/dist/node/headless/index.d.ts +1 -1
  4. package/dist/node/headless/index.js +1 -1
  5. package/dist/node/headless-OnpVk4-k.cjs +15 -0
  6. package/dist/node/{headless-D02zUEGh.js → headless-mRYilLfC.js} +2 -2
  7. package/dist/node/{headless-D02zUEGh.js.map → headless-mRYilLfC.js.map} +1 -1
  8. package/dist/node/{index-DE3-dHqw.d.ts → index-CYl7ksS6.d.ts} +12 -2
  9. package/dist/node/{index-DE3-dHqw.d.ts.map → index-CYl7ksS6.d.ts.map} +1 -1
  10. package/dist/node/{index-WKTgvhlg.d.ts → index-E8Gx4-lc.d.ts} +12 -2
  11. package/dist/node/{index-WKTgvhlg.d.ts.map → index-E8Gx4-lc.d.ts.map} +1 -1
  12. package/dist/node/index.cjs +1 -1
  13. package/dist/node/index.d.ts +2 -7
  14. package/dist/node/index.d.ts.map +1 -1
  15. package/dist/node/index.js +1 -1
  16. package/dist/node/index.js.map +1 -1
  17. package/package.json +7 -75
  18. package/src/headless/HeadlessInteractionChannel.ts +21 -1
  19. package/src/index.ts +1 -5
  20. package/src/transport-registry.ts +0 -9
  21. package/dist/node/headless-BeHAOlIM.cjs +0 -15
  22. package/dist/node/http/index.cjs +0 -1
  23. package/dist/node/http/index.d.ts +0 -2
  24. package/dist/node/http/index.js +0 -1
  25. package/dist/node/http-2Jiuflc1.js +0 -2
  26. package/dist/node/http-2Jiuflc1.js.map +0 -1
  27. package/dist/node/http-CBAvefLw.cjs +0 -1
  28. package/dist/node/index-BQLN_Lc9.d.ts +0 -78
  29. package/dist/node/index-BQLN_Lc9.d.ts.map +0 -1
  30. package/dist/node/index-BnAGE-u9.d.ts +0 -33
  31. package/dist/node/index-BnAGE-u9.d.ts.map +0 -1
  32. package/dist/node/index-BrQ4gGw0.d.ts +0 -213
  33. package/dist/node/index-BrQ4gGw0.d.ts.map +0 -1
  34. package/dist/node/index-CoeBF21y.d.ts +0 -213
  35. package/dist/node/index-CoeBF21y.d.ts.map +0 -1
  36. package/dist/node/index-DHt-2VQ-.d.ts +0 -46
  37. package/dist/node/index-DHt-2VQ-.d.ts.map +0 -1
  38. package/dist/node/index-DMwKN5Le.d.ts +0 -33
  39. package/dist/node/index-DMwKN5Le.d.ts.map +0 -1
  40. package/dist/node/index-IvYaYY6v.d.ts +0 -78
  41. package/dist/node/index-IvYaYY6v.d.ts.map +0 -1
  42. package/dist/node/index-c0M42fsA.d.ts +0 -46
  43. package/dist/node/index-c0M42fsA.d.ts.map +0 -1
  44. package/dist/node/mcp/index.cjs +0 -1
  45. package/dist/node/mcp/index.d.ts +0 -2
  46. package/dist/node/mcp/index.js +0 -1
  47. package/dist/node/mcp-BOglBJNy.cjs +0 -1
  48. package/dist/node/mcp-D3BBVK7C.js +0 -2
  49. package/dist/node/mcp-D3BBVK7C.js.map +0 -1
  50. package/dist/node/rolldown-runtime-CMqjfN_6.cjs +0 -1
  51. package/dist/node/tui/index.cjs +0 -1
  52. package/dist/node/tui/index.d.ts +0 -2
  53. package/dist/node/tui/index.js +0 -1
  54. package/dist/node/tui-Btb1q88j.js +0 -25
  55. package/dist/node/tui-Btb1q88j.js.map +0 -1
  56. package/dist/node/tui-SbUT7Zlt.cjs +0 -24
  57. package/dist/node/ws/index.cjs +0 -1
  58. package/dist/node/ws/index.d.ts +0 -2
  59. package/dist/node/ws/index.js +0 -1
  60. package/dist/node/ws-Dc2RUwVs.js +0 -2
  61. package/dist/node/ws-Dc2RUwVs.js.map +0 -1
  62. package/dist/node/ws-QNMQn5kg.cjs +0 -1
  63. package/src/http/__tests__/http-transport.test.ts +0 -55
  64. package/src/http/__tests__/routes.test.ts +0 -168
  65. package/src/http/http-transport.ts +0 -41
  66. package/src/http/index.ts +0 -4
  67. package/src/http/routes.ts +0 -152
  68. package/src/mcp/__tests__/mcp-server.test.ts +0 -66
  69. package/src/mcp/__tests__/mcp-transport.test.ts +0 -46
  70. package/src/mcp/index.ts +0 -4
  71. package/src/mcp/mcp-server.ts +0 -163
  72. package/src/mcp/mcp-transport.ts +0 -48
  73. package/src/tui/App.tsx +0 -488
  74. package/src/tui/BackgroundTaskPanel.tsx +0 -36
  75. package/src/tui/CjkTextInput.tsx +0 -199
  76. package/src/tui/ConfirmPrompt.tsx +0 -70
  77. package/src/tui/ContextWarningBanner.tsx +0 -34
  78. package/src/tui/ExecutionWorkspaceDetailPane.tsx +0 -64
  79. package/src/tui/ExecutionWorkspaceSwitcher.tsx +0 -187
  80. package/src/tui/InputArea.tsx +0 -310
  81. package/src/tui/InteractivePrompt.tsx +0 -59
  82. package/src/tui/ListPicker.tsx +0 -95
  83. package/src/tui/MenuSelect.tsx +0 -104
  84. package/src/tui/MessageList.tsx +0 -284
  85. package/src/tui/PermissionPrompt.tsx +0 -86
  86. package/src/tui/PluginTUI.tsx +0 -258
  87. package/src/tui/SessionPicker.tsx +0 -68
  88. package/src/tui/SessionStatusBar.tsx +0 -70
  89. package/src/tui/SlashAutocomplete.tsx +0 -110
  90. package/src/tui/StatusBar.tsx +0 -209
  91. package/src/tui/StreamingIndicator.tsx +0 -93
  92. package/src/tui/TextPrompt.tsx +0 -81
  93. package/src/tui/ToolCommandOutput.tsx +0 -39
  94. package/src/tui/ToolDiffBlock.tsx +0 -32
  95. package/src/tui/TransportTUI.tsx +0 -117
  96. package/src/tui/TuiInteractionChannel.ts +0 -483
  97. package/src/tui/UpdateNotice.tsx +0 -14
  98. package/src/tui/UsageSummaryEntry.tsx +0 -39
  99. package/src/tui/WaveText.tsx +0 -44
  100. package/src/tui/__tests__/InteractivePrompt.test.tsx +0 -82
  101. package/src/tui/__tests__/ListPicker.test.tsx +0 -159
  102. package/src/tui/__tests__/MenuSelect.test.tsx +0 -103
  103. package/src/tui/__tests__/PluginTUI.test.tsx +0 -167
  104. package/src/tui/__tests__/SlashAutocomplete.test.tsx +0 -140
  105. package/src/tui/__tests__/TextPrompt.test.tsx +0 -98
  106. package/src/tui/__tests__/TuiInteractionChannel.display-contract.test.ts +0 -239
  107. package/src/tui/__tests__/TuiInteractionChannel.lifecycle.test.ts +0 -297
  108. package/src/tui/__tests__/TuiInteractionChannel.requestAction.test.ts +0 -124
  109. package/src/tui/__tests__/UpdateNotice.test.tsx +0 -15
  110. package/src/tui/__tests__/abort-after-permission.test.tsx +0 -169
  111. package/src/tui/__tests__/abort-streaming-e2e.test.tsx +0 -183
  112. package/src/tui/__tests__/background-task-panel.test.tsx +0 -53
  113. package/src/tui/__tests__/background-task-row-format.test.ts +0 -59
  114. package/src/tui/__tests__/channel-factory-integration.test.ts +0 -138
  115. package/src/tui/__tests__/cjk-text-input-flow.test.ts +0 -109
  116. package/src/tui/__tests__/cjk-text-input.test.ts +0 -191
  117. package/src/tui/__tests__/command-effect-handler.test.ts +0 -127
  118. package/src/tui/__tests__/command-output-summary.test.ts +0 -95
  119. package/src/tui/__tests__/compact-event-bridge.test.ts +0 -20
  120. package/src/tui/__tests__/confirm-permission-flow.test.ts +0 -130
  121. package/src/tui/__tests__/confirm-prompt.test.tsx +0 -87
  122. package/src/tui/__tests__/execution-workspace-switcher.test.tsx +0 -110
  123. package/src/tui/__tests__/execution-workspace-view-model.test.ts +0 -93
  124. package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +0 -125
  125. package/src/tui/__tests__/input-area-flow.test.ts +0 -164
  126. package/src/tui/__tests__/message-list-rendering.test.tsx +0 -353
  127. package/src/tui/__tests__/prompt-queue.test.tsx +0 -255
  128. package/src/tui/__tests__/provider-setup-pty-e2e.test.ts +0 -233
  129. package/src/tui/__tests__/pty/pty-driver.ts +0 -135
  130. package/src/tui/__tests__/pty/tui-pty.ptytest.ts +0 -61
  131. package/src/tui/__tests__/render-channel-options.test.ts +0 -32
  132. package/src/tui/__tests__/render-markdown.test.ts +0 -72
  133. package/src/tui/__tests__/selection-flow.test.ts +0 -61
  134. package/src/tui/__tests__/session-init-poller.test.ts +0 -102
  135. package/src/tui/__tests__/session-naming.test.ts +0 -64
  136. package/src/tui/__tests__/session-switch-channel.test.tsx +0 -307
  137. package/src/tui/__tests__/slash-routing-effects.test.ts +0 -228
  138. package/src/tui/__tests__/status-activity.test.ts +0 -71
  139. package/src/tui/__tests__/status-bar.test.tsx +0 -158
  140. package/src/tui/__tests__/streaming-indicator.test.tsx +0 -137
  141. package/src/tui/__tests__/text-prompt-flow.test.ts +0 -77
  142. package/src/tui/__tests__/tui-channel-init-failure.test.ts +0 -57
  143. package/src/tui/__tests__/tui-state-manager.test.ts +0 -401
  144. package/src/tui/background-task-row-format.ts +0 -53
  145. package/src/tui/command-interaction.ts +0 -9
  146. package/src/tui/command-output-summary.ts +0 -122
  147. package/src/tui/create-default-tui-cli-adapter.ts +0 -41
  148. package/src/tui/execution-workspace-view-model.ts +0 -123
  149. package/src/tui/flows/cjk-text-input-flow.ts +0 -285
  150. package/src/tui/flows/confirm-prompt-flow.ts +0 -45
  151. package/src/tui/flows/input-area-flow.ts +0 -189
  152. package/src/tui/flows/permission-prompt-flow.ts +0 -85
  153. package/src/tui/flows/selection-flow.ts +0 -126
  154. package/src/tui/flows/session-init-poller.ts +0 -77
  155. package/src/tui/flows/text-prompt-flow.ts +0 -98
  156. package/src/tui/hooks/command-effect-handler.ts +0 -97
  157. package/src/tui/hooks/command-effect-queue.ts +0 -39
  158. package/src/tui/hooks/side-effects-types.ts +0 -35
  159. package/src/tui/hooks/useAutocomplete.ts +0 -87
  160. package/src/tui/hooks/usePluginCallbacks.ts +0 -31
  161. package/src/tui/hooks/usePluginScreenData.ts +0 -85
  162. package/src/tui/hooks/useSideEffects.ts +0 -175
  163. package/src/tui/hooks/useSlashRouting.ts +0 -118
  164. package/src/tui/hooks/useStatusLineSettings.ts +0 -37
  165. package/src/tui/hooks/useTuiChannel.ts +0 -95
  166. package/src/tui/index.ts +0 -14
  167. package/src/tui/interactions/CommandConfirm.tsx +0 -36
  168. package/src/tui/interactions/CommandPicker.tsx +0 -77
  169. package/src/tui/interactions/__tests__/CommandConfirm.test.tsx +0 -124
  170. package/src/tui/interactions/__tests__/CommandPicker.test.tsx +0 -138
  171. package/src/tui/plugin-tui-handlers.ts +0 -163
  172. package/src/tui/render-markdown.ts +0 -130
  173. package/src/tui/render.tsx +0 -117
  174. package/src/tui/session-naming.ts +0 -33
  175. package/src/tui/status-activity.ts +0 -63
  176. package/src/tui/tui-cli-adapter-context.tsx +0 -13
  177. package/src/tui/tui-cli-adapter.ts +0 -25
  178. package/src/tui/tui-state-manager.ts +0 -226
  179. package/src/tui/tui-transport.ts +0 -35
  180. package/src/tui/types.ts +0 -15
  181. package/src/tui/utils/__tests__/edit-diff.test.ts +0 -426
  182. package/src/tui/utils/__tests__/paste-detection.test.ts +0 -116
  183. package/src/tui/utils/__tests__/paste-labels.test.ts +0 -46
  184. package/src/tui/utils/__tests__/tool-call-extractor.test.ts +0 -227
  185. package/src/tui/utils/__tests__/tool-diff-summary.test.ts +0 -104
  186. package/src/tui/utils/edit-diff.ts +0 -153
  187. package/src/tui/utils/paste-labels.ts +0 -9
  188. package/src/tui/utils/tool-call-extractor.ts +0 -92
  189. package/src/tui/utils/tool-diff-summary.ts +0 -75
  190. package/src/ws/__tests__/ws-handler.test.ts +0 -409
  191. package/src/ws/__tests__/ws-transport.test.ts +0 -53
  192. package/src/ws/index.ts +0 -13
  193. package/src/ws/ws-background-messages.ts +0 -170
  194. package/src/ws/ws-handler.ts +0 -280
  195. package/src/ws/ws-protocol.ts +0 -78
  196. package/src/ws/ws-transport-configurable.ts +0 -128
  197. package/src/ws/ws-transport.ts +0 -42
@@ -1,82 +0,0 @@
1
- import React from 'react';
2
- import { render } from 'ink-testing-library';
3
- import { describe, expect, it, vi } from 'vitest';
4
- import InteractivePrompt from '../InteractivePrompt.js';
5
-
6
- const delay = () => new Promise((resolve) => setTimeout(resolve, 20));
7
-
8
- describe('InteractivePrompt', () => {
9
- it('renders a generic choice prompt and submits the selected value', async () => {
10
- const onSubmit = vi.fn();
11
- const { stdin, lastFrame } = render(
12
- <InteractivePrompt
13
- prompt={{
14
- kind: 'choice',
15
- title: 'Select item',
16
- options: [
17
- { value: 'first', label: 'First item' },
18
- { value: 'second', label: 'Second item' },
19
- ],
20
- }}
21
- onSubmit={onSubmit}
22
- onCancel={() => {}}
23
- />,
24
- );
25
-
26
- expect(lastFrame()!).toContain('Select item');
27
- expect(lastFrame()!).toContain('First item');
28
-
29
- stdin.write('\u001B[B');
30
- await delay();
31
- stdin.write('\r');
32
- await delay();
33
-
34
- expect(onSubmit).toHaveBeenCalledWith('second');
35
- });
36
-
37
- it('renders a generic text prompt and validates submitted values', async () => {
38
- const onSubmit = vi.fn();
39
- const { stdin, lastFrame } = render(
40
- <InteractivePrompt
41
- prompt={{
42
- kind: 'text',
43
- title: 'Secret',
44
- masked: true,
45
- validate: (value) => (value.length === 0 ? 'Required' : undefined),
46
- }}
47
- onSubmit={onSubmit}
48
- onCancel={() => {}}
49
- />,
50
- );
51
-
52
- stdin.write('\r');
53
- await delay();
54
- expect(onSubmit).not.toHaveBeenCalled();
55
- expect(lastFrame()!).toContain('Required');
56
-
57
- stdin.write('abc');
58
- await delay();
59
- stdin.write('\r');
60
- await delay();
61
- expect(onSubmit).toHaveBeenCalledWith('abc');
62
- });
63
-
64
- it('renders generic prompt descriptions without command-specific UI branches', () => {
65
- const { lastFrame } = render(
66
- <InteractivePrompt
67
- prompt={{
68
- kind: 'text',
69
- title: 'OpenAI API key',
70
- description:
71
- 'Setup help: API key: OpenAI API keys - https://platform.openai.com/api-keys',
72
- }}
73
- onSubmit={() => {}}
74
- onCancel={() => {}}
75
- />,
76
- );
77
-
78
- expect(lastFrame()!).toContain('OpenAI API key');
79
- expect(lastFrame()!).toContain('OpenAI API keys');
80
- expect(lastFrame()!).toContain('https://platform.openai.com/api-keys');
81
- });
82
- });
@@ -1,159 +0,0 @@
1
- import React from 'react';
2
- import { render } from 'ink-testing-library';
3
- import { Text } from 'ink';
4
- import { describe, it, expect } from 'vitest';
5
- import ListPicker from '../ListPicker.js';
6
-
7
- describe('ListPicker', () => {
8
- it('renders all items with first selected by default', () => {
9
- const items = ['Alpha', 'Beta', 'Gamma'];
10
- const { lastFrame } = render(
11
- <ListPicker
12
- items={items}
13
- renderItem={(item, isSelected) => (
14
- <Text>
15
- {isSelected ? '> ' : ' '}
16
- {item}
17
- </Text>
18
- )}
19
- onSelect={() => {}}
20
- onCancel={() => {}}
21
- />,
22
- );
23
- const frame = lastFrame()!;
24
- expect(frame).toContain('> Alpha');
25
- expect(frame).toContain(' Beta');
26
- expect(frame).toContain(' Gamma');
27
- });
28
-
29
- it('renders empty state when no items', () => {
30
- const { lastFrame } = render(
31
- <ListPicker
32
- items={[]}
33
- renderItem={(item, isSelected) => (
34
- <Text>
35
- {isSelected ? '> ' : ' '}
36
- {String(item)}
37
- </Text>
38
- )}
39
- onSelect={() => {}}
40
- onCancel={() => {}}
41
- />,
42
- );
43
- // Should render without crashing
44
- expect(lastFrame()).toBeDefined();
45
- });
46
-
47
- it('calls onSelect with item on Enter', () => {
48
- let selected = '';
49
- const items = ['Alpha', 'Beta'];
50
- const { stdin } = render(
51
- <ListPicker
52
- items={items}
53
- renderItem={(item, isSelected) => (
54
- <Text>
55
- {isSelected ? '> ' : ' '}
56
- {item}
57
- </Text>
58
- )}
59
- onSelect={(item) => {
60
- selected = item;
61
- }}
62
- onCancel={() => {}}
63
- />,
64
- );
65
- stdin.write('\r');
66
- expect(selected).toBe('Alpha');
67
- });
68
-
69
- it('navigates down with arrow key and selects', () => {
70
- let selected = '';
71
- const items = ['Alpha', 'Beta', 'Gamma'];
72
- const { stdin } = render(
73
- <ListPicker
74
- items={items}
75
- renderItem={(item, isSelected) => (
76
- <Text>
77
- {isSelected ? '> ' : ' '}
78
- {item}
79
- </Text>
80
- )}
81
- onSelect={(item) => {
82
- selected = item;
83
- }}
84
- onCancel={() => {}}
85
- />,
86
- );
87
- stdin.write('\x1B[B'); // Down arrow
88
- stdin.write('\r');
89
- expect(selected).toBe('Beta');
90
- });
91
-
92
- it('calls onCancel on Escape', async () => {
93
- const delay = (ms: number) => new Promise((r) => setTimeout(r, ms));
94
- let cancelled = false;
95
- const { stdin } = render(
96
- <ListPicker
97
- items={['Alpha']}
98
- renderItem={(item, isSelected) => (
99
- <Text>
100
- {isSelected ? '> ' : ' '}
101
- {item}
102
- </Text>
103
- )}
104
- onSelect={() => {}}
105
- onCancel={() => {
106
- cancelled = true;
107
- }}
108
- />,
109
- );
110
- stdin.write('\x1B');
111
- await delay(50);
112
- expect(cancelled).toBe(true);
113
- });
114
-
115
- it('does not go above first item', () => {
116
- const items = ['Alpha', 'Beta'];
117
- const { stdin, lastFrame } = render(
118
- <ListPicker
119
- items={items}
120
- renderItem={(item, isSelected) => (
121
- <Text>
122
- {isSelected ? '> ' : ' '}
123
- {item}
124
- </Text>
125
- )}
126
- onSelect={() => {}}
127
- onCancel={() => {}}
128
- />,
129
- );
130
- stdin.write('\x1B[A'); // Up arrow (already at 0)
131
- const frame = lastFrame()!;
132
- expect(frame).toContain('> Alpha');
133
- });
134
-
135
- it('does not go below last item', () => {
136
- let selected = '';
137
- const items = ['Alpha', 'Beta'];
138
- const { stdin } = render(
139
- <ListPicker
140
- items={items}
141
- renderItem={(item, isSelected) => (
142
- <Text>
143
- {isSelected ? '> ' : ' '}
144
- {item}
145
- </Text>
146
- )}
147
- onSelect={(item) => {
148
- selected = item;
149
- }}
150
- onCancel={() => {}}
151
- />,
152
- );
153
- stdin.write('\x1B[B'); // Down
154
- stdin.write('\x1B[B'); // Down (should stay at Beta)
155
- stdin.write('\x1B[B'); // Down (should stay at Beta)
156
- stdin.write('\r');
157
- expect(selected).toBe('Beta');
158
- });
159
- });
@@ -1,103 +0,0 @@
1
- import React from 'react';
2
- import { render } from 'ink-testing-library';
3
- import { describe, it, expect } from 'vitest';
4
- import MenuSelect from '../MenuSelect.js';
5
-
6
- const delay = (ms: number) => new Promise((r) => setTimeout(r, ms));
7
-
8
- describe('MenuSelect', () => {
9
- const items = [
10
- { label: 'Option A', value: 'a' },
11
- { label: 'Option B', value: 'b', hint: 'some hint' },
12
- { label: 'Option C', value: 'c' },
13
- ];
14
-
15
- it('renders title and all items', () => {
16
- const { lastFrame } = render(
17
- <MenuSelect title="Test Menu" items={items} onSelect={() => {}} onBack={() => {}} />,
18
- );
19
- const frame = lastFrame()!;
20
- expect(frame).toContain('Test Menu');
21
- expect(frame).toContain('Option A');
22
- expect(frame).toContain('Option B');
23
- expect(frame).toContain('Option C');
24
- });
25
-
26
- it('renders hint text when provided', () => {
27
- const { lastFrame } = render(
28
- <MenuSelect title="Test" items={items} onSelect={() => {}} onBack={() => {}} />,
29
- );
30
- expect(lastFrame()!).toContain('some hint');
31
- });
32
-
33
- it('highlights first item by default with > prefix', () => {
34
- const { lastFrame } = render(
35
- <MenuSelect title="Test" items={items} onSelect={() => {}} onBack={() => {}} />,
36
- );
37
- expect(lastFrame()!).toContain('>');
38
- });
39
-
40
- it('shows loading state', () => {
41
- const { lastFrame } = render(
42
- <MenuSelect title="Test" items={[]} onSelect={() => {}} onBack={() => {}} loading />,
43
- );
44
- expect(lastFrame()!).toContain('Loading');
45
- });
46
-
47
- it('shows error state', () => {
48
- const { lastFrame } = render(
49
- <MenuSelect title="Test" items={[]} onSelect={() => {}} onBack={() => {}} error="Failed" />,
50
- );
51
- expect(lastFrame()!).toContain('Failed');
52
- });
53
-
54
- it('calls onSelect with value on Enter', () => {
55
- let selected = '';
56
- const { stdin } = render(
57
- <MenuSelect
58
- title="Test"
59
- items={items}
60
- onSelect={(v) => {
61
- selected = v;
62
- }}
63
- onBack={() => {}}
64
- />,
65
- );
66
- stdin.write('\r');
67
- expect(selected).toBe('a');
68
- });
69
-
70
- it('calls onBack on Escape', async () => {
71
- let backed = false;
72
- const { stdin } = render(
73
- <MenuSelect
74
- title="Test"
75
- items={items}
76
- onSelect={() => {}}
77
- onBack={() => {
78
- backed = true;
79
- }}
80
- />,
81
- );
82
- stdin.write('\x1B');
83
- await delay(50);
84
- expect(backed).toBe(true);
85
- });
86
-
87
- it('navigates down with arrow key', () => {
88
- let selected = '';
89
- const { stdin } = render(
90
- <MenuSelect
91
- title="Test"
92
- items={items}
93
- onSelect={(v) => {
94
- selected = v;
95
- }}
96
- onBack={() => {}}
97
- />,
98
- );
99
- stdin.write('\x1B[B'); // Down arrow
100
- stdin.write('\r');
101
- expect(selected).toBe('b');
102
- });
103
- });
@@ -1,167 +0,0 @@
1
- // packages/agent-cli/src/ui/__tests__/PluginTUI.test.tsx
2
- import React from 'react';
3
- import { render } from 'ink-testing-library';
4
- import { describe, it, expect, vi } from 'vitest';
5
- import PluginTUI from '../PluginTUI.js';
6
- import type { ICommandPluginAdapter } from '@robota-sdk/agent-interface-transport';
7
-
8
- function mockCallbacks(): ICommandPluginAdapter {
9
- return {
10
- listInstalled: vi.fn().mockResolvedValue([]),
11
- listAvailablePlugins: vi.fn().mockResolvedValue([]),
12
- install: vi.fn().mockResolvedValue(undefined),
13
- uninstall: vi.fn().mockResolvedValue(undefined),
14
- enable: vi.fn().mockResolvedValue(undefined),
15
- disable: vi.fn().mockResolvedValue(undefined),
16
- marketplaceAdd: vi.fn().mockResolvedValue('test-marketplace'),
17
- marketplaceRemove: vi.fn().mockResolvedValue(undefined),
18
- marketplaceUpdate: vi.fn().mockResolvedValue(undefined),
19
- marketplaceList: vi.fn().mockResolvedValue([]),
20
- reloadPlugins: vi.fn().mockResolvedValue({ loadedPluginCount: 0 }),
21
- };
22
- }
23
-
24
- describe('PluginTUI', () => {
25
- it('renders main menu with Marketplace and Installed Plugins', () => {
26
- const { lastFrame } = render(<PluginTUI callbacks={mockCallbacks()} onClose={() => {}} />);
27
- const frame = lastFrame()!;
28
- expect(frame).toContain('Marketplace');
29
- expect(frame).toContain('Installed Plugins');
30
- });
31
-
32
- it('calls onClose when Escape on main menu', async () => {
33
- let closed = false;
34
- const { stdin } = render(
35
- <PluginTUI
36
- callbacks={mockCallbacks()}
37
- onClose={() => {
38
- closed = true;
39
- }}
40
- />,
41
- );
42
- stdin.write('\x1B');
43
- await new Promise((r) => setTimeout(r, 50));
44
- expect(closed).toBe(true);
45
- });
46
-
47
- it('navigates to installed plugins and shows actions', async () => {
48
- const cbs = mockCallbacks();
49
- cbs.listInstalled = vi
50
- .fn()
51
- .mockResolvedValue([{ name: 'my-plugin', description: 'A plugin', enabled: true }]);
52
- const { stdin, lastFrame } = render(<PluginTUI callbacks={cbs} onClose={() => {}} />);
53
- stdin.write('\x1B[B'); // Down to "Installed Plugins"
54
- stdin.write('\r');
55
- await new Promise((r) => setTimeout(r, 100));
56
- expect(lastFrame()!).toContain('my-plugin');
57
- });
58
-
59
- it('navigates to marketplace list on Enter', async () => {
60
- const cbs = mockCallbacks();
61
- cbs.marketplaceList = vi.fn().mockResolvedValue([{ name: 'test-mp', type: 'github' }]);
62
- const { stdin, lastFrame } = render(<PluginTUI callbacks={cbs} onClose={() => {}} />);
63
- stdin.write('\r'); // Enter on "Marketplace"
64
- await new Promise((r) => setTimeout(r, 100));
65
- const frame = lastFrame()!;
66
- expect(frame).toContain('Add Marketplace');
67
- });
68
-
69
- // Regression: arrow keys must work after navigating to a sub-screen (resolvedRef reset via key)
70
- it('arrow keys work in marketplace list after navigating from main', async () => {
71
- const cbs = mockCallbacks();
72
- cbs.marketplaceList = vi.fn().mockResolvedValue([
73
- { name: 'mp-a', type: 'github' },
74
- { name: 'mp-b', type: 'git' },
75
- ]);
76
- const { stdin, lastFrame } = render(<PluginTUI callbacks={cbs} onClose={() => {}} />);
77
- stdin.write('\r'); // Enter on "Marketplace"
78
- await new Promise((r) => setTimeout(r, 100));
79
- // Should be on marketplace-list with arrow keys working
80
- stdin.write('\x1B[B'); // Down from "Add Marketplace" to "mp-a"
81
- stdin.write('\x1B[B'); // Down to "mp-b"
82
- await new Promise((r) => setTimeout(r, 50));
83
- const frame = lastFrame()!;
84
- // "mp-b" should be highlighted (has > prefix)
85
- expect(frame).toContain('mp-b');
86
- // Select mp-b → should navigate to marketplace-action
87
- stdin.write('\r');
88
- await new Promise((r) => setTimeout(r, 50));
89
- expect(lastFrame()!).toContain('Browse plugins');
90
- });
91
-
92
- // Regression: installed plugin browse shows uninstall for already-installed plugins
93
- it('selecting installed plugin in browse shows uninstall action', async () => {
94
- const cbs = mockCallbacks();
95
- cbs.marketplaceList = vi.fn().mockResolvedValue([{ name: 'test-mp', type: 'github' }]);
96
- cbs.listAvailablePlugins = vi.fn().mockResolvedValue([
97
- { name: 'my-plugin', description: 'A plugin', installed: true },
98
- { name: 'new-plugin', description: 'Not installed', installed: false },
99
- ]);
100
- const { stdin, lastFrame } = render(<PluginTUI callbacks={cbs} onClose={() => {}} />);
101
- // Main → Marketplace
102
- stdin.write('\r');
103
- await new Promise((r) => setTimeout(r, 100));
104
- // Marketplace list → select test-mp
105
- stdin.write('\x1B[B'); // Down to "test-mp"
106
- stdin.write('\r');
107
- await new Promise((r) => setTimeout(r, 50));
108
- // Marketplace action → Browse plugins
109
- stdin.write('\r');
110
- await new Promise((r) => setTimeout(r, 100));
111
- // Browse → select installed "my-plugin"
112
- stdin.write('\r');
113
- await new Promise((r) => setTimeout(r, 50));
114
- // Should show uninstall action, not install scope
115
- expect(lastFrame()!).toContain('Uninstall');
116
- expect(lastFrame()!).not.toContain('User scope');
117
- });
118
-
119
- // Regression: uninstalled plugin in browse shows install scope selection
120
- it('selecting uninstalled plugin in browse shows install scope', async () => {
121
- const cbs = mockCallbacks();
122
- cbs.marketplaceList = vi.fn().mockResolvedValue([{ name: 'test-mp', type: 'github' }]);
123
- cbs.listAvailablePlugins = vi
124
- .fn()
125
- .mockResolvedValue([{ name: 'new-plugin', description: 'Not installed', installed: false }]);
126
- const { stdin, lastFrame } = render(<PluginTUI callbacks={cbs} onClose={() => {}} />);
127
- // Main → Marketplace
128
- stdin.write('\r');
129
- await new Promise((r) => setTimeout(r, 100));
130
- // Marketplace list → select test-mp
131
- stdin.write('\x1B[B');
132
- stdin.write('\r');
133
- await new Promise((r) => setTimeout(r, 50));
134
- // Marketplace action → Browse plugins
135
- stdin.write('\r');
136
- await new Promise((r) => setTimeout(r, 100));
137
- // Browse → select "new-plugin"
138
- stdin.write('\r');
139
- await new Promise((r) => setTimeout(r, 50));
140
- // Should show install scope, not uninstall
141
- expect(lastFrame()!).toContain('User scope');
142
- expect(lastFrame()!).toContain('Project scope');
143
- });
144
-
145
- // Regression: install passes name@marketplace format to callback
146
- it('install callback receives pluginId in name@marketplace format', async () => {
147
- const cbs = mockCallbacks();
148
- cbs.marketplaceList = vi.fn().mockResolvedValue([{ name: 'test-mp', type: 'github' }]);
149
- cbs.listAvailablePlugins = vi
150
- .fn()
151
- .mockResolvedValue([{ name: 'new-plugin', description: 'A plugin', installed: false }]);
152
- const { stdin } = render(<PluginTUI callbacks={cbs} onClose={() => {}} />);
153
- // Main → Marketplace → test-mp → Browse → new-plugin → User scope
154
- stdin.write('\r');
155
- await new Promise((r) => setTimeout(r, 100));
156
- stdin.write('\x1B[B');
157
- stdin.write('\r');
158
- await new Promise((r) => setTimeout(r, 50));
159
- stdin.write('\r'); // Browse plugins
160
- await new Promise((r) => setTimeout(r, 100));
161
- stdin.write('\r'); // new-plugin
162
- await new Promise((r) => setTimeout(r, 50));
163
- stdin.write('\r'); // User scope
164
- await new Promise((r) => setTimeout(r, 100));
165
- expect(cbs.install).toHaveBeenCalledWith('new-plugin@test-mp', 'user');
166
- }, 15000);
167
- });
@@ -1,140 +0,0 @@
1
- import React from 'react';
2
- import { render } from 'ink-testing-library';
3
- import { describe, it, expect } from 'vitest';
4
- import SlashAutocomplete from '../SlashAutocomplete.js';
5
- import type { ICommand } from '@robota-sdk/agent-interface-transport';
6
-
7
- // ink-testing-library fixes stdout.columns = 100
8
- // outer box chrome = 4 → rowWidth = 96 in tests
9
- const NAME_COL_MAX = 20;
10
-
11
- function makeCmd(name: string, description: string): ICommand {
12
- return { name, description } as ICommand;
13
- }
14
-
15
- describe('SlashAutocomplete', () => {
16
- it('renders nothing when not visible', () => {
17
- const { lastFrame } = render(
18
- <SlashAutocomplete
19
- commands={[makeCmd('help', 'Show help')]}
20
- selectedIndex={0}
21
- visible={false}
22
- />,
23
- );
24
- expect(lastFrame()).toBe('');
25
- });
26
-
27
- it('renders nothing when command list is empty', () => {
28
- const { lastFrame } = render(
29
- <SlashAutocomplete commands={[]} selectedIndex={0} visible={true} />,
30
- );
31
- expect(lastFrame()).toBe('');
32
- });
33
-
34
- it('aligns descriptions to the same column across all rows', () => {
35
- const { lastFrame } = render(
36
- <SlashAutocomplete
37
- commands={[
38
- makeCmd('go', 'Short'),
39
- makeCmd('session-persistence', 'Manage sessions'),
40
- makeCmd('help', 'Show help'),
41
- ]}
42
- selectedIndex={0}
43
- visible={true}
44
- />,
45
- );
46
- const frame = lastFrame()!;
47
- const lines = frame
48
- .split('\n')
49
- .filter((l) => l.includes('Short') || l.includes('Manage') || l.includes('Show help'));
50
- // All description texts should start at the same column index
51
- const descPositions = lines.map((l) => {
52
- // find position after the two-space separator following the name
53
- const match = / {2}\S/.exec(l.slice(l.indexOf('/') + 1));
54
- return match ? l.indexOf('/') + 1 + match.index + 2 : -1;
55
- });
56
- expect(new Set(descPositions).size).toBe(1);
57
- });
58
-
59
- it('pads short names to match the longest name in visible set', () => {
60
- const { lastFrame } = render(
61
- <SlashAutocomplete
62
- commands={[makeCmd('go', 'Run'), makeCmd('help', 'Show help')]}
63
- selectedIndex={0}
64
- visible={true}
65
- />,
66
- );
67
- const frame = lastFrame()!;
68
- // 'go' should be padded to 4 chars (length of 'help')
69
- expect(frame).toContain('/go ');
70
- });
71
-
72
- it('caps name column at NAME_COL_MAX and truncates with ellipsis', () => {
73
- const longName = 'a'.repeat(NAME_COL_MAX + 5);
74
- const { lastFrame } = render(
75
- <SlashAutocomplete
76
- commands={[makeCmd(longName, 'Description'), makeCmd('go', 'Short')]}
77
- selectedIndex={0}
78
- visible={true}
79
- />,
80
- );
81
- const frame = lastFrame()!;
82
- expect(frame).toContain('…');
83
- expect(frame).not.toContain(longName);
84
- });
85
-
86
- it('does not truncate name exactly at NAME_COL_MAX', () => {
87
- const exactName = 'b'.repeat(NAME_COL_MAX);
88
- const { lastFrame } = render(
89
- <SlashAutocomplete
90
- commands={[makeCmd(exactName, 'Desc'), makeCmd('go', 'Short')]}
91
- selectedIndex={0}
92
- visible={true}
93
- />,
94
- );
95
- const frame = lastFrame()!;
96
- expect(frame).toContain(exactName);
97
- });
98
-
99
- it('truncates long row text with ellipsis via Ink wrap', () => {
100
- const longDesc = 'X'.repeat(100);
101
- const { lastFrame } = render(
102
- <SlashAutocomplete commands={[makeCmd('cmd', longDesc)]} selectedIndex={0} visible={true} />,
103
- );
104
- expect(lastFrame()).toContain('…');
105
- expect(lastFrame()).not.toContain(longDesc);
106
- });
107
-
108
- it('handles undefined description gracefully', () => {
109
- const cmd = { name: 'cmd' } as ICommand;
110
- const { lastFrame } = render(
111
- <SlashAutocomplete commands={[cmd]} selectedIndex={0} visible={true} />,
112
- );
113
- expect(lastFrame()).toContain('cmd');
114
- });
115
-
116
- it('shows slash prefix in normal mode', () => {
117
- const { lastFrame } = render(
118
- <SlashAutocomplete
119
- commands={[makeCmd('help', 'Show help')]}
120
- selectedIndex={0}
121
- visible={true}
122
- />,
123
- );
124
- expect(lastFrame()).toContain('/help');
125
- });
126
-
127
- it('omits slash prefix in subcommand mode', () => {
128
- const { lastFrame } = render(
129
- <SlashAutocomplete
130
- commands={[makeCmd('run', 'Run task')]}
131
- selectedIndex={0}
132
- visible={true}
133
- isSubcommandMode={true}
134
- />,
135
- );
136
- const frame = lastFrame()!;
137
- expect(frame).not.toContain('/run');
138
- expect(frame).toContain('Run task');
139
- });
140
- });