Compare commits

...

4 Commits

Author SHA1 Message Date
Jim Liu 宝玉 61342ecfea chore: release v1.115.2 2026-05-10 02:49:50 -05:00
Jim Liu 宝玉 5f753dd584 fix(baoyu-post-to-x): respect Chrome plugin mode 2026-05-10 02:49:42 -05:00
Jim Liu 宝玉 076192d58e chore: release v1.115.1 2026-05-09 22:10:54 -05:00
Jim Liu 宝玉 d6d434e714 fix(image): update MiniMax default endpoint 2026-05-09 22:07:26 -05:00
15 changed files with 211 additions and 55 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
},
"metadata": {
"description": "Skills shared by Baoyu for improving daily work efficiency",
"version": "1.115.0"
"version": "1.115.2"
},
"plugins": [
{
+11
View File
@@ -2,6 +2,17 @@
English | [中文](./CHANGELOG.zh.md)
## 1.115.2 - 2026-05-10
### Fixes
- `baoyu-post-to-x`: honor explicit Codex Chrome plugin requests as a distinct browser-control mode, keep Chrome Computer Use and CDP fallbacks from silently taking over, and improve X Articles draft creation detection.
## 1.115.1 - 2026-05-10
### Fixes
- `baoyu-imagine`: change the default MiniMax image API endpoint to `https://api.minimaxi.com` to match the current official image generation documentation, while keeping `https://api.minimax.io` available through `MINIMAX_BASE_URL` overrides.
- `baoyu-image-gen`: sync the deprecated image-generation entrypoint with the same MiniMax default endpoint and regression coverage.
## 1.115.0 - 2026-05-09
### Features
+11
View File
@@ -2,6 +2,17 @@
[English](./CHANGELOG.md) | 中文
## 1.115.2 - 2026-05-10
### 修复
- `baoyu-post-to-x`:将显式请求 Codex Chrome 插件的场景作为独立浏览器控制模式处理,避免 Chrome Computer Use 或 CDP 回退流程静默接管;同时改进 X Articles 草稿创建按钮检测。
## 1.115.1 - 2026-05-10
### 修复
- `baoyu-imagine`:将默认 MiniMax 图片 API 端点改为 `https://api.minimaxi.com`,与当前官方图片生成文档保持一致;仍可通过 `MINIMAX_BASE_URL` 覆盖为 `https://api.minimax.io`
- `baoyu-image-gen`:同步已废弃图片生成入口的 MiniMax 默认端点和回归测试。
## 1.115.0 - 2026-05-09
### 新功能
+2 -2
View File
@@ -834,7 +834,7 @@ AI SDK-based image generation using OpenAI GPT Image 2, Azure OpenAI, Google, Op
| `DASHSCOPE_BASE_URL` | Custom DashScope endpoint | - |
| `ZAI_BASE_URL` | Custom Z.AI endpoint | `https://api.z.ai/api/paas/v4` |
| `BIGMODEL_BASE_URL` | Backward-compatible alias for Z.AI endpoint | - |
| `MINIMAX_BASE_URL` | Custom MiniMax endpoint | `https://api.minimax.io` |
| `MINIMAX_BASE_URL` | Custom MiniMax endpoint | `https://api.minimaxi.com` |
| `REPLICATE_BASE_URL` | Custom Replicate endpoint | - |
| `JIMENG_BASE_URL` | Custom Jimeng endpoint | `https://visual.volcengineapi.com` |
| `JIMENG_REGION` | Jimeng region | `cn-north-1` |
@@ -1167,7 +1167,7 @@ ZAI_IMAGE_MODEL=glm-image
# MiniMax
MINIMAX_API_KEY=xxx
MINIMAX_IMAGE_MODEL=image-01
# MINIMAX_BASE_URL=https://api.minimax.io
# MINIMAX_BASE_URL=https://api.minimaxi.com
# Replicate
REPLICATE_API_TOKEN=r8_xxx
+2 -2
View File
@@ -834,7 +834,7 @@ AI 驱动的生成后端。
| `DASHSCOPE_BASE_URL` | 自定义 DashScope 端点 | - |
| `ZAI_BASE_URL` | 自定义 Z.AI 端点 | `https://api.z.ai/api/paas/v4` |
| `BIGMODEL_BASE_URL` | Z.AI 端点向后兼容别名 | - |
| `MINIMAX_BASE_URL` | 自定义 MiniMax 端点 | `https://api.minimax.io` |
| `MINIMAX_BASE_URL` | 自定义 MiniMax 端点 | `https://api.minimaxi.com` |
| `REPLICATE_BASE_URL` | 自定义 Replicate 端点 | - |
| `JIMENG_BASE_URL` | 自定义即梦端点 | `https://visual.volcengineapi.com` |
| `JIMENG_REGION` | 即梦区域 | `cn-north-1` |
@@ -1167,7 +1167,7 @@ ZAI_IMAGE_MODEL=glm-image
# MiniMax
MINIMAX_API_KEY=xxx
MINIMAX_IMAGE_MODEL=image-01
# MINIMAX_BASE_URL=https://api.minimax.io
# MINIMAX_BASE_URL=https://api.minimaxi.com
# Replicate
REPLICATE_API_TOKEN=r8_xxx
@@ -24,6 +24,6 @@ Read when the user picks `--provider minimax` or sets `default_model.minimax`. D
## Official References
- [Image Generation Guide](https://platform.minimax.io/docs/guides/image-generation)
- [Text-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-t2i)
- [Image-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-i2i)
- [Image Generation Guide](https://platform.minimaxi.com/docs/guides/image-generation)
- [Text-to-Image API](https://platform.minimaxi.com/docs/api-reference/image-generation-t2i)
- [Image-to-Image API](https://platform.minimaxi.com/docs/api-reference/image-generation-i2i)
@@ -60,8 +60,11 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
};
}
test("MiniMax URL builder normalizes /v1 suffixes", (t) => {
useEnv(t, { MINIMAX_BASE_URL: "https://api.minimax.io" });
test("MiniMax URL builder uses documented default and normalizes /v1 suffixes", (t) => {
useEnv(t, { MINIMAX_BASE_URL: null });
assert.equal(buildMinimaxUrl(), "https://api.minimaxi.com/v1/image_generation");
process.env.MINIMAX_BASE_URL = "https://api.minimax.io";
assert.equal(buildMinimaxUrl(), "https://api.minimax.io/v1/image_generation");
process.env.MINIMAX_BASE_URL = "https://proxy.example.com/custom/v1/";
@@ -44,7 +44,7 @@ function getApiKey(): string | null {
}
export function buildMinimaxUrl(): string {
const base = (process.env.MINIMAX_BASE_URL || "https://api.minimax.io").replace(/\/+$/g, "");
const base = (process.env.MINIMAX_BASE_URL || "https://api.minimaxi.com").replace(/\/+$/g, "");
return base.endsWith("/v1") ? `${base}/image_generation` : `${base}/v1/image_generation`;
}
@@ -197,7 +197,7 @@ export async function generateImage(
): Promise<Uint8Array> {
const apiKey = getApiKey();
if (!apiKey) {
throw new Error("MINIMAX_API_KEY is required. Get one from https://platform.minimax.io/");
throw new Error("MINIMAX_API_KEY is required. Get one from https://platform.minimaxi.com/");
}
const body = await buildRequestBody(prompt, model, args);
@@ -24,6 +24,6 @@ Read when the user picks `--provider minimax` or sets `default_model.minimax`. D
## Official References
- [Image Generation Guide](https://platform.minimax.io/docs/guides/image-generation)
- [Text-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-t2i)
- [Image-to-Image API](https://platform.minimax.io/docs/api-reference/image-generation-i2i)
- [Image Generation Guide](https://platform.minimaxi.com/docs/guides/image-generation)
- [Text-to-Image API](https://platform.minimaxi.com/docs/api-reference/image-generation-t2i)
- [Image-to-Image API](https://platform.minimaxi.com/docs/api-reference/image-generation-i2i)
@@ -61,8 +61,11 @@ function makeArgs(overrides: Partial<CliArgs> = {}): CliArgs {
};
}
test("MiniMax URL builder normalizes /v1 suffixes", (t) => {
useEnv(t, { MINIMAX_BASE_URL: "https://api.minimax.io" });
test("MiniMax URL builder uses documented default and normalizes /v1 suffixes", (t) => {
useEnv(t, { MINIMAX_BASE_URL: null });
assert.equal(buildMinimaxUrl(), "https://api.minimaxi.com/v1/image_generation");
process.env.MINIMAX_BASE_URL = "https://api.minimax.io";
assert.equal(buildMinimaxUrl(), "https://api.minimax.io/v1/image_generation");
process.env.MINIMAX_BASE_URL = "https://proxy.example.com/custom/v1/";
@@ -44,7 +44,7 @@ function getApiKey(): string | null {
}
export function buildMinimaxUrl(): string {
const base = (process.env.MINIMAX_BASE_URL || "https://api.minimax.io").replace(/\/+$/g, "");
const base = (process.env.MINIMAX_BASE_URL || "https://api.minimaxi.com").replace(/\/+$/g, "");
return base.endsWith("/v1") ? `${base}/image_generation` : `${base}/v1/image_generation`;
}
@@ -197,7 +197,7 @@ export async function generateImage(
): Promise<Uint8Array> {
const apiKey = getApiKey();
if (!apiKey) {
throw new Error("MINIMAX_API_KEY is required. Get one from https://platform.minimax.io/");
throw new Error("MINIMAX_API_KEY is required. Get one from https://platform.minimaxi.com/");
}
const body = await buildRequestBody(prompt, model, args);
+59 -22
View File
@@ -1,7 +1,7 @@
---
name: baoyu-post-to-x
description: Posts content and articles to X (Twitter). Supports regular posts with images/videos and X Articles (long-form Markdown). In Codex, prefers the bundled Chrome Computer Use when available and falls back to real Chrome CDP scripts otherwise. Use when user asks to "post to X", "tweet", "publish to Twitter", or "share on X".
version: 1.57.0
description: Posts content and articles to X (Twitter). Supports regular posts with images/videos and X Articles (long-form Markdown). In Codex, honor explicit requests for the Codex Chrome plugin/@chrome by using the Chrome Extension workflow; otherwise use Chrome Computer Use when available and fall back to real Chrome CDP scripts only when allowed. Use when user asks to "post to X", "tweet", "publish to Twitter", or "share on X".
version: 1.57.1
metadata:
openclaw:
homepage: https://github.com/JimLiu/baoyu-skills#baoyu-post-to-x
@@ -13,9 +13,12 @@ metadata:
# Post to X (Twitter)
Posts text, images, videos, and long-form articles to X via real Chrome browser (bypasses anti-bot detection).
Posts text, images, videos, and long-form articles to X via a real Chrome browser.
In Codex, first try the bundled **Chrome Computer Use** path. Use the CDP scripts only when Chrome Computer Use is not available or the user explicitly asks for the script/CDP workflow.
In Codex, do not conflate these browser paths:
- **Codex Chrome plugin / `@chrome` / Chrome Extension**: use the bundled `chrome:Chrome` skill and its Node REPL browser client. This is required whenever the user says "Codex Chrome plugin", "Codex 自带的 Chrome 插件", `@chrome`, or similar.
- **Chrome Computer Use**: use `mcp__computer_use__.*` against the visible Google Chrome UI only when the user asks for Computer Use or no Chrome-plugin preference is stated and Computer Use is available.
- **CDP script mode**: use only as a fallback when the selected mode is unavailable or the user explicitly asks for CDP/script mode.
## Script Directory
@@ -41,15 +44,49 @@ In Codex, first try the bundled **Chrome Computer Use** path. Use the CDP script
## Execution Mode Selection (Required)
Before choosing a workflow, detect whether Codex Chrome Computer Use is enabled:
Choose exactly one mode before interacting with X:
1. If Computer Use tools are already visible, call `get_app_state` for app `Google Chrome`. In Codex these may be surfaced as `mcp__computer_use__.*`; use the exact available tool names.
2. If they are not visible and `tool_search` is available, search for `computer-use get_app_state click press_key drag scroll Google Chrome`, then call `get_app_state` for app `Google Chrome`.
3. If `get_app_state` succeeds, use **Chrome Computer Use Mode** below.
4. If Computer Use tools are unavailable or `get_app_state` fails, use **CDP Script Mode**.
5. If the user explicitly says to use Chrome Computer Use, do not fall back to CDP, Playwright, or the in-app Browser without telling the user and getting approval.
1. If the user explicitly asks for the Codex Chrome plugin, `@chrome`, the Chrome extension, or "Codex 自带的 Chrome 插件", use **Codex Chrome Plugin Mode**. Do not call Computer Use first.
2. If the user explicitly asks for Chrome Computer Use, use **Chrome Computer Use Mode**. Do not fall back to CDP, Playwright, the in-app Browser, or the Chrome plugin without telling the user and getting approval.
3. If the user explicitly asks for CDP/script mode, use **CDP Script Mode**.
4. Otherwise, prefer **Chrome Computer Use Mode** for regular posts, video posts, and quote tweets. For Markdown **X Articles with local content images**, prefer **CDP Script Mode** because placeholder selection and image-block verification are more reliable than manual DraftJS selection through Computer Use.
When Chrome Computer Use Mode is active, all X UI actions must go through the Codex Computer Use tools against the user's real Google Chrome. Shell commands are still allowed for preprocessing Markdown and copying local files to the system clipboard.
Never use the in-app Browser for X publishing workflows.
## Codex Chrome Plugin Mode
Use this mode whenever the user requests the Codex Chrome plugin, `@chrome`, or the Chrome Extension path. This uses the user's real Chrome profile and X login through the bundled Chrome plugin, not Computer Use and not CDP.
**Setup**
1. Load the `chrome:Chrome` skill before browser work.
2. Use `tool_search` for `node_repl js` if the Node REPL `js` tool is not already visible.
3. Initialize the Chrome browser client exactly as the Chrome skill specifies, then run a lightweight call such as `browser.user.openTabs()` to verify the extension connection.
4. If the first lightweight call fails, wait 2 seconds and retry once. If it still fails, follow the Chrome skill's extension checks and recovery steps. If checks pass but communication still fails, ask the user before opening a new Chrome window. Do not switch to Computer Use or CDP silently.
**General rules**
- Use the Chrome plugin's `browser.tabs.*`, `tab.playwright.*`, `tab.cua.*`, and file chooser APIs for X UI actions.
- Shell commands are allowed for Markdown preprocessing and clipboard preparation.
- If a file upload fails with `Not allowed`, tell the user: `To enable file upload, go to chrome://extensions in Chrome, click Details under the Codex extension, and enable "Allow access to file URLs." See https://developers.openai.com/codex/app/chrome-extension#upload-files for details.`
- If the Chrome plugin reports `native pipe is closed`, retry the lightweight browser call once after 2 seconds, then run the Chrome skill health checks. If Chrome is running, the extension is enabled, and the native host manifest is correct, ask permission to open a new Chrome window and retry. Do not keep sending browser actions through the broken pipe.
- Never click `Publish`, `Post`, or any externally visible submit action without explicit final confirmation from the user in the current conversation.
**X Articles**
1. Convert Markdown and keep the image map:
```bash
${BUN_X} {baseDir}/scripts/md-to-html.ts article.md --save-html /tmp/x-article-body.html > /tmp/x-article.json
```
2. Read the JSON output for `title`, `coverImage`, and `contentImages` (`placeholder` → `localPath`).
3. Open or create the article draft at `https://x.com/compose/articles`.
4. Upload the cover with the Chrome plugin file chooser flow. If upload is blocked by extension permissions, stop and report the exact permission fix above.
5. Fill the title, then copy rich HTML:
```bash
${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts html --file /tmp/x-article-body.html
```
6. Paste into the article body with a real paste keystroke through the Chrome plugin. On macOS use `Meta+V`.
7. Verify the editor text contains the article body and `XIMGPH_` placeholders. Do not rely on `tab.clipboard.readText()` as proof of the system clipboard after shell clipboard writes; on macOS verify with `pbpaste` if needed.
8. For each `contentImages` item in placeholder order, copy the image with `copy-to-clipboard.ts image <localPath>`, select the exact placeholder text, paste with the Chrome plugin, and verify the placeholder is gone and an image block appeared.
9. Open Preview and verify title, cover, body, links, and images.
10. Ask for explicit confirmation before clicking `Publish`.
## Preferences (EXTEND.md)
@@ -100,14 +137,14 @@ Checks: Chrome, profile isolation, Bun, Accessibility, clipboard, paste keystrok
---
## Chrome Computer Use Mode (Codex Preferred)
## Chrome Computer Use Mode
Use this mode whenever Codex can control `Google Chrome` with Computer Use. This uses the user's existing Chrome window, cookies, login, extensions, and X session.
Use this mode when the user explicitly asks for Chrome Computer Use, or when no Chrome-plugin preference is stated and Codex can control `Google Chrome` with Computer Use. This uses the user's existing Chrome window, cookies, login, extensions, and X session.
**General rules**:
- Start each assistant turn that controls Chrome by calling `get_app_state` for `Google Chrome`.
- Prefer element-index actions when available; use coordinates only for editor text selection or drag selection.
- Do not use the in-app Browser, Playwright, or CDP for X UI actions in this mode.
- Do not use the in-app Browser, the Chrome plugin, Playwright, or CDP for X UI actions in this mode unless the user approves a mode change.
- Never click `Publish`, `Post`, or any externally visible submit action without an explicit final confirmation from the user in the current conversation.
**Regular posts**:
@@ -155,15 +192,15 @@ Use this mode whenever Codex can control `Google Chrome` with Computer Use. This
8. Open Preview and verify title, cover, body, links, and images.
9. Ask for explicit confirmation before clicking `Publish`.
If Computer Use selection or paste becomes unreliable, stop and report the blocker instead of switching to CDP silently.
If Computer Use selection or paste becomes unreliable, stop and report the blocker instead of switching to the Chrome plugin or CDP silently.
---
## CDP Script Mode (Fallback)
Use the script sections below only when Chrome Computer Use is unavailable, unreliable, or explicitly not requested. These scripts launch or reuse a real Chrome instance via CDP and keep the browser open for review.
Use the script sections below only when the selected browser-control mode is unavailable, unreliable, or explicitly not requested. These scripts launch or reuse a real Chrome instance via CDP and keep the browser open for review.
Do not use CDP Script Mode when the user explicitly requires Chrome Computer Use unless the user approves the fallback after you explain the blocker.
Do not use CDP Script Mode when the user explicitly requires the Codex Chrome plugin or Chrome Computer Use unless the user approves the fallback after you explain the blocker.
---
@@ -188,7 +225,7 @@ ${BUN_X} {baseDir}/scripts/x-browser.ts "Hello!" --image ./photo.png
**Note**: Script opens browser with content filled in. User reviews and publishes manually.
**Chrome Computer Use preferred**: In Codex, if Chrome Computer Use is enabled, use the visible Chrome UI workflow in **Chrome Computer Use Mode** instead of running `x-browser.ts`.
**Codex mode note**: If the user explicitly requested the Codex Chrome plugin, use **Codex Chrome Plugin Mode**. Otherwise, if Chrome Computer Use is enabled, use **Chrome Computer Use Mode** instead of running `x-browser.ts`.
---
@@ -209,7 +246,7 @@ ${BUN_X} {baseDir}/scripts/x-video.ts "Check this out!" --video ./clip.mp4
**Note**: Script opens browser with content filled in. User reviews and publishes manually.
**Chrome Computer Use preferred**: In Codex, if Chrome Computer Use is enabled, use the visible Chrome UI workflow in **Chrome Computer Use Mode** instead of running `x-video.ts`.
**Codex mode note**: If the user explicitly requested the Codex Chrome plugin, use **Codex Chrome Plugin Mode**. Otherwise, if Chrome Computer Use is enabled, use **Chrome Computer Use Mode** instead of running `x-video.ts`.
**Limits**: Regular 140s max, Premium 60min. Processing: 30-60s.
@@ -232,7 +269,7 @@ ${BUN_X} {baseDir}/scripts/x-quote.ts https://x.com/user/status/123 "Great insig
**Note**: Script opens browser with content filled in. User reviews and publishes manually.
**Chrome Computer Use preferred**: In Codex, if Chrome Computer Use is enabled, use the visible Chrome UI workflow in **Chrome Computer Use Mode** instead of running `x-quote.ts`.
**Codex mode note**: If the user explicitly requested the Codex Chrome plugin, use **Codex Chrome Plugin Mode**. Otherwise, if Chrome Computer Use is enabled, use **Chrome Computer Use Mode** instead of running `x-quote.ts`.
---
@@ -254,7 +291,7 @@ ${BUN_X} {baseDir}/scripts/x-article.ts article.md --cover ./cover.jpg
**Frontmatter**: `title`, `cover_image` supported in YAML front matter.
**Chrome Computer Use preferred**: In Codex, if Chrome Computer Use is enabled, follow **Chrome Computer Use Mode** above instead of running `x-article.ts`.
**Codex mode note**: If the user explicitly requested the Codex Chrome plugin, follow **Codex Chrome Plugin Mode** above. If the user explicitly requested Chrome Computer Use, follow **Chrome Computer Use Mode**. Otherwise, for Markdown articles with local content images, use `x-article.ts` in **CDP Script Mode** so placeholders can be selected programmatically and the post-composition check can verify image count and remaining `XIMGPH_` text.
**CDP fallback note**: The script opens browser with article filled in. User reviews and publishes manually unless `--submit` is used.
@@ -283,7 +320,7 @@ pkill -f "Chrome.*remote-debugging-port" 2>/dev/null; pkill -f "Chromium.*remote
## Notes
- First run: manual login required (session persists)
- In Chrome Computer Use Mode, use the user's existing Chrome session and do not launch a separate CDP profile
- In Codex Chrome Plugin Mode and Chrome Computer Use Mode, use the user's existing Chrome session and do not launch a separate CDP profile
- CDP scripts only fill content into the browser by default; user must review and publish manually unless `--submit` is explicitly used
- Cross-platform: macOS, Linux, Windows
+69 -13
View File
@@ -4,14 +4,14 @@ Publish Markdown articles to X Articles editor with rich text formatting and ima
## Mode Selection
In Codex, prefer **Chrome Computer Use** when available:
In Codex, choose the browser-control mode from the user's wording:
1. If Computer Use tools are already visible, call `get_app_state` for `Google Chrome`.
2. If not, use `tool_search` for `computer-use get_app_state click press_key drag scroll Google Chrome`, then call `get_app_state`.
3. If that succeeds, use the Computer Use workflow below.
4. Use the CDP script workflow only when Computer Use is unavailable or explicitly requested.
1. If the user says "Codex Chrome plugin", "Codex 自带的 Chrome 插件", `@chrome`, or Chrome Extension, use **Codex Chrome Plugin Workflow**. Do not try Computer Use first.
2. If the user explicitly asks for Chrome Computer Use, use **Computer Use Workflow**.
3. If the user explicitly asks for CDP/script mode, use **CDP Script Workflow**.
4. Otherwise, use Computer Use when available; if unavailable or blocked, use CDP Script Workflow.
If the user explicitly asks for Chrome Computer Use, do not fall back to CDP, Playwright, or the in-app Browser without approval.
Never use the in-app Browser for X Article publishing. Never switch away from an explicitly requested mode without explaining the blocker and getting approval.
## Prerequisites
@@ -21,7 +21,25 @@ If the user explicitly asks for Chrome Computer Use, do not fall back to CDP, Pl
## Usage
### Chrome Computer Use (Codex Preferred)
### Codex Chrome Plugin (When Requested)
Use the `chrome:Chrome` skill and its Node REPL browser client. Verify the connection with a lightweight call such as `browser.user.openTabs()`. If it fails, wait 2 seconds and retry once, then follow the Chrome skill's health checks.
Prepare the article HTML and image map:
```bash
${BUN_X} {baseDir}/scripts/md-to-html.ts article.md --save-html /tmp/x-article-body.html > /tmp/x-article.json
```
Copy generated HTML as rich text:
```bash
${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts html --file /tmp/x-article-body.html
```
Use the Chrome plugin's tab, Playwright-wrapper, CUA, clipboard, and file chooser APIs for all X UI operations. If upload fails with `Not allowed`, stop and tell the user to enable file URL access for the Codex Chrome Extension in `chrome://extensions` → Details.
### Chrome Computer Use
Prepare the article HTML and image map:
@@ -149,7 +167,37 @@ JSON output:
| `1. item` | `<ol><li>` |
| `![](img)` | Image placeholder |
## Computer Use Workflow (Preferred in Codex)
## Codex Chrome Plugin Workflow
1. **Load Chrome skill**: use `chrome:Chrome`, not Computer Use.
2. **Connect**: initialize the Chrome plugin browser client and verify with `browser.user.openTabs()`.
3. **Parse Markdown**: run `md-to-html.ts --save-html /tmp/x-article-body.html > /tmp/x-article.json`.
4. **Read the map**: use `/tmp/x-article.json` for `title`, `coverImage`, and `contentImages`.
5. **Open X Articles**: open or claim a Chrome tab for `https://x.com/compose/articles`.
6. **Create Draft**: click the create/write button if needed, or open the target draft.
7. **Upload Cover**: use the Chrome plugin file chooser flow. If file upload returns `Not allowed`, report the Chrome Extension file-access fix and stop.
8. **Fill Title**: fill the title field.
9. **Paste Content**:
- Run `copy-to-clipboard.ts html --file /tmp/x-article-body.html`.
- Click the article body.
- Press `Meta+V` on macOS or `Control+V` on Windows/Linux through the Chrome plugin.
- Verify the article body appeared and contains `XIMGPH_` placeholders. On macOS, use `pbpaste` to verify shell-written system clipboard contents if paste is suspicious; `tab.clipboard.readText()` may not reflect the system clipboard after shell writes.
10. **Insert Images**: for each `contentImages` item in placeholder order:
- Run `copy-to-clipboard.ts image <localPath>`.
- Select the exact placeholder text (`XIMGPH_N`) in the editor.
- Press `Meta+V`/`Control+V` with the Chrome plugin.
- Wait for X to finish uploading media.
- If `XIMGPH_N` remains above the inserted image, reselect that exact text and press `BackSpace`.
- Do not press `BackSpace` unless the selected text is exactly the placeholder.
11. **Verify**:
- Inspect the editor for `XIMGPH_` residue.
- Confirm the expected number of image blocks is visible.
- Open Preview and verify title, cover, body, links, and images.
12. **Publish Safety**: ask the user for explicit final confirmation before clicking `Publish`.
If the Chrome plugin reports `native pipe is closed`, retry one lightweight browser call after 2 seconds, then run the Chrome skill health checks. If Chrome, the extension, and native host are healthy, ask the user before opening a new Chrome window and retrying.
## Computer Use Workflow
1. **Detect Computer Use**: call `get_app_state` for `Google Chrome`; use `tool_search` first if the tools are not visible.
2. **Parse Markdown**: run `md-to-html.ts --save-html /tmp/x-article-body.html > /tmp/x-article.json`.
@@ -167,7 +215,7 @@ JSON output:
- Select the exact placeholder text (`XIMGPH_N`) in the editor.
- Press `super+v`/`control+v` with Computer Use.
- Wait for X to finish uploading media.
- If `XIMGPH_N` remains above the inserted image, reselect that exact text and press `BackSpace`.
- If `XIMGPH_N` remains above the inserted image, reselect the exact text and press `BackSpace`.
- Do not press `BackSpace` unless the Computer Use state confirms the selected text is exactly the placeholder.
10. **Verify**:
- Inspect the Computer Use state for `XIMGPH_` residue.
@@ -202,10 +250,10 @@ JSON output:
User: /post-to-x article ./blog/my-post.md --cover ./thumbnail.png
Claude:
1. Detects Chrome Computer Use
1. Detects that the user requested the Codex Chrome plugin
2. Parses markdown: title="My Post", 3 content images
3. Saves `/tmp/x-article-body.html` and `/tmp/x-article.json`
4. Uses Chrome Computer Use to open X Articles and create a draft
4. Uses the Chrome plugin to open X Articles and create a draft
5. Uploads thumbnail.png as cover
6. Fills title "My Post"
7. Pastes HTML content with a real Chrome paste
@@ -219,6 +267,8 @@ Claude:
- **Cover upload fails**: Check file path and format (PNG, JPEG)
- **Images not inserting**: Verify placeholders exist in pasted content
- **Content not pasting**: Check HTML clipboard: `${BUN_X} {baseDir}/scripts/copy-to-clipboard.ts html --file /tmp/test.html`
- **Chrome plugin `native pipe is closed`**: retry once after 2 seconds, then run Chrome skill checks; ask before opening a new Chrome window if checks pass.
- **Chrome plugin upload `Not allowed`**: enable file URL access for the Codex Chrome Extension in `chrome://extensions` → Details.
- **Computer Use unavailable**: Use the CDP fallback script, unless the user explicitly required Chrome Computer Use.
- **Placeholder remains after paste**: Select only the placeholder text and press BackSpace after upload completes.
@@ -231,13 +281,19 @@ Claude:
- Downloads remote images locally
- Returns structured JSON
2. Chrome Computer Use publishes through the user's visible Chrome UI:
2. The Codex Chrome plugin publishes through the user's real Chrome session when explicitly requested:
- Uses the user's active Chrome profile and logged-in X session
- Uses the Chrome Extension browser client rather than Computer Use or CDP
- Uses `copy-to-clipboard.ts` for rich HTML and image clipboard payloads
- Keeps the final publish click under user confirmation
3. Chrome Computer Use publishes through the user's visible Chrome UI:
- Uses the user's active Chrome profile and logged-in X session
- Uses `copy-to-clipboard.ts` for rich HTML and image clipboard payloads
- Uses real keystrokes (`super+v`/`control+v`) through Codex Computer Use
- Keeps the final publish click under user confirmation
3. `x-article.ts` publishes via CDP as a fallback:
4. `x-article.ts` publishes via CDP as a fallback:
- Launches real Chrome (bypasses detection)
- Uses persistent profile (saved login)
- Navigates and fills editor via DOM manipulation
+35 -1
View File
@@ -164,7 +164,7 @@ export async function publishArticle(options: ArticleOptions): Promise<void> {
// Check if we're on the articles list page (has Write button)
console.log('[x-article] Looking for Write button...');
const writeButtonFound = await waitForElement('[data-testid="empty_state_button_text"]', 10_000);
const writeButtonFound = await waitForElement('[data-testid="empty_state_button_text"]', 30_000);
if (writeButtonFound) {
console.log('[x-article] Clicking Write button...');
@@ -172,6 +172,40 @@ export async function publishArticle(options: ArticleOptions): Promise<void> {
expression: `document.querySelector('[data-testid="empty_state_button_text"]')?.click()`,
}, { sessionId });
await sleep(2000);
} else {
console.log('[x-article] Write button not found, looking for create button...');
const createClicked = await cdp.send<{ result: { value: boolean } }>('Runtime.evaluate', {
expression: `(() => {
const selectors = [
'button[aria-label="create"]',
'button[aria-label="Create"]',
'button[aria-label*="create" i]',
'button[aria-label*="write" i]',
'a[href="/compose/articles"]'
];
for (const sel of selectors) {
const el = document.querySelector(sel);
if (el instanceof HTMLElement) {
el.click();
return true;
}
}
const buttons = [...document.querySelectorAll('button')];
const byText = buttons.find((el) => /^(create|write)$/i.test((el.textContent || '').trim()));
if (byText instanceof HTMLElement) {
byText.click();
return true;
}
return false;
})()`,
returnByValue: true,
}, { sessionId });
if (createClicked.result.value) {
console.log('[x-article] Create button clicked');
await sleep(2000);
} else {
console.log('[x-article] Create button not found');
}
}
// Wait for editor (title textarea)
@@ -218,6 +218,7 @@ async function launchChromeOnce(
try {
await waitForChromeDebugPort(port, 30_000, { includeLastError: true });
chrome.unref();
return { chrome, port };
} catch (error) {
killChrome(chrome);