aboutsummaryrefslogtreecommitdiff
path: root/extensions/chrome-profile-bridge/browser-extension/service_worker.js
AgeCommit message (Collapse)Author
3 daysextension: snapshot sandboxed pages over CDP, never return empty textIgor Soarez
On a page served with CSP `sandbox` and no allow-scripts, such as raw.githubusercontent.com, chrome.scripting.executeScript runs nothing and resolves with a null result instead of throwing. The access-denied CDP fallback never ran, page.snapshot returned undefined, and chrome_snapshot answered with {type: "text"} and no text, because JSON.stringify(undefined) is undefined. Pi saved that block in the session, and every later turn failed in token estimation with "Cannot read properties of undefined (reading 'length')", whatever the provider. When scripting resolves without an envelope, snapshot and inspect now redo the injection and the call over CDP Runtime.evaluate, which such pages still allow, and raise an error if that also returns nothing. On the pi side, safeJson always returns a string, and an empty snapshot or inspect result is a tool error rather than a text block without text. sandboxed-snapshot.test.mjs drives page.snapshot and page.inspect through a page that runs no extension script. It fails on b8b63ea.
5 daystabs: back to one window per session; keep new tabs out of the user's windowIgor Soarez
A window shows only one tab, so sessions sharing one hid each other's tabs: no real input and blank screenshots for every session but the one whose tab was active (castle-pirc-web, 2026-11-08). Time-sharing the active tab would flip tabs under the user while they sign in for an agent, so revert 66fbd30: a session gets its own unfocused window. Kept: chrome_tab new for a session with no window opens one for it instead of landing in the user's focused window. The primer and chrome_tab description now say why to reuse a tab.
5 daysoverlay.clear: read closed shadow roots and remove the host elementIgor Soarez
Diagnostics on 1Password's inline prompt: the page holds a <com-1password-uso> element and no iframe in its own DOM; the frame is inside a closed shadow root. Run in the isolated world, where chrome.dom.openOrClosedShadowRoot can read it, and remove the element that hosts a foreign extension frame. Drop the body-click events: they were never shown to help.
5 daysoverlay.clear: match the prompt's frame by the tabId in its URL, and report ↵Igor Soarez
what it saw The password manager's frame target can come without a tabId, so it was missed (error said 'unknown', remaining targets empty). Match by the ?tabId= in the frame URL too, and when the tab stays blocked report every foreign target and what the injected script saw, so the next step is based on data.
5 daysoverlay.clear: run in the page's own world with the click the fallback usesIgor Soarez
The isolated-world body events did not free a 1Password-blocked tab; the DOM click fallback (MAIN world, full pointer event shape, el.click()) did. Use that shape, aimed at the page root so no control is clicked.
5 daysoverlay.clear: click the page body outside the prompt, and wait for it to goIgor Soarez
Measured on 1Password's inline menu: blur and Escape did not free the tab, a click outside did. overlay.clear now also sends pointer/mouse events to the body and polls the debugger for about a second instead of checking once.
5 daysoverlay: the agent decides whether a password-manager prompt is in the wayIgor Soarez
The error, the tool description and the primer no longer tell the agent to ask the user first. It clears the prompt when it is only in the way, and leaves it alone and says so when it is waiting for the user to sign in or approve.
5 daysoverlay: tell the agent about a blocking password-manager prompt; ↵Igor Soarez
chrome_clear_overlay closes it on request Another extension's frame (1Password's inline prompt) makes Chrome refuse the debugger on the tab. The old path sent Escape on its own, which cannot reach that frame and would close a prompt the user may be using. Now nothing is closed automatically: the error says what is blocking and tells the agent to ask the user; chrome_clear_overlay removes the frame, blurs the field, sends Escape and reports whether the tab is usable again.
5 daysnavigate: do not require the debuggerIgor Soarez
Attaching first (for the Leave-site prompt) made chrome_navigate fail on a tab whose page holds another extension's frame, such as a 1Password inline prompt, which is exactly when navigating away is the way out. Attach is now best effort.
5 daystabs: agent sessions share one window instead of opening one eachIgor Soarez
A session's first tab used chrome.windows.create, so every new session name (and every Claude Code process) got its own window. It now goes into the window that already holds any 'Pi Session:' group; a window is created only when none exists. chrome_tab new for a session with no group also uses that window, not the user's focused one. The primer and the chrome_tab description say so.
5 daysnavigate: answer a page's Leave-site prompt instead of hangingIgor Soarez
A page with a beforeunload handler shows 'Leave site?' when the extension navigates away. Nothing answered it, so chrome_navigate waited out its timeout and the agent never learned why. The worker now hears Page.javascriptDialogOpening and dismisses the dialog (stay/cancel); page.navigate fails at once and says so. leaveIfAsked: true, for that call only, accepts a beforeunload prompt. Tool description and primer tell agents when to use it and to ask the user when unsure.
2026-09-28Drop the full-screen tab rule for automation targets (#27)Tianren Dong
0.15.54 opened Pi's automation target as a tab in the user's window while it was full screen, assuming macOS would merge a new window into it as a hidden window tab. Chrome disables automatic window tabbing (app_controller_mac.mm), so that never happens; the hidden tabs came from the tab-group bug fixed in 0.15.56. Always prefer Pi's own window.
2026-09-28Report hidden Pi tab in /chrome doctor (#26)Tianren Dong
Chrome ignores clicks and typing in hidden pages. Doctor now asks the worker whether this session's own tab is visible (automation.status includeVisibility, which never creates or activates a tab) and reports the reason and fix when it is hidden.
2026-09-28Keep tab ownership across extension reloads (#25)Tianren Dong
storage.session is wiped when the extension reloads, which it does on every pi-chrome update, so Pi lost track of its automation window and session tabs and left them behind. Mirror ownership to storage.local, restore it only when the session copy is missing, re-check every restored tab (same window; adopted tabs still in Pi's group), and drop the local copy on browser startup.
2026-09-28Create session tab groups in the tab's own windowTianren Dong
chrome.tabs.group without createProperties.windowId creates the group in the current (user's) window and moves the tab there, so a session's automation tab left its dedicated window and became a hidden inactive tab. Make the unit mock match Chrome's behavior.
2026-09-28Fill whole textareas; cover new tabs' first page with early captureTianren Dong
chrome_fill's triple-click selected one line of a multi-line textarea, so fill replaced only that line. Select value fields with the selectAll editing command before deleting. Early capture now waits for in-flight tab.new creations, so a first page that commits before the tab is tracked is still captured.
2026-09-26Stop console capture from tripping DevTools detection; scope early capture ↵Tianren Dong
to Pi tabs Fixes #9. Sites like BOSS Zhipin log an object with a getter/toString trap and close the tab if it is read. Store captured console args raw and serialize them on read without running page code (data properties only, accessors as [getter], built-ins via native getters). Inject document-start capture only into Pi's automation tab and session-tracked tabs.
2026-09-26Report hidden-tab input, append targeted typing, smarter uploads, prune ↵Tianren Dong
screenshots; release 0.15.54 - Check page visibility before trusted input: Chrome drops input to hidden pages (inactive tabs, covered/minimized windows, macOS window tabs behind full screen). Type/key/hover/scroll/tap/drag fail fast with the reason; click/fill use their DOM fallback immediately (or reject with domFallback:false). - Use an inactive tab instead of a new window for the automation target while the user's window is full screen. - chrome_type with uid/selector moves the caret to the end (moveToEndOfDocument) after its focus click. - chrome_upload_file: labels/wrappers resolve to their file input, same-origin iframe search, and intercepted file chooser for upload buttons (trusted click when visible, user-gesture click when hidden); reject multiple paths for single-file inputs; no duplicate change event. - Prune default-folder chrome_screenshot captures (7 days, newest 20 kept, retentionDays override). - input.debug without a target no longer calls tabs.get(-1). - New hidden-input unit suite.
2026-09-26Absorb fork fixes and features; release 0.15.53Tianren Dong
Adapted from community forks (ardhiqii, kkunkunya, nihar-oracle, steimerbyte): - Abort stalled /next long polls after 45s; time out, retry, and single-post results. - Fall back to CDP Runtime.evaluate when chrome.scripting is refused (about:blank automation tabs). - Load pi-chrome in same-process subagent sessions as a bridge client; client chrome_launch reports owner status. - Add chrome_cdp (validated raw CDP passthrough, background-guarded, payload summarization) and chrome_cdp_targets. - chrome_type reports before/after values, insertedAt, splice/unchanged warnings; add replace=true. - includeSnapshot waits for navigations started by the action; tab.new reports the settled tab. - CDP timeouts surface as timeouts and are not re-sent after the cleanup detach. - Activate/deactivate chrome_find and chrome_inspect with the rest of the chrome_* tools. - New unit suites: bridge-resilience, cdp-passthrough, type-evidence.
2026-09-10Improve Chrome input reliability and release 0.15.49Tianren Dong
2026-09-09Enforce hard background and release 0.15.48Tianren Dong
2026-09-09Fix session cleanup and release 0.15.47Tianren Dong
2026-06-220.15.40: keep automation targets in session groupTianren Dong
2026-06-22Keep Pi-created tabs in one session groupTianren Dong
2026-06-220.15.39: dedicated pi-chrome automation tab/window isolation (#6)TJ Dong
Stop pi-chrome from hijacking the user's active tab. Page/navigation actions with no explicit target now route to a dedicated automation window owned by the calling Pi session (falling back to a dedicated tab if a window can't be made), instead of overwriting whatever the user has open. - Session-scoped ownership: Map<sessionKey, target> in the service worker, keyed by a sessionKey forwarded from index.ts, so concurrent sessions (one shared extension) never fight over a window or close each other's tabs. - Persistence: ownership mirrored to chrome.storage.session so a service-worker restart re-hydrates and reuses the window instead of orphaning it. Cleared on full browser restart; restored windows are then left alone (never reclaimed). - getTabByParams default branch resolves the owned target; tab.activate/close/ group/ungroup pass createOwnedTarget:false so management never auto-creates a throwaway tab or falls back to the user's active tab. - Session-group integration: the dedicated-window tab still joins this session's tab group, in its own window; grouping stays best-effort. - Lifecycle: cleanup on /chrome revoke and real session end (not /reload); fire-and-forget so it never blocks /quit, /reload, or shutdown. - Tests: new test-suite/unit/automation-target.test.mjs (isolation, reuse, concurrency, restart/reconnect re-hydration, guarded tab.*, group integration, window->tab fallback, robust cleanup). Docs + CHANGELOG updated.
2026-06-070.15.38: harden chrome input overlay handlingTianren Dong
2026-06-070.15.37: harden Chrome input targetingTianren Dong
2026-06-060.15.36: richer chrome_snapshot + chrome_find/chrome_inspectTianren Dong
- chrome_snapshot returns a concise observation (layout, hints, actions, forms, page map, query matches, diff) with mode/query/maxTextChars params - new chrome_find (query -> ranked uids) and chrome_inspect (deep element context, with page.inspect fallback) - includeSnapshot on click/type/fill/key now embeds the formatted snapshot - snapshot/inspect logic shipped as eval-free packaged snapshot_injected.js, sharing window.__PI_CHROME_STATE__ (el- uid scheme) with existing input helpers
2026-06-010.15.34: tabs Pi uses join session group; fix punctuation dropped by ↵Tianren Dong
chrome_type/chrome_key (US-layout keymap)
2026-05-310.15.30: chrome_tab grouping — auto-group Pi tabs, group/ungroup actions, ↵Tianren Dong
merge by title
2026-05-310.15.29: CSP bypass via CDP Runtime.evaluate for ↵Tianren Dong
evaluate/snapshot/waitFor/initScript - evaluateInTab/executeInTab now run through CDP Runtime.evaluate instead of new Function() in MAIN world, so chrome_evaluate/chrome_snapshot work on pages that block unsafe-eval (github.com, many bank/SaaS apps). Preserves value markers (undefined/function/symbol/bigint/Error), DOMRect expansion, and the expression/statement fallback; keeps {ok,value} error envelope. - page.waitFor polls via service-worker-side CDP evaluation. - chrome_navigate initScript uses Page.addScriptToEvaluateOnNewDocument. - Add npm test (test-suite/unit/csp-eval.test.mjs) and in-browser regression challenge 42 strict-csp-evaluate; update challenge 39 notes + docs/primer.
2026-05-310.15.28: bridge self-heal, timeout diagnostics, pressKeyInPage helper, ↵Tianren Dong
DOMRect serialization, stale-target errors, pageMutated wording
2026-05-16fix: capture early console/network messages at document_startDani Bednarski
Adds an early-capture script injected at document_start on every navigation via webNavigation.onCommitted + injectImmediately:true. Wraps console.*, fetch, and XMLHttpRequest before page JS runs, so console messages, errors, unhandled rejections, and network requests during page load are captured in window.__PI_CHROME_STATE__. Sets __piChromeWrapped flags so the existing post-hoc installPiChromeInstrumentation() is idempotent (checks instrumentationInstalled). Fixes chrome_list_console_messages returning empty for errors that fire during React render / initial API calls.
2026-05-16Revert 0.16.x and 0.17.x: restore tree to v0.15.20 stateTianren Dong
Reverts the pairing + signed-envelopes work (0.16.0-0.16.1), the standalone daemon refactor (0.17.0-0.17.3), and assorted patches. These were unpublished from npm; this commit restores the working tree to match the v0.15.20 release so the branch tip and the npm 'latest' dist-tag are consistent again. Tags v0.16.0 through v0.17.3 are preserved as recovery anchors; the work is reachable via 'git checkout v0.17.3' if ever needed. Tree-equivalent to v0.15.20.
2026-05-160.16.0: mandatory pairing + signed envelopes + cross-process state coherenceTianren Dong
Major security hardening of the Chrome bridge driven by red-team findings. - Mandatory pairing via /chrome onboard (one-time per machine). Bridge pins exact chrome-extension:// origin and derives HMAC keys via HKDF. Until paired, the bridge returns idle responses and refuses to deliver commands. - Signed envelopes on /next, /result, /command. HMAC-SHA256 over protocol version, direction, method, path, extension ID, bridge ID, timestamp, fresh nonce, and SHA-256 of the body. +/-30s clock window; 5-min LRU nonce replay defense on both request and response directions; constant-time MAC compare. - Peer Pi sessions sharing the bridge sign each /command with a broker HKDF key (no bearer token transmitted) and verify the owner's signed response. - Global loopback enforcement. PI_CHROME_BRIDGE_HOST=0.0.0.0 ignored unless PI_CHROME_BRIDGE_DANGEROUS_REMOTE=1; loopback check still applies per-endpoint. - Cross-process state coherence: fs.watch + per-request mtime check on ~/.config/pi/chrome-bridge.json. A peer Pi's unpair (or any external rm/edit) propagates to the owner's in-memory state before the next privileged verify; replay caches reset on key rotation. - Body cap 1 MiB -> 413. Queue/pending/waiter caps. Malformed JSON -> 400. Non-object JSON bodies -> 400 instead of being trusted. - page.navigate initScript: try/finally around tabs.update, per-call onCommitted listener with bounded commit-wait window, one-shot delete in global onCommitted. No more stale init scripts firing on later unrelated navigations. - crypto.randomUUID() command IDs. - Manifest diet (dropped activeTab); added popup.html/popup.js for the pair UI. UX: - /chrome onboard runs install + pair in one interactive flow. - /chrome help reorganized into First-time / Day-to-day / Maintenance. - Status bar lights up as green dot only when authorized. - Early console/network capture at document_start so boot-time React errors and initial API calls aren't dropped (ported from DaniBedz/pi-chrome fork). Docs: - SECURITY.md threat model rewritten with in-scope / out-of-scope sections. - README rewritten with the install-then-pair flow front and center. - CHANGELOG 0.16.0 entry with full patch list.
2026-05-14Audit pi-chrome docsTianren Dong
2026-05-14Restore terminal Chrome authorizationTianren Dong
2026-05-13pi-chrome 0.14.2: recover from foreign-extension input overlaysTianren Dong
When a different Chrome extension (password manager, autofill popup, etc.) anchors an overlay to the focused input, Chrome's CDP refuses our Input.dispatchMouseEvent with 'Cannot access a chrome-extension:// URL of different extension'. The CDP target list shows the foreign chrome-extension:// URL as type:'other' attached to the same tab. New behaviour in cdp() wrapper: 1. On that specific error for any Input.* call, snapshot CDP targets to identify the offending extension id and log it (foreign-ext- detected event added to ring buffer). 2. Send Esc via Input.dispatchKeyEvent (key dispatch routes by focus, not coordinates, so it bypasses the overlay) to dismiss the popup. 3. Retry the original Input.* call once. 4. If retry still fails, throw a clear error naming the foreign extension id and listing three concrete workarounds: disable that extension on the page, use Tab-to-focus instead of clicking, or run /chrome quiet off.
2026-05-13pi-chrome 0.14.1: harder attachDebugger retry + trusted.debug diagnosticTianren Dong
attachDebugger now: - Calls chrome.debugger.getTargets() first and force-detaches any stale CDP session this extension already owns on the target tab before attaching. Stale sessions (left over from SW reloads or sibling detach failures) often surface on the next attach as 'Cannot access a chrome-extension:// URL of different extension'. - Retries up to two more times (180ms + 500ms) instead of once. The 500ms back-off lets Chrome's content-script registration drain on fresh navigations. - Records every attach event (attached, stale-target-found, attach- failed, retry-failed) in a 20-entry ring buffer for inspection. New diagnostic action 'trusted.debug' returns extension version, trusted mode, attached tabs, the inspected tab's URL/status, current chrome.debugger.getTargets() output, and the recent attach-event log. Use to investigate when a specific URL refuses to attach.
2026-05-13pi-chrome 0.12.1: fix 07/13/21/27/28 anti-automation regressionsTianren Dong
- 07 pointer-properties: pass force: 0.5 on every CDP mousePressed so PointerEvent.pressure reports 0.5 for trusted clicks (was 0, inconsistent with real mouse behaviour). - 13 focus-order: after a CDP-driven click on a focusable target, re-focus with focusVisible: false so :focus-visible matches pointer focus semantics (CDP press leaves :focus-visible=true otherwise). - 21 keyboard-modifiers: chrome.debugger.attach occasionally rejects with 'Cannot access a chrome-extension:// URL of different extension' immediately after navigation. attachDebugger now waits 180ms, verifies the tab URL isn't privileged, and retries once. - 27 scroll-momentum + 28 intersection-visibility: replace gentle ~1.25%/step linear taper with a ramp-up (~15%) + geometric decay (~12% drop/step). Sizes n adaptively so peak event stays ≲22px so small visibility targets get low-ratio IO samples while a clear momentum tail remains for decay-pattern detectors. - trustedDrag mousePressed also gets force: 0.5 (consistency).
2026-05-13pi-chrome 0.11.3: plain-English copy in /chrome-trusted + /chrome-doctorTianren Dong
User-facing strings rewritten in layman's terms — no more 'CDP', 'isTrusted', 'user-activation gates', 'synthetic events', 'debugger API'. /chrome-trusted picker now explains each mode by what users actually experience: yellow banner, which sites work, when to use it. /chrome-doctor reports 'Click mode' instead of 'Trusted-input mode'. Tool descriptions (read by the agent) keep precise terminology.
2026-05-13pi-chrome 0.11.2: cap trustedScroll per-event delta so IO threshold gradient ↵Tianren Dong
lands
2026-05-13pi-chrome 0.11.1: chrome_tap (CDP touch), smoother trustedScroll, cdp ↵Tianren Dong
auto-recover - chrome_tap tool: real browser-trusted touchstart/touchend via Input.dispatchTouchEvent for sites that gate on TouchEvent (mobile- first PWAs, swipe carousels). Always uses CDP; banner appears. - trustedScroll: per-step delta capped at ~80px, n=6..60 steps with a bell-shaped weight curve, 22-52ms sleep between events so IntersectionObserver and rAF samples get gradient updates (fixes intersection-visibility / smooth-scroll tests). - cdp() wrapper auto-detects stale debugger sessions (tab nav, DevTools toggle, manual cancel) and re-attaches+retries once instead of throwing 'Debugger is not attached'. Fixes intermittent failures during automated test sweeps.
2026-05-13pi-chrome 0.11.0: smart-auto trusted mode default + rename to Pi Chrome ↵Tianren Dong
Connector Trusted-input mode default flips from 'off' to 'auto': - Synthetic events still run first for every chrome_* call. - If a click produced no page change AND the target's label looks like a user-activation-gated affordance (play/copy/share/sign-in/etc), or the page just emitted NotAllowedError / 'transient activation required', the call is silently retried via chrome.debugger (CDP). - Typing auto-retries when the input value didn't change at all (editor rejected synthetic input). - Result carries autoRetried=true + autoRetryReason + syntheticAttempt so callers / agents see the upgrade happened. - Yellow debugger banner now appears only for the retry, not every call. Companion extension renamed 'Pi Existing Chrome Profile Bridge' → 'Pi Chrome Connector' (manifest, action title, SW client name, README, slash-command notify strings). One-time chrome://extensions reload needed to pick up the new name; auto-reload mechanism handles the rest. /chrome-doctor explains the active mode and what smart-retry does. /chrome-trusted picker shows 'auto (current)' first; on-mode now warned as 'banner permanently visible'. chrome_click / chrome_type tool descriptions explain auto-retry.
2026-05-13pi-chrome 0.10.1: chrome_key modifiers param for trusted chords (Cmd+V etc)Tianren Dong
2026-05-13pi-chrome 0.10.0: trusted-input mode via chrome.debugger (CDP)Tianren Dong
Add opt-in CDP path so chrome_* tools can dispatch browser-trusted input (isTrusted=true, satisfies user-activation gates: clipboard, fullscreen, file picker, autoplay). Default remains synthetic events; no behavior change unless trusted mode is enabled. Companion extension: - new "debugger" permission in manifest (one-time re-approval prompt in chrome://extensions on update) - attach/detach lifecycle per tab with idle-detach (15s) and listener for onDetach (incl. canceled_by_user) - trustedClick / trustedHover / trustedType / trustedFill / trustedKey / trustedScroll / trustedDrag use Input.dispatchMouseEvent + Input.dispatchKeyEvent through chrome.debugger.sendCommand, reusing humanized timing, in-rect coord variance, pointer continuity, Shift chord for uppercase, per-key cadence - trusted.mode / trusted.status bridge actions Pi side: - /chrome-trusted slash command (on / off / auto / status) - trusted=true param on chrome_click, chrome_type, chrome_fill, chrome_key, chrome_hover, chrome_drag, chrome_scroll - /chrome-doctor probes chrome.debugger availability and reports current trusted mode + attached tabs - README documents trusted-input mode and tradeoffs
2026-05-13pi-chrome: humanize synthetic input + anti-automation test suiteTianren Dong
Bump 0.8.0 → 0.9.1. Companion extension (service_worker.js): - humanise pointer paths: easing Bezier-ish interpolation w/ wobble, 12–42 steps, 4–18 ms gaps; pointer position persisted across calls - click point picked inside element rect, not dead center - pointer events fill movementX/Y, screenX/Y, pressure (0.5 on down), pointerId, width/height, tilt, composed - click sequence: humanMoveTo → pointerdown → focus → 45–140 ms hold → pointerup → mouseup → click - per-character typing: keydown → keypress → beforeinput → value mutate (or execCommand for contenteditable) → input → keyup, with 25–95 ms hold + 35–140 ms inter-key; auto Shift chord for uppercase/symbols - pressKeyInPage emits same cadence + handles printable insert / Backspace - hoverPage: humanised path + 80–220 ms dwell - dragPage: full HTML5 sequence (dragstart/drag/dragenter/dragover/ dragleave/drop/dragend) with shared DataTransfer + pointer continuity - new scrollPage: wheel events with front-loaded momentum-shaped deltas - self-reload on version drift: SW reads x-pi-chrome-version header, reloads when older than pi-chrome Pi side (index.ts): - new chrome_scroll tool - PI_CHROME_VERSION read from package.json (and re-read on every /next so future bumps take effect without pi restart) - /chrome-doctor escalates version mismatch to top-line ✗ error and skips downstream capability probes when stale - /next response carries x-pi-chrome-version header for SW self-heal Test suite (test-suite/): - 28 challenge pages each catching a different synthetic-event tell: isTrusted, webdriver/fingerprint, mouse entropy/timing/coords, pointer pressure/continuity, per-char cadence + framework invariants, user activation, honeypots, focus order, wheel scroll, drag+ DataTransfer, contenteditable+Selection, paste/clipboard, native select, hover dwell, React valueTracker, keyboard modifiers, touch events, stack-trace fingerprint, viewport-edge clicks, mousemove rate, scroll momentum, IntersectionObserver visibility - shared _lib.js exposes window.__verdict + writes localStorage so index.html dashboard aggregates results - notes/bypass-ideas.md documents fixes still needed per challenge
2026-05-12Update pi-packs installer and package namesTianren Dong
2026-05-08Track pi-chrome package sourceTianren Dong