@misaka-net/misakanet-setup 0.8.0 → 0.8.1
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/bin/misakanet-setup.mjs +47 -2
- package/package.json +1 -1
package/bin/misakanet-setup.mjs
CHANGED
|
@@ -197,7 +197,7 @@ function readText(path) {
|
|
|
197
197
|
* except the two that name the version (measured 2026-10-22: the 0.6.0 release pull request
|
|
198
198
|
* failed 9 e2e legs on exactly that).
|
|
199
199
|
*/
|
|
200
|
-
const VERSION = '0.8.
|
|
200
|
+
const VERSION = '0.8.1' // x-release-please-version
|
|
201
201
|
|
|
202
202
|
/**
|
|
203
203
|
* The shape of a client id, in one place.
|
|
@@ -1245,6 +1245,40 @@ let lastResponseOk = false;
|
|
|
1245
1245
|
// `--verify` can print is what actually came back (`{}` is not a diagnosis).
|
|
1246
1246
|
let lastResponsePreview = '';
|
|
1247
1247
|
|
|
1248
|
+
// The server's retry horizon on the last response, in seconds, when it sent one: the `Retry-After`
|
|
1249
|
+
// response header (seconds or an HTTP-date), the standard shape `docs/index.html` already reads on
|
|
1250
|
+
// 429s. Per-request, so a horizon from an earlier call can never leak into a later failure's words.
|
|
1251
|
+
let lastRetryAfterSeconds = null;
|
|
1252
|
+
|
|
1253
|
+
/** `Retry-After` → seconds. Both forms the header allows; anything else is "no horizon", not 0. */
|
|
1254
|
+
function retryAfterSeconds(headerValue) {
|
|
1255
|
+
const raw = String(headerValue || '').trim();
|
|
1256
|
+
if (!raw) return null;
|
|
1257
|
+
if (/^\d+$/.test(raw)) return Number(raw);
|
|
1258
|
+
const at = Date.parse(raw);
|
|
1259
|
+
if (Number.isNaN(at)) return null;
|
|
1260
|
+
return Math.max(0, Math.round((at - Date.now()) / 1000));
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* How long the server says to wait before re-running, when it says. The failure body's `retry_after`
|
|
1265
|
+
* (seconds, HTTP `Retry-After` semantics) wins over the header, and neither is guessed: a server that
|
|
1266
|
+
* stays silent gets no horizon sentence at all.
|
|
1267
|
+
*/
|
|
1268
|
+
function retryHorizonOf(result) {
|
|
1269
|
+
const fromBody = result && (result.retry_after ?? result.retry_after_seconds);
|
|
1270
|
+
const n = Number(fromBody);
|
|
1271
|
+
if (Number.isFinite(n) && n > 0) return Math.round(n);
|
|
1272
|
+
return lastRetryAfterSeconds;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
/** "45 秒" / "15 分钟" / "2 小时" — the horizon in the words a person reads a clock in. */
|
|
1276
|
+
function humanWait(seconds) {
|
|
1277
|
+
if (seconds < 60) return `${seconds} 秒`;
|
|
1278
|
+
if (seconds < 3600) return `${Math.round(seconds / 60)} 分钟`;
|
|
1279
|
+
return `${Math.round(seconds / 3600)} 小时`;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1248
1282
|
async function mcpRequest(method, params, timeoutMs = 6000, urlOverride = '') {
|
|
1249
1283
|
const headers = {
|
|
1250
1284
|
'Content-Type': 'application/json',
|
|
@@ -1260,6 +1294,7 @@ async function mcpRequest(method, params, timeoutMs = 6000, urlOverride = '') {
|
|
|
1260
1294
|
lastRequestKind = '';
|
|
1261
1295
|
lastResponseOk = false;
|
|
1262
1296
|
lastResponsePreview = '';
|
|
1297
|
+
lastRetryAfterSeconds = null;
|
|
1263
1298
|
try {
|
|
1264
1299
|
const response = await fetch(urlOverride || ENDPOINT, {
|
|
1265
1300
|
method: 'POST',
|
|
@@ -1271,6 +1306,9 @@ async function mcpRequest(method, params, timeoutMs = 6000, urlOverride = '') {
|
|
|
1271
1306
|
// below, not by this line — but "it answered with something that is not a handshake" and
|
|
1272
1307
|
// "nothing answered" are different facts (see probeOutcome()).
|
|
1273
1308
|
lastResponseOk = true;
|
|
1309
|
+
lastRetryAfterSeconds = retryAfterSeconds(
|
|
1310
|
+
response.headers && typeof response.headers.get === 'function'
|
|
1311
|
+
? response.headers.get('Retry-After') : null);
|
|
1274
1312
|
const payload = await response.json();
|
|
1275
1313
|
lastResponsePreview = JSON.stringify(payload).slice(0, 160);
|
|
1276
1314
|
if (payload && payload.error) {
|
|
@@ -1618,8 +1656,15 @@ async function ensureIdentity() {
|
|
|
1618
1656
|
: serverCode
|
|
1619
1657
|
? `服务端拒绝了这次注册(${serverCode})`
|
|
1620
1658
|
: `多半是网络(${lastRequestError || '没有收到响应'})`;
|
|
1659
|
+
// …and when the server says *how long* to wait, say that too instead of "稍后". The horizon is
|
|
1660
|
+
// never guessed and never rounded into a promise: a server that sent none keeps the vague
|
|
1661
|
+
// wording, because a made-up wait is the same lie as the network guess this replaced (issue
|
|
1662
|
+
// #3511 — the retry-horizon half; the `code` half landed with #3512).
|
|
1663
|
+
const horizon = retryHorizonOf(result);
|
|
1621
1664
|
need(`匿名注册没成功:${cause} → 读课程不受影响,但写入类工具(write_lesson)现在用不了;`
|
|
1622
|
-
+
|
|
1665
|
+
+ (horizon
|
|
1666
|
+
? `约 ${humanWait(horizon)}后重跑本命令就能拿到 token(服务端给出的重试窗口)`
|
|
1667
|
+
: '稍后重跑本命令就能拿到 token')
|
|
1623
1668
|
+ (serverHint ? `(服务端提示:${serverHint})` : ''));
|
|
1624
1669
|
return '';
|
|
1625
1670
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@misaka-net/misakanet-setup",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "MisakaNet failure lessons for your agents: writes an MCP endpoint into each agent config, rules blocks where the client takes one, and a Claude Code hook. At most one anonymous request leaves this machine at install; then only the agent's calls — unless MISAKANET_HOOK_FETCH=1 (off by default) the hook sends the failed call's error text or command.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"misakanet-setup": "bin/misakanet-setup.mjs"
|