diff --git "a/clewd.js" "b/clewd.js" --- "a/clewd.js" +++ "b/clewd.js" @@ -1,954 +1,954 @@ -/* -* https://rentry.org/teralomaniac_clewd -* https://github.com/teralomaniac/clewd -*/ -'use strict'; - -const {createServer: Server, IncomingMessage, ServerResponse} = require('node:http'), {createHash: Hash, randomUUID, randomInt, randomBytes} = require('node:crypto'), {TransformStream, ReadableStream} = require('node:stream/web'), {Readable, Writable} = require('node:stream'), {Blob} = require('node:buffer'), {existsSync: exists, writeFileSync: write, createWriteStream} = require('node:fs'), {join: joinP} = require('node:path'), {ClewdSuperfetch: Superfetch, SuperfetchAvailable, SuperfetchFoldersMk, SuperfetchFoldersRm} = require('./lib/clewd-superfetch'), {AI, fileName, genericFixes, bytesToSize, setTitle, checkResErr, Replacements, Main} = require('./lib/clewd-utils'), ClewdStream = require('./lib/clewd-stream'); - -/******************************************************* */ -let currentIndex, Firstlogin = true, changeflag = 0, changing, changetime = 0, totaltime, uuidOrgArray = [], model, cookieModel, tokens, apiKey, timestamp, regexLog, isPro, modelList = []; - -const url = require('url'); -const asyncPool = async (poolLimit, array, iteratorFn) => { - const ret = [], executing = []; - for (const item of array) { - const p = Promise.resolve().then(() => iteratorFn(item)); - ret.push(p); - if (poolLimit <= array.length) { - const e = p.then(() => executing.splice(executing.indexOf(e), 1)); - executing.push(e); - if (executing.length >= poolLimit) await Promise.race(executing); - } - } - return Promise.all(ret); -}, convertToType = value => { - if (value === 'true') return true; - if (value === 'false') return false; - if (/^\d+$/.test(value)) return parseInt(value); - return value; -}, CookieChanger = (resetTimer = true, cleanup = false) => { - if (Config.CookieArray?.length <= 1) { - return changing = false; - } else { - changeflag = 0, changing = true; - if(!cleanup) { - currentIndex = (currentIndex + 1) % Config.CookieArray.length; - console.log(`Changing Cookie...\n`); - } - setTimeout(() => { - onListen(); - resetTimer && (timestamp = Date.now()); - }, !Config.rProxy || Config.rProxy === AI.end() ? 15000 + timestamp - Date.now() : 0); - } -}, CookieCleaner = (flag, percentage) => { - Config.WastedCookie.push(flag + '@' + Config.CookieArray[currentIndex].split('@').toReversed()[0]); - Config.CookieArray.splice(currentIndex, 1), Config.Cookie = ''; - Config.Cookiecounter < 0 && console.log(`[progress]: ${percentage.toFixed(2)}%\n[length]: ${Config.CookieArray.length}\n`); - console.log(`Cleaning Cookie...\n`); - writeSettings(Config); - return CookieChanger(true, true); -}, padtxt = content => { - const {countTokens} = require('@anthropic-ai/tokenizer'); - tokens = countTokens(content); - const padtxt = String(Config.Settings.padtxt).split(',').reverse(), maxtokens = parseInt(padtxt[0]), extralimit = parseInt(padtxt[1]) || 1000, minlimit = parseInt(padtxt[2]); - const placeholder = (tokens > maxtokens - extralimit && minlimit ? Config.placeholder_byte : Config.placeholder_token) || randomBytes(randomInt(5, 15)).toString('hex'); - const placeholdertokens = countTokens(placeholder.trim()); - for (let match; match = content.match(/<\|padtxt.*?(\d+)t.*?\|>/); content = content.replace(match[0], placeholder.repeat(parseInt(match[1]) / placeholdertokens))) tokens += parseInt(match[1]); - if(/<\|padtxt off.*?\|>/.test(content)) return content.replace(/\s*<\|padtxt.*?\|>\s*/g, '\n\n'); - const padding = placeholder.repeat(Math.min(maxtokens, (tokens <= maxtokens - extralimit ? maxtokens - tokens : minlimit ? minlimit : extralimit)) / placeholdertokens); - content = /<\|padtxt.*?\|>/.test(content) ? content.replace(/<\|padtxt.*?\|>/, padding).replace(/\s*<\|padtxt.*?\|>\s*/g, '\n\n') : !apiKey ? padding + '\n\n\n' + content.trim() : content; - return content; -}, xmlPlot_merge = (content, mergeTag, nonsys) => { - if (/(\n\n|^\s*)xmlPlot:\s*/.test(content)) { - content = (nonsys ? content : content.replace(/(\n\n|^\s*)(? { - let matches = content.match(new RegExp(` *"(/?)(.*)\\1(.*?)" *: *"(.*?)" *`, 'gm')); - matches && matches.forEach(match => { - try { - const reg = / *"(\/?)(.*)\1(.*?)" *: *"(.*?)" *<\/regex>/.exec(match); - regexLog += match + '\n'; - content = content.replace(new RegExp(reg[2], reg[3]), JSON.parse(`"${reg[4].replace(/\\?"/g, '\\"')}"`)); - } catch (err) { - console.log(`Regex error: ` + match + '\n' + err); - } - }); - return content; -}, xmlPlot = (content, nonsys = false) => { - regexLog = ''; - //一次正则 - content = xmlPlot_regex(content, 1); - //一次role合并 - const mergeTag = { - all: !content.includes('<|Merge Disable|>'), - system: !content.includes('<|Merge System Disable|>'), - human: !content.includes('<|Merge Human Disable|>'), - assistant: !content.includes('<|Merge Assistant Disable|>') - }; - content = xmlPlot_merge(content, mergeTag, nonsys); - //自定义插入 - let splitContent = content.split(/\n\n(?=Assistant:|Human:)/g), match; - while ((match = /<@(\d+)>(.*?)<\/@\1>/gs.exec(content)) !== null) { - let index = splitContent.length - parseInt(match[1]) - 1; - index >= 0 && (splitContent[index] += '\n\n' + match[2]); - content = content.replace(match[0], ''); - } - content = splitContent.join('\n\n').replace(/<@(\d+)>.*?<\/@\1>/gs, ''); - //二次正则 - content = xmlPlot_regex(content, 2); - //二次role合并 - content = xmlPlot_merge(content, mergeTag, nonsys); - //Plain Prompt - let segcontentHuman = content.split('\n\nHuman:'); - let segcontentlastIndex = segcontentHuman.length - 1; - if (!apiKey && segcontentlastIndex >= 2 && segcontentHuman[segcontentlastIndex].includes('<|Plain Prompt Enable|>') && !content.includes('\n\nPlainPrompt:')) { - content = segcontentHuman.slice(0, segcontentlastIndex).join('\n\nHuman:') + '\n\nPlainPrompt:' + segcontentHuman.slice(segcontentlastIndex).join('\n\nHuman:').replace(/\n\nHuman: *PlainPrompt:/, '\n\nPlainPrompt:'); - } - //三次正则 - content = xmlPlot_regex(content, 3); - //消除空XML tags、两端空白符和多余的\n - content = content.replace(/.*?<\/regex>/gm, '') - .replace(/\r\n|\r/gm, '\n') - .replace(/\s*<\|curtail\|>\s*/g, '\n') - .replace(/\s*<\|join\|>\s*/g, '') - .replace(/\s*<\|space\|>\s*/g, ' ') - .replace(/\s*\n\n(H(uman)?|A(ssistant)?): +/g, '\n\n$1: ') - .replace(/<\|(\\.*?)\|>/g, function(match, p1) { - try { - return JSON.parse(`"${p1.replace(/\\?"/g, '\\"')}"`); - } catch { return match } - }); - //确保格式正确 - if (apiKey) { - content = content.replace(/(\n\nHuman:(?!.*?\n\nAssistant:).*?|(?\s*(.*?)(?:\n\nAssistant:\s*)?$/s, '\n\n$1'); - content.includes('<|reverseHA|>') && (content = content.replace(/\s*<\|reverseHA\|>\s*/g, '\n\n').replace(/Assistant|Human/g, function(match) {return match === 'Human' ? 'Assistant' : 'Human'}).replace(/\n(A|H): /g, function(match, p1) {return p1 === 'A' ? '\nH: ' : '\nA: '})); - return content.replace(Config.Settings.padtxt ? /\s*<\|(?!padtxt).*?\|>\s*/g : /\s*<\|.*?\|>\s*/g, '\n\n').trim().replace(/^.+:/, '\n\n$&').replace(/(?<=\n)\n(?=\n)/g, ''); - } else { - return content.replace(Config.Settings.padtxt ? /\s*<\|(?!padtxt).*?\|>\s*/g : /\s*<\|.*?\|>\s*/g, '\n\n').trim().replace(/^Human: *|\n\nAssistant: *$/g, '').replace(/(?<=\n)\n(?=\n)/g, ''); - } -}, waitForChange = () => { - return new Promise(resolve => { - const interval = setInterval(() => { - if (!changing) { - clearInterval(interval); - resolve(); - } - }, 100); - }); -}; -/******************************************************* */ - -let ChangedSettings, UnknownSettings, Logger; - -const ConfigPath = joinP(__dirname, './config.js'), LogPath = joinP(__dirname, './log.txt'), Conversation = { - char: null, - uuid: null, - depth: 0 -}, cookies = {}; - -let uuidOrg, curPrompt = {}, prevPrompt = {}, prevMessages = [], prevImpersonated = false, Config = { - Cookie: '', - CookieArray: [], - WastedCookie: [], - unknownModels: [], - Cookiecounter: 3, - CookieIndex: 0, - ProxyPassword: '', - Ip: (process.env.Cookie || process.env.CookieArray) ? '0.0.0.0' : '127.0.0.1', - Port: process.env.PORT || 8444, - localtunnel: false, - BufferSize: 1, - SystemInterval: 3, - rProxy: '', - api_rProxy: '', - placeholder_token: '', - placeholder_byte: '', - PromptExperimentFirst: '', - PromptExperimentNext: '', - PersonalityFormat: '{{char}}\'s personality: {{personality}}', - ScenarioFormat: 'Dialogue scenario: {{scenario}}', - Settings: { - RenewAlways: true, - RetryRegenerate: false, - PromptExperiments: true, - SystemExperiments: true, - PreventImperson: true, - AllSamples: false, - NoSamples: false, - StripAssistant: false, - StripHuman: false, - PassParams: true, - ClearFlags: true, - PreserveChats: false, - LogMessages: true, - FullColon: true, - padtxt: "1000,1000,15000", - xmlPlot: true, - SkipRestricted: false, - Artifacts: false, - Superfetch: true - } -}; - -ServerResponse.prototype.json = async function(body, statusCode = 200, headers) { - body = body instanceof Promise ? await body : body; - this.headersSent || this.writeHead(statusCode, { - 'Content-Type': 'application/json', - ...headers && headers - }); - this.end('object' == typeof body ? JSON.stringify(body) : body); - return this; -}; - -Array.prototype.sample = function() { - return this[Math.floor(Math.random() * this.length)]; -}; - -const updateParams = res => { - updateCookies(res); -}, updateCookies = res => { - let cookieNew = ''; - res instanceof Response ? cookieNew = res.headers?.get('set-cookie') : res?.superfetch ? cookieNew = res.headers?.['set-cookie'] : 'string' == typeof res && (cookieNew = res.split('\n').join('')); - if (!cookieNew) { - return; - } - let cookieArr = cookieNew.split(/;\s?/gi).filter((prop => false === /^(path|expires|domain|HttpOnly|Secure|SameSite)[=;]*/i.test(prop))); - for (const cookie of cookieArr) { - const divide = cookie.split(/^(.*?)=\s*(.*)/), cookieName = divide[1], cookieVal = divide[2]; - cookies[cookieName] = cookieVal; - } -}, getCookies = () => { - const cookieNames = Object.keys(cookies); - return cookieNames.map(((name, idx) => `${name}=${cookies[name]}${idx === cookieNames.length - 1 ? '' : ';'}`)).join(' ').replace(/(\s+)$/gi, ''); -}, deleteChat = async uuid => { - if (!uuid) { - return; - } - if (uuid === Conversation.uuid) { - Conversation.uuid = null; - Conversation.depth = 0; - } - if (Config.Settings.PreserveChats) { - return; - } - const res = await (Config.Settings.Superfetch ? Superfetch : fetch)(`${Config.rProxy || AI.end()}/api/organizations/${uuidOrg}/chat_conversations/${uuid}`, { - headers: { - ...AI.hdr(), - Cookie: getCookies() - }, - method: 'DELETE' - }); - updateParams(res); -}, onListen = async () => { -/***************************** */ - if (Firstlogin) { - Firstlogin = false, timestamp = Date.now(), totaltime = Config.CookieArray.length; - console.log(`${Main}\nhttp://${Config.Ip}:${Config.Port}/v1\n\n${Object.keys(Config.Settings).map((setting => UnknownSettings?.includes(setting) ? `??? ${setting}: ${Config.Settings[setting]}` : `${setting}: ${ChangedSettings?.includes(setting) ? '' : ''}${Config.Settings[setting]}`)).sort().join('\n')}\n`); //↓ - if (Config.Settings.Superfetch) { - SuperfetchAvailable(true); - SuperfetchFoldersMk(); - } - if (Config.localtunnel) { - const localtunnel = require('localtunnel'); - localtunnel({ port: Config.Port }).then((tunnel) => { - console.log(`\nTunnel URL for outer websites: ${tunnel.url}/v1\n`); - }) - } - } - if (Config.CookieArray?.length > 0) { - const cookieInfo = /(?:(claude[-_][a-z0-9-_]*?)@)?(?:sessionKey=)?(sk-ant-sid01-[\w-]{86}-[\w-]{6}AA)/.exec(Config.CookieArray[currentIndex]); - cookieInfo?.[2] && (Config.Cookie = 'sessionKey=' + cookieInfo[2]); - changetime++; - if (model && cookieInfo?.[1] && !/claude[\w]*?_pro/.test(cookieInfo?.[1]) && cookieInfo?.[1] != model) return CookieChanger(false); - } - let percentage = ((changetime + Math.max(Config.CookieIndex - 1, 0)) / totaltime) * 100 - if (Config.Cookiecounter < 0 && percentage > 100) { - console.log(`\n※※※Cookie cleanup completed※※※\n\n`); - return process.exit(); - } - try { -/***************************** */ - if ('SET YOUR COOKIE HERE' === Config.Cookie || Config.Cookie?.length < 1) { - return changing = false, console.log(`No cookie available, enter apiKey-only mode.\n`); //throw Error('Set your cookie inside config.js'); - } - updateCookies(Config.Cookie); -/**************************** */ - const bootstrapRes = await (Config.Settings.Superfetch ? Superfetch : fetch)((Config.rProxy || AI.end()) + `/api/bootstrap`, { - method: 'GET', - headers: { - ...AI.hdr(), - Cookie: getCookies() - } - }); - await checkResErr(bootstrapRes); - const bootstrap = await bootstrapRes.json(); - if (bootstrap.account === null) { - console.log(`Null!`); - return CookieCleaner('Null', percentage); - } - const bootAccInfo = bootstrap.account.memberships.find(item => item.organization.capabilities.includes('chat')).organization; - cookieModel = bootstrap.statsig.values.layer_configs["HPOHwBLNLQLxkj5Yn4bfSkgCQnBX28kPR7h/BNKdVLw="]?.value?.console_default_model_override?.model || bootstrap.statsig.values.dynamic_configs["6zA9wvTedwkzjLxWy9PVe7yydI00XDQ6L5Fejjq/2o8="]?.value?.model; - isPro = bootAccInfo.capabilities.includes('claude_pro') && 'claude_pro' || bootAccInfo.capabilities.includes('raven') && 'claude_team_pro'; - const unknown = cookieModel && !(AI.mdl().includes(cookieModel) || Config.unknownModels.includes(cookieModel)); - if (Config.CookieArray?.length > 0 && (isPro || cookieModel) != Config.CookieArray[currentIndex].split('@')[0] || unknown) { - Config.CookieArray[currentIndex] = (isPro || cookieModel) + '@' + Config.Cookie; - unknown && Config.unknownModels.push(cookieModel); - writeSettings(Config); - } - if (!isPro && model && model != cookieModel) return CookieChanger(); - console.log(Config.CookieArray?.length > 0 ? `(index: ${currentIndex + 1 || Config.CookieArray.length}) Logged in %o` : 'Logged in %o', { //console.log('Logged in %o', { ↓ - name: bootAccInfo.name?.split('@')?.[0], - mail: bootstrap.account.email_address, // - cookieModel, // - capabilities: bootAccInfo.capabilities - }); //↓ - if (uuidOrgArray.includes(bootAccInfo.uuid) && percentage <= 100 && Config.CookieArray?.length > 0 || bootAccInfo.api_disabled_reason && !bootAccInfo.api_disabled_until || !bootstrap.account.completed_verification_at) { - const flag = bootAccInfo.api_disabled_reason ? 'Disabled' : !bootstrap.account.completed_verification_at ? 'Unverified' : 'Overlap'; - console.log(`${flag}!`); - return CookieCleaner(flag, percentage); - } else uuidOrgArray.push(bootAccInfo.uuid); - if (Config.Cookiecounter < 0) { - console.log(`[progress]: ${percentage.toFixed(2)}%\n[length]: ${Config.CookieArray.length}\n`); - return CookieChanger(); - } -/**************************** */ - const accRes = await (Config.Settings.Superfetch ? Superfetch : fetch)((Config.rProxy || AI.end()) + '/api/organizations', { - method: 'GET', - headers: { - ...AI.hdr(), - Cookie: getCookies() - } - }); - await checkResErr(accRes); - const accInfo = (await accRes.json())?.find(item => item.capabilities.includes('chat')); //const accInfo = (await accRes.json())?.[0];\nif (!accInfo || accInfo.error) {\n throw Error(`Couldn't get account info: "${accInfo?.error?.message || accRes.statusText}"`);\n}\nif (!accInfo?.uuid) {\n throw Error('Invalid account id');\n} - setTitle('ok'); - updateParams(accRes); - uuidOrg = accInfo?.uuid; - if (accInfo?.active_flags.length > 0) { - let banned = false; // - const now = new Date, formattedFlags = accInfo.active_flags.map((flag => { - const days = ((new Date(flag.expires_at).getTime() - now.getTime()) / 864e5).toFixed(2); - 'consumer_banned' === flag.type && (banned = true); // - return { - type: flag.type, - remaining_days: days - }; - })); - console.warn(`${banned ? '' : ''}Your account has warnings %o`, formattedFlags); //console.warn('Your account has warnings %o', formattedFlags); - await Promise.all(accInfo.active_flags.map((flag => (async type => { - if (!Config.Settings.ClearFlags) { - return; - } - if ('consumer_restricted_mode' === type || 'consumer_banned' === type) { //if ('consumer_restricted_mode' === type) { - return; - } - const req = await (Config.Settings.Superfetch ? Superfetch : fetch)(`${Config.rProxy || AI.end()}/api/organizations/${uuidOrg}/flags/${type}/dismiss`, { - headers: { - ...AI.hdr(), - Cookie: getCookies() - }, - method: 'POST' - }); - updateParams(req); - const json = await req.json(); - console.log(`${type}: ${json.error ? json.error.message || json.error.type || json.detail : 'OK'}`); - })(flag.type)))); - console.log(`${banned ? 'Banned' : 'Restricted'}!`); // - if (banned) return CookieCleaner('Banned') // - else if (Config.Settings.SkipRestricted) return CookieChanger(); // - } - if (bootstrap.account.settings.preview_feature_uses_artifacts != Config.Settings.Artifacts) { - const settingsRes = await (Config.Settings.Superfetch ? Superfetch : fetch)((Config.rProxy || AI.end()) + `/api/account`, { - method: 'PUT', - headers: { - ...AI.hdr(), - Cookie: getCookies() - }, - body: JSON.stringify({ settings: Object.assign(bootstrap.account.settings, { preview_feature_uses_artifacts: Config.Settings.Artifacts }) }), - }); - await checkResErr(settingsRes); - updateParams(settingsRes); - } - changing = false; - const convRes = await (Config.Settings.Superfetch ? Superfetch : fetch)(`${Config.rProxy || AI.end()}/api/organizations/${accInfo.uuid}/chat_conversations`, { //const convRes = await fetch(`${Config.rProxy || AI.end()}/api/organizations/${uuidOrg}/chat_conversations`, { - method: 'GET', - headers: { - ...AI.hdr(), - Cookie: getCookies() - } - }), conversations = await convRes.json(); - updateParams(convRes); - conversations.length > 0 && await asyncPool(10, conversations, async (conv) => await deleteChat(conv.uuid)); //await Promise.all(conversations.map((conv => deleteChat(conv.uuid)))); -/***************************** */ - } catch (err) { - if (err.message === 'Invalid authorization') { - console.log(`Invalid!`); - return CookieCleaner('Invalid', percentage); - } - console.error('Clewd:\n%o', err); - CookieChanger(); - } -/***************************** */ -}, writeSettings = async (config, firstRun = false) => { - if (process.env.Cookie || process.env.CookieArray) return; // - write(ConfigPath, `/*\n* https://rentry.org/teralomaniac_clewd\n* https://github.com/teralomaniac/clewd\n*/\n\n// SET YOUR COOKIE BELOW\n\nmodule.exports = ${JSON.stringify(config, null, 4)}\n\n/*\n BufferSize\n * How many characters will be buffered before the AI types once\n * lower = less chance of \`PreventImperson\` working properly\n\n ---\n\n SystemInterval\n * How many messages until \`SystemExperiments alternates\`\n\n ---\n\n Other settings\n * https://gitgud.io/ahsk/clewd/#defaults\n * and\n * https://gitgud.io/ahsk/clewd/-/blob/master/CHANGELOG.md\n */`.trim().replace(/((? { - if ('OPTIONS' === req.method) { - return ((req, res) => { - res.writeHead(200, { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'Authorization, Content-Type', - 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS' - }).end(); - })(0, res); - } - const URL = url.parse(req.url.replace(/\/v1(\?.*)\$(\/.*)$/, '/v1$2$1'), true); - const api_rProxy = URL.query?.api_rProxy || Config.api_rProxy; - req.url = URL.pathname; - switch (req.url) { - case '/v1/models': -/***************************** */ - (async (req, res) => { - let models; - if (/oaiKey:/.test(req.headers.authorization)) { - try { - const modelsRes = await fetch(api_rProxy.replace(/(\/v1)?\/? *$/, '') + '/v1/models', { - method: 'GET', - headers: { authorization: req.headers.authorization.match(/(?<=oaiKey:).*/)?.[0].split(',')[0].trim() } - }); - models = await modelsRes.json(); - } catch(err) {} - } - res.json({ - data: [ - ...AI.mdl().concat(Config.unknownModels).map((name => ({ id: name }))) - ].concat(models?.data).reduce((acc, current, index) => { - index === 0 && modelList.splice(0); - if (current?.id && acc.every(model => model.id != current.id)) { - acc.push(current); - modelList.push(current.id); - } - return acc; - }, []) - }); - })(req, res); //res.json({\n data: AI.mdl().map((name => ({\n id: name\n })))\n}); -/***************************** */ - break; - - case '/v1/chat/completions': - ((req, res) => { - setTitle('recv...'); - let fetchAPI; - const abortControl = new AbortController, {signal} = abortControl; - res.socket.on('close', (async () => { - abortControl.signal.aborted || abortControl.abort(); - })); - const buffer = []; - req.on('data', (chunk => { - buffer.push(chunk); - })); - req.on('end', (async () => { - let clewdStream, titleTimer, samePrompt = false, shouldRenew = true, retryRegen = false, exceeded_limit = false, nochange = false; //let clewdStream, titleTimer, samePrompt = false, shouldRenew = true, retryRegen = false; - try { - const body = JSON.parse(Buffer.concat(buffer).toString()); - let {temperature} = body; - temperature = typeof temperature === 'number' ? Math.max(.1, Math.min(1, temperature)) : undefined; //temperature = Math.max(.1, Math.min(1, temperature)); - let {messages} = body; -/************************* */ - const thirdKey = req.headers.authorization?.match(/(?<=(3rd|oai)Key:).*/), oaiAPI = /oaiKey:/.test(req.headers.authorization), forceModel = /--force/.test(body.model); - apiKey = thirdKey?.[0].split(',').map(item => item.trim()) || req.headers.authorization?.match(/sk-ant-api\d\d-[\w-]{86}-[\w-]{6}AA/g); - model = apiKey || forceModel || isPro ? body.model.replace(/--force/, '').trim() : cookieModel; - let max_tokens_to_sample = body.max_tokens, stop_sequences = body.stop, top_p = typeof body.top_p === 'number' ? body.top_p : undefined, top_k = typeof body.top_k === 'number' ? body.top_k : undefined; - if (!apiKey && (Config.ProxyPassword != '' && req.headers.authorization != 'Bearer ' + Config.ProxyPassword || !uuidOrg)) { - throw Error(uuidOrg ? 'ProxyPassword Wrong' : 'No cookie available or apiKey format wrong'); - } else if (!changing && !apiKey && (!isPro && model != cookieModel)) CookieChanger(); - await waitForChange(); -/************************* */ - if (messages?.length < 1) { - throw Error('Select OpenAI as completion source'); - } - if (!body.stream && 1 === messages.length && JSON.stringify(messages.sort() || []) === JSON.stringify([ { - role: 'user', - content: 'Hi' - } ].sort())) { - return res.json({ - choices: [ { - message: { - content: Main - } - } ] - }); - } - res.setHeader('Access-Control-Allow-Origin', '*'); - body.stream && res.setHeader('Content-Type', 'text/event-stream'); - if (!body.stream && messages?.[0]?.content?.startsWith('From the list below, choose a word that best represents a character\'s outfit description, action, or emotion in their dialogue')) { - return res.json({ - choices: [ { - message: { - content: 'neutral' - } - } ] - }); - } - if (Config.Settings.AllSamples && Config.Settings.NoSamples) { - console.log('having AllSamples and NoSamples both set to true is not supported'); - throw Error('Only one can be used at the same time: AllSamples/NoSamples'); - } - //const model = body.model;//if (model === AI.mdl()[0]) {// return;//} - if (!modelList.includes(model) && !/claude-.*/.test(model) && !forceModel) { - throw Error('Invalid model selected: ' + model); - } - curPrompt = { - firstUser: messages.find((message => 'user' === message.role)), - firstSystem: messages.find((message => 'system' === message.role)), - firstAssistant: messages.find((message => 'assistant' === message.role)), - lastUser: messages.findLast((message => 'user' === message.role)), - lastSystem: messages.findLast((message => 'system' === message.role && '[Start a new chat]' !== message.content)), - lastAssistant: messages.findLast((message => 'assistant' === message.role)) - }; - prevPrompt = { - ...prevMessages.length > 0 && { - firstUser: prevMessages.find((message => 'user' === message.role)), - firstSystem: prevMessages.find((message => 'system' === message.role)), - firstAssistant: prevMessages.find((message => 'assistant' === message.role)), - lastUser: prevMessages.findLast((message => 'user' === message.role)), - lastSystem: prevMessages.find((message => 'system' === message.role && '[Start a new chat]' !== message.content)), - lastAssistant: prevMessages.findLast((message => 'assistant' === message.role)) - } - }; - samePrompt = JSON.stringify(messages.filter((message => 'system' !== message.role)).sort()) === JSON.stringify(prevMessages.filter((message => 'system' !== message.role)).sort()); - const sameCharDiffChat = !samePrompt && curPrompt.firstSystem?.content === prevPrompt.firstSystem?.content && curPrompt.firstUser?.content !== prevPrompt.firstUser?.content; - shouldRenew = Config.Settings.RenewAlways || !Conversation.uuid || prevImpersonated || !Config.Settings.RenewAlways && samePrompt || sameCharDiffChat; - retryRegen = Config.Settings.RetryRegenerate && samePrompt && null != Conversation.uuid; - samePrompt || (prevMessages = JSON.parse(JSON.stringify(messages))); - let type = ''; - if (apiKey) { type = 'api'; } else if (retryRegen) { //if (retryRegen) { - type = 'R'; - fetchAPI = await (async (signal, model) => { - let res; - const body = { - prompt: '', - parent_message_uuid: '', - timezone: AI.zone(), - attachments: [], - files: [], - rendering_mode: 'raw' - }; - let headers = { - ...AI.hdr(Conversation.uuid || ''), - Accept: 'text/event-stream', - Cookie: getCookies() - }; - if (Config.Settings.Superfetch) { - const names = Object.keys(headers), values = Object.values(headers); - headers = names.map(((header, idx) => `${header}: ${values[idx]}`)); - } - res = await (Config.Settings.Superfetch ? Superfetch : fetch)((Config.rProxy || AI.end()) + `/api/organizations/${uuidOrg || ''}/chat_conversations/${Conversation.uuid || ''}/retry_completion`, { - stream: true, - signal, - method: 'POST', - body: JSON.stringify(body), - headers - }); - updateParams(res); - await checkResErr(res); - return res; - })(signal, model); - } else if (shouldRenew) { - Conversation.uuid && await deleteChat(Conversation.uuid); - fetchAPI = await (async signal => { - Conversation.uuid = randomUUID().toString(); - Conversation.depth = 0; - const res = await (Config.Settings.Superfetch ? Superfetch : fetch)(`${Config.rProxy || AI.end()}/api/organizations/${uuidOrg}/chat_conversations`, { - signal, - headers: { - ...AI.hdr(), - Cookie: getCookies() - }, - method: 'POST', - body: JSON.stringify({ - uuid: Conversation.uuid, - name: '' - }) - }); - updateParams(res); - await checkResErr(res); - return res; - })(signal); - type = 'r'; - } else if (samePrompt) {} else { - const systemExperiment = !Config.Settings.RenewAlways && Config.Settings.SystemExperiments; - if (!systemExperiment || systemExperiment && Conversation.depth >= Config.SystemInterval) { - type = 'c-r'; - Conversation.depth = 0; - } else { - type = 'c-c'; - Conversation.depth++; - } - } - let {prompt, systems} = ((messages, type) => { - const rgxScenario = /^\[Circumstances and context of the dialogue: ([\s\S]+?)\.?\]$/i, rgxPerson = /^\[([\s\S]+?)'s personality: ([\s\S]+?)\]$/i, messagesClone = JSON.parse(JSON.stringify(messages)), realLogs = messagesClone.filter((message => [ 'user', 'assistant' ].includes(message.role))), sampleLogs = messagesClone.filter((message => message.name)), mergedLogs = [ ...sampleLogs, ...realLogs ]; - mergedLogs.forEach(((message, idx) => { - const next = mergedLogs[idx + 1]; - message.customname = (message => [ 'assistant', 'user' ].includes(message.role) && null != message.name && !(message.name in Replacements))(message); - if (next && !Config.Settings.xmlPlot) { //if (next) { - if ('name' in message && 'name' in next) { - if (message.name === next.name) { - message.content += '\n' + next.content; - next.merged = true; - } - } else if ('system' !== next.role) { - if (next.role === message.role) { - message.content += '\n' + next.content; - next.merged = true; - } - } else { - message.content += '\n' + next.content; - next.merged = true; - } - } - })); - const lastAssistant = realLogs.findLast((message => !message.merged && 'assistant' === message.role)); - lastAssistant && Config.Settings.StripAssistant && (lastAssistant.strip = true); - const lastUser = realLogs.findLast((message => !message.merged && 'user' === message.role)); - lastUser && Config.Settings.StripHuman && (lastUser.strip = true); - const systemMessages = messagesClone.filter((message => 'system' === message.role && !('name' in message))); - systemMessages.forEach(((message, idx) => { - const scenario = message.content.match(rgxScenario)?.[1], personality = message.content.match(rgxPerson); - if (scenario) { - message.content = Config.ScenarioFormat.replace(/{{scenario}}/gim, scenario); - message.scenario = true; - } - if (3 === personality?.length) { - message.content = Config.PersonalityFormat.replace(/{{char}}/gim, personality[1]).replace(/{{personality}}/gim, personality[2]); - message.personality = true; - } - message.main = 0 === idx; - message.jailbreak = idx === systemMessages.length - 1; - ' ' === message.content && (message.discard = true); - })); - Config.Settings.AllSamples && !Config.Settings.NoSamples && realLogs.forEach((message => { - if (![ lastUser, lastAssistant ].includes(message)) { - if ('user' === message.role) { - message.name = message.customname ? message.name : 'example_user'; - message.role = 'system'; - } else if ('assistant' === message.role) { - message.name = message.customname ? message.name : 'example_assistant'; - message.role = 'system'; - } else if (!message.customname) { - throw Error('Invalid role ' + message.name); - } - } - })); - Config.Settings.NoSamples && !Config.Settings.AllSamples && sampleLogs.forEach((message => { - if ('example_user' === message.name) { - message.role = 'user'; - } else if ('example_assistant' === message.name) { - message.role = 'assistant'; - } else if (!message.customname) { - throw Error('Invalid role ' + message.name); - } - message.customname || delete message.name; - })); - let systems = []; - if (![ 'r', 'R', 'api' ].includes(type)) { - lastUser.strip = true; - systemMessages.forEach((message => message.discard = message.discard || 'c-c' === type ? !message.jailbreak : !message.jailbreak && !message.main)); - systems = systemMessages.filter((message => !message.discard)).map((message => `"${message.content.substring(0, 25).replace(/\n/g, '\\n').trim()}..."`)); - messagesClone.forEach((message => message.discard = message.discard || mergedLogs.includes(message) && ![ lastUser ].includes(message))); - } - const prompt = messagesClone.map(((message, idx) => { - if (message.merged || message.discard) { - return ''; - } - if (message.content.length < 1) { - return message.content; - } - let spacing = ''; -/******************************** */ - if (Config.Settings.xmlPlot) { - idx > 0 && (spacing = '\n\n'); - const prefix = message.customname ? message.role + ': ' + message.name.replaceAll('_', ' ') + ': ' : 'system' !== message.role || message.name ? Replacements[message.name || message.role] + ': ' : 'xmlPlot: ' + Replacements[message.role]; - return `${spacing}${message.strip ? '' : prefix}${message.content}`; - } else { -/******************************** */ - idx > 0 && (spacing = systemMessages.includes(message) ? '\n' : '\n\n'); - const prefix = message.customname ? message.name.replaceAll('_', ' ') + ': ' : 'system' !== message.role || message.name ? Replacements[message.name || message.role] + ': ' : '' + Replacements[message.role]; - return `${spacing}${message.strip ? '' : prefix}${'system' === message.role ? message.content : message.content.trim()}`; - } // - })); - return { - prompt: prompt.join(''), //genericFixes(prompt.join('')).trim(), - systems - }; - })(messages, type); -/******************************** */ - const legacy = /claude-([12]|instant)/i.test(model), messagesAPI = thirdKey || !legacy && !/<\|completeAPI\|>/.test(prompt) || /<\|messagesAPI\|>/.test(prompt), messagesLog = /<\|messagesLog\|>/.test(prompt), fusion = apiKey && messagesAPI && /<\|Fusion Mode\|>/.test(prompt), wedge = '\r'; - const stopSet = /<\|stopSet *(\[.*?\]) *\|>/.exec(prompt)?.[1], stopRevoke = /<\|stopRevoke *(\[.*?\]) *\|>/.exec(prompt)?.[1]; - if (stop_sequences || stopSet || stopRevoke) stop_sequences = JSON.parse(stopSet || '[]').concat(stop_sequences).concat(['\n\nHuman:', '\n\nAssistant:']).filter(item => !JSON.parse(stopRevoke || '[]').includes(item) && item); - apiKey && (type = oaiAPI ? 'oai_api' : messagesAPI ? 'msg_api' : type); - prompt = Config.Settings.xmlPlot ? xmlPlot(prompt, legacy && !/claude-2\.1/i.test(model)) : apiKey ? `\n\nHuman: ${genericFixes(prompt)}\n\nAssistant:` : genericFixes(prompt).trim(); - Config.Settings.FullColon && (prompt = !legacy ? - prompt.replace(fusion ? /\n(?!\nAssistant:\s*$)(?=\n(Human|Assistant):)/gs : apiKey ? /(? 0 ? ' ' + systems.join(' / ') : ''}`); - 'R' !== type || prompt || (prompt = '...regen...'); - Logger?.write(`\n\n-------\n[${(new Date).toLocaleString()}]\n${Main}\n####### ${model} (${type})\n${JSON.stringify({FusionMode: fusion, PassParams: Config.Settings.PassParams, stop_sequences, temperature, top_k, top_p}, null, 2)}\n\n####### regex:\n${regexLog}\n####### PROMPT ${tokens}t:\n${prompt}\n--\n####### REPLY:\n`); //Logger?.write(`\n\n-------\n[${(new Date).toLocaleString()}]\n####### MODEL: ${model}\n####### PROMPT (${type}):\n${prompt}\n--\n####### REPLY:\n`); - retryRegen || (fetchAPI = await (async (signal, model, prompt, temperature, type) => { -/******************************** */ - if (apiKey) { - let messages, system, key = apiKey[Math.floor(Math.random() * apiKey.length)]; - if (messagesAPI) { - const rounds = prompt.replace(/^(?!.*\n\nHuman:)/s, '\n\nHuman:').split('\n\nHuman:'); - messages = rounds.slice(1).flatMap(round => { - const turns = round.split('\n\nAssistant:'); - return [{role: 'user', content: turns[0].trim()}].concat(turns.slice(1).flatMap(turn => [{role: 'assistant', content: turn.trim()}])); - }).reduce((acc, current) => { - if (Config.Settings.FullColon && acc.length > 0 && (acc[acc.length - 1].role === current.role || !acc[acc.length - 1].content)) { - acc[acc.length - 1].content += (current.role === 'user' ? 'Human' : 'Assistant').replace(/.*/, legacy ? '\n$&﹕ ' : '\n' + wedge + '\n$&: ') + current.content; - } else acc.push(current); - return acc; - }, []).filter(message => message.content), oaiAPI ? messages.unshift({role: 'system', content: rounds[0].trim()}) : system = rounds[0].trim(); - messagesLog && console.log({system, messages}); - } - const res = await fetch((api_rProxy || 'https://api.anthropic.com').replace(/(\/v1)? *$/, thirdKey ? '$1' : '/v1').trim('/') + (oaiAPI ? '/chat/completions' : messagesAPI ? '/messages' : '/complete'), { - method: 'POST', - signal, - headers: { - 'anthropic-version': '2023-06-01', - 'authorization': 'Bearer ' + key, - 'Content-Type': 'application/json', - 'User-Agent': AI.agent(), - 'x-api-key': key, - }, - body: JSON.stringify({ - ...oaiAPI || messagesAPI ? { - max_tokens : max_tokens_to_sample, - messages, - system - } : { - max_tokens_to_sample, - prompt - }, - model, - stop_sequences, - stream: true, - temperature, - top_k, - top_p - }), - }); - await checkResErr(res); - return res; - } -/******************************** */ - const attachments = []; - if (Config.Settings.PromptExperiments) { - let splitedprompt = prompt.split('\n\nPlainPrompt:'); // - prompt = splitedprompt[0]; // - attachments.push({ - extracted_content: prompt, - file_name: 'paste.txt', //fileName(), - file_type: 'txt', //'text/plain', - file_size: Buffer.from(prompt).byteLength - }); - prompt = 'r' === type ? Config.PromptExperimentFirst : Config.PromptExperimentNext; - splitedprompt.length > 1 && (prompt += splitedprompt[1]); // - } - let res; - const body = { - attachments, - files: [], - model: isPro || forceModel ? model : undefined, - rendering_mode: 'raw', - ...Config.Settings.PassParams && { - max_tokens_to_sample, // - //stop_sequences, // - top_k, // - top_p, // - temperature - }, - prompt: prompt || '', - timezone: AI.zone() - }; - let headers = { - ...AI.hdr(Conversation.uuid || ''), - Accept: 'text/event-stream', - Cookie: getCookies() - }; - res = await (Config.Settings.Superfetch ? Superfetch : fetch)(`${Config.rProxy || AI.end()}/api/organizations/${uuidOrg || ''}/chat_conversations/${Conversation.uuid || ''}/completion`, { - stream: true, - signal, - method: 'POST', - body: JSON.stringify(body), - headers - }); - updateParams(res); - await checkResErr(res); - return res; - })(signal, model, prompt, temperature, type)); - const response = Writable.toWeb(res); - clewdStream = new ClewdStream({ - config: { - ...Config, - Settings: { - ...Config.Settings, - Superfetch: apiKey ? false : Config.Settings.Superfetch - } - }, //config: Config, - version: Main, - minSize: Config.BufferSize, - model, - streaming: true === body.stream, - abortControl, - source: fetchAPI - }, Logger); - titleTimer = setInterval((() => setTitle('recv ' + bytesToSize(clewdStream.size))), 300); - (!apiKey && Config.Settings.Superfetch) ? await Readable.toWeb(fetchAPI.body).pipeThrough(clewdStream).pipeTo(response) : await fetchAPI.body.pipeThrough(clewdStream).pipeTo(response); //Config.Settings.Superfetch ? await Readable.toWeb(fetchAPI.body).pipeThrough(clewdStream).pipeTo(response) : await fetchAPI.body.pipeThrough(clewdStream).pipeTo(response); - } catch (err) { - if ('AbortError' === err.name) { - res.end(); - } else { - nochange = true, exceeded_limit = err.exceeded_limit; // - err.planned ? console.log(`${err.status || 'Aborted'}!\n`) : console.error('Clewd:\n%o', err); //err.planned || console.error('Clewd:\n%o', err); - res.json({ - error: { - message: 'clewd: ' + (err.message || err.name || err.type), - type: err.type || err.name || err.code, - param: null, - code: err.code || 500 - } - }, 500); - } - } - clearInterval(titleTimer); - if (clewdStream) { - clewdStream.censored && console.warn('likely your account is hard-censored'); - prevImpersonated = clewdStream.impersonated; - exceeded_limit = clewdStream.error.exceeded_limit; // - clewdStream.error.status < 200 || clewdStream.error.status >= 300 || clewdStream.error.message === 'Overloaded' && (nochange = true); // - setTitle('ok ' + bytesToSize(clewdStream.size)); - if (clewdStream.compModel && !(AI.mdl().includes(clewdStream.compModel) || Config.unknownModels.includes(clewdStream.compModel)) && !apiKey) { - Config.unknownModels.push(clewdStream.compModel); - writeSettings(Config); - } - console.log(`${200 == fetchAPI.status ? '' : ''}${fetchAPI.status}!\n`); - clewdStream.empty(); - } - const shouldChange = exceeded_limit || !nochange && Config.Cookiecounter > 0 && changeflag++ >= Config.Cookiecounter - 1; // - if (!apiKey && (shouldChange || prevImpersonated)) { //if (prevImpersonated) { - try { - await deleteChat(Conversation.uuid); - } catch (err) {} -/******************************** */ - if (shouldChange) { - exceeded_limit && console.log(`Exceeded limit!\n`); - changeflag = 0; - CookieChanger(); - } -/******************************** */ - } - })); - })(req, res); - break; - - case '/v1/complete': - res.json({ - error: { - message: 'clewd: Set "Chat Completion source" to OpenAI instead of Claude. Enable "External" models aswell', - code: 404 - } - }, 404); - break; - - default: - !['/', '/v1', '/favicon.ico'].includes(req.url) && (console.log('unknown request: ' + req.url)); //console.log('unknown request: ' + req.url); - res.writeHead(200, {'Content-Type': 'text/html'}); // - res.write(`\n\n\n\n\n\n\n${Main}

完全开源、免费且禁止商用

点击复制反向代理: Copy Link
填入OpenAI API反向���理并选择OpenAI分类中的claude模型(酒馆需打开Show "External" models,仅在api模式有模型选择差异)

教程与FAQ: Rentry | Discord


❗警惕任何高风险cookie/伪api(25k cookie)购买服务,以及破坏中文AI开源共享环境倒卖免费资源抹去署名的群组(🈲黑名单:酒馆小二、AI新服务、浅睡(鲑鱼)、赛博女友制作人(青麈/overloaded/科普晓百生)🈲)\n\n`); // - res.end(); //res.json(// {// error: {// message: '404 Not Found',// type: 404,// param: null,// code: 404// }//}, 404); - } -})); - -!async function() { - await (async () => { - if (exists(ConfigPath)) { - const userConfig = require(ConfigPath), validConfigs = Object.keys(Config), parsedConfigs = Object.keys(userConfig), parsedSettings = Object.keys(userConfig.Settings), invalidConfigs = parsedConfigs.filter((config => !validConfigs.includes(config))), validSettings = Object.keys(Config.Settings); - UnknownSettings = parsedSettings.filter((setting => !validSettings.includes(setting))); - invalidConfigs.forEach((config => { - console.warn(`unknown config in config.js: ${config}`); - })); - UnknownSettings.forEach((setting => { - console.warn(`unknown setting in config.js: Settings.${setting}`); - })); - const missingConfigs = validConfigs.filter((config => !parsedConfigs.includes(config))), missingSettings = validSettings.filter((config => !parsedSettings.includes(config))); - missingConfigs.forEach((config => { - console.warn(`adding missing config in config.js: ${config}`); - userConfig[config] = Config[config]; - })); - missingSettings.forEach((setting => { - console.warn(`adding missing setting in config.js: Settings.${setting}`); - userConfig.Settings[setting] = Config.Settings[setting]; - })); - ChangedSettings = parsedSettings.filter((setting => Config.Settings[setting] !== userConfig.Settings[setting])); - (missingConfigs.length > 0 || missingSettings.length > 0) && await writeSettings(userConfig); - userConfig.Settings.LogMessages && (Logger = createWriteStream(LogPath)); - Config = { - ...Config, - ...userConfig - }; - } else { - Config.Cookie = 'SET YOUR COOKIE HERE'; - writeSettings(Config, true); - } - })(); -/***************************** */ - for (let key in Config) { - if (key === 'Settings') { - for (let setting in Config.Settings) { - Config.Settings[setting] = process.env[setting] ? convertToType(process.env[setting]) : Config.Settings[setting]; - } - } else { - Config[key] = process.env[key] ? convertToType(process.env[key]) : Config[key]; - } - } - Config.rProxy = Config.rProxy.replace(/\/$/, ''); - Config.CookieArray = [...new Set([Config.CookieArray].join(',').match(/(claude[-_][a-z0-9-_]*?@)?(sessionKey=)?sk-ant-sid01-[\w-]{86}-[\w-]{6}AA/g))]; - Config.unknownModels = Config.unknownModels.reduce((prev, cur) => !cur || prev.includes(cur) || AI.mdl().includes(cur) ? prev : [...prev, cur], []); - writeSettings(Config); - currentIndex = Config.CookieIndex > 0 ? Config.CookieIndex - 1 : Config.Cookiecounter >= 0 ? Math.floor(Math.random() * Config.CookieArray.length) : 0; -/***************************** */ - Proxy.listen(Config.Port, Config.Ip, onListen); - Proxy.on('error', (err => { - console.error('Proxy error\n%o', err); - })); -}(); - -const cleanup = async () => { - console.log('cleaning...'); - try { - await deleteChat(Conversation.uuid); - SuperfetchFoldersRm(); - Logger?.close(); - } catch (err) {} - process.exit(); -}; - -process.on('SIGHUP', cleanup); - -process.on('SIGTERM', cleanup); - -process.on('SIGINT', cleanup); - -process.on('exit', (async () => { - console.log('exiting...'); +/* +* https://rentry.org/teralomaniac_clewd +* https://github.com/teralomaniac/clewd +*/ +'use strict'; + +const {createServer: Server, IncomingMessage, ServerResponse} = require('node:http'), {createHash: Hash, randomUUID, randomInt, randomBytes} = require('node:crypto'), {TransformStream, ReadableStream} = require('node:stream/web'), {Readable, Writable} = require('node:stream'), {Blob} = require('node:buffer'), {existsSync: exists, writeFileSync: write, createWriteStream} = require('node:fs'), {join: joinP} = require('node:path'), {ClewdSuperfetch: Superfetch, SuperfetchAvailable, SuperfetchFoldersMk, SuperfetchFoldersRm} = require('./lib/clewd-superfetch'), {AI, fileName, genericFixes, bytesToSize, setTitle, checkResErr, Replacements, Main} = require('./lib/clewd-utils'), ClewdStream = require('./lib/clewd-stream'); + +/******************************************************* */ +let currentIndex, Firstlogin = true, changeflag = 0, changing, changetime = 0, totaltime, uuidOrgArray = [], model, cookieModel, tokens, apiKey, timestamp, regexLog, isPro, modelList = []; + +const url = require('url'); +const asyncPool = async (poolLimit, array, iteratorFn) => { + const ret = [], executing = []; + for (const item of array) { + const p = Promise.resolve().then(() => iteratorFn(item)); + ret.push(p); + if (poolLimit <= array.length) { + const e = p.then(() => executing.splice(executing.indexOf(e), 1)); + executing.push(e); + if (executing.length >= poolLimit) await Promise.race(executing); + } + } + return Promise.all(ret); +}, convertToType = value => { + if (value === 'true') return true; + if (value === 'false') return false; + if (/^\d+$/.test(value)) return parseInt(value); + return value; +}, CookieChanger = (resetTimer = true, cleanup = false) => { + if (Config.CookieArray?.length <= 1) { + return changing = false; + } else { + changeflag = 0, changing = true; + if(!cleanup) { + currentIndex = (currentIndex + 1) % Config.CookieArray.length; + console.log(`Changing Cookie...\n`); + } + setTimeout(() => { + onListen(); + resetTimer && (timestamp = Date.now()); + }, !Config.rProxy || Config.rProxy === AI.end() ? 15000 + timestamp - Date.now() : 0); + } +}, CookieCleaner = (flag, percentage) => { + Config.WastedCookie.push(flag + '@' + Config.CookieArray[currentIndex].split('@').toReversed()[0]); + Config.CookieArray.splice(currentIndex, 1), Config.Cookie = ''; + Config.Cookiecounter < 0 && console.log(`[progress]: ${percentage.toFixed(2)}%\n[length]: ${Config.CookieArray.length}\n`); + console.log(`Cleaning Cookie...\n`); + writeSettings(Config); + return CookieChanger(true, true); +}, padtxt = content => { + const {countTokens} = require('@anthropic-ai/tokenizer'); + tokens = countTokens(content); + const padtxt = String(Config.Settings.padtxt).split(',').reverse(), maxtokens = parseInt(padtxt[0]), extralimit = parseInt(padtxt[1]) || 1000, minlimit = parseInt(padtxt[2]); + const placeholder = (tokens > maxtokens - extralimit && minlimit ? Config.placeholder_byte : Config.placeholder_token) || randomBytes(randomInt(5, 15)).toString('hex'); + const placeholdertokens = countTokens(placeholder.trim()); + for (let match; match = content.match(/<\|padtxt.*?(\d+)t.*?\|>/); content = content.replace(match[0], placeholder.repeat(parseInt(match[1]) / placeholdertokens))) tokens += parseInt(match[1]); + if(/<\|padtxt off.*?\|>/.test(content)) return content.replace(/\s*<\|padtxt.*?\|>\s*/g, '\n\n'); + const padding = placeholder.repeat(Math.min(maxtokens, (tokens <= maxtokens - extralimit ? maxtokens - tokens : minlimit ? minlimit : extralimit)) / placeholdertokens); + content = /<\|padtxt.*?\|>/.test(content) ? content.replace(/<\|padtxt.*?\|>/, padding).replace(/\s*<\|padtxt.*?\|>\s*/g, '\n\n') : !apiKey ? padding + '\n\n\n' + content.trim() : content; + return content; +}, xmlPlot_merge = (content, mergeTag, nonsys) => { + if (/(\n\n|^\s*)xmlPlot:\s*/.test(content)) { + content = (nonsys ? content : content.replace(/(\n\n|^\s*)(? { + let matches = content.match(new RegExp(` *"(/?)(.*)\\1(.*?)" *: *"(.*?)" *`, 'gm')); + matches && matches.forEach(match => { + try { + const reg = / *"(\/?)(.*)\1(.*?)" *: *"(.*?)" *<\/regex>/.exec(match); + regexLog += match + '\n'; + content = content.replace(new RegExp(reg[2], reg[3]), JSON.parse(`"${reg[4].replace(/\\?"/g, '\\"')}"`)); + } catch (err) { + console.log(`Regex error: ` + match + '\n' + err); + } + }); + return content; +}, xmlPlot = (content, nonsys = false) => { + regexLog = ''; + //一次正则 + content = xmlPlot_regex(content, 1); + //一次role合并 + const mergeTag = { + all: !content.includes('<|Merge Disable|>'), + system: !content.includes('<|Merge System Disable|>'), + human: !content.includes('<|Merge Human Disable|>'), + assistant: !content.includes('<|Merge Assistant Disable|>') + }; + content = xmlPlot_merge(content, mergeTag, nonsys); + //自定义插入 + let splitContent = content.split(/\n\n(?=Assistant:|Human:)/g), match; + while ((match = /<@(\d+)>(.*?)<\/@\1>/gs.exec(content)) !== null) { + let index = splitContent.length - parseInt(match[1]) - 1; + index >= 0 && (splitContent[index] += '\n\n' + match[2]); + content = content.replace(match[0], ''); + } + content = splitContent.join('\n\n').replace(/<@(\d+)>.*?<\/@\1>/gs, ''); + //二次正则 + content = xmlPlot_regex(content, 2); + //二次role合并 + content = xmlPlot_merge(content, mergeTag, nonsys); + //Plain Prompt + let segcontentHuman = content.split('\n\nHuman:'); + let segcontentlastIndex = segcontentHuman.length - 1; + if (!apiKey && segcontentlastIndex >= 2 && segcontentHuman[segcontentlastIndex].includes('<|Plain Prompt Enable|>') && !content.includes('\n\nPlainPrompt:')) { + content = segcontentHuman.slice(0, segcontentlastIndex).join('\n\nHuman:') + '\n\nPlainPrompt:' + segcontentHuman.slice(segcontentlastIndex).join('\n\nHuman:').replace(/\n\nHuman: *PlainPrompt:/, '\n\nPlainPrompt:'); + } + //三次正则 + content = xmlPlot_regex(content, 3); + //消除空XML tags、两端空白符和多余的\n + content = content.replace(/.*?<\/regex>/gm, '') + .replace(/\r\n|\r/gm, '\n') + .replace(/\s*<\|curtail\|>\s*/g, '\n') + .replace(/\s*<\|join\|>\s*/g, '') + .replace(/\s*<\|space\|>\s*/g, ' ') + .replace(/\s*\n\n(H(uman)?|A(ssistant)?): +/g, '\n\n$1: ') + .replace(/<\|(\\.*?)\|>/g, function(match, p1) { + try { + return JSON.parse(`"${p1.replace(/\\?"/g, '\\"')}"`); + } catch { return match } + }); + //确保格式正确 + if (apiKey) { + content = content.replace(/(\n\nHuman:(?!.*?\n\nAssistant:).*?|(?\s*(.*?)(?:\n\nAssistant:\s*)?$/s, '\n\n$1'); + content.includes('<|reverseHA|>') && (content = content.replace(/\s*<\|reverseHA\|>\s*/g, '\n\n').replace(/Assistant|Human/g, function(match) {return match === 'Human' ? 'Assistant' : 'Human'}).replace(/\n(A|H): /g, function(match, p1) {return p1 === 'A' ? '\nH: ' : '\nA: '})); + return content.replace(Config.Settings.padtxt ? /\s*<\|(?!padtxt).*?\|>\s*/g : /\s*<\|.*?\|>\s*/g, '\n\n').trim().replace(/^.+:/, '\n\n$&').replace(/(?<=\n)\n(?=\n)/g, ''); + } else { + return content.replace(Config.Settings.padtxt ? /\s*<\|(?!padtxt).*?\|>\s*/g : /\s*<\|.*?\|>\s*/g, '\n\n').trim().replace(/^Human: *|\n\nAssistant: *$/g, '').replace(/(?<=\n)\n(?=\n)/g, ''); + } +}, waitForChange = () => { + return new Promise(resolve => { + const interval = setInterval(() => { + if (!changing) { + clearInterval(interval); + resolve(); + } + }, 100); + }); +}; +/******************************************************* */ + +let ChangedSettings, UnknownSettings, Logger; + +const ConfigPath = joinP(__dirname, './config.js'), LogPath = joinP(__dirname, './log.txt'), Conversation = { + char: null, + uuid: null, + depth: 0 +}, cookies = {}; + +let uuidOrg, curPrompt = {}, prevPrompt = {}, prevMessages = [], prevImpersonated = false, Config = { + Cookie: '', + CookieArray: [], + WastedCookie: [], + unknownModels: [], + Cookiecounter: 3, + CookieIndex: 0, + ProxyPassword: '', + Ip: (process.env.Cookie || process.env.CookieArray) ? '0.0.0.0' : '0.0.0.0', + Port: process.env.PORT || 7860, + localtunnel: false, + BufferSize: 1, + SystemInterval: 3, + rProxy: '', + api_rProxy: '', + placeholder_token: '', + placeholder_byte: '', + PromptExperimentFirst: '', + PromptExperimentNext: '', + PersonalityFormat: '{{char}}\'s personality: {{personality}}', + ScenarioFormat: 'Dialogue scenario: {{scenario}}', + Settings: { + RenewAlways: true, + RetryRegenerate: false, + PromptExperiments: true, + SystemExperiments: true, + PreventImperson: true, + AllSamples: false, + NoSamples: false, + StripAssistant: false, + StripHuman: false, + PassParams: true, + ClearFlags: true, + PreserveChats: false, + LogMessages: true, + FullColon: true, + padtxt: "1000,1000,15000", + xmlPlot: true, + SkipRestricted: false, + Artifacts: false, + Superfetch: true + } +}; + +ServerResponse.prototype.json = async function(body, statusCode = 200, headers) { + body = body instanceof Promise ? await body : body; + this.headersSent || this.writeHead(statusCode, { + 'Content-Type': 'application/json', + ...headers && headers + }); + this.end('object' == typeof body ? JSON.stringify(body) : body); + return this; +}; + +Array.prototype.sample = function() { + return this[Math.floor(Math.random() * this.length)]; +}; + +const updateParams = res => { + updateCookies(res); +}, updateCookies = res => { + let cookieNew = ''; + res instanceof Response ? cookieNew = res.headers?.get('set-cookie') : res?.superfetch ? cookieNew = res.headers?.['set-cookie'] : 'string' == typeof res && (cookieNew = res.split('\n').join('')); + if (!cookieNew) { + return; + } + let cookieArr = cookieNew.split(/;\s?/gi).filter((prop => false === /^(path|expires|domain|HttpOnly|Secure|SameSite)[=;]*/i.test(prop))); + for (const cookie of cookieArr) { + const divide = cookie.split(/^(.*?)=\s*(.*)/), cookieName = divide[1], cookieVal = divide[2]; + cookies[cookieName] = cookieVal; + } +}, getCookies = () => { + const cookieNames = Object.keys(cookies); + return cookieNames.map(((name, idx) => `${name}=${cookies[name]}${idx === cookieNames.length - 1 ? '' : ';'}`)).join(' ').replace(/(\s+)$/gi, ''); +}, deleteChat = async uuid => { + if (!uuid) { + return; + } + if (uuid === Conversation.uuid) { + Conversation.uuid = null; + Conversation.depth = 0; + } + if (Config.Settings.PreserveChats) { + return; + } + const res = await (Config.Settings.Superfetch ? Superfetch : fetch)(`${Config.rProxy || AI.end()}/api/organizations/${uuidOrg}/chat_conversations/${uuid}`, { + headers: { + ...AI.hdr(), + Cookie: getCookies() + }, + method: 'DELETE' + }); + updateParams(res); +}, onListen = async () => { +/***************************** */ + if (Firstlogin) { + Firstlogin = false, timestamp = Date.now(), totaltime = Config.CookieArray.length; + console.log(`${Main}\nhttp://${Config.Ip}:${Config.Port}/v1\n\n${Object.keys(Config.Settings).map((setting => UnknownSettings?.includes(setting) ? `??? ${setting}: ${Config.Settings[setting]}` : `${setting}: ${ChangedSettings?.includes(setting) ? '' : ''}${Config.Settings[setting]}`)).sort().join('\n')}\n`); //↓ + if (Config.Settings.Superfetch) { + SuperfetchAvailable(true); + SuperfetchFoldersMk(); + } + if (Config.localtunnel) { + const localtunnel = require('localtunnel'); + localtunnel({ port: Config.Port }).then((tunnel) => { + console.log(`\nTunnel URL for outer websites: ${tunnel.url}/v1\n`); + }) + } + } + if (Config.CookieArray?.length > 0) { + const cookieInfo = /(?:(claude[-_][a-z0-9-_]*?)@)?(?:sessionKey=)?(sk-ant-sid01-[\w-]{86}-[\w-]{6}AA)/.exec(Config.CookieArray[currentIndex]); + cookieInfo?.[2] && (Config.Cookie = 'sessionKey=' + cookieInfo[2]); + changetime++; + if (model && cookieInfo?.[1] && !/claude[\w]*?_pro/.test(cookieInfo?.[1]) && cookieInfo?.[1] != model) return CookieChanger(false); + } + let percentage = ((changetime + Math.max(Config.CookieIndex - 1, 0)) / totaltime) * 100 + if (Config.Cookiecounter < 0 && percentage > 100) { + console.log(`\n※※※Cookie cleanup completed※※※\n\n`); + return process.exit(); + } + try { +/***************************** */ + if ('SET YOUR COOKIE HERE' === Config.Cookie || Config.Cookie?.length < 1) { + return changing = false, console.log(`No cookie available, enter apiKey-only mode.\n`); //throw Error('Set your cookie inside config.js'); + } + updateCookies(Config.Cookie); +/**************************** */ + const bootstrapRes = await (Config.Settings.Superfetch ? Superfetch : fetch)((Config.rProxy || AI.end()) + `/api/bootstrap`, { + method: 'GET', + headers: { + ...AI.hdr(), + Cookie: getCookies() + } + }); + await checkResErr(bootstrapRes); + const bootstrap = await bootstrapRes.json(); + if (bootstrap.account === null) { + console.log(`Null!`); + return CookieCleaner('Null', percentage); + } + const bootAccInfo = bootstrap.account.memberships.find(item => item.organization.capabilities.includes('chat')).organization; + cookieModel = bootstrap.statsig.values.layer_configs["HPOHwBLNLQLxkj5Yn4bfSkgCQnBX28kPR7h/BNKdVLw="]?.value?.console_default_model_override?.model || bootstrap.statsig.values.dynamic_configs["6zA9wvTedwkzjLxWy9PVe7yydI00XDQ6L5Fejjq/2o8="]?.value?.model; + isPro = bootAccInfo.capabilities.includes('claude_pro') && 'claude_pro' || bootAccInfo.capabilities.includes('raven') && 'claude_team_pro'; + const unknown = cookieModel && !(AI.mdl().includes(cookieModel) || Config.unknownModels.includes(cookieModel)); + if (Config.CookieArray?.length > 0 && (isPro || cookieModel) != Config.CookieArray[currentIndex].split('@')[0] || unknown) { + Config.CookieArray[currentIndex] = (isPro || cookieModel) + '@' + Config.Cookie; + unknown && Config.unknownModels.push(cookieModel); + writeSettings(Config); + } + if (!isPro && model && model != cookieModel) return CookieChanger(); + console.log(Config.CookieArray?.length > 0 ? `(index: ${currentIndex + 1 || Config.CookieArray.length}) Logged in %o` : 'Logged in %o', { //console.log('Logged in %o', { ↓ + name: bootAccInfo.name?.split('@')?.[0], + mail: bootstrap.account.email_address, // + cookieModel, // + capabilities: bootAccInfo.capabilities + }); //↓ + if (uuidOrgArray.includes(bootAccInfo.uuid) && percentage <= 100 && Config.CookieArray?.length > 0 || bootAccInfo.api_disabled_reason && !bootAccInfo.api_disabled_until || !bootstrap.account.completed_verification_at) { + const flag = bootAccInfo.api_disabled_reason ? 'Disabled' : !bootstrap.account.completed_verification_at ? 'Unverified' : 'Overlap'; + console.log(`${flag}!`); + return CookieCleaner(flag, percentage); + } else uuidOrgArray.push(bootAccInfo.uuid); + if (Config.Cookiecounter < 0) { + console.log(`[progress]: ${percentage.toFixed(2)}%\n[length]: ${Config.CookieArray.length}\n`); + return CookieChanger(); + } +/**************************** */ + const accRes = await (Config.Settings.Superfetch ? Superfetch : fetch)((Config.rProxy || AI.end()) + '/api/organizations', { + method: 'GET', + headers: { + ...AI.hdr(), + Cookie: getCookies() + } + }); + await checkResErr(accRes); + const accInfo = (await accRes.json())?.find(item => item.capabilities.includes('chat')); //const accInfo = (await accRes.json())?.[0];\nif (!accInfo || accInfo.error) {\n throw Error(`Couldn't get account info: "${accInfo?.error?.message || accRes.statusText}"`);\n}\nif (!accInfo?.uuid) {\n throw Error('Invalid account id');\n} + setTitle('ok'); + updateParams(accRes); + uuidOrg = accInfo?.uuid; + if (accInfo?.active_flags.length > 0) { + let banned = false; // + const now = new Date, formattedFlags = accInfo.active_flags.map((flag => { + const days = ((new Date(flag.expires_at).getTime() - now.getTime()) / 864e5).toFixed(2); + 'consumer_banned' === flag.type && (banned = true); // + return { + type: flag.type, + remaining_days: days + }; + })); + console.warn(`${banned ? '' : ''}Your account has warnings %o`, formattedFlags); //console.warn('Your account has warnings %o', formattedFlags); + await Promise.all(accInfo.active_flags.map((flag => (async type => { + if (!Config.Settings.ClearFlags) { + return; + } + if ('consumer_restricted_mode' === type || 'consumer_banned' === type) { //if ('consumer_restricted_mode' === type) { + return; + } + const req = await (Config.Settings.Superfetch ? Superfetch : fetch)(`${Config.rProxy || AI.end()}/api/organizations/${uuidOrg}/flags/${type}/dismiss`, { + headers: { + ...AI.hdr(), + Cookie: getCookies() + }, + method: 'POST' + }); + updateParams(req); + const json = await req.json(); + console.log(`${type}: ${json.error ? json.error.message || json.error.type || json.detail : 'OK'}`); + })(flag.type)))); + console.log(`${banned ? 'Banned' : 'Restricted'}!`); // + if (banned) return CookieCleaner('Banned') // + else if (Config.Settings.SkipRestricted) return CookieChanger(); // + } + if (bootstrap.account.settings.preview_feature_uses_artifacts != Config.Settings.Artifacts) { + const settingsRes = await (Config.Settings.Superfetch ? Superfetch : fetch)((Config.rProxy || AI.end()) + `/api/account`, { + method: 'PUT', + headers: { + ...AI.hdr(), + Cookie: getCookies() + }, + body: JSON.stringify({ settings: Object.assign(bootstrap.account.settings, { preview_feature_uses_artifacts: Config.Settings.Artifacts }) }), + }); + await checkResErr(settingsRes); + updateParams(settingsRes); + } + changing = false; + const convRes = await (Config.Settings.Superfetch ? Superfetch : fetch)(`${Config.rProxy || AI.end()}/api/organizations/${accInfo.uuid}/chat_conversations`, { //const convRes = await fetch(`${Config.rProxy || AI.end()}/api/organizations/${uuidOrg}/chat_conversations`, { + method: 'GET', + headers: { + ...AI.hdr(), + Cookie: getCookies() + } + }), conversations = await convRes.json(); + updateParams(convRes); + conversations.length > 0 && await asyncPool(10, conversations, async (conv) => await deleteChat(conv.uuid)); //await Promise.all(conversations.map((conv => deleteChat(conv.uuid)))); +/***************************** */ + } catch (err) { + if (err.message === 'Invalid authorization') { + console.log(`Invalid!`); + return CookieCleaner('Invalid', percentage); + } + console.error('Clewd:\n%o', err); + CookieChanger(); + } +/***************************** */ +}, writeSettings = async (config, firstRun = false) => { + if (process.env.Cookie || process.env.CookieArray) return; // + write(ConfigPath, `/*\n* https://rentry.org/teralomaniac_clewd\n* https://github.com/teralomaniac/clewd\n*/\n\n// SET YOUR COOKIE BELOW\n\nmodule.exports = ${JSON.stringify(config, null, 4)}\n\n/*\n BufferSize\n * How many characters will be buffered before the AI types once\n * lower = less chance of \`PreventImperson\` working properly\n\n ---\n\n SystemInterval\n * How many messages until \`SystemExperiments alternates\`\n\n ---\n\n Other settings\n * https://gitgud.io/ahsk/clewd/#defaults\n * and\n * https://gitgud.io/ahsk/clewd/-/blob/master/CHANGELOG.md\n */`.trim().replace(/((? { + if ('OPTIONS' === req.method) { + return ((req, res) => { + res.writeHead(200, { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'Authorization, Content-Type', + 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS' + }).end(); + })(0, res); + } + const URL = url.parse(req.url.replace(/\/v1(\?.*)\$(\/.*)$/, '/v1$2$1'), true); + const api_rProxy = URL.query?.api_rProxy || Config.api_rProxy; + req.url = URL.pathname; + switch (req.url) { + case '/v1/models': +/***************************** */ + (async (req, res) => { + let models; + if (/oaiKey:/.test(req.headers.authorization)) { + try { + const modelsRes = await fetch(api_rProxy.replace(/(\/v1)?\/? *$/, '') + '/v1/models', { + method: 'GET', + headers: { authorization: req.headers.authorization.match(/(?<=oaiKey:).*/)?.[0].split(',')[0].trim() } + }); + models = await modelsRes.json(); + } catch(err) {} + } + res.json({ + data: [ + ...AI.mdl().concat(Config.unknownModels).map((name => ({ id: name }))) + ].concat(models?.data).reduce((acc, current, index) => { + index === 0 && modelList.splice(0); + if (current?.id && acc.every(model => model.id != current.id)) { + acc.push(current); + modelList.push(current.id); + } + return acc; + }, []) + }); + })(req, res); //res.json({\n data: AI.mdl().map((name => ({\n id: name\n })))\n}); +/***************************** */ + break; + + case '/v1/chat/completions': + ((req, res) => { + setTitle('recv...'); + let fetchAPI; + const abortControl = new AbortController, {signal} = abortControl; + res.socket.on('close', (async () => { + abortControl.signal.aborted || abortControl.abort(); + })); + const buffer = []; + req.on('data', (chunk => { + buffer.push(chunk); + })); + req.on('end', (async () => { + let clewdStream, titleTimer, samePrompt = false, shouldRenew = true, retryRegen = false, exceeded_limit = false, nochange = false; //let clewdStream, titleTimer, samePrompt = false, shouldRenew = true, retryRegen = false; + try { + const body = JSON.parse(Buffer.concat(buffer).toString()); + let {temperature} = body; + temperature = typeof temperature === 'number' ? Math.max(.1, Math.min(1, temperature)) : undefined; //temperature = Math.max(.1, Math.min(1, temperature)); + let {messages} = body; +/************************* */ + const thirdKey = req.headers.authorization?.match(/(?<=(3rd|oai)Key:).*/), oaiAPI = /oaiKey:/.test(req.headers.authorization), forceModel = /--force/.test(body.model); + apiKey = thirdKey?.[0].split(',').map(item => item.trim()) || req.headers.authorization?.match(/sk-ant-api\d\d-[\w-]{86}-[\w-]{6}AA/g); + model = apiKey || forceModel || isPro ? body.model.replace(/--force/, '').trim() : cookieModel; + let max_tokens_to_sample = body.max_tokens, stop_sequences = body.stop, top_p = typeof body.top_p === 'number' ? body.top_p : undefined, top_k = typeof body.top_k === 'number' ? body.top_k : undefined; + if (!apiKey && (Config.ProxyPassword != '' && req.headers.authorization != 'Bearer ' + Config.ProxyPassword || !uuidOrg)) { + throw Error(uuidOrg ? 'ProxyPassword Wrong' : 'No cookie available or apiKey format wrong'); + } else if (!changing && !apiKey && (!isPro && model != cookieModel)) CookieChanger(); + await waitForChange(); +/************************* */ + if (messages?.length < 1) { + throw Error('Select OpenAI as completion source'); + } + if (!body.stream && 1 === messages.length && JSON.stringify(messages.sort() || []) === JSON.stringify([ { + role: 'user', + content: 'Hi' + } ].sort())) { + return res.json({ + choices: [ { + message: { + content: Main + } + } ] + }); + } + res.setHeader('Access-Control-Allow-Origin', '*'); + body.stream && res.setHeader('Content-Type', 'text/event-stream'); + if (!body.stream && messages?.[0]?.content?.startsWith('From the list below, choose a word that best represents a character\'s outfit description, action, or emotion in their dialogue')) { + return res.json({ + choices: [ { + message: { + content: 'neutral' + } + } ] + }); + } + if (Config.Settings.AllSamples && Config.Settings.NoSamples) { + console.log('having AllSamples and NoSamples both set to true is not supported'); + throw Error('Only one can be used at the same time: AllSamples/NoSamples'); + } + //const model = body.model;//if (model === AI.mdl()[0]) {// return;//} + if (!modelList.includes(model) && !/claude-.*/.test(model) && !forceModel) { + throw Error('Invalid model selected: ' + model); + } + curPrompt = { + firstUser: messages.find((message => 'user' === message.role)), + firstSystem: messages.find((message => 'system' === message.role)), + firstAssistant: messages.find((message => 'assistant' === message.role)), + lastUser: messages.findLast((message => 'user' === message.role)), + lastSystem: messages.findLast((message => 'system' === message.role && '[Start a new chat]' !== message.content)), + lastAssistant: messages.findLast((message => 'assistant' === message.role)) + }; + prevPrompt = { + ...prevMessages.length > 0 && { + firstUser: prevMessages.find((message => 'user' === message.role)), + firstSystem: prevMessages.find((message => 'system' === message.role)), + firstAssistant: prevMessages.find((message => 'assistant' === message.role)), + lastUser: prevMessages.findLast((message => 'user' === message.role)), + lastSystem: prevMessages.find((message => 'system' === message.role && '[Start a new chat]' !== message.content)), + lastAssistant: prevMessages.findLast((message => 'assistant' === message.role)) + } + }; + samePrompt = JSON.stringify(messages.filter((message => 'system' !== message.role)).sort()) === JSON.stringify(prevMessages.filter((message => 'system' !== message.role)).sort()); + const sameCharDiffChat = !samePrompt && curPrompt.firstSystem?.content === prevPrompt.firstSystem?.content && curPrompt.firstUser?.content !== prevPrompt.firstUser?.content; + shouldRenew = Config.Settings.RenewAlways || !Conversation.uuid || prevImpersonated || !Config.Settings.RenewAlways && samePrompt || sameCharDiffChat; + retryRegen = Config.Settings.RetryRegenerate && samePrompt && null != Conversation.uuid; + samePrompt || (prevMessages = JSON.parse(JSON.stringify(messages))); + let type = ''; + if (apiKey) { type = 'api'; } else if (retryRegen) { //if (retryRegen) { + type = 'R'; + fetchAPI = await (async (signal, model) => { + let res; + const body = { + prompt: '', + parent_message_uuid: '', + timezone: AI.zone(), + attachments: [], + files: [], + rendering_mode: 'raw' + }; + let headers = { + ...AI.hdr(Conversation.uuid || ''), + Accept: 'text/event-stream', + Cookie: getCookies() + }; + if (Config.Settings.Superfetch) { + const names = Object.keys(headers), values = Object.values(headers); + headers = names.map(((header, idx) => `${header}: ${values[idx]}`)); + } + res = await (Config.Settings.Superfetch ? Superfetch : fetch)((Config.rProxy || AI.end()) + `/api/organizations/${uuidOrg || ''}/chat_conversations/${Conversation.uuid || ''}/retry_completion`, { + stream: true, + signal, + method: 'POST', + body: JSON.stringify(body), + headers + }); + updateParams(res); + await checkResErr(res); + return res; + })(signal, model); + } else if (shouldRenew) { + Conversation.uuid && await deleteChat(Conversation.uuid); + fetchAPI = await (async signal => { + Conversation.uuid = randomUUID().toString(); + Conversation.depth = 0; + const res = await (Config.Settings.Superfetch ? Superfetch : fetch)(`${Config.rProxy || AI.end()}/api/organizations/${uuidOrg}/chat_conversations`, { + signal, + headers: { + ...AI.hdr(), + Cookie: getCookies() + }, + method: 'POST', + body: JSON.stringify({ + uuid: Conversation.uuid, + name: '' + }) + }); + updateParams(res); + await checkResErr(res); + return res; + })(signal); + type = 'r'; + } else if (samePrompt) {} else { + const systemExperiment = !Config.Settings.RenewAlways && Config.Settings.SystemExperiments; + if (!systemExperiment || systemExperiment && Conversation.depth >= Config.SystemInterval) { + type = 'c-r'; + Conversation.depth = 0; + } else { + type = 'c-c'; + Conversation.depth++; + } + } + let {prompt, systems} = ((messages, type) => { + const rgxScenario = /^\[Circumstances and context of the dialogue: ([\s\S]+?)\.?\]$/i, rgxPerson = /^\[([\s\S]+?)'s personality: ([\s\S]+?)\]$/i, messagesClone = JSON.parse(JSON.stringify(messages)), realLogs = messagesClone.filter((message => [ 'user', 'assistant' ].includes(message.role))), sampleLogs = messagesClone.filter((message => message.name)), mergedLogs = [ ...sampleLogs, ...realLogs ]; + mergedLogs.forEach(((message, idx) => { + const next = mergedLogs[idx + 1]; + message.customname = (message => [ 'assistant', 'user' ].includes(message.role) && null != message.name && !(message.name in Replacements))(message); + if (next && !Config.Settings.xmlPlot) { //if (next) { + if ('name' in message && 'name' in next) { + if (message.name === next.name) { + message.content += '\n' + next.content; + next.merged = true; + } + } else if ('system' !== next.role) { + if (next.role === message.role) { + message.content += '\n' + next.content; + next.merged = true; + } + } else { + message.content += '\n' + next.content; + next.merged = true; + } + } + })); + const lastAssistant = realLogs.findLast((message => !message.merged && 'assistant' === message.role)); + lastAssistant && Config.Settings.StripAssistant && (lastAssistant.strip = true); + const lastUser = realLogs.findLast((message => !message.merged && 'user' === message.role)); + lastUser && Config.Settings.StripHuman && (lastUser.strip = true); + const systemMessages = messagesClone.filter((message => 'system' === message.role && !('name' in message))); + systemMessages.forEach(((message, idx) => { + const scenario = message.content.match(rgxScenario)?.[1], personality = message.content.match(rgxPerson); + if (scenario) { + message.content = Config.ScenarioFormat.replace(/{{scenario}}/gim, scenario); + message.scenario = true; + } + if (3 === personality?.length) { + message.content = Config.PersonalityFormat.replace(/{{char}}/gim, personality[1]).replace(/{{personality}}/gim, personality[2]); + message.personality = true; + } + message.main = 0 === idx; + message.jailbreak = idx === systemMessages.length - 1; + ' ' === message.content && (message.discard = true); + })); + Config.Settings.AllSamples && !Config.Settings.NoSamples && realLogs.forEach((message => { + if (![ lastUser, lastAssistant ].includes(message)) { + if ('user' === message.role) { + message.name = message.customname ? message.name : 'example_user'; + message.role = 'system'; + } else if ('assistant' === message.role) { + message.name = message.customname ? message.name : 'example_assistant'; + message.role = 'system'; + } else if (!message.customname) { + throw Error('Invalid role ' + message.name); + } + } + })); + Config.Settings.NoSamples && !Config.Settings.AllSamples && sampleLogs.forEach((message => { + if ('example_user' === message.name) { + message.role = 'user'; + } else if ('example_assistant' === message.name) { + message.role = 'assistant'; + } else if (!message.customname) { + throw Error('Invalid role ' + message.name); + } + message.customname || delete message.name; + })); + let systems = []; + if (![ 'r', 'R', 'api' ].includes(type)) { + lastUser.strip = true; + systemMessages.forEach((message => message.discard = message.discard || 'c-c' === type ? !message.jailbreak : !message.jailbreak && !message.main)); + systems = systemMessages.filter((message => !message.discard)).map((message => `"${message.content.substring(0, 25).replace(/\n/g, '\\n').trim()}..."`)); + messagesClone.forEach((message => message.discard = message.discard || mergedLogs.includes(message) && ![ lastUser ].includes(message))); + } + const prompt = messagesClone.map(((message, idx) => { + if (message.merged || message.discard) { + return ''; + } + if (message.content.length < 1) { + return message.content; + } + let spacing = ''; +/******************************** */ + if (Config.Settings.xmlPlot) { + idx > 0 && (spacing = '\n\n'); + const prefix = message.customname ? message.role + ': ' + message.name.replaceAll('_', ' ') + ': ' : 'system' !== message.role || message.name ? Replacements[message.name || message.role] + ': ' : 'xmlPlot: ' + Replacements[message.role]; + return `${spacing}${message.strip ? '' : prefix}${message.content}`; + } else { +/******************************** */ + idx > 0 && (spacing = systemMessages.includes(message) ? '\n' : '\n\n'); + const prefix = message.customname ? message.name.replaceAll('_', ' ') + ': ' : 'system' !== message.role || message.name ? Replacements[message.name || message.role] + ': ' : '' + Replacements[message.role]; + return `${spacing}${message.strip ? '' : prefix}${'system' === message.role ? message.content : message.content.trim()}`; + } // + })); + return { + prompt: prompt.join(''), //genericFixes(prompt.join('')).trim(), + systems + }; + })(messages, type); +/******************************** */ + const legacy = /claude-([12]|instant)/i.test(model), messagesAPI = thirdKey || !legacy && !/<\|completeAPI\|>/.test(prompt) || /<\|messagesAPI\|>/.test(prompt), messagesLog = /<\|messagesLog\|>/.test(prompt), fusion = apiKey && messagesAPI && /<\|Fusion Mode\|>/.test(prompt), wedge = '\r'; + const stopSet = /<\|stopSet *(\[.*?\]) *\|>/.exec(prompt)?.[1], stopRevoke = /<\|stopRevoke *(\[.*?\]) *\|>/.exec(prompt)?.[1]; + if (stop_sequences || stopSet || stopRevoke) stop_sequences = JSON.parse(stopSet || '[]').concat(stop_sequences).concat(['\n\nHuman:', '\n\nAssistant:']).filter(item => !JSON.parse(stopRevoke || '[]').includes(item) && item); + apiKey && (type = oaiAPI ? 'oai_api' : messagesAPI ? 'msg_api' : type); + prompt = Config.Settings.xmlPlot ? xmlPlot(prompt, legacy && !/claude-2\.1/i.test(model)) : apiKey ? `\n\nHuman: ${genericFixes(prompt)}\n\nAssistant:` : genericFixes(prompt).trim(); + Config.Settings.FullColon && (prompt = !legacy ? + prompt.replace(fusion ? /\n(?!\nAssistant:\s*$)(?=\n(Human|Assistant):)/gs : apiKey ? /(? 0 ? ' ' + systems.join(' / ') : ''}`); + 'R' !== type || prompt || (prompt = '...regen...'); + Logger?.write(`\n\n-------\n[${(new Date).toLocaleString()}]\n${Main}\n####### ${model} (${type})\n${JSON.stringify({FusionMode: fusion, PassParams: Config.Settings.PassParams, stop_sequences, temperature, top_k, top_p}, null, 2)}\n\n####### regex:\n${regexLog}\n####### PROMPT ${tokens}t:\n${prompt}\n--\n####### REPLY:\n`); //Logger?.write(`\n\n-------\n[${(new Date).toLocaleString()}]\n####### MODEL: ${model}\n####### PROMPT (${type}):\n${prompt}\n--\n####### REPLY:\n`); + retryRegen || (fetchAPI = await (async (signal, model, prompt, temperature, type) => { +/******************************** */ + if (apiKey) { + let messages, system, key = apiKey[Math.floor(Math.random() * apiKey.length)]; + if (messagesAPI) { + const rounds = prompt.replace(/^(?!.*\n\nHuman:)/s, '\n\nHuman:').split('\n\nHuman:'); + messages = rounds.slice(1).flatMap(round => { + const turns = round.split('\n\nAssistant:'); + return [{role: 'user', content: turns[0].trim()}].concat(turns.slice(1).flatMap(turn => [{role: 'assistant', content: turn.trim()}])); + }).reduce((acc, current) => { + if (Config.Settings.FullColon && acc.length > 0 && (acc[acc.length - 1].role === current.role || !acc[acc.length - 1].content)) { + acc[acc.length - 1].content += (current.role === 'user' ? 'Human' : 'Assistant').replace(/.*/, legacy ? '\n$&﹕ ' : '\n' + wedge + '\n$&: ') + current.content; + } else acc.push(current); + return acc; + }, []).filter(message => message.content), oaiAPI ? messages.unshift({role: 'system', content: rounds[0].trim()}) : system = rounds[0].trim(); + messagesLog && console.log({system, messages}); + } + const res = await fetch((api_rProxy || 'https://api.anthropic.com').replace(/(\/v1)? *$/, thirdKey ? '$1' : '/v1').trim('/') + (oaiAPI ? '/chat/completions' : messagesAPI ? '/messages' : '/complete'), { + method: 'POST', + signal, + headers: { + 'anthropic-version': '2023-06-01', + 'authorization': 'Bearer ' + key, + 'Content-Type': 'application/json', + 'User-Agent': AI.agent(), + 'x-api-key': key, + }, + body: JSON.stringify({ + ...oaiAPI || messagesAPI ? { + max_tokens : max_tokens_to_sample, + messages, + system + } : { + max_tokens_to_sample, + prompt + }, + model, + stop_sequences, + stream: true, + temperature, + top_k, + top_p + }), + }); + await checkResErr(res); + return res; + } +/******************************** */ + const attachments = []; + if (Config.Settings.PromptExperiments) { + let splitedprompt = prompt.split('\n\nPlainPrompt:'); // + prompt = splitedprompt[0]; // + attachments.push({ + extracted_content: prompt, + file_name: 'paste.txt', //fileName(), + file_type: 'txt', //'text/plain', + file_size: Buffer.from(prompt).byteLength + }); + prompt = 'r' === type ? Config.PromptExperimentFirst : Config.PromptExperimentNext; + splitedprompt.length > 1 && (prompt += splitedprompt[1]); // + } + let res; + const body = { + attachments, + files: [], + model: isPro || forceModel ? model : undefined, + rendering_mode: 'raw', + ...Config.Settings.PassParams && { + max_tokens_to_sample, // + //stop_sequences, // + top_k, // + top_p, // + temperature + }, + prompt: prompt || '', + timezone: AI.zone() + }; + let headers = { + ...AI.hdr(Conversation.uuid || ''), + Accept: 'text/event-stream', + Cookie: getCookies() + }; + res = await (Config.Settings.Superfetch ? Superfetch : fetch)(`${Config.rProxy || AI.end()}/api/organizations/${uuidOrg || ''}/chat_conversations/${Conversation.uuid || ''}/completion`, { + stream: true, + signal, + method: 'POST', + body: JSON.stringify(body), + headers + }); + updateParams(res); + await checkResErr(res); + return res; + })(signal, model, prompt, temperature, type)); + const response = Writable.toWeb(res); + clewdStream = new ClewdStream({ + config: { + ...Config, + Settings: { + ...Config.Settings, + Superfetch: apiKey ? false : Config.Settings.Superfetch + } + }, //config: Config, + version: Main, + minSize: Config.BufferSize, + model, + streaming: true === body.stream, + abortControl, + source: fetchAPI + }, Logger); + titleTimer = setInterval((() => setTitle('recv ' + bytesToSize(clewdStream.size))), 300); + (!apiKey && Config.Settings.Superfetch) ? await Readable.toWeb(fetchAPI.body).pipeThrough(clewdStream).pipeTo(response) : await fetchAPI.body.pipeThrough(clewdStream).pipeTo(response); //Config.Settings.Superfetch ? await Readable.toWeb(fetchAPI.body).pipeThrough(clewdStream).pipeTo(response) : await fetchAPI.body.pipeThrough(clewdStream).pipeTo(response); + } catch (err) { + if ('AbortError' === err.name) { + res.end(); + } else { + nochange = true, exceeded_limit = err.exceeded_limit; // + err.planned ? console.log(`${err.status || 'Aborted'}!\n`) : console.error('Clewd:\n%o', err); //err.planned || console.error('Clewd:\n%o', err); + res.json({ + error: { + message: 'clewd: ' + (err.message || err.name || err.type), + type: err.type || err.name || err.code, + param: null, + code: err.code || 500 + } + }, 500); + } + } + clearInterval(titleTimer); + if (clewdStream) { + clewdStream.censored && console.warn('likely your account is hard-censored'); + prevImpersonated = clewdStream.impersonated; + exceeded_limit = clewdStream.error.exceeded_limit; // + clewdStream.error.status < 200 || clewdStream.error.status >= 300 || clewdStream.error.message === 'Overloaded' && (nochange = true); // + setTitle('ok ' + bytesToSize(clewdStream.size)); + if (clewdStream.compModel && !(AI.mdl().includes(clewdStream.compModel) || Config.unknownModels.includes(clewdStream.compModel)) && !apiKey) { + Config.unknownModels.push(clewdStream.compModel); + writeSettings(Config); + } + console.log(`${200 == fetchAPI.status ? '' : ''}${fetchAPI.status}!\n`); + clewdStream.empty(); + } + const shouldChange = exceeded_limit || !nochange && Config.Cookiecounter > 0 && changeflag++ >= Config.Cookiecounter - 1; // + if (!apiKey && (shouldChange || prevImpersonated)) { //if (prevImpersonated) { + try { + await deleteChat(Conversation.uuid); + } catch (err) {} +/******************************** */ + if (shouldChange) { + exceeded_limit && console.log(`Exceeded limit!\n`); + changeflag = 0; + CookieChanger(); + } +/******************************** */ + } + })); + })(req, res); + break; + + case '/v1/complete': + res.json({ + error: { + message: 'clewd: Set "Chat Completion source" to OpenAI instead of Claude. Enable "External" models aswell', + code: 404 + } + }, 404); + break; + + default: + !['/', '/v1', '/favicon.ico'].includes(req.url) && (console.log('unknown request: ' + req.url)); //console.log('unknown request: ' + req.url); + res.writeHead(200, {'Content-Type': 'text/html'}); // + res.write(`\n\n\n\n\n\n\n${Main}

完全开源、免费且禁止商用

点击复制反向代理: Copy Link
填入OpenAI API反向代理并选择OpenAI分类中的claude模型(酒馆需打开Show "External" models,仅在api模式有模型选择差异)

教程与FAQ: Rentry | Discord


❗警惕任何高风险cookie/伪api(25k cookie)购买服务,以及破坏中文AI开源共享环境倒卖免费资源抹去署名的群组(🈲黑名单:酒馆小二、AI新服务、浅睡(鲑鱼)、赛博女友制作人(青麈/overloaded/科普晓百生)🈲)\n\n`); // + res.end(); //res.json(// {// error: {// message: '404 Not Found',// type: 404,// param: null,// code: 404// }//}, 404); + } +})); + +!async function() { + await (async () => { + if (exists(ConfigPath)) { + const userConfig = require(ConfigPath), validConfigs = Object.keys(Config), parsedConfigs = Object.keys(userConfig), parsedSettings = Object.keys(userConfig.Settings), invalidConfigs = parsedConfigs.filter((config => !validConfigs.includes(config))), validSettings = Object.keys(Config.Settings); + UnknownSettings = parsedSettings.filter((setting => !validSettings.includes(setting))); + invalidConfigs.forEach((config => { + console.warn(`unknown config in config.js: ${config}`); + })); + UnknownSettings.forEach((setting => { + console.warn(`unknown setting in config.js: Settings.${setting}`); + })); + const missingConfigs = validConfigs.filter((config => !parsedConfigs.includes(config))), missingSettings = validSettings.filter((config => !parsedSettings.includes(config))); + missingConfigs.forEach((config => { + console.warn(`adding missing config in config.js: ${config}`); + userConfig[config] = Config[config]; + })); + missingSettings.forEach((setting => { + console.warn(`adding missing setting in config.js: Settings.${setting}`); + userConfig.Settings[setting] = Config.Settings[setting]; + })); + ChangedSettings = parsedSettings.filter((setting => Config.Settings[setting] !== userConfig.Settings[setting])); + (missingConfigs.length > 0 || missingSettings.length > 0) && await writeSettings(userConfig); + userConfig.Settings.LogMessages && (Logger = createWriteStream(LogPath)); + Config = { + ...Config, + ...userConfig + }; + } else { + Config.Cookie = 'SET YOUR COOKIE HERE'; + writeSettings(Config, true); + } + })(); +/***************************** */ + for (let key in Config) { + if (key === 'Settings') { + for (let setting in Config.Settings) { + Config.Settings[setting] = process.env[setting] ? convertToType(process.env[setting]) : Config.Settings[setting]; + } + } else { + Config[key] = process.env[key] ? convertToType(process.env[key]) : Config[key]; + } + } + Config.rProxy = Config.rProxy.replace(/\/$/, ''); + Config.CookieArray = [...new Set([Config.CookieArray].join(',').match(/(claude[-_][a-z0-9-_]*?@)?(sessionKey=)?sk-ant-sid01-[\w-]{86}-[\w-]{6}AA/g))]; + Config.unknownModels = Config.unknownModels.reduce((prev, cur) => !cur || prev.includes(cur) || AI.mdl().includes(cur) ? prev : [...prev, cur], []); + writeSettings(Config); + currentIndex = Config.CookieIndex > 0 ? Config.CookieIndex - 1 : Config.Cookiecounter >= 0 ? Math.floor(Math.random() * Config.CookieArray.length) : 0; +/***************************** */ + Proxy.listen(Config.Port, Config.Ip, onListen); + Proxy.on('error', (err => { + console.error('Proxy error\n%o', err); + })); +}(); + +const cleanup = async () => { + console.log('cleaning...'); + try { + await deleteChat(Conversation.uuid); + SuperfetchFoldersRm(); + Logger?.close(); + } catch (err) {} + process.exit(); +}; + +process.on('SIGHUP', cleanup); + +process.on('SIGTERM', cleanup); + +process.on('SIGINT', cleanup); + +process.on('exit', (async () => { + console.log('exiting...'); })); \ No newline at end of file