Skip to content

Commit

Permalink
feat: remote engine management
Browse files Browse the repository at this point in the history
  • Loading branch information
urmauur committed Dec 30, 2024
1 parent a6a0cb3 commit 2c76bc3
Show file tree
Hide file tree
Showing 18 changed files with 981 additions and 337 deletions.
20 changes: 13 additions & 7 deletions core/src/browser/extensions/enginesManagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Engines,
EngineVariant,
EngineReleased,
EngineConfig,
DefaultEngineVariant,
} from '../../types'
import { BaseExtension, ExtensionTypeEnum } from '../extension'
Expand Down Expand Up @@ -54,18 +55,15 @@ export abstract class EngineManagementExtension extends BaseExtension {
* @param name - Inference engine name.
* @returns A Promise that resolves to intall of engine.
*/
abstract installEngine(
name: InferenceEngine,
engineConfig: { variant: string; version?: string }
): Promise<{ messages: string }>
abstract installEngine(name: string, engineConfig: EngineConfig): Promise<{ messages: string }>

/**
* @param name - Inference engine name.
* @returns A Promise that resolves to unintall of engine.
*/
abstract uninstallEngine(
name: InferenceEngine,
engineConfig: { variant: string; version: string }
engineConfig: EngineConfig
): Promise<{ messages: string }>

/**
Expand All @@ -81,11 +79,19 @@ export abstract class EngineManagementExtension extends BaseExtension {
*/
abstract setDefaultEngineVariant(
name: InferenceEngine,
engineConfig: { variant: string; version: string }
engineConfig: EngineConfig
): Promise<{ messages: string }>

/**
* @returns A Promise that resolves to update engine.
*/
abstract updateEngine(name: InferenceEngine): Promise<{ messages: string }>
abstract updateEngine(
name: InferenceEngine,
engineConfig?: EngineConfig
): Promise<{ messages: string }>

/**
* @returns A Promise that resolves to an object of remote models list .
*/
abstract getRemoteModels(name: InferenceEngine | string): Promise<any>
}
25 changes: 25 additions & 0 deletions core/src/types/engine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ export type Engines = {
[key in InferenceEngine]: EngineVariant[]
}

export type EngineMetadata = {
get_models_url?: string
api_key_template?: string
transform_req?: {
chat_completions?: {
url?: string
template?: string
}
}
transform_resp?: {
chat_completions?: {
template?: string
}
}
}

export type EngineVariant = {
engine: InferenceEngine
name: string
Expand All @@ -23,6 +39,15 @@ export type EngineReleased = {
size: number
}

export type EngineConfig = {
version?: string
variant?: string
type?: string
url?: string
api_key?: string
metadata?: EngineMetadata
}

export enum EngineEvent {
OnEngineUpdate = 'OnEngineUpdate',
}
31 changes: 20 additions & 11 deletions extensions/engine-management-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
InferenceEngine,
DefaultEngineVariant,
Engines,
EngineConfig,
EngineVariant,
EngineReleased,
executeOnMain,
Expand Down Expand Up @@ -76,6 +77,18 @@ export default class JSONEngineManagementExtension extends EngineManagementExten
) as Promise<Engines>
}

/**
* @returns A Promise that resolves to an object of list engines.
*/
async getRemoteModels(name: string): Promise<any> {
return this.queue.add(() =>
ky
.get(`${API_URL}/v1/models/remote/${name}`)
.json<any>()
.then((e) => e)
) as Promise<any>
}

