Merge pull request #3261 from gitbutlerapp/Convert-cloud-api-object-to-class

Convert cloud api object to class
This commit is contained in:
Mattias Granlund 2024-03-21 20:35:59 +01:00 committed by GitHub
commit 41aecc8121
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 199 additions and 256 deletions

View File

@ -1,10 +1,10 @@
import { MessageRole, type AIClient, type PromptMessage } from '$lib/backend/aiClient'; import { MessageRole, type AIClient, type PromptMessage } from '$lib/backend/aiClient';
import type { ModelKind } from '$lib/backend/aiService'; import type { ModelKind } from '$lib/backend/aiService';
import type { getCloudApiClient } from '$lib/backend/cloud'; import type { CloudClient } from '$lib/backend/cloud';
export class ButlerAIClient implements AIClient { export class ButlerAIClient implements AIClient {
constructor( constructor(
private cloud: ReturnType<typeof getCloudApiClient>, private cloud: CloudClient,
private userToken: string, private userToken: string,
private modelKind: ModelKind private modelKind: ModelKind
) {} ) {}
@ -12,7 +12,7 @@ export class ButlerAIClient implements AIClient {
async evaluate(prompt: string) { async evaluate(prompt: string) {
const messages: PromptMessage[] = [{ role: MessageRole.User, content: prompt }]; const messages: PromptMessage[] = [{ role: MessageRole.User, content: prompt }];
const response = await this.cloud.ai.evaluatePrompt(this.userToken, { const response = await this.cloud.evaluateAIPrompt(this.userToken, {
messages, messages,
max_tokens: 400, max_tokens: 400,
model_kind: this.modelKind model_kind: this.modelKind

View File

@ -1,3 +1,4 @@
import { CloudClient } from './cloud';
import { AnthropicAIClient } from '$lib/backend/aiClients/anthropic'; import { AnthropicAIClient } from '$lib/backend/aiClients/anthropic';
import { ButlerAIClient } from '$lib/backend/aiClients/butler'; import { ButlerAIClient } from '$lib/backend/aiClients/butler';
import { OpenAIClient } from '$lib/backend/aiClients/openAI'; import { OpenAIClient } from '$lib/backend/aiClients/openAI';
@ -9,7 +10,6 @@ import {
ModelKind, ModelKind,
OpenAIModelName OpenAIModelName
} from '$lib/backend/aiService'; } from '$lib/backend/aiService';
import { getCloudApiClient } from '$lib/backend/cloud';
import * as toasts from '$lib/utils/toasts'; import * as toasts from '$lib/utils/toasts';
import { expect, test, describe, vi } from 'vitest'; import { expect, test, describe, vi } from 'vitest';
import type { AIClient } from '$lib/backend/aiClient'; import type { AIClient } from '$lib/backend/aiClient';
@ -42,7 +42,7 @@ class DummyGitConfigService implements GitConfigService {
} }
const fetchMock = vi.fn(); const fetchMock = vi.fn();
const cloud = getCloudApiClient({ fetch: fetchMock }); const cloud = new CloudClient(fetchMock);
class DummyAIClient implements AIClient { class DummyAIClient implements AIClient {
constructor(private response = 'lorem ipsum') {} constructor(private response = 'lorem ipsum') {}

View File

@ -5,7 +5,7 @@ import { splitMessage } from '$lib/utils/commitMessage';
import * as toasts from '$lib/utils/toasts'; import * as toasts from '$lib/utils/toasts';
import OpenAI from 'openai'; import OpenAI from 'openai';
import type { AIClient } from '$lib/backend/aiClient'; import type { AIClient } from '$lib/backend/aiClient';
import type { getCloudApiClient } from '$lib/backend/cloud'; import type { CloudClient } from '$lib/backend/cloud';
import type { GitConfigService } from '$lib/backend/gitConfigService'; import type { GitConfigService } from '$lib/backend/gitConfigService';
const diffLengthLimit = 20000; const diffLengthLimit = 20000;
@ -87,7 +87,7 @@ type SummarizeBranchOpts = {
export class AIService { export class AIService {
constructor( constructor(
private gitConfig: GitConfigService, private gitConfig: GitConfigService,
private cloud: ReturnType<typeof getCloudApiClient> private cloud: CloudClient
) {} ) {}
async validateConfiguration(userToken?: string): Promise<boolean> { async validateConfiguration(userToken?: string): Promise<boolean> {

View File

@ -1,8 +1,7 @@
import { isLoading, invoke } from './ipc'; import { invoke } from './ipc';
import { nanoid } from 'nanoid';
import type { PromptMessage } from '$lib/backend/aiClient'; import type { PromptMessage } from '$lib/backend/aiClient';
import type { ModelKind } from '$lib/backend/aiService'; import type { ModelKind } from '$lib/backend/aiService';
import { PUBLIC_API_BASE_URL, PUBLIC_CHAIN_API } from '$env/static/public'; import { PUBLIC_API_BASE_URL } from '$env/static/public';
const apiUrl = new URL('/api/', new URL(PUBLIC_API_BASE_URL)); const apiUrl = new URL('/api/', new URL(PUBLIC_API_BASE_URL));
@ -10,12 +9,6 @@ function getUrl(path: string) {
return new URL(path, apiUrl).toString(); return new URL(path, apiUrl).toString();
} }
const chainApiUrl = new URL(PUBLIC_CHAIN_API);
function getChainUrl(path: string) {
return new URL(path, chainApiUrl).toString();
}
export type Feedback = { export type Feedback = {
id: number; id: number;
user_id: number; user_id: number;
@ -67,40 +60,6 @@ async function parseResponseJSON(response: Response) {
} }
} }
type FetchMiddleware = (f: typeof fetch) => typeof fetch;
function fetchWith(fetch: typeof window.fetch, ...middlewares: FetchMiddleware[]) {
return middlewares.reduce((f, middleware) => middleware(f), fetch);
}
function withRequestId(fetch: any) {
return async (url: RequestInfo | URL, options: any) => {
const requestId = nanoid();
if (!options) options = {};
options.headers = {
...options?.headers,
'X-Request-Id': requestId
};
const result = fetch(url, options);
return result;
};
}
function withLog(fetch: any) {
return async (url: RequestInfo | URL, options: any) => {
const item = { name: url.toString(), startedAt: new Date() };
try {
isLoading.push(item);
const resp = await fetch(url, options);
return resp;
} catch (e: any) {
console.error('fetch', e);
throw e;
} finally {
isLoading.pop(item);
}
};
}
interface EvaluatePromptParams { interface EvaluatePromptParams {
messages: PromptMessage[]; messages: PromptMessage[];
temperature?: number; temperature?: number;
@ -108,42 +67,39 @@ interface EvaluatePromptParams {
model_kind?: ModelKind; model_kind?: ModelKind;
} }
export function getCloudApiClient( export class CloudClient {
{ fetch: realFetch }: { fetch: typeof window.fetch } = { fetch: ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>) &
fetch: window.fetch ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>);
constructor(realFetch: typeof window.fetch = window.fetch) {
this.fetch = realFetch;
} }
) {
const fetch = fetchWith(realFetch, withRequestId, withLog); async createLoginToken(): Promise<LoginToken> {
return { const response = await this.fetch(getUrl('login/token.json'), {
login: {
token: {
create: (): Promise<LoginToken> =>
fetch(getUrl('login/token.json'), {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({}) body: JSON.stringify({})
}) });
.then(parseResponseJSON) const token = await parseResponseJSON(response);
.then((token) => {
const url = new URL(token.url); const url = new URL(token.url);
url.host = apiUrl.host; url.host = apiUrl.host;
return { return {
...token, ...token,
url: url.toString() url: url.toString()
}; };
})
},
user: {
get: (token: string): Promise<User> =>
fetch(getUrl(`login/user/${token}.json`), {
method: 'GET'
}).then(parseResponseJSON)
} }
},
feedback: { async getLoginUser(token: string): Promise<User> {
create: async ( const response = await this.fetch(getUrl(`login/user/${token}.json`), {
method: 'GET'
});
return parseResponseJSON(response);
}
async createFeedback(
token: string | undefined, token: string | undefined,
params: { params: {
email?: string; email?: string;
@ -153,7 +109,7 @@ export function getCloudApiClient(
data?: Blob | File; data?: Blob | File;
repo?: Blob | File; repo?: Blob | File;
} }
): Promise<Feedback> => { ): Promise<Feedback> {
const formData = new FormData(); const formData = new FormData();
formData.append('message', params.message); formData.append('message', params.message);
if (params.email) formData.append('email', params.email); if (params.email) formData.append('email', params.email);
@ -162,22 +118,25 @@ export function getCloudApiClient(
if (params.repo) formData.append('repo', params.repo); if (params.repo) formData.append('repo', params.repo);
if (params.data) formData.append('data', params.data); if (params.data) formData.append('data', params.data);
const headers: HeadersInit = token ? { 'X-Auth-Token': token } : {}; const headers: HeadersInit = token ? { 'X-Auth-Token': token } : {};
return fetch(getUrl(`feedback`), { const response = await this.fetch(getUrl(`feedback`), {
method: 'PUT', method: 'PUT',
headers, headers,
body: formData body: formData
}).then(parseResponseJSON); });
return parseResponseJSON(response);
} }
},
user: { async getUser(token: string): Promise<User> {
get: (token: string): Promise<User> => const response = await this.fetch(getUrl(`user.json`), {
fetch(getUrl(`user.json`), {
method: 'GET', method: 'GET',
headers: { headers: {
'X-Auth-Token': token 'X-Auth-Token': token
} }
}).then(parseResponseJSON), });
update: async (token: string, params: { name?: string; picture?: File }) => { return parseResponseJSON(response);
}
async updateUser(token: string, params: { name?: string; picture?: File }): Promise<any> {
const formData = new FormData(); const formData = new FormData();
if (params.name) { if (params.name) {
formData.append('name', params.name); formData.append('name', params.name);
@ -185,111 +144,98 @@ export function getCloudApiClient(
if (params.picture) { if (params.picture) {
formData.append('avatar', params.picture); formData.append('avatar', params.picture);
} }
return fetch(getUrl(`user.json`), { const response = await this.fetch(getUrl(`user.json`), {
method: 'PUT', method: 'PUT',
headers: { headers: {
'X-Auth-Token': token 'X-Auth-Token': token
}, },
body: formData body: formData
}).then(parseResponseJSON); });
return parseResponseJSON(response);
} }
},
ai: { async evaluateAIPrompt(
evaluatePrompt: (token: string, params: EvaluatePromptParams): Promise<{ message: string }> => token: string,
fetch(getUrl('evaluate_prompt/predict.json'), { params: EvaluatePromptParams
): Promise<{ message: string }> {
const response = await this.fetch(getUrl('evaluate_prompt/predict.json'), {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-Auth-Token': token 'X-Auth-Token': token
}, },
body: JSON.stringify(params) body: JSON.stringify(params)
}).then(parseResponseJSON) });
}, return parseResponseJSON(response);
chat: {
new: (token: string, repositoryId: string): Promise<{ id: string }> =>
fetch(getChainUrl(`${repositoryId}/chat`), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Auth-Token': token
} }
}).then(parseResponseJSON),
history: ( async createProject(
token: string, token: string,
repositoryId: string, params: {
chatId: string name: string;
): Promise<{ history: []; sequence: number }> => description?: string;
fetch(getChainUrl(`${repositoryId}/chat/${chatId}`), { uid?: string;
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-Auth-Token': token
} }
}).then(parseResponseJSON), ): Promise<Project> {
newMessage: ( const response = await this.fetch(getUrl('projects.json'), {
token: string,
repositoryId: string,
chatId: string,
message: string
): Promise<{ sequence: number }> =>
fetch(getChainUrl(`${repositoryId}/chat/${chatId}`), {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'X-Auth-Token': token
},
body: JSON.stringify({ text: message })
}).then(parseResponseJSON)
},
projects: {
create: (
token: string,
params: { name: string; description?: string; uid?: string }
): Promise<Project> =>
fetch(getUrl('projects.json'), {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-Auth-Token': token 'X-Auth-Token': token
}, },
body: JSON.stringify(params) body: JSON.stringify(params)
}).then(parseResponseJSON), });
update: ( return parseResponseJSON(response);
}
async updateProject(
token: string, token: string,
repositoryId: string, repositoryId: string,
params: { name: string; description?: string } params: {
): Promise<Project> => name: string;
fetch(getUrl(`projects/${repositoryId}.json`), { description?: string;
}
): Promise<Project> {
const response = await this.fetch(getUrl(`projects/${repositoryId}.json`), {
method: 'PUT', method: 'PUT',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-Auth-Token': token 'X-Auth-Token': token
}, },
body: JSON.stringify(params) body: JSON.stringify(params)
}).then(parseResponseJSON), });
list: (token: string): Promise<Project[]> => return parseResponseJSON(response);
fetch(getUrl('projects.json'), { }
async listProjects(token: string): Promise<Project[]> {
const response = await this.fetch(getUrl('projects.json'), {
method: 'GET', method: 'GET',
headers: { headers: {
'X-Auth-Token': token 'X-Auth-Token': token
} }
}).then(parseResponseJSON), });
get: (token: string, repositoryId: string): Promise<Project> => return parseResponseJSON(response);
fetch(getUrl(`projects/${repositoryId}.json`), { }
async getProject(token: string, repositoryId: string): Promise<Project> {
const response = await this.fetch(getUrl(`projects/${repositoryId}.json`), {
method: 'GET', method: 'GET',
headers: { headers: {
'X-Auth-Token': token 'X-Auth-Token': token
} }
}).then(parseResponseJSON), });
delete: (token: string, repositoryId: string): Promise<void> => return parseResponseJSON(response);
fetch(getUrl(`projects/${repositoryId}.json`), { }
async deleteProject(token: string, repositoryId: string): Promise<void> {
const response = await this.fetch(getUrl(`projects/${repositoryId}.json`), {
method: 'DELETE', method: 'DELETE',
headers: { headers: {
'X-Auth-Token': token 'X-Auth-Token': token
} }
}).then(parseResponseJSON) });
return parseResponseJSON(response);
} }
};
} }
export async function syncToCloud(projectId: string | undefined) { export async function syncToCloud(projectId: string | undefined) {

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import SectionCard from './SectionCard.svelte'; import SectionCard from './SectionCard.svelte';
import WelcomeSigninAction from './WelcomeSigninAction.svelte'; import WelcomeSigninAction from './WelcomeSigninAction.svelte';
import { getCloudApiClient } from '$lib/backend/cloud'; import { CloudClient } from '$lib/backend/cloud';
import Link from '$lib/components/Link.svelte'; import Link from '$lib/components/Link.svelte';
import Spacer from '$lib/components/Spacer.svelte'; import Spacer from '$lib/components/Spacer.svelte';
import Toggle from '$lib/components/Toggle.svelte'; import Toggle from '$lib/components/Toggle.svelte';
@ -17,9 +17,9 @@
export let project: Project; export let project: Project;
const userService = getContextByClass(UserService); const userService = getContextByClass(UserService);
const cloud = getContextByClass(CloudClient);
const user = userService.user; const user = userService.user;
const cloud = getCloudApiClient();
const aiGenEnabled = projectAiGenEnabled(project.id); const aiGenEnabled = projectAiGenEnabled(project.id);
const aiGenAutoBranchNamingEnabled = projectAiGenAutoBranchNamingEnabled(project.id); const aiGenAutoBranchNamingEnabled = projectAiGenAutoBranchNamingEnabled(project.id);
@ -30,7 +30,7 @@
onMount(async () => { onMount(async () => {
if (!project?.api) return; if (!project?.api) return;
if (!$user) return; if (!$user) return;
const cloudProject = await cloud.projects.get($user.access_token, project.api.repository_id); const cloudProject = await cloud.getProject($user.access_token, project.api.repository_id);
if (cloudProject === project.api) return; if (cloudProject === project.api) return;
dispatch('updated', { ...project, api: { ...cloudProject, sync: project.api.sync } }); dispatch('updated', { ...project, api: { ...cloudProject, sync: project.api.sync } });
}); });
@ -40,7 +40,7 @@
try { try {
const cloudProject = const cloudProject =
project.api ?? project.api ??
(await cloud.projects.create($user.access_token, { (await cloud.createProject($user.access_token, {
name: project.title, name: project.title,
description: project.description, description: project.description,
uid: project.id uid: project.id

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import Button from './Button.svelte'; import Button from './Button.svelte';
import { getCloudApiClient, type LoginToken } from '$lib/backend/cloud'; import { CloudClient, type LoginToken } from '$lib/backend/cloud';
import { UserService } from '$lib/stores/user'; import { UserService } from '$lib/stores/user';
import { getContextByClass } from '$lib/utils/context'; import { getContextByClass } from '$lib/utils/context';
import * as toasts from '$lib/utils/toasts'; import * as toasts from '$lib/utils/toasts';
@ -8,7 +8,7 @@
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { derived, writable } from 'svelte/store'; import { derived, writable } from 'svelte/store';
const cloud = getCloudApiClient(); const cloud = getContextByClass(CloudClient);
const userService = getContextByClass(UserService); const userService = getContextByClass(UserService);
const user = userService.user; const user = userService.user;
@ -21,7 +21,7 @@
let signUpOrLoginLoading = false; let signUpOrLoginLoading = false;
async function pollForUser(token: string) { async function pollForUser(token: string) {
const apiUser = await cloud.login.user.get(token).catch(() => null); const apiUser = await cloud.getLoginUser(token).catch(() => null);
if (apiUser) { if (apiUser) {
userService.setUser(apiUser); userService.setUser(apiUser);
return; return;
@ -36,7 +36,7 @@
async function onSignUpOrLoginClick() { async function onSignUpOrLoginClick() {
signUpOrLoginLoading = true; signUpOrLoginLoading = true;
try { try {
token.set(await cloud.login.token.create()); token.set(await cloud.createLoginToken());
} catch (err: any) { } catch (err: any) {
console.error(err); console.error(err);
toasts.error('Could not create login token'); toasts.error('Could not create login token');

View File

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import TextArea from './TextArea.svelte'; import TextArea from './TextArea.svelte';
import { CloudClient } from '$lib/backend/cloud';
import { invoke } from '$lib/backend/ipc'; import { invoke } from '$lib/backend/ipc';
import * as zip from '$lib/backend/zip'; import * as zip from '$lib/backend/zip';
import Button from '$lib/components/Button.svelte'; import Button from '$lib/components/Button.svelte';
@ -9,11 +10,9 @@
import { getContextByClass } from '$lib/utils/context'; import { getContextByClass } from '$lib/utils/context';
import * as toasts from '$lib/utils/toasts'; import * as toasts from '$lib/utils/toasts';
import { getVersion } from '@tauri-apps/api/app'; import { getVersion } from '@tauri-apps/api/app';
import type { getCloudApiClient } from '$lib/backend/cloud';
import { page } from '$app/stores'; import { page } from '$app/stores';
export let cloud: ReturnType<typeof getCloudApiClient>; const cloud = getContextByClass(CloudClient);
const userService = getContextByClass(UserService); const userService = getContextByClass(UserService);
const user = userService.user; const user = userService.user;
@ -76,7 +75,7 @@
? zip.projectData({ projectId }).then((path) => readZipFile(path, 'project.zip')) ? zip.projectData({ projectId }).then((path) => readZipFile(path, 'project.zip'))
: undefined : undefined
]).then(async ([logs, data, repo]) => ]).then(async ([logs, data, repo]) =>
cloud.feedback.create($user?.access_token, { cloud.createFeedback($user?.access_token, {
email, email,
message, message,
context, context,

View File

@ -1,16 +1,14 @@
import { resetPostHog, setPostHogUser } from '$lib/analytics/posthog'; import { resetPostHog, setPostHogUser } from '$lib/analytics/posthog';
import { resetSentry, setSentryUser } from '$lib/analytics/sentry'; import { resetSentry, setSentryUser } from '$lib/analytics/sentry';
import { getCloudApiClient, type User } from '$lib/backend/cloud';
import { invoke } from '$lib/backend/ipc'; import { invoke } from '$lib/backend/ipc';
import { observableToStore } from '$lib/rxjs/store'; import { observableToStore } from '$lib/rxjs/store';
import { sleep } from '$lib/utils/sleep'; import { sleep } from '$lib/utils/sleep';
import { openExternalUrl } from '$lib/utils/url'; import { openExternalUrl } from '$lib/utils/url';
import { BehaviorSubject, Observable, Subject, distinct, map, merge, shareReplay } from 'rxjs'; import { BehaviorSubject, Observable, Subject, distinct, map, merge, shareReplay } from 'rxjs';
import type { CloudClient, User } from '$lib/backend/cloud';
import type { Readable } from 'svelte/motion'; import type { Readable } from 'svelte/motion';
export class UserService { export class UserService {
private readonly cloud = getCloudApiClient();
readonly reset$ = new Subject<User | undefined>(); readonly reset$ = new Subject<User | undefined>();
readonly loading$ = new BehaviorSubject(false); readonly loading$ = new BehaviorSubject(false);
@ -35,7 +33,7 @@ export class UserService {
user: Readable<User | undefined>; user: Readable<User | undefined>;
error: Readable<string | undefined>; error: Readable<string | undefined>;
constructor() { constructor(private cloud: CloudClient) {
[this.user, this.error] = observableToStore(this.user$); [this.user, this.error] = observableToStore(this.user$);
} }
@ -60,7 +58,7 @@ export class UserService {
this.logout(); this.logout();
this.loading$.next(true); this.loading$.next(true);
try { try {
const token = await this.cloud.login.token.create(); const token = await this.cloud.createLoginToken();
openExternalUrl(token.url); openExternalUrl(token.url);
// Assumed min time for login flow // Assumed min time for login flow
@ -78,7 +76,7 @@ export class UserService {
private async pollForUser(token: string): Promise<User | undefined> { private async pollForUser(token: string): Promise<User | undefined> {
let apiUser: User | null; let apiUser: User | null;
for (let i = 0; i < 120; i++) { for (let i = 0; i < 120; i++) {
apiUser = await this.cloud.login.user.get(token).catch(() => null); apiUser = await this.cloud.getLoginUser(token).catch(() => null);
if (apiUser) { if (apiUser) {
this.setUser(apiUser); this.setUser(apiUser);
return apiUser; return apiUser;

View File

@ -3,6 +3,7 @@
import { AIService } from '$lib/backend/aiService'; import { AIService } from '$lib/backend/aiService';
import { AuthService } from '$lib/backend/auth'; import { AuthService } from '$lib/backend/auth';
import { CloudClient } from '$lib/backend/cloud';
import { GitConfigService } from '$lib/backend/gitConfigService'; import { GitConfigService } from '$lib/backend/gitConfigService';
import { ProjectService } from '$lib/backend/projects'; import { ProjectService } from '$lib/backend/projects';
import { PromptService } from '$lib/backend/prompt'; import { PromptService } from '$lib/backend/prompt';
@ -24,7 +25,6 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
export let data: LayoutData; export let data: LayoutData;
$: ({ cloud } = data);
const userSettings = loadUserSettings(); const userSettings = loadUserSettings();
initTheme(userSettings); initTheme(userSettings);
@ -38,6 +38,7 @@
$: setContext(AIService, data.aiService); $: setContext(AIService, data.aiService);
$: setContext(PromptService, data.promptService); $: setContext(PromptService, data.promptService);
$: setContext(AuthService, data.authService); $: setContext(AuthService, data.authService);
$: setContext(CloudClient, data.cloud);
let shareIssueModal: ShareIssueModal; let shareIssueModal: ShareIssueModal;
@ -68,7 +69,7 @@
<slot /> <slot />
</div> </div>
<Toaster /> <Toaster />
<ShareIssueModal bind:this={shareIssueModal} {cloud} /> <ShareIssueModal bind:this={shareIssueModal} />
<ToastController /> <ToastController />
<AppUpdater /> <AppUpdater />
<PromptModal /> <PromptModal />

View File

@ -2,7 +2,7 @@ import { initPostHog } from '$lib/analytics/posthog';
import { initSentry } from '$lib/analytics/sentry'; import { initSentry } from '$lib/analytics/sentry';
import { AIService } from '$lib/backend/aiService'; import { AIService } from '$lib/backend/aiService';
import { AuthService } from '$lib/backend/auth'; import { AuthService } from '$lib/backend/auth';
import { getCloudApiClient } from '$lib/backend/cloud'; import { CloudClient } from '$lib/backend/cloud';
import { GitConfigService } from '$lib/backend/gitConfigService'; import { GitConfigService } from '$lib/backend/gitConfigService';
import { ProjectService } from '$lib/backend/projects'; import { ProjectService } from '$lib/backend/projects';
import { PromptService } from '$lib/backend/prompt'; import { PromptService } from '$lib/backend/prompt';
@ -39,11 +39,12 @@ export async function load({ fetch: realFetch }: { fetch: typeof fetch }) {
// https://github.com/sveltejs/kit/issues/905 // https://github.com/sveltejs/kit/issues/905
const defaultPath = await (await import('@tauri-apps/api/path')).homeDir(); const defaultPath = await (await import('@tauri-apps/api/path')).homeDir();
const cloud = new CloudClient(realFetch);
const authService = new AuthService(); const authService = new AuthService();
const projectService = new ProjectService(defaultPath); const projectService = new ProjectService(defaultPath);
const updaterService = new UpdaterService(); const updaterService = new UpdaterService();
const promptService = new PromptService(); const promptService = new PromptService();
const userService = new UserService(); const userService = new UserService(cloud);
const user$ = userService.user$; const user$ = userService.user$;
// We're declaring a remoteUrl$ observable here that is written to by `BaseBranchService`. This // We're declaring a remoteUrl$ observable here that is written to by `BaseBranchService`. This
@ -55,8 +56,6 @@ export async function load({ fetch: realFetch }: { fetch: typeof fetch }) {
const remoteUrl$ = new BehaviorSubject<string | undefined>(undefined); const remoteUrl$ = new BehaviorSubject<string | undefined>(undefined);
const githubService = new GitHubService(userService.accessToken$, remoteUrl$); const githubService = new GitHubService(userService.accessToken$, remoteUrl$);
const cloud = getCloudApiClient({ fetch: realFetch });
const gitConfig = new GitConfigService(); const gitConfig = new GitConfigService();
const aiService = new AIService(gitConfig, cloud); const aiService = new AIService(gitConfig, cloud);

View File

@ -57,7 +57,7 @@
async function onDetailsUpdated(e: { detail: Project }) { async function onDetailsUpdated(e: { detail: Project }) {
const api = const api =
$user && e.detail.api $user && e.detail.api
? await cloud.projects.update($user?.access_token, e.detail.api.repository_id, { ? await cloud.updateProject($user?.access_token, e.detail.api.repository_id, {
name: e.detail.title, name: e.detail.title,
description: e.detail.description description: e.detail.description
}) })

View File

@ -60,7 +60,7 @@
$: if ($user && !loaded) { $: if ($user && !loaded) {
loaded = true; loaded = true;
cloud.user.get($user?.access_token).then((cloudUser) => { cloud.getUser($user?.access_token).then((cloudUser) => {
cloudUser.github_access_token = $user?.github_access_token; // prevent overwriting with null cloudUser.github_access_token = $user?.github_access_token; // prevent overwriting with null
userService.setUser(cloudUser); userService.setUser(cloudUser);
}); });
@ -88,7 +88,7 @@
const picture = formData.get('picture') as File | undefined; const picture = formData.get('picture') as File | undefined;
try { try {
const updatedUser = await cloud.user.update($user.access_token, { const updatedUser = await cloud.updateUser($user.access_token, {
name: newName, name: newName,
picture: picture picture: picture
}); });