Skip to content

Commit a07f278

Browse files
committed
chore: narrow windows branch scope to desktop delivery
1 parent 2f35690 commit a07f278

60 files changed

Lines changed: 1766 additions & 2709 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Cancel closed PR Desktop E2E runs
2+
3+
on:
4+
pull_request:
5+
types: [closed]
6+
7+
permissions:
8+
actions: write
9+
contents: read
10+
pull-requests: read
11+
12+
jobs:
13+
cancel-desktop-e2e-runs:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Cancel stale Desktop E2E pull_request runs for this PR
17+
uses: actions/github-script@v7
18+
with:
19+
script: |
20+
const prNumber = context.payload.pull_request.number;
21+
const workflowId = "desktop-e2e.yml";
22+
const owner = context.repo.owner;
23+
const repo = context.repo.repo;
24+
25+
const workflowRuns = await github.paginate(github.rest.actions.listWorkflowRuns, {
26+
owner,
27+
repo,
28+
workflow_id: workflowId,
29+
event: "pull_request",
30+
per_page: 100,
31+
});
32+
33+
const cancellableRuns = workflowRuns.filter((run) => {
34+
const matchesPr = run.pull_requests?.some((pr) => pr.number === prNumber);
35+
const isCancellable = run.status !== "completed";
36+
37+
return matchesPr && isCancellable;
38+
});
39+
40+
if (cancellableRuns.length === 0) {
41+
core.info(`No queued or in-progress Desktop E2E pull_request runs found for PR #${prNumber}.`);
42+
return;
43+
}
44+
45+
for (const run of cancellableRuns) {
46+
core.info(`Canceling Desktop E2E run ${run.id} for PR #${prNumber} (status: ${run.status}).`);
47+
48+
await github.rest.actions.cancelWorkflowRun({
49+
owner,
50+
repo,
51+
run_id: run.id,
52+
});
53+
}
54+
55+
core.info(`Requested cancellation for ${cancellableRuns.length} Desktop E2E run(s) for PR #${prNumber}.`);

