@xfe-repo/cli 2.0.11 → 2.0.13
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/CHANGELOG.md +18 -0
- package/dist/app.js +12 -5
- package/dist/components/Prompts/FuzzyPathPrompt.js +9 -3
- package/dist/components/Prompts/ListPrompt.js +5 -3
- package/dist/components/Prompts/OptionList.d.ts +6 -0
- package/dist/components/Prompts/OptionList.js +14 -0
- package/dist/components/Prompts/SearchCheckboxPrompt.js +10 -3
- package/dist/components/Prompts/SearchListPrompt.js +9 -3
- package/dist/components/Prompts/index.d.ts +1 -0
- package/dist/components/Prompts/index.js +5 -5
- package/dist/hooks/use-adapters.js +4 -6
- package/dist/views/MenuView.d.ts +1 -0
- package/dist/views/MenuView.js +5 -8
- package/dist/views/PromptView.d.ts +7 -1
- package/dist/views/PromptView.js +6 -2
- package/dist/views/RunnerView.d.ts +1 -0
- package/dist/views/RunnerView.js +40 -3
- package/package.json +3 -3
- package/dist/backend/http-backend.d.ts +0 -47
- package/dist/backend/http-backend.js +0 -84
- package/dist/backend/server-task-commands.d.ts +0 -9
- package/dist/backend/server-task-commands.js +0 -262
- package/dist/command-catalog.d.ts +0 -19
- package/dist/command-catalog.js +0 -67
- package/dist/command.d.ts +0 -45
- package/dist/command.js +0 -88
- package/dist/completion-core.d.ts +0 -64
- package/dist/completion-core.js +0 -201
- package/dist/completion.d.ts +0 -21
- package/dist/completion.js +0 -127
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @xfe-repo/cli
|
|
2
2
|
|
|
3
|
+
## 2.0.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 修复窗口高度计算问题
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @xfe-repo/cli-presets@2.0.8
|
|
10
|
+
- @xfe-repo/cli-core@2.0.8
|
|
11
|
+
|
|
12
|
+
## 2.0.12
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 优化体验
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @xfe-repo/cli-presets@2.0.7
|
|
19
|
+
- @xfe-repo/cli-core@2.0.7
|
|
20
|
+
|
|
3
21
|
## 2.0.11
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/app.js
CHANGED
|
@@ -10,7 +10,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
10
10
|
* 统一使用 useKeymap 注册全局快捷键,避免多 useInput 冲突
|
|
11
11
|
*/
|
|
12
12
|
import { useState, useEffect, useCallback, useRef, useSyncExternalStore } from 'react';
|
|
13
|
-
import { Box, Static, Text, useApp } from 'ink';
|
|
13
|
+
import { Box, Static, Text, useApp, useWindowSize } from 'ink';
|
|
14
14
|
import { ConfirmInput, Spinner } from '@inkjs/ui';
|
|
15
15
|
import { InkLogger } from './adapters/ink-adapter.js';
|
|
16
16
|
import { useLogs } from './hooks/use-adapters.js';
|
|
@@ -18,6 +18,8 @@ import { KeymapProvider, useKeymap, KeymapLayer } from './hooks/use-keymap.js';
|
|
|
18
18
|
import { useSessionManager } from './hooks/use-session-manager.js';
|
|
19
19
|
import { MenuView } from './views/MenuView.js';
|
|
20
20
|
import { RunnerView } from './views/RunnerView.js';
|
|
21
|
+
const BOTTOM_RESERVED_ROWS = 1;
|
|
22
|
+
const MIN_VIEW_ROWS = 1;
|
|
21
23
|
// ============================================================
|
|
22
24
|
// App
|
|
23
25
|
// ============================================================
|
|
@@ -163,6 +165,8 @@ function AppInner({ runner, toast, scriptName }) {
|
|
|
163
165
|
const emptyLoggerRef = useRef(new InkLogger());
|
|
164
166
|
const activeLogger = activeSession?.logger ?? emptyLoggerRef.current;
|
|
165
167
|
const { entries: logEntries } = useLogs(activeLogger);
|
|
168
|
+
const { rows: terminalRows } = useWindowSize();
|
|
169
|
+
const availableViewRows = resolveAvailableViewRows({ terminalRows });
|
|
166
170
|
// 当 session 被移除但 view 仍为 session 时,自动回退到菜单(直接命令模式不回退,等待 doExit 完成)
|
|
167
171
|
useEffect(() => {
|
|
168
172
|
if (scriptName)
|
|
@@ -172,16 +176,16 @@ function AppInner({ runner, toast, scriptName }) {
|
|
|
172
176
|
}
|
|
173
177
|
}, [view, sessionManager.sessions, scriptName]);
|
|
174
178
|
// ── 渲染 ──
|
|
175
|
-
const renderView = () => {
|
|
179
|
+
const renderView = (availableRows) => {
|
|
176
180
|
switch (view.type) {
|
|
177
181
|
case 'loading':
|
|
178
182
|
return _jsx(Spinner, { label: "\u521D\u59CB\u5316\u63D2\u4EF6..." });
|
|
179
183
|
case 'menu':
|
|
180
|
-
return (_jsx(MenuView, { projectName: projectDisplayName, commandsWithSource: runner.getCommandsWithSource(), activeSessions: sessionManager.activeSessions, runningCommandNames: sessionManager.runningScriptNames, badges: badges, storeSnapshot: storeSnapshot, toast: toast, onSelect: handleSelect, onFocusSession: handleFocusSession, onExit: handleExit, onUserInput: handleUserInput }));
|
|
184
|
+
return (_jsx(MenuView, { availableRows: availableRows, projectName: projectDisplayName, commandsWithSource: runner.getCommandsWithSource(), activeSessions: sessionManager.activeSessions, runningCommandNames: sessionManager.runningScriptNames, badges: badges, storeSnapshot: storeSnapshot, toast: toast, onSelect: handleSelect, onFocusSession: handleFocusSession, onExit: handleExit, onUserInput: handleUserInput }));
|
|
181
185
|
case 'session': {
|
|
182
186
|
if (!activeSession)
|
|
183
187
|
return null;
|
|
184
|
-
return (_jsx(RunnerView, { projectName: projectDisplayName, scriptName: activeSession.scriptName, startTime: activeSession.startTime, endTime: activeSession.endTime, prompt: activeSession.prompt, spinner: activeSession.spinner, badges: badges, storeSnapshot: storeSnapshot, hasError: activeSession.status === 'error', isCompleted: activeSession.status === 'completed', isSingleCommand: !!scriptName, isReadOnly: activeSession.isReadOnly }));
|
|
188
|
+
return (_jsx(RunnerView, { availableRows: availableRows, projectName: projectDisplayName, scriptName: activeSession.scriptName, startTime: activeSession.startTime, endTime: activeSession.endTime, prompt: activeSession.prompt, spinner: activeSession.spinner, badges: badges, storeSnapshot: storeSnapshot, hasError: activeSession.status === 'error', isCompleted: activeSession.status === 'completed', isSingleCommand: !!scriptName, isReadOnly: activeSession.isReadOnly }));
|
|
185
189
|
}
|
|
186
190
|
case 'exit-confirm':
|
|
187
191
|
return (_jsxs(Box, { flexDirection: "column", gap: 1, borderStyle: "round", borderColor: "yellow", paddingX: 1, children: [_jsxs(Text, { color: "yellow", bold: true, children: ["\u5F53\u524D\u6709 ", sessionManager.runningSessions.length, " \u4E2A\u4EFB\u52A1\u6B63\u5728\u8FD0\u884C"] }), _jsx(Text, { children: "\u9000\u51FA\u5C06\u7EC8\u6B62\u6240\u6709\u8FD0\u884C\u4E2D\u7684\u4EFB\u52A1\uFF0C\u786E\u8BA4\u9000\u51FA\uFF1F(\u56DE\u8F66\u76F4\u63A5\u9000\u51FA)" }), _jsx(ConfirmInput, { onConfirm: doExit, onCancel: () => setView({ type: 'menu' }) })] }));
|
|
@@ -189,7 +193,7 @@ function AppInner({ runner, toast, scriptName }) {
|
|
|
189
193
|
return (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["ERROR: ", view.message] }) }));
|
|
190
194
|
}
|
|
191
195
|
};
|
|
192
|
-
return (_jsxs(_Fragment, { children: [_jsx(Static, { items: logEntries, children: (entry, i) => _jsx(LogLine, { entry: entry }, `${entry.timestamp}-${i}`) }), renderView()] }));
|
|
196
|
+
return (_jsxs(_Fragment, { children: [_jsx(Static, { items: logEntries, children: (entry, i) => _jsx(LogLine, { entry: entry }, `${entry.timestamp}-${i}`) }), _jsx(Box, { flexDirection: "column", maxHeight: availableViewRows, overflow: "hidden", children: renderView(availableViewRows) })] }));
|
|
193
197
|
}
|
|
194
198
|
// ─── Render Helpers ─────────────────────────────────────────
|
|
195
199
|
function LogLine({ entry }) {
|
|
@@ -204,4 +208,7 @@ function LogLine({ entry }) {
|
|
|
204
208
|
return _jsx(Text, { color: color, children: entry.message });
|
|
205
209
|
return _jsx(Text, { children: entry.message });
|
|
206
210
|
}
|
|
211
|
+
function resolveAvailableViewRows({ terminalRows }) {
|
|
212
|
+
return Math.max(MIN_VIEW_ROWS, terminalRows - BOTTOM_RESERVED_ROWS);
|
|
213
|
+
}
|
|
207
214
|
//# sourceMappingURL=app.js.map
|
|
@@ -8,9 +8,15 @@ import fs from 'fs';
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import { useKeymap, KeymapLayer } from '../../hooks/use-keymap.js';
|
|
10
10
|
import { useSelectableList } from './use-selectable-list.js';
|
|
11
|
-
import { OptionList, SearchBar } from './OptionList.js';
|
|
12
|
-
|
|
11
|
+
import { OptionList, SearchBar, resolveOptionPageSize } from './OptionList.js';
|
|
12
|
+
const SEARCH_PROMPT_RESERVED_ROWS = 3;
|
|
13
|
+
export const FuzzyPathPrompt = memo(function FuzzyPathPrompt({ question, onSubmit, maxContentRows }) {
|
|
13
14
|
const options = useMemo(() => collectPaths(question.rootPath || '.', question.itemType || 'any', question.excludePath).map((p) => ({ key: p, label: p, value: p })), [question.rootPath, question.itemType, question.excludePath]);
|
|
15
|
+
const pageSize = resolveOptionPageSize({
|
|
16
|
+
pageSize: question.pageSize,
|
|
17
|
+
maxRows: maxContentRows,
|
|
18
|
+
reservedRows: SEARCH_PROMPT_RESERVED_ROWS,
|
|
19
|
+
});
|
|
14
20
|
const { searchText, filtered, selectedIndex, selectedItem } = useSelectableList({
|
|
15
21
|
items: options,
|
|
16
22
|
searchable: true,
|
|
@@ -23,7 +29,7 @@ export const FuzzyPathPrompt = memo(function FuzzyPathPrompt({ question, onSubmi
|
|
|
23
29
|
},
|
|
24
30
|
layer: KeymapLayer.Navigation,
|
|
25
31
|
});
|
|
26
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SearchBar, { text: searchText }), _jsx(OptionList, { options: filtered, selectedIndex: selectedIndex, pageSize:
|
|
32
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SearchBar, { text: searchText }), _jsx(OptionList, { options: filtered, selectedIndex: selectedIndex, pageSize: pageSize }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u8F93\u5165\u641C\u7D22 \u2191\u2193 \u9009\u62E9 \u21B5 \u786E\u8BA4" }) })] }));
|
|
27
33
|
});
|
|
28
34
|
// ─── Helpers ────────────────────────────────────────────────
|
|
29
35
|
function collectPaths(rootPath, itemType, excludePath) {
|
|
@@ -6,11 +6,13 @@ import { memo } from 'react';
|
|
|
6
6
|
import { Box, Text } from 'ink';
|
|
7
7
|
import { useKeymap, KeymapLayer } from '../../hooks/use-keymap.js';
|
|
8
8
|
import { useSelectableList } from './use-selectable-list.js';
|
|
9
|
-
import { OptionList } from './OptionList.js';
|
|
9
|
+
import { OptionList, resolveOptionPageSize } from './OptionList.js';
|
|
10
10
|
import { normalizeChoices } from './index.js';
|
|
11
|
-
|
|
11
|
+
const LIST_PROMPT_RESERVED_ROWS = 2;
|
|
12
|
+
export const ListPrompt = memo(function ListPrompt({ question, onSubmit, maxContentRows }) {
|
|
12
13
|
const options = normalizeChoices(question.choices);
|
|
13
14
|
const defaultIndex = question.default != null ? options.findIndex((o) => o.value === question.default) : -1;
|
|
15
|
+
const pageSize = resolveOptionPageSize({ pageSize: question.pageSize, maxRows: maxContentRows, reservedRows: LIST_PROMPT_RESERVED_ROWS });
|
|
14
16
|
const { filtered, selectedIndex, selectedItem } = useSelectableList({ items: options, defaultIndex });
|
|
15
17
|
useKeymap({
|
|
16
18
|
key: 'return',
|
|
@@ -20,6 +22,6 @@ export const ListPrompt = memo(function ListPrompt({ question, onSubmit }) {
|
|
|
20
22
|
},
|
|
21
23
|
layer: KeymapLayer.Navigation,
|
|
22
24
|
});
|
|
23
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(OptionList, { options: filtered, selectedIndex: selectedIndex, pageSize:
|
|
25
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(OptionList, { options: filtered, selectedIndex: selectedIndex, pageSize: pageSize }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191\u2193 \u9009\u62E9 \u21B5 \u786E\u8BA4" }) })] }));
|
|
24
26
|
});
|
|
25
27
|
//# sourceMappingURL=ListPrompt.js.map
|
|
@@ -13,10 +13,16 @@ interface OptionListProps {
|
|
|
13
13
|
readonly pageSize: number;
|
|
14
14
|
readonly mode?: 'single' | 'checkbox';
|
|
15
15
|
}
|
|
16
|
+
interface ResolveOptionPageSizeOptions {
|
|
17
|
+
readonly pageSize?: number;
|
|
18
|
+
readonly maxRows?: number;
|
|
19
|
+
readonly reservedRows?: number;
|
|
20
|
+
}
|
|
16
21
|
export declare const OptionList: import("react").NamedExoticComponent<OptionListProps>;
|
|
17
22
|
export declare const SearchBar: import("react").NamedExoticComponent<{
|
|
18
23
|
readonly text: string;
|
|
19
24
|
}>;
|
|
25
|
+
export declare function resolveOptionPageSize({ pageSize, maxRows, reservedRows }: ResolveOptionPageSizeOptions): number;
|
|
20
26
|
export declare function computeWindow<T>(items: readonly T[], selectedIndex: number, pageSize: number): {
|
|
21
27
|
visibleItems: T[];
|
|
22
28
|
startIndex: number;
|
|
@@ -27,6 +27,11 @@ const CHECKBOX_OPTION_PREFIX_WIDTH = 4;
|
|
|
27
27
|
const FOCUSED_MARK = '❯';
|
|
28
28
|
const EMPTY_MARK = ' ';
|
|
29
29
|
const DESCRIPTION_COLOR = 'gray';
|
|
30
|
+
const DEFAULT_OPTION_PAGE_SIZE = 10;
|
|
31
|
+
const MIN_OPTION_PAGE_SIZE = 1;
|
|
32
|
+
const OPTION_LIST_TOP_MARGIN_ROWS = 1;
|
|
33
|
+
const OPTION_CONTENT_ROW_HEIGHT = 2;
|
|
34
|
+
const SCROLL_INDICATOR_ROWS = 2;
|
|
30
35
|
const OptionPrefix = memo(function OptionPrefix({ mode, checked, isFocused, }) {
|
|
31
36
|
const prefixWidth = mode === 'checkbox' ? CHECKBOX_OPTION_PREFIX_WIDTH : OPTION_PREFIX_WIDTH;
|
|
32
37
|
return (_jsxs(Box, { width: prefixWidth, flexShrink: 0, children: [_jsx(Text, { color: isFocused ? 'cyan' : undefined, children: isFocused ? FOCUSED_MARK : EMPTY_MARK }), mode === 'checkbox' && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(CheckboxIcon, { checked: checked })] })), _jsx(Text, { children: " " })] }));
|
|
@@ -38,6 +43,15 @@ const CheckboxIcon = memo(function CheckboxIcon({ checked }) {
|
|
|
38
43
|
return _jsx(Text, { color: checked ? 'green' : 'gray', children: checked ? '◉' : '○' });
|
|
39
44
|
});
|
|
40
45
|
// ─── Helpers ────────────────────────────────────────────────
|
|
46
|
+
export function resolveOptionPageSize({ pageSize, maxRows, reservedRows = 0 }) {
|
|
47
|
+
const requestedPageSize = pageSize ?? DEFAULT_OPTION_PAGE_SIZE;
|
|
48
|
+
if (maxRows == null)
|
|
49
|
+
return requestedPageSize;
|
|
50
|
+
const availableOptionRows = maxRows - reservedRows - OPTION_LIST_TOP_MARGIN_ROWS - SCROLL_INDICATOR_ROWS;
|
|
51
|
+
const safeOptionRows = Math.max(OPTION_CONTENT_ROW_HEIGHT, availableOptionRows);
|
|
52
|
+
const heightLimitedPageSize = Math.floor(safeOptionRows / OPTION_CONTENT_ROW_HEIGHT);
|
|
53
|
+
return Math.max(MIN_OPTION_PAGE_SIZE, Math.min(requestedPageSize, heightLimitedPageSize));
|
|
54
|
+
}
|
|
41
55
|
export function computeWindow(items, selectedIndex, pageSize) {
|
|
42
56
|
const totalItems = items.length;
|
|
43
57
|
const windowSize = Math.min(pageSize, totalItems);
|
|
@@ -6,9 +6,11 @@ import { memo, useState, useMemo } from 'react';
|
|
|
6
6
|
import { Box, Text } from 'ink';
|
|
7
7
|
import { useKeymap, KeymapLayer } from '../../hooks/use-keymap.js';
|
|
8
8
|
import { useSelectableList } from './use-selectable-list.js';
|
|
9
|
-
import { OptionList, SearchBar } from './OptionList.js';
|
|
9
|
+
import { OptionList, SearchBar, resolveOptionPageSize } from './OptionList.js';
|
|
10
10
|
import { normalizeChoices } from './index.js';
|
|
11
|
-
|
|
11
|
+
const SEARCH_PROMPT_RESERVED_ROWS = 3;
|
|
12
|
+
const ERROR_MESSAGE_ROWS = 2;
|
|
13
|
+
export const SearchCheckboxPrompt = memo(function SearchCheckboxPrompt({ question, onSubmit, maxContentRows }) {
|
|
12
14
|
const defaultSet = useMemo(() => {
|
|
13
15
|
if (question.default == null)
|
|
14
16
|
return new Set();
|
|
@@ -19,6 +21,11 @@ export const SearchCheckboxPrompt = memo(function SearchCheckboxPrompt({ questio
|
|
|
19
21
|
const defaultIndex = firstDefault != null ? allOptions.findIndex((o) => o.value === firstDefault) : -1;
|
|
20
22
|
const [items, setItems] = useState(allOptions);
|
|
21
23
|
const [error, setError] = useState('');
|
|
24
|
+
const pageSize = resolveOptionPageSize({
|
|
25
|
+
pageSize: question.pageSize,
|
|
26
|
+
maxRows: maxContentRows,
|
|
27
|
+
reservedRows: SEARCH_PROMPT_RESERVED_ROWS + (error ? ERROR_MESSAGE_ROWS : 0),
|
|
28
|
+
});
|
|
22
29
|
const { searchText, filtered, selectedIndex } = useSelectableList({ items, defaultIndex, searchable: true });
|
|
23
30
|
const toggleItem = () => {
|
|
24
31
|
const target = filtered[selectedIndex];
|
|
@@ -45,6 +52,6 @@ export const SearchCheckboxPrompt = memo(function SearchCheckboxPrompt({ questio
|
|
|
45
52
|
useKeymap({ key: ' ', handler: toggleItem, layer: KeymapLayer.Navigation });
|
|
46
53
|
useKeymap({ key: 'ctrl+o', handler: toggleAll, layer: KeymapLayer.Navigation });
|
|
47
54
|
useKeymap({ key: 'return', handler: submit, layer: KeymapLayer.Navigation });
|
|
48
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SearchBar, { text: searchText }), _jsx(OptionList, { mode: "checkbox", options: filtered, selectedIndex: selectedIndex, pageSize:
|
|
55
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SearchBar, { text: searchText }), _jsx(OptionList, { mode: "checkbox", options: filtered, selectedIndex: selectedIndex, pageSize: pageSize }), error && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "red", children: error }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u8F93\u5165\u641C\u7D22 \u2191\u2193 \u9009\u62E9 \u7A7A\u683C \u5207\u6362 Ctrl+O \u5168\u9009 \u21B5 \u786E\u8BA4" }) })] }));
|
|
49
56
|
});
|
|
50
57
|
//# sourceMappingURL=SearchCheckboxPrompt.js.map
|
|
@@ -6,11 +6,17 @@ import { memo } from 'react';
|
|
|
6
6
|
import { Box, Text } from 'ink';
|
|
7
7
|
import { useKeymap, KeymapLayer } from '../../hooks/use-keymap.js';
|
|
8
8
|
import { useSelectableList } from './use-selectable-list.js';
|
|
9
|
-
import { OptionList, SearchBar } from './OptionList.js';
|
|
9
|
+
import { OptionList, SearchBar, resolveOptionPageSize } from './OptionList.js';
|
|
10
10
|
import { normalizeChoices } from './index.js';
|
|
11
|
-
|
|
11
|
+
const SEARCH_PROMPT_RESERVED_ROWS = 3;
|
|
12
|
+
export const SearchListPrompt = memo(function SearchListPrompt({ question, onSubmit, maxContentRows }) {
|
|
12
13
|
const options = normalizeChoices(question.choices);
|
|
13
14
|
const defaultIndex = question.default != null ? options.findIndex((o) => o.value === question.default) : -1;
|
|
15
|
+
const pageSize = resolveOptionPageSize({
|
|
16
|
+
pageSize: question.pageSize,
|
|
17
|
+
maxRows: maxContentRows,
|
|
18
|
+
reservedRows: SEARCH_PROMPT_RESERVED_ROWS,
|
|
19
|
+
});
|
|
14
20
|
const { searchText, filtered, selectedIndex, selectedItem } = useSelectableList({
|
|
15
21
|
items: options,
|
|
16
22
|
defaultIndex,
|
|
@@ -24,6 +30,6 @@ export const SearchListPrompt = memo(function SearchListPrompt({ question, onSub
|
|
|
24
30
|
},
|
|
25
31
|
layer: KeymapLayer.Navigation,
|
|
26
32
|
});
|
|
27
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SearchBar, { text: searchText }), _jsx(OptionList, { options: filtered, selectedIndex: selectedIndex, pageSize:
|
|
33
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SearchBar, { text: searchText }), _jsx(OptionList, { options: filtered, selectedIndex: selectedIndex, pageSize: pageSize }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u8F93\u5165\u641C\u7D22 \u2191\u2193 \u9009\u62E9 \u21B5 \u786E\u8BA4" }) })] }));
|
|
28
34
|
});
|
|
29
35
|
//# sourceMappingURL=SearchListPrompt.js.map
|
|
@@ -3,6 +3,7 @@ import type { SelectOption } from './use-selectable-list.js';
|
|
|
3
3
|
export interface PromptViewProps {
|
|
4
4
|
readonly question: PromptQuestion;
|
|
5
5
|
readonly onSubmit: (value: any) => void;
|
|
6
|
+
readonly maxContentRows?: number;
|
|
6
7
|
}
|
|
7
8
|
export { InputPrompt } from './InputPrompt.js';
|
|
8
9
|
export { ConfirmPrompt } from './ConfirmPrompt.js';
|
|
@@ -16,19 +16,19 @@ export { ListPrompt } from './ListPrompt.js';
|
|
|
16
16
|
export { SearchListPrompt } from './SearchListPrompt.js';
|
|
17
17
|
export { SearchCheckboxPrompt } from './SearchCheckboxPrompt.js';
|
|
18
18
|
export { FuzzyPathPrompt } from './FuzzyPathPrompt.js';
|
|
19
|
-
export const Prompt = memo(function Prompt({ question, onSubmit }) {
|
|
19
|
+
export const Prompt = memo(function Prompt({ question, onSubmit, maxContentRows }) {
|
|
20
20
|
switch (question.type) {
|
|
21
21
|
case 'confirm':
|
|
22
22
|
return _jsx(ConfirmPrompt, { question: question, onSubmit: onSubmit });
|
|
23
23
|
case 'search-list':
|
|
24
|
-
return _jsx(SearchListPrompt, { question: question, onSubmit: onSubmit });
|
|
24
|
+
return _jsx(SearchListPrompt, { question: question, onSubmit: onSubmit, maxContentRows: maxContentRows });
|
|
25
25
|
case 'search-checkbox':
|
|
26
|
-
return _jsx(SearchCheckboxPrompt, { question: question, onSubmit: onSubmit });
|
|
26
|
+
return _jsx(SearchCheckboxPrompt, { question: question, onSubmit: onSubmit, maxContentRows: maxContentRows });
|
|
27
27
|
case 'fuzzy-path':
|
|
28
|
-
return _jsx(FuzzyPathPrompt, { question: question, onSubmit: onSubmit });
|
|
28
|
+
return _jsx(FuzzyPathPrompt, { question: question, onSubmit: onSubmit, maxContentRows: maxContentRows });
|
|
29
29
|
case 'list':
|
|
30
30
|
case 'rawlist':
|
|
31
|
-
return _jsx(ListPrompt, { question: question, onSubmit: onSubmit });
|
|
31
|
+
return _jsx(ListPrompt, { question: question, onSubmit: onSubmit, maxContentRows: maxContentRows });
|
|
32
32
|
case 'input':
|
|
33
33
|
default:
|
|
34
34
|
return _jsx(InputPrompt, { question: question, onSubmit: onSubmit });
|
|
@@ -17,20 +17,18 @@ import { useState, useEffect, useCallback, useRef } from 'react';
|
|
|
17
17
|
*/
|
|
18
18
|
const FLUSH_DELAY_MS = 16;
|
|
19
19
|
export function useLogs(logger) {
|
|
20
|
-
const [entries, setEntries] = useState(
|
|
20
|
+
const [entries, setEntries] = useState([]);
|
|
21
21
|
const bufferRef = useRef([]);
|
|
22
22
|
const timerRef = useRef(null);
|
|
23
23
|
const pendingRef = useRef([]);
|
|
24
24
|
useEffect(() => {
|
|
25
25
|
// 恢复上一个 logger 未刷新的缓冲条目(解决 session 关闭时日志丢失的竞态)
|
|
26
26
|
const pending = pendingRef.current;
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
const unrendered = logger.getUnrenderedEntries();
|
|
28
|
+
if (pending.length > 0 || unrendered.length > 0) {
|
|
29
|
+
setEntries((prev) => [...prev, ...pending, ...unrendered]);
|
|
29
30
|
pendingRef.current = [];
|
|
30
31
|
}
|
|
31
|
-
else {
|
|
32
|
-
setEntries(logger.getUnrenderedEntries());
|
|
33
|
-
}
|
|
34
32
|
const flushBuffer = () => {
|
|
35
33
|
timerRef.current = null;
|
|
36
34
|
const batch = bufferRef.current;
|
package/dist/views/MenuView.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { CommandWithSource, StatusBadge, PluginStoreSnapshot } from '@xfe-r
|
|
|
8
8
|
import type { RunSession } from '../hooks/use-session-manager.js';
|
|
9
9
|
import type { InkToast } from '../adapters/ink-adapter.js';
|
|
10
10
|
interface MenuViewProps {
|
|
11
|
+
readonly availableRows: number;
|
|
11
12
|
readonly projectName: string;
|
|
12
13
|
readonly commandsWithSource: CommandWithSource[];
|
|
13
14
|
readonly activeSessions: [string, RunSession][];
|
package/dist/views/MenuView.js
CHANGED
|
@@ -6,7 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
6
6
|
* 所有交互逻辑已下沉到子组件和 useKeymap 系统
|
|
7
7
|
*/
|
|
8
8
|
import { useState, useMemo, useEffect, useCallback, memo } from 'react';
|
|
9
|
-
import { Box, Text
|
|
9
|
+
import { Box, Text } from 'ink';
|
|
10
10
|
import Link from 'ink-link';
|
|
11
11
|
import { resolveBadgeProp } from '@xfe-repo/cli-core';
|
|
12
12
|
import { buildCommandCatalog } from '../completion/catalog.js';
|
|
@@ -23,17 +23,14 @@ const LOGO_HEIGHT = 7;
|
|
|
23
23
|
const BASE_CHROME_HEIGHT = 12;
|
|
24
24
|
const TASK_GRID_CHROME_HEIGHT = 6;
|
|
25
25
|
const MIN_SUGGESTIONS = 3;
|
|
26
|
-
|
|
27
|
-
const BOTTOM_RESERVE = 0;
|
|
26
|
+
const MIN_VISIBLE_SUGGESTIONS = 1;
|
|
28
27
|
// ─── Component ──────────────────────────────────────────────
|
|
29
|
-
export const MenuView = memo(function MenuView({ projectName, commandsWithSource, activeSessions, runningCommandNames, badges, storeSnapshot, toast, onSelect, onFocusSession, onExit, onUserInput, }) {
|
|
28
|
+
export const MenuView = memo(function MenuView({ availableRows, projectName, commandsWithSource, activeSessions, runningCommandNames, badges, storeSnapshot, toast, onSelect, onFocusSession, onExit, onUserInput, }) {
|
|
30
29
|
const hasActiveSessions = activeSessions.length > 0;
|
|
31
30
|
const [focusArea, setFocusArea] = useState('commands');
|
|
32
|
-
// ── 终端高度自适应 ──
|
|
33
|
-
const { rows: terminalRows } = useWindowSize();
|
|
34
31
|
const chromeHeight = BASE_CHROME_HEIGHT + (hasActiveSessions ? TASK_GRID_CHROME_HEIGHT : 0);
|
|
35
|
-
const showLogo =
|
|
36
|
-
const maxSuggestions = Math.max(
|
|
32
|
+
const showLogo = availableRows >= chromeHeight + LOGO_HEIGHT + MIN_SUGGESTIONS;
|
|
33
|
+
const maxSuggestions = Math.max(MIN_VISIBLE_SUGGESTIONS, availableRows - chromeHeight - (showLogo ? LOGO_HEIGHT : 0));
|
|
37
34
|
const effectiveFocus = hasActiveSessions ? focusArea : 'commands';
|
|
38
35
|
// 单一共享定时器驱动所有任务卡片的时间刷新
|
|
39
36
|
useElapsedTime(hasActiveSessions ? 1 : null);
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 根据 PromptQuestion 类型路由到对应的 Prompt 组件
|
|
5
5
|
*/
|
|
6
|
-
import type { PromptViewProps } from '../components/Prompts/index.js';
|
|
6
|
+
import type { PromptViewProps as PromptProps } from '../components/Prompts/index.js';
|
|
7
|
+
interface PromptViewProps {
|
|
8
|
+
readonly question: PromptProps['question'];
|
|
9
|
+
readonly onSubmit: PromptProps['onSubmit'];
|
|
10
|
+
readonly maxRows?: number;
|
|
11
|
+
}
|
|
7
12
|
export declare const PromptView: import("react").NamedExoticComponent<PromptViewProps>;
|
|
13
|
+
export {};
|
|
8
14
|
//# sourceMappingURL=PromptView.d.ts.map
|
package/dist/views/PromptView.js
CHANGED
|
@@ -7,8 +7,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
import { memo } from 'react';
|
|
8
8
|
import { Box, Text } from 'ink';
|
|
9
9
|
import { Prompt } from '../components/Prompts/index.js';
|
|
10
|
+
// ─── Constants ──────────────────────────────────────────────
|
|
11
|
+
const PROMPT_VIEW_CHROME_ROWS = 3;
|
|
12
|
+
const MIN_PROMPT_CONTENT_ROWS = 1;
|
|
10
13
|
// ─── Main Component ─────────────────────────────────────────
|
|
11
|
-
export const PromptView = memo(function PromptView({ question, onSubmit }) {
|
|
12
|
-
|
|
14
|
+
export const PromptView = memo(function PromptView({ question, onSubmit, maxRows }) {
|
|
15
|
+
const maxContentRows = maxRows == null ? undefined : Math.max(MIN_PROMPT_CONTENT_ROWS, maxRows - PROMPT_VIEW_CHROME_ROWS);
|
|
16
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, maxHeight: maxRows, overflow: "hidden", children: [_jsxs(Text, { bold: true, wrap: "truncate-end", children: [_jsx(Text, { color: "green", children: "?" }), " ", question.message, question.default != null && question.type !== 'confirm' && _jsxs(Text, { dimColor: true, children: [" (", String(question.default), ")"] })] }), _jsx(Prompt, { question: question, onSubmit: onSubmit, maxContentRows: maxContentRows })] }));
|
|
13
17
|
});
|
|
14
18
|
//# sourceMappingURL=PromptView.js.map
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { InkPrompt, InkSpinner } from '../adapters/ink-adapter.js';
|
|
5
5
|
import type { StatusBadge, PluginStoreSnapshot } from '@xfe-repo/cli-core';
|
|
6
6
|
interface RunnerViewProps {
|
|
7
|
+
readonly availableRows: number;
|
|
7
8
|
readonly projectName: string;
|
|
8
9
|
readonly scriptName: string;
|
|
9
10
|
readonly startTime: number;
|
package/dist/views/RunnerView.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* @xfe-repo/cli - 运行视图
|
|
4
4
|
*/
|
|
@@ -9,11 +9,48 @@ import { PromptView } from './PromptView.js';
|
|
|
9
9
|
import { Logo } from '../components/Logo.js';
|
|
10
10
|
import { SpinnerStatus } from '../components/SpinnerStatus.js';
|
|
11
11
|
import { StatusBar } from '../components/StatusBar.js';
|
|
12
|
-
|
|
12
|
+
// ─── Constants ──────────────────────────────────────────────
|
|
13
|
+
const LOGO_HEIGHT = 7;
|
|
14
|
+
const STATUS_BAR_HEIGHT = 3;
|
|
15
|
+
const SPINNER_STATUS_HEIGHT = 1;
|
|
16
|
+
const CONTENT_GAP_HEIGHT = 1;
|
|
17
|
+
const MIN_PROMPT_ROWS = 1;
|
|
18
|
+
const PROMPT_WITH_LOGO_MIN_ROWS = 8;
|
|
19
|
+
export const RunnerView = memo(function RunnerView({ availableRows, projectName, scriptName, startTime, endTime, prompt, spinner, badges, storeSnapshot, hasError, isCompleted, isSingleCommand, isReadOnly, }) {
|
|
13
20
|
const { request, complete } = usePrompt(prompt);
|
|
14
21
|
const spinnerState = useSpinnerState(spinner);
|
|
15
|
-
|
|
22
|
+
const hasPrompt = !isReadOnly && !!request;
|
|
23
|
+
const layout = resolveRunnerLayout({
|
|
24
|
+
availableRows,
|
|
25
|
+
hasPrompt,
|
|
26
|
+
hasSpinnerStatus: isSpinnerStatusVisible(spinnerState),
|
|
27
|
+
hasStatusBar: !isSingleCommand,
|
|
28
|
+
});
|
|
29
|
+
return (_jsxs(Box, { flexDirection: "column", children: [layout.showLogo && _jsx(Logo, {}), _jsxs(Box, { flexDirection: "column", gap: 1, overflowX: "hidden", children: [_jsx(SpinnerStatus, { state: spinnerState }), hasPrompt && _jsx(PromptView, { question: request.question, maxRows: layout.promptRows, onSubmit: complete }, request.id)] }), !isSingleCommand && (_jsxs(Box, { gap: 1, borderStyle: "round", borderColor: isReadOnly ? 'blue' : 'cyan', paddingX: 1, overflowX: "hidden", children: [_jsx(StatusBar, { projectName: projectName, scriptName: scriptName, startTime: startTime, endTime: endTime, badges: badges, storeSnapshot: storeSnapshot }), _jsx(StatusHint, { isReadOnly: isReadOnly, hasError: hasError, isCompleted: isCompleted })] }))] }));
|
|
16
30
|
});
|
|
31
|
+
// ─── Layout Helpers ────────────────────────────────────────
|
|
32
|
+
function resolveRunnerLayout({ availableRows, hasPrompt, hasSpinnerStatus, hasStatusBar }) {
|
|
33
|
+
const fixedRows = resolveFixedRows({ hasSpinnerStatus, hasStatusBar });
|
|
34
|
+
if (!hasPrompt) {
|
|
35
|
+
return { showLogo: availableRows >= fixedRows + LOGO_HEIGHT };
|
|
36
|
+
}
|
|
37
|
+
const showLogo = availableRows >= fixedRows + LOGO_HEIGHT + PROMPT_WITH_LOGO_MIN_ROWS;
|
|
38
|
+
const logoRows = showLogo ? LOGO_HEIGHT : 0;
|
|
39
|
+
const availablePromptRows = availableRows - fixedRows - logoRows;
|
|
40
|
+
return {
|
|
41
|
+
showLogo,
|
|
42
|
+
promptRows: Math.max(MIN_PROMPT_ROWS, availablePromptRows),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function resolveFixedRows({ hasSpinnerStatus, hasStatusBar, }) {
|
|
46
|
+
const spinnerRows = hasSpinnerStatus ? SPINNER_STATUS_HEIGHT : 0;
|
|
47
|
+
const contentGapRows = hasSpinnerStatus ? CONTENT_GAP_HEIGHT : 0;
|
|
48
|
+
const statusBarRows = hasStatusBar ? STATUS_BAR_HEIGHT : 0;
|
|
49
|
+
return spinnerRows + contentGapRows + statusBarRows;
|
|
50
|
+
}
|
|
51
|
+
function isSpinnerStatusVisible(state) {
|
|
52
|
+
return state.active || !!state.variant;
|
|
53
|
+
}
|
|
17
54
|
function StatusHint({ isReadOnly, hasError, isCompleted }) {
|
|
18
55
|
if (isReadOnly) {
|
|
19
56
|
const prefix = hasError ? '错误发生,' : isCompleted ? '任务完成,' : null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xfe-repo/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.13",
|
|
4
4
|
"description": "XFE CLI - Ink-based terminal UI for project scaffolding",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"ink-link": "^5.0.0",
|
|
18
18
|
"react": "^19.1.0",
|
|
19
19
|
"zod": "^4.3.6",
|
|
20
|
-
"@xfe-repo/cli-
|
|
21
|
-
"@xfe-repo/cli-
|
|
20
|
+
"@xfe-repo/cli-core": "2.0.8",
|
|
21
|
+
"@xfe-repo/cli-presets": "2.0.8"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^24.3.0",
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* XFE CLI HTTP Backend Gateway。
|
|
3
|
-
*
|
|
4
|
-
* CLI 通过该适配器连接本地或远端 xfe server。
|
|
5
|
-
*/
|
|
6
|
-
export interface BackendGatewayOptions {
|
|
7
|
-
readonly baseUrl: string;
|
|
8
|
-
readonly tenantId?: string;
|
|
9
|
-
}
|
|
10
|
-
export interface CreateRemoteTaskOptions {
|
|
11
|
-
readonly repoUrl: string;
|
|
12
|
-
readonly taskId: string;
|
|
13
|
-
readonly description: string;
|
|
14
|
-
readonly documentUrl?: string;
|
|
15
|
-
readonly baseBranch?: string;
|
|
16
|
-
readonly branchName?: string;
|
|
17
|
-
readonly workflowId?: string;
|
|
18
|
-
readonly prepareWorkspace?: boolean;
|
|
19
|
-
}
|
|
20
|
-
export interface StartRemoteTaskOptions {
|
|
21
|
-
readonly taskId: string;
|
|
22
|
-
readonly executor?: string;
|
|
23
|
-
readonly autoApprove?: boolean;
|
|
24
|
-
}
|
|
25
|
-
export interface RespondClientRequestOptions {
|
|
26
|
-
readonly requestId: string;
|
|
27
|
-
readonly action: 'accept' | 'decline' | 'cancel';
|
|
28
|
-
readonly content?: Record<string, unknown>;
|
|
29
|
-
}
|
|
30
|
-
export interface ListClientRequestsOptions {
|
|
31
|
-
readonly status?: string;
|
|
32
|
-
}
|
|
33
|
-
export declare class HttpBackendGateway {
|
|
34
|
-
private readonly baseUrl;
|
|
35
|
-
private readonly tenantId?;
|
|
36
|
-
constructor(options: BackendGatewayOptions);
|
|
37
|
-
health(): Promise<unknown>;
|
|
38
|
-
createTask(options: CreateRemoteTaskOptions): Promise<unknown>;
|
|
39
|
-
startTask(options: StartRemoteTaskOptions): Promise<unknown>;
|
|
40
|
-
listClientRequests(options?: ListClientRequestsOptions): Promise<unknown>;
|
|
41
|
-
respondClientRequest(options: RespondClientRequestOptions): Promise<unknown>;
|
|
42
|
-
private get;
|
|
43
|
-
private post;
|
|
44
|
-
private createUrl;
|
|
45
|
-
private createHeaders;
|
|
46
|
-
}
|
|
47
|
-
//# sourceMappingURL=http-backend.d.ts.map
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* XFE CLI HTTP Backend Gateway。
|
|
3
|
-
*
|
|
4
|
-
* CLI 通过该适配器连接本地或远端 xfe server。
|
|
5
|
-
*/
|
|
6
|
-
// ─── Gateway ───────────────────────────────────────────────
|
|
7
|
-
export class HttpBackendGateway {
|
|
8
|
-
baseUrl;
|
|
9
|
-
tenantId;
|
|
10
|
-
constructor(options) {
|
|
11
|
-
this.baseUrl = trimTrailingSlash(options.baseUrl);
|
|
12
|
-
this.tenantId = options.tenantId;
|
|
13
|
-
}
|
|
14
|
-
async health() {
|
|
15
|
-
return this.get('/health');
|
|
16
|
-
}
|
|
17
|
-
async createTask(options) {
|
|
18
|
-
return this.post('/tasks', options);
|
|
19
|
-
}
|
|
20
|
-
async startTask(options) {
|
|
21
|
-
return this.post(`/tasks/${encodeURIComponent(options.taskId)}/start`, {
|
|
22
|
-
executor: options.executor,
|
|
23
|
-
autoApprove: options.autoApprove,
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
async listClientRequests(options = {}) {
|
|
27
|
-
return this.get('/client-requests', { status: options.status });
|
|
28
|
-
}
|
|
29
|
-
async respondClientRequest(options) {
|
|
30
|
-
return this.post(`/client-requests/${encodeURIComponent(options.requestId)}/respond`, {
|
|
31
|
-
action: options.action,
|
|
32
|
-
content: options.content,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
async get(pathname, query) {
|
|
36
|
-
const response = await fetch(this.createUrl(pathname, query), { headers: this.createHeaders() });
|
|
37
|
-
return readResponse(response);
|
|
38
|
-
}
|
|
39
|
-
async post(pathname, body) {
|
|
40
|
-
const response = await fetch(this.createUrl(pathname), {
|
|
41
|
-
method: 'POST',
|
|
42
|
-
headers: { ...this.createHeaders(), 'Content-Type': 'application/json' },
|
|
43
|
-
body: JSON.stringify(body),
|
|
44
|
-
});
|
|
45
|
-
return readResponse(response);
|
|
46
|
-
}
|
|
47
|
-
createUrl(pathname, query = {}) {
|
|
48
|
-
const url = new URL(`${this.baseUrl}${pathname}`);
|
|
49
|
-
for (const [key, value] of Object.entries(query)) {
|
|
50
|
-
if (value)
|
|
51
|
-
url.searchParams.set(key, value);
|
|
52
|
-
}
|
|
53
|
-
return url.toString();
|
|
54
|
-
}
|
|
55
|
-
createHeaders() {
|
|
56
|
-
if (!this.tenantId)
|
|
57
|
-
return {};
|
|
58
|
-
return { 'x-xfe-tenant-id': this.tenantId };
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
// ─── Helpers ───────────────────────────────────────────────
|
|
62
|
-
async function readResponse(response) {
|
|
63
|
-
const body = await readJson(response);
|
|
64
|
-
if (!response.ok) {
|
|
65
|
-
const message = typeof body === 'object' && body && 'error' in body ? String(body.error) : response.statusText;
|
|
66
|
-
throw new Error(`HTTP ${response.status}: ${message}`);
|
|
67
|
-
}
|
|
68
|
-
return body;
|
|
69
|
-
}
|
|
70
|
-
async function readJson(response) {
|
|
71
|
-
const text = await response.text();
|
|
72
|
-
if (!text)
|
|
73
|
-
return undefined;
|
|
74
|
-
try {
|
|
75
|
-
return JSON.parse(text);
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
return text;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
function trimTrailingSlash(value) {
|
|
82
|
-
return value.replace(/\/+$/, '');
|
|
83
|
-
}
|
|
84
|
-
//# sourceMappingURL=http-backend.js.map
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* XFE CLI Server/Task 静态命令。
|
|
3
|
-
*
|
|
4
|
-
* 负责本地 server 启动、远端任务创建和 client request 交互循环。
|
|
5
|
-
*/
|
|
6
|
-
import { Command } from 'commander';
|
|
7
|
-
export declare function registerServerCommands(program: Command): void;
|
|
8
|
-
export declare function registerTaskCommands(program: Command): void;
|
|
9
|
-
//# sourceMappingURL=server-task-commands.d.ts.map
|