Skip to content

Commit c3d2ff3

Browse files
Add Atlas Cloud as a built-in provider (#404)
## Summary - add Atlas Cloud as a built-in OpenAI-compatible provider with `https://api.atlascloud.ai/v1`, `ATLASCLOUD_API_KEY`, and `qwen/qwen3.5-flash` as the default model - wire Atlas Cloud through provider validation, key-prefix detection, onboarding IPC parsing, provider shortlist, public base-url fallback, and provider error help URLs - update the existing English/Chinese provider lists only; no sponsor blocks, logos, credits, or partner copy were added ## Validation - `vitest run packages/shared/src/config.test.ts packages/shared/src/base-url.test.ts packages/providers/src/index.test.ts packages/providers/src/validate.test.ts packages/core/src/errors.test.ts apps/desktop/src/main/onboarding-ipc.test.ts` (228 tests passed) - `biome check` on changed source/test/README files - `tsc --noEmit` for `packages/shared`, `packages/providers`, `packages/core`, and `apps/desktop/tsconfig.node.json` - live Atlas Cloud `/v1/models` check returned 118 models, including `qwen/qwen3.5-flash` and `deepseek-ai/deepseek-v4-pro` - commit hook ran full-repo `biome check .` successfully Note: the repository pre-push hook runs the full test suite. In this local environment, desktop suites that import Electron fail because Electron's install script is blocked by pnpm's approve-builds policy (`Electron failed to install correctly`). The targeted provider/onboarding tests and typechecks above pass. --------- Co-authored-by: binyangzhu000-sudo <224954946+binyangzhu000-sudo@users.noreply.github.com> Co-authored-by: Qihan <3045019481@qq.com>
1 parent f30a85e commit c3d2ff3

16 files changed

Lines changed: 128 additions & 8 deletions

File tree

‎.changeset/atlascloud-provider.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@open-codesign/shared": patch
3+
"@open-codesign/providers": patch
4+
"@open-codesign/core": patch
5+
"@open-codesign/desktop": patch
6+
---
7+
8+
Add Atlas Cloud as a built-in OpenAI-compatible provider for onboarding, validation, model discovery, and provider selection.

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ After each stable tag push, CI syncs SHAs back into `packaging/` and publishes d
226226
On first launch, Open CoDesign opens the Settings page. Pick the path that matches how you already use models:
227227
228228
- **ChatGPT subscription** — sign in with ChatGPT to use Codex models without pasting an API key.
229-
- **API key** — paste Anthropic (`sk-ant-...`), OpenAI (`sk-...`), Google Gemini, OpenRouter, SiliconFlow, DeepSeek, or another supported provider key.
229+
- **API key** — paste Anthropic (`sk-ant-...`), OpenAI (`sk-...`), Atlas Cloud, Google Gemini, OpenRouter, SiliconFlow, DeepSeek, or another supported provider key.
230230
- **Local / keyless** — use Ollama or an IP-allowlisted OpenAI-compatible gateway.
231231
232232
For a custom endpoint that accepts requests without an API key, select **No API key required** in the custom-provider form. Choose the endpoint's wire protocol (including **OpenAI Responses** when appropriate), enter its base URL and default model, and leave the key empty. Local/private endpoint testing still requires the separate network confirmation. Automatic model discovery never sends API keys; for authenticated endpoints, enter a key and click **Test connection**. Switching an existing provider to keyless removes its stored key on save; switching back requires a stored or newly entered key.
@@ -278,7 +278,7 @@ Contract tests check loaded instructions, supported source examples, preference
278278
## What you get
279279
280280
### Models and providers
281-
- **Unified provider model** — Anthropic, OpenAI, Gemini, DeepSeek, OpenRouter, SiliconFlow, local Ollama, or any OpenAI-compatible relay; keyless (IP-allowlisted) proxies supported
281+
- **Unified provider model** — Anthropic, OpenAI, Atlas Cloud, Gemini, DeepSeek, OpenRouter, SiliconFlow, local Ollama, or any OpenAI-compatible relay; keyless (IP-allowlisted) proxies supported
282282
- **One-click import and sign-in** — bring Claude Code / Codex API-key provider configs across, or sign in with ChatGPT subscription for Codex models
283283
- **Dynamic model picker** — every provider exposes its real model catalogue, not a hardcoded shortlist
284284

‎README.zh-CN.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ scoop install opencoworkai/open-codesign
188188
首次启动时,Open CoDesign 会直接打开设置页。按你已有的模型入口选择即可:
189189
190190
- **ChatGPT 订阅登录**:直接登录 ChatGPT,使用 Codex 模型,无需粘贴 API Key。
191-
- **API Key**:粘贴 Anthropic(`sk-ant-...`)、OpenAI(`sk-...`)、Google Gemini、OpenRouter、SiliconFlow、DeepSeek 或其他支持的 provider key。
191+
- **API Key**:粘贴 Anthropic(`sk-ant-...`)、OpenAI(`sk-...`)、Atlas Cloud、Google Gemini、OpenRouter、SiliconFlow、DeepSeek 或其他支持的 provider key。
192192
- **本地 / Keyless**:使用 Ollama,或使用 IP 白名单的 OpenAI 兼容网关。
193193
194194
凭证会保存在 `~/.config/open-codesign/config.toml`,ChatGPT OAuth token 会保存在应用配置目录下的 token store 中。除非你选择的模型入口本身需要联网,请求内容不会额外离开你的机器。
@@ -220,7 +220,7 @@ scoop install opencoworkai/open-codesign
220220
## 你能得到什么
221221
222222
### 模型与提供商
223-
- **统一的 provider 抽象**:支持 Anthropic、OpenAI、Gemini、DeepSeek、OpenRouter、SiliconFlow、本地 Ollama,以及任意 OpenAI-compatible relay;同时支持无 key 的 IP 白名单代理
223+
- **统一的 provider 抽象**:支持 Anthropic、OpenAI、Atlas Cloud、Gemini、DeepSeek、OpenRouter、SiliconFlow、本地 Ollama,以及任意 OpenAI-compatible relay;同时支持无 key 的 IP 白名单代理
224224
- **一键导入和登录**:Claude Code / Codex 的 API key provider 配置可以直接带进来,也可以用 ChatGPT 订阅登录使用 Codex 模型
225225
- **动态模型选择器**:每个 provider 都会展示真实模型列表,而不是一小撮写死的选项
226226

‎apps/desktop/src/main/onboarding-ipc.test.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,25 @@ describe('registerOnboardingIpc — validate-key passes baseUrl to pingProvider'
903903
expect(pingProvider).toHaveBeenCalledWith('anthropic', 'sk-ant-test', undefined);
904904
});
905905

