openzoo 0.50.95 → 0.50.97
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/bin/claude-zoo.js +28 -3
- package/lib/aoe.js +8 -2
- package/lib/cursorbackend.js +47 -22
- package/lib/cursorbackend.js.bak-hiddenbots +4072 -0
- package/lib/grokbotAccount.js +4 -2
- package/lib/grokbotAccount.js.bak-hiddenbots +386 -0
- package/lib/grokbotweb.js +10 -1
- package/lib/grokcli.js +89 -7
- package/lib/launch.js +18 -24
- package/lib/modelroute/README.md +1 -0
- package/lib/modelroute/catalog.json +1 -0
- package/lib/modelroute/outcomes.json +1566 -0
- package/lib/modelroute/router.json +1 -0
- package/lib/modelroute.js +737 -0
- package/lib/ozSpendChip.js +187 -29
- package/lib/podagent.mjs +1425 -0
- package/lib/proxy.js +127 -48
- package/lib/runguard.js +31 -0
- package/lib/setup.js +12 -13
- package/lib/spill.js +2031 -0
- package/lib/stripeOnramp.js +50 -0
- package/lib/subscription.js +207 -0
- package/lib/worktree.mjs +424 -0
- package/package.json +1 -1
package/bin/claude-zoo.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* the local openzoo proxy on :8402 (x402 per-call payment, no Anthropic
|
|
7
7
|
* subscription, no login):
|
|
8
8
|
*
|
|
9
|
-
* - starts the proxy if it is not already
|
|
9
|
+
* - starts the proxy if it is not already THIS version on :8402 (steals stale)
|
|
10
10
|
* - applies claudeZooEnv(): ANTHROPIC_BASE_URL=localhost:PORT/v1,
|
|
11
11
|
* AUTH_TOKEN=sk-openzoo, ANTHROPIC_API_KEY deleted, compaction disabled
|
|
12
12
|
* (the proxy binds the prefix), 1M-token ceiling restored
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
* `claude-code-cli` (same directory, symlinked to occ).
|
|
17
17
|
*/
|
|
18
18
|
import { spawn } from 'node:child_process';
|
|
19
|
-
import { existsSync, readdirSync as fsReaddir } from 'node:fs';
|
|
19
|
+
import { existsSync, readdirSync as fsReaddir, readFileSync } from 'node:fs';
|
|
20
|
+
import { execSync } from 'node:child_process';
|
|
20
21
|
import { homedir, platform } from 'node:os';
|
|
21
22
|
import { dirname, join, sep } from 'node:path';
|
|
22
23
|
import { fileURLToPath } from 'node:url';
|
|
@@ -50,10 +51,34 @@ if (!occ) {
|
|
|
50
51
|
const PROXY_PORT = Number(process.env.OPENZOO_PROXY_PORT || 8402);
|
|
51
52
|
const PROXY_URL = `http://localhost:${PROXY_PORT}/v1`;
|
|
52
53
|
|
|
54
|
+
function mineVersion() {
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(readFileSync(join(shimRoot, 'package.json'), 'utf8')).version;
|
|
57
|
+
} catch { return ''; }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function stealPort(port) {
|
|
61
|
+
const n = Number(port);
|
|
62
|
+
for (const cmd of [
|
|
63
|
+
`lsof -t -iTCP:${n} -sTCP:LISTEN | xargs kill -9`,
|
|
64
|
+
`fuser -k ${n}/tcp`,
|
|
65
|
+
]) {
|
|
66
|
+
try { execSync(cmd, { stdio: 'ignore', timeout: 2000, shell: true }); } catch { /* missing */ }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
53
70
|
async function proxyUp() {
|
|
54
71
|
try {
|
|
55
72
|
const r = await fetch(`http://localhost:${PROXY_PORT}/v1/info`, { signal: AbortSignal.timeout(1500) });
|
|
56
|
-
|
|
73
|
+
if (!r.ok) return false;
|
|
74
|
+
const j = await r.json().catch(() => ({}));
|
|
75
|
+
const mine = mineVersion();
|
|
76
|
+
if (mine && String(j.version || '') !== mine) {
|
|
77
|
+
console.error(`claude: stale proxy v${j.version || '?'} on :${PROXY_PORT} — stealing for v${mine}`);
|
|
78
|
+
stealPort(PROXY_PORT);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return true;
|
|
57
82
|
} catch { return false; }
|
|
58
83
|
}
|
|
59
84
|
|
package/lib/aoe.js
CHANGED
|
@@ -287,9 +287,15 @@ export async function setupAoe(argv = []) {
|
|
|
287
287
|
|
|
288
288
|
const base = `http://localhost:${config.port}/v1`;
|
|
289
289
|
if (!flags.has('--no-proxy')) {
|
|
290
|
-
|
|
291
|
-
|
|
290
|
+
const { oursOn, packageVersion, killListen } = await import('./proxy.js');
|
|
291
|
+
if (await oursOn(config.port)) {
|
|
292
|
+
console.error(`openzoo: proxy v${packageVersion()} already on ${base}`);
|
|
292
293
|
} else {
|
|
294
|
+
if (await proxyUp(base)) {
|
|
295
|
+
console.error(`openzoo: stale proxy on ${base} — stealing :${config.port}`);
|
|
296
|
+
killListen(config.port);
|
|
297
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
298
|
+
}
|
|
293
299
|
const { pid, logPath } = startDetachedProxy({ tunnel: flags.has('--tunnel') });
|
|
294
300
|
let up = false;
|
|
295
301
|
for (let i = 0; i < 40 && !up; i++) {
|
package/lib/cursorbackend.js
CHANGED
|
@@ -44,7 +44,8 @@ import {
|
|
|
44
44
|
readWakeups, writeWakeups, shapeWakeup, parseWakeupEvery, wantsWakeupCron,
|
|
45
45
|
DEFAULT_WAKEUP_PROMPT, addDeletedIds, filterDeleted,
|
|
46
46
|
} from './grokbotAccount.js';
|
|
47
|
-
import { formatSpendFooter, mergeTurnProof } from './spendProof.js';
|
|
47
|
+
import { spendChipLabel, formatSpendFooter, mergeTurnProof } from './spendProof.js';
|
|
48
|
+
import { spendLinesOnly, writeSpendHud } from './ozSpendChip.js';
|
|
48
49
|
import { zooModelIds, zooModelRow, mediaKindOf } from './models.js';
|
|
49
50
|
import { prefixVisitorRichText } from './grokbotweb.js';
|
|
50
51
|
import {
|
|
@@ -651,10 +652,10 @@ async function fetchEnsureSandBox(req, body, log) {
|
|
|
651
652
|
try {
|
|
652
653
|
const remote = await podJson('/api/listAgents', {}, log);
|
|
653
654
|
if (Array.isArray(remote) && remote.length) {
|
|
654
|
-
const merged = rosterForEvent(mergeAgentLists(remote), agentActivity);
|
|
655
|
+
const merged = rosterForEvent(mergeAgentLists(remote), agentActivity, { includeHidden: true }); // GROKROOM-HIDDEN-BOTS
|
|
655
656
|
saveAgents(merged);
|
|
656
657
|
const active = focusedAgentId || merged[0]?.id;
|
|
657
|
-
ssePush('agents', { agents: merged, activeAgentId: active });
|
|
658
|
+
ssePush('agents', { agents: merged.filter((a) => !a.hidden), activeAgentId: active }); // GROKROOM-HIDDEN-BOTS
|
|
658
659
|
log(`cursor-backend: discovered roster n=${merged.length} account=${realPod.accountId}`);
|
|
659
660
|
}
|
|
660
661
|
} catch (e) {
|
|
@@ -836,9 +837,9 @@ async function proxyPodHttp(req, res, full, body, log) {
|
|
|
836
837
|
try {
|
|
837
838
|
const parsed = JSON.parse(String(inflateBody(cap.buf, cap.respHeaders)));
|
|
838
839
|
if (Array.isArray(parsed)) {
|
|
839
|
-
const merged = rosterForEvent(mergeAgentLists(parsed), agentActivity);
|
|
840
|
+
const merged = rosterForEvent(mergeAgentLists(parsed), agentActivity, { includeHidden: true }); // GROKROOM-HIDDEN-BOTS
|
|
840
841
|
saveAgents(merged);
|
|
841
|
-
jsonSend(res, merged);
|
|
842
|
+
jsonSend(res, merged.filter((a) => !a.hidden)); // GROKROOM-HIDDEN-BOTS
|
|
842
843
|
log(`cursor-backend: listAgents 200 merged n=${merged.length} account=${activeAccountId || '?'}`);
|
|
843
844
|
return true;
|
|
844
845
|
}
|
|
@@ -971,7 +972,7 @@ function pushCreatedAgent(agent, { select = true } = {}) {
|
|
|
971
972
|
const active = focusedAgentId || agent.id;
|
|
972
973
|
const list = rosterForEvent(cachedAgentList() || [], agentActivity);
|
|
973
974
|
ssePush('agent-upserted', { agent, activeAgentId: active });
|
|
974
|
-
ssePush('agents', { agents: list, activeAgentId: active });
|
|
975
|
+
ssePush('agents', { agents: list.filter((a) => !a.hidden), activeAgentId: active }); // GROKROOM-HIDDEN-BOTS
|
|
975
976
|
}
|
|
976
977
|
|
|
977
978
|
/** Grok Bot Helper daemon: GET /local-exec/requests is SSE, POST /local-exec/responses
|
|
@@ -1880,7 +1881,15 @@ async function zooSpendOverlay(data) {
|
|
|
1880
1881
|
const bal = Number.isFinite(wallet) && wallet > 0.004
|
|
1881
1882
|
? wallet
|
|
1882
1883
|
: (Number.isFinite(credit) && credit > 0.004 ? credit : null);
|
|
1883
|
-
|
|
1884
|
+
const label = spent > 0.00005 ? spendChipLabel({
|
|
1885
|
+
billedUsd: x.billedUsd,
|
|
1886
|
+
directUsd: x.directUsd,
|
|
1887
|
+
spent,
|
|
1888
|
+
would,
|
|
1889
|
+
saved,
|
|
1890
|
+
pct,
|
|
1891
|
+
}) : '';
|
|
1892
|
+
const footer = formatSpendFooter({
|
|
1884
1893
|
billedUsd: x.billedUsd,
|
|
1885
1894
|
directUsd: x.directUsd,
|
|
1886
1895
|
spent,
|
|
@@ -1890,6 +1899,12 @@ async function zooSpendOverlay(data) {
|
|
|
1890
1899
|
balance: bal,
|
|
1891
1900
|
x402: x,
|
|
1892
1901
|
});
|
|
1902
|
+
const body = spendLinesOnly(footer) || (spent > 0.00005
|
|
1903
|
+
? `spent $${spent.toFixed(4)} · OpenRouter would $${would.toFixed(4)} · saved $${saved.toFixed(4)} (${pct.toFixed(0)}%)`
|
|
1904
|
+
: '');
|
|
1905
|
+
const state = { spent, would, saved, pct, label, billedUsd: x.billedUsd, directUsd: x.directUsd, balance: bal, body };
|
|
1906
|
+
try { writeSpendHud(state); } catch { /* */ }
|
|
1907
|
+
return state;
|
|
1893
1908
|
}
|
|
1894
1909
|
|
|
1895
1910
|
const MODELS_PATH = path.join(os.homedir(), '.openzoo', 'grokbot-models.json');
|
|
@@ -2941,7 +2956,7 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
|
|
|
2941
2956
|
'This is a public visitor chat. You do not have filesystem, shell, or local-exec access on the host Mac.',
|
|
2942
2957
|
'Reply in chat only. Do not claim you will write files, run commands, or use local tools.',
|
|
2943
2958
|
'Each human line is prefixed with that visitor\'s shortname and a colon, like "rex: hello". Different shortnames are different people. Address them by that name.',
|
|
2944
|
-
'
|
|
2959
|
+
'Do not write spend totals, solscan links, memos, or ::oz-spend:: into the reply. The host paints a HUD pill.',
|
|
2945
2960
|
'Prior turns of THIS Grok Bot chat are in the messages below. Do not claim the thread starts blank or that earlier questions did not arrive.',
|
|
2946
2961
|
].join(' ')
|
|
2947
2962
|
: [
|
|
@@ -2984,8 +2999,8 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
|
|
|
2984
2999
|
'Never claim you lack filesystem access or local-exec. If a tool errors, report the error.',
|
|
2985
3000
|
'After using tools you MUST still write a normal chat reply: what you did, file paths written, and what to open. Empty content is a bug. A new user message cancels this turn — leave a visible reply before that happens.',
|
|
2986
3001
|
'Do not stop mid-task. Do not write "Stopped on research", "nothing to open yet", "no app files written this turn", or "say go again". Keep using tools until the files the user asked for exist on disk THIS turn. Summarize only after those writes succeed.',
|
|
2987
|
-
'
|
|
2988
|
-
'The operator wallet pays every zoo call via x402 (TOKEN/USDC on Solana; SOL gas is sponsored). If
|
|
3002
|
+
'Do not write spend totals, solscan links, memos, or ::oz-spend:: into the reply. The host paints a HUD pill.',
|
|
3003
|
+
'The operator wallet pays every zoo call via x402 (TOKEN/USDC on Solana; SOL gas is sponsored). If the spend HUD is visible or a prior turn paid, the call WAS paid. Do not say you have no money, cannot afford tools, or need the human to fund you. A 402/underfunded tool error is a host retry — continue the job.',
|
|
2989
3004
|
'Prior turns of THIS Grok Bot chat are in the messages below. Do not claim the thread starts blank or that earlier questions did not arrive.',
|
|
2990
3005
|
].join(' '),
|
|
2991
3006
|
},
|
|
@@ -3075,17 +3090,22 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
|
|
|
3075
3090
|
if (r.status === 402) {
|
|
3076
3091
|
const raw = zooTextFromMessage(data?.choices?.[0]?.message, data)
|
|
3077
3092
|
|| data?.error?.message
|
|
3078
|
-
|| 'openzoo
|
|
3093
|
+
|| 'openzoo payment required (HTTP 402).';
|
|
3094
|
+
data.error = data.error || {};
|
|
3095
|
+
data.error.message = raw;
|
|
3079
3096
|
try {
|
|
3080
|
-
const { withOnrampLink } = await import('./stripeOnramp.js');
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3097
|
+
const { withOnrampLink, isFundInstruction } = await import('./stripeOnramp.js');
|
|
3098
|
+
// Settle/upstream 402s keep the real message. Whop copy-paste only
|
|
3099
|
+
// when this is a genuine empty-wallet / fund-me instruction.
|
|
3100
|
+
if (isFundInstruction(raw)) {
|
|
3101
|
+
const { loadOrCreateWallet } = await import('./wallet.js');
|
|
3102
|
+
const w = loadOrCreateWallet();
|
|
3103
|
+
const usd = Number(String(raw).match(/≈\$([0-9.]+)/)?.[1]);
|
|
3104
|
+
data.error.message = await withOnrampLink(raw, {
|
|
3105
|
+
solana: w.keypair.publicKey.toBase58(),
|
|
3106
|
+
usd,
|
|
3107
|
+
});
|
|
3108
|
+
}
|
|
3089
3109
|
} catch { /* keep proxy copy */ }
|
|
3090
3110
|
}
|
|
3091
3111
|
return { r, data };
|
|
@@ -3232,7 +3252,7 @@ async function zooComplete(prompt, log, agentId, parsed = {}, opts = {}) {
|
|
|
3232
3252
|
if (lastData && typeof lastData === 'object') {
|
|
3233
3253
|
lastData.x402 = mergeTurnProof(turnX402, lastData);
|
|
3234
3254
|
}
|
|
3235
|
-
try {
|
|
3255
|
+
try { await zooSpendOverlay(lastData); } catch { /* HUD dump; never append to the reply */ }
|
|
3236
3256
|
return { text, data: lastData };
|
|
3237
3257
|
}
|
|
3238
3258
|
|
|
@@ -3297,6 +3317,12 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
|
|
|
3297
3317
|
// Revive: the minutely cron (scripts/revive-bots.sh) calls this after it has
|
|
3298
3318
|
// made sure the hijack + app are alive. Every non-group bot gets a wakeup
|
|
3299
3319
|
// timer if it lost one; nothing is spawned, nothing already armed is touched.
|
|
3320
|
+
if (name === 'ozSpend') {
|
|
3321
|
+
let state = { spent: 0, would: 0, saved: 0, pct: 0, label: '' };
|
|
3322
|
+
try { state = await zooSpendOverlay(null); } catch { /* */ }
|
|
3323
|
+
jsonSend(res, { ok: true, ...state });
|
|
3324
|
+
return true;
|
|
3325
|
+
}
|
|
3300
3326
|
if (name === 'ozRevive') {
|
|
3301
3327
|
let parsed = {};
|
|
3302
3328
|
try { parsed = JSON.parse(String(body || '{}')); } catch { parsed = {}; }
|
|
@@ -3603,7 +3629,6 @@ async function handleHijackedPodHttp(req, res, full, body, log) {
|
|
|
3603
3629
|
text = `model set to ${id}`;
|
|
3604
3630
|
}
|
|
3605
3631
|
}
|
|
3606
|
-
try { text += await zooSpendOverlay({}); } catch { /* */ }
|
|
3607
3632
|
} else {
|
|
3608
3633
|
const members = groupMemberIds(agentId);
|
|
3609
3634
|
if (members.length) {
|