@vocmarket/voc-skill 0.3.22 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/mcp/src/core/credentials.js +73 -3
- package/mcp/src/core/payment-links.js +23 -0
- package/mcp/src/features/fmode-image-analysis/image-analysis.js +16 -12
- package/mcp/src/tools/voc-api-catalog-run.js +15 -9
- package/package.json +1 -1
- package/skills/voc-api-catalog/SKILL.md +9 -4
- package/skills/voc-api-catalog/references/error-codes.md +8 -3
|
@@ -64,6 +64,56 @@ function readEnvLocalFiles() {
|
|
|
64
64
|
}, {});
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// 合并读取 Claude Code 的 settings env(用户级 + 项目级,含 .local 覆盖文件)。
|
|
68
|
+
// Claude Code 用户的 fmode newapi SK 默认就放在这里的 env.ANTHROPIC_AUTH_TOKEN。
|
|
69
|
+
function readClaudeSettingsEnv() {
|
|
70
|
+
const files = [
|
|
71
|
+
path.join(os.homedir(), '.claude', 'settings.json'),
|
|
72
|
+
path.join(os.homedir(), '.claude', 'settings.local.json'),
|
|
73
|
+
path.join(process.cwd(), '.claude', 'settings.json'),
|
|
74
|
+
path.join(process.cwd(), '.claude', 'settings.local.json'),
|
|
75
|
+
];
|
|
76
|
+
const merged = {};
|
|
77
|
+
for (const filePath of files) {
|
|
78
|
+
const json = readJsonMaybe(filePath);
|
|
79
|
+
const env = json && typeof json.env === 'object' && json.env ? json.env : null;
|
|
80
|
+
if (!env) continue;
|
|
81
|
+
for (const [key, value] of Object.entries(env)) {
|
|
82
|
+
if (merged[key] === undefined && typeof value === 'string' && value.trim()) {
|
|
83
|
+
merged[key] = value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return merged;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 读取 ~/.fmode/config.json(及项目级 ./.fmode/config.json)里的 newapi token。
|
|
91
|
+
function readFmodeConfig() {
|
|
92
|
+
const files = [
|
|
93
|
+
path.join(os.homedir(), '.fmode', 'config.json'),
|
|
94
|
+
path.join(process.cwd(), '.fmode', 'config.json'),
|
|
95
|
+
];
|
|
96
|
+
const merged = {};
|
|
97
|
+
for (const filePath of files) {
|
|
98
|
+
const json = readJsonMaybe(filePath);
|
|
99
|
+
for (const [key, value] of Object.entries(json || {})) {
|
|
100
|
+
if (merged[key] === undefined) merged[key] = value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return merged;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 仅当 ANTHROPIC_AUTH_TOKEN 看起来是 fmode 的 newapi SK 时才采纳:
|
|
107
|
+
// - 必须 sk- 开头,且排除真 Anthropic 官方 key(sk-ant- 开头);
|
|
108
|
+
// - 若 settings 里设了 ANTHROPIC_BASE_URL,必须指向 fmode(否则这把 token 是发往别处的,不能拿来当 fmode 计费 token)。
|
|
109
|
+
function pickFmodeAnthropicToken(env) {
|
|
110
|
+
const token = env && typeof env.ANTHROPIC_AUTH_TOKEN === 'string' ? env.ANTHROPIC_AUTH_TOKEN.trim() : '';
|
|
111
|
+
if (!token || !/^sk-/i.test(token) || /^sk-ant-/i.test(token)) return '';
|
|
112
|
+
const base = String((env && (env.ANTHROPIC_BASE_URL || env.ANTHROPIC_API_BASE)) || '').toLowerCase();
|
|
113
|
+
if (base && !base.includes('fmode')) return '';
|
|
114
|
+
return token;
|
|
115
|
+
}
|
|
116
|
+
|
|
67
117
|
function readVocToken(input = {}) {
|
|
68
118
|
const workspaceEnv = readEnvLocalFiles();
|
|
69
119
|
const claudeCreds = readJsonMaybe(path.join(os.homedir(), '.claude', 'voc-credentials.json'));
|
|
@@ -91,11 +141,17 @@ function readXiaohongshuToken(input = {}) {
|
|
|
91
141
|
}
|
|
92
142
|
|
|
93
143
|
// NewAPI(fmode-api)计费 token,以 sk- 开头。
|
|
94
|
-
// 计费已迁移到 NewAPI:优先用它发请求,鉴权失败再回退平台 sessionToken
|
|
95
|
-
//
|
|
144
|
+
// 计费已迁移到 NewAPI:优先用它发请求,鉴权失败再回退平台 sessionToken(apig,原路径保留)。
|
|
145
|
+
// 关键:这把 sk- 就是 Claude Code 的 env.ANTHROPIC_AUTH_TOKEN(在 ~/.claude/settings.json),
|
|
146
|
+
// 默认装完技能不必再手动配置——这里直接从 settings.json / 进程环境读取它,避免「明明配了 SK 却判缺 token、被甩进老付费框」的死循环。
|
|
147
|
+
// 取值优先级:显式入参 → .env.local / 环境变量 FMODE_API_KEY·NEWAPI_TOKEN
|
|
148
|
+
// → ~/.claude/voc-credentials.json 的 fmodeApiKey → ~/.fmode/config.json 的 newapiToken/fmodeApiToken
|
|
149
|
+
// → Claude Code env(进程注入 + ~/.claude/settings.json)的 ANTHROPIC_AUTH_TOKEN(sk-、且 base 指向 fmode)。
|
|
96
150
|
function readNewApiToken(input = {}) {
|
|
97
151
|
const workspaceEnv = readEnvLocalFiles();
|
|
98
152
|
const claudeCreds = readJsonMaybe(path.join(os.homedir(), '.claude', 'voc-credentials.json'));
|
|
153
|
+
const fmodeConfig = readFmodeConfig();
|
|
154
|
+
const claudeEnv = readClaudeSettingsEnv();
|
|
99
155
|
return firstNonEmpty([
|
|
100
156
|
input.newapiToken,
|
|
101
157
|
input.newApiToken,
|
|
@@ -111,6 +167,17 @@ function readNewApiToken(input = {}) {
|
|
|
111
167
|
process.env.FMODE_API_TOKEN,
|
|
112
168
|
claudeCreds.fmodeApiKey,
|
|
113
169
|
claudeCreds.fmodeApiToken,
|
|
170
|
+
fmodeConfig.newapiToken,
|
|
171
|
+
fmodeConfig.newApiToken,
|
|
172
|
+
fmodeConfig.fmodeApiKey,
|
|
173
|
+
fmodeConfig.fmodeApiToken,
|
|
174
|
+
// Claude Code 显式写在 settings.json env 里的 fmode key(少见,但优先于自动推断的 ANTHROPIC_AUTH_TOKEN)
|
|
175
|
+
claudeEnv.FMODE_API_KEY,
|
|
176
|
+
claudeEnv.NEWAPI_TOKEN,
|
|
177
|
+
// Claude Code 默认入口:进程注入的 ANTHROPIC_AUTH_TOKEN(Claude Code 会把 settings.env 注入子进程)
|
|
178
|
+
pickFmodeAnthropicToken(process.env),
|
|
179
|
+
// 兜底:直接读 ~/.claude/settings.json 里的 env.ANTHROPIC_AUTH_TOKEN(独立运行、未被注入时)
|
|
180
|
+
pickFmodeAnthropicToken(claudeEnv),
|
|
114
181
|
]);
|
|
115
182
|
}
|
|
116
183
|
|
|
@@ -120,5 +187,8 @@ module.exports = {
|
|
|
120
187
|
readNewApiToken,
|
|
121
188
|
readEnvFileMaybe,
|
|
122
189
|
readWorkspaceEnvLocal,
|
|
123
|
-
envLocalCandidates
|
|
190
|
+
envLocalCandidates,
|
|
191
|
+
readClaudeSettingsEnv,
|
|
192
|
+
readFmodeConfig,
|
|
193
|
+
pickFmodeAnthropicToken
|
|
124
194
|
};
|
|
@@ -126,6 +126,28 @@ function buildWrongTokenTypeMessage(paymentUrl, recharge = {}, options = {}) {
|
|
|
126
126
|
].join('\n');
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
// 没读到 NewAPI(fmode-api)的 sk- 计费 token 时的提示。
|
|
130
|
+
// 关键:这是「没读到 SK」这种**可恢复**状态,不是余额不足、也不是功能用不了——
|
|
131
|
+
// 先引导 agent 从 Claude Code 的 settings.json 取 SK 重试,充值只作为最后兜底(且仅真 402 才需要)。
|
|
132
|
+
function buildMissingNewApiTokenMessage(paymentUrl, recharge = {}, options = {}) {
|
|
133
|
+
const platformLabel = options.platformLabel || '小红书';
|
|
134
|
+
const lines = [
|
|
135
|
+
`还没读到 NewAPI(fmode-api)计费 token,暂时不能进行真实 ${platformLabel} 采集——但这是**配置/读取**问题,不是余额不足,也不代表这个功能用不了。`,
|
|
136
|
+
'',
|
|
137
|
+
'这把计费 token 就是 Claude Code 的 `ANTHROPIC_AUTH_TOKEN`(`sk-` 开头),通常配在用户级 `~/.claude/settings.json` 的 `env` 里。',
|
|
138
|
+
'',
|
|
139
|
+
'请按这个顺序自救(优先,不要直接让用户去点充值):',
|
|
140
|
+
'1. 读取 `~/.claude/settings.json` 的 `env.ANTHROPIC_AUTH_TOKEN`(确认是 `sk-` 开头、`ANTHROPIC_BASE_URL` 指向 `api.fmode.cn`)。',
|
|
141
|
+
'2. 用 `FMODE_API_KEY=sk-…`(或入参 `newapiToken`)把它传给本工具,重新调用一次。',
|
|
142
|
+
'3. 仍报缺 token / 鉴权失败时,才考虑回退 `r:` 开头的会话 token,或确认是否真的 `402 余额不足`。'
|
|
143
|
+
];
|
|
144
|
+
if (paymentUrl) {
|
|
145
|
+
lines.push('', `(仅当确认是余额不足时才充值:${paymentUrl})`);
|
|
146
|
+
}
|
|
147
|
+
lines.push('', '系统只会用 token 发起请求,绝不在报告、日志或聊天输出里回显 token。');
|
|
148
|
+
return lines.join('\n');
|
|
149
|
+
}
|
|
150
|
+
|
|
129
151
|
function buildFmodeApiRechargeUrl() {
|
|
130
152
|
return FMODE_API_RECHARGE_URL;
|
|
131
153
|
}
|
|
@@ -160,6 +182,7 @@ module.exports = {
|
|
|
160
182
|
buildVocRechargeUrl,
|
|
161
183
|
buildVocWorkshopUrl,
|
|
162
184
|
buildMissingTokenMessage,
|
|
185
|
+
buildMissingNewApiTokenMessage,
|
|
163
186
|
buildRechargeRequiredMessage,
|
|
164
187
|
buildWrongTokenTypeMessage,
|
|
165
188
|
buildFmodeApiRechargeUrl,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const { readVocToken, readEnvFileMaybe, envLocalCandidates } = require('../../core/credentials');
|
|
3
|
+
const { readVocToken, readNewApiToken, readEnvFileMaybe, envLocalCandidates } = require('../../core/credentials');
|
|
4
4
|
const { buildVocRechargeInfo } = require('../../core/payment-links');
|
|
5
5
|
const { okResult, errorResult } = require('../../core/result-envelope');
|
|
6
6
|
|
|
@@ -30,12 +30,12 @@ async function analyzeFmodeImage(input = {}) {
|
|
|
30
30
|
if (!token) {
|
|
31
31
|
const recharge = await buildVocRechargeInfo();
|
|
32
32
|
return friendlyResult('needs_token', buildMissingTokenMessage(recharge.paymentUrl, recharge), {
|
|
33
|
-
summary: { imageCount: images.length, model: provider.model },
|
|
33
|
+
summary: { imageCount: images.length, model: provider.model, errorKind: 'missing_token', recoverable: true },
|
|
34
34
|
data: { recharge, model: provider.model, imageCount: images.length },
|
|
35
35
|
nextActions: [
|
|
36
|
-
|
|
37
|
-
'
|
|
38
|
-
'
|
|
36
|
+
'从 ~/.claude/settings.json 取 env.ANTHROPIC_AUTH_TOKEN(sk-,base 指向 api.fmode.cn),用 FMODE_API_KEY=sk-… 或入参 newapiToken/fmodeToken 传入后重试',
|
|
37
|
+
'或在当前工作区 .env.local 配置 FMODE_API_KEY / VOC_TOKEN / FMODE_TOKEN / TIHAO_SESSION_TOKEN',
|
|
38
|
+
'仅当确认是 402 余额不足时才打开充值链接,缺 token 不是没钱、也不代表功能用不了'
|
|
39
39
|
]
|
|
40
40
|
});
|
|
41
41
|
}
|
|
@@ -61,8 +61,8 @@ async function analyzeFmodeImage(input = {}) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
if (response.status === 401) {
|
|
64
|
-
return friendlyResult('needs_valid_token', '
|
|
65
|
-
summary: { imageCount: images.length, model: provider.model },
|
|
64
|
+
return friendlyResult('needs_valid_token', 'token 鉴权没过(无效/过期/类型不对),这是**可恢复**问题,不代表功能用不了、也不是没钱。优先从 `~/.claude/settings.json` 取 `env.ANTHROPIC_AUTH_TOKEN`(`sk-`)用 `FMODE_API_KEY=sk-…` 传入重试;仍不行再换有效 vocToken / sessionToken。', {
|
|
65
|
+
summary: { imageCount: images.length, model: provider.model, errorKind: 'auth', recoverable: true },
|
|
66
66
|
data: { model: provider.model, imageCount: images.length }
|
|
67
67
|
});
|
|
68
68
|
}
|
|
@@ -216,7 +216,11 @@ function resolveFmodeToken(input = {}) {
|
|
|
216
216
|
process.env.DOUBAO_VISION_TOKEN,
|
|
217
217
|
process.env.VIDEO_ANALYSIS_TOKEN,
|
|
218
218
|
process.env.TIHAO_SESSION_TOKEN,
|
|
219
|
-
readVocToken(input)
|
|
219
|
+
readVocToken(input),
|
|
220
|
+
// 兜底:NewAPI(fmode-api)的 sk- 计费 token——它就是 Claude Code 的
|
|
221
|
+
// ANTHROPIC_AUTH_TOKEN(~/.claude/settings.json 的 env),api.fmode.cn 直接认。
|
|
222
|
+
// 默认装完技能即用,避免「明明配了 SK 却判缺 token」的死循环。
|
|
223
|
+
readNewApiToken(input)
|
|
220
224
|
]);
|
|
221
225
|
}
|
|
222
226
|
|
|
@@ -371,12 +375,12 @@ function buildAssistantMessage({ analysis, provider, images, files }) {
|
|
|
371
375
|
function buildMissingTokenMessage(paymentUrl, recharge = {}) {
|
|
372
376
|
const packageUrl = recharge.recommendedPackageUrl || recharge.workshopUrl || paymentUrl;
|
|
373
377
|
return [
|
|
374
|
-
'
|
|
378
|
+
'还没读到可用于 Fmode 图片分析的计费 token,暂时不能调用 vision 模型——这是**配置/读取**问题,不是余额不足,也不代表这个功能用不了。',
|
|
375
379
|
'',
|
|
376
|
-
|
|
377
|
-
|
|
380
|
+
'这把计费 token 就是 Claude Code 的 `ANTHROPIC_AUTH_TOKEN`(`sk-` 开头),通常配在用户级 `~/.claude/settings.json` 的 `env` 里(`ANTHROPIC_BASE_URL` 指向 `api.fmode.cn`)。',
|
|
381
|
+
'请优先取出它,用 `FMODE_API_KEY=sk-…`(或入参 `newapiToken`/`fmodeToken`)传给本工具重试;也可写进当前工作区 `.env.local`。',
|
|
378
382
|
'',
|
|
379
|
-
|
|
383
|
+
`(仅当确认是 402 余额不足时才充值:${paymentUrl},套餐入口:${packageUrl})`,
|
|
380
384
|
'',
|
|
381
385
|
'系统只会把 token 用作接口鉴权,不会在聊天、报告、日志或输出文件里回显。'
|
|
382
386
|
].join('\n');
|
|
@@ -11,6 +11,7 @@ const { callSocialGateway, redactSecret } = require('../providers/voc-gateway');
|
|
|
11
11
|
const {
|
|
12
12
|
buildVocRechargeInfo,
|
|
13
13
|
buildMissingTokenMessage,
|
|
14
|
+
buildMissingNewApiTokenMessage,
|
|
14
15
|
buildFmodeApiRechargeUrl,
|
|
15
16
|
buildFmodeApiRechargeMessage,
|
|
16
17
|
buildPermissionDeniedMessage
|
|
@@ -172,13 +173,13 @@ async function callVocApi(input = {}) {
|
|
|
172
173
|
const recharge = await buildVocRechargeInfo();
|
|
173
174
|
return {
|
|
174
175
|
status: 'needs_token',
|
|
175
|
-
assistantMessage:
|
|
176
|
-
summary: { endpoint: endpoint?.id || rawPath },
|
|
176
|
+
assistantMessage: buildMissingNewApiTokenMessage(recharge.paymentUrl, recharge, { platformLabel: '社交平台' }),
|
|
177
|
+
summary: { endpoint: endpoint?.id || rawPath, errorKind: 'missing_token', recoverable: true },
|
|
177
178
|
data: { recharge },
|
|
178
179
|
files: [],
|
|
179
180
|
nextActions: [
|
|
180
|
-
|
|
181
|
-
'
|
|
181
|
+
'从 ~/.claude/settings.json 取 env.ANTHROPIC_AUTH_TOKEN(sk-),用 FMODE_API_KEY=sk-… 或入参 newapiToken 传入后重新调用',
|
|
182
|
+
'仍不行再回退以 r: 开头的会话 token;仅当确认是 402 余额不足时才打开充值链接'
|
|
182
183
|
],
|
|
183
184
|
warnings: [],
|
|
184
185
|
errors: []
|
|
@@ -249,16 +250,21 @@ async function callVocApi(input = {}) {
|
|
|
249
250
|
return {
|
|
250
251
|
status: 'needs_valid_token',
|
|
251
252
|
assistantMessage: [
|
|
252
|
-
'当前
|
|
253
|
+
'当前 token 没有通过校验,暂时不能调用该接口——这是 token 缺失/无效/失效的**可恢复**问题,不是关键词/类目/参数问题,也不代表这个功能用不了、更不是余额不足。',
|
|
253
254
|
'',
|
|
254
|
-
'
|
|
255
|
+
'优先自救:NewAPI(fmode-api)计费用的 `sk-` 就是 Claude Code 的 `ANTHROPIC_AUTH_TOKEN`(在 `~/.claude/settings.json` 的 `env` 里)。请取出它(确认 `sk-` 开头、`ANTHROPIC_BASE_URL` 指向 `api.fmode.cn`),用 `FMODE_API_KEY=sk-…` 或入参 `newapiToken` 传入后重试。',
|
|
255
256
|
'',
|
|
256
|
-
|
|
257
|
+
'若 `sk-` 仍校验不过,再回退以 `r:` 开头的会话 token。',
|
|
258
|
+
'',
|
|
259
|
+
`(仅当确认是 402 余额不足时才需充值:${recharge.paymentUrl})`
|
|
257
260
|
].join('\n'),
|
|
258
|
-
summary: { endpoint: endpoint?.id || proxyPath, errorKind: kind, httpStatus },
|
|
261
|
+
summary: { endpoint: endpoint?.id || proxyPath, errorKind: kind, httpStatus, recoverable: true },
|
|
259
262
|
data: { recharge },
|
|
260
263
|
files: [],
|
|
261
|
-
nextActions: [
|
|
264
|
+
nextActions: [
|
|
265
|
+
'从 ~/.claude/settings.json 取 env.ANTHROPIC_AUTH_TOKEN(sk-),用 FMODE_API_KEY=sk-… 或入参 newapiToken 传入后重试',
|
|
266
|
+
'仍不行再回退以 r: 开头的 session token;仅当确认是 402 余额不足时才打开充值链接'
|
|
267
|
+
],
|
|
262
268
|
warnings: [],
|
|
263
269
|
errors: []
|
|
264
270
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vocmarket/voc-skill",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.23",
|
|
4
4
|
"description": "Claude Code VOC intelligence skills for Xiaohongshu and Douyin trend reports, live social VOC collection, Fmode image analysis, single-issue deep dives, boss/operator action plans, and multi-turn memory.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -35,13 +35,17 @@ VOC 后端是一个**通用转发网关**(`https://server.fmode.cn/api/voc-soc
|
|
|
35
35
|
|
|
36
36
|
## Token(计费已迁移 NewAPI)
|
|
37
37
|
|
|
38
|
-
请求头统一 `Authorization: Bearer ${token}`,**优先用 NewAPI(fmode-api)的 `sk-` token
|
|
38
|
+
请求头统一 `Authorization: Bearer ${token}`,**优先用 NewAPI(fmode-api)的 `sk-` token**。
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
> **这把 `sk-` 计费 token 是什么、从哪来**:它就是 **Claude Code 自己的 `ANTHROPIC_AUTH_TOKEN`**——配在用户级 **`~/.claude/settings.json` 的 `env.ANTHROPIC_AUTH_TOKEN`**(`sk-` 开头,且 `ANTHROPIC_BASE_URL` 指向 `api.fmode.cn`)。Claude Code 用户**装完技能即用,无需任何额外配置**:工具会自动从这里(以及进程环境)读到它。所以 newapi(fmode-api)≠ 某个要单独开通的东西,它用的就是你正在用的这把 fmode key。
|
|
41
|
+
|
|
42
|
+
- 取值优先级:入参 `newapiToken`/`fmodeApiKey` → `.env.local` / 环境变量 `FMODE_API_KEY`·`NEWAPI_TOKEN` → `~/.claude/voc-credentials.json` 的 `fmodeApiKey` → `~/.fmode/config.json` 的 `newapiToken`/`fmodeApiToken` → **`~/.claude/settings.json` 的 `env.ANTHROPIC_AUTH_TOKEN`(Claude Code 默认入口,`sk-` 开头)**。
|
|
43
|
+
- `sk-` 鉴权失败(401,含「未入仓」类报错)且存在 `r:` 开头的会话 token 时,工具会**自动回退**用 `r:` sessionToken 走原 apig 接口重试一次(过渡期:服务端未迁完 / 该号未入仓)。此回退是**正常**的原始付费路径,不是出故障。
|
|
42
44
|
- 兼容:若把 `sk-` 直接填进 `VOC_TOKEN`,也会被当作 NewAPI token 使用。
|
|
43
45
|
- token **绝不回显**;底层鉴权/入仓原始报错不甩给用户,只给友好提示。
|
|
44
46
|
|
|
47
|
+
> **缺 token / token 无效时该怎么自救(重要,别进死循环)**:这是**可恢复**状态,不是「技能用不了」。先确认 `~/.claude/settings.json` 的 `env.ANTHROPIC_AUTH_TOKEN` 是否为 `sk-` 开头的 fmode key;若工具仍报缺 token,把它取出来用 `FMODE_API_KEY=sk-…`(或入参 `newapiToken`)传给工具再调一次。**不要**因为提示里出现充值/付费链接就让用户去点充值——**只有真正 `402 余额不足` 才需要充值**,缺 token 只是没读到这把 SK 而已。
|
|
48
|
+
|
|
45
49
|
> 错误码(401/402/403 等)的完整对照、真实报文样例与处理纪律见 **`references/error-codes.md`**。
|
|
46
50
|
|
|
47
51
|
## 禁止事项(护栏)
|
|
@@ -49,13 +53,14 @@ VOC 后端是一个**通用转发网关**(`https://server.fmode.cn/api/voc-soc
|
|
|
49
53
|
- 禁止把**入参/参数错误**(`needs_input`)误报成「采集不到数据 / 该关键词没结果 / 类目不支持」。参数错就如实说参数错,并用 `voc_api_doc` 核对后重试。
|
|
50
54
|
- 禁止把**上游不稳定**(`upstream_unstable`,如 5xx / 连接超时 / fetch failed)误报成「关键词问题 / 类目不支持 / 余额不足」。如实说明是上游接口波动,稍后重试。
|
|
51
55
|
- 禁止把 **token 无效/类型不对**(`needs_valid_token`)误报成「余额不足」或「类目不支持」。
|
|
56
|
+
- 禁止把**缺 token / token 无效**(`needs_token` / `needs_valid_token`)误报成「这个技能用不了 / 功能不可用」——它是**可恢复**的:SK 就在 `~/.claude/settings.json` 的 `env.ANTHROPIC_AUTH_TOKEN`,取出来用 `FMODE_API_KEY=sk-…` 传入重试即可,别因此放弃或去点充值。
|
|
52
57
|
- 禁止输出「某行业不可用 / 系统只支持某类目」这类结论——**任何行业、任何关键词都能采集**,前提只是 token 类型正确且有额度。
|
|
53
58
|
- 失败时禁止退回 web search / yt-dlp / ffmpeg / 让用户手动传媒体文件来「绕过」采集。
|
|
54
59
|
|
|
55
60
|
## 状态码含义(详表见 `references/error-codes.md`)
|
|
56
61
|
|
|
57
62
|
- `ok`:调用成功,`data.result` 是上游返回数据。
|
|
58
|
-
- `needs_token
|
|
63
|
+
- `needs_token`:没读到任何 token(**可恢复,不是没钱、不是功能用不了**)——优先去 `~/.claude/settings.json` 取 `env.ANTHROPIC_AUTH_TOKEN`(`sk-`)用 `FMODE_API_KEY=sk-…` 传入重试。`needs_valid_token`:token 缺失/失效/无效(HTTP 401)——**不是没钱**,提示配置有效 `sk-`(或回退 `r:`)token。
|
|
59
64
|
- `needs_recharge`:**仅 HTTP 402(余额不足)**,提示充值,跳 `https://app.fmode.cn/dev/studio/?balance=fmodeapi`。
|
|
60
65
|
- `needs_permission`:**HTTP 403(账号禁用/无权限)**,**≠ 余额不足**,不引导充值,提示联系服务方确认权限。
|
|
61
66
|
- `needs_input`:必填参数缺失或参数错误(不是类目/余额问题)。
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# VOC 社交数据采集 · 错误码速查
|
|
2
2
|
|
|
3
|
-
> 给 Agent
|
|
3
|
+
> 给 Agent 按需查阅。**核心纪律:不是所有报错都是「余额不足」,也不是所有报错都代表「功能用不了」。** 只有 **HTTP 402** 才提示充值;
|
|
4
4
|
> **401 = token 问题**(缺失/失效/类型不对),**403 = 账号禁用/无权限**,都**不是没钱**,不要引导充值。
|
|
5
|
+
> **缺 token / 401 是可恢复的**:NewAPI 计费用的 `sk-` 就是 Claude Code 的 `ANTHROPIC_AUTH_TOKEN`(在 `~/.claude/settings.json` 的 `env` 里),取出来用 `FMODE_API_KEY=sk-…` 传入重试即可,**别当成「这个技能不能用」而放弃或去点充值**。
|
|
5
6
|
|
|
6
7
|
适用范围:`voc_api_call` / `voc_api_doc`(voc-social 通用转发网关 `https://server.fmode.cn/api/voc-social/<proxyPath>`)。
|
|
7
8
|
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
| HTTP | 后端 `mess` 样例 | 判定 kind | 工具 status | 给客户 Agent 的话术 | Agent 该做什么 |
|
|
11
12
|
|------|------------------|-----------|-------------|----------------------|----------------|
|
|
12
13
|
| 200 | —(`{"code":200,"data":{...}}`) | — | `ok` | 正常返回 | 直接用 `data.result` |
|
|
13
|
-
| 401 | `Invalid NewAPI token` / `Missing NewAPI token` / `请输入API_KEY或用户sessionToken` / `当前用户不存在,无使用权限` | `auth` | `needs_valid_token`(无任何 token 时 `needs_token`) | 「token
|
|
14
|
+
| 401 | `Invalid NewAPI token` / `Missing NewAPI token` / `请输入API_KEY或用户sessionToken` / `当前用户不存在,无使用权限` | `auth` | `needs_valid_token`(无任何 token 时 `needs_token`) | 「token 缺失/失效/类型不对,**不是没钱、也不是关键词/类目问题,更不是功能用不了**」 | **先**从 `~/.claude/settings.json` 取 `env.ANTHROPIC_AUTH_TOKEN`(`sk-`),用 `FMODE_API_KEY=sk-…` 传入重试;不行再回退 `r:` sessionToken |
|
|
14
15
|
| 402 | `余额不足` / `额度不足` | `billing` | `needs_recharge` | 「余额不足,请充值」 | 给充值链接 `https://app.fmode.cn/dev/studio/?balance=fmodeapi`(自动打开余额弹窗) |
|
|
15
16
|
| 403 | `用户禁用` / `无权限` / `permission`(**不含**余额/开通关键词) | `permission` | `needs_permission` | 「账号被禁用或没有该接口权限,**≠ 余额不足**」 | 联系服务方确认账号状态/接口权限,**不要**引导充值 |
|
|
16
17
|
| 403 | 含 `未开通` / `没开通` / `余额` / `额度` / `insufficient` / `balance` | `billing` | `needs_recharge` | 「需开通/充值后使用」 | 给充值/开通链接 |
|
|
@@ -46,8 +47,12 @@ HTTP 403 {"code":403,"mess":"用户禁用或无权限"}
|
|
|
46
47
|
|
|
47
48
|
计费已迁移到 **NewAPI(fmode-api)**,请求头统一 `Authorization: Bearer ${token}`:
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
> **这把 `sk-` 是什么**:它就是 **Claude Code 自己的 `ANTHROPIC_AUTH_TOKEN`**(配在 `~/.claude/settings.json` 的 `env`,`sk-` 开头,`ANTHROPIC_BASE_URL` 指向 `api.fmode.cn`)。所以 Claude Code 用户**装完即用、零额外配置**,工具会自动从 settings.json / 进程环境读到它。newapi(fmode-api)不是要单独开通的东西,就是你正在用的这把 fmode key。
|
|
51
|
+
|
|
52
|
+
1. **优先**用 NewAPI 的 `sk-` token,取值优先级:
|
|
53
|
+
入参 `newapiToken/fmodeApiKey` → `.env.local` / 环境变量 `FMODE_API_KEY`/`NEWAPI_TOKEN` → `~/.claude/voc-credentials.json` 的 `fmodeApiKey` → `~/.fmode/config.json` 的 `newapiToken/fmodeApiToken` → **`~/.claude/settings.json` 的 `env.ANTHROPIC_AUTH_TOKEN`(Claude Code 默认入口)**。
|
|
50
54
|
- 兼容历史:若 `VOC_TOKEN` 槽里填的就是 `sk-`,也当作 NewAPI token 使用。
|
|
55
|
+
- 若工具报 `needs_token`/`needs_valid_token`:先去 `~/.claude/settings.json` 取 `env.ANTHROPIC_AUTH_TOKEN`,用 `FMODE_API_KEY=sk-…` 传入重试——**这是缺 token 的标准自救,不是去点充值**。
|
|
51
56
|
2. 若 `sk-` token **鉴权失败(kind=auth / 401,含「未入仓」类报错)** 且存在平台 `r:` sessionToken,**自动回退** sessionToken 重试一次。这正是规避「用户拿 token 请求时遇到入仓错误」的方式——不再因为 token 是 `sk-` 就直接拒绝。
|
|
52
57
|
3. **402(余额不足)/ 403(无权限)不回退**——它们是终态,按上表处理。
|
|
53
58
|
4. 底层「未入仓」/鉴权原始报错**不甩给用户**,对外只给上表的友好提示。
|