906+
it('forwards Atlas Cloud validation to the provider validator', async () => {
907+
const { pingProvider } = await import('@open-codesign/providers');
908+
vi.mocked(pingProvider).mockClear();
909+
const handler = handlers.get('onboarding:validate-key');
910+
expect(handler).toBeDefined();
911+
912+
await handler?.({} as unknown, {
913+
provider: 'atlascloud',
914+
apiKey: 'apikey-test',
915+
baseUrl: 'https://api.atlascloud.ai/v1',
916+
});
917+
918+
expect(pingProvider).toHaveBeenCalledWith(
919+
'atlascloud',
920+
'apikey-test',
921+
'https://api.atlascloud.ai/v1',
922+
);
923+
});
924+
906925
it('allows explicitly keyless Ollama validation with an empty apiKey', async () => {
907926
const { pingProvider } = await import('@open-codesign/providers');
908927
vi.mocked(pingProvider).mockClear();

‎apps/desktop/src/main/onboarding/provider-parsers.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ export function parseValidateKey(raw: unknown): ValidateKeyInput {
222222
const providerId = provider.trim();
223223
if (!isSupportedOnboardingProvider(providerId)) {
224224
throw new CodesignError(
225-
`Provider "${providerId}" is not supported. Only anthropic, openai, openrouter, ollama.`,
225+
`Provider "${providerId}" is not supported. Only anthropic, openai, atlascloud, openrouter, ollama.`,
226226
ERROR_CODES.PROVIDER_NOT_SUPPORTED,
227227
);
228228
}

‎packages/core/src/agent.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ function supportsImageInput(wire: WireApi | undefined, modelId: string): boolean
271271
const BUILTIN_PUBLIC_BASE_URLS: Record<string, string> = {
272272
anthropic: 'https://api.anthropic.com',
273273
openai: 'https://api.openai.com/v1',
274+
atlascloud: 'https://api.atlascloud.ai/v1',
274275
openrouter: 'https://openrouter.ai/api/v1',
275276
};
276277

‎packages/core/src/errors.test.ts‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ describe('rewriteUpstreamMessage', () => {
3030
expect(result.message).toContain('openrouter.ai/settings/keys');
3131
});
3232

33+
it('rewrites leaked openai URL to Atlas Cloud URL', () => {
34+
const result = rewriteUpstreamMessage(LEAKED, 'atlascloud', 401);
35+
expect(result.rewritten).toBe(true);
36+
expect(result.message).not.toContain('openai.com');
37+
expect(result.message).toContain('atlascloud.ai');
38+
});
39+
3340
it('rewrites to deepseek URL even though it is not in the typed enum', () => {
3441
const result = rewriteUpstreamMessage(LEAKED, 'deepseek', 401);
3542
expect(result.message).toContain('platform.deepseek.com/api_keys');

‎packages/core/src/errors.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { CodesignError, ERROR_CODES } from '@open-codesign/shared';
2121

2222
export const PROVIDER_KEY_HELP_URL: Partial<Record<ProviderId, string>> = {
2323
openai: 'https://platform.openai.com/account/api-keys',
24+
atlascloud: 'https://atlascloud.ai/',
2425
anthropic: 'https://console.anthropic.com/settings/keys',
2526
openrouter: 'https://openrouter.ai/settings/keys',
2627
google: 'https://aistudio.google.com/app/apikey',

‎packages/providers/src/index.test.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ vi.mock('@mariozechner/pi-ai', () => ({
99
completeSimple: (...args: unknown[]) => completeSimpleMock(...args),
1010
}));
1111

12-
import { complete, inferReasoning } from './index';
12+
import { complete, detectProviderFromKey, inferReasoning } from './index';
1313

1414
const MODEL: ModelRef = { provider: 'openai', modelId: 'gpt-4o' };
1515

@@ -18,6 +18,12 @@ afterEach(() => {
1818
completeSimpleMock.mockReset();
1919
});
2020

21+
describe('detectProviderFromKey', () => {
22+
it('recognizes Atlas Cloud API keys', () => {
23+
expect(detectProviderFromKey(' apikey-test ')).toBe('atlascloud');
24+
});
25+
});
26+
2127
describe('complete', () => {
2228
it.each([
2329
[undefined, 'low'],

‎packages/providers/src/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,7 @@ export function detectProviderFromKey(key: string): ModelRef['provider'] | null
592592
const trimmed = key.trim();
593593
if (trimmed.startsWith('sk-ant-')) return 'anthropic';
594594
if (trimmed.startsWith('sk-or-')) return 'openrouter';
595+
if (trimmed.startsWith('apikey-')) return 'atlascloud';
595596
if (trimmed.startsWith('sk-')) return 'openai';
596597
if (trimmed.startsWith('AIza')) return 'google';
597598
if (trimmed.startsWith('xai-')) return 'xai';

0 commit comments

Comments
 (0)