Augment API Token Tool

获取Augment API的访问令牌

// ==UserScript==
// @name         Augment API Token Tool
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  获取Augment API的访问令牌
// @author       backgroundPower
// @license      MIT
// @match        https://*.augmentcode.com/*
// @match        https://augmentcode.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_setClipboard
// @grant        unsafeWindow
// @connect      *
// ==/UserScript==

(function() {
    'use strict';

    const clientID = "v";

    // 工具函数
    function base64URLEncode(buffer) {
        return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)))
            .replace(/\+/g, "-")
            .replace(/\//g, "_")
            .replace(/=/g, "");
    }

    async function sha256Hash(input) {
        const encoder = new TextEncoder();
        const data = encoder.encode(input);
        const hashBuffer = await crypto.subtle.digest('SHA-256', data);
        return hashBuffer;
    }

    async function createOAuthState() {
        // 生成随机字节
        const codeVerifierArray = new Uint8Array(32);
        window.crypto.getRandomValues(codeVerifierArray);
        const codeVerifier = base64URLEncode(codeVerifierArray.buffer);

        // 创建 code challenge
        const codeChallenge = base64URLEncode(await sha256Hash(codeVerifier));

        // 生成随机 state
        const stateArray = new Uint8Array(8);
        window.crypto.getRandomValues(stateArray);
        const state = base64URLEncode(stateArray.buffer);

        const oauthState = {
            codeVerifier,
            codeChallenge,
            state,
            creationTime: Date.now()
        };

        // 存储状态以供后续使用
        GM_setValue('oauthState', JSON.stringify(oauthState));

        return oauthState;
    }

    function generateAuthorizeURL(oauthState) {
        const params = new URLSearchParams({
            response_type: "code",
            code_challenge: oauthState.codeChallenge,
            client_id: clientID,
            state: oauthState.state,
            prompt: "login",
        });

        return `https://auth.augmentcode.com/authorize?${params.toString()}`;
    }

    // 解析授权码函数
    function parseCode(code) {
        try {
            const parsed = JSON.parse(code);
            if (!parsed.code || !parsed.tenant_url) {
                throw new Error("缺少必要的code或tenant_url字段");
            }
            return {
                code: parsed.code,
                state: parsed.state,
                tenant_url: parsed.tenant_url
            };
        } catch (e) {
            throw new Error("JSON解析失败: " + e.message);
        }
    }

    async function getAccessToken(tenant_url, codeVerifier, code) {
        // 确保tenant_url以/结尾
        if (!tenant_url.endsWith('/')) {
            tenant_url = tenant_url + '/';
        }

        console.log("正在请求token...");
        console.log("URL:", `${tenant_url}token`);
        console.log("codeVerifier:", codeVerifier);
        console.log("code:", code);

        const data = {
            grant_type: "authorization_code",
            client_id: clientID,
            code_verifier: codeVerifier,
            redirect_uri: "",
            code: code,
        };

        console.log("请求数据:", JSON.stringify(data));

        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "POST",
                url: `${tenant_url}token`,
                headers: {
                    "Content-Type": "application/json"
                },
                data: JSON.stringify(data),
                onload: function(response) {
                    try {
                        console.log("API响应状态:", response.status);
                        console.log("API响应头:", response.responseHeaders);
                        console.log("API响应内容:", response.responseText);

                        const json = JSON.parse(response.responseText);
                        console.log("解析后的响应:", json);

                        if (json.access_token) {
                            resolve(json.access_token);
                        } else {
                            reject(`No access token found in response. Full response: ${JSON.stringify(json)}`);
                        }
                    } catch (e) {
                        reject(`Error parsing response: ${e}. Raw response: ${response.responseText}`);
                    }
                },
                onerror: function(error) {
                    console.error("请求错误:", error);
                    reject(`Request error: ${error}`);
                }
            });
        });
    }

    // 启动认证流程
    async function startAuthFlow() {
        const oauthState = await createOAuthState();
        const url = generateAuthorizeURL(oauthState);
        window.location.href = url;
    }

    // 处理授权回调
    function handleAuthCallback() {
        // 检查URL中是否有code参数
        const urlParams = new URLSearchParams(window.location.search);
        const code = urlParams.get('code');
        const state = urlParams.get('state');

        if (code && state) {
            const storedState = GM_getValue('oauthState');
            if (!storedState) {
                showMessage('认证状态丢失,请重试');
                return;
            }

            const oauthState = JSON.parse(storedState);
            if (state !== oauthState.state) {
                showMessage('状态验证失败,可能存在安全风险');
                return;
            }

            // 提取tenant_url
            const tenant_url = window.location.origin + '/';

            // 构建结果对象
            const result = {
                code: code,
                state: state,
                tenant_url: tenant_url
            };

            // 显示结果
            showMessage(`
                <h3>认证成功!</h3>
                <p>代码:${code}</p>
                <button id="getToken">获取令牌</button>
            `);

            document.getElementById('getToken').addEventListener('click', async () => {
                try {
                    const token = await getAccessToken(tenant_url, oauthState.codeVerifier, code);
                    GM_setValue('access_token', token);

                    // 创建持久性浮窗
                    createCredentialsFloater(tenant_url, token);
                } catch (error) {
                    showMessage(`获取令牌失败: ${error}`);
                }
            });
        }
    }

    function sendChatMessage(tenant_url, token) {
        GM_xmlhttpRequest({
            method: "POST",
            url: `${tenant_url}chat-stream`,
            headers: {
                "Content-Type": "application/json",
                "Authorization": `Bearer ${token}`
            },
            data: JSON.stringify({
                chat_history: [
                    {
                        response_text: "你好! 我是 Augment,很高兴为你提供帮助。",
                        request_message: "你好,我是用户"
                    }
                ],
                message: "我叫什么名字",
                mode: "CHAT"
            }),
            onload: function(response) {
                showMessage(`<h3>聊天响应:</h3><pre>${response.responseText}</pre>`);
            },
            onerror: function(error) {
                showMessage(`发送聊天请求失败: ${error}`);
            }
        });
    }

    // 显示消息的UI函数
    function showMessage(message, autoHide = false) {
        try {
            let messageDiv = document.getElementById('augment-api-message');
            if (!messageDiv) {
                messageDiv = document.createElement('div');
                messageDiv.id = 'augment-api-message';
                messageDiv.style.cssText = `
                    position: fixed;
                    bottom: 90px;
                    right: 20px;
                    background: white;
                    border: 1px solid #ccc;
                    padding: 20px;
                    z-index: 10000;
                    max-width: 400px;
                    box-shadow: 0 0 10px rgba(0,0,0,0.2);
                    border-radius: 5px;
                    font-family: Arial, sans-serif;
                    animation: fadeIn 0.3s;
                `;

                // 添加关闭按钮
                const closeBtn = document.createElement('div');
                closeBtn.style.cssText = `
                    position: absolute;
                    top: 5px;
                    right: 10px;
                    cursor: pointer;
                    font-size: 18px;
                    color: #999;
                `;
                closeBtn.innerHTML = '×';
                closeBtn.onclick = () => {
                    messageDiv.style.display = 'none';
                };

                messageDiv.appendChild(closeBtn);
                document.body.appendChild(messageDiv);

                // 添加样式
                const style = document.createElement('style');
                style.textContent = `
                    @keyframes fadeIn {
                        from { opacity: 0; transform: translateY(20px); }
                        to { opacity: 1; transform: translateY(0); }
                    }
                    #augment-api-message button:hover {
                        opacity: 0.9;
                    }
                `;
                document.head.appendChild(style);
            }

            // 内容容器,避免覆盖关闭按钮
            let contentDiv = messageDiv.querySelector('.message-content');
            if (!contentDiv) {
                contentDiv = document.createElement('div');
                contentDiv.className = 'message-content';
                messageDiv.appendChild(contentDiv);
            }

            contentDiv.innerHTML = message;
            messageDiv.style.display = 'block';

            // 如果设置了自动隐藏
            if (autoHide) {
                setTimeout(() => {
                    messageDiv.style.display = 'none';
                }, 8000);
            }
        } catch (e) {
            console.error('显示消息时出错:', e);
            alert('Augment API Tool: ' + message.replace(/<[^>]*>/g, ' '));
        }
    }

    // 显示JSON处理界面
    function showJsonProcessingUI() {
        showMessage(`
            <h3>处理授权 JSON</h3>
            <p>请在下方粘贴授权码 JSON:</p>
            <textarea id="authCodeInput" style="width:100%;height:100px;margin:10px 0;padding:8px;border:1px solid #ccc;border-radius:4px;"></textarea>
            <button id="processAuthCode" style="background:#4285f4;color:white;padding:10px;border:none;border-radius:4px;cursor:pointer;width:100%;">处理授权码</button>
        `);

        document.getElementById('processAuthCode')?.addEventListener('click', processAuthCode);
    }

    // 处理授权码
    function processAuthCode() {
        const authCodeInput = document.getElementById('authCodeInput');
        const clipText = authCodeInput ? authCodeInput.value : "";

        if (!clipText) {
            alert('请先输入授权码');
            return;
        }

        // 将输入的代码发送到处理函数
        const storedState = GM_getValue('oauthState');
        if (!storedState) {
            showMessage('认证状态丢失,请重新开始流程');
            return;
        }

        console.log("开始处理授权码...");
        console.log("授权码原文:", clipText);

        const oauthState = JSON.parse(storedState);
        console.log("存储的OAuth状态:", oauthState);

        try {
            const parsedCode = parseCode(clipText);
            console.log("解析后的授权码数据:", parsedCode);

            if (parsedCode && parsedCode.code) {
                // 显示处理中的消息
                showMessage(`<h3>正在处理...</h3><p>请稍候</p><pre style="font-size:12px;max-height:200px;overflow:auto;background:#f5f5f5;padding:5px;margin-top:10px;">正在向 ${parsedCode.tenant_url} 请求访问令牌...</pre>`);

                getAccessToken(parsedCode.tenant_url, oauthState.codeVerifier, parsedCode.code)
                                    .then(token => {
                    console.log("成功获取令牌:", token);
                    GM_setValue('access_token', token);

                    // 创建持久性浮窗
                    createCredentialsFloater(parsedCode.tenant_url, token);
                })
                .catch(err => {
                    console.error("获取令牌失败:", err);

                    // 尝试使用code作为token的后备方案
                    const tryUseCodeAsToken = confirm("获取令牌失败。可能是因为授权码无法转换为访问令牌。\n\n您想要尝试直接使用授权码(code)作为令牌吗?");

                    if (tryUseCodeAsToken) {
                        console.log("使用code作为token:", parsedCode.code);
                        createCredentialsFloater(parsedCode.tenant_url, parsedCode.code);
                    } else {
                        showMessage(`获取令牌失败: ${err}`);
                    }
                });
            } else {
                showMessage(`授权码格式不正确,请确保包含code和tenant_url信息`);
            }
        } catch (e) {
            showMessage(`解析授权码失败: ${e.message},请确保输入的是完整的JSON格式授权码`);
        }
    }

    // 注册(不可用)菜单命令
    GM_registerMenuCommand("获取Augment API令牌", startAuthFlow);
    GM_registerMenuCommand("处理授权JSON", showJsonProcessingUI);

    // 确保DOM完全加载后执行
    function init() {
        // 检查是否在授权回调页面
        const currentUrl = window.location.href;
        if (currentUrl.includes('code=') && currentUrl.includes('state=')) {
            handleAuthCallback();
            return;
        }

        // 检查是否在授权码复制页面
        if (currentUrl.includes('augmentcode.com') && document.title.includes('Augment')) {
            // 尝试找到并自动点击复制按钮
            setTimeout(() => {
                const copyButtons = Array.from(document.querySelectorAll('button')).filter(button =>
                    button.textContent && button.textContent.toLowerCase().includes('copy')
                );

                if (copyButtons.length > 0) {
                    console.log('找到复制按钮,自动点击...');
                    copyButtons[0].click();
                }
            }, 1000);
        }

        // 只在 augmentcode.com 域名下显示界面元素
        const isAugmentDomain = window.location.hostname.includes('augmentcode.com');

        if (isAugmentDomain) {
            // 创建固定的悬浮菜单按钮
            createFloatingMenu();
        } else {
            console.log('Augment API Token Tool 已加载 - 仅在 augmentcode.com 显示界面');
        }
    }

    // 创建浮动菜单
    function createFloatingMenu() {
        // 创建菜单按钮
        const menuBtn = document.createElement('div');
        menuBtn.id = 'augment-api-menu-btn';
        menuBtn.style.cssText = `
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: #4285f4;
            color: white;
            width: 50px;
            height: 50px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 20px;
            font-weight: bold;
            cursor: pointer;
            box-shadow: 0 2px 10px rgba(0,0,0,0.2);
            z-index: 9998;
        `;
        menuBtn.textContent = "A";
        menuBtn.title = "Augment API 工具";

        // 创建菜单容器
        const menuContainer = document.createElement('div');
        menuContainer.id = 'augment-api-menu';
        menuContainer.style.cssText = `
            position: fixed;
            bottom: 80px;
            right: 20px;
            background: white;
            border-radius: 5px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.2);
            z-index: 9997;
            display: none;
            overflow: hidden;
        `;

        // 添加菜单项
        const menuItems = [
            {
                text: "1. 登录(不可用)获取授权 JSON",
                action: startAuthFlow
            },
            {
                text: "2. 处理 JSON 信息",
                action: showJsonProcessingUI
            }
        ];

        menuItems.forEach(item => {
            const menuItem = document.createElement('div');
            menuItem.style.cssText = `
                padding: 12px 20px;
                cursor: pointer;
                white-space: nowrap;
                border-bottom: 1px solid #eee;
                color: #000;
            `;
            menuItem.textContent = item.text;
            menuItem.onmouseover = () => menuItem.style.backgroundColor = '#f5f5f5';
            menuItem.onmouseout = () => menuItem.style.backgroundColor = 'white';
            menuItem.onclick = () => {
                menuContainer.style.display = 'none';
                item.action();
            };
            menuContainer.appendChild(menuItem);
        });

        // 点击按钮显示/隐藏菜单
        menuBtn.onclick = () => {
            if (menuContainer.style.display === 'none') {
                menuContainer.style.display = 'block';
            } else {
                menuContainer.style.display = 'none';
            }
        };

        // 点击其他地方隐藏菜单
        document.addEventListener('click', (e) => {
            if (e.target !== menuBtn && !menuContainer.contains(e.target) && menuContainer.style.display === 'block') {
                menuContainer.style.display = 'none';
            }
        });

        // 添加到页面
        document.body.appendChild(menuBtn);
        document.body.appendChild(menuContainer);

        console.log('Augment API Token Tool 已加载');
    }

    // 确保DOM加载完成后执行初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    // 创建固定浮窗显示凭证信息
    function createCredentialsFloater(tenant_url, token) {
        // 删除已有的浮窗(如果存在)
        const existingFloater = document.getElementById('augment-api-floater');
        if (existingFloater) {
            existingFloater.remove();
        }

        // 创建新浮窗
        const floater = document.createElement('div');
        floater.id = 'augment-api-floater';
        floater.style.cssText = `
            position: fixed;
            right: 20px;
            top: 20%;
            width: 350px;
            background: white;
            border: 1px solid #ccc;
            box-shadow: 0 0 10px rgba(0,0,0,0.2);
            border-radius: 5px;
            z-index: 9999;
            font-family: Arial, sans-serif;
            padding: 15px;
        `;

        // 添加标题栏
        const header = document.createElement('div');
        header.style.cssText = `
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
            border-bottom: 1px solid #eee;
            padding-bottom: 10px;
        `;

        const title = document.createElement('h3');
        title.textContent = 'Augment API 凭证';
        title.style.margin = '0';

        const closeBtn = document.createElement('div');
        closeBtn.innerHTML = '×';
        closeBtn.style.cssText = `
            cursor: pointer;
            font-size: 20px;
            color: #999;
        `;
        closeBtn.onclick = () => floater.style.display = 'none';

        header.appendChild(title);
        header.appendChild(closeBtn);
        floater.appendChild(header);

        // 内容区域
        const content = document.createElement('div');

        // tenant_url 部分
        const tenantUrlDiv = document.createElement('div');
        tenantUrlDiv.style.marginBottom = '15px';

        const tenantUrlLabel = document.createElement('div');
        tenantUrlLabel.innerHTML = '<strong>tenant_url:</strong>';
        tenantUrlLabel.style.marginBottom = '5px';

        const tenantUrlValue = document.createElement('div');
        tenantUrlValue.textContent = tenant_url;
        tenantUrlValue.style.cssText = `
            padding: 8px;
            background: #f5f5f5;
            border-radius: 4px;
            word-break: break-all;
            font-family: monospace;
            margin-bottom: 5px;
        `;

        const copyTenantUrlBtn = document.createElement('button');
        copyTenantUrlBtn.textContent = '复制 tenant_url';
        copyTenantUrlBtn.style.cssText = `
            background: #4285f4;
            color: white;
            border: none;
            padding: 5px 10px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        `;
        copyTenantUrlBtn.onclick = () => {
            GM_setClipboard(tenant_url);
            copyTenantUrlBtn.textContent = '✓ 已复制';
            setTimeout(() => {
                copyTenantUrlBtn.textContent = '复制 tenant_url';
            }, 1500);
        };

        tenantUrlDiv.appendChild(tenantUrlLabel);
        tenantUrlDiv.appendChild(tenantUrlValue);
        tenantUrlDiv.appendChild(copyTenantUrlBtn);

        // token 部分
        const tokenDiv = document.createElement('div');

        const tokenLabel = document.createElement('div');
        tokenLabel.innerHTML = '<strong>token:</strong>';
        tokenLabel.style.marginBottom = '5px';

        const tokenValue = document.createElement('div');
        tokenValue.textContent = token;
        tokenValue.style.cssText = `
            padding: 8px;
            background: #f5f5f5;
            border-radius: 4px;
            word-break: break-all;
            font-family: monospace;
            margin-bottom: 5px;
            max-height: 100px;
            overflow-y: auto;
            font-size: 12px;
        `;

        const copyTokenBtn = document.createElement('button');
        copyTokenBtn.textContent = '复制 token';
        copyTokenBtn.style.cssText = `
            background: #4285f4;
            color: white;
            border: none;
            padding: 5px 10px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
        `;
        copyTokenBtn.onclick = () => {
            GM_setClipboard(token);
            copyTokenBtn.textContent = '✓ 已复制';
            setTimeout(() => {
                copyTokenBtn.textContent = '复制 token';
            }, 1500);
        };

        tokenDiv.appendChild(tokenLabel);
        tokenDiv.appendChild(tokenValue);
        tokenDiv.appendChild(copyTokenBtn);

        content.appendChild(tenantUrlDiv);
        content.appendChild(tokenDiv);
        floater.appendChild(content);

        // 添加到页面
        document.body.appendChild(floater);

        // 使浮窗可拖动
        makeElementDraggable(floater, header);

        return floater;
    }

    // 使元素可拖动的函数
    function makeElementDraggable(element, handle) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

        if (handle) {
            // 如果指定了拖动把手,则把手触发拖动事件
            handle.style.cursor = 'move';
            handle.onmousedown = dragMouseDown;
        } else {
            // 否则,整个元素触发拖动事件
            element.onmousedown = dragMouseDown;
        }

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            // 获取鼠标初始位置
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            // 鼠标移动时调用elementDrag
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            // 计算新位置
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            // 设置元素的新位置
            element.style.top = (element.offsetTop - pos2) + "px";
            element.style.left = (element.offsetLeft - pos1) + "px";
        }

        function closeDragElement() {
            // 停止拖动
            document.onmouseup = null;
            document.onmousemove = null;
        }
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址