Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/code/src/main/trpc/routers/os.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@ export const osRouter = router({
*/
getWorktreeLocation: publicProcedure.query(() => getWorktreeLocation()),

/**
* Get the user's home directory.
*/
getHomeDir: publicProcedure.output(z.string()).query(() => os.homedir()),

/**
* Read a file and return it as a base64 data URL
* Used for image thumbnails in the editor
Expand Down
104 changes: 104 additions & 0 deletions apps/code/src/renderer/api/posthogClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,4 +385,108 @@ describe("PostHogAPIClient", () => {
expect(result.length).toBe(50);
});
});

describe("Task automations", () => {
function buildAutomationClient() {
const client = new PostHogAPIClient(
"http://localhost:8000",
async () => "token",
async () => "token",
123,
);
const api = {
get: vi.fn(),
post: vi.fn(),
patch: vi.fn(),
delete: vi.fn(),
};
(client as unknown as { api: typeof api }).api = api;
return { client, api };
}

it("listTaskAutomations returns the paginated results array", async () => {
const { client, api } = buildAutomationClient();
api.get.mockResolvedValue({ results: [{ id: "a" }, { id: "b" }] });
const result = await client.listTaskAutomations();
expect(api.get).toHaveBeenCalledWith(
"/api/projects/{project_id}/task_automations/",
expect.objectContaining({
path: { project_id: "123" },
query: {},
}),
);
expect(result).toEqual([{ id: "a" }, { id: "b" }]);
});

it("listTaskAutomations handles a missing results field", async () => {
const { client, api } = buildAutomationClient();
api.get.mockResolvedValue({});
expect(await client.listTaskAutomations()).toEqual([]);
});

it("createTaskAutomation POSTs the input body to the team-scoped path", async () => {
const { client, api } = buildAutomationClient();
api.post.mockResolvedValue({ id: "new" });
const result = await client.createTaskAutomation({
name: "Daily audit",
prompt: "Audit my flags",
cron_expression: "0 9 * * *",
repository: "",
timezone: "America/New_York",
enabled: true,
});
expect(api.post).toHaveBeenCalledWith(
"/api/projects/{project_id}/task_automations/",
expect.objectContaining({
path: { project_id: "123" },
body: expect.objectContaining({
name: "Daily audit",
prompt: "Audit my flags",
cron_expression: "0 9 * * *",
repository: "",
timezone: "America/New_York",
enabled: true,
}),
}),
);
expect(result).toEqual({ id: "new" });
});

it("updateTaskAutomation PATCHes the team-and-id-scoped path", async () => {
const { client, api } = buildAutomationClient();
api.patch.mockResolvedValue({ id: "abc" });
await client.updateTaskAutomation("abc", { enabled: false });
expect(api.patch).toHaveBeenCalledWith(
"/api/projects/{project_id}/task_automations/{id}/",
expect.objectContaining({
path: { project_id: "123", id: "abc" },
body: { enabled: false },
}),
);
});

it("deleteTaskAutomation DELETEs the team-and-id-scoped path", async () => {
const { client, api } = buildAutomationClient();
api.delete.mockResolvedValue(undefined);
await client.deleteTaskAutomation("abc");
expect(api.delete).toHaveBeenCalledWith(
"/api/projects/{project_id}/task_automations/{id}/",
expect.objectContaining({
path: { project_id: "123", id: "abc" },
}),
);
});

it("runTaskAutomationNow POSTs to the /run/ endpoint", async () => {
const { client, api } = buildAutomationClient();
api.post.mockResolvedValue({ id: "abc" });
await client.runTaskAutomationNow("abc");
expect(api.post).toHaveBeenCalledWith(
"/api/projects/{project_id}/task_automations/{id}/run/",
expect.objectContaining({
path: { project_id: "123", id: "abc" },
}),
);
});
});
});
71 changes: 71 additions & 0 deletions apps/code/src/renderer/api/posthogClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,77 @@ export class PostHogAPIClient {
});
}

async listTaskAutomations(): Promise<Schemas.TaskAutomation[]> {
const teamId = await this.getTeamId();
const data = await this.api.get(
`/api/projects/{project_id}/task_automations/`,
{
path: { project_id: teamId.toString() },
query: {},
},
);
return data.results ?? [];
}

async createTaskAutomation(
input: Pick<
Schemas.TaskAutomation,
"name" | "prompt" | "cron_expression" | "repository"
> &
Partial<
Pick<
Schemas.TaskAutomation,
"github_integration" | "timezone" | "template_id" | "enabled"
>
>,
): Promise<Schemas.TaskAutomation> {
const teamId = await this.getTeamId();
const data = await this.api.post(
`/api/projects/{project_id}/task_automations/`,
{
path: { project_id: teamId.toString() },
body: input as unknown as Schemas.TaskAutomation,
},
);
return data;
}

async updateTaskAutomation(
automationId: string,
updates: Schemas.PatchedTaskAutomation,
): Promise<Schemas.TaskAutomation> {
const teamId = await this.getTeamId();
const data = await this.api.patch(
`/api/projects/{project_id}/task_automations/{id}/`,
{
path: { project_id: teamId.toString(), id: automationId },
body: updates,
},
);
return data;
}

async deleteTaskAutomation(automationId: string): Promise<void> {
const teamId = await this.getTeamId();
await this.api.delete(`/api/projects/{project_id}/task_automations/{id}/`, {
path: { project_id: teamId.toString(), id: automationId },
});
}

async runTaskAutomationNow(
automationId: string,
): Promise<Schemas.TaskAutomation> {
const teamId = await this.getTeamId();
const data = await this.api.post(
`/api/projects/{project_id}/task_automations/{id}/run/`,
{
path: { project_id: teamId.toString(), id: automationId },
body: {} as unknown as Schemas.TaskAutomation,
},
);
return data;
}
Comment on lines +1073 to +1085

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The /run/ endpoint is a trigger action that doesn't need a request body. Sending {} as unknown as Schemas.TaskAutomation is a double type-cast that silently bypasses TypeScript — if the generated client requires no body for this route the cast can cause a type error elsewhere, and if it's optional the empty object is unnecessary noise. Omitting the body property entirely is cleaner and more honest about the API contract.

Suggested change
async runTaskAutomationNow(
automationId: string,
): Promise<Schemas.TaskAutomation> {
const teamId = await this.getTeamId();
const data = await this.api.post(
`/api/projects/{project_id}/task_automations/{id}/run/`,
{
path: { project_id: teamId.toString(), id: automationId },
body: {} as unknown as Schemas.TaskAutomation,
},
);
return data;
}
async runTaskAutomationNow(
automationId: string,
): Promise<Schemas.TaskAutomation> {
const teamId = await this.getTeamId();
const data = await this.api.post(
`/api/projects/{project_id}/task_automations/{id}/run/`,
{
path: { project_id: teamId.toString(), id: automationId },
},
);
return data;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/code/src/renderer/api/posthogClient.ts
Line: 1073-1085

Comment:
The `/run/` endpoint is a trigger action that doesn't need a request body. Sending `{} as unknown as Schemas.TaskAutomation` is a double type-cast that silently bypasses TypeScript — if the generated client requires no body for this route the cast can cause a type error elsewhere, and if it's optional the empty object is unnecessary noise. Omitting the `body` property entirely is cleaner and more honest about the API contract.

```suggestion
  async runTaskAutomationNow(
    automationId: string,
  ): Promise<Schemas.TaskAutomation> {
    const teamId = await this.getTeamId();
    const data = await this.api.post(
      `/api/projects/{project_id}/task_automations/{id}/run/`,
      {
        path: { project_id: teamId.toString(), id: automationId },
      },
    );
    return data;
  }
```

How can I resolve this? If you propose a fix, please make it concise.


async sendRunCommand(
taskId: string,
runId: string,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions apps/code/src/renderer/assets/services/asana.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions apps/code/src/renderer/assets/services/gmail.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions apps/code/src/renderer/assets/services/google_calendar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions apps/code/src/renderer/assets/services/google_drive.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading