Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# CreditDaddy

**AI 编程工具多账号本地管理 + 每日积分自动领取(领鸡蛋)助手:Qoder · WorkBuddy · ZCode · mirasim · 妙手 · Trae · MiniMax · ModelScope · 豆包 Doubao · 讯飞 Loomy,集成 10Router 额度总览、用量同步与网页会话额度卡(Token Harbor / 小米 MiMo / River / ModelScope 魔搭 / LongCat)**
**AI 编程工具多账号本地管理 + 每日积分自动领取(领鸡蛋)助手:Qoder · WorkBuddy · ZCode · mirasim · 妙手 · Trae · MiniMax · ModelScope · 豆包 Doubao · 讯飞 Loomy · Meoo 秒悟,集成 10Router 额度总览、用量同步与网页会话额度卡(Token Harbor / 小米 MiMo / River / ModelScope 魔搭 / LongCat)**

[![Release](https://img.shields.io/github/v/release/techysy/CreditDaddy?label=%E7%89%88%E6%9C%AC&color=2563eb)](https://github.com/techysy/CreditDaddy/releases/latest)
[![CI](https://img.shields.io/github/actions/workflow/status/techysy/CreditDaddy/ci.yml?branch=main&label=CI)](https://github.com/techysy/CreditDaddy/actions/workflows/ci.yml)
Expand Down Expand Up @@ -144,6 +144,7 @@
| River AI | WorkOS localStorage JWT 变体(三源收割) | `$25` 免费额度 `balance_usd`,会话过期自动续期 |
| ModelScope 魔搭(issue #25) | 会话 cookie(双源) | 魔粒余额,卡面拆「今日发放 / 长期」两腿;每日额度登录自动发放、无签到端点 |
| LongCat 美团(issue #29) | 美团 SSO 会话 cookie | Token 资源包:Balance 汇总 + 逐包明细腿(官方消耗顺序、赠送包徽注、到期日) |
| Meoo 秒悟 | meoo.com 会话 cookie(阿里云 SSO 手机号 + 短信登录) | 每日首登积分(`/api/mc/entitlement/init` 触发,当前 +2000/天、约 6~7 天过期)自动领取 + 积分余额;会话失效(`NEED_LOGIN`)提示重新捕获;首版不推 10Router 卡 |

> 注:办公小浣熊曾按 issue #27 调研接入,因 `raccoon_token` 异步写回时序不可靠已于 v1.5.0 回滚移除。

Expand Down Expand Up @@ -343,6 +344,7 @@ CreditDaddy/
│ ├── localDetect.js # 客户端已安装环境侦测
│ ├── logger.js # 内存环形日志与按日归档
│ ├── longcatClient.js # LongCat Token 资源包与身份请求
│ ├── meooClient.js # Meoo 秒悟每日首登积分与积分余额
│ ├── mirasimClient.js # mirasim 套餐 / 额度请求
│ ├── mirasimLocal.js # 本机 mirasim 凭据解密与切号
│ ├── minimaxAuth.js # MiniMax 浏览器登录(OAuth 设备码 + S256 PKCE)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "creditdaddy",
"version": "1.7.2",
"description": "CreditDaddy — Qoder / WorkBuddy / ZCode / mirasim / 妙手 / Trae / MiniMax / ModelScope / 豆包 Doubao / 讯飞 Loomy / WPS 灵犀多账号本地管理与每日积分自动签到助手,及 10Router 供应商 Cookie 会话捕获与外部配额(Token Harbor / 小米 MiMo / River / ModelScope 魔搭 / LongCat)",
"description": "CreditDaddy — Qoder / WorkBuddy / ZCode / mirasim / 妙手 / Trae / MiniMax / ModelScope / 豆包 Doubao / 讯飞 Loomy / WPS 灵犀 / Meoo 秒悟多账号本地管理与每日积分自动签到助手,及 10Router 供应商 Cookie 会话捕获与外部配额(Token Harbor / 小米 MiMo / River / ModelScope 魔搭 / LongCat)",
"homepage": "https://github.com/techysy/CreditDaddy",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion scripts/release-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ import('./src/constants.js').then(({ PROVIDERS, productOf }) => {
" 2>/dev/null || true)
if [ -n "$PROVIDERS" ]; then
# catpaw 在文案里叫「妙手」,单独给别名
alias_of() { case "$1" in catpaw) echo "妙手";; tokenharbor) echo "Token Harbor";; 'xiaomi-mimo') echo "MiMo";; lingxi) echo "灵犀";; *) echo "$1";; esac; }
alias_of() { case "$1" in catpaw) echo "妙手";; tokenharbor) echo "Token Harbor";; 'xiaomi-mimo') echo "MiMo";; lingxi) echo "灵犀";; meoo) echo "秒悟";; *) echo "$1";; esac; }
ROOT_DESC=$(node -p "require('./package.json').description")
STALE=0
while IFS= read -r product; do
Expand Down
2 changes: 1 addition & 1 deletion src/checkin.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const ZCODE_FIRST_DELAY_MS = 2 * 60 * 1000;
// 签到日 = 对应时点起的日历日:dayKey(now, provider) = (now + (8 - 刷新小时)h) 的 UTC 日期
const DAY_REFRESH_HOUR_UTC8 = { qoder: 10, 'qoder-cn': 10 };
// 领取日志的积分单位:默认 Credits(Qoder/WorkBuddy 系),网页会话线各自带单位
const CHECKIN_UNIT = { lingxi: '灵点', doubao: '额度' };
const CHECKIN_UNIT = { lingxi: '灵点', doubao: '额度', meoo: '秒悟积分' };

