19solution that changes as little code as possible.
2021Use your 'fetch' tool to debug HTTP vals making requests to them and examining the responses.
2223Use your 'requests' tool to debug all kinds of vals. It'll show you the logs made during the file execution and the attributes associated it, including headers, http status, run time, error messages and error stack traces.
Townie2Messages.tsx9 matches
256</>
257);
258case "fetch":
259return (
260<Details
269summary={(
270<>
271<div>fetch:</div>
272<div>{args?.valPath}</div>
273<div>{args?.urlPath || "/"}</div>
275)}>
276{result?.type === "success" ? (
277<div className="fetch-result">
278<div className="fetch-header">
279<span className={`status-badge ${result.data.status >= 200 && result.data.status < 300 ? 'success' :
280result.data.status >= 300 && result.data.status < 400 ? 'redirect' :
284<span className="response-time">{result.data.responseTime}ms</span>
285</div>
286<div className="fetch-section">
287<h4>Headers</h4>
288<pre className="fetch-headers">{JSON.stringify(result.data.headers, null, 2)}</pre>
289</div>
290<div className="fetch-section">
291<h4>Response Body</h4>
292<pre className="fetch-body">
293{typeof result.data.body === 'object'
294? JSON.stringify(result.data.body, null, 2)
298</div>
299) : (
300<div className="fetch-error">
301<h4>Error</h4>
302<pre>{result?.message || "Unknown error"}</pre>
1import { makeChangeValTypeTool } from "./change-val-type.ts";
2import { makeFetchTool } from "./fetch.ts";
3import { makeTextEditorTool } from "./text-editor.ts";
4import { makeTracesTool } from "./traces.ts";
8makeTextEditorTool,
9makeChangeValTypeTool,
10makeFetchTool,
11makeTracesTool,
12thinkTool,
11* Creates a tool for making HTTP requests to vals in a Val Town project
12*/
13export const makeFetchTool = (
14{ bearerToken, project, branch_id }: { bearerToken?: string; project?: any; branch_id?: string } = {},
15) =>
16tool({
17name: "fetch",
18description: "Make an HTTP request to a Val Town val and return the response. Useful for testing HTTP vals. The HTTP response body will be truncated to 5000 characters.",
19parameters: z.object({
68return {
69type: "error",
70message: `Error fetching val at path '${valPath}': ${error.message}`,
71};
72}
83return {
84type: "error",
85message: `The val at path '${valPath}' is not an HTTP val. Only HTTP vals can be called with fetch.`,
86};
87}
111let response;
112try {
113response = await fetch(valEndpoint + urlPath, options);
114} catch (error: any) {
115// Return error information
Townie2.cursorrules3 matches
239
240// Inject data to avoid extra round-trips
241const initialData = await fetchInitialData();
242const dataScript = `<script>
243window.__INITIAL_DATA__ = ${JSON.stringify(initialData)};
2862875. **API Design:**
288- `fetch` handler is the entry point for HTTP vals
289- Run the Hono app with `export default app.fetch // This is the entry point for HTTP vals`
290291
Townie2CreditBalance.tsx5 matches
7const [loading, setLoading] = useState(true);
89const fetchBalance = async () => {
10try {
11const response = await fetch("/api/credit-balance");
12if (response.ok) {
13const data = await response.json();
15setBalance(data.balance);
16} else {
17console.error("Failed to fetch balance");
18}
19} catch (err) {
20console.error("Error fetching balance:", err);
21} finally {
22setLoading(false);
2526useEffect(() => {
27fetchBalance();
28}, []);
29
Townie2ChatRouteSingleColumn.tsx13 matches
51files={project.data?.files}
52branchId={branchId}
53refetch={project.refetch}
54/>
55</ProjectContext.Provider>
61files,
62branchId,
63refetch,
64}: {
65project: any;
66files: any[];
67branchId: string;
68refetch: () => void;
69}) {
70const [images, setImages] = useState<(string|null)[]>([]);
71const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
72const { audio, user } = useContext(AppContext);
73const { balance, loading: balanceLoading, refetch: refetchBalance } = useCreditBalance();
7475const {
94useLoadingFavicon(running);
9596// Track when requests end and refetch balance
97const prevRunning = useRef(running);
98useEffect(() => {
99// If running changed from true to false, request just ended
100if (prevRunning.current === true && running === false) {
101refetchBalance();
102}
103prevRunning.current = running;
104}, [running, refetchBalance]);
105106// Auto-poll balance every 4 seconds when credits are insufficient
110if (hasInsufficientCredits) {
111const intervalId = setInterval(() => {
112refetchBalance();
113}, 4000); // 4 seconds
114
115return () => clearInterval(intervalId);
116}
117}, [balanceLoading, balance, refetchBalance]);
118119useEffect(() => {
120if (!messages?.length) return;
121let last = messages.at(-1);
122if (shouldRefetch(last)) {
123refetch();
124}
125}, [messages]);
196<button
197onClick={() => {
198refetchBalance();
199}}
200className="icon-button"
227}
228229function shouldRefetch (message) {
230for (let i = 0; i < message?.parts?.length; i++) {
231let part = message.parts[i];
Townie2BranchSelect.tsx1 match
32return;
33}
34branches.refetch();
35if (res?.branch?.id) {
36navigate(`/chat/${projectId}/branch/${res.branch.id}`);
29}
3031const response = await fetch(url, {
32...options,
33headers: {