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
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,16 @@ On first launch, the desktop app opens a SetupWizard only when model configurati

You can add or edit models any time under **Settings → Models**. Credentials are stored locally by the desktop app. If `.env` already contains `MODEL_BASE_URL`, `MODEL_API_KEY`, and `MODEL_NAME`, MicroClaw can prefill or auto-apply that configuration.

GitHub Copilot is not supported in MicroClaw. Existing credentials and configuration are retained, but its models cannot be selected or used for desktop chats. Other cloud and local model providers remain available.
#### Internal provider testing

GitHub Copilot is disabled by default: it is absent from onboarding, model settings and model pickers, and the desktop rejects its sign-in and model-selection requests. Existing credentials and configuration are retained, but cannot be used for desktop chats while disabled. Internal testing can opt in by setting `MICROCLAW_INTERNAL_GITHUB_COPILOT=1` in the process environment **before starting MicroClaw** (including packaged builds). Fully exit an existing app instance first; the flag is not a persisted user setting and takes effect after restart. Browser-only development keeps Copilot disabled.

```powershell
$env:MICROCLAW_INTERNAL_GITHUB_COPILOT = '1'
& "$HOME\.microclaw\MicroClawDesktop.exe"
```

Remove the environment variable and fully restart the app to disable the internal feature again. Other cloud and local model providers remain available.

#### Local models (experimental)

Expand Down
11 changes: 11 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ MicroClaw 仅支持 **Windows 10/11**。对大多数用户来说,真正需要

之后你可以在 **设置 → 模型** 中随时添加或修改。凭据由桌面应用本地保存,不需要手动创建配置文件。如果 `.env` 已经包含 `MODEL_BASE_URL`、`MODEL_API_KEY` 和 `MODEL_NAME`,MicroClaw 可以直接预填甚至自动写入配置。

#### 内部提供商测试

GitHub Copilot 默认关闭,不显示在首次配置、模型设置和模型选择器中,桌面端也会拒绝其登录和模型选择请求。已有凭据和配置会保留,但关闭时不能用于桌面聊天。内部测试可在启动 MicroClaw **之前**,在进程环境中设置 `MICROCLAW_INTERNAL_GITHUB_COPILOT=1`,开发版和打包版均适用。请先完全退出已有应用实例;此开关不是持久化的用户设置,重启后才会生效。纯浏览器开发模式仍保持关闭。

```powershell
$env:MICROCLAW_INTERNAL_GITHUB_COPILOT = '1'
& "$HOME\.microclaw\MicroClawDesktop.exe"
```

删除该环境变量并完全重启应用,即可再次关闭此内部功能。其他云端和本地模型提供商不受影响。

#### 本地模型(实验性)

**设置 → 模型** 以统一卡片展示云端提供商和“本地模型”(llama.cpp);已有自定义 API 连接仍可管理,但总览不再提供“添加兼容 API”按钮或未配置的占位卡片。点击“管理”在原卡片中展开连接编辑。这里保存连接不切换默认模型,也不发送 AI 测试消息;初次配置仍保留原有引导流程。默认模型单独选择。
Expand Down
5 changes: 2 additions & 3 deletions desktop/electron-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ nsis:
shortcutName: OpenClaw

extraResources:
- from: dist/github-copilot-auth-worker.js
to: github-copilot-auth-worker.js
- from: src/openclaw-approval-replay-compat.mjs
to: openclaw-approval-replay-compat.mjs
- from: src/openclaw-fs-safe-compat.mjs
Expand Down Expand Up @@ -90,9 +92,6 @@ extraResources:

files:
- dist/**/*
- "!dist/github-copilot-*"
- "!dist/internal-features.*"
- "!dist/model-provider-plugins.*"
- renderer/dist/**/*
- assets/**/*
- "!**/node_modules/*/{CHANGELOG.md,README.md,readme.md,README,readme}"
Expand Down
22 changes: 22 additions & 0 deletions desktop/renderer/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ interface ClipboardImageInput {
data: ArrayBuffer;
}