let timerHandle = null;
let zcodeTimerHandle = null;
Expand Down
4 changes: 3 additions & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const WEB_USAGE_PATH = '/api/v2/me/usages/big_model_credits';
export const JOB_TOKEN_EXCHANGE_PATH = '/api/v1/jobToken/exchange';

// 账号的 provider = 产品 + 区域。新增产品时在此登记,并在 providers.js 挂上对应实现。
export const PROVIDERS = ['qoder', 'qoder-cn', 'workbuddy', 'workbuddy-intl', 'zcode', 'mirasim', 'catpaw', 'trae', 'trae-intl', 'minimax', 'minimax-global', 'tokenharbor', 'xiaomi-mimo', 'river', 'modelscope', 'longcat', 'doubao', 'loomy', 'lingxi'];
export const PROVIDERS = ['qoder', 'qoder-cn', 'workbuddy', 'workbuddy-intl', 'zcode', 'mirasim', 'catpaw', 'trae', 'trae-intl', 'minimax', 'minimax-global', 'tokenharbor', 'xiaomi-mimo', 'river', 'modelscope', 'longcat', 'doubao', 'loomy', 'lingxi', 'meoo'];
export const PROVIDER_LABEL = {
qoder: 'Qoder 国际版',
'qoder-cn': 'Qoder 国内版',
Expand All @@ -56,6 +56,7 @@ export const PROVIDER_LABEL = {
doubao: '豆包',
loomy: '讯飞 Loomy',
lingxi: 'WPS 灵犀',
meoo: 'Meoo 秒悟',
};
/** provider → 产品线(qoder / workbuddy / zcode / mirasim / catpaw / trae / minimax / tokenharbor / xiaomi-mimo / river) */
export const productOf = (provider) => {
Expand All @@ -74,6 +75,7 @@ export const productOf = (provider) => {
if (p === 'doubao') return 'doubao';
if (p === 'loomy') return 'loomy';
if (p === 'lingxi') return 'lingxi';
if (p === 'meoo') return 'meoo';
return 'qoder';
};
/** provider 是否国内版 */
Expand Down
16 changes: 16 additions & 0 deletions src/cookieSites.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,22 @@ export const COOKIE_SITES = [
accountName: 'WPS 灵犀',
hint: '每日自动签到(+100 灵点,七天一轮第七天 200)+ 推送 10Router 出余额卡,Cookie 只存本机。多号分别捕获即按 WPS 账号分行并存。',
},
{
id: 'meoo',
name: 'Meoo 秒悟',
provider: 'meoo',
label: 'Meoo 秒悟',
loginUrl: 'https://meoo.com',
homeUrl: 'https://meoo.com',
// 探测 = 权益接口本身(2026-11-13 实测):带会话 cookie 返 { success:true, data:{ userPointInfo… } };
// 未登录 401 { errCode:'NEED_LOGIN' }。身份走 /api/v1/user/me 的 user_id(多号归户)。
// 每日首登积分由 entitlement/init 触发(网页端登录后同款调用),CD 每日代调一次。
verify: 'api',
cookieUrls: ['https://meoo.com'],
accountName: 'Meoo 秒悟',
no10rSync: true, // 首版不推 10Router 外部配额卡(积分只在 Meoo 内消费)
hint: '登录 meoo.com(阿里云账号手机号 + 短信)后捕获会话;CD 每日自动领取首登积分(当前 +2000/天,约 6~7 天后过期)并展示积分余额。会话失效(NEED_LOGIN)时请重新登录捕获,Cookie 只存本机。',
},
// ⚠ 码道 CodeArts(issue #40)已于 2026-10-30 撤下,不再提供:
// 华为云控制台会话受闲置超时 + WAF 指印(HWWAFSESID / browserCheckResult)绑定,
// 浏览器里能靠 SPA 持续续期,本机无法续 —— 实测新鲜会话当日内即失效(网关回登录页),
Expand Down
73 changes: 71 additions & 2 deletions src/daemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,36 @@ async function reprobeCookieAccount(site, a) {
logger.info('COOKIE-SITE', '{name} 就地重探通过({identity},签到{signed},灵点 {balance})', { name: site.name, identity: uid ? 'uid ' + uidMasked : '无 uid', signed: st.checkedIn ? '已签' : '未签', balance: Number.isFinite(st.balance) ? st.balance : '—' });
return finish({ id: a.id, name: a.name, ok: true, identity: uid ? `uid:${uidMasked}` : null, balance: Number.isFinite(st.balance) ? st.balance : null, quotaSnapshot: Number.isFinite(st.balance) ? { used: 0, total: st.balance, isCreditBalance: true, at: now } : null });
}
if (site.id === 'meoo') {
// Meoo 秒悟:权益接口即探测(只读,不调 entitlement/init——发放动作只走签到线),
// 刷新积分余额快照;身份走 user/me 的 user_id(老行缺失时回填 + 名字升级)
const { fetchMeooEntitlement, fetchMeooIdentity, maskMeooUserId } = await import('./meooClient.js');
const ent = await fetchMeooEntitlement(cookie).catch((e) => ({ auth: true, ok: false, code: e?.message || 'network' }));
if (!ent.auth) return finish({ id: a.id, name: a.name, ok: false, error: 'Meoo 会话已失效(NEED_LOGIN),请重新登录捕获' });
if (!ent.ok) return finish({ id: a.id, name: a.name, ok: false, error: '探测失败,请稍后再试或重新登录捕获' });
const ident = await fetchMeooIdentity(cookie).catch(() => null);
const userId = ident?.ok ? ident.userId : null;
const masked = userId ? maskMeooUserId(userId) : null;
const bal = Number.isFinite(ent.balance) ? ent.balance : null;
await withAccounts((list) => {
const hit = list.find((x) => x.id === a.id);
if (!hit) return;
hit.meta = {
...(hit.meta || {}),
cookieSiteSession: { cookie, capturedAt: hit.meta?.cookieSiteSession?.capturedAt || now },
...(userId && !hit.meta?.identityHash ? { identityHash: identityHashOf(`uid:${userId}`) } : {}),
...(ent.grants ? { meooGrants: { grants: ent.grants, at: now } } : {}),
...(bal !== null ? { quotaSnapshot: { used: 0, total: bal, isCreditBalance: true, at: now } } : {}),
};
if (userId) {
hit.uid = String(userId);
if (canAutoRename(site, hit)) hit.name = `${site.accountName}(${masked})`;
}
hit.updatedAt = now;
});
logger.info('COOKIE-SITE', '{name} 就地重探通过({identity},积分 {balance})', { name: site.name, identity: masked ? 'uid ' + masked : '无 uid', balance: bal ?? '—' });
return finish({ id: a.id, name: a.name, ok: true, identity: masked ? `uid:${masked}` : null, balance: bal, quotaSnapshot: bal !== null ? { used: 0, total: bal, isCreditBalance: true, at: now } : null });
}
if (site.id === 'doubao') {
// 豆包(issue #32):detail 接口一次拿全(会话有效性 + 签到状态 + 累计点数),
// 重探 = 刷新额度快照;无 10R 同步(豆包不推外部配额卡,错误文案不再误导)
Expand Down Expand Up @@ -764,7 +794,7 @@ async function handleApi(req, res, url) {
// 单位标注:各家余额单位互异(Token / USD / CNY / 魔粒 / 积分)且都不是「积分」
// 口径——带 unit 返回,面板 creditSum 按 unit 有无决定是否计入「剩余积分」合计
//(有单位 = 不同量纲,不相加)。HTML 探针站(tokenharbor)是百分比,同样排除。
const UNIT_BY_SITE = { 'xiaomi-mimo': 'CNY', 'river': 'USD', 'longcat': 'Token', 'modelscope': '魔粒', 'lingxi': '灵点' };
const UNIT_BY_SITE = { 'xiaomi-mimo': 'CNY', 'river': 'USD', 'longcat': 'Token', 'modelscope': '魔粒', 'lingxi': '灵点', 'meoo': '秒悟积分' };
const unit = UNIT_BY_SITE[site.id] || (hasTotal ? '' : '%');
return json(res, 200, { quota: {
total: hasTotal ? total : 100,
Expand Down Expand Up @@ -1280,11 +1310,13 @@ async function handleApi(req, res, url) {
msBalance: a.meta?.msBalance || null,
longcatPacks: a.meta?.longcatPacks || null,
lingxiPacks: a.meta?.lingxiPacks || null, // 智点资源包快照(issue #53)
meooGrants: a.meta?.meooGrants || null, // Meoo 积分逐笔发放快照(带到期日)
lastProbe: a.meta?.lastProbe || null, // 呼吸灯数据源:ok / warn / dead + 失败原因
}));
const latest = rows.reduce((t, r) => (r.capturedAt && (!t || r.capturedAt > t) ? r.capturedAt : t), null);
return {
id: site.id, name: site.name, label: site.label, loginUrl: site.loginUrl, hint: site.hint,
...(site.no10rSync === true ? { no10rSync: true } : {}),
accountId: rows[0]?.id || null, capturedAt: latest, accounts: rows,
};
}),
Expand Down Expand Up @@ -1495,6 +1527,37 @@ async function handleApi(req, res, url) {
}
}

// Meoo 秒悟:权益接口即探测(带 cookie 返 success:true,未登录 401 NEED_LOGIN)。
// 只读——不在捕获时调 entitlement/init,首登积分发放统一走签到线。身份 = user/me 的 user_id。
let meooUserId = null;
let meooMasked = null;
let meooGrants = null;
if (site.id === 'meoo') {
try {
const { fetchMeooEntitlement, fetchMeooIdentity, maskMeooUserId } = await import('./meooClient.js');
if (!cookie) {
logger.warn('COOKIE-SITE', '{name} 未读到会话 cookie', { name: site.name });
return json(res, 400, { error: '未读到 Meoo 登录会话,请在登录窗完成登录后再关闭' });
}
const ent = await fetchMeooEntitlement(cookie);
if (!ent.auth) {
logger.warn('COOKIE-SITE', '{name} 权益接口探测被拒:{code}', { name: site.name, code: ent.code });
return json(res, 401, { error: '会话不可用,请重新登录该站点后再捕获(探测 401/403)' });
}
if (!ent.ok) return json(res, 502, { error: '探测站点失败:' + (ent.code || '') });
const ident = await fetchMeooIdentity(cookie).catch(() => null);
meooUserId = ident?.ok ? ident.userId : null;
meooMasked = meooUserId ? maskMeooUserId(meooUserId) : null;
if (ent.grants) meooGrants = { grants: ent.grants, at: now };
identity = meooUserId ? `uid:${meooUserId}` : null; // 有 user_id → 哈希归户;无 → 单例兜底
quotaSnapshot = { used: 0, total: Number(ent.balance) || 0, isCreditBalance: true };
logger.info('COOKIE-SITE', '{name} 探测通过({identity},积分 {balance})', { name: site.name, identity: meooUserId ? 'uid ' + meooMasked : '无 uid(单例)', balance: Number.isFinite(ent.balance) ? ent.balance : '—' });
} catch (meooErr) {
logger.warn('COOKIE-SITE', '{name} 权益接口抓取异常:{err}', { name: site.name, err: meooErr.message });
return json(res, 502, { error: '探测站点失败:' + (meooErr.message || '') });
}
}

const ih = identityHashOf(mimoUserId || riverSession?.user?.id || msSession?.id || lcSession?.userId || identity);
const quotaMeta = quotaSnapshot ? { quotaSnapshot: { ...quotaSnapshot, at: now } } : {};
const cred = riverSession ? riverSession.accessToken : cookie; // 落盘的凭据主体
Expand All @@ -1511,6 +1574,7 @@ async function handleApi(req, res, url) {
else if (site.id === 'modelscope') accName = `ModelScope (${identity || '会话'})`;
else if (site.id === 'longcat') accName = `LongCat (${identity || '会话'})`;
else if (site.id === 'lingxi') accName = site.accountName + (lxUidMasked ? `(${lxUidMasked})` : ''); // uid 后缀脱敏(前3后3),归户仍用原始 uid
else if (site.id === 'meoo') accName = site.accountName + (meooMasked ? `(${meooMasked})` : ''); // user_id 脱敏后缀,归户仍用原始值
else accName = site.accountName + (identity ? `(${identity})` : '');
// 自动行名只在「没手改过且现名属自动名家族」时写入(meta.renamed + isAutoGeneratedName 双闸)
const autoName = (row) => { if (canAutoRename(site, row)) row.name = accName; };
Expand Down Expand Up @@ -1538,6 +1602,8 @@ async function handleApi(req, res, url) {
...(lxSnapshot ? { lingxiPacks: lxSnapshot } : {}),
};
if (lcSession?.userId) { hit.uid = String(lcSession.userId); autoName(hit); }
if (meooUserId) { hit.uid = String(meooUserId); autoName(hit); }
if (meooGrants) hit.meta.meooGrants = meooGrants;
hit.updatedAt = now;
accountId = hit.id;
} else {
Expand All @@ -1546,7 +1612,7 @@ async function handleApi(req, res, url) {
provider: site.provider,
token: cred,
expiresAt: site.id === 'river' ? (riverSession?.expiresAt || undefined) : undefined,
uid: mimoUserId || riverUid || (msSession?.id ? String(msSession.id) : undefined) || (lcSession?.userId ? String(lcSession.userId) : undefined) || (lxUid ? String(lxUid) : undefined) || undefined,
uid: mimoUserId || riverUid || (msSession?.id ? String(msSession.id) : undefined) || (lcSession?.userId ? String(lcSession.userId) : undefined) || (lxUid ? String(lxUid) : undefined) || (meooUserId ? String(meooUserId) : undefined) || undefined,
source: 'web-session',
meta: {
cookieSiteId: site.id,
Expand All @@ -1561,6 +1627,7 @@ async function handleApi(req, res, url) {
...(lcPacks ? { longcatPacks: longcatSnapshot(lcPacks) } : {}),
...(lcSession ? { longcatSession: lcSession } : {}),
...(lxSnapshot ? { lingxiPacks: lxSnapshot } : {}),
...(meooGrants ? { meooGrants } : {}),
},
});
acc.verified = true;
Expand Down Expand Up @@ -1623,6 +1690,7 @@ async function handleApi(req, res, url) {
// ——失败说明会话可能已死,恰好要把 message 状态卡推上去让 10R 卡面可见。
const touchedSources = new Set();
const sourceOf = async (a) => {
if (site.no10rSync === true) return null; // 不推 10R 卡的站点(如 Meoo)不进推送名单
if (site.id === 'tokenharbor') return 'tokenharbor';
try {
const m = await import(
Expand Down Expand Up @@ -2065,6 +2133,7 @@ async function handleApi(req, res, url) {
trae: dayKey(Date.now(), 'trae'),
minimax: dayKey(Date.now(), 'minimax'),
lingxi: dayKey(Date.now(), 'lingxi'), // 网页会话签到线(#53):0 点翻日,徽标/isDone 的日界比对用
meoo: dayKey(Date.now(), 'meoo'), // Meoo 首登积分线:同为 0 点翻日
},
scheduler: await getSchedulerInfo(),
riskIdentity: riskIdentityAvailable(),
Expand Down
Loading
Loading