Skip to content

Commit e2ecc9c

Browse files
authored
feat: cleanup logging + agent logs (#166)
1 parent a54e936 commit e2ecc9c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+394
-203
lines changed

apps/array/src/api/generated.ts

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19467,30 +19467,3 @@ export class ApiClient {
1946719467
export function createApiClient(fetcher: Fetcher, baseUrl?: string) {
1946819468
return new ApiClient(fetcher).setBaseUrl(baseUrl ?? "");
1946919469
}
19470-
19471-
/**
19472-
Example usage:
19473-
const api = createApiClient((method, url, params) =>
19474-
fetch(url, { method, body: JSON.stringify(params) }).then((res) => res.json()),
19475-
);
19476-
api.get("/users").then((users) => console.log(users));
19477-
api.post("/users", { body: { name: "John" } }).then((user) => console.log(user));
19478-
api.put("/users/:id", { path: { id: 1 }, body: { name: "John" } }).then((user) => console.log(user));
19479-
19480-
// With error handling
19481-
const result = await api.get("/users/{id}", { path: { id: "123" }, withResponse: true });
19482-
if (result.ok) {
19483-
// Access data directly
19484-
const user = result.data;
19485-
console.log(user);
19486-
19487-
// Or use the json() method for compatibility
19488-
const userFromJson = await result.json();
19489-
console.log(userFromJson);
19490-
} else {
19491-
const error = result.data;
19492-
console.error(`Error ${result.status}:`, error);
19493-
}
19494-
*/
19495-
19496-
// </ApiClient>

apps/array/src/api/posthogClient.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import { logger } from "@renderer/lib/logger";
12
import type { LogEntry, RepositoryConfig, Task, TaskRun } from "@shared/types";
23
import { buildApiFetcher } from "./fetcher";
34
import { createApiClient, type Schemas } from "./generated";
45

6+
const log = logger.scope("posthog-client");
7+
58
export class PostHogAPIClient {
69
private api: ReturnType<typeof createApiClient>;
710
private _teamId: number | null = null;
@@ -195,7 +198,7 @@ export class PostHogAPIClient {
195198
const response = await fetch(logUrl);
196199

197200
if (!response.ok) {
198-
console.warn(
201+
log.warn(
199202
`Failed to fetch logs: ${response.status} ${response.statusText}`,
200203
);
201204
return [];
@@ -211,7 +214,7 @@ export class PostHogAPIClient {
211214
.split("\n")
212215
.map((line) => JSON.parse(line) as LogEntry);
213216
} catch (err) {
214-
console.warn("Failed to fetch task logs from latest run", err);
217+
log.warn("Failed to fetch task logs from latest run", err);
215218
return [];
216219
}
217220
}

apps/array/src/main/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
type MenuItemConstructorOptions,
1111
shell,
1212
} from "electron";
13+
import "./lib/logger";
1314
import { ANALYTICS_EVENTS } from "../types/analytics.js";
1415
import { registerAgentIpc, type TaskController } from "./services/agent.js";
1516
import { setupAgentHotReload } from "./services/dev-reload.js";

apps/array/src/main/lib/logger.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import log from "electron-log/main";
2+
3+
// Initialize IPC transport to forward main process logs to renderer dev tools
4+
log.initialize();
5+
6+
log.transports.file.level = "info";
7+
log.transports.console.level = "info";
8+
9+
export const logger = {
10+
info: (message: string, ...args: unknown[]) => log.info(message, ...args),
11+
warn: (message: string, ...args: unknown[]) => log.warn(message, ...args),
12+
error: (message: string, ...args: unknown[]) => log.error(message, ...args),
13+
debug: (message: string, ...args: unknown[]) => log.debug(message, ...args),
14+
15+
scope: (name: string) => {
16+
const scoped = log.scope(name);
17+
return {
18+
info: (message: string, ...args: unknown[]) =>
19+
scoped.info(message, ...args),
20+
warn: (message: string, ...args: unknown[]) =>
21+
scoped.warn(message, ...args),
22+
error: (message: string, ...args: unknown[]) =>
23+
scoped.error(message, ...args),
24+
debug: (message: string, ...args: unknown[]) =>
25+
scoped.debug(message, ...args),
26+
};
27+
},
28+
};
29+
30+
export type Logger = typeof logger;
31+
export type ScopedLogger = ReturnType<typeof logger.scope>;

apps/array/src/main/preload.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
TabContextMenuResult,
77
TaskContextMenuResult,
88
} from "./services/contextMenu.types.js";
9+
import "electron-log/preload";
910

1011
interface MessageBoxOptions {
1112
type?: "none" | "info" | "error" | "question" | "warning";

apps/array/src/main/services/agent.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,25 @@ import { randomUUID } from "node:crypto";
22
import { mkdirSync, rmSync, symlinkSync } from "node:fs";
33
import { tmpdir } from "node:os";
44
import { join } from "node:path";
5-
import { Agent, PermissionMode } from "@posthog/agent";
5+
import { Agent, type OnLogCallback, PermissionMode } from "@posthog/agent";
66
import {
77
app,
88
type BrowserWindow,
99
type IpcMainInvokeEvent,
1010
ipcMain,
1111
} from "electron";
12+
import { logger } from "../lib/logger";
13+
14+
const log = logger.scope("agent");
15+
16+
const onAgentLog: OnLogCallback = (level, scope, message, data) => {
17+
const scopedLog = logger.scope(scope);
18+
if (data !== undefined) {
19+
scopedLog[level](message, data);
20+
} else {
21+
scopedLog[level](message);
22+
}
23+
};
1224

1325
interface AgentStartParams {
1426
taskId: string;
@@ -96,12 +108,9 @@ export function registerAgentIpc(
96108
process.env.CLAUDE_CONFIG_DIR || join(app.getPath("home"), ".claude");
97109
const statsigPath = join(claudeConfigDir, "statsig");
98110
rmSync(statsigPath, { recursive: true, force: true });
99-
console.log(
100-
"[agent] Cleared statsig cache to work around input_examples bug",
101-
);
111+
log.info("Cleared statsig cache to work around input_examples bug");
102112
} catch (error) {
103-
// Ignore errors if the folder doesn't exist
104-
console.warn("[agent] Could not clear statsig cache:", error);
113+
log.warn("Could not clear statsig cache:", error);
105114
}
106115

107116
const taskId = randomUUID();
@@ -123,7 +132,7 @@ export function registerAgentIpc(
123132
if (stderrBuffer.length > 50) {
124133
stderrBuffer.shift();
125134
}
126-
console.error(`[agent][claude-stderr] ${text}`);
135+
log.error(`[claude-stderr] ${text}`);
127136
emitToRenderer({
128137
type: "status",
129138
ts: Date.now(),
@@ -146,6 +155,7 @@ export function registerAgentIpc(
146155
posthogApiUrl: apiHost,
147156
posthogProjectId: projectId,
148157
debug: true,
158+
onLog: onAgentLog,
149159
});
150160

151161
const controllerEntry: TaskController = {
@@ -181,7 +191,7 @@ export function registerAgentIpc(
181191
});
182192
}
183193
} catch (err) {
184-
console.warn("[agent] failed to fetch task progress", err);
194+
log.warn("Failed to fetch task progress", err);
185195
}
186196
};
187197

@@ -214,7 +224,7 @@ export function registerAgentIpc(
214224
} catch {}
215225
symlinkSync(process.execPath, nodeSymlinkPath);
216226
} catch (err) {
217-
console.warn("[agent] Failed to setup mock node environment", err);
227+
log.warn("Failed to setup mock node environment", err);
218228
}
219229

220230
const newPath = `${mockNodeDir}:${process.env.PATH || ""}`;
@@ -259,7 +269,7 @@ export function registerAgentIpc(
259269

260270
emitToRenderer({ type: "done", success: true, ts: Date.now() });
261271
} catch (err) {
262-
console.error("[agent] task execution failed", err);
272+
log.error("Task execution failed", err);
263273
let errorMessage = err instanceof Error ? err.message : String(err);
264274
const cause =
265275
err instanceof Error && "cause" in err && err.cause

apps/array/src/main/services/fileWatcher.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import {
77
type IpcMainInvokeEvent,
88
ipcMain,
99
} from "electron";
10+
import { logger } from "../lib/logger";
11+
12+
const log = logger.scope("file-watcher");
1013

1114
const WATCHER_IGNORE_PATTERNS = ["**/node_modules/**", "**/.git/**"];
1215
const DEBOUNCE_MS = 100;
@@ -72,7 +75,7 @@ class FileService {
7275
return a.name.localeCompare(b.name);
7376
});
7477
} catch (error) {
75-
console.error("Failed to list directory:", error);
78+
log.error("Failed to list directory:", error);
7679
return [];
7780
}
7881
}
@@ -115,7 +118,7 @@ class FileService {
115118
repoPath,
116119
(err, events) => {
117120
if (err) {
118-
console.error("Watcher error:", err);
121+
log.error("Watcher error:", err);
119122
return;
120123
}
121124

@@ -162,7 +165,7 @@ class FileService {
162165
gitDirToWatch,
163166
(err, events) => {
164167
if (err) {
165-
console.error("Git watcher error:", err);
168+
log.error("Git watcher error:", err);
166169
return;
167170
}
168171
if (
@@ -175,7 +178,7 @@ class FileService {
175178
},
176179
);
177180
} catch (error) {
178-
console.warn("Failed to set up git watcher:", error);
181+
log.warn("Failed to set up git watcher:", error);
179182
}
180183

181184
state.subscription = subscription;
@@ -196,7 +199,7 @@ class FileService {
196199
ignore: WATCHER_IGNORE_PATTERNS,
197200
});
198201
} catch (error) {
199-
console.error("Failed to write snapshot:", error);
202+
log.error("Failed to write snapshot:", error);
200203
}
201204

202205
await state.subscription?.unsubscribe();

apps/array/src/main/services/folders.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ import type {
55
TaskFolderAssociation,
66
WorktreeInfo,
77
} from "../../shared/types";
8+
import { logger } from "../lib/logger";
89
import { clearAllStoreData, foldersStore } from "./store";
910

11+
const log = logger.scope("folders");
12+
1013
function generateFolderId(): string {
1114
return `folder_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
1215
}
@@ -54,7 +57,7 @@ async function removeFolder(folderId: string): Promise<void> {
5457

5558
foldersStore.set("folders", filtered);
5659
foldersStore.set("taskAssociations", filteredAssociations);
57-
console.log(`Removed folder with ID: ${folderId}`);
60+
log.debug(`Removed folder with ID: ${folderId}`);
5861
}
5962

6063
async function updateFolderAccessed(folderId: string): Promise<void> {
@@ -148,7 +151,7 @@ export function registerFoldersIpc(): void {
148151
try {
149152
return await getFolders();
150153
} catch (error) {
151-
console.error("Failed to get folders:", error);
154+
log.error("Failed to get folders:", error);
152155
return [];
153156
}
154157
},
@@ -163,7 +166,7 @@ export function registerFoldersIpc(): void {
163166
try {
164167
return await addFolder(folderPath);
165168
} catch (error) {
166-
console.error(`Failed to add folder ${folderPath}:`, error);
169+
log.error(`Failed to add folder ${folderPath}:`, error);
167170
throw error;
168171
}
169172
},
@@ -175,7 +178,7 @@ export function registerFoldersIpc(): void {
175178
try {
176179
await removeFolder(folderId);
177180
} catch (error) {
178-
console.error(`Failed to remove folder ${folderId}:`, error);
181+
log.error(`Failed to remove folder ${folderId}:`, error);
179182
throw error;
180183
}
181184
},
@@ -187,7 +190,7 @@ export function registerFoldersIpc(): void {
187190
try {
188191
await updateFolderAccessed(folderId);
189192
} catch (error) {
190-
console.error(`Failed to update folder with ID: ${folderId}:`, error);
193+
log.error(`Failed to update folder with ID: ${folderId}:`, error);
191194
}
192195
},
193196
);
@@ -197,9 +200,9 @@ export function registerFoldersIpc(): void {
197200
async (_event: IpcMainInvokeEvent): Promise<void> => {
198201
try {
199202
clearAllStoreData();
200-
console.log("Cleared all application data");
203+
log.info("Cleared all application data");
201204
} catch (error) {
202-
console.error("Failed to clear all data:", error);
205+
log.error("Failed to clear all data:", error);
203206
throw error;
204207
}
205208
},
@@ -211,7 +214,7 @@ export function registerFoldersIpc(): void {
211214
try {
212215
return await getTaskAssociations();
213216
} catch (error) {
214-
console.error("Failed to get task associations:", error);
217+
log.error("Failed to get task associations:", error);
215218
return [];
216219
}
217220
},
@@ -226,7 +229,7 @@ export function registerFoldersIpc(): void {
226229
try {
227230
return await getTaskAssociation(taskId);
228231
} catch (error) {
229-
console.error(`Failed to get task association for ${taskId}:`, error);
232+
log.error(`Failed to get task association for ${taskId}:`, error);
230233
return null;
231234
}
232235
},
@@ -244,7 +247,7 @@ export function registerFoldersIpc(): void {
244247
try {
245248
return await setTaskAssociation(taskId, folderId, folderPath, worktree);
246249
} catch (error) {
247-
console.error(`Failed to set task association for ${taskId}:`, error);
250+
log.error(`Failed to set task association for ${taskId}:`, error);
248251
throw error;
249252
}
250253
},
@@ -260,7 +263,7 @@ export function registerFoldersIpc(): void {
260263
try {
261264
return await updateTaskWorktree(taskId, worktree);
262265
} catch (error) {
263-
console.error(`Failed to update worktree for ${taskId}:`, error);
266+
log.error(`Failed to update worktree for ${taskId}:`, error);
264267
return null;
265268
}
266269
},
@@ -272,10 +275,7 @@ export function registerFoldersIpc(): void {
272275
try {
273276
await removeTaskAssociation(taskId);
274277
} catch (error) {
275-
console.error(
276-
`Failed to remove task association for ${taskId}:`,
277-
error,
278-
);
278+
log.error(`Failed to remove task association for ${taskId}:`, error);
279279
throw error;
280280
}
281281
},
@@ -287,7 +287,7 @@ export function registerFoldersIpc(): void {
287287
try {
288288
await clearTaskWorktree(taskId);
289289
} catch (error) {
290-
console.error(`Failed to clear worktree for ${taskId}:`, error);
290+
log.error(`Failed to clear worktree for ${taskId}:`, error);
291291
throw error;
292292
}
293293
},

0 commit comments

Comments
 (0)