Skip to content
Merged
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 .changeset/anonymous-kilocode-onboarding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---

New users can now start using Kilo Code immediately without any configuration - a default Kilo Code Gateway profile with a free model is automatically set up on first launch
3,164 changes: 1,287 additions & 1,877 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/api/providers/kilocode-openrouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ export class KilocodeOpenrouterHandler extends OpenRouterHandler {
}

public override async fetchModel() {
if (!this.options.kilocodeToken || !this.options.openRouterBaseUrl) {
throw new Error(KILOCODE_TOKEN_REQUIRED_ERROR)
if (!this.options.openRouterBaseUrl) {
throw new Error("OpenRouter base URL is required")
}

const [models, endpoints, defaultModel] = await Promise.all([
Expand Down
6 changes: 3 additions & 3 deletions src/api/providers/kilocode/getKilocodeDefaultModel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { openRouterDefaultModelId, type ProviderSettings } from "@roo-code/types"
import { openRouterDefaultModelId } from "@roo-code/types"
import { getKiloUrlFromToken } from "@roo-code/types"
import { TelemetryService } from "@roo-code/telemetry"
import { z } from "zod"
Expand All @@ -23,7 +23,7 @@ async function fetchKilocodeDefaultModel(
): Promise<Defaults> {
try {
const path = organizationId ? `/organizations/${organizationId}/defaults` : `/defaults`
const url = getKiloUrlFromToken(`https://api.kilo.ai/api${path}`, kilocodeToken)
const url = getKiloUrlFromToken(`https://api.kilo.ai/api${path}`, kilocodeToken ?? "")
Copy link
Collaborator

Choose a reason for hiding this comment

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

this will not work properly, but whatever


const headers: Record<string, string> = {
...DEFAULT_HEADERS,
Expand Down Expand Up @@ -60,7 +60,7 @@ export async function getKilocodeDefaultModel(
organizationId?: OrganizationId,
): Promise<Defaults> {
const key = JSON.stringify({
kilocodeToken,
kilocodeToken: kilocodeToken ?? "anonymous",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
kilocodeToken: kilocodeToken ?? "anonymous",
kilocodeToken,

organizationId,
})
let defaultModelPromise = cache.get(key)
Expand Down
33 changes: 26 additions & 7 deletions src/core/config/ProviderSettingsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,16 @@ export class ProviderSettingsManager {
modes.map((mode) => [mode.slug, this.defaultConfigId]),
)

// kilocode_change start: Anonymous kilocode onboarding - set default provider for new users
private readonly defaultProviderProfiles: ProviderProfiles = {
currentApiConfigName: "default",
apiConfigs: { default: { id: this.defaultConfigId } },
apiConfigs: {
default: {
id: this.defaultConfigId,
apiProvider: "kilocode",
kilocodeModel: "minimax/minimax-m2.1:free",
},
},
modeApiConfigs: this.defaultModeApiConfigs,
migrations: {
rateLimitSecondsMigrated: true, // Mark as migrated on fresh installs
Expand All @@ -79,6 +86,7 @@ export class ProviderSettingsManager {
claudeCodeLegacySettingsMigrated: true, // Mark as migrated on fresh installs
},
}
// kilocode_change end

// kilocode_change start
private pendingDuplicateIdRepairReport: Record<string, string[]> | null = null
Expand Down Expand Up @@ -140,10 +148,15 @@ export class ProviderSettingsManager {
async init_runMigrations() {
try {
return await this.lock(async () => {
const providerProfiles = await this.load()
// kilocode_change start: Check if this is a new user (no stored config)
const storedContent = await this.context.secrets.get(this.secretsKey)
const isNewUser = !storedContent
// kilocode_change end

const providerProfiles = await this.loadFromContent(storedContent)

if (!providerProfiles) {
await this.store(this.defaultProviderProfiles)
if (isNewUser) {
await this.store(providerProfiles)
return
}

Expand Down Expand Up @@ -700,7 +713,8 @@ export class ProviderSettingsManager {
public async export() {
try {
return await this.lock(async () => {
const profiles = providerProfilesSchema.parse(await this.load())
const providerProfiles = await this.load()
const profiles = providerProfilesSchema.parse(providerProfiles)
const configs = profiles.apiConfigs
for (const name in configs) {
// Avoid leaking properties from other providers.
Expand Down Expand Up @@ -760,9 +774,13 @@ export class ProviderSettingsManager {
}

private async load(): Promise<ProviderProfiles> {
try {
const content = await this.context.secrets.get(this.secretsKey)
const content = await this.context.secrets.get(this.secretsKey)
return this.loadFromContent(content)
}

// kilocode_change start: Extract content parsing to avoid double-fetching in init_runMigrations
private loadFromContent(content: string | undefined): ProviderProfiles {
try {
if (!content) {
return this.defaultProviderProfiles
}
Expand Down Expand Up @@ -801,6 +819,7 @@ export class ProviderSettingsManager {
throw new Error(`Failed to read provider profiles from secrets: ${error}`)
}
}
// kilocode_change end

/**
* Sanitizes a provider config by resetting invalid/removed apiProvider values.
Expand Down
26 changes: 19 additions & 7 deletions src/core/config/__tests__/ProviderSettingsManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const mockContext = {
describe("ProviderSettingsManager", () => {
let providerSettingsManager: ProviderSettingsManager

beforeEach(() => {
beforeEach(async () => {
vi.clearAllMocks()
// Reset all mock implementations to default successful behavior
mockSecrets.get.mockResolvedValue(null)
Expand All @@ -43,6 +43,12 @@ describe("ProviderSettingsManager", () => {
// then reinitializing, and spying on internals of said initialization.
// I'm not convinced this test coverage means very much, so this fix makes a complicated puzzle happen to fall in place
// Also this override resets itself, but fortunately no test required triple initialization...

// Wait for the first manager's initialization to complete, then clear mock calls
// This is needed because new users get the default kilocode config stored
await providerSettingsManager.initialize()
vi.clearAllMocks()

providerSettingsManager.initialize = async () => {
providerSettingsManager = new ProviderSettingsManager(mockContext)
await providerSettingsManager.initialize()
Expand All @@ -51,15 +57,21 @@ describe("ProviderSettingsManager", () => {
})

describe("initialize", () => {
it("should not write to storage when secrets.get returns null", async () => {
// kilocode_change start: test updated to expect kilocode default profile for new users
it("should initialize kilocode default profile when secrets.get returns null", async () => {
// Mock readConfig to return null
mockSecrets.get.mockResolvedValueOnce(null)

await providerSettingsManager.initialize()

// Should not write to storage because readConfig returns defaultConfig
expect(mockSecrets.store).not.toHaveBeenCalled()
// Should write to storage with default kilocode profile for new users
expect(mockSecrets.store).toHaveBeenCalled()
const calls = mockSecrets.store.mock.calls
const storedConfig = JSON.parse(calls[calls.length - 1][1])
expect(storedConfig.apiConfigs.default.apiProvider).toBe("kilocode")
expect(storedConfig.apiConfigs.default.kilocodeModel).toBe("minimax/minimax-m2.1:free")
})
// kilocode_change end

it("should not initialize config if it exists and migrations are complete", async () => {
mockSecrets.get.mockResolvedValue(
Expand Down Expand Up @@ -437,7 +449,7 @@ describe("ProviderSettingsManager", () => {
mockSecrets.get.mockRejectedValue(new Error("Storage failed"))

await expect(providerSettingsManager.initialize()).rejects.toThrow(
"Failed to initialize config: Error: Failed to read provider profiles from secrets: Error: Storage failed",
"Failed to initialize config: Error: Storage failed",
)
})
})
Expand Down Expand Up @@ -498,7 +510,7 @@ describe("ProviderSettingsManager", () => {
mockSecrets.get.mockRejectedValue(new Error("Read failed"))

await expect(providerSettingsManager.listConfig()).rejects.toThrow(
"Failed to list configs: Error: Failed to read provider profiles from secrets: Error: Read failed",
"Failed to list configs: Error: Read failed",
)
})
})
Expand Down Expand Up @@ -935,7 +947,7 @@ describe("ProviderSettingsManager", () => {
mockSecrets.get.mockRejectedValue(new Error("Storage failed"))

await expect(providerSettingsManager.hasConfig("test")).rejects.toThrow(
"Failed to check config existence: Error: Failed to read provider profiles from secrets: Error: Storage failed",
"Failed to check config existence: Error: Storage failed",
)
})
})
Expand Down
6 changes: 4 additions & 2 deletions src/shared/checkExistApiConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ export function checkExistKey(config: ProviderSettings | undefined) {
return false
}

// Special case for fake-ai, claude-code, openai-codex, qwen-code, and roo providers which don't need any configuration.
// Special case for human-relay, fake-ai, claude-code, openai-codex, qwen-code, roo and kilocode providers which don't need any configuration.
if (
config.apiProvider &&
["human-relay", "fake-ai", "claude-code", "openai-codex", "qwen-code", "roo"].includes(config.apiProvider) // kilocode_change: add human-relay
["human-relay", "fake-ai", "claude-code", "openai-codex", "qwen-code", "roo", "kilocode"].includes(
config.apiProvider,
) // kilocode_change: add kilocode for anonymous access
) {
return true
}
Expand Down
28 changes: 17 additions & 11 deletions webview-ui/src/components/kilocode/settings/providers/KiloCode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,23 @@ export const KiloCode = ({
</Button>
</div>
) : (
<Button
variant="secondary"
onClick={() => {
vscode.postMessage({
type: "switchTab",
tab: "auth",
values: { returnTo: "settings", profileName: currentApiConfigName },
})
}}>
{t("kilocode:settings.provider.login")}
</Button>
<>
<div className="flex flex-row items-center gap-1 text-vscode-charts-green text-sm">
<div className="codicon codicon-info" />
<div>{t("kilocode:settings.provider.loginForPremiumModels")}</div>
</div>
<Button
variant="secondary"
onClick={() => {
vscode.postMessage({
type: "switchTab",
tab: "auth",
values: { returnTo: "settings", profileName: currentApiConfigName },
})
}}>
{t("kilocode:settings.provider.login")}
</Button>
</>
))}

<VSCodeTextField
Expand Down
1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/ar/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/ca/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/cs/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/de/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/en/kilocode.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"apiKey": "Kilo Code API Key",
"login": "Log in at Kilo Code",
"logout": "Log out from Kilo Code",
"loginForPremiumModels": "Log in to access premium models & features",
"providerRouting": {
"title": "Provider Routing",
"managedByOrganization": "Manage Organization-level Provider Routing",
Expand Down
1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/es/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/fr/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/hi/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/id/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/it/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/ja/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/ko/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/nl/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/pl/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/pt-BR/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/ru/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/th/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/tr/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webview-ui/src/i18n/locales/uk/kilocode.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading