WWW.AIZHUSHOU.SHOP
标签聚合 AI

/tag/AI

LinuxDo 最新话题 · 2026-05-24 02:00:07+08:00 · tech

青山AI中转站 一家专业做 Claude Max 渠道的中转站 无需多言 尽管测试 不掺假 不混合出售 是max就是max 是什么渠道就是什么渠道 我们的优势:**充值1RMB:1USD 倍率1.2** 仅一个分组(ccmax 可外接 7*24(高缓存命中率,成本效益显著)的) 支持企业对接 团队对接 可开发票 如在使用中 有不懂的 尽管问 充没充值 在没在我这边用 都可以帮你解答(小白福音) 官网: 青山ClaudeAI中转站 点击直达 官网: https://zz.aigm99.cn (支持LinuxDo 一键登录) QQ群:1091368043(群里不定时还有抽奖活动 520当天发了一千多刀出去 在群里的朋友肯定都了解青山) 没有过多的图片展示 支持测试 注册了 进群联系客服或者群主 安排10刀余额 5 个帖子 - 3 位参与者 阅读完整话题

V2EX - 技术 · 2026-05-24 01:56:36+08:00 · tech

陆陆续续花了接近 3 天时间才改了一版本。 之前是 改完现在是 最初的版本是,AI 直出的文案和样式,文案不说人话,样式也看着着就别扭。 新版是,每一幕和 AI 描述自己的想法然后定制调出来的,个人感官上舒服了很多。 然后想着未来可能会用 AI 做更多新产品,手动调也太麻烦了,大家是套模板还是有啥神奇的 skill 或 prompt 可以提高效率么?求分享。 另外自荐产品 名字:叙野 Lorewild 网址: https://lorewild.com 推特: https://x.com/ethan180953 定位是“用 AI 故事学外语”和“AI 角色对话学外语”,支持中文、英语、日语、韩语、德语、西班牙语六个语言。类似酒馆,也可以自建角色和剧本,目前提供了轻量的学习层。 md ,一点流量没有,得多尝试 build in public 啥的整起来。

V2EX - 技术 · 2026-05-24 01:32:29+08:00 · tech

陆陆续续花了接近 3 天时间才改了一版本。 之前是 改完现在是 最初的版本是,AI 直出的文案和样式,文案不说人话,样式也看着着就别扭。 新版是,每一幕和 AI 描述自己的想法然后定制调出来的,个人感官上舒服了很多。 然后想着未来可能会用 AI 做更多新产品,手动调也太麻烦了,大家是套模板还是有啥神奇的 skill 或 prompt 可以提高效率么?求分享。 另外自荐产品 名字:叙野 Lorewild 网址: https://lorewild.com 推特: https://x.com/ethan180953 定位是“用 AI 故事学外语”和“AI 角色对话学外语”,支持中文、英语、日语、韩语、德语、西班牙语六个语言。类似酒馆,也可以自建角色和剧本,目前提供了轻量的学习层。 md ,一点流量没有,得多尝试 build in public 啥的整起来。

V2EX - 技术 · 2026-05-24 00:59:28+08:00 · tech

陆陆续续花了接近 3 天时间才改了一版本。 之前是 改完现在是 最初的版本是,AI 直出的文案和样式,文案不说人话,样式也看着着就别扭。 新版是,每一幕和 AI 描述自己的想法然后定制调出来的,个人感官上舒服了很多。 然后想着未来可能会用 AI 做更多新产品,手动调也太麻烦了,大家是套模板还是有啥神奇的 skill 或 prompt 可以提高效率么?求分享。 另外自荐产品 名字:叙野 Lorewild 网址: https://lorewild.com 推特: https://x.com/ethan180953 定位是“用 AI 故事学外语”和“AI 角色对话学外语”,支持中文、英语、日语、韩语、德语、西班牙语六个语言。类似酒馆,也可以自建角色和剧本,目前提供了轻量的学习层。 md ,一点流量没有,得多尝试 build in public 啥的整起来。

LinuxDo 最新话题 · 2026-05-24 00:41:06+08:00 · tech

