mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-15 06:59:48 +08:00
Compare commits
4 Commits
v2.5.2
..
c9a50cc908
| Author | SHA1 | Date | |
|---|---|---|---|
| c9a50cc908 | |||
| 348dc82612 | |||
| dba3d732c3 | |||
| 661bdd08ff |
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Jim Liu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1387,7 +1387,9 @@ This project was inspired by and builds upon the following open source projects:
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
Unless otherwise noted, this repository is licensed under the [MIT License](./LICENSE).
|
||||
|
||||
Published ClawHub skills follow ClawHub registry rules and are distributed under `MIT-0`. Third-party code and assets retain their original licenses where noted.
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
+3
-1
@@ -1388,7 +1388,9 @@ HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 /baoyu-danger
|
||||
|
||||
## 许可证
|
||||
|
||||
MIT
|
||||
除另有说明外,本仓库采用 [MIT License](./LICENSE) 授权。
|
||||
|
||||
发布到 ClawHub 的 skill 根据 ClawHub registry 规则以 `MIT-0` 分发。第三方代码和素材按其注明的原始许可授权。
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
Generated
+5
@@ -5,6 +5,7 @@
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "baoyu-skills",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@@ -5018,12 +5019,14 @@
|
||||
},
|
||||
"packages/baoyu-chrome-cdp": {
|
||||
"version": "0.1.1",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"bun": ">=1.2.0"
|
||||
}
|
||||
},
|
||||
"packages/baoyu-codex-imagegen": {
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"codex-imagegen": "src/main.ts"
|
||||
},
|
||||
@@ -5033,6 +5036,7 @@
|
||||
},
|
||||
"packages/baoyu-fetch": {
|
||||
"version": "0.1.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mozilla/readability": "^0.6.0",
|
||||
"chrome-launcher": "^1.2.1",
|
||||
@@ -5061,6 +5065,7 @@
|
||||
},
|
||||
"packages/baoyu-md": {
|
||||
"version": "0.1.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fflate": "^0.8.2",
|
||||
"front-matter": "^4.0.2",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "baoyu-skills",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "baoyu-chrome-cdp",
|
||||
"version": "0.1.1",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "baoyu-codex-imagegen",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"description": "Generate images via Codex CLI's built-in image_gen tool from non-Codex runtimes (Claude Code, Hermes, …).",
|
||||
"bin": {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "baoyu-fetch",
|
||||
"version": "0.1.2",
|
||||
"license": "MIT",
|
||||
"description": "Read URLs into high-quality Markdown or JSON with Chrome CDP and site adapters.",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "baoyu-md",
|
||||
"version": "0.1.1",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
|
||||
@@ -456,19 +456,19 @@ export class GeminiClient extends GemMixin {
|
||||
const generated_images: GeneratedImage[] = [];
|
||||
const wants_generated =
|
||||
get_nested_value(candidate, [12, 7, 0], null) != null ||
|
||||
/http:\/\/googleusercontent\.com\/image_generation_content\/\d+/.test(text);
|
||||
/http:\/\/googleusercontent\.com\/image_generation_content\/\d+/.test(text) ||
|
||||
// Gemini 3 no longer emits the legacy marker in the candidate text; the generated
|
||||
// image arrives in a later response part. Detect it from the raw response instead.
|
||||
/\/gg-dl\//.test(txt) ||
|
||||
/image_generation_content\/\d+/.test(txt);
|
||||
|
||||
if (wants_generated) {
|
||||
const image_part = find_generated_image_part(response_json as unknown[], body_index, candidate_index, 1);
|
||||
const img_body = image_part?.body ?? null;
|
||||
|
||||
if (!img_body) {
|
||||
throw new ImageGenerationError(
|
||||
'Failed to parse generated images. Please update gemini_webapi to the latest version. If the error persists and is caused by the package, please report it on GitHub.',
|
||||
);
|
||||
}
|
||||
|
||||
const img_candidate = get_nested_value<unknown[]>(img_body, [4, candidate_index], []);
|
||||
// Not every candidate carries a generated image (e.g. multi-candidate responses).
|
||||
// If this candidate has no image part, skip extraction instead of failing the whole call.
|
||||
const img_candidate = img_body ? get_nested_value<unknown[]>(img_body, [4, candidate_index], []) : [];
|
||||
const finished = get_nested_value<string | null>(img_candidate, [1, 0], null);
|
||||
if (finished) {
|
||||
text = finished.replace(/http:\/\/googleusercontent\.com\/image_generation_content\/\d+/g, '').trimEnd();
|
||||
|
||||
@@ -98,3 +98,68 @@ test('parseMarkdown resolves encoded spaces and literal percent image paths', as
|
||||
assert.equal(result.contentImages[0]?.localPath, path.join(root, 'Pasted image.png'));
|
||||
assert.equal(result.contentImages[1]?.localPath, path.join(root, '100%.png'));
|
||||
});
|
||||
|
||||
test('parseMarkdown renders CJK-adjacent bold and italics (no literal asterisks)', async (t) => {
|
||||
const root = await makeTempDir('x-md-to-html-cjk-bold-');
|
||||
t.after(() => fs.rm(root, { recursive: true, force: true }));
|
||||
|
||||
const markdownPath = path.join(root, 'post.md');
|
||||
const tempDir = path.join(root, 'tmp');
|
||||
await fs.mkdir(tempDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
markdownPath,
|
||||
[
|
||||
'# 标题',
|
||||
'',
|
||||
'分工在变细。**国际大厂卷基础设施,中文项目卷场景落地。**这其实是生态成熟的表现。',
|
||||
'',
|
||||
'半角场景 **Top 10 里平均有 8 个** 项目。',
|
||||
'',
|
||||
'斜体 *数据来源 GitHub* 收尾。',
|
||||
'',
|
||||
'参考 **[docs][d]** 了解更多。',
|
||||
'',
|
||||
'[d]: https://example.com',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
const result = await parseMarkdown(markdownPath, { tempDir });
|
||||
|
||||
// Bold directly adjacent to CJK (closing ** followed by CJK) must render.
|
||||
assert.match(result.html, /<strong>国际大厂卷基础设施,中文项目卷场景落地。<\/strong>/);
|
||||
assert.match(result.html, /<strong>Top 10 里平均有 8 个<\/strong>/);
|
||||
// Italics.
|
||||
assert.match(result.html, /<em>数据来源 GitHub<\/em>/);
|
||||
// Reference-style links inside emphasis must render as links, not plain text.
|
||||
assert.match(result.html, /<strong><a href="https:\/\/example\.com" rel="noopener noreferrer nofollow">docs<\/a><\/strong>/);
|
||||
// No literal emphasis delimiters should leak into the output.
|
||||
assert.doesNotMatch(result.html, /\*\*/);
|
||||
assert.doesNotMatch(result.html, /(?<!\*)\*(?!\*)[^*\n]+\*(?!\*)/);
|
||||
});
|
||||
|
||||
test('parseMarkdown does not decode author-written literal HTML entities into tags', async (t) => {
|
||||
const root = await makeTempDir('x-md-to-html-entities-');
|
||||
t.after(() => fs.rm(root, { recursive: true, force: true }));
|
||||
|
||||
const markdownPath = path.join(root, 'post.md');
|
||||
const tempDir = path.join(root, 'tmp');
|
||||
await fs.mkdir(tempDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
markdownPath,
|
||||
[
|
||||
'# 标题',
|
||||
'',
|
||||
'正文中写 <b>literal</b> 想显示字面标签。**加粗**收尾。',
|
||||
'',
|
||||
'代码里写 `<b>` 同样保留。',
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
const result = await parseMarkdown(markdownPath, { tempDir });
|
||||
|
||||
// CJK-adjacent bold still renders.
|
||||
assert.match(result.html, /<strong>加粗<\/strong>/);
|
||||
// Author-written literal entities must NOT be decoded into real tags.
|
||||
assert.doesNotMatch(result.html, /<b>literal<\/b>/);
|
||||
assert.match(result.html, /<b>literal<\/b>/);
|
||||
});
|
||||
|
||||
@@ -136,6 +136,21 @@ function highlightCode(code: string, lang: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize CJK-adjacent emphasis so `marked` renders it correctly.
|
||||
//
|
||||
// `marked`'s emphasis tokenizer treats a closing `**`/`*` directly followed by a
|
||||
// CJK character as not right-flanking, so it leaves the delimiters literal
|
||||
// (e.g. `**加粗**这` renders as plain text with the asterisks intact). We round-trip
|
||||
// the markdown through `remark-cjk-friendly`, whose stringify serializes the
|
||||
// boundary character as an HTML entity (`这`); the entity is treated as
|
||||
// punctuation by `marked`'s flanking rules, so emphasis parses as expected.
|
||||
//
|
||||
// We deliberately do NOT decode the entities afterward. They are valid HTML
|
||||
// character references that render correctly when the article HTML is pasted into
|
||||
// the X editor, and `marked` only emits them for characters outside the emphasis
|
||||
// span (the boundary char), never inside it. A blanket decode of the rendered
|
||||
// HTML would risk turning author-written literal entities (e.g. `<b>`
|
||||
// meant to display `<b>` as text) into real tags, so we leave them intact.
|
||||
function preprocessCjkMarkdown(markdown: string): string {
|
||||
try {
|
||||
const processor = unified()
|
||||
@@ -143,8 +158,7 @@ function preprocessCjkMarkdown(markdown: string): string {
|
||||
.use(remarkCjkFriendly)
|
||||
.use(remarkStringify);
|
||||
|
||||
const result = String(processor.processSync(markdown));
|
||||
return result.replace(/&#x([0-9A-Fa-f]+);/g, (_, hex: string) => String.fromCodePoint(parseInt(hex, 16)));
|
||||
return String(processor.processSync(markdown));
|
||||
} catch {
|
||||
return markdown;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user