/**
* @param name - Inference engine name.
* @returns A Promise that resolves to an array of installed engine.
Expand Down Expand Up @@ -130,10 +143,7 @@ export default class JSONEngineManagementExtension extends EngineManagementExten
* @param name - Inference engine name.
* @returns A Promise that resolves to intall of engine.
*/
async installEngine(
name: InferenceEngine,
engineConfig: { variant: string; version?: string }
) {
async installEngine(name: string, engineConfig: EngineConfig) {
return this.queue.add(() =>
ky
.post(`${API_URL}/v1/engines/${name}/install`, { json: engineConfig })
Expand All @@ -145,10 +155,7 @@ export default class JSONEngineManagementExtension extends EngineManagementExten
* @param name - Inference engine name.
* @returns A Promise that resolves to unintall of engine.
*/
async uninstallEngine(
name: InferenceEngine,
engineConfig: { variant: string; version: string }
) {
async uninstallEngine(name: InferenceEngine, engineConfig: EngineConfig) {
return this.queue.add(() =>
ky
.delete(`${API_URL}/v1/engines/${name}/install`, { json: engineConfig })
Expand Down Expand Up @@ -176,7 +183,7 @@ export default class JSONEngineManagementExtension extends EngineManagementExten
*/
async setDefaultEngineVariant(
name: InferenceEngine,
engineConfig: { variant: string; version: string }
engineConfig: EngineConfig
) {
return this.queue.add(() =>
ky
Expand All @@ -188,9 +195,11 @@ export default class JSONEngineManagementExtension extends EngineManagementExten
/**
* @returns A Promise that resolves to update engine.
*/
async updateEngine(name: InferenceEngine) {
async updateEngine(name: InferenceEngine, engineConfig?: EngineConfig) {
return this.queue.add(() =>
ky.post(`${API_URL}/v1/engines/${name}/update`).then((e) => e)
ky
.post(`${API_URL}/v1/engines/${name}/update`, { json: engineConfig })
.then((e) => e)
) as Promise<{ messages: string }>
}

Expand Down
64 changes: 32 additions & 32 deletions extensions/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -634,161 +634,161 @@ __metadata:

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Fassistant-extension%40workspace%3Aassistant-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Fconversational-extension%40workspace%3Aconversational-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Fengine-management-extension%40workspace%3Aengine-management-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-anthropic-extension%40workspace%3Ainference-anthropic-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Finference-anthropic-extension%40workspace%3Ainference-anthropic-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Finference-anthropic-extension%40workspace%3Ainference-anthropic-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-cohere-extension%40workspace%3Ainference-cohere-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Finference-cohere-extension%40workspace%3Ainference-cohere-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Finference-cohere-extension%40workspace%3Ainference-cohere-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Finference-cortex-extension%40workspace%3Ainference-cortex-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-groq-extension%40workspace%3Ainference-groq-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Finference-groq-extension%40workspace%3Ainference-groq-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Finference-groq-extension%40workspace%3Ainference-groq-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-martian-extension%40workspace%3Ainference-martian-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Finference-martian-extension%40workspace%3Ainference-martian-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Finference-martian-extension%40workspace%3Ainference-martian-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-mistral-extension%40workspace%3Ainference-mistral-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Finference-mistral-extension%40workspace%3Ainference-mistral-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Finference-mistral-extension%40workspace%3Ainference-mistral-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-nvidia-extension%40workspace%3Ainference-nvidia-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Finference-nvidia-extension%40workspace%3Ainference-nvidia-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Finference-nvidia-extension%40workspace%3Ainference-nvidia-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-openai-extension%40workspace%3Ainference-openai-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Finference-openai-extension%40workspace%3Ainference-openai-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Finference-openai-extension%40workspace%3Ainference-openai-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-openrouter-extension%40workspace%3Ainference-openrouter-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Finference-openrouter-extension%40workspace%3Ainference-openrouter-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Finference-openrouter-extension%40workspace%3Ainference-openrouter-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Finference-triton-trt-llm-extension%40workspace%3Ainference-triton-trtllm-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Finference-triton-trt-llm-extension%40workspace%3Ainference-triton-trtllm-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Finference-triton-trt-llm-extension%40workspace%3Ainference-triton-trtllm-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Fmodel-extension%40workspace%3Amodel-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Fmonitoring-extension%40workspace%3Amonitoring-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Fmonitoring-extension%40workspace%3Amonitoring-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Fmonitoring-extension%40workspace%3Amonitoring-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

"@janhq/core@file:../../core/package.tgz::locator=%40janhq%2Ftensorrt-llm-extension%40workspace%3Atensorrt-llm-extension":
version: 0.1.10
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=048f32&locator=%40janhq%2Ftensorrt-llm-extension%40workspace%3Atensorrt-llm-extension"
resolution: "@janhq/core@file:../../core/package.tgz#../../core/package.tgz::hash=fc8b55&locator=%40janhq%2Ftensorrt-llm-extension%40workspace%3Atensorrt-llm-extension"
dependencies:
rxjs: "npm:^7.8.1"
ulidx: "npm:^2.3.0"
checksum: 10c0/b48796ca697fffa5aeb33b5c20927a2c3c0b6080a17fccdfa4030919baa8d5c9e3d68d45f87da1eda7842285d7b642361f3793af8aac8d4399c5b13d940a6a42
checksum: 10c0/6fe2bfd30aed514c9897bd62ed36d8ce54d9ebbd358533dad5080772913889e99eefd5eb1a26580a3b290dbf6c0d71c816232e15c80269757df8b0210d746c6f
languageName: node
linkType: hard

Expand Down
8 changes: 8 additions & 0 deletions web/helpers/atoms/Extension.atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,11 @@ export const showSettingActiveLocalEngineAtom = atomWithStorage<string[]>(
undefined,
{ getOnInit: true }
)

const SHOW_SETTING_ACTIVE_REMOTE_ENGINE = 'showSettingActiveRemoteEngine'
export const showSettingActiveRemoteEngineAtom = atomWithStorage<string[]>(
SHOW_SETTING_ACTIVE_REMOTE_ENGINE,
[],
undefined,
{ getOnInit: true }
)
Loading

0 comments on commit 2c76bc3

Please sign in to comment.