lark-remote 0.1.2 → 0.1.4
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 +65 -0
- package/README.en.md +2 -5
- package/README.md +1 -4
- package/dist/bridge/index.d.ts +5 -1
- package/dist/bridge/index.js +12 -2
- package/dist/card/card-budget.js +1 -2
- package/dist/card/run-renderer.js +1 -0
- package/dist/card/run-state.d.ts +4 -0
- package/dist/config/codex-config.d.ts +3 -5
- package/dist/config/codex-config.js +7 -7
- package/dist/config/dir.d.ts +1 -0
- package/dist/config/dir.js +4 -0
- package/dist/config/index.d.ts +0 -10
- package/dist/config/index.js +0 -11
- package/dist/connector/index.js +28 -0
- package/dist/index.js +74 -21
- package/dist/router/config/codex.js +8 -12
- package/dist/router/config/index.d.ts +10 -0
- package/dist/router/config/index.js +17 -0
- package/dist/router/index.d.ts +40 -1
- package/dist/router/index.js +514 -48
- package/dist/router/utils.d.ts +3 -0
- package/dist/router/utils.js +15 -3
- package/dist/runner/claude/runner.d.ts +0 -1
- package/dist/runner/claude/runner.js +1 -1
- package/dist/runner/codex/runner.d.ts +0 -2
- package/dist/runner/codex/runner.js +1 -1
- package/dist/runner/common/spawning-runner.d.ts +0 -1
- package/dist/runner/common/spawning-runner.js +2 -4
- package/dist/runner/kimi/runner.d.ts +0 -1
- package/dist/runner/kimi/runner.js +1 -1
- package/dist/runner/opencode/runner.d.ts +0 -2
- package/dist/runner/opencode/runner.js +1 -1
- package/dist/runner/pi/runner.d.ts +0 -1
- package/dist/runner/pi/runner.js +1 -1
- package/dist/runner/probe.d.ts +45 -0
- package/dist/runner/probe.js +87 -0
- package/dist/runner/types.d.ts +3 -0
- package/dist/session/claude/sessions.d.ts +1 -1
- package/dist/session/claude/sessions.js +77 -31
- package/dist/session/codex/rollout-reader.js +9 -0
- package/dist/session/kimi/sessions.d.ts +3 -1
- package/dist/session/kimi/sessions.js +9 -3
- package/dist/session/opencode/sessions.d.ts +0 -2
- package/dist/session/opencode/sessions.js +1 -1
- package/dist/startup-contact.d.ts +4 -2
- package/dist/startup-contact.js +7 -4
- package/dist/workspace/index.d.ts +19 -1
- package/dist/workspace/index.js +51 -5
- package/package.json +39 -4
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { parseCliArgs, resolveConfigDir, setConfigDir, printHelp, printVersion,
|
|
|
5
5
|
import { FeishuConnector } from './connector/index.js';
|
|
6
6
|
import { ClaudeRunner, CodexExecRunner, OpencodeExecRunner, PiRunner, KimiRunner, } from './runner/index.js';
|
|
7
7
|
import { AgentRegistry } from './runner/registry.js';
|
|
8
|
+
import { probeAllAgents } from './runner/probe.js';
|
|
8
9
|
import { SessionReaderRegistry, SessionStore } from './session/index.js';
|
|
9
10
|
import { ClaudeSessionReader, CodexSessionReader, OpencodeSessionReader, PiSessionReader, KimiSessionReader, } from './session/index.js';
|
|
10
11
|
import { CommandRouter, isImmediateAction, formatUsageStats, } from './router/index.js';
|
|
@@ -21,6 +22,7 @@ import { markdownDiv } from './card/collapsible.js';
|
|
|
21
22
|
import { sessionEventPanel } from './router/card-helpers.js';
|
|
22
23
|
import { newSessionButton, agentDisplayName } from './card/card-shared.js';
|
|
23
24
|
import path from 'node:path';
|
|
25
|
+
import fs from 'node:fs';
|
|
24
26
|
/** Send auto-resume card for a restored session */
|
|
25
27
|
async function sendAutoResumeCard(connector, contact, cwd, session, state) {
|
|
26
28
|
if (!contact.userId)
|
|
@@ -145,14 +147,13 @@ function setupInstanceLockAndHandlers(instanceLock, logger, configDir) {
|
|
|
145
147
|
function initializeRunner(config, configDir, cliArgs) {
|
|
146
148
|
const agentRegistry = new AgentRegistry();
|
|
147
149
|
// P1-15: Claude factory reads from configContainer (not closure) so runtime
|
|
148
|
-
// config changes (
|
|
150
|
+
// config changes (model, effort, stopGraceMs) take effect after
|
|
149
151
|
// bridge.setConfig() + clearRunners(). Same pattern as codex/pi/kimi.
|
|
150
152
|
agentRegistry.register('claude', (ws) => {
|
|
151
153
|
const container = agentRegistry.getConfigContainer();
|
|
152
154
|
const latestConfig = container?.current ?? config;
|
|
153
155
|
const claudeConfig = latestConfig.claude;
|
|
154
156
|
return new ClaudeRunner({
|
|
155
|
-
binary: claudeConfig.binary,
|
|
156
157
|
model: claudeConfig.model,
|
|
157
158
|
effort: claudeConfig.effort,
|
|
158
159
|
stopGraceMs: claudeConfig.stopGraceMs,
|
|
@@ -170,7 +171,6 @@ function initializeRunner(config, configDir, cliArgs) {
|
|
|
170
171
|
const latestConfig = container?.current ?? config;
|
|
171
172
|
const codexConfig = getAgentConfig(latestConfig, 'codex');
|
|
172
173
|
return new CodexExecRunner({
|
|
173
|
-
binary: codexConfig?.binary ?? 'codex',
|
|
174
174
|
model: codexConfig?.model,
|
|
175
175
|
modelProvider: codexConfig?.modelProvider,
|
|
176
176
|
reasoningEffort: codexConfig?.reasoningEffort,
|
|
@@ -182,20 +182,17 @@ function initializeRunner(config, configDir, cliArgs) {
|
|
|
182
182
|
});
|
|
183
183
|
// Register OpencodeExecRunner (run mode)
|
|
184
184
|
// Using `opencode run --format json --auto` instead of HTTP+SSE server mode
|
|
185
|
-
const opencodeConfig = getAgentConfig(config, 'opencode');
|
|
186
185
|
// Register session reader (same instance as used by runner)
|
|
187
186
|
const sessionReaderRegistry = new SessionReaderRegistry();
|
|
188
187
|
sessionReaderRegistry.register('claude', new ClaudeSessionReader());
|
|
189
188
|
sessionReaderRegistry.register('codex', codexSessionReader);
|
|
190
189
|
// Register OpencodeSessionReader (CLI version)
|
|
191
190
|
// Using `opencode session list` and `opencode export` instead of HTTP API
|
|
192
|
-
const opencodeSessionReader = new OpencodeSessionReader(
|
|
193
|
-
binary: opencodeConfig?.binary ?? 'opencode',
|
|
194
|
-
});
|
|
191
|
+
const opencodeSessionReader = new OpencodeSessionReader();
|
|
195
192
|
agentRegistry.register('opencode', (ws) => {
|
|
196
193
|
// Get latest config from container (set below for pi)
|
|
197
194
|
const container = agentRegistry.getConfigContainer();
|
|
198
|
-
const latestConfig = container?.current;
|
|
195
|
+
const latestConfig = container?.current ?? config;
|
|
199
196
|
const ocConfig = getAgentConfig(latestConfig, 'opencode');
|
|
200
197
|
// Build model string with validation: provider/model format
|
|
201
198
|
let model;
|
|
@@ -210,7 +207,6 @@ function initializeRunner(config, configDir, cliArgs) {
|
|
|
210
207
|
}
|
|
211
208
|
}
|
|
212
209
|
return new OpencodeExecRunner({
|
|
213
|
-
binary: ocConfig?.binary ?? 'opencode',
|
|
214
210
|
model,
|
|
215
211
|
// P1-15: read stopGraceMs from latestConfig (not startup closure)
|
|
216
212
|
stopGraceMs: latestConfig.claude?.stopGraceMs ?? DEFAULT_STOP_GRACE_MS,
|
|
@@ -230,10 +226,9 @@ function initializeRunner(config, configDir, cliArgs) {
|
|
|
230
226
|
agentRegistry.register('pi', (ws) => {
|
|
231
227
|
// 每次 factory 调用时从 registry 获取最新 config
|
|
232
228
|
const container = agentRegistry.getConfigContainer();
|
|
233
|
-
const latestConfig = container?.current;
|
|
229
|
+
const latestConfig = container?.current ?? config;
|
|
234
230
|
const piConf = getAgentConfig(latestConfig, 'pi');
|
|
235
231
|
return new PiRunner({
|
|
236
|
-
binary: piConf?.binary ?? 'pi',
|
|
237
232
|
provider: piConf?.provider ?? 'Volcano',
|
|
238
233
|
model: piConf?.model ?? 'glm-5.2',
|
|
239
234
|
thinking: piConf?.thinking ?? 'medium',
|
|
@@ -252,12 +247,12 @@ function initializeRunner(config, configDir, cliArgs) {
|
|
|
252
247
|
agentRegistry.register('kimi', (ws) => {
|
|
253
248
|
// 每次 factory 调用时从 registry 获取最新 config
|
|
254
249
|
const container = agentRegistry.getConfigContainer();
|
|
255
|
-
const latestConfig = container?.current;
|
|
250
|
+
const latestConfig = container?.current ?? config;
|
|
256
251
|
const kimiConf = getAgentConfig(latestConfig, 'kimi');
|
|
257
252
|
return new KimiRunner({
|
|
258
|
-
binary: kimiConf?.binary ?? 'kimi',
|
|
259
253
|
model: kimiConf?.model ?? 'kimi-code/k3',
|
|
260
254
|
thinkingEffort: kimiConf?.thinkingEffort ?? 'max',
|
|
255
|
+
// P1-15: read stopGraceMs from latestConfig (not startup closure)
|
|
261
256
|
stopGraceMs: latestConfig.claude?.stopGraceMs ?? DEFAULT_STOP_GRACE_MS,
|
|
262
257
|
pidDir: configDir,
|
|
263
258
|
workspace: ws,
|
|
@@ -273,10 +268,25 @@ function initializeRunner(config, configDir, cliArgs) {
|
|
|
273
268
|
agentRegistry.registerDisplayName('kimi', 'Kimi');
|
|
274
269
|
// 设置全局 registry,供 agentDisplayName 等全局函数使用
|
|
275
270
|
AgentRegistry.setGlobalInstance(agentRegistry);
|
|
271
|
+
// Probe agent CLI availability (fire-and-forget, populates cache for /config card).
|
|
272
|
+
// Don't block startup — log unavailable agents as warnings.
|
|
273
|
+
probeAllAgents()
|
|
274
|
+
.then((availability) => {
|
|
275
|
+
const unavailable = [...availability.entries()].filter(([, ok]) => !ok).map(([kind]) => kind);
|
|
276
|
+
if (unavailable.length > 0) {
|
|
277
|
+
getLogger().warn(`[probe] agent CLI not found or not functional: ${unavailable.join(', ')}`);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
getLogger().info('[probe] all agent CLIs available');
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
.catch(() => {
|
|
284
|
+
// Probe failure is non-fatal; /config card will retry on open.
|
|
285
|
+
});
|
|
276
286
|
return { agentRegistry, sessionReaderRegistry };
|
|
277
287
|
}
|
|
278
288
|
/** Setup message and card action handlers. */
|
|
279
|
-
function setupMessageHandlers(connector, router, bridge, sessionStore, workspaceStore, binder, logger) {
|
|
289
|
+
function setupMessageHandlers(connector, router, bridge, sessionStore, workspaceStore, binder, logger, config) {
|
|
280
290
|
connector.setMessageHandler((msg) => {
|
|
281
291
|
// 绑定/授权闸门:仅 owner 放行;非 owner 静默丢弃;未绑定时要求 PIN 认领
|
|
282
292
|
const decision = binder.classify(msg.userId, msg.content, msg.chatId);
|
|
@@ -286,9 +296,50 @@ function setupMessageHandlers(connector, router, bridge, sessionStore, workspace
|
|
|
286
296
|
if (decision.kind === 'pin_wrong')
|
|
287
297
|
return;
|
|
288
298
|
if (decision.kind === 'bind_success') {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
299
|
+
// First-run onboarding: set default cwd + send welcome + Help card.
|
|
300
|
+
// setCwd is synchronous (outside the async closure) so that the user's
|
|
301
|
+
// next message — which will hit the `owner` branch now that
|
|
302
|
+
// startup-contact.json has been written — finds cwd already set.
|
|
303
|
+
// process.cwd() is the only sensible default: the user started
|
|
304
|
+
// lark-remote in the directory they want to work in.
|
|
305
|
+
// setCwd only when no cwd is set yet (e.g. fresh start after bind).
|
|
306
|
+
// Don't overwrite an existing cwd — the user may have already set one
|
|
307
|
+
// via /cd or a previous bind. Use realpath for canonical form (matching
|
|
308
|
+
// Claude JSONL cwd, e.g. /tmp → /private/tmp on macOS).
|
|
309
|
+
let resolvedCwd;
|
|
310
|
+
try {
|
|
311
|
+
resolvedCwd = fs.realpathSync(process.cwd());
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
resolvedCwd = process.cwd();
|
|
315
|
+
}
|
|
316
|
+
if (!sessionStore.getCwd(msg.userId)) {
|
|
317
|
+
sessionStore.setCwd(msg.userId, resolvedCwd);
|
|
318
|
+
}
|
|
319
|
+
void (async () => {
|
|
320
|
+
try {
|
|
321
|
+
// Message 1: bind confirmation + status (combined to avoid
|
|
322
|
+
// Feishu out-of-order delivery across three separate messages).
|
|
323
|
+
await connector.sendWithRetry(msg.chatId, {
|
|
324
|
+
text: `✅ 已绑定到本账号,此后仅你可使用本应用\n` +
|
|
325
|
+
`📂 当前工作目录: \`${resolvedCwd}\`\n` +
|
|
326
|
+
`🤖 默认 Agent: ${agentDisplayName(config.defaultAgent)}\n` +
|
|
327
|
+
`💡 直接输入消息即可开始对话,或 /cd 切换目录`,
|
|
328
|
+
});
|
|
329
|
+
// Message 2: Help card.
|
|
330
|
+
// Use router.cmdHelp() + manual send (without replyTo) so the Help
|
|
331
|
+
// card appears as an independent message, not a reply to the PIN
|
|
332
|
+
// message. router.handle('/help') would set replyTo=msg.messageId
|
|
333
|
+
// (the PIN), causing the card to nest under the PIN in Feishu UI.
|
|
334
|
+
const helpResult = router.cmdHelp();
|
|
335
|
+
if (helpResult.card) {
|
|
336
|
+
await connector.sendWithRetry(msg.chatId, { card: helpResult.card });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
logger.error('[binder] onboarding send failed:', err);
|
|
341
|
+
}
|
|
342
|
+
})();
|
|
292
343
|
return;
|
|
293
344
|
}
|
|
294
345
|
// decision.kind === 'owner':正常处理(不再每条覆盖 startup-contact)
|
|
@@ -343,7 +394,9 @@ function setupMessageHandlers(connector, router, bridge, sessionStore, workspace
|
|
|
343
394
|
if (!workspace && workspaceStore) {
|
|
344
395
|
const workspaces = workspaceStore.list();
|
|
345
396
|
if (workspaces.length > 0) {
|
|
346
|
-
|
|
397
|
+
// NOTE: fallback uses insertion order, not sort preference — by design
|
|
398
|
+
// (workspace-sorting.md §9: cwd fallback stays insertion-order for now)
|
|
399
|
+
workspace = workspaces[0].path;
|
|
347
400
|
}
|
|
348
401
|
}
|
|
349
402
|
// Step4/D4: 入队时刻(T0)快照 agent+session,随任务闭包带到 T1 执行时刻,
|
|
@@ -486,7 +539,7 @@ async function main() {
|
|
|
486
539
|
logger.info('config loaded');
|
|
487
540
|
logger.info(`configDir = ${configDir}`);
|
|
488
541
|
logger.info(`feishu.appId = ${config.feishu.appId}`);
|
|
489
|
-
logger.info(`claude.
|
|
542
|
+
logger.info(`claude.model = ${config.claude.model}`);
|
|
490
543
|
if (cliArgs.settings) {
|
|
491
544
|
logger.info(`claude.settings = ${cliArgs.settings}`);
|
|
492
545
|
}
|
|
@@ -522,7 +575,7 @@ async function main() {
|
|
|
522
575
|
restartSpawner: () => spawnReplacementBridge(path.join(configDir, 'logs')),
|
|
523
576
|
sessionReaderRegistry,
|
|
524
577
|
});
|
|
525
|
-
setupMessageHandlers(connector, router, bridge, sessionStore, workspaceStore, binder, logger);
|
|
578
|
+
setupMessageHandlers(connector, router, bridge, sessionStore, workspaceStore, binder, logger, config);
|
|
526
579
|
try {
|
|
527
580
|
await connector.connect();
|
|
528
581
|
}
|
|
@@ -532,7 +585,7 @@ async function main() {
|
|
|
532
585
|
}
|
|
533
586
|
if (binder.isBound()) {
|
|
534
587
|
// 仅已绑定时发送启动通知;未绑定时不打扰(PIN 引导已在控制台输出)
|
|
535
|
-
await sendStartupHello(connector, startupContactStore);
|
|
588
|
+
await sendStartupHello(connector, startupContactStore, { dev: cliArgs.dev });
|
|
536
589
|
}
|
|
537
590
|
// Auto-restore: resume the persisted sessionId if available
|
|
538
591
|
const restoredContact = startupContactStore.getContact();
|
|
@@ -6,14 +6,12 @@ export class CodexConfigBuilder {
|
|
|
6
6
|
buildFields(displayConfig) {
|
|
7
7
|
const fields = [];
|
|
8
8
|
// 从 codex config.toml 动态读取 provider 和 model
|
|
9
|
-
const
|
|
10
|
-
const codexCfg = loadCodexConfig({ binary: codexBinary });
|
|
9
|
+
const codexCfg = loadCodexConfig();
|
|
11
10
|
const codexProviderNames = codexCfg.providerNames;
|
|
12
11
|
const currentModelProvider = displayConfig.agents?.codex?.modelProvider ?? codexCfg.currentProvider;
|
|
13
12
|
const currentCodexModel = displayConfig.agents?.codex?.model ?? codexCfg.currentModel;
|
|
14
|
-
const currentReasoningEffort = displayConfig.agents?.codex?.reasoningEffort ??
|
|
15
|
-
|
|
16
|
-
// 传入当前 provider 过滤模型列���
|
|
13
|
+
const currentReasoningEffort = displayConfig.agents?.codex?.reasoningEffort ?? getDefaultReasoningEffort(currentCodexModel);
|
|
14
|
+
// 传入当前 provider 过滤模型列表
|
|
17
15
|
const codexModelOptions = codexCfg.modelOptions(currentModelProvider);
|
|
18
16
|
fields.push({
|
|
19
17
|
key: 'agents.codex.modelProvider',
|
|
@@ -42,7 +40,7 @@ export class CodexConfigBuilder {
|
|
|
42
40
|
label: '推理强度',
|
|
43
41
|
type: 'select',
|
|
44
42
|
// 档位按目录实际声明透传(P2-5):codex 支持 none/Custom,不再按标准枚举过滤
|
|
45
|
-
options: getReasoningEffortOptions(currentCodexModel
|
|
43
|
+
options: getReasoningEffortOptions(currentCodexModel),
|
|
46
44
|
currentValue: currentReasoningEffort,
|
|
47
45
|
});
|
|
48
46
|
return fields;
|
|
@@ -50,10 +48,9 @@ export class CodexConfigBuilder {
|
|
|
50
48
|
handleFieldChange(key, value, config) {
|
|
51
49
|
const patches = [];
|
|
52
50
|
patches.push({ key, value });
|
|
53
|
-
// provider 变更时,重置 model
|
|
51
|
+
// provider 变更时,重置 model 为新 provider 的首个模型
|
|
54
52
|
if (key === 'agents.codex.modelProvider' && typeof value === 'string') {
|
|
55
|
-
const
|
|
56
|
-
const codexCfg = loadCodexConfig({ binary: codexBinary });
|
|
53
|
+
const codexCfg = loadCodexConfig();
|
|
57
54
|
const newModelOptions = codexCfg.modelOptions(value);
|
|
58
55
|
const currentModel = config.agents?.codex?.model;
|
|
59
56
|
const currentModelIsValid = newModelOptions.some((m) => m === currentModel);
|
|
@@ -86,14 +83,13 @@ export class CodexConfigBuilder {
|
|
|
86
83
|
* 对 undefined 做键删除,P1)。
|
|
87
84
|
*/
|
|
88
85
|
effortPatchForModel(config, newModel) {
|
|
89
|
-
const codexBinary = config.agents?.codex?.binary ?? 'codex';
|
|
90
86
|
const currentReasoningEffort = config.agents?.codex?.reasoningEffort;
|
|
91
|
-
const newModelSupportedEfforts = getReasoningEffortOptions(newModel
|
|
87
|
+
const newModelSupportedEfforts = getReasoningEffortOptions(newModel);
|
|
92
88
|
const isCurrentEffortValid = currentReasoningEffort && newModelSupportedEfforts.includes(currentReasoningEffort);
|
|
93
89
|
if (isCurrentEffortValid)
|
|
94
90
|
return null;
|
|
95
91
|
const middle = newModelSupportedEfforts[Math.floor((newModelSupportedEfforts.length - 1) / 2)];
|
|
96
|
-
const newEffort = middle ?? getDefaultReasoningEffort(newModel
|
|
92
|
+
const newEffort = middle ?? getDefaultReasoningEffort(newModel);
|
|
97
93
|
return { key: 'agents.codex.reasoningEffort', value: newEffort };
|
|
98
94
|
}
|
|
99
95
|
}
|
|
@@ -16,3 +16,13 @@ import type { AgentConfigCardBuilder } from './types.js';
|
|
|
16
16
|
export declare function getConfigBuilder(agent: AgentKind): AgentConfigCardBuilder;
|
|
17
17
|
/** List all registered agent kinds. */
|
|
18
18
|
export declare function listRegisteredAgents(): AgentKind[];
|
|
19
|
+
/**
|
|
20
|
+
* Sort agent kinds for UI lists: agents that are definitively unavailable
|
|
21
|
+
* (availability === false) sink to the bottom; everything else keeps its
|
|
22
|
+
* input order. The sort is stable, so relative order within both groups is
|
|
23
|
+
* preserved (e.g. registration order in the config card selector).
|
|
24
|
+
*
|
|
25
|
+
* `undefined` (probe cache missing/expired) is treated as "keep in place",
|
|
26
|
+
* so a stale cache cannot misplace installed agents.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sortAgentsByAvailability(kinds: AgentKind[], isAvailable: (kind: AgentKind) => boolean | undefined): AgentKind[];
|
|
@@ -35,4 +35,21 @@ export function getConfigBuilder(agent) {
|
|
|
35
35
|
export function listRegisteredAgents() {
|
|
36
36
|
return Object.keys(builders);
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Sort agent kinds for UI lists: agents that are definitively unavailable
|
|
40
|
+
* (availability === false) sink to the bottom; everything else keeps its
|
|
41
|
+
* input order. The sort is stable, so relative order within both groups is
|
|
42
|
+
* preserved (e.g. registration order in the config card selector).
|
|
43
|
+
*
|
|
44
|
+
* `undefined` (probe cache missing/expired) is treated as "keep in place",
|
|
45
|
+
* so a stale cache cannot misplace installed agents.
|
|
46
|
+
*/
|
|
47
|
+
export function sortAgentsByAvailability(kinds, isAvailable) {
|
|
48
|
+
const unavailable = new Set(kinds.filter((kind) => isAvailable(kind) === false));
|
|
49
|
+
return [...kinds].sort((a, b) => {
|
|
50
|
+
const rankA = unavailable.has(a) ? 1 : 0;
|
|
51
|
+
const rankB = unavailable.has(b) ? 1 : 0;
|
|
52
|
+
return rankA - rankB;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
38
55
|
//# sourceMappingURL=index.js.map
|
package/dist/router/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ interface CommandResult {
|
|
|
17
17
|
* These bypass the serial queue and execute immediately.
|
|
18
18
|
*
|
|
19
19
|
* ws.use — /ws list card "使用 <name>"
|
|
20
|
+
* ws.page — /ws list card "上一页/下一页"
|
|
20
21
|
* queue.immediate — queue card "立即执行" button (§9.6)
|
|
21
22
|
* queue.cancel — queue card "撤销" button
|
|
22
23
|
* queue.diagnose — queue card "诊断" button
|
|
@@ -25,6 +26,7 @@ interface CommandResult {
|
|
|
25
26
|
* new-session — clear sessionId only
|
|
26
27
|
* stop — interrupt current run
|
|
27
28
|
* ls.file — send file only
|
|
29
|
+
* ws.page — paginate /ws list only
|
|
28
30
|
* ws.remove — delete workspace alias only
|
|
29
31
|
* resume.use — set sessionId + agent for correct reader routing
|
|
30
32
|
* help.* — read-only help commands
|
|
@@ -73,6 +75,8 @@ interface CardActionResponse {
|
|
|
73
75
|
[key: string]: unknown;
|
|
74
76
|
}
|
|
75
77
|
export declare class CommandRouter {
|
|
78
|
+
/** Valid agent kinds — single source of truth for resume.use / resume.page / cmdResume. */
|
|
79
|
+
static readonly VALID_AGENTS: readonly ["claude", "codex", "opencode", "pi", "kimi"];
|
|
76
80
|
private sessionStore;
|
|
77
81
|
private bridge;
|
|
78
82
|
private config;
|
|
@@ -96,6 +100,11 @@ export declare class CommandRouter {
|
|
|
96
100
|
* millisecond); the counter guarantees uniqueness regardless of timing.
|
|
97
101
|
*/
|
|
98
102
|
private orderExecKeyCounter;
|
|
103
|
+
/**
|
|
104
|
+
* Per-user sort preference for /ws list (memory-only, not persisted).
|
|
105
|
+
* Default 'recent' (most recently used first), can toggle to 'alpha'.
|
|
106
|
+
*/
|
|
107
|
+
private wsSortPreference;
|
|
99
108
|
/**
|
|
100
109
|
* Session reader registry for multi-agent path. Resolves the right
|
|
101
110
|
* reader by `config.defaultAgent` (and per-agent override params).
|
|
@@ -187,6 +196,11 @@ export declare class CommandRouter {
|
|
|
187
196
|
* Mirrors handleLsPage — updates the same card, never sends a new one.
|
|
188
197
|
*/
|
|
189
198
|
private handleResumePage;
|
|
199
|
+
/**
|
|
200
|
+
* Handle active.page: paginate the /active run list in place.
|
|
201
|
+
* Mirrors handleResumePage — updates the same card, never sends a new one.
|
|
202
|
+
*/
|
|
203
|
+
private handleActivePage;
|
|
190
204
|
/**
|
|
191
205
|
* Handle ls.browse: browse directory without switching cwd.
|
|
192
206
|
*/
|
|
@@ -223,12 +237,36 @@ export declare class CommandRouter {
|
|
|
223
237
|
* Handle order.delete: remove the order and update card in place.
|
|
224
238
|
*/
|
|
225
239
|
private handleOrderDelete;
|
|
240
|
+
/**
|
|
241
|
+
* Handle order.page: paginate the /order list in place.
|
|
242
|
+
* Mirrors handleLsPage — updates the same card, never sends a new one.
|
|
243
|
+
*/
|
|
244
|
+
private handleOrderPage;
|
|
245
|
+
/**
|
|
246
|
+
* Handle ws.use card action: switch to the workspace, then refresh the /ws
|
|
247
|
+
* list card in place so "recent" sort order is immediately visible.
|
|
248
|
+
*
|
|
249
|
+
* Unlike the command-line `/ws use` (which returns text), the card action
|
|
250
|
+
* uses toast + inplace refresh for a cleaner UX (matching ws.remove/ws.sort).
|
|
251
|
+
* If auto-resume produces a card, it is sent as a separate reply.
|
|
252
|
+
*/
|
|
253
|
+
private handleWsUse;
|
|
226
254
|
/**
|
|
227
255
|
* Handle ws.remove: remove the workspace alias and refresh the /ws list card
|
|
228
256
|
* in place (mirrors handleOrderDelete). Without this the stale alias would
|
|
229
257
|
* remain visible on the card the user just clicked.
|
|
230
258
|
*/
|
|
231
259
|
private handleWsRemove;
|
|
260
|
+
/**
|
|
261
|
+
* Handle ws.page: paginate the /ws list in place.
|
|
262
|
+
* Mirrors handleOrderPage — updates the same card, never sends a new one.
|
|
263
|
+
*/
|
|
264
|
+
private handleWsPage;
|
|
265
|
+
/**
|
|
266
|
+
* Handle ws.sort: toggle sort preference and refresh the /ws list card.
|
|
267
|
+
* Sort preference is memory-only (not persisted); restart resets to 'recent'.
|
|
268
|
+
*/
|
|
269
|
+
private handleWsSort;
|
|
232
270
|
/**
|
|
233
271
|
* `ls.file` card button: send file to Feishu, rejecting if > 30MB.
|
|
234
272
|
*/
|
|
@@ -273,7 +311,7 @@ export declare class CommandRouter {
|
|
|
273
311
|
*/
|
|
274
312
|
private propagateConfigSave;
|
|
275
313
|
private executeCommand;
|
|
276
|
-
|
|
314
|
+
cmdHelp(): CommandResult;
|
|
277
315
|
private cmdStatus;
|
|
278
316
|
private cmdPs;
|
|
279
317
|
private cmdStop;
|
|
@@ -307,6 +345,7 @@ export declare class CommandRouter {
|
|
|
307
345
|
/**
|
|
308
346
|
* Build CardKit 2.0 /active card from bridge memory (not file system).
|
|
309
347
|
* Shows only runs that were started by THIS bridge process.
|
|
348
|
+
* Paginates with ACTIVE_PAGE_SIZE (default 20) items per page.
|
|
310
349
|
*/
|
|
311
350
|
private buildActiveCardFromMemory;
|
|
312
351
|
private cmdReconnect;
|