Initial commit

This commit is contained in:
Jordan
2026-04-01 23:16:45 +01:00
commit 91cfdaee72
200 changed files with 25589 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
import { z } from "zod";
import fs from "fs";
import path from "path";
import axios from "axios";
import { sessionCredentials } from "../../auth/credentials.js";
import { withAuthParams } from "../helpers/authSchema.js";
const LOCAL_SERVER_URL = `http://localhost:${process.env.ACAI_HOST_PORT || 29871}`;
export function registerAuthTools(server) {
server.tool(
"refresh_acai_token",
`Refresh the Acai JWT token when it has expired (403 "Token no válido" errors). This re-reads the token from the .acai file on disk. If the token on disk is also expired, it calls the Python server to renew it. Use this tool when any other tool fails with a 403 token error.`,
withAuthParams({}),
{ readOnlyHint: false, destructiveHint: false },
async (_args, extra) => {
try {
const projectDir = process.env.ACAI_PROJECT_DIR || "";
const acaiFilePath = projectDir ? path.join(projectDir, ".acai") : "";
if (!acaiFilePath) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: "ACAI_PROJECT_DIR not set" }) }],
isError: true,
};
}
// Step 1: Try reading fresh token from .acai (Python server may have already refreshed it)
let token = "";
let tokenHash = "";
let domain = "";
try {
const data = JSON.parse(fs.readFileSync(acaiFilePath, "utf-8"));
token = data.token || "";
tokenHash = data.tokenHash || "";
domain = data.domain || "";
} catch (e) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: `Cannot read .acai: ${e.message}` }) }],
isError: true,
};
}
// Step 2: Check if token is expired by decoding JWT
let isExpired = false;
try {
const payload = token.split(".")[1];
const decoded = JSON.parse(Buffer.from(payload, "base64").toString());
isExpired = Date.now() / 1000 > (decoded.exp || 0) - 300;
} catch {
isExpired = true;
}
// Step 3: If expired, ask Python server to refresh it
if (isExpired) {
try {
// Call the compile-module endpoint pattern — but we need a refresh endpoint
// Use the server's existing auto-refresh: just call any endpoint that triggers refresh
// The simplest: GET /api/projects which auto-refreshes expired tokens
const res = await axios.get(`${LOCAL_SERVER_URL}/api/projects`, { timeout: 15000 });
// Re-read .acai after server refreshed it
const data = JSON.parse(fs.readFileSync(acaiFilePath, "utf-8"));
token = data.token || "";
tokenHash = data.tokenHash || "";
} catch (e) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: `Token refresh failed: ${e.message}` }) }],
isError: true,
};
}
}
// Step 4: Update credentials in memory
const webUrl = process.env.ACAI_WEB_URL || "";
const website = domain || process.env.ACAI_WEBSITE || "";
const freshCreds = {
token,
tokenHash,
website,
web_url: webUrl,
profileName: "stdio",
role: "developer",
};
sessionCredentials.set("_default", freshCreds);
if (extra?.sessionId) {
sessionCredentials.set(extra.sessionId, freshCreds);
}
return {
content: [{
type: "text",
text: JSON.stringify({
success: true,
message: "Token refreshed successfully",
expired_before: isExpired,
domain: website,
}, null, 2),
}],
};
} catch (error) {
return {
content: [{ type: "text", text: JSON.stringify({ success: false, error: error.message }) }],
isError: true,
};
}
}
);
}