apps/controller/src/app/container.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,10 @@ const NEXU_OFFICIAL_MODEL_REFRESH_INTERVAL_MS = 60 * 1000;
7777
export async function createContainer(): Promise<ControllerContainer> {
7878
const configStore = new NexuConfigStore(env);
7979
await configStore.reconcileConfiguredDesktopCloudState();
80-
if (env.manageOpenclawProcess) {
81-
await configStore.syncManagedRuntimeGateway({
82-
port: env.openclawGatewayPort,
83-
authMode: env.openclawGatewayToken ? "token" : "none",
84-
});
85-
}
80+
await configStore.syncManagedRuntimeGateway({
81+
port: env.openclawGatewayPort,
82+
authMode: env.openclawGatewayToken ? "token" : "none",
83+
});
8684
const artifactsStore = new ArtifactsStore(env);
8785
const compiledStore = new CompiledOpenClawStore(env);
8886
const configWriter = new OpenClawConfigWriter(env);

apps/controller/src/app/env.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const booleanSchema = z
3737
.enum(["true", "false", "1", "0"])
3838
.transform((value) => value === "true" || value === "1");
3939

40+
function booleanWithDefault(defaultValue: boolean) {
41+
return booleanSchema.optional().transform((value) => value ?? defaultValue);
42+
}
43+
4044
const openclawOwnershipModeSchema = z.enum(["external", "internal"]);
4145

4246
function parseUrlPort(value: string): number | null {
@@ -93,8 +97,8 @@ const envSchema = z.object({
9397
OPENCLAW_LAUNCHD_LABEL: z.string().optional(),
9498
LITELLM_BASE_URL: z.string().optional(),
9599
LITELLM_API_KEY: z.string().optional(),
96-
RUNTIME_MANAGE_OPENCLAW_PROCESS: booleanSchema.default("false"),
97-
RUNTIME_GATEWAY_PROBE_ENABLED: booleanSchema.default("true"),
100+
RUNTIME_MANAGE_OPENCLAW_PROCESS: booleanWithDefault(false),
101+
RUNTIME_GATEWAY_PROBE_ENABLED: booleanWithDefault(true),
98102
RUNTIME_SYNC_INTERVAL_MS: z.coerce.number().int().positive().default(2000),
99103
RUNTIME_HEALTH_INTERVAL_MS: z.coerce.number().int().positive().default(5000),
100104
DEFAULT_MODEL_ID: z.string().default("link/gemini-3-flash-preview"),
@@ -103,8 +107,6 @@ const envSchema = z.object({
103107
VITE_POSTHOG_API_KEY: z.string().optional(),
104108
POSTHOG_HOST: z.string().optional(),
105109
VITE_POSTHOG_HOST: z.string().optional(),
106-
AMPLITUDE_API_KEY: z.string().optional(),
107-
VITE_AMPLITUDE_API_KEY: z.string().optional(),
108110
});
109111

110112
const parsed = envSchema.parse(process.env);
@@ -202,8 +204,6 @@ export const env = {
202204
posthogApiKey:
203205
parsed.POSTHOG_API_KEY?.trim() || parsed.VITE_POSTHOG_API_KEY?.trim(),
204206
posthogHost: parsed.POSTHOG_HOST?.trim() || parsed.VITE_POSTHOG_HOST?.trim(),
205-
amplitudeApiKey:
206-
parsed.AMPLITUDE_API_KEY?.trim() || parsed.VITE_AMPLITUDE_API_KEY?.trim(),
207207
};
208208

209209
export type ControllerEnv = typeof env;

apps/controller/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ async function main(): Promise<void> {
99
const container = await createContainer();
1010
const stopBackgroundLoops = await bootstrapController(container);
1111
const app = createApp(container);
12-
1312
const server = serve(
1413
{
1514
fetch: app.fetch,

apps/controller/src/lib/channel-binding-compiler.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ export function compileChannelsConfig(params: {
278278
? {
279279
feishu: {
280280
enabled: true,
281+
// Card Kit streaming: replies stream in real-time via feishu
282+
// interactive cards. Without these, replies arrive as plain text
283+
// after the full LLM response completes (no streaming UX).
281284
streaming: true,
282285
renderMode: "card",
283286
dmPolicy: "open",

apps/controller/src/lib/openclaw-runtime-bridge.ts

Lines changed: 0 additions & 41 deletions
This file was deleted.

apps/controller/src/runtime/openclaw-process.ts

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import path from "node:path";
77
import { createInterface } from "node:readline";
88
import type { ControllerEnv } from "../app/env.js";
99
import { logger } from "../lib/logger.js";
10-
import { resolveOpenClawEntryPath } from "../lib/openclaw-runtime-bridge.js";
1110

1211
const MAX_CONSECUTIVE_RESTARTS = 10;
1312
const BASE_RESTART_DELAY_MS = 3000;
@@ -38,6 +37,20 @@ function findWorkspaceRoot(startDir: string): string | null {
3837
return null;
3938
}
4039

40+
function resolveOpenclawEntryFromBin(binPath: string): string | null {
41+
const resolvedBinPath = path.resolve(binPath.trim());
42+
if (resolvedBinPath.endsWith(".mjs") && existsSync(resolvedBinPath)) {
43+
return resolvedBinPath;
44+
}
45+
46+
const entry = path.resolve(
47+
path.dirname(resolvedBinPath),
48+
"..",
49+
"node_modules/openclaw/openclaw.mjs",
50+
);
51+
return existsSync(entry) ? entry : null;
52+
}
53+
4154
export interface OpenClawRuntimeEvent {
4255
event: string;
4356
payload?: unknown;
@@ -123,55 +136,49 @@ export class OpenClawProcessManager {
123136
let extraEnv: Record<string, string> = {};
124137

125138
if (electronExec) {
126-
const entry = resolveOpenClawEntryPath({
127-
openclawBin: this.env.openclawBin,
128-
workspaceRoot:
139+
const openclawEntryFromBin = resolveOpenclawEntryFromBin(
140+
this.env.openclawBin,
141+
);
142+
if (openclawEntryFromBin) {
143+
cmd = electronExec;
144+
args = [openclawEntryFromBin, "gateway", "run"];
145+
extraEnv = { ELECTRON_RUN_AS_NODE: "1" };
146+
} else {
147+
const workspaceRoot =
129148
process.env.NEXU_WORKSPACE_ROOT?.trim() ||
130-
findWorkspaceRoot(process.cwd()),
131-
});
132-
if (!entry) {
133-
throw new Error(
134-
"Unable to resolve OpenClaw entry point from OPENCLAW_BIN",
135-
);
149+
findWorkspaceRoot(process.cwd());
150+
const runtimeEntryPath = workspaceRoot
151+
? path.join(
152+
workspaceRoot,
153+
"openclaw-runtime",
154+
"node_modules",
155+
"openclaw",
156+
"openclaw.mjs",
157+
)
158+
: null;
159+
160+
if (runtimeEntryPath && existsSync(runtimeEntryPath)) {
161+
cmd = electronExec;
162+
args = [runtimeEntryPath, "gateway", "run"];
163+
extraEnv = { ELECTRON_RUN_AS_NODE: "1" };
164+
} else {
165+
// Resolve the openclaw entry point relative to the bin script
166+
const entry = resolveOpenclawEntryFromBin(this.env.openclawBin);
167+
if (!entry) {
168+
throw new Error(
169+
"Unable to resolve OpenClaw entry point from OPENCLAW_BIN",
170+
);
171+
}
172+
cmd = electronExec;
173+
args = [entry, "gateway", "run"];
174+
extraEnv = { ELECTRON_RUN_AS_NODE: "1" };
175+
}
136176
}
137-
logger.info(
138-
{
139-
openclawBin: this.env.openclawBin,
140-
entry,
141-
electronExec,
142-
},
143-
"controller resolved openclaw entry via repo-root openclaw runtime bridge",
144-
);
145-
cmd = electronExec;
146-
args = [entry, "gateway", "run"];
147-
extraEnv = { ELECTRON_RUN_AS_NODE: "1" };
148177
} else {
149-
logger.info(
150-
{
151-
openclawBin: this.env.openclawBin,
152-
electronExec: null,
153-
},
154-
"controller using direct openclaw binary path",
155-
);
156178
cmd = this.env.openclawBin;
157179
args = ["gateway", "run"];
158180
}
159181

160-
logger.info(
161-
{
162-
mode: electronExec ? "electron-entry" : "direct-bin",
163-
cmd,
164-
args,
165-
cwd: path.resolve(this.env.openclawStateDir),
166-
openclawBin: this.env.openclawBin,
167-
openclawBinExists: existsSync(this.env.openclawBin),
168-
electronExec,
169-
electronExecExists: electronExec ? existsSync(electronExec) : null,
170-
platform: process.platform,
171-
},
172-
"openclaw spawn contract",
173-
);
174-
175182
const child = spawn(cmd, args, {
176183
cwd: path.resolve(this.env.openclawStateDir),
177184
env: {
@@ -232,13 +239,7 @@ export class OpenClawProcessManager {
232239

233240
child.once("error", (error) => {
234241
logger.error(
235-
{
236-
error: error.message,
237-
code: "code" in error ? error.code : undefined,
238-
path: "path" in error ? error.path : undefined,
239-
syscall: "syscall" in error ? error.syscall : undefined,
240-
spawnargs: "spawnargs" in error ? error.spawnargs : undefined,
241-
},
242+
{ error: error.message },
242243
"failed to spawn openclaw process",
243244
);
244245
this.child = null;

apps/controller/src/runtime/sessions-runtime.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,16 +1224,9 @@ export class SessionsRuntime {
12241224
return true;
12251225
}
12261226

1227-
if (normalized === sessionKey || UUID_LIKE_TITLE_PATTERN.test(normalized)) {
1228-
return true;
1229-
}
1230-
1231-
if (normalized.endsWith(" · qqbot")) {
1232-
const qqbotLabel = normalized.slice(0, -" · qqbot".length).trim();
1233-
return this.isOpaqueQqbotValue(qqbotLabel, "user");
1234-
}
1235-
1236-
return false;
1227+
return (
1228+
normalized === sessionKey || UUID_LIKE_TITLE_PATTERN.test(normalized)
1229+
);
12371230
}
12381231

12391232
private extractExactChatTargetMetadata(

0 commit comments

Comments
 (0)