Skip to content
Open
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
11 changes: 11 additions & 0 deletions backend/internal/service/session/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,24 @@ func (s *Service) TeardownProject(ctx context.Context, project domain.ProjectID)
continue
}
if _, err := s.Kill(ctx, rec.ID); err != nil {
if isIncompleteHandleError(err) {
continue
}
return err
}
}
_, err = s.Cleanup(ctx, project)
return err
}

func isIncompleteHandleError(err error) bool {
if errors.Is(err, sessionmanager.ErrIncompleteHandle) {
return true
}
var apiErr *apierr.Error
return errors.As(err, &apiErr) && apiErr.Code == "SESSION_INCOMPLETE_HANDLE"
}

// List returns sessions as enriched display models after applying API filters.
func (s *Service) List(ctx context.Context, filter ListFilter) ([]domain.Session, error) {
recs, err := s.listRecords(ctx, filter.ProjectID)
Expand Down
14 changes: 14 additions & 0 deletions backend/internal/service/session/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,20 @@ func TestTeardownProjectStopsOnKillError(t *testing.T) {
}
}

func TestTeardownProjectContinuesOnIncompleteHandle(t *testing.T) {
st := newFakeStore()
st.sessions["mer-1"] = domain.SessionRecord{ID: "mer-1", ProjectID: "mer"}
fc := &fakeCommander{killErr: sessionmanager.ErrIncompleteHandle}
svc := &Service{manager: fc, store: st}

if err := svc.TeardownProject(context.Background(), "mer"); err != nil {
t.Fatalf("TeardownProject: %v", err)
}
if len(fc.cleanupProjects) != 1 || fc.cleanupProjects[0] != "mer" {
t.Fatalf("cleanup projects = %#v, want [mer]", fc.cleanupProjects)
}
}

func TestSpawnOrchestratorCleanKillsActiveOrchestratorsBeforeSpawn(t *testing.T) {
st := newFakeStore()
st.projects["mer"] = domain.ProjectRecord{ID: "mer"}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,9 @@ ipcMain.handle("daemon:getStatus", () => daemonStatus);
ipcMain.handle("daemon:start", () => startDaemon());
ipcMain.handle("daemon:stop", () => stopDaemon());
ipcMain.handle("app:getVersion", () => app.getVersion());
ipcMain.handle("telemetry:getBootstrap", () => buildTelemetryBootstrap(process.env, app.getVersion(), process.platform));
ipcMain.handle("telemetry:getBootstrap", () =>
buildTelemetryBootstrap(process.env, app.getVersion(), process.platform),
);
ipcMain.handle("app:chooseDirectory", async () => {
const options: OpenDialogOptions = {
properties: ["openDirectory"],
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/renderer/components/TelemetryBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export class TelemetryBoundary extends React.Component<Props, State> {
<div className="flex h-screen items-center justify-center bg-background px-6 text-center text-foreground">
<div>
<h1 className="text-lg font-semibold">The app hit an unexpected error.</h1>
<p className="mt-2 text-sm text-muted-foreground">Restart the app or check the daemon logs if this keeps happening.</p>
<p className="mt-2 text-sm text-muted-foreground">
Restart the app or check the daemon logs if this keeps happening.
</p>
</div>
</div>
);
Expand Down
6 changes: 1 addition & 5 deletions frontend/src/renderer/lib/telemetry.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { describe, expect, it } from "vitest";
import {
routeSurface,
sanitizeRendererExceptionProperties,
sanitizeRendererProperties,
} from "./telemetry";
import { routeSurface, sanitizeRendererExceptionProperties, sanitizeRendererProperties } from "./telemetry";

describe("telemetry sanitizers", () => {
it("categorizes routes without exporting raw paths", () => {
Expand Down
7 changes: 2 additions & 5 deletions frontend/src/renderer/lib/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export async function sanitizeRendererProperties(
case "ao.renderer.orchestrator_open_requested": {
const projectIDHash = await hashedTelemetryID(properties?.project_id);
if (projectIDHash) safe.project_id_hash = projectIDHash;
break
break;
}
}
return safe;
Expand Down Expand Up @@ -157,10 +157,7 @@ export async function captureRendererEvent(event: string, properties?: Record<st
posthog.capture(event, safeProperties);
}

export async function captureRendererException(
error: unknown,
properties?: Record<string, unknown>,
): Promise<void> {
export async function captureRendererException(error: unknown, properties?: Record<string, unknown>): Promise<void> {
if (!(await initTelemetry())) return;
const safeProperties = await sanitizeRendererExceptionProperties(error, properties);
posthog.capture("ao.renderer.exception", safeProperties);
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/shared/telemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import { buildTelemetryBootstrap, defaultDataDir, loadOrCreateTelemetryInstallId
const tempDirs: string[] = [];

afterEach(async () => {
await Promise.all(tempDirs.splice(0).map((dir) => import("node:fs/promises").then(({ rm }) => rm(dir, { recursive: true, force: true }))));
await Promise.all(
tempDirs
.splice(0)
.map((dir) => import("node:fs/promises").then(({ rm }) => rm(dir, { recursive: true, force: true }))),
);
});

test("defaultDataDir prefers AO_DATA_DIR", () => {
Expand Down