lark-coding-assistant 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -208,6 +208,8 @@ lca daemon restart
208
208
 
209
209
  `daemon stop/restart` 只断开或恢复飞书连接,不停止受管 tmux session。顶层 `stop [name]` 才会停止对应 agent 和 tmux session。
210
210
 
211
+ `lca status` 会先显示 daemon 状态,再用一个表格列出全部 session;session 按 `codex`、`traex`、`claude` 分组排序,每个 session 占一行,当前连接使用 `●` 标记。表格同时显示终端状态、tmux 状态和绝对工作目录。使用 `lca status <name>` 可只查看指定 session;daemon 不可用时仍会读取本地状态,并将无法确认的运行信息明确标出。
212
+
211
213
  ## 绑定规则
212
214
 
213
215
  首次扫码会保存可信 owner。后续启动会自动沿用该 owner 和私聊,通常不再需要绑定码。
package/dist/cli.js CHANGED
@@ -1024,6 +1024,152 @@ function validSessionId(value) {
1024
1024
  return /^[a-zA-Z0-9_-]{1,40}$/.test(value);
1025
1025
  }
1026
1026
 
1027
+ // src/cli-status.ts
1028
+ var HEADERS = ["current", "agent", "session", "status", "tmux", "cwd"];
1029
+ var LABELS = {
1030
+ current: "\u5F53\u524D",
1031
+ agent: "Agent",
1032
+ session: "Session",
1033
+ status: "\u72B6\u6001",
1034
+ tmux: "tmux",
1035
+ cwd: "\u5DE5\u4F5C\u76EE\u5F55"
1036
+ };
1037
+ function formatCliStatus(report) {
1038
+ const daemon = [
1039
+ `Daemon: ${report.daemon.status}`,
1040
+ ...report.daemon.pid ? [`PID ${report.daemon.pid}`] : [],
1041
+ ...report.daemon.version ? [`daemon ${report.daemon.version}`] : [],
1042
+ `CLI ${report.cliVersion}`
1043
+ ].join(" \xB7 ");
1044
+ const sessions = [...report.sessions].sort(compareRuntimeSessions);
1045
+ if (report.requestedSessionId && sessions.length === 0) {
1046
+ return `${daemon}
1047
+ Sessions: 0
1048
+
1049
+ \u672A\u627E\u5230 session\u300C${sanitize(report.requestedSessionId)}\u300D\uFF0C\u8BF7\u8FD0\u884C lca status \u67E5\u770B\u5168\u90E8 session\u3002`;
1050
+ }
1051
+ if (sessions.length === 0) return `${daemon}
1052
+ Sessions: 0
1053
+
1054
+ \u6682\u65E0\u53D7\u7BA1 session\u3002`;
1055
+ const rows = sessions.map(toStatusRow);
1056
+ return `${daemon}
1057
+ Sessions: ${rows.length}
1058
+
1059
+ ${renderTable(rows, report.columns ?? 120)}`;
1060
+ }
1061
+ function fallbackRuntimeSessions(state, requestedSessionId) {
1062
+ return Object.values(state.sessions ?? {}).filter(({ id }) => !requestedSessionId || id === requestedSessionId).map((session) => ({
1063
+ session,
1064
+ active: session.id === state.activeSessionId
1065
+ }));
1066
+ }
1067
+ function compareRuntimeSessions(left, right) {
1068
+ const agent = AGENT_IDS.indexOf(left.session.agent) - AGENT_IDS.indexOf(right.session.agent);
1069
+ return agent || left.session.id.localeCompare(right.session.id);
1070
+ }
1071
+ function toStatusRow(runtime) {
1072
+ return {
1073
+ current: runtime.active ? "\u25CF" : "",
1074
+ agent: runtime.session.agent,
1075
+ session: runtime.session.id,
1076
+ status: runtimeStatusLabel(runtime),
1077
+ tmux: runtime.paneAlive === void 0 ? "\u65E0\u6CD5\u786E\u8BA4" : runtime.paneAlive ? "\u8FD0\u884C\u4E2D" : "\u5DF2\u505C\u6B62",
1078
+ cwd: runtime.session.cwd
1079
+ };
1080
+ }
1081
+ function runtimeStatusLabel(runtime) {
1082
+ if (runtime.paneAlive === void 0) return "\u65E0\u6CD5\u786E\u8BA4";
1083
+ if (!runtime.paneAlive || runtime.screen?.state === "exited") return "\u5DF2\u505C\u6B62";
1084
+ if (runtime.screen?.state === "failed") return "\u6267\u884C\u5931\u8D25";
1085
+ if (runtime.screen?.interaction?.kind === "approval") return "\u7B49\u5F85\u5BA1\u6279";
1086
+ if (runtime.screen?.interaction?.kind === "question") return "\u7B49\u5F85\u56DE\u7B54";
1087
+ if (runtime.screen?.interaction?.kind === "choice") return "\u7B49\u5F85\u9009\u62E9";
1088
+ if (runtime.screen?.state === "input") return "\u7B49\u5F85\u8F93\u5165";
1089
+ if (runtime.screen?.state === "running") return "\u6267\u884C\u4E2D";
1090
+ if (runtime.screen?.state === "starting") return "\u542F\u52A8\u4E2D";
1091
+ if (runtime.screen?.state === "idle") return "\u7B49\u5F85\u7528\u6237\u8F93\u5165";
1092
+ return "\u72B6\u6001\u672A\u77E5";
1093
+ }
1094
+ function renderTable(rows, terminalColumns) {
1095
+ const desired = HEADERS.map((key) => Math.max(
1096
+ displayWidth(LABELS[key]),
1097
+ ...rows.map((row) => displayWidth(sanitize(row[key])))
1098
+ ));
1099
+ const widths = [
1100
+ desired[0] ?? 4,
1101
+ desired[1] ?? 6,
1102
+ Math.min(desired[2] ?? 7, 24),
1103
+ desired[3] ?? 4,
1104
+ desired[4] ?? 4,
1105
+ desired[5] ?? 8
1106
+ ];
1107
+ const fixedWidth = widths.slice(0, -1).reduce((sum, width) => sum + width, 0);
1108
+ const tableOverhead = HEADERS.length * 3 + 1;
1109
+ const cwdAvailable = terminalColumns - fixedWidth - tableOverhead;
1110
+ widths[5] = Math.max(18, Math.min(widths[5] ?? 18, cwdAvailable));
1111
+ const border = (left, middle, right) => `${left}${widths.map((width) => "\u2500".repeat(width + 2)).join(middle)}${right}`;
1112
+ const line = (row) => `\u2502${HEADERS.map((key, index) => {
1113
+ const value = sanitize(row[key]);
1114
+ const width = widths[index] ?? 0;
1115
+ return ` ${fit(value, width, key === "cwd")} `;
1116
+ }).join("\u2502")}\u2502`;
1117
+ return [
1118
+ border("\u250C", "\u252C", "\u2510"),
1119
+ line(LABELS),
1120
+ border("\u251C", "\u253C", "\u2524"),
1121
+ ...rows.map(line),
1122
+ border("\u2514", "\u2534", "\u2518")
1123
+ ].join("\n");
1124
+ }
1125
+ function fit(value, width, middle = false) {
1126
+ const fitted = displayWidth(value) <= width ? value : middle ? truncateMiddle(value, width) : `${takeStart(value, Math.max(0, width - 1))}\u2026`;
1127
+ return `${fitted}${" ".repeat(Math.max(0, width - displayWidth(fitted)))}`;
1128
+ }
1129
+ function truncateMiddle(value, width) {
1130
+ if (width <= 1) return "\u2026".slice(0, width);
1131
+ const remaining = width - 1;
1132
+ const leftWidth = Math.ceil(remaining / 2);
1133
+ return `${takeStart(value, leftWidth)}\u2026${takeEnd(value, remaining - leftWidth)}`;
1134
+ }
1135
+ function takeStart(value, width) {
1136
+ let result2 = "";
1137
+ let used = 0;
1138
+ for (const character of value) {
1139
+ const characterWidth = displayWidth(character);
1140
+ if (used + characterWidth > width) break;
1141
+ result2 += character;
1142
+ used += characterWidth;
1143
+ }
1144
+ return result2;
1145
+ }
1146
+ function takeEnd(value, width) {
1147
+ let result2 = "";
1148
+ let used = 0;
1149
+ for (const character of [...value].reverse()) {
1150
+ const characterWidth = displayWidth(character);
1151
+ if (used + characterWidth > width) break;
1152
+ result2 = character + result2;
1153
+ used += characterWidth;
1154
+ }
1155
+ return result2;
1156
+ }
1157
+ function displayWidth(value) {
1158
+ let width = 0;
1159
+ for (const character of value) {
1160
+ const code = character.codePointAt(0) ?? 0;
1161
+ if (/\p{Mark}/u.test(character)) continue;
1162
+ width += isWide(code) ? 2 : 1;
1163
+ }
1164
+ return width;
1165
+ }
1166
+ function isWide(code) {
1167
+ return code >= 4352 && (code <= 4447 || code === 9001 || code === 9002 || code >= 11904 && code <= 42191 && code !== 12351 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65049 || code >= 65072 && code <= 65135 || code >= 65280 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 129791 || code >= 131072 && code <= 262141);
1168
+ }
1169
+ function sanitize(value) {
1170
+ return value.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, "").replace(/[\u0000-\u001f\u007f]+/g, " ").trim();
1171
+ }
1172
+
1027
1173
  // src/cli.ts
