Skip to content

Commit ae07f29

Browse files
author
Joshua Chittick
committed
fix: improve daemon log controls and Lark live status formatting
1 parent e465a24 commit ae07f29

File tree

3 files changed

+98
-5
lines changed

3 files changed

+98
-5
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ode",
3-
"version": "0.0.89",
3+
"version": "0.0.90",
44
"description": "Coding anywhere with your coding agents connected",
55
"module": "packages/core/index.ts",
66
"type": "module",

packages/core/cli.ts

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env bun
22

33
import { spawn } from "child_process";
4+
import { existsSync, readFileSync } from "fs";
45
import packageJson from "../../package.json" with { type: "json" };
56
import { getWebHost, getWebPort } from "@/config";
67
import { runDaemon } from "@/core/daemon/manager";
@@ -37,6 +38,7 @@ function printHelp(): void {
3738
"Usage:",
3839
" ode [--foreground]",
3940
" ode status",
41+
" ode log [--info|--error] [--tail [N]]",
4042
" ode restart",
4143
" ode stop",
4244
" ode onboard",
@@ -48,6 +50,8 @@ function printHelp(): void {
4850
"Examples:",
4951
" ode",
5052
" ode status",
53+
" ode log --error",
54+
" ode log --tail 200",
5155
" ode restart",
5256
" ode stop",
5357
" ode onboard",
@@ -144,7 +148,7 @@ async function waitForStopped(timeoutMs: number): Promise<boolean> {
144148
async function startBackground(): Promise<void> {
145149
const state = daemonState();
146150
if (state.status === "ready" && state.readyMessage && managerRunning(state)) {
147-
console.log(state.readyMessage);
151+
console.log(fallbackReadyMessage());
148152
return;
149153
}
150154
ensureDaemonRunning();
@@ -161,6 +165,83 @@ function formatTimestamp(value: number | null): string {
161165
return new Date(value).toLocaleString();
162166
}
163167

168+
type LogFilterLevel = "all" | "info" | "error";
169+
170+
function parseLogFilterLevel(commandArgs: string[]): LogFilterLevel {
171+
if (commandArgs.includes("--error")) return "error";
172+
if (commandArgs.includes("--info")) return "info";
173+
return "all";
174+
}
175+
176+
function parseLogTailLimit(commandArgs: string[]): number | null {
177+
const tailWithValue = commandArgs.find((arg) => arg.startsWith("--tail="));
178+
if (tailWithValue) {
179+
const rawValue = tailWithValue.slice("--tail=".length).trim();
180+
const parsed = Number(rawValue);
181+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 200;
182+
}
183+
184+
const tailIndex = commandArgs.indexOf("--tail");
185+
if (tailIndex < 0) return null;
186+
187+
const nextArg = commandArgs[tailIndex + 1];
188+
if (!nextArg || nextArg.startsWith("--")) return 200;
189+
190+
const parsed = Number(nextArg);
191+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 200;
192+
}
193+
194+
function lineMatchesLogLevel(line: string, level: LogFilterLevel): boolean {
195+
if (level === "all") return true;
196+
197+
if (line.startsWith("{")) {
198+
try {
199+
const parsed = JSON.parse(line) as { level?: unknown };
200+
if (typeof parsed.level === "number") {
201+
if (level === "error") return parsed.level >= 50;
202+
return parsed.level >= 30;
203+
}
204+
} catch {
205+
// Ignore malformed JSON and use fallback matching.
206+
}
207+
}
208+
209+
if (level === "error") {
210+
return line.toLowerCase().includes("error")
211+
|| line.includes("Unhandled rejection")
212+
|| line.includes("Uncaught exception");
213+
}
214+
215+
return true;
216+
}
217+
218+
function showLogs(commandArgs: string[]): void {
219+
const logPath = getDaemonLogPath();
220+
if (!existsSync(logPath)) {
221+
console.log(`No daemon logs found yet at ${logPath}`);
222+
return;
223+
}
224+
225+
const filterLevel = parseLogFilterLevel(commandArgs);
226+
const tailLimit = parseLogTailLimit(commandArgs);
227+
const content = readFileSync(logPath, "utf8");
228+
if (content.length === 0) {
229+
console.log(`Daemon log is empty at ${logPath}`);
230+
return;
231+
}
232+
233+
const lines = content.split(/\r?\n/).filter((line) => line.length > 0);
234+
const filtered = lines.filter((line) => lineMatchesLogLevel(line, filterLevel));
235+
const output = tailLimit === null ? filtered : filtered.slice(-tailLimit);
236+
237+
if (output.length === 0) {
238+
console.log(`No ${filterLevel} logs found in ${logPath}`);
239+
return;
240+
}
241+
242+
console.log(output.join("\n"));
243+
}
244+
164245
async function showStatus(): Promise<void> {
165246
const state = daemonState();
166247
const daemonIsRunning = managerRunning(state);
@@ -173,7 +254,7 @@ async function showStatus(): Promise<void> {
173254
console.log("Upgrade: none pending");
174255
}
175256
if (daemonIsRunning) {
176-
console.log("ode is running, setting UI is running on localhost:9293...");
257+
console.log(`ode is running, setting UI is accessible at ${getLocalSettingsUrl()}`);
177258
return;
178259
}
179260
console.log("ode is installed but not running, can run it with ode");
@@ -271,6 +352,11 @@ if (command === "status") {
271352
process.exit(0);
272353
}
273354

355+
if (command === "log") {
356+
showLogs(args.slice(1));
357+
process.exit(0);
358+
}
359+
274360
if (command === "restart") {
275361
await restartDaemonCommand();
276362
process.exit(0);

packages/ims/lark/client.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ function buildLarkPostContent(text: string, asMarkdown: boolean): Record<string,
225225
};
226226
}
227227

228+
function shouldUseLarkMarkdown(text: string, asMarkdown: boolean): boolean {
229+
if (asMarkdown) return true;
230+
return /`[^`]+`|\*[^*]+\*|_[^_]+_|^\s*[-*]\s+/m.test(text);
231+
}
232+
228233
function stripLarkMentionMarkup(text: string): string {
229234
return text
230235
.replace(/<at\b[^>]*>.*?<\/at>/g, " ")
@@ -298,11 +303,12 @@ async function sendMessage(
298303
text: string,
299304
asMarkdown = true
300305
): Promise<string | undefined> {
306+
const useMarkdown = shouldUseLarkMarkdown(text, asMarkdown);
301307
return sendLarkMessage({
302308
channelId,
303309
threadId: threadId || "",
304310
msgType: "post",
305-
content: buildLarkPostContent(text, asMarkdown),
311+
content: buildLarkPostContent(text, useMarkdown),
306312
});
307313
}
308314

@@ -364,9 +370,10 @@ async function updateMessage(
364370
}
365371
}
366372

373+
const useMarkdown = shouldUseLarkMarkdown(text, asMarkdown);
367374
const payload = {
368375
msg_type: "post",
369-
content: JSON.stringify(buildLarkPostContent(text, asMarkdown)),
376+
content: JSON.stringify(buildLarkPostContent(text, useMarkdown)),
370377
};
371378

372379
try {

0 commit comments

Comments
 (0)