摸鱼时间让Ai写了个sub2api自动测活工具 开发调优 感谢站里的佬友贡献的公益站 有时候佬们贡献公益站出现大量掉号的情况,请求的时候会挨个去轮询,所以下游可能会长时间导致持续链接却没有响应的状态。 然后,就有了这个自动测活工具脚本,测试过的账号会根据相应的结果自动开启或关闭账号 。 // ==UserScript== // @name Sub2API 账号模型巡检并自动下线 // @namespace https://s… // ==UserScript== // @name Sub2API 账号模型巡检并自动下线 // @namespace https://sinry.example // @version 0.2.0 // @description 分批巡检账号模型;401 自动关闭并永久跳过;429 usage_limit_reached 视为正常;支持 5h 冷却和进度条 // @match http://127.0.0.1:8317/admin/accounts* // @run-at document-start // @grant none // ==/UserScript== (function () { 'use strict'; const CONFIG = { apiBase: location.origin, pageSize: 100, defaultTimeoutMs: 45000, defaultBatchSize: 100, recentCheckWindowMs: 5 * 60 * 60 * 1000, prompt: 'hi', onlyCheckSchedulable: false, stopOnFirstModelFailure: true, preferredModels: ['gpt-5.4', 'gpt-4o-mini', 'gpt-4o', 'gpt-4.1', 'gpt-4.1-mini'], defaultTestModel: 'gpt-5.4', pageAuthTokenKey: 'auth_token', authStorageKey: '__sub2api_checker_auth__', timeoutStorageKey: '__sub2api_checker_timeout_ms__', testModelStorageKey: '__sub2api_checker_test_model__', batchSizeStorageKey: '__sub2api_checker_batch_size__', cacheStorageKey: '__sub2api_checker_account_cache_v1__', cacheVersion: 1, }; function storageGet(storage, key) { try { return storage.getItem(key); } catch (_) { return null; } } function storageSet(storage, key, value) { try { storage.setItem(key, value); return true; } catch (_) { return false; } } function normalizeTimestampMap(input) { const result = {}; if (!input || typeof input !== 'object') return result; for (const [rawKey, rawValue] of Object.entries(input)) { const key = String(rawKey || '').trim(); const value = Number(rawValue); if (!key || !Number.isFinite(value) || value <= 0) continue; result[key] = value; } return result; } function loadCheckerCache() { const fallback = { version: CONFIG.cacheVersion, recentChecks: {}, unauthorizedAccounts: {}, }; try { const raw = storageGet(localStorage, CONFIG.cacheStorageKey); if (!raw) return fallback; const parsed = JSON.parse(raw); return { version: CONFIG.cacheVersion, recentChecks: normalizeTimestampMap(parsed?.recentChecks), unauthorizedAccounts: normalizeTimestampMap(parsed?.unauthorizedAccounts), }; } catch (err) { console.warn('[sub2api-checker] failed to load cache:', err); return fallback; } } function persistCheckerCache() { storageSet(localStorage, CONFIG.cacheStorageKey, JSON.stringify(state.cache)); } function getCachedAuthToken() { const raw = storageGet(localStorage, CONFIG.pageAuthTokenKey) || storageGet(sessionStorage, CONFIG.pageAuthTokenKey) || storageGet(localStorage, CONFIG.authStorageKey) || ''; return raw ? (raw.startsWith('Bearer ') ? raw : `Bearer ${raw}`) : ''; } function getAccountKey(accountOrId) { const value = typeof accountOrId === 'object' ? accountOrId?.id : accountOrId; const id = Number(value); if (!Number.isFinite(id) || id <= 0) return ''; return String(Math.trunc(id)); } function pruneCache(now = Date.now()) { const cutoff = now - CONFIG.recentCheckWindowMs; let dirty = false; for (const [key, ts] of Object.entries(state.cache.recentChecks)) { if (!Number.isFinite(ts) || ts < cutoff) { delete state.cache.recentChecks[key]; dirty = true; } } for (const [key, ts] of Object.entries(state.cache.unauthorizedAccounts)) { if (!Number.isFinite(ts) || ts <= 0) { delete state.cache.unauthorizedAccounts[key]; dirty = true; } } if (dirty) persistCheckerCache(); } function hasRecentCheck(accountOrId, now = Date.now()) { const key = getAccountKey(accountOrId); if (!key) return false; const ts = Number(state.cache.recentChecks[key] || 0); return Number.isFinite(ts) && now - ts < CONFIG.recentCheckWindowMs; } function markRecentCheck(accountOrId, now = Date.now()) { const key = getAccountKey(accountOrId); if (!key) return false; const wasRecent = hasRecentCheck(key, now); state.cache.recentChecks[key] = now; persistCheckerCache(); return !wasRecent; } function isUnauthorizedAccount(accountOrId) { const key = getAccountKey(accountOrId); if (!key) return false; const ts = state.cache.unauthorizedAccounts[key]; const value = Number(ts); return Number.isFinite(value) && value > 0; } function markUnauthorizedAccount(accountOrId, now = Date.now()) { const key = getAccountKey(accountOrId); if (!key) return false; const existed = isUnauthorizedAccount(key); state.cache.unauthorizedAccounts[key] = now; persistCheckerCache(); return !existed; } const state = { authHeader: getCachedAuthToken(), timeoutMs: Number(storageGet(localStorage, CONFIG.timeoutStorageKey) || CONFIG.defaultTimeoutMs), testModel: storageGet(localStorage, CONFIG.testModelStorageKey) || CONFIG.defaultTestModel, batchSize: Number(storageGet(localStorage, CONFIG.batchSizeStorageKey) || CONFIG.defaultBatchSize), cache: loadCheckerCache(), running: false, stopRequested: false, panelReady: false, collapsed: true, stats: { total: 0, checked: 0, ok: 0, enabled: 0, disabled: 0, skipped: 0, failed: 0, excludedUnauthorized: 0, }, progress: { totalPool: 0, checkedInWindow: 0, batchTotal: 0, batchDone: 0, excludedUnauthorized: 0, coolingCount: 0, eligibleCount: 0, }, }; function log(msg, type = 'info') { const time = new Date().toLocaleTimeString(); const line = `[${time}] ${msg}`; console[type === 'error' ? 'error' : 'log'](`[sub2api-checker] ${line}`); const box = document.querySelector('#sub2api-checker-log'); if (!box) return; const color = type === 'error' ? '#ff7875' : type === 'warn' ? '#ffd666' : type === 'success' ? '#95de64' : '#d9d9d9'; const row = document.createElement('div'); row.style.color = color; row.textContent = line; box.appendChild(row); box.scrollTop = box.scrollHeight; } function saveAuth(auth) { if (!auth || typeof auth !== 'string') return; const normalized = auth.startsWith('Bearer ') ? auth : `Bearer ${auth}`; state.authHeader = normalized; storageSet(localStorage, CONFIG.authStorageKey, normalized); const input = document.querySelector('#sub2api-checker-auth'); if (input && !input.value) input.value = normalized; log('已捕获 Authorization', 'success'); } function saveTimeoutMs(timeoutMs) { const n = Number(timeoutMs); if (!Number.isFinite(n) || n < 1000) return false; state.timeoutMs = n; storageSet(localStorage, CONFIG.timeoutStorageKey, String(n)); const input = document.querySelector('#sub2api-checker-timeout'); if (input) input.value = String(Math.floor(n / 1000)); return true; } function saveTestModel(model) { const normalized = String(model || '').trim(); if (!normalized) return false; state.testModel = normalized; storageSet(localStorage, CONFIG.testModelStorageKey, normalized); const input = document.querySelector('#sub2api-checker-test-model'); if (input) input.value = normalized; return true; } function saveBatchSize(batchSize) { const n = Math.floor(Number(batchSize)); if (!Number.isFinite(n) || n < 1) return false; state.batchSize = n; storageSet(localStorage, CONFIG.batchSizeStorageKey, String(n)); const input = document.querySelector('#sub2api-checker-batch-size'); if (input) input.value = String(n); return true; } function injectAuthSniffer() { const script = document.createElement('script'); script.textContent = ` (() => { const emit = (auth) => { if (!auth) return; document.dispatchEvent(new CustomEvent('__sub2api_checker_auth__', { detail: auth })); }; const pickAuth = (headersLike) => { try { if (!headersLike) return ''; if (headersLike instanceof Headers) { return headersLike.get('Authorization') || headersLike.get('authorization') || ''; } if (Array.isArray(headersLike)) { for (const [k, v] of headersLike) { if (String(k).toLowerCase() === 'authorization') return v || ''; } return ''; } if (typeof headersLike === 'object') { for (const key of Object.keys(headersLike)) { if (key.toLowerCase() === 'authorization') return headersLike[key] || ''; } } } catch (_) {} return ''; }; const origFetch = window.fetch; if (origFetch) { window.fetch = function(input, init) { const auth = pickAuth(init && init.headers) || pickAuth(input && input.headers); if (auth) emit(auth); return origFetch.apply(this, arguments); }; } const origOpen = XMLHttpRequest.prototype.open; const origSetHeader = XMLHttpRequest.prototype.setRequestHeader; XMLHttpRequest.prototype.open = function() { this.__sub2apiAuth = ''; return origOpen.apply(this, arguments); }; XMLHttpRequest.prototype.setRequestHeader = function(name, value) { if (String(name).toLowerCase() === 'authorization' && value) { this.__sub2apiAuth = value; emit(value); } return origSetHeader.apply(this, arguments); }; })(); `; document.documentElement.appendChild(script); script.remove(); document.addEventListener('__sub2api_checker_auth__', (event) => { saveAuth(event.detail); }); } function updateStats() { const el = document.querySelector('#sub2api-checker-stats'); if (!el) return; const s = state.stats; el.textContent = `全部账号 ${s.total} | 已处理 ${s.checked} | 正常 ${s.ok} | 已启用 ${s.enabled} | 已关闭 ${s.disabled} | 跳过 ${s.skipped} | 异常 ${s.failed} | 401排除 ${s.excludedUnauthorized}`; } function updateProgress() { const p = state.progress; const progressSummary = document.querySelector('#sub2api-checker-progress-summary'); const progressFill = document.querySelector('#sub2api-checker-progress-fill'); const batchSummary = document.querySelector('#sub2api-checker-batch-summary'); const batchFill = document.querySelector('#sub2api-checker-batch-fill'); if (progressSummary) { progressSummary.textContent = `已巡检 ${p.checkedInWindow} / 全部账号 ${p.totalPool}`; } if (progressFill) { const ratio = p.totalPool > 0 ? Math.min(100, Math.max(0, (p.checkedInWindow / p.totalPool) * 100)) : 0; progressFill.style.width = `${ratio}%`; } if (batchSummary) { batchSummary.textContent = `本轮 ${p.batchDone} / ${p.batchTotal} | 401排除 ${p.excludedUnauthorized} | 5h冷却 ${p.coolingCount} | 可巡检 ${p.eligibleCount}`; } if (batchFill) { const ratio = p.batchTotal > 0 ? Math.min(100, Math.max(0, (p.batchDone / p.batchTotal) * 100)) : 0; batchFill.style.width = `${ratio}%`; } } function resetProgress() { state.progress = { totalPool: 0, checkedInWindow: 0, batchTotal: 0, batchDone: 0, excludedUnauthorized: 0, coolingCount: 0, eligibleCount: 0, }; updateProgress(); } function updatePanelCollapsed() { const shell = document.querySelector('#sub2api-checker-shell'); const root = document.querySelector('#sub2api-checker-panel'); const toggle = document.querySelector('#sub2api-checker-toggle'); if (!root || !toggle || !shell) return; root.style.width = state.collapsed ? '0px' : '460px'; root.style.opacity = state.collapsed ? '0' : '1'; root.style.marginRight = state.collapsed ? '0px' : '12px'; root.style.pointerEvents = state.collapsed ? 'none' : 'auto'; root.style.transform = state.collapsed ? 'translateX(12px)' : 'translateX(0)'; toggle.textContent = state.collapsed ? '账号巡检' : '收起'; toggle.style.borderRadius = state.collapsed ? '10px 0 0 10px' : '10px'; shell.style.pointerEvents = 'auto'; } function ensurePanel() { if (state.panelReady) return; state.panelReady = true; const shell = document.createElement('div'); shell.id = 'sub2api-checker-shell'; shell.style.cssText = ` position: fixed; right: 0; top: 120px; z-index: 1000000; display: flex; flex-direction: row; align-items: flex-start; pointer-events: auto; `; document.body.appendChild(shell); const toggle = document.createElement('button'); toggle.id = 'sub2api-checker-toggle'; toggle.style.cssText = ` padding: 10px 8px; border: 0; border-radius: 10px 0 0 10px; background: #1677ff; color: #fff; cursor: pointer; writing-mode: vertical-rl; text-orientation: mixed; box-shadow: 0 8px 24px rgba(0,0,0,.25); transition: transform .28s ease, box-shadow .28s ease, border-radius .28s ease; font: 12px/1.2 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,PingFang SC,Microsoft YaHei,sans-serif; `; toggle.addEventListener('mouseenter', () => { toggle.style.transform = 'translateX(-2px)'; toggle.style.boxShadow = '0 10px 28px rgba(0,0,0,.32)'; }); toggle.addEventListener('mouseleave', () => { toggle.style.transform = 'translateX(0)'; toggle.style.boxShadow = '0 8px 24px rgba(0,0,0,.25)'; }); toggle.addEventListener('click', () => { state.collapsed = !state.collapsed; updatePanelCollapsed(); }); shell.appendChild(toggle); const root = document.createElement('div'); root.id = 'sub2api-checker-panel'; root.style.cssText = ` width: 0; opacity: 0; overflow: hidden; transition: width .28s ease, opacity .22s ease, margin-right .28s ease, transform .28s ease; transform: translateX(12px); `; root.innerHTML = ` <div id="sub2api-checker-panel-inner" style=" width:460px; background:rgba(16, 18, 27, 0.96); color:#fff; border:1px solid #30363d; border-radius:12px; box-shadow:0 8px 24px rgba(0,0,0,.35); font:12px/1.5 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,PingFang SC,Microsoft YaHei,sans-serif; overflow:hidden; "> <div style="padding:12px 14px;border-bottom:1px solid #30363d;font-weight:700;">Sub2API 账号模型巡检</div> <div style="padding:12px 14px;display:flex;flex-direction:column;gap:8px;"> <label style="display:flex;flex-direction:column;gap:4px;"> <span>Authorization(优先自动捕获,抓不到再手填)</span> <input id="sub2api-checker-auth" type="text" placeholder="Bearer xxxxxx" style="width:100%;box-sizing:border-box;padding:8px;border-radius:8px;border:1px solid #434a57;background:#111723;color:#fff;" /> </label> <div style="display:flex;gap:8px;"> <label style="display:flex;flex:1;flex-direction:column;gap:4px;"> <span>单模型超时时间(秒)</span> <input id="sub2api-checker-timeout" type="number" min="1" step="1" placeholder="45" style="width:100%;box-sizing:border-box;padding:8px;border-radius:8px;border:1px solid #434a57;background:#111723;color:#fff;" /> </label> <label style="display:flex;flex:1;flex-direction:column;gap:4px;"> <span>本次巡检数量</span> <input id="sub2api-checker-batch-size" type="number" min="1" step="1" placeholder="100" style="width:100%;box-sizing:border-box;padding:8px;border-radius:8px;border:1px solid #434a57;background:#111723;color:#fff;" /> </label> </div> <label style="display:flex;flex-direction:column;gap:4px;"> <span>测试模型</span> <input id="sub2api-checker-test-model" type="text" placeholder="gpt-5.4" style="width:100%;box-sizing:border-box;padding:8px;border-radius:8px;border:1px solid #434a57;background:#111723;color:#fff;" /> </label> <div style="display:flex;gap:8px;align-items:center;"> <button id="sub2api-checker-start" style="flex:1;padding:8px 10px;border:0;border-radius:8px;background:#1677ff;color:#fff;cursor:pointer;">开始巡检</button> <button id="sub2api-checker-stop" style="flex:1;padding:8px 10px;border:0;border-radius:8px;background:#fa541c;color:#fff;cursor:pointer;">停止</button> </div> <div style="display:flex;flex-direction:column;gap:6px;padding:10px;border:1px solid #30363d;border-radius:8px;background:#0f1521;"> <div id="sub2api-checker-progress-summary" style="color:#e6f4ff;">已巡检 0 / 全部账号 0</div> <div style="height:8px;border-radius:999px;background:#1f2430;overflow:hidden;"> <div id="sub2api-checker-progress-fill" style="width:0%;height:100%;background:linear-gradient(90deg,#1677ff,#69b1ff);transition:width .2s ease;"></div> </div> <div id="sub2api-checker-batch-summary" style="color:#bfbfbf;">本轮 0 / 0 | 401排除 0 | 5h冷却 0 | 可巡检 0</div> <div style="height:8px;border-radius:999px;background:#1f2430;overflow:hidden;"> <div id="sub2api-checker-batch-fill" style="width:0%;height:100%;background:linear-gradient(90deg,#52c41a,#b7eb8f);transition:width .2s ease;"></div> </div> </div> <div id="sub2api-checker-stats" style="color:#bfbfbf;">全部账号 0 | 已处理 0 | 正常 0 | 已启用 0 | 已关闭 0 | 跳过 0 | 异常 0 | 401排除 0</div> <div id="sub2api-checker-log" style="height:320px;overflow:auto;background:#0b0f17;border:1px solid #30363d;border-radius:8px;padding:8px;"></div> </div> </div> `; shell.appendChild(root); const authInput = root.querySelector('#sub2api-checker-auth'); authInput.value = state.authHeader; authInput.addEventListener('change', () => { const v = authInput.value.trim(); if (v) saveAuth(v); }); const timeoutInput = root.querySelector('#sub2api-checker-timeout'); timeoutInput.value = String(Math.floor(state.timeoutMs / 1000)); timeoutInput.addEventListener('change', () => { const sec = Number(timeoutInput.value || 0); if (!saveTimeoutMs(sec * 1000)) { timeoutInput.value = String(Math.floor(state.timeoutMs / 1000)); log('超时时间无效,需大于等于 1 秒', 'error'); return; } log(`已设置单模型超时 ${sec} 秒`, 'success'); }); const batchInput = root.querySelector('#sub2api-checker-batch-size'); batchInput.value = String(Math.max(1, Math.floor(state.batchSize || CONFIG.defaultBatchSize))); batchInput.addEventListener('change', () => { const size = Number(batchInput.value || 0); if (!saveBatchSize(size)) { batchInput.value = String(Math.max(1, Math.floor(state.batchSize || CONFIG.defaultBatchSize))); log('巡检数量无效,需大于等于 1', 'error'); return; } log(`已设置本次巡检数量 ${state.batchSize}`, 'success'); }); const testModelInput = root.querySelector('#sub2api-checker-test-model'); testModelInput.value = state.testModel; testModelInput.addEventListener('change', () => { const model = testModelInput.value.trim(); if (!saveTestModel(model)) { testModelInput.value = state.testModel; log('测试模型不能为空', 'error'); return; } log(`已设置测试模型 ${state.testModel}`, 'success'); }); root.querySelector('#sub2api-checker-start').addEventListener('click', () => run().catch((err) => { log(`运行异常:${err.message}`, 'error'); state.running = false; updateStats(); updateProgress(); })); root.querySelector('#sub2api-checker-stop').addEventListener('click', () => { state.stopRequested = true; log('已请求停止,当前请求结束后退出', 'warn'); }); updatePanelCollapsed(); updateStats(); updateProgress(); } async function waitDomReady() { if (document.body) return; await new Promise((resolve) => { const timer = setInterval(() => { if (document.body) { clearInterval(timer); resolve(); } }, 50); }); } async function apiFetch(url, options = {}) { const headers = new Headers(options.headers || {}); if (state.authHeader && !headers.has('Authorization')) { headers.set('Authorization', state.authHeader); } return fetch(url, { ...options, headers, credentials: 'include', }); } async function fetchAccounts() { let page = 1; const items = []; while (true) { const url = new URL('/api/v1/admin/accounts', CONFIG.apiBase); url.searchParams.set('page', String(page)); url.searchParams.set('page_size', String(CONFIG.pageSize)); url.searchParams.set('platform', ''); url.searchParams.set('type', ''); url.searchParams.set('status', ''); url.searchParams.set('privacy_mode', ''); url.searchParams.set('group', ''); url.searchParams.set('search', ''); url.searchParams.set('timezone', Intl.DateTimeFormat().resolvedOptions().timeZone || 'Asia/Shanghai'); const resp = await apiFetch(url.toString(), { headers: { Accept: 'application/json, text/plain, */*' }, }); if (!resp.ok) throw new Error(`账号列表请求失败:HTTP ${resp.status}`); const json = await resp.json(); if (json.code !== 0) throw new Error(`账号列表返回异常:${json.message || json.code}`); const pageItems = json?.data?.items || []; items.push(...pageItems); const pages = Number(json?.data?.pages || 1); if (page >= pages || pageItems.length === 0) break; page += 1; } return items; } function getModels(account) { const targetModel = String(state.testModel || '').trim(); if (targetModel) return [targetModel]; const mapping = account?.credentials?.model_mapping || {}; const keys = Object.keys(mapping).filter(Boolean); if (keys.length <= 1) return keys; const preferred = []; for (const model of CONFIG.preferredModels) { if (keys.includes(model)) preferred.push(model); } const rest = keys.filter((k) => !preferred.includes(k)).sort(); return [...preferred, ...rest]; } function extractStatusCode(text) { const source = String(text || ''); const matchers = [ /API returned (\d{3})/i, /Authentication failed \((\d{3})\)/i, /\bHTTP (\d{3})\b/i, /\bstatus(?: code)?[:= ]+(\d{3})\b/i, ]; for (const matcher of matchers) { const matched = source.match(matcher); if (matched) { const code = Number(matched[1]); if (Number.isFinite(code)) return code; } } return null; } function extractTrailingJson(text) { const source = String(text || ''); const start = source.indexOf('{'); const end = source.lastIndexOf('}'); if (start < 0 || end <= start) return null; try { return JSON.parse(source.slice(start, end + 1)); } catch (_) { return null; } } function classifyFailure(reasonText, explicitStatus) { const raw = String(reasonText || '').trim(); const status = Number.isFinite(Number(explicitStatus)) ? Number(explicitStatus) : extractStatusCode(raw); const body = extractTrailingJson(raw); const errorType = String(body?.error?.type || body?.type || '').trim(); const bodyMessage = String(body?.error?.message || body?.message || '').trim(); const normalizedReason = bodyMessage || raw || '未知错误'; if ( status === 401 || /Authentication failed \(401\)/i.test(raw) || /API returned 401/i.test(raw) || /\b401 Unauthorized\b/i.test(raw) ) { return { classification: 'unauthorized', rawStatus: 401, reason: normalizedReason, rawReason: raw || normalizedReason, }; } if ( status === 429 && ( errorType === 'usage_limit_reached' || /usage_limit_reached/i.test(raw) || /The usage limit has been reached/i.test(raw) ) ) { return { classification: 'quota_exhausted', rawStatus: 429, reason: normalizedReason, rawReason: raw || normalizedReason, }; } return { classification: 'other_failure', rawStatus: status, reason: raw || normalizedReason, rawReason: raw || normalizedReason, }; } function isBackendUnauthorizedDisabled(account) { if (!account || account.schedulable !== false) return false; return classifyFailure(account.error_message || '').classification === 'unauthorized'; } function shouldExcludeUnauthorized(account) { if (!account) return false; if (isUnauthorizedAccount(account.id)) return true; if (isBackendUnauthorizedDisabled(account)) { markUnauthorizedAccount(account.id); return true; } return false; } function buildRunPools(accounts, now = Date.now()) { pruneCache(now); const pool = []; let excludedUnauthorized = 0; let coolingCount = 0; for (const account of accounts) { if (!getAccountKey(account?.id)) continue; if (shouldExcludeUnauthorized(account)) { excludedUnauthorized += 1; continue; } if (CONFIG.onlyCheckSchedulable && !account.schedulable) { continue; } pool.push(account); } const eligible = []; for (const account of pool) { if (hasRecentCheck(account.id, now)) { coolingCount += 1; } else { eligible.push(account); } } return { pool, eligible, excludedUnauthorized, coolingCount, checkedInWindow: coolingCount, }; } async function testModel(accountId, modelId) { const controller = new AbortController(); let timer = null; const clearTimer = () => { if (timer) { clearTimeout(timer); timer = null; } }; const resetTimer = () => { clearTimer(); timer = setTimeout(() => controller.abort(), state.timeoutMs); }; try { resetTimer(); const resp = await apiFetch(`${CONFIG.apiBase}/api/v1/admin/accounts/${accountId}/test`, { method: 'POST', headers: { Accept: '*/*', 'Content-Type': 'application/json', }, body: JSON.stringify({ model_id: modelId, prompt: CONFIG.prompt }), signal: controller.signal, }); if (!resp.ok) { clearTimer(); const text = await resp.text(); const classified = classifyFailure(text, resp.status); return { ok: false, classification: classified.classification, reason: classified.reason, rawReason: classified.rawReason, rawStatus: classified.rawStatus, }; } const reader = resp.body?.getReader(); if (!reader) { clearTimer(); const text = await resp.text(); return { ok: false, classification: 'other_failure', reason: `无响应流:${String(text || '').slice(0, 200)}`, rawReason: text, rawStatus: resp.status, }; } const decoder = new TextDecoder(); let buffer = ''; while (true) { resetTimer(); const { value, done } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }).replace(/\r/g, ''); let splitIndex; while ((splitIndex = buffer.indexOf('\n\n')) >= 0) { const chunk = buffer.slice(0, splitIndex); buffer = buffer.slice(splitIndex + 2); const dataLines = chunk .split('\n') .map((line) => line.trim()) .filter((line) => line.startsWith('data:')) .map((line) => line.slice(5).trim()); for (const line of dataLines) { if (!line) continue; let event; try { event = JSON.parse(line); } catch (_) { continue; } if (event.type === 'error') { clearTimer(); const classified = classifyFailure(event.error || '未知错误'); return { ok: false, classification: classified.classification, reason: classified.reason, rawReason: classified.rawReason, rawStatus: classified.rawStatus, }; } if (event.type === 'test_complete') { clearTimer(); return { ok: !!event.success, classification: event.success ? 'success' : 'other_failure', reason: event.success ? 'success' : 'test_complete=false', rawReason: event.success ? 'success' : 'test_complete=false', rawStatus: resp.status, }; } } } } clearTimer(); return { ok: false, classification: 'other_failure', reason: '响应流结束但没有 test_complete', rawReason: '响应流结束但没有 test_complete', rawStatus: resp.status, }; } catch (err) { clearTimer(); return { ok: false, classification: 'other_failure', reason: err?.name === 'AbortError' ? '请求超时' : (err?.message || String(err)), rawReason: err?.message || String(err), rawStatus: null, }; } } async function setAccountSchedulable(accountId, schedulable) { const resp = await apiFetch(`${CONFIG.apiBase}/api/v1/admin/accounts/${accountId}/schedulable`, { method: 'POST', headers: { Accept: 'application/json, text/plain, */*', 'Content-Type': 'application/json', }, body: JSON.stringify({ schedulable: !!schedulable }), }); if (!resp.ok) { return { ok: false, reason: `HTTP ${resp.status}` }; } const json = await resp.json(); if (json.code !== 0) { return { ok: false, reason: json.message || `code=${json.code}` }; } return { ok: true, data: json.data }; } function resetStats() { state.stats = { total: 0, checked: 0, ok: 0, enabled: 0, disabled: 0, skipped: 0, failed: 0, excludedUnauthorized: 0, }; resetProgress(); updateStats(); const logBox = document.querySelector('#sub2api-checker-log'); if (logBox) logBox.innerHTML = ''; } async function ensureAuth() { const cached = getCachedAuthToken(); if (cached) { saveAuth(cached); return true; } if (state.authHeader) return true; const fromInput = document.querySelector('#sub2api-checker-auth')?.value?.trim(); if (fromInput) { saveAuth(fromInput); return true; } const manual = window.prompt('没有自动捕获到 Authorization,请粘贴 Bearer token'); if (!manual) return false; saveAuth(manual.trim()); return true; } function markAccountCompleted(accountId) { const counted = markRecentCheck(accountId); state.stats.checked += 1; state.progress.batchDone += 1; if (counted) { state.progress.checkedInWindow += 1; } updateStats(); updateProgress(); } async function run() { if (state.running) { log('已有任务在运行', 'warn'); return; } if (!(await ensureAuth())) { log('缺少 Authorization,已取消', 'error'); return; } state.running = true; state.stopRequested = false; resetStats(); pruneCache(); try { state.collapsed = false; updatePanelCollapsed(); const batchSize = Math.max(1, Math.floor(Number(state.batchSize) || CONFIG.defaultBatchSize)); const now = Date.now(); log('开始拉取账号列表'); const accounts = await fetchAccounts(); const pools = buildRunPools(accounts, now); const selectedAccounts = pools.eligible.slice(0, batchSize); state.stats.total = pools.pool.length; state.stats.excludedUnauthorized = pools.excludedUnauthorized; state.progress.totalPool = pools.pool.length; state.progress.checkedInWindow = pools.checkedInWindow; state.progress.batchTotal = selectedAccounts.length; state.progress.batchDone = 0; state.progress.excludedUnauthorized = pools.excludedUnauthorized; state.progress.coolingCount = pools.coolingCount; state.progress.eligibleCount = pools.eligible.length; updateStats(); updateProgress(); log(`共获取 ${accounts.length} 个账号`, 'success'); log(`401 已排除 ${pools.excludedUnauthorized} 个;全部账号 ${pools.pool.length} 个;5h 内已巡检 ${pools.checkedInWindow} 个`, 'success'); log(`本轮计划巡检 ${batchSize} 个,实际入选 ${selectedAccounts.length} 个`, 'success'); if (!selectedAccounts.length) { log('当前没有可巡检账号:要么都在 5 小时冷却内,要么都已被 401 排除', 'warn'); return; } for (const account of selectedAccounts) { if (state.stopRequested) break; const title = `#${account.id} ${account.name || '(未命名)'}`; const models = getModels(account); if (!models.length) { state.stats.failed += 1; log(`${title} 没有 model_mapping,准备关闭`, 'error'); const off = await setAccountSchedulable(account.id, false); if (off.ok) { state.stats.disabled += 1; log(`${title} 已关闭 schedulable`, 'success'); } else { log(`${title} 关闭失败:${off.reason}`, 'error'); } markAccountCompleted(account.id); continue; } log(`${title} 开始测试 ${models.length} 个模型`); let accountOk = true; let failClassification = 'success'; let failReason = ''; let sawQuotaExhausted = false; let sawActualSuccess = false; let interrupted = false; for (let index = 0; index < models.length; index += 1) { if (state.stopRequested && index > 0) { interrupted = true; break; } const model = models[index]; log(`${title} 测试模型 ${model}`); const result = await testModel(account.id, model); if (result.ok) { sawActualSuccess = true; log(`${title} 模型 ${model} 正常`, 'success'); continue; } if (result.classification === 'quota_exhausted') { sawQuotaExhausted = true; log(`${title} 模型 ${model} 额度已用完,视为正常:${result.reason}`, 'warn'); continue; } accountOk = false; failClassification = result.classification; failReason = `模型 ${model} 异常:${result.reason}`; log(`${title} ${failReason}`, 'error'); if (CONFIG.stopOnFirstModelFailure) break; } if (interrupted) { log(`${title} 已停止,当前账号未完成全部模型测试,不记入本轮结果`, 'warn'); break; } if (accountOk) { state.stats.ok += 1; if (!account.schedulable && sawActualSuccess) { const on = await setAccountSchedulable(account.id, true); if (on.ok) { state.stats.enabled += 1; log(`${title} 全部模型正常,已重新启用 schedulable`, 'success'); } else { log(`${title} 模型正常但重新启用失败:${on.reason}`, 'error'); } } else if (!account.schedulable && sawQuotaExhausted && !sawActualSuccess) { log(`${title} 命中 usage_limit_reached,视为正常,但保持当前关闭状态不自动启用`, 'warn'); } else if (sawQuotaExhausted && !sawActualSuccess) { log(`${title} 模型额度已用完,账号视为正常`, 'success'); } else { log(`${title} 全部模型正常`, 'success'); } markAccountCompleted(account.id); continue; } state.stats.failed += 1; if (failClassification === 'unauthorized') { if (account.schedulable) { const off = await setAccountSchedulable(account.id, false); if (off.ok) { state.stats.disabled += 1; markUnauthorizedAccount(account.id); log(`${title} 401 已关闭 schedulable,并标记为永久跳过(原因:${failReason})`, 'success'); } else { log(`${title} 401 关闭失败:${off.reason}`, 'error'); } } else { markUnauthorizedAccount(account.id); log(`${title} 401 账号已是关闭状态,已标记为永久跳过`, 'success'); } } else { const off = await setAccountSchedulable(account.id, false); if (off.ok) { state.stats.disabled += 1; log(`${title} 已关闭 schedulable(原因:${failReason})`, 'success'); } else { log(`${title} 关闭失败:${off.reason}`, 'error'); } } markAccountCompleted(account.id); } if (state.stopRequested) { log('任务已按要求停止', 'warn'); } else { log('巡检完成', 'success'); } } finally { state.running = false; updateStats(); updateProgress(); } } injectAuthSniffer(); waitDomReady().then(() => { ensurePanel(); if (state.authHeader) { log('脚本已就绪,已从本地缓存 auth_token 读取 Authorization', 'success'); } else { log('脚本已就绪,未发现 auth_token;可刷新页面自动捕获或手动粘贴'); } }); })(); 使用方法: 把// @match http://127.0.0.1:8317/admin/accounts\ * 改成你自己的 下载浏览器插件油猴导入代码 佬友的已经做的很好了 主要增添的功能 1. 分批巡检(5h之内不会重复巡检) 可视化巡检进度 429额度用完的不会关闭调度 已经检测为401的不会重复检测 注意:尽量不要反复测活 9 个帖子 - 4 位参与者 阅读完整话题

