A portable Hermes skill + script package for exporting Youdao Cloud Notes into local files, metadata, ledgers, progress records, and audit reports.
This package focuses on Youdao Cloud Notes → local export artifacts. It does not write to gbrain or any downstream knowledge base by itself.
- Inventory Youdao Cloud Notes without reading note bodies or mutating cloud state.
- Read normal notes through the verified
youdaonoteCLI. - Upgrade legacy normal
.noteitems through the verified visible Playwright workflow. - Handle readable-empty notes correctly.
- Classify sensitive, attachment, media, and source-corrupt notes.
- Reconcile old/new fileIds after legacy note upgrade.
- Delete only verified old originals/duplicate copies to the Youdao recycle bin.
- Clean converted suffixes such as
X(1).notewhen safe. - Run in fixed groups with durable ledgers,
progress.json, stdout heartbeat, resumable execution, and final audit.
This skill is designed to be easy for an AI agent or operator to install and verify, but it does not automatically install dependencies.
The skill package provides scripts, templates, and checks. The calling agent/operator is responsible for explicitly installing and configuring runtime dependencies after user approval.
The runner/checker must not silently:
- install Python packages;
- install browser binaries;
- install CLIs;
- modify shell startup files;
- log in to Youdao automatically;
- print credentials, cookies, tokens, or sensitive note bodies.
Required runtime capabilities:
- Python 3.9+.
- Playwright installed in the selected Python interpreter.
- Playwright Chromium installed for that same interpreter.
- A configured
youdaonoteCLI. - Network access to Youdao Cloud Notes.
- A writable local output directory.
- For legacy
.noteupgrade: a visible persistent browser profile logged in to Youdao Cloud Notes.
Configuration precedence:
defaults < local config file < YOUDAO_EXPORT_* environment variables
Supported portable environment variables:
| Setting | Config key | Environment variable | Default |
|---|---|---|---|
| Python interpreter for runner/browser automation | python_bin |
YOUDAO_EXPORT_PYTHON_BIN |
python3 |
| Youdao CLI command/path | cli_path |
YOUDAO_EXPORT_CLI |
youdaonote |
| Local run output root | output_root |
YOUDAO_EXPORT_OUTPUT_ROOT |
~/.youdao-notes-export/runs |
| Persistent browser profile directory | browser_profile_dir |
YOUDAO_EXPORT_BROWSER_PROFILE_DIR |
~/.youdao-notes-export/browser-profile |
| Group size | batch_size |
YOUDAO_EXPORT_BATCH_SIZE |
10 |
| Inventory retry attempts | inventory_max_attempts |
YOUDAO_EXPORT_INVENTORY_MAX_ATTEMPTS |
3 |
| Inventory retry delay seconds | inventory_retry_delay_seconds |
YOUDAO_EXPORT_INVENTORY_RETRY_DELAY_SECONDS |
30 |
| Inventory heartbeat seconds | inventory_heartbeat_seconds |
YOUDAO_EXPORT_INVENTORY_HEARTBEAT_SECONDS |
600 |
Choose one Python interpreter and use it consistently as python_bin / YOUDAO_EXPORT_PYTHON_BIN.
macOS with Homebrew:
brew install python
python3 --versionUbuntu/Debian/WSL:
sudo apt-get update
sudo apt-get install -y python3 python3-pip python3-venv
python3 --versionIf you use a virtual environment:
python3 -m venv .venv
. .venv/bin/activate
python --versionThen set python_bin to that interpreter, for example python, .venv/bin/python, or an absolute path.
Use the same interpreter that will be configured as python_bin:
<python_bin> -m pip install playwright
<python_bin> -m playwright install chromiumExamples:
python3 -m pip install playwright
python3 -m playwright install chromiumor, inside a venv:
. .venv/bin/activate
python -m pip install playwright
python -m playwright install chromiumInstall the verified youdaonote CLI using its official/project-specific installation instructions. This README intentionally does not guess a package manager command because the CLI distribution source must be verified by the operator.
After installing it, make sure either:
youdaonoteis available onPATH; orcli_pathinexport_config.yamlpoints to the installed command; orYOUDAO_EXPORT_CLI=/absolute/path/to/youdaonoteis set for the current run.
Verify:
youdaonote checkIf the command is not on PATH, use the explicit path:
/path/to/youdaonote check
export YOUDAO_EXPORT_CLI=/path/to/youdaonoteDo not print CLI config files, auth tokens, cookies, or credentials in logs.
Create a local config from the template. Keep local configuration outside the skill package:
mkdir -p ~/.config/youdao-notes-export
cp templates/export_config.example.yaml ~/.config/youdao-notes-export/export_config.yaml
export YOUDAO_EXPORT_CONFIG="$HOME/.config/youdao-notes-export/export_config.yaml"Minimal example:
python_bin: python3
cli_path: youdaonote
output_root: ~/.youdao-notes-export/runs
browser_profile_dir: ~/.youdao-notes-export/browser-profile
batch_size: 10
allow_cloud_mutation: false
inventory_max_attempts: 3
inventory_retry_delay_seconds: 30
inventory_heartbeat_seconds: 600Equivalent temporary environment override example:
export YOUDAO_EXPORT_PYTHON_BIN=python3
export YOUDAO_EXPORT_CLI=/path/to/youdaonote
export YOUDAO_EXPORT_OUTPUT_ROOT="$HOME/.youdao-notes-export/runs"
export YOUDAO_EXPORT_BROWSER_PROFILE_DIR="$HOME/.youdao-notes-export/browser-profile"
export YOUDAO_EXPORT_BATCH_SIZE=10Prefer command-level temporary env/config for portability. Writing to shell startup files is a persistent local change and should require explicit user approval.
Run the portable checker first:
python3 scripts/check_env.py --config "$YOUDAO_EXPORT_CONFIG"The checker outputs JSON and only reports status/hints. It does not install anything.
Expected success shape:
{
"dependency_model": "check_only_no_auto_install",
"ok": true
}Optional remote CLI check:
python3 scripts/check_env.py --config "$YOUDAO_EXPORT_CONFIG" --remoteRun the runner health check:
python3 scripts/youdao_export_all.py health --config "$YOUDAO_EXPORT_CONFIG" --dry-run
python3 scripts/youdao_export_all.py health --config "$YOUDAO_EXPORT_CONFIG"--dry-run skips Playwright browser launch. Non-dry-run verifies that Playwright imports and Chromium launches in the configured python_bin interpreter.
Legacy .note upgrade uses a visible persistent Playwright profile. The user must log in once:
python3 scripts/youdao_export_all.py browser-login --config "$YOUDAO_EXPORT_CONFIG"A browser window opens. The user scans/finishes Youdao login. The profile is stored under browser_profile_dir and reused by later runs.
If a later run reaches a login page, the runner pauses the whole mutation phase as browser_login_required instead of failing every item.
Read-only discovery and planning:
python3 scripts/youdao_export_all.py inventory --config "$YOUDAO_EXPORT_CONFIG" --run-id <run_id>
python3 scripts/youdao_export_all.py plan-groups --config "$YOUDAO_EXPORT_CONFIG" --run-dir ~/.youdao-notes-export/runs/<run_id> --batch-size 10Run groups through the heartbeat consumer wrapper. AI agents should not run long run-groups jobs directly unless they implement an equivalent stdout/progress consumer and real user-visible delivery sink. Printing to stdout is only local observation and must not be counted as user delivery for chat/agent platforms.
Read-only mode first when possible:
python3 scripts/agent_heartbeat_consumer_example.py \
--sink-command '<host-agent-deliver-command>' \
--delivery-log ~/.youdao-notes-export/runs/<run_id>/agent_delivery_log.jsonl \
-- \
python3 scripts/youdao_export_all.py run-groups \
--config "$YOUDAO_EXPORT_CONFIG" \
--run-dir ~/.youdao-notes-export/runs/<run_id>Cloud mutation only after explicit user approval:
python3 scripts/agent_heartbeat_consumer_example.py \
--sink-command '<host-agent-deliver-command>' \
--delivery-log ~/.youdao-notes-export/runs/<run_id>/agent_delivery_log.jsonl \
-- \
python3 scripts/youdao_export_all.py run-groups \
--config "$YOUDAO_EXPORT_CONFIG" \
--run-dir ~/.youdao-notes-export/runs/<run_id> \
--allow-cloud-mutationOptional local progress inspection:
python3 scripts/youdao_progress_watch.py --run-dir ~/.youdao-notes-export/runs/<run_id>Release/user-visible sync is not satisfied by log heartbeat alone. The calling agent must prove that heartbeat summaries reached the user channel, or record a delivery failure and fallback path.
Cleanup and audit:
python3 scripts/youdao_export_all.py cleanup-renames --config "$YOUDAO_EXPORT_CONFIG" --run-dir ~/.youdao-notes-export/runs/<run_id> --allow-cloud-mutation
python3 scripts/youdao_export_all.py audit --config "$YOUDAO_EXPORT_CONFIG" --run-dir ~/.youdao-notes-export/runs/<run_id>Cloud mutation is disabled unless --allow-cloud-mutation is provided.
When an AI agent installs or uses this skill for a user, follow this sequence.
- Read
SKILL.md, thisREADME.md,templates/export_config.example.yaml, and relevant scripts before running commands. - Explain the dependency model to the user: the skill checks dependencies but does not auto-install them.
- Before any installation or persistent configuration change, ask for explicit approval and state:
- what will be installed/changed;
- why it is needed;
- scope of the change;
- risks/impact;
- rollback/removal path;
- verification command.
- Do not invent the
youdaonoteCLI installation source. If unknown, ask the user/operator for the official CLI source or an already installed path. - Do not print credentials, cookies, token files, CLI config contents, or note bodies.
- For long exports, run
run-groupsthroughscripts/agent_heartbeat_consumer_example.pyor an equivalent consumer implemented by the host agent. Recordrun_dir, process/session id, log path,progress.json, andagent_delivery_log.jsonl; relay compact heartbeat updates at the agreed cadence and verify user-visible delivery.
Use this as the default machine-executable checklist:
# 1. Create local config
mkdir -p ~/.config/youdao-notes-export
cp templates/export_config.example.yaml ~/.config/youdao-notes-export/export_config.yaml
export YOUDAO_EXPORT_CONFIG="$HOME/.config/youdao-notes-export/export_config.yaml"
# 2. Check current dependency state; no installation is performed
python3 scripts/check_env.py --config "$YOUDAO_EXPORT_CONFIG"
# 3. If Playwright is missing, ask user approval, then install explicitly
<python_bin> -m pip install playwright
<python_bin> -m playwright install chromium
# 4. If youdaonote is missing, do NOT guess installation.
# Ask for official install instructions or an installed command path, then configure one of:
# - cli_path in export_config.yaml
# - YOUDAO_EXPORT_CLI=/absolute/path/to/youdaonote
# 5. Verify after dependencies/config are ready
python3 scripts/check_env.py --config "$YOUDAO_EXPORT_CONFIG"
python3 scripts/youdao_export_all.py health --config "$YOUDAO_EXPORT_CONFIG" --dry-run
python3 scripts/youdao_export_all.py health --config "$YOUDAO_EXPORT_CONFIG"
# 6. Ask user to log in through visible browser profile if legacy upgrade is needed
python3 scripts/youdao_export_all.py browser-login --config "$YOUDAO_EXPORT_CONFIG"
# 7. Run read-only inventory and planning
python3 scripts/youdao_export_all.py inventory --config "$YOUDAO_EXPORT_CONFIG" --run-id <run_id>
python3 scripts/youdao_export_all.py plan-groups --config "$YOUDAO_EXPORT_CONFIG" --run-dir ~/.youdao-notes-export/runs/<run_id> --batch-size <N>
# 8. Run mutation only with explicit user approval, through the heartbeat consumer wrapper
python3 scripts/agent_heartbeat_consumer_example.py \
--sink-command '<host-agent-deliver-command>' \
--delivery-log ~/.youdao-notes-export/runs/<run_id>/agent_delivery_log.jsonl \
-- \
python3 scripts/youdao_export_all.py run-groups \
--config "$YOUDAO_EXPORT_CONFIG" \
--run-dir ~/.youdao-notes-export/runs/<run_id> \
--allow-cloud-mutation
# 9. Verify user-visible heartbeat delivery, not only runner logs
python3 - <<'PY'
from pathlib import Path
log = Path('~/.youdao-notes-export/runs/<run_id>/agent_delivery_log.jsonl').expanduser()
if not log.exists() or log.stat().st_size == 0:
raise SystemExit('missing agent-visible delivery log')
print(log.read_text(encoding='utf-8').splitlines()[-1])
PY
# 10. Final cleanup/audit
python3 scripts/youdao_export_all.py cleanup-renames --config "$YOUDAO_EXPORT_CONFIG" --run-dir ~/.youdao-notes-export/runs/<run_id> --allow-cloud-mutation
python3 scripts/youdao_export_all.py audit --config "$YOUDAO_EXPORT_CONFIG" --run-dir ~/.youdao-notes-export/runs/<run_id>The runner emits channel-neutral JSONL heartbeat lines such as:
{"type":"heartbeat","event":"item-complete","items_done":3,"items_total":11,"groups_done":1,"groups_total":4,"paused":false}A calling agent should forward compact summaries to the current user channel. The provided wrapper already prints group/phase summaries and writes agent_delivery_log.jsonl:
python3 scripts/agent_heartbeat_consumer_example.py \
--sink-command '<host-agent-deliver-command>' \
--delivery-log ~/.youdao-notes-export/runs/<run_id>/agent_delivery_log.jsonl \
-- \
python3 scripts/youdao_export_all.py run-groups \
--config "$YOUDAO_EXPORT_CONFIG" \
--run-dir ~/.youdao-notes-export/runs/<run_id> \
--allow-cloud-mutationExample user-visible summary:
Youdao export <run_id>: group-complete, phase=run-groups, items=3/21, groups=1/7, counts={'upgrade_success': 3}
Forward immediately on:
- login required;
- service outage;
- retry exhaustion;
- unsafe/unknown mutation state;
- process exit;
- final completion.
A release or third-party-agent validation is incomplete unless agent_delivery_log.jsonl contains delivered=true entries from a real host delivery sink for at least inventory/plan completion when applicable, every group-complete, run-groups-complete, and audit-complete; or contains explicit delivered=false failure records plus a documented fallback action. Default stdout output records delivered=false unless --stdout-is-delivery is explicitly used for a local CLI user who is directly watching the terminal.
Do not hold the active chat turn open with long blocking waits just to watch progress. Prefer background process + short polls/watcher/cron/gateway delivery.
The verified workflow is visible Playwright with a persistent profile. The user scans the Youdao QR login once, then the export runner reuses the profile.
For old-note upgrade, the runner first opens the standard note URL and verifies that the current page is the exact target old note. Only if target verification fails due to Youdao Web SPA redirect/jump does it retry with search/folder fallback. Upgrade is clicked only after target verification.
If converted copies such as X(1).note appear, they are not automatically treated as junk. The runner/audit must verify which copy is the readable converted note before rename/delete cleanup.
A run writes to ~/.youdao-notes-export/runs/<run_id>/ with inventory, immutable groups, result ledgers, failure ledgers, progress, journals, screenshots when applicable, and final audit.
Typical layout:
~/.youdao-notes-export/runs/<run_id>/
inventory.jsonl
inventory_attempts.jsonl
groups/group_001.json
group_results/group_001.results.jsonl
group_summaries/group_001.summary.json
failures/
actions/
progress.json
final_audit.json
Do not publish or share local run-output directories without reviewing them. They may contain fileIds, local paths, screenshots, metadata, and redacted-but-still-private operational context.
- Default mode is read-only.
- Any cloud mutation requires explicit
--allow-cloud-mutation. - Never clear the Youdao recycle bin.
- Delete/rename only after verification.
- Do not print credentials or sensitive note bodies.
- Pause on login loss, global service outage, retry exhaustion, or unknown post-mutation state.
- Continue on ordinary item-level failures by recording them in failure/manual-review ledgers.
- Treat local configs, browser profiles, ledgers, screenshots, and run outputs as private operational artifacts.
The supervisor can discover a host delivery sink without hard-coding any platform. Discovery order:
- CLI:
--sink-command <host-agent-deliver-command> - Environment variables:
YOUDAO_EXPORT_DELIVERY_COMMAND,AGENT_DELIVERY_COMMAND,HERMES_DELIVERY_COMMAND - Config file:
delivery_command: <host-agent-deliver-command>
Preflight before a notification-sensitive long run:
python3 scripts/agent_heartbeat_consumer_example.py \
--config "$YOUDAO_EXPORT_CONFIG" \
--check-deliveryFor tests that claim user-visible notification, fail closed when no sink is available:
python3 scripts/agent_heartbeat_consumer_example.py \
--config "$YOUDAO_EXPORT_CONFIG" \
--delivery-required \
--check-deliveryEquivalent config/env controls:
delivery_command: my-agent-deliver-current-user
delivery_required: trueexport AGENT_DELIVERY_COMMAND='my-agent-deliver-current-user'
export AGENT_DELIVERY_REQUIRED=trueHost command contract:
- stdin: one compact progress summary;
- exit
0: the host platform/agent accepted/sent the user-visible message; - non-zero: delivery failed;
- stderr/stdout: concise failure reason.
The public package only verifies this contract with fake/local sinks. Real platform delivery must be supplied and tested by the host agent.
Long runs require a host/calling-agent supervisor. This package is channel-neutral: it produces progress signals, but the host agent must actively observe and deliver them. Do not hard-code WeChat/Telegram/Slack/etc. into this skill.
Supported supervisor patterns:
-
Foreground child stdout consumer
The wrapper launches the runner and consumes JSONL heartbeat directly:
python3 scripts/agent_heartbeat_consumer_example.py \ --sink-command '<host-agent-deliver-command>' \ --delivery-log ~/.youdao-notes-export/runs/<run_id>/agent_delivery_log.jsonl \ -- \ python3 scripts/youdao_export_all.py run-groups \ --config "$YOUDAO_EXPORT_CONFIG" \ --run-dir ~/.youdao-notes-export/runs/<run_id> \ --allow-cloud-mutation
-
Background process + progress polling
If the host agent starts the runner in its own background-process system, stdout may not remain attached to the active chat turn. In that case, a supervisor must poll
progress.json:python3 scripts/agent_heartbeat_consumer_example.py \ --poll-progress ~/.youdao-notes-export/runs/<run_id>/progress.json \ --poll-interval 30 \ --sink-command '<host-agent-deliver-command>' \ --delivery-log ~/.youdao-notes-export/runs/<run_id>/agent_delivery_log.jsonl
-
External scheduler / watcher
A cron job, gateway worker, or platform-native agent watcher may read
progress.json, group result files, and audit output, then write equivalent delivery records. It must preserve the same semantics:delivered=trueonly for real host delivery; stdout/log observation isdelivered=false.
Delivery log requirements:
delivered=true: the host sink accepted/sent the user-visible message.delivered=false: stdout-only observation or host delivery failure.- delivery failures must include the error and fallback action.
This is the core release gate for agent-visible progress. Runner heartbeat and local logs are necessary, but not sufficient.
For local/CI validation, this package includes a channel-neutral host sink example:
python3 scripts/local_outbox_delivery_sink.py --outbox /tmp/youdao-delivery-outbox.jsonlUse it through discovery or --sink-command:
export AGENT_DELIVERY_COMMAND="python3 scripts/local_outbox_delivery_sink.py --outbox /tmp/youdao-delivery-outbox.jsonl"
python3 scripts/agent_heartbeat_consumer_example.py \
--check-deliveryThen run a heartbeat/supervisor command normally. Successful records will have delivered=true, and the outbox file will contain the accepted summaries. This proves the skill can discover and call a host sink end-to-end. It does not prove delivery to WeChat/Telegram/Slack/etc.; real platform delivery still belongs to the host agent's own sink command.
Do not publish this working directory directly if it contains local runtime artifacts.
A clean public package should include only source/docs/templates, for example:
SKILL.md
README.md
scripts/
templates/
references/
LICENSE
.gitignore
RELEASE_NOTES.md
Exclude:
runs/
**/__pycache__/
*.pyc
*.bak*
config.resolved.*
*.log
screenshots/
.browser-profile/