lark-remote 0.5.12 → 0.5.14
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 +28 -2
- package/README.en.md +1 -14
- package/README.md +1 -14
- package/dist/agent-kinds.d.ts +27 -0
- package/dist/agent-kinds.js +38 -0
- package/dist/bridge/approval-actions.d.ts +95 -0
- package/dist/bridge/approval-actions.js +104 -0
- package/dist/bridge/approval-coordinator.d.ts +1 -0
- package/dist/bridge/approval-coordinator.js +12 -24
- package/dist/bridge/final-usage.d.ts +8 -17
- package/dist/bridge/final-usage.js +1 -18
- package/dist/bridge/index.d.ts +52 -38
- package/dist/bridge/index.js +255 -433
- package/dist/bridge/queue-manager.d.ts +25 -1
- package/dist/bridge/queue-manager.js +179 -160
- package/dist/bridge/run-approval.d.ts +16 -0
- package/dist/bridge/run-approval.js +41 -0
- package/dist/bridge/run-usage.d.ts +32 -0
- package/dist/bridge/run-usage.js +87 -0
- package/dist/bridge/session-writeback.d.ts +19 -0
- package/dist/bridge/session-writeback.js +36 -0
- package/dist/card/approval-render.d.ts +3 -2
- package/dist/card/approval-render.js +3 -2
- package/dist/card/bash-card-session.js +2 -5
- package/dist/{router → card}/card-helpers.d.ts +7 -7
- package/dist/{router → card}/card-helpers.js +8 -8
- package/dist/card/card-session.d.ts +6 -2
- package/dist/card/card-session.js +28 -28
- package/dist/card/card-shared.d.ts +4 -4
- package/dist/card/card-shared.js +4 -10
- package/dist/card/collapsible.js +3 -2
- package/dist/card/final-answer-overflow.d.ts +1 -0
- package/dist/card/final-answer-overflow.js +46 -0
- package/dist/card/run-card-session.d.ts +2 -2
- package/dist/card/run-card-session.js +4 -9
- package/dist/card/run-renderer.d.ts +14 -0
- package/dist/card/run-renderer.js +126 -23
- package/dist/card/run-state.d.ts +40 -51
- package/dist/card/run-state.js +96 -34
- package/dist/card/usage-format.d.ts +30 -0
- package/dist/{router → card}/usage-format.js +3 -10
- package/dist/clone.js +6 -1
- package/dist/config/codex-config.js +4 -4
- package/dist/config/index.d.ts +18 -20
- package/dist/config/index.js +6 -20
- package/dist/config/pi-config.js +6 -4
- package/dist/connector/index.d.ts +2 -4
- package/dist/connector/index.js +7 -11
- package/dist/{runner/dsh → dsh}/auth.js +3 -4
- package/dist/dsh/home.d.ts +2 -0
- package/dist/dsh/home.js +8 -0
- package/dist/dsh/protocol.d.ts +9 -0
- package/dist/dsh/protocol.js +61 -0
- package/dist/inbound/turn-assembler.js +6 -0
- package/dist/index.js +25 -414
- package/dist/message-handlers.d.ts +26 -0
- package/dist/message-handlers.js +275 -0
- package/dist/model-catalog.d.ts +14 -0
- package/dist/model-catalog.js +25 -0
- package/dist/platform/path.d.ts +9 -0
- package/dist/platform/path.js +18 -0
- package/dist/router/active-run-usage.d.ts +4 -0
- package/dist/router/active-run-usage.js +12 -0
- package/dist/router/card-action-payload.d.ts +1 -1
- package/dist/router/card-action-payload.js +1 -1
- package/dist/router/commands/ls.d.ts +9 -0
- package/dist/router/commands/ls.js +243 -0
- package/dist/router/commands/order.d.ts +12 -0
- package/dist/router/commands/order.js +178 -0
- package/dist/router/commands/resume.d.ts +48 -0
- package/dist/router/commands/resume.js +246 -0
- package/dist/router/commands/ws.d.ts +15 -0
- package/dist/router/commands/ws.js +189 -0
- package/dist/router/config/claude.js +5 -4
- package/dist/router/config/codex.js +2 -6
- package/dist/router/config/common/render.d.ts +5 -1
- package/dist/router/config/common/render.js +6 -13
- package/dist/router/config/common/turn-idle.d.ts +2 -0
- package/dist/router/config/common/turn-idle.js +9 -0
- package/dist/router/config/dsh.d.ts +1 -1
- package/dist/router/config/index.d.ts +1 -1
- package/dist/router/config/kimi.js +2 -6
- package/dist/router/config/opencode.js +2 -6
- package/dist/router/config/types.d.ts +11 -3
- package/dist/router/index.d.ts +55 -33
- package/dist/router/index.js +233 -1376
- package/dist/router/paging.d.ts +13 -0
- package/dist/router/paging.js +20 -0
- package/dist/router/path-validation.d.ts +27 -0
- package/dist/router/path-validation.js +45 -0
- package/dist/runner/agent-choices-common.d.ts +1 -1
- package/dist/runner/claude/runner.d.ts +23 -6
- package/dist/runner/claude/runner.js +6 -20
- package/dist/runner/claude/session.js +1 -1
- package/dist/runner/codex/app-server/runner.d.ts +3 -1
- package/dist/runner/codex/app-server/translator.d.ts +13 -0
- package/dist/runner/codex/app-server/translator.js +114 -151
- package/dist/runner/common/acp/base-acp-runner.d.ts +3 -1
- package/dist/runner/common/acp/base-translator.d.ts +3 -8
- package/dist/runner/common/acp/base-translator.js +16 -7
- package/dist/runner/common/connection-based-runner.d.ts +3 -1
- package/dist/runner/common/jsonrpc/transport.js +17 -7
- package/dist/runner/common/spawning-runner.js +10 -4
- package/dist/runner/common/turn-started.d.ts +2 -0
- package/dist/runner/common/turn-started.js +10 -0
- package/dist/runner/dsh/client.d.ts +2 -10
- package/dist/runner/dsh/client.js +13 -220
- package/dist/runner/dsh/daemon.d.ts +0 -2
- package/dist/runner/dsh/daemon.js +9 -7
- package/dist/runner/dsh/errors.d.ts +5 -0
- package/dist/runner/dsh/errors.js +11 -0
- package/dist/runner/dsh/exit-cancel.d.ts +0 -2
- package/dist/runner/dsh/exit-cancel.js +2 -2
- package/dist/runner/dsh/inflight.d.ts +0 -2
- package/dist/runner/dsh/inflight.js +1 -1
- package/dist/runner/dsh/mux-stream.d.ts +26 -0
- package/dist/runner/dsh/mux-stream.js +176 -0
- package/dist/runner/dsh/runner.d.ts +21 -1
- package/dist/runner/dsh/runner.js +76 -73
- package/dist/runner/dsh/translator.d.ts +1 -11
- package/dist/runner/dsh/translator.js +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/kimi/acp/runner.d.ts +22 -1
- package/dist/runner/kimi/acp/runner.js +83 -60
- package/dist/runner/kimi/acp/translator.js +1 -12
- package/dist/runner/opencode/acp/error-monitor.d.ts +11 -0
- package/dist/runner/opencode/acp/error-monitor.js +31 -2
- package/dist/runner/opencode/acp/runner.d.ts +3 -1
- package/dist/runner/opencode/acp/translator.js +1 -12
- package/dist/runner/pi/rpc/runner.d.ts +2 -1
- package/dist/runner/pi/rpc/translator.d.ts +2 -8
- package/dist/runner/pi/rpc/translator.js +2 -7
- package/dist/runner/probe.d.ts +1 -1
- package/dist/runner/probe.js +10 -9
- package/dist/runner/registry.d.ts +2 -20
- package/dist/runner/registry.js +0 -32
- package/dist/runner/types.d.ts +37 -155
- package/dist/runner/types.js +4 -24
- package/dist/session/claude/session-index.d.ts +11 -4
- package/dist/session/claude/session-index.js +34 -10
- package/dist/session/claude/session-reader.d.ts +1 -1
- package/dist/session/claude/sessions.d.ts +2 -2
- package/dist/session/claude/sessions.js +41 -26
- package/dist/session/codex/rollout-reader.d.ts +1 -1
- package/dist/session/codex/rollout-reader.js +14 -13
- package/dist/session/codex/sessions.d.ts +1 -1
- package/dist/session/codex/sessions.js +4 -2
- package/dist/session/common/jsonl.js +7 -1
- package/dist/session/common/pagination.d.ts +2 -0
- package/dist/session/common/pagination.js +1 -1
- package/dist/session/common/recency.d.ts +6 -0
- package/dist/session/common/recency.js +7 -5
- package/dist/session/common/two-pass.d.ts +1 -1
- package/dist/session/common/usage-accumulator.d.ts +1 -1
- package/dist/session/dsh/sessions.d.ts +1 -1
- package/dist/session/dsh/sessions.js +37 -12
- package/dist/session/kimi/sessions.d.ts +1 -1
- package/dist/session/kimi/sessions.js +37 -10
- package/dist/session/opencode/sessions.d.ts +1 -1
- package/dist/session/opencode/sessions.js +53 -48
- package/dist/session/pi/sessions.d.ts +1 -1
- package/dist/session/pi/sessions.js +23 -9
- package/dist/session/registry.d.ts +2 -1
- package/dist/session/session-store.d.ts +1 -2
- package/dist/session/session-store.js +14 -7
- package/dist/session-contract.d.ts +178 -0
- package/dist/session-contract.js +9 -0
- package/package.json +1 -1
- package/dist/router/usage-format.d.ts +0 -60
- /package/dist/{runner/dsh → dsh}/auth.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/types.d.ts +0 -0
- /package/dist/{runner/dsh → dsh}/types.js +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { COMPACT_ACTION_CMDS, DIRECT_RETURN_CMDS, SESSION_MUTATING_ACTION_CMDS, isImmediateAction, } from './router/index.js';
|
|
3
|
+
import { InboundTurnAssembler } from './inbound/turn-assembler.js';
|
|
4
|
+
import { InboundAuthorizer } from './inbound/authorize.js';
|
|
5
|
+
import { stripPlaceholders } from './inbound/placeholder.js';
|
|
6
|
+
import { agentDisplayName } from './card/card-shared.js';
|
|
7
|
+
import { sleep } from './common/sleep.js';
|
|
8
|
+
import { bestEffortUnlink } from './common/fs.js';
|
|
9
|
+
import { dispatchOrderExecForQueue } from './router/order-exec-dispatch.js';
|
|
10
|
+
import { buildCardActionFullValue } from './router/card-action-payload.js';
|
|
11
|
+
import { actionFeedbackText } from './router/card-action-feedback.js';
|
|
12
|
+
const SESSION_MUTATING_FLUSH_BUDGET_MS = 2_000;
|
|
13
|
+
/**
|
|
14
|
+
* /stop 命令别名单源:普通消息的停止分支与 clone 活跃期拦截的排除条件
|
|
15
|
+
* 共用同一判定,防止将来加别名时两处漂移。
|
|
16
|
+
*/
|
|
17
|
+
export function isStopCommand(text) {
|
|
18
|
+
const t = text.trim().toLowerCase();
|
|
19
|
+
return t === '/stop' || t === '/t';
|
|
20
|
+
}
|
|
21
|
+
export function setupMessageHandlers(deps) {
|
|
22
|
+
const { connector, router, bridge, sessionStore, workspaceStore, binder, logger, config, cloneSession, } = deps;
|
|
23
|
+
// 入站统一装配器:图/文任意顺序 → 同一个 turn、同一个 prompt。
|
|
24
|
+
const assembler = new InboundTurnAssembler({
|
|
25
|
+
onCommit: (turn, prompt) => {
|
|
26
|
+
const messageId = turn.messageIds[turn.messageIds.length - 1] ?? '';
|
|
27
|
+
const ctx = {
|
|
28
|
+
userId: turn.userId,
|
|
29
|
+
chatId: turn.chatId,
|
|
30
|
+
messageId,
|
|
31
|
+
turnMessageIds: turn.messageIds,
|
|
32
|
+
};
|
|
33
|
+
let cwd = sessionStore.getCwd(turn.userId) ?? '';
|
|
34
|
+
if (!cwd) {
|
|
35
|
+
const recent = [...workspaceStore.list()].sort((a, b) => b.lastUsedAt - a.lastUsedAt);
|
|
36
|
+
if (recent.length > 0)
|
|
37
|
+
cwd = recent[0].path;
|
|
38
|
+
}
|
|
39
|
+
const binding = bridge.currentBinding(turn.userId);
|
|
40
|
+
bridge.enqueue(cwd, async () => {
|
|
41
|
+
await router.handle(prompt, ctx, {
|
|
42
|
+
cwdOverride: cwd,
|
|
43
|
+
binding,
|
|
44
|
+
allowCommandPrefix: false,
|
|
45
|
+
});
|
|
46
|
+
}, {
|
|
47
|
+
taskMeta: {
|
|
48
|
+
userId: turn.userId,
|
|
49
|
+
chatId: turn.chatId,
|
|
50
|
+
messageId,
|
|
51
|
+
messagePreview: prompt.slice(0, 3000),
|
|
52
|
+
binding,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
onReceipt: async (ctx, text) => {
|
|
57
|
+
await bridge.sendResult({ text }, ctx);
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
router.setInboundFlusher(() => assembler.flushAll('flush'));
|
|
61
|
+
const authorizer = new InboundAuthorizer({
|
|
62
|
+
binder,
|
|
63
|
+
onBind: (userId, chatId) => {
|
|
64
|
+
let resolvedCwd;
|
|
65
|
+
try {
|
|
66
|
+
resolvedCwd = fs.realpathSync(process.cwd());
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
resolvedCwd = process.cwd();
|
|
70
|
+
}
|
|
71
|
+
if (!sessionStore.getCwd(userId)) {
|
|
72
|
+
sessionStore.setCwd(userId, resolvedCwd);
|
|
73
|
+
}
|
|
74
|
+
return (async () => {
|
|
75
|
+
try {
|
|
76
|
+
await connector.sendWithRetry(chatId, {
|
|
77
|
+
text: `✅ 已绑定到本账号,此后仅你可使用本应用\n` +
|
|
78
|
+
`📂 当前工作目录: \`${resolvedCwd}\`\n` +
|
|
79
|
+
`🤖 默认 Agent: ${agentDisplayName(config.defaultAgent)}\n` +
|
|
80
|
+
`💡 直接输入消息即可开始对话,或 /cd 切换目录`,
|
|
81
|
+
});
|
|
82
|
+
const helpResult = router.cmdHelp();
|
|
83
|
+
if (helpResult.card) {
|
|
84
|
+
await connector.sendWithRetry(chatId, { card: helpResult.card });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
logger.error('[binder] onboarding send failed:', err);
|
|
89
|
+
}
|
|
90
|
+
})();
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
connector.setInboundMediaDetectedHandler((msg) => {
|
|
94
|
+
const authorization = authorizer.authorize(msg.userId, msg.chatId);
|
|
95
|
+
if (authorization === 'rejected') {
|
|
96
|
+
logger.warn(`[media] rejected inbound media from non-owner ${msg.userId}`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (cloneSession.isActive()) {
|
|
100
|
+
logger.info(`[media] dropped inbound media during clone session ${msg.messageId}`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!router.config.inboundMedia.enabled) {
|
|
104
|
+
assembler.ingest({
|
|
105
|
+
kind: 'rejected',
|
|
106
|
+
userId: msg.userId,
|
|
107
|
+
chatId: msg.chatId,
|
|
108
|
+
messageId: msg.messageId,
|
|
109
|
+
replyToMessageId: msg.replyToMessageId,
|
|
110
|
+
rawContentType: msg.rawContentType,
|
|
111
|
+
rejectedKind: 'file',
|
|
112
|
+
reason: '入站媒体保存已关闭(inboundMedia.enabled: false),未保存文件。' +
|
|
113
|
+
'如需自动保存图片/文件,请开启后重试',
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const outcome = (async () => {
|
|
118
|
+
const payload = await connector.downloadInboundMedia(msg, {
|
|
119
|
+
maxFileSizeMb: router.config.inboundMedia.maxFileSizeMb,
|
|
120
|
+
});
|
|
121
|
+
try {
|
|
122
|
+
return await bridge.saveInboundMedia(payload);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
for (const item of payload.media)
|
|
126
|
+
bestEffortUnlink(item.tempPath);
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
})();
|
|
130
|
+
assembler.ingest({
|
|
131
|
+
kind: 'media',
|
|
132
|
+
userId: msg.userId,
|
|
133
|
+
chatId: msg.chatId,
|
|
134
|
+
messageId: msg.messageId,
|
|
135
|
+
replyToMessageId: msg.replyToMessageId,
|
|
136
|
+
rawContentType: msg.rawContentType,
|
|
137
|
+
outcome,
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
connector.setMessageHandler((msg) => {
|
|
141
|
+
const decision = authorizer.authorize(msg.userId, msg.chatId);
|
|
142
|
+
if (decision === 'rejected' || decision === 'bind_success')
|
|
143
|
+
return;
|
|
144
|
+
logger.info(`message from ${msg.userId} (${msg.rawContentType}): ${msg.content.slice(0, 100)}`);
|
|
145
|
+
void connector.addReaction(msg.messageId, 'Typing');
|
|
146
|
+
if (cloneSession.isActive() && !isStopCommand(msg.content)) {
|
|
147
|
+
void cloneSession
|
|
148
|
+
.handleMessage(msg.content, {
|
|
149
|
+
userId: msg.userId,
|
|
150
|
+
chatId: msg.chatId,
|
|
151
|
+
messageId: msg.messageId,
|
|
152
|
+
})
|
|
153
|
+
.catch((err) => logger.error('[clone] message handling failed:', err));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const content = router.expandAliasMessage(msg.content);
|
|
157
|
+
if (isStopCommand(content)) {
|
|
158
|
+
void (async () => {
|
|
159
|
+
const interrupt = await bridge.interruptCurrentRunDetailed({
|
|
160
|
+
userId: msg.userId,
|
|
161
|
+
chatId: msg.chatId,
|
|
162
|
+
});
|
|
163
|
+
const ctx = { userId: msg.userId, chatId: msg.chatId, messageId: msg.messageId };
|
|
164
|
+
if (!interrupt.stopped) {
|
|
165
|
+
await bridge.sendResult({ text: '当前没有运行中的进程' }, ctx);
|
|
166
|
+
}
|
|
167
|
+
else if ((interrupt.stoppedCount ?? 1) > 1) {
|
|
168
|
+
await bridge.sendResult({ text: `✅ 已停止 ${interrupt.stoppedCount} 个运行中的任务` }, ctx);
|
|
169
|
+
}
|
|
170
|
+
})().catch((err) => logger.error('[control] /stop failed:', err));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const stripped = stripPlaceholders(content);
|
|
174
|
+
if (stripped.unknownTags.length > 0) {
|
|
175
|
+
logger.warn(`[inbound] unknown placeholder tags stripped: ${stripped.unknownTags.join(', ')} ` +
|
|
176
|
+
`(msg_type=${msg.rawContentType})`);
|
|
177
|
+
}
|
|
178
|
+
const commandEligible = msg.rawContentType === 'text' && stripped.kinds.length === 0 && stripped.clean !== '';
|
|
179
|
+
if (commandEligible && (stripped.clean.startsWith('/') || stripped.clean.startsWith('!'))) {
|
|
180
|
+
void (async () => {
|
|
181
|
+
await assembler.flush(msg.userId, msg.chatId, 'flush');
|
|
182
|
+
await router.handle(stripped.clean, {
|
|
183
|
+
userId: msg.userId,
|
|
184
|
+
chatId: msg.chatId,
|
|
185
|
+
messageId: msg.messageId,
|
|
186
|
+
}, { allowCommandPrefix: true });
|
|
187
|
+
})().catch((err) => logger.error('[control] command failed:', err));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
assembler.ingest({
|
|
191
|
+
kind: 'text',
|
|
192
|
+
userId: msg.userId,
|
|
193
|
+
chatId: msg.chatId,
|
|
194
|
+
messageId: msg.messageId,
|
|
195
|
+
replyToMessageId: msg.replyToMessageId,
|
|
196
|
+
rawContentType: msg.rawContentType,
|
|
197
|
+
text: stripped.clean,
|
|
198
|
+
placeholders: stripped.kinds,
|
|
199
|
+
unknownTags: stripped.unknownTags,
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
connector.setCardActionHandler(async (action) => {
|
|
203
|
+
if (!binder.isOwner(action.operator.openId)) {
|
|
204
|
+
binder.recordRejectedCardAction(action.operator.openId);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const actionValue = action.action.value;
|
|
208
|
+
if (!actionValue?.cmd)
|
|
209
|
+
return;
|
|
210
|
+
logger.info(`card action: ${actionValue.cmd}`);
|
|
211
|
+
const userId = action.operator.openId;
|
|
212
|
+
const chatId = action.chatId;
|
|
213
|
+
const messageId = action.messageId;
|
|
214
|
+
void connector.addReaction(messageId, 'Typing');
|
|
215
|
+
if (actionValue.cmd === 'stop') {
|
|
216
|
+
void router
|
|
217
|
+
.handleCardAction({ ...actionValue, cmd: 'stop' }, { userId, chatId, messageId })
|
|
218
|
+
.catch((err) => logger.error('[control] card stop failed:', err));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const isImmediate = isImmediateAction(actionValue.cmd);
|
|
222
|
+
const cwd = sessionStore.getCwd(userId) ?? '';
|
|
223
|
+
const fullValue = buildCardActionFullValue(actionValue, action);
|
|
224
|
+
if (SESSION_MUTATING_ACTION_CMDS.has(actionValue.cmd)) {
|
|
225
|
+
const flushP = assembler
|
|
226
|
+
.flush(userId, chatId, 'flush')
|
|
227
|
+
.catch((err) => logger.error('[control] inbound flush failed:', err));
|
|
228
|
+
await Promise.race([flushP, sleep(SESSION_MUTATING_FLUSH_BUDGET_MS)]);
|
|
229
|
+
}
|
|
230
|
+
if (DIRECT_RETURN_CMDS.has(actionValue.cmd)) {
|
|
231
|
+
return router.handleCardAction(fullValue, { userId, chatId, messageId });
|
|
232
|
+
}
|
|
233
|
+
if (actionValue.cmd === 'order.exec') {
|
|
234
|
+
void dispatchOrderExecForQueue({
|
|
235
|
+
router,
|
|
236
|
+
bridge,
|
|
237
|
+
cwd,
|
|
238
|
+
orderId: actionValue.orderId,
|
|
239
|
+
ctx: { userId, chatId, messageId },
|
|
240
|
+
}).catch((err) => logger.error('[control] order.exec dispatch failed:', err));
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const forwardActionFeedback = (res) => {
|
|
244
|
+
const text = actionFeedbackText(res);
|
|
245
|
+
if (!text)
|
|
246
|
+
return;
|
|
247
|
+
void bridge
|
|
248
|
+
.sendResult({ text }, { userId, chatId, messageId })
|
|
249
|
+
.catch((err) => logger.error('[control] card action feedback failed:', err));
|
|
250
|
+
};
|
|
251
|
+
if (isImmediate) {
|
|
252
|
+
bridge.enqueueImmediate(cwd, async () => {
|
|
253
|
+
const res = await router.handleCardAction(fullValue, { userId, chatId, messageId });
|
|
254
|
+
forwardActionFeedback(res);
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
bridge.enqueue(cwd, async () => {
|
|
259
|
+
const res = await router.handleCardAction(fullValue, { userId, chatId, messageId });
|
|
260
|
+
forwardActionFeedback(res);
|
|
261
|
+
}, {
|
|
262
|
+
taskMeta: {
|
|
263
|
+
userId,
|
|
264
|
+
chatId,
|
|
265
|
+
messageId: router.mintQueueTaskKey('card', actionValue.cmd),
|
|
266
|
+
feishuReplyTo: messageId,
|
|
267
|
+
messagePreview: `card action: ${actionValue.cmd}`,
|
|
268
|
+
kind: COMPACT_ACTION_CMDS.has(actionValue.cmd) ? 'compact' : 'command',
|
|
269
|
+
editable: !COMPACT_ACTION_CMDS.has(actionValue.cmd),
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=message-handlers.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude 模型 ID ↔ CLI alias 的双向表。
|
|
3
|
+
*
|
|
4
|
+
* 中立层(谁都不许反向依赖它):config 要写默认值、runner/claude 要把 alias 传给
|
|
5
|
+
* CLI、router/config 要把 alias 显示给用户。此前这张表住在 config/,于是
|
|
6
|
+
* 「地基层被 runner 反向 import」;挪到这里后三方共同引用同一份。
|
|
7
|
+
*
|
|
8
|
+
* 表内容来自 Claude CLI 的模型别名(alias 是 CLI 侧的稳定名字,model ID 会随
|
|
9
|
+
* 版本变),新增一行只改这里。
|
|
10
|
+
*/
|
|
11
|
+
/** 模型 ID → alias 映射。 */
|
|
12
|
+
export declare const MODEL_ID_TO_ALIAS: Record<string, string>;
|
|
13
|
+
/** alias → model ID 反向映射。 */
|
|
14
|
+
export declare const MODEL_ALIAS_TO_ID: Record<string, string>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude 模型 ID ↔ CLI alias 的双向表。
|
|
3
|
+
*
|
|
4
|
+
* 中立层(谁都不许反向依赖它):config 要写默认值、runner/claude 要把 alias 传给
|
|
5
|
+
* CLI、router/config 要把 alias 显示给用户。此前这张表住在 config/,于是
|
|
6
|
+
* 「地基层被 runner 反向 import」;挪到这里后三方共同引用同一份。
|
|
7
|
+
*
|
|
8
|
+
* 表内容来自 Claude CLI 的模型别名(alias 是 CLI 侧的稳定名字,model ID 会随
|
|
9
|
+
* 版本变),新增一行只改这里。
|
|
10
|
+
*/
|
|
11
|
+
/** 模型 ID → alias 映射。 */
|
|
12
|
+
export const MODEL_ID_TO_ALIAS = {
|
|
13
|
+
'claude-opus-4-8': 'opus',
|
|
14
|
+
'claude-sonnet-4-6': 'sonnet',
|
|
15
|
+
'claude-haiku-4-5-20250501': 'haiku',
|
|
16
|
+
'claude-fable-5': 'fable',
|
|
17
|
+
};
|
|
18
|
+
/** alias → model ID 反向映射。 */
|
|
19
|
+
export const MODEL_ALIAS_TO_ID = {
|
|
20
|
+
opus: 'claude-opus-4-8',
|
|
21
|
+
sonnet: 'claude-sonnet-4-6',
|
|
22
|
+
haiku: 'claude-haiku-4-5-20250501',
|
|
23
|
+
fable: 'claude-fable-5',
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=model-catalog.js.map
|
package/dist/platform/path.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
export interface PathOptions {
|
|
2
2
|
platform?: NodeJS.Platform;
|
|
3
3
|
}
|
|
4
|
+
/**
|
|
5
|
+
* realpath 归一(macOS `/tmp` → `/private/tmp`),失败回退原样输入。
|
|
6
|
+
*
|
|
7
|
+
* 各 session reader 此前有三种口径:静默回退(opencode)、回退 + warn(kimi
|
|
8
|
+
* 单会话读)、裸调让一次 ENOENT 把整张会话列表清掉(kimi list)。统一成
|
|
9
|
+
* 「回退优先于丢结果」,失败原因交给调用方记日志(`onFailure`),因为
|
|
10
|
+
* ENOENT(陈旧 cwd)是正常空态、EACCES/EBUSY 才需要留痕。
|
|
11
|
+
*/
|
|
12
|
+
export declare function safeRealpath(cwd: string, onFailure?: (err: unknown) => void): string;
|
|
4
13
|
/**
|
|
5
14
|
* 路径的「判等键」形态:把 samePath 的平台归一规则单源化,供 Map/Set 键控
|
|
6
15
|
* 场景使用(按 cwd 建倒排索引时不能每次查询做 O(n) 两两比较)。不变量:
|
package/dist/platform/path.js
CHANGED
|
@@ -9,8 +9,26 @@
|
|
|
9
9
|
* 全部函数接受显式 `platform` 参数(默认 currentPlatform),使 win32 语义
|
|
10
10
|
* 可在任意宿主的单元测试中验证。
|
|
11
11
|
*/
|
|
12
|
+
import fs from 'node:fs';
|
|
12
13
|
import path from 'node:path';
|
|
13
14
|
import { currentPlatform, isWin32 } from './select.js';
|
|
15
|
+
/**
|
|
16
|
+
* realpath 归一(macOS `/tmp` → `/private/tmp`),失败回退原样输入。
|
|
17
|
+
*
|
|
18
|
+
* 各 session reader 此前有三种口径:静默回退(opencode)、回退 + warn(kimi
|
|
19
|
+
* 单会话读)、裸调让一次 ENOENT 把整张会话列表清掉(kimi list)。统一成
|
|
20
|
+
* 「回退优先于丢结果」,失败原因交给调用方记日志(`onFailure`),因为
|
|
21
|
+
* ENOENT(陈旧 cwd)是正常空态、EACCES/EBUSY 才需要留痕。
|
|
22
|
+
*/
|
|
23
|
+
export function safeRealpath(cwd, onFailure) {
|
|
24
|
+
try {
|
|
25
|
+
return fs.realpathSync(cwd);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
onFailure?.(err);
|
|
29
|
+
return cwd;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
14
32
|
function resolveOptions(opts) {
|
|
15
33
|
return { platform: opts?.platform ?? currentPlatform };
|
|
16
34
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ActiveRunSnapshot } from '../bridge/index.js';
|
|
2
|
+
import type { SessionDisplayUsage } from '../card/usage-format.js';
|
|
3
|
+
/** 从 bridge 的活跃 run 快照提取会话卡需要的用量字段。 */
|
|
4
|
+
export declare function activeRunUsage(activeRun: ActiveRunSnapshot | undefined): SessionDisplayUsage | undefined;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** 从 bridge 的活跃 run 快照提取会话卡需要的用量字段。 */
|
|
2
|
+
export function activeRunUsage(activeRun) {
|
|
3
|
+
if (!activeRun || activeRun.contextLength === undefined)
|
|
4
|
+
return undefined;
|
|
5
|
+
return {
|
|
6
|
+
contextLength: activeRun.contextLength,
|
|
7
|
+
compactCount: activeRun.compactCount,
|
|
8
|
+
cacheReadTokens: activeRun.cacheReadTokens,
|
|
9
|
+
cacheCreationTokens: activeRun.cacheCreationTokens,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=active-run-usage.js.map
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* out-of-band fields.
|
|
10
10
|
*
|
|
11
11
|
* Extracted from src/index.ts so the entry transform is behavior-testable
|
|
12
|
-
* (
|
|
12
|
+
* (see message-handlers.ts / message-handlers.test.ts).
|
|
13
13
|
*/
|
|
14
14
|
import type { CardActionEvent } from '@larksuite/channel';
|
|
15
15
|
import type { CardActionPayload } from './index.js';
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* out-of-band fields.
|
|
10
10
|
*
|
|
11
11
|
* Extracted from src/index.ts so the entry transform is behavior-testable
|
|
12
|
-
* (
|
|
12
|
+
* (see message-handlers.ts / message-handlers.test.ts).
|
|
13
13
|
*/
|
|
14
14
|
/**
|
|
15
15
|
* Build the full card action payload for router.handleCardAction.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CommandContext, CommandResult } from '../../bridge/index.js';
|
|
2
|
+
export declare const LS_PAGE_SIZE = 30;
|
|
3
|
+
export interface LsCommandOptions {
|
|
4
|
+
cwd?: string;
|
|
5
|
+
offset?: number;
|
|
6
|
+
rootDir?: string;
|
|
7
|
+
q?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function buildLsCommand(args: string[], _ctx: CommandContext, options: LsCommandOptions): CommandResult;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { cardButton } from '../../card/card-shared.js';
|
|
5
|
+
import { paginationBar, searchBar } from '../../card/card-helpers.js';
|
|
6
|
+
import { inspectPath, statPath } from '../path-validation.js';
|
|
7
|
+
import { pageSlice } from '../paging.js';
|
|
8
|
+
export const LS_PAGE_SIZE = 30;
|
|
9
|
+
function formatBytes(bytes) {
|
|
10
|
+
if (bytes < 1024)
|
|
11
|
+
return `${bytes}B`;
|
|
12
|
+
if (bytes < 1024 * 1024)
|
|
13
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
14
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
15
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
16
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
|
|
17
|
+
}
|
|
18
|
+
function buildFileCard(filePath) {
|
|
19
|
+
const resolution = statPath(filePath);
|
|
20
|
+
const stat = resolution.ok ? resolution.stat : undefined;
|
|
21
|
+
const sizeText = stat ? formatBytes(stat.size) : '未知';
|
|
22
|
+
const mtimeText = stat ? stat.mtime.toLocaleString('zh-CN', { hour12: false }) : '未知';
|
|
23
|
+
const parentDir = path.dirname(filePath);
|
|
24
|
+
const buttons = [
|
|
25
|
+
cardButton('📎 下载', { cmd: 'ls.file', path: filePath }, { type: 'primary', size: 'small' }),
|
|
26
|
+
];
|
|
27
|
+
if (parentDir !== filePath) {
|
|
28
|
+
buttons.push(cardButton('上级', { cmd: 'ls.browse', path: parentDir, root: parentDir }, { type: 'default', size: 'small' }));
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
card: {
|
|
32
|
+
schema: '2.0',
|
|
33
|
+
config: { wide_screen_mode: true, update_multi: true },
|
|
34
|
+
header: {
|
|
35
|
+
title: { tag: 'plain_text', content: `📄 ${path.basename(filePath)}` },
|
|
36
|
+
template: 'blue',
|
|
37
|
+
},
|
|
38
|
+
body: {
|
|
39
|
+
elements: [
|
|
40
|
+
{ tag: 'div', text: { tag: 'lark_md', content: `\`${filePath}\`\n${sizeText}` } },
|
|
41
|
+
{ tag: 'div', text: { tag: 'lark_md', content: `修改时间: ${mtimeText}` } },
|
|
42
|
+
{
|
|
43
|
+
tag: 'column_set',
|
|
44
|
+
columns: buttons.map((button) => ({
|
|
45
|
+
tag: 'column',
|
|
46
|
+
width: 'auto',
|
|
47
|
+
elements: [button],
|
|
48
|
+
})),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function buildLsCommand(args, _ctx, options) {
|
|
56
|
+
const { cwd, offset = 0, rootDir } = options;
|
|
57
|
+
if (!cwd)
|
|
58
|
+
return { text: '请先使用 /cd <path> 设置工作目录' };
|
|
59
|
+
const filter = options.q?.trim() || undefined;
|
|
60
|
+
let targetDir = cwd;
|
|
61
|
+
if (args[0]) {
|
|
62
|
+
const arg = args[0].startsWith('~') ? path.join(os.homedir(), args[0].slice(1)) : args[0];
|
|
63
|
+
const potentialDir = path.resolve(cwd, arg);
|
|
64
|
+
const kind = inspectPath(potentialDir);
|
|
65
|
+
if (kind === 'missing' || kind === 'unreadable') {
|
|
66
|
+
return { text: `ls: ${args[0]}: No such file or directory` };
|
|
67
|
+
}
|
|
68
|
+
if (kind === 'file')
|
|
69
|
+
return buildFileCard(potentialDir);
|
|
70
|
+
targetDir = potentialDir;
|
|
71
|
+
}
|
|
72
|
+
const browseRoot = rootDir ? path.resolve(cwd, rootDir) : targetDir;
|
|
73
|
+
try {
|
|
74
|
+
const entries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
75
|
+
const dirs = [];
|
|
76
|
+
const files = [];
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
let isDir = entry.isDirectory();
|
|
79
|
+
let size;
|
|
80
|
+
if (!isDir) {
|
|
81
|
+
const resolution = statPath(path.join(targetDir, entry.name));
|
|
82
|
+
if (resolution.ok) {
|
|
83
|
+
isDir = resolution.stat.isDirectory();
|
|
84
|
+
if (!isDir)
|
|
85
|
+
size = resolution.stat.size;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (isDir)
|
|
89
|
+
dirs.push(entry.name);
|
|
90
|
+
else
|
|
91
|
+
files.push({ name: entry.name, size });
|
|
92
|
+
}
|
|
93
|
+
dirs.sort((a, b) => a.localeCompare(b));
|
|
94
|
+
files.sort((a, b) => a.name.localeCompare(b.name));
|
|
95
|
+
const allItems = [
|
|
96
|
+
...dirs.map((name) => ({ name, isDir: true })),
|
|
97
|
+
...files.map((file) => ({ name: file.name, isDir: false, size: file.size })),
|
|
98
|
+
];
|
|
99
|
+
const ql = filter?.toLowerCase();
|
|
100
|
+
const visibleItems = ql
|
|
101
|
+
? allItems.filter((item) => item.name.toLowerCase().includes(ql))
|
|
102
|
+
: allItems;
|
|
103
|
+
const visibleDirCount = visibleItems.reduce((count, item) => count + (item.isDir ? 1 : 0), 0);
|
|
104
|
+
const visibleFileCount = visibleItems.length - visibleDirCount;
|
|
105
|
+
const { totalCount, totalPages, safeOffset, currentPage, pageItems, hasPagination } = pageSlice(visibleItems, offset, LS_PAGE_SIZE);
|
|
106
|
+
const parentDir = path.dirname(targetDir);
|
|
107
|
+
const headerElements = [];
|
|
108
|
+
if (parentDir !== targetDir) {
|
|
109
|
+
headerElements.push(cardButton('上级', { cmd: 'ls.browse', path: parentDir, root: browseRoot }, { type: 'default', size: 'small' }));
|
|
110
|
+
}
|
|
111
|
+
if (targetDir !== browseRoot) {
|
|
112
|
+
headerElements.push(cardButton('返回', { cmd: 'ls.browse', path: browseRoot, root: browseRoot }, { type: 'default', size: 'small' }));
|
|
113
|
+
}
|
|
114
|
+
headerElements.push(cardButton('刷新', {
|
|
115
|
+
cmd: 'ls.refresh',
|
|
116
|
+
path: targetDir,
|
|
117
|
+
offset: safeOffset,
|
|
118
|
+
root: browseRoot,
|
|
119
|
+
...(filter ? { q: filter } : {}),
|
|
120
|
+
}, { type: 'default', size: 'small' }));
|
|
121
|
+
if (targetDir !== cwd) {
|
|
122
|
+
headerElements.push(cardButton('切换', { cmd: 'ls.switch', path: targetDir, root: browseRoot }, { type: 'primary', size: 'small' }));
|
|
123
|
+
}
|
|
124
|
+
const dirButton = (name) => cardButton(`📁 ${name}`, { cmd: 'ls.browse', path: path.join(targetDir, name), root: browseRoot }, { type: 'default' });
|
|
125
|
+
const fileButton = (name, size) => cardButton(`📄 ${name}${size !== undefined ? ` (${formatBytes(size)})` : ''}`, { cmd: 'ls.file', path: path.join(targetDir, name) }, { type: 'default', size: 'small' });
|
|
126
|
+
const elements = [
|
|
127
|
+
{
|
|
128
|
+
tag: 'div',
|
|
129
|
+
text: {
|
|
130
|
+
tag: 'lark_md',
|
|
131
|
+
content: `\`${targetDir}\`\n共 ${visibleDirCount} 目录, ${visibleFileCount} 文件`,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
tag: 'column_set',
|
|
136
|
+
columns: headerElements.map((element) => ({
|
|
137
|
+
tag: 'column',
|
|
138
|
+
width: 'auto',
|
|
139
|
+
elements: [element],
|
|
140
|
+
})),
|
|
141
|
+
},
|
|
142
|
+
{ tag: 'hr' },
|
|
143
|
+
searchBar({
|
|
144
|
+
cmd: 'ls.filter',
|
|
145
|
+
placeholder: '搜索本层目录/文件名,输完回车',
|
|
146
|
+
currentQuery: filter,
|
|
147
|
+
extra: { path: targetDir, root: browseRoot },
|
|
148
|
+
}),
|
|
149
|
+
];
|
|
150
|
+
if (filter) {
|
|
151
|
+
elements.push({
|
|
152
|
+
tag: 'div',
|
|
153
|
+
text: {
|
|
154
|
+
tag: 'lark_md',
|
|
155
|
+
content: `🔍 筛选:"${filter}" · 命中 ${totalCount} 项(点「清除筛选」取消)`,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
const pageDirs = pageItems.filter((item) => item.isDir);
|
|
160
|
+
const pageFiles = pageItems.filter((item) => !item.isDir);
|
|
161
|
+
if (filter && visibleItems.length === 0) {
|
|
162
|
+
elements.push({
|
|
163
|
+
tag: 'div',
|
|
164
|
+
text: { tag: 'lark_md', content: `🔍 无匹配条目(关键词:"${filter}")` },
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
elements.push({
|
|
169
|
+
tag: 'div',
|
|
170
|
+
text: { tag: 'lark_md', content: `**📂 目录 (${visibleDirCount})**` },
|
|
171
|
+
});
|
|
172
|
+
if (pageDirs.length > 0) {
|
|
173
|
+
for (const dir of pageDirs) {
|
|
174
|
+
elements.push({
|
|
175
|
+
tag: 'column_set',
|
|
176
|
+
flex_mode: 'none',
|
|
177
|
+
columns: [{ tag: 'column', width: 'auto', elements: [dirButton(dir.name)] }],
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else if (visibleDirCount === 0) {
|
|
182
|
+
elements.push({ tag: 'div', text: { tag: 'lark_md', content: '无子目录' } });
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
elements.push({
|
|
186
|
+
tag: 'div',
|
|
187
|
+
text: { tag: 'lark_md', content: `(第 ${currentPage} 页无目录)` },
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
elements.push({ tag: 'hr' });
|
|
191
|
+
elements.push({
|
|
192
|
+
tag: 'div',
|
|
193
|
+
text: { tag: 'lark_md', content: `**📄 文件 (${visibleFileCount})**` },
|
|
194
|
+
});
|
|
195
|
+
if (pageFiles.length > 0) {
|
|
196
|
+
for (const file of pageFiles) {
|
|
197
|
+
elements.push({
|
|
198
|
+
tag: 'column_set',
|
|
199
|
+
flex_mode: 'none',
|
|
200
|
+
columns: [
|
|
201
|
+
{ tag: 'column', width: 'auto', elements: [fileButton(file.name, file.size)] },
|
|
202
|
+
],
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else if (visibleFileCount === 0) {
|
|
207
|
+
elements.push({ tag: 'div', text: { tag: 'lark_md', content: '无文件' } });
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
elements.push({
|
|
211
|
+
tag: 'div',
|
|
212
|
+
text: { tag: 'lark_md', content: `(第 ${currentPage} 页无文件)` },
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (hasPagination) {
|
|
217
|
+
elements.push({ tag: 'hr' });
|
|
218
|
+
elements.push(...paginationBar({
|
|
219
|
+
cmd: 'ls.page',
|
|
220
|
+
offset: safeOffset,
|
|
221
|
+
pageSize: LS_PAGE_SIZE,
|
|
222
|
+
total: totalCount,
|
|
223
|
+
extra: { path: targetDir, root: browseRoot, ...(filter ? { q: filter } : {}) },
|
|
224
|
+
label: `**第 ${currentPage}/${totalPages} 页**(共 ${totalCount} 项)`,
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
card: {
|
|
229
|
+
schema: '2.0',
|
|
230
|
+
config: { wide_screen_mode: true, update_multi: true },
|
|
231
|
+
header: {
|
|
232
|
+
title: { tag: 'plain_text', content: `📁 ${path.basename(targetDir)}` },
|
|
233
|
+
template: 'blue',
|
|
234
|
+
},
|
|
235
|
+
body: { elements },
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
return { text: `读取目录失败: ${err.message}` };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=ls.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CommandContext, CommandResult } from '../../bridge/index.js';
|
|
2
|
+
import type { OrderEntry } from '../../order/index.js';
|
|
3
|
+
export declare const ORDER_PAGE_SIZE = 8;
|
|
4
|
+
export interface OrderCommandDeps {
|
|
5
|
+
reload(): void;
|
|
6
|
+
list(): OrderEntry[];
|
|
7
|
+
save(text: string): void;
|
|
8
|
+
updateText(id: string, text: string): void;
|
|
9
|
+
setAlias(id: string, alias: string | undefined): void;
|
|
10
|
+
resolveTarget(target: string): OrderEntry | undefined;
|
|
11
|
+
}
|
|
12
|
+
export declare function buildOrderCommand(args: string[], ctx: CommandContext, offset: number, deps: OrderCommandDeps): CommandResult;
|