1028
1174
  var program = new Command();
1029
1175
  var paths = resolveAppPaths();
@@ -1193,17 +1339,30 @@ async function attachLocal(name) {
1193
1339
  }
1194
1340
  async function runStatus(name) {
1195
1341
  try {
1196
- const response = await requestDaemon(paths.socket, { method: "status", sessionId: name });
1342
+ const [response, health] = await Promise.all([
1343
+ requestDaemon(paths.socket, { method: "status", sessionId: name }),
1344
+ daemonHealth(paths)
1345
+ ]);
1197
1346
  if (!response.ok) throw new Error(response.error);
1198
- console.log(JSON.stringify(response.value, null, 2));
1347
+ const runtime = response.value;
1348
+ console.log(formatCliStatus({
1349
+ daemon: health.status === "running" ? { status: "running", pid: health.info.pid, version: health.info.version } : health,
1350
+ cliVersion: packageInfo.version,
1351
+ state: runtime.state,
1352
+ sessions: runtime.sessions ?? fallbackRuntimeSessions(runtime.state, name),
1353
+ requestedSessionId: name,
1354
+ columns: process.stdout.columns
1355
+ }));
1199
1356
  } catch {
1200
- const state = await store.loadState();
1201
- const health = await daemonHealth(paths);
1202
- console.log(JSON.stringify({
1203
- daemon: health.status,
1204
- ...health.status === "unresponsive" ? { daemonPid: health.pid } : {},
1205
- state
1206
- }, null, 2));
1357
+ const [state, health] = await Promise.all([store.loadState(), daemonHealth(paths)]);
1358
+ console.log(formatCliStatus({
1359
+ daemon: health.status === "running" ? { status: "running", pid: health.info.pid, version: health.info.version } : health,
1360
+ cliVersion: packageInfo.version,
1361
+ state,
1362
+ sessions: fallbackRuntimeSessions(state, name),
1363
+ requestedSessionId: name,
1364
+ columns: process.stdout.columns
1365
+ }));
1207
1366
  }
1208
1367
  }
1209
1368
  async function runStop(name) {