LinuxDo 最新话题 · 2026-05-24 00:37:41+08:00 · tech

1、在cc switch中找到open ai官方,查看其中的auth.json文件 2、使用[ https://chatgpt.com/api/auth/session ]去找到相关的信息: 下面内容都可以从这个网站里获取: { “auth_mode”: “chatgpt”, “OPENAI_API_KEY”: null, “tokens”: { “id_token”: “”, “access_token”: “”, “refresh_token”: “”, “account_id”: “” }, “last_refresh”: “” } 3、为了方便查看[ https://chatgpt.com/api/auth/session ]里的信息,可以使用这个网站( https://gtxx3600.github.io/GPTSession2CPAandSub2API/)把所有获取的信息去解析成规整格式的json,方便找到id_token、access_token、account_id、last_refresh;这个refresh_token我直接默认了但也可以。 4、保存后那个文件,再打开codex,就能成功启动了!!! 16 个帖子 - 12 位参与者 阅读完整话题

LinuxDo 最新话题 · 2026-05-24 00:33:20+08:00 · tech

{ “message”: “auth_not_found: no auth available (providers=codex, model=gpt-5.4-mini)”, “type”: “server_error”, “code”: “internal_server_error” } 报错是这个,不知道为啥生图一直要用5.4mini,之前在codex里用挺正常的,都是对话直接让他生图,生完改图也可以,最近这样子越来越难成功,有时候它自己直接画矢量图拼凑给我。今天直接就是这个报错了,不知道有没有佬碰到过?cpa反代到newapi,基本都是默认,没有改变过 13 个帖子 - 5 位参与者 阅读完整话题

V2EX - 技术 · 2026-05-24 00:29:53+08:00 · tech

陆陆续续花了接近 3 天时间才改了一版本。 之前是 改完现在是 最初的版本是,AI 直出的文案和样式,文案不说人话,样式也看着着就别扭。 新版是,每一幕和 AI 描述自己的想法然后定制调出来的,个人感官上舒服了很多。 然后想着未来可能会用 AI 做更多新产品,手动调也太麻烦了,大家是套模板还是有啥神奇的 skill 或 prompt 可以提高效率么?求分享。 另外自荐产品 名字:叙野 Lorewild 网址: https://lorewild.com 推特: https://x.com/ethan180953 定位是“用 AI 故事学外语”和“AI 角色对话学外语”,支持中文、英语、日语、韩语、德语、西班牙语六个语言。类似酒馆,也可以自建角色和剧本,目前提供了轻量的学习层。 md ,一点流量没有,得多尝试 build in public 啥的整起来。

V2EX - 技术 · 2026-05-24 00:29:53+08:00 · tech

陆陆续续花了接近 3 天时间才改了一版本。 之前是 改完现在是 最初的版本是,AI 直出的文案和样式,文案不说人话,样式也看着着就别扭。 新版是,每一幕和 AI 描述自己的想法然后定制调出来的,个人感官上舒服了很多。 然后想着未来可能会用 AI 做更多新产品,手动调也太麻烦了,大家是套模板还是有啥神奇的 skill 或 prompt 可以提高效率么?求分享。 另外自荐产品 名字:叙野 Lorewild 网址: https://lorewild.com 推特: https://x.com/ethan180953 定位是“用 AI 故事学外语”和“AI 角色对话学外语”,支持中文、英语、日语、韩语、德语、西班牙语六个语言。类似酒馆,也可以自建角色和剧本,目前提供了轻量的学习层。 md ,一点流量没有,得多尝试 build in public 啥的整起来。

V2EX - 技术 · 2026-05-24 00:23:15+08:00 · tech

陆陆续续花了接近 3 天时间才改了一版本。 之前是 改完现在是 最初的版本是,AI 直出的文案和样式,文案不说人话,样式也看着着就别扭。 新版是,每一幕和 AI 描述自己的想法然后定制调出来的,个人感官上舒服了很多。 然后想着未来可能会用 AI 做更多新产品,手动调也太麻烦了,大家是套模板还是有啥神奇的 skill 或 prompt 可以提高效率么?求分享。 另外自荐产品 名字:叙野 Lorewild 网址: https://lorewild.com 推特: https://x.com/ethan180953 定位是“用 AI 故事学外语”和“AI 角色对话学外语”,支持中文、英语、日语、韩语、德语、西班牙语六个语言。类似酒馆,也可以自建角色和剧本,目前提供了轻量的学习层。 md ,一点流量没有,得多尝试 build in public 啥的整起来。

V2EX - 技术 · 2026-05-24 00:09:58+08:00 · tech

陆陆续续花了接近 3 天时间才改了一版本。 之前是 改完现在是 最初的版本是,AI 直出的文案和样式,文案不说人话,样式也看着着就别扭。 新版是,每一幕和 AI 描述自己的想法然后定制调出来的,个人感官上舒服了很多。 然后想着未来可能会用 AI 做更多新产品,手动调也太麻烦了,大家是套模板还是有啥神奇的 skill 或 prompt 可以提高效率么?求分享。 另外自荐产品 名字:叙野 Lorewild 网址: https://lorewild.com 推特: https://x.com/ethan180953 定位是“用 AI 故事学外语”和“AI 角色对话学外语”,支持中文、英语、日语、韩语、德语、西班牙语六个语言。类似酒馆,也可以自建角色和剧本,目前提供了轻量的学习层。 md ,一点流量没有,得多尝试 build in public 啥的整起来。

IT之家 · 2026-05-23 23:49:19+08:00 · tech

IT之家 5 月 23 日消息,据《商业内幕》23 日(今天)下午报道,OpenAI 发布了安全研究员的招聘信息,研究当 AI 能够训练出自己的“更强版本”时可能出现的问题。 职位聚合网站显示,这则招聘信息面向 OpenAI Preparedness 安全团队,本月上线。该岗位年薪高达 29.5 万至 44.5 万美元 (IT之家注:现汇率约合 200.8 万至 303 万元人民币),目标是寻找“ 能够支持递归式自我改进准备工作的强大技术执行者 ”。 招聘信息写道:“这项工作依赖于对未来可能存在、但当前未必已经存在的问题进行推理。因此,这一岗位尤其需要具备 良好品味和策略 (tasteful and strategic)的人才。” 当前,OpenAI 正着眼于实现自身研究工作的自动化的目标。去年 10 月,CEO 奥尔特曼曾表示,OpenAI 的目标是在今年 9 月前 利用数十万枚芯片运行“自动化 AI 研究实习生” ,并在 2028 年 3 月前实现“ 真正的自动化 AI 研究员 ”。他补充说,“我们完全有可能无法实现这一目标。但考虑到潜在影响极其巨大,我们认为公开说明这些目标符合公众利益。” Anthropic 今年 4 月发布研究,尝试 利用 AI 模型监督更强大的 AI 模型 ,并取得了有限但积极的结果。今年 5 月,Anthropic 联合创始人兼政策主管杰克 · 克拉克提出观点:到 2028 年底前,AI 研发完全脱离人类参与的概率“大约为 60%”。 OpenAI 的这则招聘信息提到,研究员预计需要 重点研究如何防御“数据投毒”攻击 ,即攻击者通过训练数据破坏 AI 模型的行为;需要开发用于 解释模型推理过程 的工具,或通过实验理解模型的安全性与潜在风险;需要“追踪技术岗位自动化进展”,其中包括衡量 AI 编程工具的使用情况。

v2ex · 2026-05-23 23:29:01+08:00 · tech

这是一个 AI 原生的软件交付工作流引擎,能把 PRD (产品需求文档)直接变成真正可上线的代码和 Pull Request 。 它不是简单的一次性让 AI 聊聊天生成代码,而是把整个开发流程做成结构化、可暂停恢复、可审计的工作流:需求解析、任务拆分、AI 实现、TDD 测试、代码 Review ,最后自动创建 PR 。 对 AI Agent 和自动化交付感兴趣的朋友,欢迎来喷 https://github.com/pony-maggie/code_minions

v2ex · 2026-05-23 23:24:48+08:00 · tech

这是一个 AI 原生的软件交付工作流引擎,能把 PRD (产品需求文档)直接变成真正可上线的代码和 Pull Request 。 它不是简单的一次性让 AI 聊聊天生成代码,而是把整个开发流程做成结构化、可暂停恢复、可审计的工作流:需求解析、任务拆分、AI 实现、TDD 测试、代码 Review ,最后自动创建 PR 。 对 AI Agent 和自动化交付感兴趣的朋友,欢迎来喷 https://github.com/pony-maggie/code_minions