pliceclogs-og/deepseek.js
type-two e5b8504bc6 Import pliceclogs as-is — the original Discogs seller extension
Snapshot of the working tree exactly as it stood, no edits. This is the predecessor
PRICEGOD was rewritten from ("kept intact, untouched" per pricegod/README.md), and it
is still the only place the DYMO scale is actually implemented —
rfid-daemon/index.js:1068-1265: HID discovery, parseScaleReport, one-shot read, and a
streaming /weight + /scale/start|stop session whose JSON shape PRICEGOD's daemon.js
already speaks.

Preserved verbatim on purpose (hence -og), including the known bug: DYMO_PIDS at
index.js:1082 is [0x8003, 0x8004], so it cannot see the bench M25 (0x8009). Fix that
in whatever daemon inherits the scale, not here.

node_modules stays ignored (22M of the 24M tree). No credentials in the import: the
two PEM markers in utils.js/sheets.js only strip headers off a key read from settings,
and mrpadmin / johnking are an SSH and a Postgres username, both key/trust auth.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 13:47:09 +10:00

347 lines
13 KiB
JavaScript

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
function normMarker(v) {
return String(v || '')
.trim()
.replace(/^"+|"+$/g, '')
.replace(/[\s_]+/g, '')
.toUpperCase();
}
function isVisible(el) {
if (!el) return false;
const style = window.getComputedStyle(el);
if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') return false;
const rect = el.getBoundingClientRect();
return rect.width > 0 && rect.height > 0;
}
async function waitForInputEl(timeoutMs) {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const named = document.querySelector('textarea[name="search"]');
if (isVisible(named)) return named;
const els = [
...document.querySelectorAll('textarea'),
...document.querySelectorAll('div[contenteditable="true"]'),
].filter(isVisible);
const best = els.find(e => (e.getAttribute('aria-label') || '').toLowerCase().includes('message'))
|| els.find(e => (e.getAttribute('aria-label') || '').toLowerCase().includes('prompt'))
|| els[0];
if (best) return best;
await sleep(250);
}
return null;
}
function setInputText(el, text) {
el.focus();
if (el.tagName === 'TEXTAREA' || el.tagName === 'INPUT') {
const proto = el.tagName === 'TEXTAREA'
? HTMLTextAreaElement.prototype
: HTMLInputElement.prototype;
const desc = Object.getOwnPropertyDescriptor(proto, 'value');
if (desc && desc.set) desc.set.call(el, text);
else el.value = text;
try {
el.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, cancelable: true, inputType: 'insertFromPaste', data: text }));
} catch {}
try {
el.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'insertFromPaste', data: text }));
} catch {
el.dispatchEvent(new Event('input', { bubbles: true }));
}
el.dispatchEvent(new Event('change', { bubbles: true }));
return;
}
const editable = el.getAttribute('contenteditable') === 'true';
if (editable) {
el.focus();
const sel = window.getSelection && window.getSelection();
if (sel) {
const range = document.createRange();
range.selectNodeContents(el);
sel.removeAllRanges();
sel.addRange(range);
}
try {
document.execCommand('insertText', false, text);
} catch {
el.textContent = text;
}
} else {
el.textContent = text;
}
try {
el.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'insertFromPaste', data: text }));
} catch {
el.dispatchEvent(new Event('input', { bubbles: true }));
}
}
function isAriaDisabled(el) {
const v = (el?.getAttribute('aria-disabled') || '').toLowerCase();
return v === 'true';
}
function findSendButtonNear(inputEl) {
const containers = [];
let p = inputEl;
for (let i = 0; i < 7 && p; i++) {
if (p.parentElement) containers.push(p.parentElement);
p = p.parentElement;
}
const selectors = [
'div._52c986b[role="button"]',
'div._52c986b',
'div.ds-icon-button[role="button"]',
'[role="button"].ds-icon-button',
'button[type="submit"]',
'button[aria-label*="Send"]',
'button[aria-label*="send"]',
];
for (const c of containers) {
for (const sel of selectors) {
const btns = [...c.querySelectorAll(sel)].filter(isVisible);
const usable = btns.filter(b => !isAriaDisabled(b));
if (usable.length) return usable[usable.length - 1];
}
}
const any = [
...document.querySelectorAll('div._52c986b[role="button"]'),
...document.querySelectorAll('div._52c986b'),
...document.querySelectorAll('div.ds-icon-button[role="button"]'),
].filter(isVisible);
const usable = any.filter(b => !isAriaDisabled(b));
return usable.length ? usable[usable.length - 1] : null;
}
function clickLikeAUser(el) {
if (!el) return;
const r = el.getBoundingClientRect ? el.getBoundingClientRect() : null;
const clientX = r ? Math.round(r.left + r.width / 2) : 1;
const clientY = r ? Math.round(r.top + r.height / 2) : 1;
try { el.dispatchEvent(new PointerEvent('pointerdown', { bubbles: true, clientX, clientY, pointerType: 'mouse', isPrimary: true })); } catch {}
try { el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, clientX, clientY })); } catch {}
try { el.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, clientX, clientY, pointerType: 'mouse', isPrimary: true })); } catch {}
try { el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, clientX, clientY })); } catch {}
try { el.dispatchEvent(new MouseEvent('click', { bubbles: true, clientX, clientY })); } catch {}
try { el.click(); } catch {}
}
function isGenerating() {
const stop = document.querySelector('[aria-label*="Stop"], [aria-label*="stop"]');
if (isVisible(stop)) return true;
const progress = document.querySelector('[role="progressbar"]');
if (isVisible(progress)) return true;
const spinner = document.querySelector('.ds-loading, .loading, [class*="loading"]');
if (isVisible(spinner)) return true;
return false;
}
function extractJsonObjects(text, maxObjects = 25) {
if (!text) return [];
const s = String(text);
const out = [];
let i = 0;
while (i < s.length && out.length < maxObjects) {
const start = s.indexOf('{', i);
if (start === -1) break;
let depth = 0;
let inStr = false;
let esc = false;
for (let j = start; j < s.length; j++) {
const ch = s[j];
if (inStr) {
if (esc) esc = false;
else if (ch === '\\\\') esc = true;
else if (ch === '"') inStr = false;
continue;
}
if (ch === '"') { inStr = true; continue; }
if (ch === '{') depth++;
else if (ch === '}') {
depth--;
if (depth === 0) {
out.push(s.slice(start, j + 1));
i = j + 1;
break;
}
}
if (j === s.length - 1) i = s.length;
}
if (i === start) i = start + 1;
}
return out;
}
function chooseBestJsonFromText(text, marker) {
const markerNorm = normMarker(marker);
const candidates = extractJsonObjects(text, 25);
const expectedKeys = [
'summary',
'store_description',
'features',
'sound_profile',
'highlight_tracks',
'production',
'market_insight',
'sales_angles',
'embedding',
];
let best = null;
let bestScore = -Infinity;
for (const cand of candidates) {
let parsed;
try { parsed = JSON.parse(cand); } catch { continue; }
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) continue;
let score = 0;
const eomNorm = normMarker(parsed.eom);
if (markerNorm && eomNorm === markerNorm) score += 200;
for (const k of expectedKeys) if (Object.prototype.hasOwnProperty.call(parsed, k)) score += 10;
if (parsed.summary === 'Short factual description (1-2 sentences)') score -= 50;
if (parsed.store_description === 'Punchy, slightly opinionated record store blurb (max 80 words, human tone, not generic AI)') score -= 50;
if (cand.length > 2000) score += 5;
if (score > bestScore) { bestScore = score; best = parsed; }
}
return best;
}
function extractLastResponseText() {
const selectors = [
'main',
'[role="main"]',
'[class*="chat"]',
];
const parts = [];
for (const sel of selectors) {
const el = document.querySelector(sel);
if (!el || !isVisible(el)) continue;
const t = (el.innerText || '').trim();
if (t.length > 50) parts.push(t);
}
if (!parts.length) return (document.body?.innerText || '').trim() || null;
parts.sort((a, b) => b.length - a.length);
return parts[0];
}
async function waitForJsonAndText(marker, timeoutMs) {
const start = Date.now();
const want = normMarker(marker);
while (Date.now() - start < timeoutMs) {
const txt = extractLastResponseText() || '';
if (txt) {
const parsed = chooseBestJsonFromText(txt, marker);
if (parsed) {
const got = normMarker(parsed?.eom);
if (!want || got === want) return { text: txt, json: parsed };
return { text: txt, json: parsed };
}
if (!isGenerating() && txt.trimStart().startsWith('{')) {
const fallback = chooseBestJsonFromText(txt, marker);
if (fallback) return { text: txt, json: fallback };
}
}
await sleep(500);
}
const finalTxt = extractLastResponseText();
return { text: finalTxt, json: chooseBestJsonFromText(finalTxt, marker) || null };
}
async function sendToBackground(message) {
return await new Promise(resolve => {
try {
chrome.runtime.sendMessage(message, (resp) => {
const err = chrome.runtime?.lastError?.message;
if (err) resolve({ ok: false, error: err });
else resolve(resp || { ok: false, error: 'No response' });
});
} catch (e) {
resolve({ ok: false, error: e.message || 'sendMessage failed' });
}
});
}
async function run() {
const job = await new Promise(resolve => chrome.storage.local.get(['geminiJob'], r => resolve(r.geminiJob || null)));
if (!job || !job.prompt) return;
const provider = job.provider || 'gemini';
if (provider !== 'deepseek') return;
if (job.status !== 'pending' && job.status !== 'running') return;
if (job.status === 'pending') {
await new Promise(resolve => chrome.storage.local.set({ geminiJob: { ...job, status: 'running', started_at: new Date().toISOString() } }, resolve));
}
const inputEl = await waitForInputEl(45000);
if (!inputEl) {
await new Promise(resolve => chrome.storage.local.set({ geminiJob: { ...job, status: 'error', error: 'DeepSeek input box not found.', failed_at: new Date().toISOString() } }, resolve));
return;
}
setInputText(inputEl, job.prompt);
await sleep(600);
const sendBtn = findSendButtonNear(inputEl);
if (!sendBtn) {
await new Promise(resolve => chrome.storage.local.set({ geminiJob: { ...job, status: 'error', error: 'DeepSeek send button not found.', failed_at: new Date().toISOString() } }, resolve));
return;
}
try { sendBtn.scrollIntoView({ block: 'center', inline: 'center' }); } catch {}
clickLikeAUser(sendBtn);
const marker = '__PLICE_EOM__';
const { text: responseText, json: outputJson } = await waitForJsonAndText(marker, 300000);
if (!responseText) {
await new Promise(resolve => chrome.storage.local.set({ geminiJob: { ...job, status: 'error', error: 'No DeepSeek response detected.', failed_at: new Date().toISOString() } }, resolve));
return;
}
const result = {
job_id: job.job_id,
model: job.model || 'deepseek-web',
provider: 'deepseek',
release_id: job.release_id || null,
discogs_url: job.discogs_url || null,
ai_url: location.href,
created_at: job.created_at,
prompt: job.prompt,
input_json: job.input_json || null,
output_text: responseText,
output_json: outputJson,
};
await new Promise(resolve => chrome.storage.local.set({ geminiJob: { ...job, status: 'posting', posting_at: new Date().toISOString() } }, resolve));
const postResp = await sendToBackground({ action: 'postGeminiResult', result });
if (!postResp || !postResp.ok) {
const err = postResp?.error || 'Post failed.';
await new Promise(resolve => chrome.storage.local.set({ geminiJob: { ...job, status: 'error', error: err, failed_at: new Date().toISOString() }, geminiLastExchange: result }, resolve));
return;
}
await new Promise(resolve => chrome.storage.local.set({ geminiJob: { ...job, status: 'done', done_at: new Date().toISOString() }, geminiLastExchange: result }, resolve));
const closeOnSuccess = await new Promise(resolve =>
chrome.storage.local.get(['geminiCloseTabOnSuccess'], r => resolve(r.geminiCloseTabOnSuccess !== false))
);
if (closeOnSuccess) {
await sendToBackground({ action: 'closeSenderTab' });
}
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
run();
} else {
window.addEventListener('DOMContentLoaded', () => run(), { once: true });
}