redskillhub-upload 1.0.0-alpha.5 → 1.0.0-alpha.6
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/README.md +4 -5
- package/cli/auth.mjs +10 -41
- package/cli/config.mjs +2 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Publish input can be a local Skill directory or a `.zip` file. User-provided zip
|
|
|
16
16
|
|
|
17
17
|
## API Endpoints
|
|
18
18
|
|
|
19
|
-
All paths and hosts are centralized in `cli/config.mjs` (`PATHS` + `DEFAULT_API_BASE` / `
|
|
19
|
+
All paths and hosts are centralized in `cli/config.mjs` (`PATHS` + `DEFAULT_API_BASE` / `BETA_API_BASE`).
|
|
20
20
|
|
|
21
21
|
| Path Key | Path | Host | Description |
|
|
22
22
|
|----------|------|------|-------------|
|
|
@@ -24,16 +24,15 @@ All paths and hosts are centralized in `cli/config.mjs` (`PATHS` + `DEFAULT_API_
|
|
|
24
24
|
| `SUBMIT_SKILL_VERSION` | `/api/sns/v1/openapi/skillhub/submit_skill_version` | `DEFAULT_API_BASE` | 提交 skill 版本 |
|
|
25
25
|
| `CREATE_CLI_OAUTH_DEVICE_CODE` | `/api/sns/v1/creator/red_skill/create_cli_oauth_device_code` | `DEFAULT_API_BASE` | 创建 CLI OAuth 设备码 |
|
|
26
26
|
| `POLL_CLI_OAUTH_DEVICE_TOKEN` | `/api/sns/v1/creator/red_skill/poll_cli_oauth_device_token` | `DEFAULT_API_BASE` | 轮询 CLI OAuth 授权状态 |
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
27
|
+
| `EXCHANGE_CLI_OAUTH_TOKEN` | `/api/sns/v1/creator/red_skill/exchange_cli_oauth_token` | `DEFAULT_API_BASE` / `BETA_API_BASE` | 通过 SkillHub 服务端换取 access_token |
|
|
28
|
+
| `REFRESH_CLI_OAUTH_TOKEN` | `/api/sns/v1/creator/red_skill/refresh_cli_oauth_token` | `DEFAULT_API_BASE` / `BETA_API_BASE` | 通过 SkillHub 服务端续期 access_token |
|
|
29
29
|
|
|
30
30
|
- `DEFAULT_API_BASE` = `https://edith.xiaohongshu.com` — 生产业务接口
|
|
31
31
|
- `BETA_API_BASE` = `https://edith.beta.xiaohongshu.com` — beta 业务接口
|
|
32
|
-
- `DEFAULT_OAS_BASE` = `https://openaccount.xiaohongshu.com` — OAuth2 开放平台接口
|
|
33
32
|
- 所有命令都支持 `--env prod|beta`,默认 `prod`;`--env beta` 时业务接口使用 `BETA_API_BASE`
|
|
34
33
|
- prod 沿用 `~/.skillhub-upload`,beta 使用 `~/.skillhub-upload/beta`,凭证、待授权、二维码、标签缓存和临时包相互隔离
|
|
35
34
|
- 业务接口 host 可通过 `--api-base` 参数或 `SKILLHUB_UPLOAD_API_BASE` 环境变量覆盖
|
|
36
|
-
-
|
|
35
|
+
- OAuth token 换取与刷新统一走 SkillHub 服务端 broker,CLI 不保存 appSecret
|
|
37
36
|
|
|
38
37
|
## Auth Flow
|
|
39
38
|
|
package/cli/auth.mjs
CHANGED
|
@@ -3,9 +3,6 @@ import fs from 'node:fs/promises';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_API_BASE,
|
|
6
|
-
DEFAULT_APP_ID,
|
|
7
|
-
DEFAULT_APP_SECRET,
|
|
8
|
-
DEFAULT_OAS_BASE,
|
|
9
6
|
DEFAULT_SCOPE,
|
|
10
7
|
DEFAULT_SCOPES,
|
|
11
8
|
PATHS,
|
|
@@ -108,34 +105,17 @@ function unwrapResponseBody(body) {
|
|
|
108
105
|
return body?.data && typeof body.data === 'object' ? body.data : body;
|
|
109
106
|
}
|
|
110
107
|
|
|
111
|
-
function resolveAppSecret(flags = {}, env = process.env) {
|
|
112
|
-
return flags.appSecret
|
|
113
|
-
|| flags['app-secret']
|
|
114
|
-
|| env.SKILLHUB_UPLOAD_APP_SECRET
|
|
115
|
-
|| DEFAULT_APP_SECRET;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
108
|
function buildTokenRequestBody(fields = {}) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
code: fields.authorizationCode,
|
|
109
|
+
return {
|
|
110
|
+
authorization_code: fields.authorizationCode,
|
|
122
111
|
code_verifier: fields.codeVerifier
|
|
123
112
|
};
|
|
124
|
-
if (fields.appSecret) {
|
|
125
|
-
body.app_secret = fields.appSecret;
|
|
126
|
-
}
|
|
127
|
-
return body;
|
|
128
113
|
}
|
|
129
114
|
|
|
130
115
|
function buildRefreshRequestBody(fields = {}) {
|
|
131
|
-
|
|
132
|
-
app_id: fields.appId,
|
|
116
|
+
return {
|
|
133
117
|
refresh_token: fields.refreshToken
|
|
134
118
|
};
|
|
135
|
-
if (fields.appSecret) {
|
|
136
|
-
body.app_secret = fields.appSecret;
|
|
137
|
-
}
|
|
138
|
-
return body;
|
|
139
119
|
}
|
|
140
120
|
|
|
141
121
|
function getRejectMessage(body, fallback) {
|
|
@@ -231,14 +211,12 @@ export async function pollDeviceToken({ apiBase, deviceCode, fetchImpl = compati
|
|
|
231
211
|
}
|
|
232
212
|
|
|
233
213
|
export async function exchangeToken({
|
|
234
|
-
tokenUrl = `${
|
|
235
|
-
appId = DEFAULT_APP_ID,
|
|
236
|
-
appSecret = '',
|
|
214
|
+
tokenUrl = `${DEFAULT_API_BASE}${PATHS.EXCHANGE_CLI_OAUTH_TOKEN}`,
|
|
237
215
|
authorizationCode,
|
|
238
216
|
codeVerifier,
|
|
239
217
|
fetchImpl = compatibleFetch
|
|
240
218
|
}) {
|
|
241
|
-
const body = buildTokenRequestBody({
|
|
219
|
+
const body = buildTokenRequestBody({ authorizationCode, codeVerifier });
|
|
242
220
|
const response = await fetchImpl(tokenUrl, {
|
|
243
221
|
method: 'POST',
|
|
244
222
|
headers: { 'content-type': 'application/json' },
|
|
@@ -257,13 +235,11 @@ export async function exchangeToken({
|
|
|
257
235
|
}
|
|
258
236
|
|
|
259
237
|
export async function refreshToken({
|
|
260
|
-
tokenUrl = `${
|
|
261
|
-
appId = DEFAULT_APP_ID,
|
|
262
|
-
appSecret = '',
|
|
238
|
+
tokenUrl = `${DEFAULT_API_BASE}${PATHS.REFRESH_CLI_OAUTH_TOKEN}`,
|
|
263
239
|
refreshToken: rt,
|
|
264
240
|
fetchImpl = compatibleFetch
|
|
265
241
|
}) {
|
|
266
|
-
const body = buildRefreshRequestBody({
|
|
242
|
+
const body = buildRefreshRequestBody({ refreshToken: rt });
|
|
267
243
|
const response = await fetchImpl(tokenUrl, {
|
|
268
244
|
method: 'POST',
|
|
269
245
|
headers: { 'content-type': 'application/json' },
|
|
@@ -283,7 +259,6 @@ export async function refreshToken({
|
|
|
283
259
|
|
|
284
260
|
function buildCredentialsFromToken(token, nowMs, errorCode) {
|
|
285
261
|
const credentials = {
|
|
286
|
-
appId: token.app_id || token.appId || DEFAULT_APP_ID,
|
|
287
262
|
accessToken: token.access_token || token.accessToken || '',
|
|
288
263
|
refreshToken: token.refresh_token || token.refreshToken || '',
|
|
289
264
|
expireTimeMs: String(token.expire_time_ms || token.expireTimeMs || nowMs + Number(token.expires_in || 7200) * 1000),
|
|
@@ -349,6 +324,7 @@ export async function login({
|
|
|
349
324
|
now = () => Date.now(),
|
|
350
325
|
promptStream = process.stdout
|
|
351
326
|
} = {}) {
|
|
327
|
+
const apiBase = resolveApiBase(flags, env);
|
|
352
328
|
const cachedCredentials = await readCredentials(env, flags);
|
|
353
329
|
if (isValidCredentials(cachedCredentials, now())) {
|
|
354
330
|
return maskCredentials(cachedCredentials);
|
|
@@ -357,12 +333,9 @@ export async function login({
|
|
|
357
333
|
// access_token 过期但 refresh_token 仍有效,静默续期
|
|
358
334
|
if (canRefreshCredentials(cachedCredentials, now())) {
|
|
359
335
|
try {
|
|
360
|
-
const appSecret = resolveAppSecret(flags, env);
|
|
361
336
|
const token = await refreshToken({
|
|
362
|
-
appId: cachedCredentials.appId || DEFAULT_APP_ID,
|
|
363
|
-
appSecret,
|
|
364
337
|
refreshToken: cachedCredentials.refreshToken,
|
|
365
|
-
tokenUrl:
|
|
338
|
+
tokenUrl: `${apiBase}${PATHS.REFRESH_CLI_OAUTH_TOKEN}`,
|
|
366
339
|
fetchImpl
|
|
367
340
|
});
|
|
368
341
|
await writeCredentials(buildCredentialsFromToken(token, now(), 'OAS_TOKEN_REFRESH_INVALID_RESPONSE'), env, flags);
|
|
@@ -376,8 +349,6 @@ export async function login({
|
|
|
376
349
|
}
|
|
377
350
|
}
|
|
378
351
|
|
|
379
|
-
const apiBase = resolveApiBase(flags, env);
|
|
380
|
-
|
|
381
352
|
// 从磁盘恢复未完成的授权状态,避免进程重启后要求用户重新授权
|
|
382
353
|
let pending = await readPendingAuth(env, flags, now);
|
|
383
354
|
let codeVerifier;
|
|
@@ -457,11 +428,9 @@ export async function login({
|
|
|
457
428
|
}
|
|
458
429
|
|
|
459
430
|
const token = await exchangeToken({
|
|
460
|
-
appId: DEFAULT_APP_ID,
|
|
461
|
-
appSecret: resolveAppSecret(flags, env),
|
|
462
431
|
authorizationCode,
|
|
463
432
|
codeVerifier,
|
|
464
|
-
tokenUrl:
|
|
433
|
+
tokenUrl: `${apiBase}${PATHS.EXCHANGE_CLI_OAUTH_TOKEN}`,
|
|
465
434
|
fetchImpl
|
|
466
435
|
});
|
|
467
436
|
await writeCredentials(buildCredentialsFromToken(token, now(), 'OAS_TOKEN_EXCHANGE_INVALID_RESPONSE'), env, flags);
|
package/cli/config.mjs
CHANGED
|
@@ -3,9 +3,6 @@ import path from 'node:path';
|
|
|
3
3
|
|
|
4
4
|
export const DEFAULT_API_BASE = 'https://edith.xiaohongshu.com';
|
|
5
5
|
export const BETA_API_BASE = 'https://edith-redskill.sl.beta.xiaohongshu.com';
|
|
6
|
-
export const DEFAULT_OAS_BASE = 'https://openaccount.xiaohongshu.com';
|
|
7
|
-
export const DEFAULT_APP_ID = 'xhsyfHfmZQAQ24o';
|
|
8
|
-
export const DEFAULT_APP_SECRET = 'skHlFZ8BTk2bVmsSt1aYjv1NxcJjuOwhSev4oraniDa6eyn8L9oqBWvCtsEBnmCmWG';
|
|
9
6
|
export const DEFAULT_SCOPES = ['base_info', 'skill_publish'];
|
|
10
7
|
export const DEFAULT_SCOPE = DEFAULT_SCOPES.join(',');
|
|
11
8
|
|
|
@@ -14,8 +11,8 @@ export const PATHS = {
|
|
|
14
11
|
SUBMIT_SKILL_VERSION: '/api/sns/v1/creator/red_skill/cli_submit_skill_version',
|
|
15
12
|
CREATE_CLI_OAUTH_DEVICE_CODE: '/api/sns/v1/creator/red_skill/create_cli_oauth_device_code',
|
|
16
13
|
POLL_CLI_OAUTH_DEVICE_TOKEN: '/api/sns/v1/creator/red_skill/poll_cli_oauth_device_token',
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
EXCHANGE_CLI_OAUTH_TOKEN: '/api/sns/v1/creator/red_skill/exchange_cli_oauth_token',
|
|
15
|
+
REFRESH_CLI_OAUTH_TOKEN: '/api/sns/v1/creator/red_skill/refresh_cli_oauth_token',
|
|
19
16
|
QUERY_CONTENT_TAG_CONFIG: '/api/sns/v1/activity_platform/config/query_config'
|
|
20
17
|
};
|
|
21
18
|
export const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;
|