type GitHubCopilotLoginEvent =
| {
sessionId: string;
status: "code";
verificationUrl: string;
userCode: string;
expiresInMs: number;
}
| { sessionId: string; status: "success"; defaultModel?: string }
| { sessionId: string; status: "cancelled" }
| { sessionId: string; status: "error"; message: string };

interface SkillEntry {
id: string;
name: string;
Expand Down Expand Up @@ -178,6 +190,9 @@ type UpdateCheckResult =
};

interface OpenClawAPI {
readonly features: {
readonly githubCopilot: boolean;
};
gateway: {
getPort(): Promise<number>;
getStatus(): Promise<string>;
Expand Down Expand Up @@ -357,6 +372,13 @@ interface OpenClawAPI {
modelName: string;
reasoningEffort?: string;
}): Promise<{ ok: boolean; message: string; baseUrl?: string }>;
prepareGitHubCopilot(): Promise<{ restartRequired: boolean }>;
startGitHubCopilotLogin(): Promise<{ sessionId: string }>;
cancelGitHubCopilotLogin(sessionId?: string): Promise<{ cancelled: boolean }>;
disconnectGitHubCopilot(): Promise<{ disconnected: true; removedProfiles: number }>;
getGitHubCopilotStatus(): Promise<{ authenticated: boolean }>;
listGitHubCopilotModels(): Promise<Array<{ id: string; name: string }>>;
onGitHubCopilotLoginEvent(callback: (event: GitHubCopilotLoginEvent) => void): () => void;
};
window: {
minimize(): Promise<void>;
Expand Down
12 changes: 12 additions & 0 deletions desktop/renderer/src/assets/github-copilot-black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions desktop/renderer/src/browser-openclaw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function createBrowserOpenClawMock(): OpenClawAPI {
];

return {
features: { githubCopilot: false },
gateway: {
getStatus: async () => "running",
getPort: async () => 18789,
Expand Down Expand Up @@ -320,6 +321,13 @@ export function createBrowserOpenClawMock(): OpenClawAPI {
throw new Error("Model registration requires the desktop Gateway");
},
testConnection: async () => ({ ok: true, message: "Connection successful" }),
prepareGitHubCopilot: async () => ({ restartRequired: false }),
startGitHubCopilotLogin: async () => ({ sessionId: "browser-dev" }),
cancelGitHubCopilotLogin: async () => ({ cancelled: true }),
disconnectGitHubCopilot: async () => ({ disconnected: true, removedProfiles: 0 }),
getGitHubCopilotStatus: async () => ({ authenticated: false }),
listGitHubCopilotModels: async () => [],
onGitHubCopilotLoginEvent: noopSub,
},
plugin: {
weixin: {
Expand Down
24 changes: 12 additions & 12 deletions desktop/renderer/src/components/DefaultModelPicker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ const PopoverStub = defineComponent({
template: `<div><div @click="$emit('update:visible', !visible)"><slot name="reference" /></div><div v-show="visible"><slot /></div></div>`,
});
const models: ConfiguredModel[] = [
{ providerKey: "enterprise", id: "gpt-5.5", name: "GPT-5.5", source: "custom" },
{ providerKey: "enterprise", id: "claude", name: "Claude", source: "custom" },
{ providerKey: "github-copilot", id: "gpt-5.5", name: "GPT-5.5", source: "auth-managed" },
{ providerKey: "github-copilot", id: "claude", name: "Claude", source: "auth-managed" },
{ providerKey: "llama-cpp", id: "qwen3.5-4b-q4_k_m", name: "Qwen3.5 4B", source: "managed" },
{ providerKey: "llama-cpp", id: "qwen3.5-9b-q4_k_m", name: "Qwen3.5 9B", source: "managed" },
];
Expand All @@ -28,7 +28,7 @@ async function open(realPopover = false) {
attachTo: document.body,
props: {
models,
modelValue: "enterprise/gpt-5.5",
modelValue: "github-copilot/gpt-5.5",
modelStatus: (model) => (model.providerKey === "llama-cpp" ? "Not downloaded" : ""),
},
global: {
Expand Down Expand Up @@ -95,18 +95,18 @@ describe("DefaultModelPicker", () => {
it("groups the settings default list and expands only the current default's provider", async () => {
const wrapper = await open();
expect(wrapper.get('[data-testid="default-model-picker"]').text()).toContain(
"enterprise · GPT-5.5",
"GitHub Copilot · GPT-5.5",
);
expect(wrapper.get('[data-provider-group="enterprise"]').attributes("aria-expanded")).toBe(
expect(wrapper.get('[data-provider-group="github-copilot"]').attributes("aria-expanded")).toBe(
"true",
);
expect(wrapper.get('[data-provider-group="llama-cpp"]').attributes("aria-expanded")).toBe(
"false",
);
expect(wrapper.get('[data-model-ref="llama-cpp/qwen3.5-4b-q4_k_m"]').isVisible()).toBe(false);
await wrapper.get('[data-provider-group="enterprise"]').trigger("click");
await wrapper.get('[data-provider-group="github-copilot"]').trigger("click");
await wrapper.get('[data-provider-group="llama-cpp"]').trigger("click");
expect(wrapper.get('[data-model-ref="enterprise/gpt-5.5"]').isVisible()).toBe(false);
expect(wrapper.get('[data-model-ref="github-copilot/gpt-5.5"]').isVisible()).toBe(false);
expect(wrapper.get('[data-model-ref="llama-cpp/qwen3.5-4b-q4_k_m"]').isVisible()).toBe(true);
expect(wrapper.emitted("change")).toBeUndefined();
});
Expand All @@ -115,11 +115,11 @@ describe("DefaultModelPicker", () => {
const wrapper = await open();
await wrapper.get(".model-search").setValue("qwen");
expect(wrapper.get('[data-model-ref="llama-cpp/qwen3.5-4b-q4_k_m"]').isVisible()).toBe(true);
expect(wrapper.find('[data-provider-group="enterprise"]').exists()).toBe(false);
expect(wrapper.find('[data-provider-group="github-copilot"]').exists()).toBe(false);
await wrapper.get(".model-search").setValue("absent");
expect(wrapper.text()).toContain("No matching models.");
await wrapper.get(".clear-search").trigger("click");
expect(wrapper.get('[data-provider-group="enterprise"]').attributes("aria-expanded")).toBe(
expect(wrapper.get('[data-provider-group="github-copilot"]').attributes("aria-expanded")).toBe(
"true",
);
expect(wrapper.get('[data-provider-group="llama-cpp"]').attributes("aria-expanded")).toBe(
Expand All @@ -143,11 +143,11 @@ describe("DefaultModelPicker", () => {

it("preserves collapsed groups on reopening and refreshes the current default without inventing one", async () => {
const wrapper = await open();
await wrapper.get('[data-provider-group="enterprise"]').trigger("click");
await wrapper.get('[data-provider-group="github-copilot"]').trigger("click");
await wrapper.get('[data-testid="default-model-picker"]').trigger("click");
await wrapper.get('[data-testid="default-model-picker"]').trigger("click");
await wrapper.setProps({ models: [...models] });
expect(wrapper.get('[data-provider-group="enterprise"]').attributes("aria-expanded")).toBe(
expect(wrapper.get('[data-provider-group="github-copilot"]').attributes("aria-expanded")).toBe(
"false",
);
await wrapper.setProps({ modelValue: "" });
Expand All @@ -165,7 +165,7 @@ describe("DefaultModelPicker", () => {
expect(wrapper.get('[data-testid="default-model-picker"]').attributes("aria-expanded")).toBe(
"false",
);
await wrapper.get('[data-model-ref="enterprise/gpt-5.5"]').trigger("click");
await wrapper.get('[data-model-ref="github-copilot/gpt-5.5"]').trigger("click");
expect(wrapper.emitted("change")).toBeUndefined();
});
});
Loading
Loading