openzoo 0.50.65 → 0.50.66
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cursorbackend.js +27 -1
- package/lib/xclaims.js +18 -0
- package/package.json +1 -1
package/lib/cursorbackend.js
CHANGED
|
@@ -2320,6 +2320,18 @@ const LOCAL_TOOLS = [
|
|
|
2320
2320
|
parameters: { type: 'object', properties: {} },
|
|
2321
2321
|
},
|
|
2322
2322
|
},
|
|
2323
|
+
{
|
|
2324
|
+
type: 'function',
|
|
2325
|
+
function: {
|
|
2326
|
+
name: 'x_compose',
|
|
2327
|
+
description: 'Type reply text into the X composer on the current page of the given browser, LINE BY LINE with real Enter keys so newlines survive (fill flattens them). Click/focus the reply box first (or open the tweet). Then click the Reply button yourself. browser: chrome or brave.',
|
|
2328
|
+
parameters: {
|
|
2329
|
+
type: 'object',
|
|
2330
|
+
properties: { browser: { type: 'string', enum: ['chrome', 'brave'] }, text: { type: 'string' } },
|
|
2331
|
+
required: ['browser', 'text'],
|
|
2332
|
+
},
|
|
2333
|
+
},
|
|
2334
|
+
},
|
|
2323
2335
|
];
|
|
2324
2336
|
|
|
2325
2337
|
export const LOCAL_TOOL_NAMES = LOCAL_TOOLS.map((t) => t.function.name);
|
|
@@ -2525,6 +2537,19 @@ async function runLocalTool(name, args, log, ctx = {}) {
|
|
|
2525
2537
|
if (name === 'x_done') return JSON.stringify(xclaims.markDone({ tweet: args.tweet || args.id, by: me.id, name: me.name, url: args.url, lane: args.lane }));
|
|
2526
2538
|
if (name === 'x_release') return JSON.stringify(xclaims.releaseTweet({ tweet: args.tweet || args.id, by: me.id }));
|
|
2527
2539
|
if (name === 'x_claims') return JSON.stringify(xclaims.listClaims());
|
|
2540
|
+
if (name === 'x_compose') {
|
|
2541
|
+
const server = String(args.browser || 'chrome').toLowerCase() === 'brave' ? 'brave-devtools' : 'chrome-devtools';
|
|
2542
|
+
const plan = xclaims.composePlan(args.text);
|
|
2543
|
+
let last = '';
|
|
2544
|
+
for (const op of plan.ops) {
|
|
2545
|
+
const tname = `${server}__${op.tool}`;
|
|
2546
|
+
if (!hostMcpHas(tname)) return JSON.stringify({ ok: false, error: `${tname} not attached` });
|
|
2547
|
+
last = String(await callHostMcp(tname, op.args));
|
|
2548
|
+
if (op.tool === 'evaluate_script' && /"ok":\s*false/.test(last)) return JSON.stringify({ ok: false, error: last.slice(0, 300) });
|
|
2549
|
+
}
|
|
2550
|
+
log(`cursor-backend: x_compose ${server} lines=${plan.lines}`);
|
|
2551
|
+
return JSON.stringify({ ok: true, browser: server, linesTyped: plan.lines, composer: last.slice(0, 700), next: 'verify the composer text above shows separate lines, then click the Reply button' });
|
|
2552
|
+
}
|
|
2528
2553
|
}
|
|
2529
2554
|
if (hostMcpHas(name)) {
|
|
2530
2555
|
return await callHostMcp(name, args);
|
|
@@ -2685,7 +2710,8 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
|
|
|
2685
2710
|
return brief ? `${who} Standing brief (persisted): ${brief}` : who;
|
|
2686
2711
|
})(),
|
|
2687
2712
|
`You HAVE local tools on the user's computer via ${via}.`,
|
|
2688
|
-
'Tools: read_file, write_file, exec, list_dir, screenshot, click, type_text, key, ui_tree, focus_app, open_url, create_agent, set_brief, list_agents, message_agent, schedule_wakeup, cancel_wakeup, ship_crew, ship_forge, ship_launch_worker, ship_status, ship_review, ship_open_pr, x_claim, x_done, x_release, x_claims.',
|
|
2713
|
+
'Tools: read_file, write_file, exec, list_dir, screenshot, click, type_text, key, ui_tree, focus_app, open_url, create_agent, set_brief, list_agents, message_agent, schedule_wakeup, cancel_wakeup, ship_crew, ship_forge, ship_launch_worker, ship_status, ship_review, ship_open_pr, x_claim, x_done, x_release, x_claims, x_compose.',
|
|
2714
|
+
'Posting on X: NEVER fill the reply box (newlines get flattened). Open the tweet, click the reply box, then x_compose {browser, text} which types line by line with Enter, then click Reply.',
|
|
2689
2715
|
'X reply bots: x_claims first (skip those ids), x_claim the tweet BEFORE drafting (ok:false -> pick another), x_done with our reply URL after posting, x_release if you back out. Never reply to a tweet you did not claim.',
|
|
2690
2716
|
'If no sidebar bot is named Firstmate and the human brings code or repo work, ask ONE question: which repo path to set up Grok Ship for. Then call ship_crew with that cwd. Do not start coding outside the factory.',
|
|
2691
2717
|
'Grok Ship (software factory): ship_crew sets up Firstmate + a crewmate per repo. A crewmate ships with ship_launch_worker -> ship_status until pushed -> ship_review (fresh, diff-only) -> ship_open_pr only when gate.clean. Never merge; the human does.',
|
package/lib/xclaims.js
CHANGED
|
@@ -104,3 +104,21 @@ export function listClaims({ home = os.homedir(), now = Date.now() } = {}) {
|
|
|
104
104
|
}
|
|
105
105
|
return { done, live, total: Object.keys(claims).length };
|
|
106
106
|
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* X's composer is a Draft-style contenteditable: one `fill` flattens every
|
|
110
|
+
* newline (measured: "openzoo.fun/core" + "x402 · PAID…" fused into
|
|
111
|
+
* "corex402"). Real keystrokes work: type a line, press Enter, type the next.
|
|
112
|
+
* Pure planner — the host executes these against chrome-devtools / brave-devtools.
|
|
113
|
+
*/
|
|
114
|
+
export const X_COMPOSER_SELECTOR = '[data-testid="tweetTextarea_0"], [data-testid="tweetTextarea_0RichTextInputContainer"] [contenteditable="true"], div[role="textbox"][contenteditable="true"]';
|
|
115
|
+
export function composePlan(text) {
|
|
116
|
+
const lines = String(text || '').replace(/\r\n?/g, '\n').split('\n');
|
|
117
|
+
const ops = [{ tool: 'evaluate_script', args: { function: `() => { const el = document.querySelector(${JSON.stringify(X_COMPOSER_SELECTOR)}); if (!el) return { ok: false, error: 'no composer on this page' }; el.focus(); return { ok: true }; }` } }];
|
|
118
|
+
lines.forEach((line, i) => {
|
|
119
|
+
if (i) ops.push({ tool: 'press_key', args: { key: 'Enter' } });
|
|
120
|
+
if (line) ops.push({ tool: 'type_text', args: { text: line } });
|
|
121
|
+
});
|
|
122
|
+
ops.push({ tool: 'evaluate_script', args: { function: `() => { const el = document.querySelector(${JSON.stringify(X_COMPOSER_SELECTOR)}); const t = el ? (el.innerText || el.textContent || '') : ''; return { ok: !!el, lines: t.split('\\n').filter(Boolean).length, text: t.slice(0, 600) }; }` } });
|
|
123
|
+
return { lines: lines.length, ops };
|
|
124
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.66",
|
|
4
4
|
"description": "Local x402-paying proxy + MCP server for openzoo.fun — point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|