Install
openclaw skills install lynk-managerManage and update LYNK.id page content by editing text blocks, toggling block visibility, updating product links, and monitoring page state via browser or CLI.
openclaw skills install lynk-manager
— If this skill saves you time, tip: https://www.tip.md/oyi77
Manage LYNK.id pages via browser automation or via the opencli "lynk" CLI — edit text blocks, update product links, manage block visibility, and monitor page content. Use when updating LYNK page copy, flash sale text, CTAs, or any block content on lynk.id.
When the opencli lynk command family is available, prefer it over direct browser automation. The typical pattern:
# Update a specific text block from an HTML template
python skills/lynk-manager/scripts/lynk_update_copy.py parenting hero
python skills/lynk-manager/scripts/lynk_update_copy.py parenting social
python skills/lynk-manager/scripts/lynk_update_copy.py parenting urgency
This script:
parenting hero) → concrete LYNK product + block IDsskills/lynk-manager/templates/opencli lynk update-text \
--product-id <PRODUCT_ID> \
--block-id <BLOCK_ID> \
--file <TEMPLATE_PATH>
Use this flow when:
NOTE:
scripts/lynk_update_copy.pyships with placeholder block IDs. After the first manual run where you confirm the right TEXT blocks, updateMAPPINGin that script with the real block IDs.
openclaw52304E3ADEE500922EC9218675D69770https://lynk.id/admin/my-lynks/home⚠️ ALWAYS check existing tabs first before opening new ones. Old tabs persist, new tabs may die.
// Step 1: Verify tab is correct
browser tabs → look for lynk.id tab
// Step 2: Verify URL before trusting any action result
// If response URL is NOT lynk.id → action went to wrong tab
jendralbot → https://lynk.id/jendralbotLYNK pages are composed of blocks rendered as <li class="mb-2 wp"> elements.
Block types visible in admin:
TEXT — Free text/HTML content blockclass="mb-2 wp" (visible on public page)class="mb-2 block-inactive wp" (hidden from public)href links in the DOMhistory.pushState, not a page load// Step 1: Set up router intercept BEFORE clicking
window._navUrls = [];
const origPush = history.pushState.bind(history);
history.pushState = function(s,t,u) { window._navUrls.push(u); return origPush(s,t,u); };
// Step 2: Click the TEXT block row
const allPs = Array.from(document.querySelectorAll('p')).filter(p => p.textContent.trim() === 'TEXT');
const li = allPs[0].closest('li');
const cursor = li.querySelector('[class*=cursor]');
(cursor || li).click();
// Step 3: Read the navigation URL
window._navUrls[0]
// OR check document.location.href if router already navigated
https://lynk.id/admin/text/<full-block-id>
li.id attribute (NOT data-block-id) — li.id contains the full routing IDhttps://lynk.id/admin/text/6984b6cd9cc82e6d20f03b00-9027-7319838250-1770305229764Once you have the edit URL:
browser navigate targetId=52304E3ADEE500922EC9218675D69770 url=<edit-url>
// Run this on https://lynk.id/admin/my-lynks/home
() => {
const allLis = Array.from(document.querySelectorAll('li.mb-2'));
return allLis.map((li, i) => {
const p = li.querySelector('p');
const text = p ? p.textContent.trim().slice(0, 30) : '?';
const isHidden = li.classList.contains('block-inactive');
return i + ': [' + (isHidden ? 'HIDDEN' : 'ACTIVE') + '] ' + text + ' | li.id=' + li.id;
}).join('\n');
}
// Run on https://lynk.id/admin/my-lynks/home
() => {
window._navUrls = [];
const origPush = history.pushState.bind(history);
history.pushState = function(s,t,u) { window._navUrls.push(u); return origPush(s,t,u); };
const textPs = Array.from(document.querySelectorAll('p')).filter(p => p.textContent.trim() === 'TEXT');
// Click the Nth TEXT block (0-indexed):
const N = 0;
const li = textPs[N]?.closest('li');
if (li) {
const cursor = li.querySelector('[class*=cursor]');
(cursor || li).click();
}
return 'clicked, check _navUrls';
}
// Then:
() => window._navUrls.join('\n') || document.location.href
// Run on the edit page: https://lynk.id/admin/text/<block-id>
() => {
const editor = document.querySelector('.ql-editor, [contenteditable=true]');
if (!editor) return 'no editor found';
// Read current content
return editor.innerHTML;
}
// Update content:
() => {
const editor = document.querySelector('.ql-editor, [contenteditable=true]');
editor.focus();
document.execCommand('selectAll', false, null);
document.execCommand('delete', false, null);
editor.innerHTML = '<p><strong>🔥 New Content Here</strong></p><p>More text...</p>';
editor.dispatchEvent(new Event('input', { bubbles: true }));
return 'updated';
}
// Save (click Update button):
() => {
const updateBtn = Array.from(document.querySelectorAll('button'))
.find(b => b.textContent.trim() === 'Update');
if (updateBtn) { updateBtn.click(); return 'clicked Update'; }
return 'Update button not found';
}
// Close success modal:
() => {
const closeBtn = Array.from(document.querySelectorAll('button'))
.find(b => b.textContent.trim() === 'Close');
if (closeBtn) { closeBtn.click(); return 'closed'; }
return 'no close btn';
}
Navigate to admin home
browser navigate targetId=52304E3ADEE500922EC9218675D69770 url=https://lynk.id/admin/my-lynks/home
Set router intercept & click target TEXT block
// (use evaluate with targetId)
window._navUrls = [];
history.pushState = function(s,t,u) { window._navUrls.push(u); return origPush(s,t,u); };
// click target TEXT block li
Get edit URL
window._navUrls[0] || document.location.href
Navigate to edit URL
browser navigate targetId=... url=<edit-url>
Read & update editor content
Click Update button → verify success toast
Close modal → back to home
| Block | li.id (prefix) | Description | State |
|---|---|---|---|
| Header flash sale | 6984b6cd9cc82e6d20f03b00 | Flash sale intro + bullet points | Active |
| Jasa video | 699d637830b03b9fcfe07857 | Video production service text | Hidden |
| CTA footer | 69b34858d2c63432a6eb89cf | Closing CTA flash sale | Active |
Note: Full li.id includes timestamp suffix. Always intercept via click → navUrls to get exact current ID.
Use the "..." menu on any block:
() => {
const allLis = Array.from(document.querySelectorAll('li.mb-2'));
const target = allLis[N]; // N = block index
const menuBtn = target.querySelector('[class*=more], [class*=dots], button:last-child');
if (menuBtn) { menuBtn.click(); return 'opened menu'; }
return 'no menu btn found';
}
// Then click Hide/Show option in menu
// Open public LYNK page to verify changes
browser navigate targetId=... url=https://lynk.id/jendralbot
// Take screenshot to verify live content
act tool sometimes routes to wrong tab even with explicit targetIdresponse.url — if not lynk.id, wrong tab was usedevaluate with explicit targetId instead of act when possible?id=<id>&fk_page_id=home → WRONG (opens Add New, not Edit)/admin/text/<full-id> → CORRECT (opens Edit for existing block)history.pushState not captured_navUrls is empty after click, the router may have already navigateddocument.location.href instead — it shows the current URL after navigationquerySelectorAll('p') filtering for TEXT text will find all TEXT block labels[0], [1], [2] to target specific blockseditor.innerHTML firstli.id and data-block-id are DIFFERENT valuesli.id (the id attribute of the <li> element)data-block-id on inner elements may be an older/different ID<p><strong>🔥 FLASH SALE — Berakhir [WAKTU]!</strong></p>
<p>Semua tools AI dijual <strong>IDR 75.000</strong> saja (aslinya IDR 250K–499K).</p>
<p>⏰ Harga naik kembali normal [TANGGAL] — stok terbatas!</p>
<p>✅ Sudah dipakai 500+ seller Indonesia</p>
<p>✅ Langsung aktif, tidak perlu install</p>
<p>✅ Garansi uang kembali 7 hari</p>
<p>🚨 <strong>Jangan tunggu, harga naik [TANGGAL] jam [JAM]!</strong></p>
<p>👇 <strong>Pilih tools yang kamu butuhkan:</strong></p>
<p><strong>🔥 FLASH SALE — Berakhir [WAKTU]!</strong> Semua produk AI harga spesial <strong>IDR 75.000</strong>. Stok terbatas. <strong>Klik sekarang sebelum harga naik!</strong> ⏰</p>
Last updated: 2026-03-14 | Learned from live session editing jendralbot page