@clawpump/claw-agent 0.1.8 → 0.1.11
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/agent/.mailmap +4 -0
- package/agent/apps/desktop/README.md +3 -3
- package/agent/apps/desktop/assets/icon.icns +0 -0
- package/agent/apps/desktop/assets/icon.ico +0 -0
- package/agent/apps/desktop/assets/icon.png +0 -0
- package/agent/apps/desktop/electron/backend-ready.cjs +2 -2
- package/agent/apps/desktop/electron/dashboard-token.cjs +3 -3
- package/agent/apps/desktop/electron/hardening.cjs +1 -1
- package/agent/apps/desktop/electron/main.cjs +67 -67
- package/agent/apps/desktop/index.html +1 -1
- package/agent/apps/desktop/package.json +16 -16
- package/agent/apps/desktop/public/apple-touch-icon.png +0 -0
- package/agent/apps/desktop/public/claw-mark.png +0 -0
- package/agent/apps/desktop/scripts/after-pack.cjs +1 -1
- package/agent/apps/desktop/scripts/set-exe-identity.cjs +2 -2
- package/agent/apps/desktop/scripts/test-desktop.mjs +3 -3
- package/agent/apps/desktop/src/app/chat/composer/controls.tsx +2 -0
- package/agent/apps/desktop/src/app/chat/composer/index.tsx +10 -0
- package/agent/apps/desktop/src/app/chat/composer/pod-credits.tsx +49 -0
- package/agent/apps/desktop/src/app/chat/index.tsx +1 -1
- package/agent/apps/desktop/src/app/chat/sidebar/index.tsx +4 -2
- package/agent/apps/desktop/src/app/desktop-controller.tsx +18 -0
- package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-request.ts +1 -1
- package/agent/apps/desktop/src/app/messaging/index.tsx +5 -5
- package/agent/apps/desktop/src/app/routes.ts +9 -1
- package/agent/apps/desktop/src/app/session/hooks/use-message-stream.ts +3 -3
- package/agent/apps/desktop/src/app/settings/constants.ts +5 -5
- package/agent/apps/desktop/src/app/settings/model-settings.tsx +1 -1
- package/agent/apps/desktop/src/app/settings/providers-settings.tsx +46 -1
- package/agent/apps/desktop/src/app/settings/uninstall-section.tsx +5 -5
- package/agent/apps/desktop/src/app/types.ts +9 -1
- package/agent/apps/desktop/src/app/wallet/index.tsx +244 -0
- package/agent/apps/desktop/src/app/x402/index.tsx +162 -0
- package/agent/apps/desktop/src/components/assistant-ui/thread.tsx +1 -1
- package/agent/apps/desktop/src/components/brand-mark.tsx +2 -2
- package/agent/apps/desktop/src/components/chat/intro-copy.jsonl +6 -6
- package/agent/apps/desktop/src/components/chat/intro.tsx +4 -4
- package/agent/apps/desktop/src/components/model-picker.tsx +64 -4
- package/agent/apps/desktop/src/components/pod-setup-dialog.tsx +227 -0
- package/agent/apps/desktop/src/hermes.ts +109 -3
- package/agent/apps/desktop/src/i18n/en.ts +80 -78
- package/agent/apps/desktop/src/i18n/ja.ts +82 -82
- package/agent/apps/desktop/src/i18n/runtime.test.ts +2 -2
- package/agent/apps/desktop/src/i18n/zh-hant.ts +82 -82
- package/agent/apps/desktop/src/i18n/zh.ts +87 -87
- package/agent/apps/desktop/src/lib/desktop-fs.ts +1 -1
- package/agent/apps/desktop/src/lib/desktop-slash-commands.ts +4 -4
- package/agent/apps/desktop/src/store/composer.ts +7 -0
- package/agent/apps/desktop/src/store/onboarding.ts +5 -5
- package/agent/apps/desktop/src/themes/presets.ts +54 -54
- package/agent/cli.py +184 -10
- package/agent/hermes_cli/distribution.py +188 -8
- package/agent/hermes_cli/gui_uninstall.py +11 -6
- package/agent/hermes_cli/main.py +9 -4
- package/agent/hermes_cli/providers.py +29 -0
- package/agent/hermes_cli/web_server.py +180 -2
- package/agent/plugins/model-providers/usepod/__init__.py +7 -1
- package/agent/scripts/install.sh +3 -1
- package/agent/scripts/release.py +1 -0
- package/agent/web/src/components/ChatSidebar.tsx +5 -0
- package/agent/web/src/components/ModelPickerDialog.tsx +28 -1
- package/agent/web/src/components/PodCredits.tsx +57 -0
- package/agent/web/src/components/PodSetupDialog.tsx +240 -0
- package/agent/web/src/lib/api.ts +23 -0
- package/package.json +1 -1
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Zap } from "lucide-react";
|
|
3
|
+
import { api } from "@/lib/api";
|
|
4
|
+
import type { AgentWalletBalance } from "@/lib/api";
|
|
5
|
+
import { Button } from "@nous-research/ui/ui/components/button";
|
|
6
|
+
import { Input } from "@nous-research/ui/ui/components/input";
|
|
7
|
+
import { Select, SelectOption } from "@nous-research/ui/ui/components/select";
|
|
8
|
+
import { Spinner } from "@nous-research/ui/ui/components/spinner";
|
|
9
|
+
|
|
10
|
+
const DEFAULT_AMOUNT = "5";
|
|
11
|
+
|
|
12
|
+
const walletLabel = (w: AgentWalletBalance): string =>
|
|
13
|
+
w.name ||
|
|
14
|
+
(w.wallet_address
|
|
15
|
+
? `${w.wallet_address.slice(0, 4)}…${w.wallet_address.slice(-4)}`
|
|
16
|
+
: w.agent_id);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One-screen Pod setup for the web dashboard: pick a ClawPump agent wallet, pick
|
|
20
|
+
* an amount, confirm. `usepod_provision` registers + funds a pod from that wallet
|
|
21
|
+
* on-chain in one call, and the backend switches the session onto Pod as the
|
|
22
|
+
* provider. The single confirm is the on-chain USDC spend; everything else is
|
|
23
|
+
* prefilled. Mirrors the desktop PodSetupDialog.
|
|
24
|
+
*/
|
|
25
|
+
export default function PodSetupDialog({
|
|
26
|
+
onClose,
|
|
27
|
+
onProvisioned,
|
|
28
|
+
}: {
|
|
29
|
+
onClose: () => void;
|
|
30
|
+
onProvisioned: (model: string) => void;
|
|
31
|
+
}) {
|
|
32
|
+
const [wallets, setWallets] = useState<AgentWalletBalance[] | null>(null);
|
|
33
|
+
const [loadError, setLoadError] = useState<string | null>(null);
|
|
34
|
+
const [agentId, setAgentId] = useState("");
|
|
35
|
+
const [amount, setAmount] = useState(DEFAULT_AMOUNT);
|
|
36
|
+
const [busy, setBusy] = useState(false);
|
|
37
|
+
const [error, setError] = useState<string | null>(null);
|
|
38
|
+
const [done, setDone] = useState<{
|
|
39
|
+
model: string;
|
|
40
|
+
amount: number;
|
|
41
|
+
signature?: string;
|
|
42
|
+
fundingError?: string;
|
|
43
|
+
} | null>(null);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
let cancelled = false;
|
|
47
|
+
void api
|
|
48
|
+
.getWalletBalances()
|
|
49
|
+
.then((r) => {
|
|
50
|
+
if (cancelled) return;
|
|
51
|
+
const rows = (r.wallets ?? []).filter((w) => w.agent_id);
|
|
52
|
+
setWallets(rows);
|
|
53
|
+
if (rows.length > 0) {
|
|
54
|
+
const best = [...rows].sort(
|
|
55
|
+
(a, b) => (b.usdc_balance ?? 0) - (a.usdc_balance ?? 0),
|
|
56
|
+
)[0];
|
|
57
|
+
setAgentId(best.agent_id);
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
.catch((e) => !cancelled && setLoadError(e instanceof Error ? e.message : "Failed to load wallets"));
|
|
61
|
+
return () => {
|
|
62
|
+
cancelled = true;
|
|
63
|
+
};
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
const rows = wallets ?? [];
|
|
67
|
+
const selected = rows.find((w) => w.agent_id === agentId) ?? null;
|
|
68
|
+
const amountNum = Number(amount);
|
|
69
|
+
const balance = selected?.usdc_balance ?? 0;
|
|
70
|
+
const insufficient = Number.isFinite(amountNum) && amountNum > 0 && amountNum > balance;
|
|
71
|
+
const canFund =
|
|
72
|
+
!busy && Boolean(agentId) && Number.isFinite(amountNum) && amountNum > 0 && !insufficient;
|
|
73
|
+
|
|
74
|
+
const heading = useMemo(() => (done ? "Pod ready" : "Set up Pod"), [done]);
|
|
75
|
+
|
|
76
|
+
// Ref guard: setBusy is async, so a fast double-click could fire two
|
|
77
|
+
// provisions (= double on-chain spend) before the disabled state re-renders.
|
|
78
|
+
const submitting = useRef(false);
|
|
79
|
+
|
|
80
|
+
const fund = async () => {
|
|
81
|
+
if (!canFund || submitting.current) return;
|
|
82
|
+
submitting.current = true;
|
|
83
|
+
setBusy(true);
|
|
84
|
+
setError(null);
|
|
85
|
+
try {
|
|
86
|
+
const res = await api.provisionPod(agentId, amountNum);
|
|
87
|
+
if (!res.ok || !res.model) {
|
|
88
|
+
setError(res.error || res.funding_error || "Pod setup failed. Check the wallet balance and try again.");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// Show the "Pod ready" view; the actual session switch + close happens on
|
|
92
|
+
// Done (onProvisioned), because the switch unmounts this dialog.
|
|
93
|
+
setDone({
|
|
94
|
+
amount: amountNum,
|
|
95
|
+
model: res.model,
|
|
96
|
+
signature: res.signature,
|
|
97
|
+
fundingError: res.funding_error || undefined,
|
|
98
|
+
});
|
|
99
|
+
} catch (e) {
|
|
100
|
+
setError(e instanceof Error ? e.message : "Pod setup failed.");
|
|
101
|
+
} finally {
|
|
102
|
+
setBusy(false);
|
|
103
|
+
submitting.current = false;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div
|
|
109
|
+
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4"
|
|
110
|
+
onClick={() => {
|
|
111
|
+
if (busy) return;
|
|
112
|
+
// After a successful provision, dismissing applies the switch (same as
|
|
113
|
+
// Done) so a backdrop click doesn't silently skip using the new Pod.
|
|
114
|
+
if (done) onProvisioned(done.model);
|
|
115
|
+
else onClose();
|
|
116
|
+
}}
|
|
117
|
+
>
|
|
118
|
+
<div
|
|
119
|
+
className="w-full max-w-md rounded-lg border border-border bg-card shadow-xl"
|
|
120
|
+
onClick={(e) => e.stopPropagation()}
|
|
121
|
+
>
|
|
122
|
+
<div className="flex items-center gap-2 border-b border-border px-4 py-3">
|
|
123
|
+
<Zap className="h-4 w-4 text-primary" />
|
|
124
|
+
<h2 className="text-sm font-semibold">
|
|
125
|
+
{done?.fundingError ? "Pod created — funding failed" : heading}
|
|
126
|
+
</h2>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
{done ? (
|
|
130
|
+
<div className="flex flex-col gap-3 p-4">
|
|
131
|
+
{done.fundingError ? (
|
|
132
|
+
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-3 text-sm">
|
|
133
|
+
<div className="font-semibold text-amber-300">⚠ Pod created but not funded</div>
|
|
134
|
+
<div className="mt-0.5 text-muted-foreground">
|
|
135
|
+
The on-chain deposit didn’t confirm ({done.fundingError}). The pod (
|
|
136
|
+
<span className="font-mono">{done.model}</span>) is selected but has no balance — it
|
|
137
|
+
may settle shortly, or run Set up Pod again to top it up.
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
) : (
|
|
141
|
+
<div className="rounded-md border border-emerald-500/30 bg-emerald-500/10 px-3 py-3 text-sm">
|
|
142
|
+
<div className="font-semibold text-emerald-300">⚡ Using Pod</div>
|
|
143
|
+
<div className="mt-0.5 text-muted-foreground">
|
|
144
|
+
Funded <span className="font-mono">${done.amount.toFixed(2)} USDC</span> · model{" "}
|
|
145
|
+
<span className="font-mono">{done.model}</span>. New chats run on Pod automatically.
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
149
|
+
{done.signature && (
|
|
150
|
+
<a
|
|
151
|
+
className="text-xs text-primary hover:underline"
|
|
152
|
+
href={`https://solscan.io/tx/${done.signature}`}
|
|
153
|
+
rel="noreferrer"
|
|
154
|
+
target="_blank"
|
|
155
|
+
>
|
|
156
|
+
View funding transaction ↗
|
|
157
|
+
</a>
|
|
158
|
+
)}
|
|
159
|
+
<div className="flex justify-end">
|
|
160
|
+
<Button onClick={() => onProvisioned(done.model)}>Done</Button>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
) : (
|
|
164
|
+
<>
|
|
165
|
+
<div className="flex flex-col gap-4 p-4">
|
|
166
|
+
<p className="text-xs leading-relaxed text-muted-foreground">
|
|
167
|
+
Fund a private inference Pod from a ClawPump agent wallet and use it as your model
|
|
168
|
+
provider. You only pay for what you use — the Pod holds a prepaid USDC balance.
|
|
169
|
+
</p>
|
|
170
|
+
|
|
171
|
+
{wallets === null ? (
|
|
172
|
+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
173
|
+
<Spinner className="text-xs" /> Loading your wallets…
|
|
174
|
+
</div>
|
|
175
|
+
) : loadError || rows.length === 0 ? (
|
|
176
|
+
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-300">
|
|
177
|
+
{loadError ||
|
|
178
|
+
"No ClawPump agent wallets found. Create one in the ClawPump dashboard first."}
|
|
179
|
+
</div>
|
|
180
|
+
) : (
|
|
181
|
+
<>
|
|
182
|
+
<label className="flex flex-col gap-1.5 text-sm">
|
|
183
|
+
<span className="font-medium">Pay from wallet</span>
|
|
184
|
+
<Select value={agentId} onValueChange={setAgentId}>
|
|
185
|
+
{rows.map((w) => (
|
|
186
|
+
<SelectOption key={w.agent_id} value={w.agent_id}>
|
|
187
|
+
{`${walletLabel(w)} — $${(w.usdc_balance ?? 0).toFixed(2)} USDC`}
|
|
188
|
+
</SelectOption>
|
|
189
|
+
))}
|
|
190
|
+
</Select>
|
|
191
|
+
</label>
|
|
192
|
+
|
|
193
|
+
<label className="flex flex-col gap-1.5 text-sm">
|
|
194
|
+
<span className="font-medium">Amount to fund (USDC)</span>
|
|
195
|
+
<Input
|
|
196
|
+
type="number"
|
|
197
|
+
min="0"
|
|
198
|
+
step="1"
|
|
199
|
+
value={amount}
|
|
200
|
+
onChange={(e) => setAmount(e.target.value)}
|
|
201
|
+
/>
|
|
202
|
+
<span className="text-xs text-muted-foreground">
|
|
203
|
+
Wallet balance: <span className="font-mono">${balance.toFixed(2)} USDC</span>
|
|
204
|
+
</span>
|
|
205
|
+
</label>
|
|
206
|
+
|
|
207
|
+
{insufficient && (
|
|
208
|
+
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-300">
|
|
209
|
+
Not enough USDC in this wallet for ${amountNum.toFixed(2)}. Pick a smaller amount.
|
|
210
|
+
</div>
|
|
211
|
+
)}
|
|
212
|
+
{error && (
|
|
213
|
+
<div className="rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive">
|
|
214
|
+
{error}
|
|
215
|
+
</div>
|
|
216
|
+
)}
|
|
217
|
+
</>
|
|
218
|
+
)}
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<div className="flex justify-end gap-2 border-t border-border px-4 py-3">
|
|
222
|
+
<Button variant="outline" disabled={busy} onClick={onClose}>
|
|
223
|
+
Cancel
|
|
224
|
+
</Button>
|
|
225
|
+
<Button disabled={!canFund} onClick={() => void fund()}>
|
|
226
|
+
{busy ? (
|
|
227
|
+
<span className="flex items-center gap-2">
|
|
228
|
+
<Spinner className="text-xs" /> Funding Pod…
|
|
229
|
+
</span>
|
|
230
|
+
) : (
|
|
231
|
+
`Fund $${Number.isFinite(amountNum) ? amountNum.toFixed(2) : "0.00"} & use Pod`
|
|
232
|
+
)}
|
|
233
|
+
</Button>
|
|
234
|
+
</div>
|
|
235
|
+
</>
|
|
236
|
+
)}
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
);
|
|
240
|
+
}
|
package/agent/web/src/lib/api.ts
CHANGED
|
@@ -343,6 +343,19 @@ export interface WalletTransferResponse {
|
|
|
343
343
|
result?: unknown;
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
+
export interface PodStatusResponse {
|
|
347
|
+
connected: boolean;
|
|
348
|
+
balance_usdc?: number | null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export interface PodProvisionResponse {
|
|
352
|
+
ok: boolean;
|
|
353
|
+
model?: string;
|
|
354
|
+
signature?: string;
|
|
355
|
+
funding_error?: string;
|
|
356
|
+
error?: string;
|
|
357
|
+
}
|
|
358
|
+
|
|
346
359
|
export interface X402Pricing {
|
|
347
360
|
network?: string;
|
|
348
361
|
asset?: string;
|
|
@@ -464,6 +477,16 @@ export const api = {
|
|
|
464
477
|
body: JSON.stringify(body),
|
|
465
478
|
}),
|
|
466
479
|
|
|
480
|
+
// ── UsePod "Pod" — pay-as-you-go inference funded from a ClawPump wallet ──
|
|
481
|
+
getPodStatus: () =>
|
|
482
|
+
fetchJSON<PodStatusResponse>("/api/clawpump/pod/status"),
|
|
483
|
+
provisionPod: (agentId: string, amount: number) =>
|
|
484
|
+
fetchJSON<PodProvisionResponse>("/api/clawpump/pod/provision", {
|
|
485
|
+
method: "POST",
|
|
486
|
+
headers: { "Content-Type": "application/json" },
|
|
487
|
+
body: JSON.stringify({ agent_id: agentId, amount }),
|
|
488
|
+
}),
|
|
489
|
+
|
|
467
490
|
// ── Agent Mail (AgentMail) ─────────────────────────────────────────
|
|
468
491
|
// Every call carries an explicit agent_id — the MCP requires it once the
|
|
469
492
|
// account has more than one agent.
|