Skip to content

Commit 391fc0b

Browse files
committed
Merge upstream/main into main
2 parents 76cfad7 + c838a4d commit 391fc0b

865 files changed

Lines changed: 59317 additions & 7288 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/labeler.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@
240240
- changed-files:
241241
- any-glob-to-any-file:
242242
- "extensions/device-pair/**"
243+
"extensions: acpx":
244+
- changed-files:
245+
- any-glob-to-any-file:
246+
- "extensions/acpx/**"
243247
"extensions: minimax-portal-auth":
244248
- changed-files:
245249
- any-glob-to-any-file:

.github/workflows/auto-response.yml

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ name: Auto response
33
on:
44
issues:
55
types: [opened, edited, labeled]
6+
issue_comment:
7+
types: [created]
68
pull_request_target:
79
types: [labeled]
810

@@ -42,6 +44,7 @@ jobs:
4244
{
4345
label: "r: testflight",
4446
close: true,
47+
commentTriggers: ["testflight"],
4548
message: "Not available, build from source.",
4649
},
4750
{
@@ -55,11 +58,76 @@ jobs:
5558
close: true,
5659
lock: true,
5760
lockReason: "off-topic",
61+
commentTriggers: ["moltbook"],
5862
message:
5963
"OpenClaw is not affiliated with Moltbook, and issues related to Moltbook should not be submitted here.",
6064
},
6165
];
6266
67+
const maintainerTeam = "maintainer";
68+
const pingWarningMessage =
69+
"Please don’t spam-ping multiple maintainers at once. Be patient, or join our community Discord for help: https://discord.gg/clawd";
70+
const mentionRegex = /@([A-Za-z0-9-]+)/g;
71+
const maintainerCache = new Map();
72+
const normalizeLogin = (login) => login.toLowerCase();
73+
74+
const isMaintainer = async (login) => {
75+
if (!login) {
76+
return false;
77+
}
78+
const normalized = normalizeLogin(login);
79+
if (maintainerCache.has(normalized)) {
80+
return maintainerCache.get(normalized);
81+
}
82+
let isMember = false;
83+
try {
84+
const membership = await github.rest.teams.getMembershipForUserInOrg({
85+
org: context.repo.owner,
86+
team_slug: maintainerTeam,
87+
username: normalized,
88+
});
89+
isMember = membership?.data?.state === "active";
90+
} catch (error) {
91+
if (error?.status !== 404) {
92+
throw error;
93+
}
94+
}
95+
maintainerCache.set(normalized, isMember);
96+
return isMember;
97+
};
98+
99+
const countMaintainerMentions = async (body, authorLogin) => {
100+
if (!body) {
101+
return 0;
102+
}
103+
const normalizedAuthor = authorLogin ? normalizeLogin(authorLogin) : "";
104+
if (normalizedAuthor && (await isMaintainer(normalizedAuthor))) {
105+
return 0;
106+
}
107+
108+
const haystack = body.toLowerCase();
109+
const teamMention = `@${context.repo.owner.toLowerCase()}/${maintainerTeam}`;
110+
if (haystack.includes(teamMention)) {
111+
return 3;
112+
}
113+
114+
const mentions = new Set();
115+
for (const match of body.matchAll(mentionRegex)) {
116+
mentions.add(normalizeLogin(match[1]));
117+
}
118+
if (normalizedAuthor) {
119+
mentions.delete(normalizedAuthor);
120+
}
121+
122+
let count = 0;
123+
for (const login of mentions) {
124+
if (await isMaintainer(login)) {
125+
count += 1;
126+
}
127+
}
128+
return count;
129+
};
130+
63131
const triggerLabel = "trigger-response";
64132
const target = context.payload.issue ?? context.payload.pull_request;
65133
if (!target) {
@@ -72,6 +140,63 @@ jobs:
72140
.filter((name) => typeof name === "string"),
73141
);
74142
143+
const issue = context.payload.issue;
144+
const pullRequest = context.payload.pull_request;
145+
const comment = context.payload.comment;
146+
if (comment) {
147+
const authorLogin = comment.user?.login ?? "";
148+
if (comment.user?.type === "Bot" || authorLogin.endsWith("[bot]")) {
149+
return;
150+
}
151+
152+
const commentBody = comment.body ?? "";
153+
const responses = [];
154+
const mentionCount = await countMaintainerMentions(commentBody, authorLogin);
155+
if (mentionCount >= 3) {
156+
responses.push(pingWarningMessage);
157+
}
158+
159+
const commentHaystack = commentBody.toLowerCase();
160+
const commentRule = rules.find((item) =>
161+
(item.commentTriggers ?? []).some((trigger) =>
162+
commentHaystack.includes(trigger),
163+
),
164+
);
165+
if (commentRule) {
166+
responses.push(commentRule.message);
167+
}
168+
169+
if (responses.length > 0) {
170+
await github.rest.issues.createComment({
171+
owner: context.repo.owner,
172+
repo: context.repo.repo,
173+
issue_number: target.number,
174+
body: responses.join("\n\n"),
175+
});
176+
}
177+
return;
178+
}
179+
180+
if (issue) {
181+
const action = context.payload.action;
182+
if (action === "opened" || action === "edited") {
183+
const issueText = `${issue.title ?? ""}\n${issue.body ?? ""}`.trim();
184+
const authorLogin = issue.user?.login ?? "";
185+
const mentionCount = await countMaintainerMentions(
186+
issueText,
187+
authorLogin,
188+
);
189+
if (mentionCount >= 3) {
190+
await github.rest.issues.createComment({
191+
owner: context.repo.owner,
192+
repo: context.repo.repo,
193+
issue_number: issue.number,
194+
body: pingWarningMessage,
195+
});
196+
}
197+
}
198+
}
199+
75200
const hasTriggerLabel = labelSet.has(triggerLabel);
76201
if (hasTriggerLabel) {
77202
labelSet.delete(triggerLabel);
@@ -94,7 +219,6 @@ jobs:
94219
return;
95220
}
96221
97-
const issue = context.payload.issue;
98222
if (issue) {
99223
const title = issue.title ?? "";
100224
const body = issue.body ?? "";
@@ -136,7 +260,6 @@ jobs:
136260
const noisyPrMessage =
137261
"Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.";
138262
139-
const pullRequest = context.payload.pull_request;
140263
if (pullRequest) {
141264
if (labelSet.has(dirtyLabel)) {
142265
await github.rest.issues.createComment({

.github/workflows/ci.yml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,12 +418,23 @@ jobs:
418418
include:
419419
- runtime: node
420420
task: lint
421+
shard_index: 0
422+
shard_count: 1
421423
command: pnpm lint
422424
- runtime: node
423425
task: test
426+
shard_index: 1
427+
shard_count: 2
428+
command: pnpm canvas:a2ui:bundle && pnpm test
429+
- runtime: node
430+
task: test
431+
shard_index: 2
432+
shard_count: 2
424433
command: pnpm canvas:a2ui:bundle && pnpm test
425434
- runtime: node
426435
task: protocol
436+
shard_index: 0
437+
shard_count: 1
427438
command: pnpm protocol:check
428439
steps:
429440
- name: Checkout
@@ -495,6 +506,12 @@ jobs:
495506
pnpm -v
496507
pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
497508
509+
- name: Configure test shard (Windows)
510+
if: matrix.task == 'test'
511+
run: |
512+
echo "OPENCLAW_TEST_SHARDS=${{ matrix.shard_count }}" >> "$GITHUB_ENV"
513+
echo "OPENCLAW_TEST_SHARD_INDEX=${{ matrix.shard_index }}" >> "$GITHUB_ENV"
514+
498515
- name: Configure vitest JSON reports
499516
if: matrix.task == 'test'
500517
run: echo "OPENCLAW_VITEST_REPORT_DIR=$RUNNER_TEMP/vitest-reports" >> "$GITHUB_ENV"
@@ -512,7 +529,7 @@ jobs:
512529
if: matrix.task == 'test'
513530
uses: actions/upload-artifact@v4
514531
with:
515-
name: vitest-reports-${{ runner.os }}-${{ matrix.runtime }}
532+
name: vitest-reports-${{ runner.os }}-${{ matrix.runtime }}-shard${{ matrix.shard_index }}of${{ matrix.shard_count }}
516533
path: |
517534
${{ env.OPENCLAW_VITEST_REPORT_DIR }}
518535
${{ runner.temp }}/vitest-slowest.md

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Repository Guidelines
22

33
- Repo: https://github.com/openclaw/openclaw
4+
- In chat replies, file references must be repo-root relative only (example: `extensions/bluebubbles/src/channel.ts:80`); never absolute paths or `~/...`.
45
- GitHub issues/comments/PR comments: use literal multiline strings or `-F - <<'EOF'` (or $'...') for real newlines; never embed "\\n".
56
- GitHub comment footgun: never use `gh issue/pr comment -b "..."` when body contains backticks or shell chars. Always use single-quoted heredoc (`-F - <<'EOF'`) so no command substitution/escaping corruption.
67
- GitHub linking footgun: don’t wrap issue/PR refs like `#24643` in backticks when you want auto-linking. Use plain `#24643` (optionally add full URL).

0 commit comments

Comments
 (0)