mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-24 18:12:48 +03:00
Merge pull request #3261 from gitbutlerapp/Convert-cloud-api-object-to-class
Convert cloud api object to class
This commit is contained in:
commit
41aecc8121
@ -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
|
||||||
|
@ -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') {}
|
||||||
|
@ -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> {
|
||||||
|
@ -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,188 +67,175 @@ 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: {
|
method: 'POST',
|
||||||
token: {
|
headers: {
|
||||||
create: (): Promise<LoginToken> =>
|
'Content-Type': 'application/json'
|
||||||
fetch(getUrl('login/token.json'), {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({})
|
|
||||||
})
|
|
||||||
.then(parseResponseJSON)
|
|
||||||
.then((token) => {
|
|
||||||
const url = new URL(token.url);
|
|
||||||
url.host = apiUrl.host;
|
|
||||||
return {
|
|
||||||
...token,
|
|
||||||
url: url.toString()
|
|
||||||
};
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
user: {
|
body: JSON.stringify({})
|
||||||
get: (token: string): Promise<User> =>
|
});
|
||||||
fetch(getUrl(`login/user/${token}.json`), {
|
const token = await parseResponseJSON(response);
|
||||||
method: 'GET'
|
const url = new URL(token.url);
|
||||||
}).then(parseResponseJSON)
|
url.host = apiUrl.host;
|
||||||
}
|
return {
|
||||||
},
|
...token,
|
||||||
feedback: {
|
url: url.toString()
|
||||||
create: async (
|
};
|
||||||
token: string | undefined,
|
}
|
||||||
params: {
|
|
||||||
email?: string;
|
async getLoginUser(token: string): Promise<User> {
|
||||||
message: string;
|
const response = await this.fetch(getUrl(`login/user/${token}.json`), {
|
||||||
context?: string;
|
method: 'GET'
|
||||||
logs?: Blob | File;
|
});
|
||||||
data?: Blob | File;
|
return parseResponseJSON(response);
|
||||||
repo?: Blob | File;
|
}
|
||||||
}
|
|
||||||
): Promise<Feedback> => {
|
async createFeedback(
|
||||||
const formData = new FormData();
|
token: string | undefined,
|
||||||
formData.append('message', params.message);
|
params: {
|
||||||
if (params.email) formData.append('email', params.email);
|
email?: string;
|
||||||
if (params.context) formData.append('context', params.context);
|
message: string;
|
||||||
if (params.logs) formData.append('logs', params.logs);
|
context?: string;
|
||||||
if (params.repo) formData.append('repo', params.repo);
|
logs?: Blob | File;
|
||||||
if (params.data) formData.append('data', params.data);
|
data?: Blob | File;
|
||||||
const headers: HeadersInit = token ? { 'X-Auth-Token': token } : {};
|
repo?: Blob | File;
|
||||||
return fetch(getUrl(`feedback`), {
|
|
||||||
method: 'PUT',
|
|
||||||
headers,
|
|
||||||
body: formData
|
|
||||||
}).then(parseResponseJSON);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
get: (token: string): Promise<User> =>
|
|
||||||
fetch(getUrl(`user.json`), {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'X-Auth-Token': token
|
|
||||||
}
|
|
||||||
}).then(parseResponseJSON),
|
|
||||||
update: async (token: string, params: { name?: string; picture?: File }) => {
|
|
||||||
const formData = new FormData();
|
|
||||||
if (params.name) {
|
|
||||||
formData.append('name', params.name);
|
|
||||||
}
|
|
||||||
if (params.picture) {
|
|
||||||
formData.append('avatar', params.picture);
|
|
||||||
}
|
|
||||||
return fetch(getUrl(`user.json`), {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'X-Auth-Token': token
|
|
||||||
},
|
|
||||||
body: formData
|
|
||||||
}).then(parseResponseJSON);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ai: {
|
|
||||||
evaluatePrompt: (token: string, params: EvaluatePromptParams): Promise<{ message: string }> =>
|
|
||||||
fetch(getUrl('evaluate_prompt/predict.json'), {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-Auth-Token': token
|
|
||||||
},
|
|
||||||
body: JSON.stringify(params)
|
|
||||||
}).then(parseResponseJSON)
|
|
||||||
},
|
|
||||||
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: (
|
|
||||||
token: string,
|
|
||||||
repositoryId: string,
|
|
||||||
chatId: string
|
|
||||||
): Promise<{ history: []; sequence: number }> =>
|
|
||||||
fetch(getChainUrl(`${repositoryId}/chat/${chatId}`), {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-Auth-Token': token
|
|
||||||
}
|
|
||||||
}).then(parseResponseJSON),
|
|
||||||
newMessage: (
|
|
||||||
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',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-Auth-Token': token
|
|
||||||
},
|
|
||||||
body: JSON.stringify(params)
|
|
||||||
}).then(parseResponseJSON),
|
|
||||||
update: (
|
|
||||||
token: string,
|
|
||||||
repositoryId: string,
|
|
||||||
params: { name: string; description?: string }
|
|
||||||
): Promise<Project> =>
|
|
||||||
fetch(getUrl(`projects/${repositoryId}.json`), {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-Auth-Token': token
|
|
||||||
},
|
|
||||||
body: JSON.stringify(params)
|
|
||||||
}).then(parseResponseJSON),
|
|
||||||
list: (token: string): Promise<Project[]> =>
|
|
||||||
fetch(getUrl('projects.json'), {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'X-Auth-Token': token
|
|
||||||
}
|
|
||||||
}).then(parseResponseJSON),
|
|
||||||
get: (token: string, repositoryId: string): Promise<Project> =>
|
|
||||||
fetch(getUrl(`projects/${repositoryId}.json`), {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'X-Auth-Token': token
|
|
||||||
}
|
|
||||||
}).then(parseResponseJSON),
|
|
||||||
delete: (token: string, repositoryId: string): Promise<void> =>
|
|
||||||
fetch(getUrl(`projects/${repositoryId}.json`), {
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: {
|
|
||||||
'X-Auth-Token': token
|
|
||||||
}
|
|
||||||
}).then(parseResponseJSON)
|
|
||||||
}
|
}
|
||||||
};
|
): Promise<Feedback> {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('message', params.message);
|
||||||
|
if (params.email) formData.append('email', params.email);
|
||||||
|
if (params.context) formData.append('context', params.context);
|
||||||
|
if (params.logs) formData.append('logs', params.logs);
|
||||||
|
if (params.repo) formData.append('repo', params.repo);
|
||||||
|
if (params.data) formData.append('data', params.data);
|
||||||
|
const headers: HeadersInit = token ? { 'X-Auth-Token': token } : {};
|
||||||
|
const response = await this.fetch(getUrl(`feedback`), {
|
||||||
|
method: 'PUT',
|
||||||
|
headers,
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
return parseResponseJSON(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUser(token: string): Promise<User> {
|
||||||
|
const response = await this.fetch(getUrl(`user.json`), {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-Auth-Token': token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return parseResponseJSON(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateUser(token: string, params: { name?: string; picture?: File }): Promise<any> {
|
||||||
|
const formData = new FormData();
|
||||||
|
if (params.name) {
|
||||||
|
formData.append('name', params.name);
|
||||||
|
}
|
||||||
|
if (params.picture) {
|
||||||
|
formData.append('avatar', params.picture);
|
||||||
|
}
|
||||||
|
const response = await this.fetch(getUrl(`user.json`), {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'X-Auth-Token': token
|
||||||
|
},
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
return parseResponseJSON(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async evaluateAIPrompt(
|
||||||
|
token: string,
|
||||||
|
params: EvaluatePromptParams
|
||||||
|
): Promise<{ message: string }> {
|
||||||
|
const response = await this.fetch(getUrl('evaluate_prompt/predict.json'), {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Auth-Token': token
|
||||||
|
},
|
||||||
|
body: JSON.stringify(params)
|
||||||
|
});
|
||||||
|
return parseResponseJSON(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createProject(
|
||||||
|
token: string,
|
||||||
|
params: {
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
uid?: string;
|
||||||
|
}
|
||||||
|
): Promise<Project> {
|
||||||
|
const response = await this.fetch(getUrl('projects.json'), {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Auth-Token': token
|
||||||
|
},
|
||||||
|
body: JSON.stringify(params)
|
||||||
|
});
|
||||||
|
return parseResponseJSON(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateProject(
|
||||||
|
token: string,
|
||||||
|
repositoryId: string,
|
||||||
|
params: {
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
): Promise<Project> {
|
||||||
|
const response = await this.fetch(getUrl(`projects/${repositoryId}.json`), {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Auth-Token': token
|
||||||
|
},
|
||||||
|
body: JSON.stringify(params)
|
||||||
|
});
|
||||||
|
return parseResponseJSON(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async listProjects(token: string): Promise<Project[]> {
|
||||||
|
const response = await this.fetch(getUrl('projects.json'), {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-Auth-Token': token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return parseResponseJSON(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProject(token: string, repositoryId: string): Promise<Project> {
|
||||||
|
const response = await this.fetch(getUrl(`projects/${repositoryId}.json`), {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'X-Auth-Token': token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return parseResponseJSON(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteProject(token: string, repositoryId: string): Promise<void> {
|
||||||
|
const response = await this.fetch(getUrl(`projects/${repositoryId}.json`), {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'X-Auth-Token': token
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return parseResponseJSON(response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function syncToCloud(projectId: string | undefined) {
|
export async function syncToCloud(projectId: string | undefined) {
|
||||||
|
@ -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
|
||||||
|
@ -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');
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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 />
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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
|
||||||
})
|
})
|
||||||
|
@ -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
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user