ai-fly 0.5.0-alpha.1 → 0.6.0
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/dist/ai-fly.js +4 -4
- package/dist/app/main.js +139 -41
- package/dist/{daemon-Oh-SQUeo.mjs → daemon-D5J50r7U.mjs} +1 -1
- package/dist/{import-DpW4ignw.mjs → import-DN8bNBJU.mjs} +1 -1
- package/dist/{revoke-CQdhajpu.mjs → revoke-DMx9hZUH.mjs} +1 -1
- package/dist/{run-nJ_AEsMr.mjs → run-C-CxjlLu.mjs} +1 -1
- package/dist/{runtime-D9rSByOO.mjs → runtime-DKmrkfNT.mjs} +9 -1
- package/dist/{serve-BlWkrYrl.mjs → serve-BB6-4Ixd.mjs} +129 -40
- package/dist/{serve-B7CRdGoy.mjs → serve-DJ8id2Qo.mjs} +1 -1
- package/dist/{share-BogH4NQJ.mjs → share-mozydAfj.mjs} +1 -1
- package/dist/{status-COzqwi-v.mjs → status-BlvQvxGW.mjs} +1 -1
- package/dist/{status-VCGexKPW.mjs → status-DsFpGrzC.mjs} +1 -1
- package/package.json +11 -11
- package/webui/dist/assets/index-8ao-de1h.js +64 -0
- package/webui/dist/assets/index-PfiSmwv4.css +2 -0
- package/webui/dist/index.html +2 -2
- package/webui/dist/assets/index-DQr5cBwc.js +0 -67
- package/webui/dist/assets/index-Dne1d-yC.css +0 -2
package/dist/ai-fly.js
CHANGED
|
@@ -66,7 +66,7 @@ async function dispatch(command, rest) {
|
|
|
66
66
|
const lazy = (load) => {
|
|
67
67
|
return (argv, ctx) => load().then((m) => m.run([...argv], ctx));
|
|
68
68
|
};
|
|
69
|
-
return match(command).with("serve", () => lazy(() => import("./serve-
|
|
69
|
+
return match(command).with("serve", () => lazy(() => import("./serve-DJ8id2Qo.mjs"))(rest)).with("app", async () => {
|
|
70
70
|
const { join, dirname } = await import("node:path");
|
|
71
71
|
const { fileURLToPath } = await import("node:url");
|
|
72
72
|
const { spawn } = await import("node:child_process");
|
|
@@ -81,7 +81,7 @@ async function dispatch(command, rest) {
|
|
|
81
81
|
return await new Promise((resolve) => {
|
|
82
82
|
child.once("exit", (c) => resolve(c ?? 0));
|
|
83
83
|
});
|
|
84
|
-
}).with("daemon", () => lazy(() => import("./daemon-
|
|
84
|
+
}).with("daemon", () => lazy(() => import("./daemon-D5J50r7U.mjs"))(rest)).with("presets", () => lazy(() => import("./presets-BwkVV7E2.mjs"))(rest)).with("hooks", () => lazy(() => import("./hooks-BztmB61b.mjs"))(rest)).with("secret", () => lazy(() => import("./secret-BrfkuRkP.mjs"))(rest)).with("settings", async () => (await import("./settings-15j9BB6j.mjs")).runAsSettings(rest, {})).with("relay", async () => (await import("./settings-15j9BB6j.mjs")).runAsRelay(rest, {})).with("test", () => lazy(() => import("./test-ChqaPwHA.mjs"))(rest)).with("service", () => lazy(() => import("./service-P-vz61tx.mjs"))(rest)).with("group", () => lazy(() => import("./group-Dh64zhKn.mjs"))(rest)).with("share", () => lazy(() => import("./share-mozydAfj.mjs"))(rest)).with("revoke", () => lazy(() => import("./revoke-DMx9hZUH.mjs"))(rest)).with("join", () => lazy(() => import("./join-CLD4fdCf.mjs"))(rest)).with("import", () => lazy(() => import("./import-DN8bNBJU.mjs"))(rest)).with("run", () => lazy(() => import("./run-C-CxjlLu.mjs"))(rest)).with("ports", () => lazy(() => import("./ports-DmBKgLSi.mjs"))(rest)).with("forget", () => lazy(() => import("./forget-DGJwF3Gy.mjs"))(rest)).with("services", () => lazy(() => import("./services-Deh7Gxwa.mjs"))(rest)).with("key", () => {
|
|
85
85
|
if (rest[0] === "add") return lazy(() => import("./key-JLJewbw9.mjs"))(rest);
|
|
86
86
|
return lazy(() => import("./key-Cpw-HzT4.mjs"))(rest);
|
|
87
87
|
}).with("status", async () => {
|
|
@@ -90,8 +90,8 @@ async function dispatch(command, rest) {
|
|
|
90
90
|
const wantConsumer = scope === "consumer" || scope !== "provider" && consumersRootExists();
|
|
91
91
|
const argv = scope === "provider" || scope === "consumer" ? tail : rest;
|
|
92
92
|
let code = 0;
|
|
93
|
-
if (wantProvider) code = Math.max(code, await lazy(() => import("./status-
|
|
94
|
-
if (wantConsumer) code = Math.max(code, await lazy(() => import("./status-
|
|
93
|
+
if (wantProvider) code = Math.max(code, await lazy(() => import("./status-DsFpGrzC.mjs"))(argv));
|
|
94
|
+
if (wantConsumer) code = Math.max(code, await lazy(() => import("./status-BlvQvxGW.mjs"))(argv));
|
|
95
95
|
if (!wantProvider && !wantConsumer) process.stdout.write("nothing to report: no provider storage and no imported consumers\n");
|
|
96
96
|
return code;
|
|
97
97
|
}).otherwise(() => {
|
package/dist/app/main.js
CHANGED
|
@@ -3775,6 +3775,7 @@ async function forwardHttp(ctx, plan, t) {
|
|
|
3775
3775
|
/** js-backend-fetch 路径:连接期探测 + fetch(fetchImpl 测试注入缝保留于此)。 */
|
|
3776
3776
|
const nativeResponse = async () => {
|
|
3777
3777
|
const probe = ctx.probeConnect ?? defaultProbeConnect;
|
|
3778
|
+
if (ctrl.signal.aborted || ctx.signal.aborted) throw new ProbeFailedError();
|
|
3778
3779
|
try {
|
|
3779
3780
|
await probe(plan.url, t.connectMs);
|
|
3780
3781
|
} catch {
|
|
@@ -3921,6 +3922,8 @@ async function forwardHttp(ctx, plan, t) {
|
|
|
3921
3922
|
}
|
|
3922
3923
|
//#endregion
|
|
3923
3924
|
//#region src/provider/engine.ts
|
|
3925
|
+
/** per-peer 并发授权会话容量(LRU 逐出——内核暂无会话终结通知的防泄漏上限)。 */
|
|
3926
|
+
const SESSION_AUTH_CAP = 32;
|
|
3924
3927
|
/** 错误码 → 载体响应(shared 映射表;null status(本地中止族)→ 504 语义)。 */
|
|
3925
3928
|
function errorCarrierResponse(code, message) {
|
|
3926
3929
|
const m = ERROR_HTTP_MAPPING[code] ?? {
|
|
@@ -4041,23 +4044,34 @@ var ProviderEngine = class {
|
|
|
4041
4044
|
async refreshSession(ps) {
|
|
4042
4045
|
if (!ps.authed()) return;
|
|
4043
4046
|
const dir = this.directory();
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4047
|
+
let anyAlive = false;
|
|
4048
|
+
for (const sessionId of [...ps.sessionAuthIds()]) {
|
|
4049
|
+
const sa = ps.sessionAuthOf(sessionId);
|
|
4050
|
+
if (sa === void 0) continue;
|
|
4051
|
+
const { valid } = evaluateKeyring(sa.keyring, dir);
|
|
4052
|
+
if (valid.length === 0) {
|
|
4053
|
+
ps.dropSessionAuth(sessionId);
|
|
4054
|
+
continue;
|
|
4055
|
+
}
|
|
4056
|
+
anyAlive = true;
|
|
4057
|
+
const header = buildAuthOk(valid, dir, { refresh: true });
|
|
4058
|
+
const json = JSON.stringify(header);
|
|
4059
|
+
if (json === sa.lastView) {
|
|
4060
|
+
sa.grants = valid;
|
|
4061
|
+
continue;
|
|
4062
|
+
}
|
|
4063
|
+
this.catalogSeq++;
|
|
4064
|
+
sa.grants = valid;
|
|
4065
|
+
sa.lastView = json;
|
|
4066
|
+
ps.notifyWatch(sessionId, header);
|
|
4067
|
+
}
|
|
4068
|
+
if (!anyAlive) {
|
|
4048
4069
|
this.keyIndex.untrack(ps);
|
|
4049
4070
|
ps.notifyWatchInvalidated();
|
|
4050
4071
|
this.fabric.disconnect(ps.peerId).catch(() => void 0);
|
|
4051
4072
|
return;
|
|
4052
4073
|
}
|
|
4053
|
-
const header = buildAuthOk(valid, dir, { refresh: true });
|
|
4054
|
-
const json = JSON.stringify(header);
|
|
4055
|
-
if (json === ps.lastView) return;
|
|
4056
|
-
this.catalogSeq++;
|
|
4057
|
-
ps.grants = valid;
|
|
4058
|
-
ps.lastView = json;
|
|
4059
4074
|
this.keyIndex.track(ps);
|
|
4060
|
-
ps.notifyWatch(header);
|
|
4061
4075
|
}
|
|
4062
4076
|
async addPeer(peerId) {
|
|
4063
4077
|
if (this.stopped) return;
|
|
@@ -4104,12 +4118,14 @@ var ProviderPeerServer = class {
|
|
|
4104
4118
|
disposed = false;
|
|
4105
4119
|
active = /* @__PURE__ */ new Map();
|
|
4106
4120
|
acquired = /* @__PURE__ */ new Map();
|
|
4107
|
-
/**
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4121
|
+
/**
|
|
4122
|
+
* session_id → 授权态(LRU;上限防泄漏——内核暂无「会话终结」通知,旧
|
|
4123
|
+
* session 条目由容量逐出。恢复轮 sessionId 稳定,逐出只发生在远超活跃
|
|
4124
|
+
* 会话数的场景)。
|
|
4125
|
+
*/
|
|
4126
|
+
sessionAuths = /* @__PURE__ */ new Map();
|
|
4127
|
+
/** watch 长轮询等待者(目录变更按各自 session 视图唤醒;失效 → 401)。 */
|
|
4128
|
+
watchWaiters = /* @__PURE__ */ new Map();
|
|
4113
4129
|
constructor(engine, peerId, fabric) {
|
|
4114
4130
|
this.engine = engine;
|
|
4115
4131
|
this.peerId = peerId;
|
|
@@ -4118,31 +4134,85 @@ var ProviderPeerServer = class {
|
|
|
4118
4134
|
async start(serveHttpImpl) {
|
|
4119
4135
|
this.server = await serveHttpImpl(this.fabric, this.peerId, (req) => this.handle(req));
|
|
4120
4136
|
}
|
|
4137
|
+
/** 任一会话持有有效授权(peer 级观测)。 */
|
|
4121
4138
|
authed() {
|
|
4122
|
-
|
|
4139
|
+
for (const sa of this.sessionAuths.values()) if (sa.grants.length > 0) return true;
|
|
4140
|
+
return false;
|
|
4141
|
+
}
|
|
4142
|
+
/** 该会话的授权态(无 → undefined:未 AUTH/已失效/被逐出)。访问即提升
|
|
4143
|
+
* LRU 位次(Map 重插——访问序逐出,非仅插入序)。 */
|
|
4144
|
+
sessionAuthOf(sessionId) {
|
|
4145
|
+
const sa = this.sessionAuths.get(sessionId);
|
|
4146
|
+
if (sa !== void 0) {
|
|
4147
|
+
this.sessionAuths.delete(sessionId);
|
|
4148
|
+
this.sessionAuths.set(sessionId, sa);
|
|
4149
|
+
}
|
|
4150
|
+
return sa;
|
|
4151
|
+
}
|
|
4152
|
+
/** 会话 id 快照(engine.refreshSession 迭代用)。 */
|
|
4153
|
+
sessionAuthIds() {
|
|
4154
|
+
return [...this.sessionAuths.keys()];
|
|
4155
|
+
}
|
|
4156
|
+
/** 摘除会话授权态(refresh 裁剪路径——全钥失效的会话;watch 随之唤醒 401)。 */
|
|
4157
|
+
dropSessionAuth(sessionId) {
|
|
4158
|
+
this.sessionAuths.delete(sessionId);
|
|
4159
|
+
this.invalidateWatch(sessionId);
|
|
4160
|
+
}
|
|
4161
|
+
/** 写入该会话授权态(LRU 容量逐出最旧)。 */
|
|
4162
|
+
putSessionAuth(sessionId, keyring, grants) {
|
|
4163
|
+
this.sessionAuths.delete(sessionId);
|
|
4164
|
+
this.sessionAuths.set(sessionId, {
|
|
4165
|
+
keyring,
|
|
4166
|
+
grants,
|
|
4167
|
+
lastView: ""
|
|
4168
|
+
});
|
|
4169
|
+
while (this.sessionAuths.size > SESSION_AUTH_CAP) {
|
|
4170
|
+
const oldest = this.sessionAuths.keys().next().value;
|
|
4171
|
+
if (oldest === void 0) break;
|
|
4172
|
+
this.sessionAuths.delete(oldest);
|
|
4173
|
+
this.invalidateWatch(oldest);
|
|
4174
|
+
}
|
|
4123
4175
|
}
|
|
4124
4176
|
get keys() {
|
|
4125
|
-
|
|
4177
|
+
const out = /* @__PURE__ */ new Set();
|
|
4178
|
+
for (const sa of this.sessionAuths.values()) for (const k of sa.keyring) out.add(k);
|
|
4179
|
+
return [...out];
|
|
4126
4180
|
}
|
|
4127
4181
|
get keyIds() {
|
|
4128
|
-
|
|
4182
|
+
const out = /* @__PURE__ */ new Set();
|
|
4183
|
+
for (const sa of this.sessionAuths.values()) for (const g of sa.grants) out.add(g.keyId);
|
|
4184
|
+
return out;
|
|
4129
4185
|
}
|
|
4130
4186
|
/** 撤键/目录变更后的刷新:唤醒 watch 长轮询(全量视图;refresh 语义不变)。 */
|
|
4131
4187
|
async pushRefresh(header) {
|
|
4132
|
-
this.
|
|
4133
|
-
this.notifyWatch(header);
|
|
4188
|
+
this.notifyWatchAll(header);
|
|
4134
4189
|
}
|
|
4135
4190
|
disconnect(reason) {
|
|
4136
4191
|
this.disposeLocal(reason);
|
|
4137
4192
|
}
|
|
4138
|
-
/**
|
|
4139
|
-
|
|
4140
|
-
|
|
4193
|
+
/** 会话授权失效(drop/无效 AUTH/逐出):唤醒该会话全部 watch 等待者以
|
|
4194
|
+
* 401 收敛(5.2-P2——此前删集合不唤醒,在途长轮询干等到 20s 超时)。 */
|
|
4195
|
+
invalidateWatch(sessionId) {
|
|
4196
|
+
const waiters = this.watchWaiters.get(sessionId);
|
|
4197
|
+
if (waiters === void 0) return;
|
|
4198
|
+
for (const wake of [...waiters]) wake("invalid");
|
|
4199
|
+
this.watchWaiters.delete(sessionId);
|
|
4200
|
+
}
|
|
4201
|
+
/** 唤醒指定会话的 watch 等待者(该会话自己的刷新视图)。 */
|
|
4202
|
+
notifyWatch(sessionId, view) {
|
|
4203
|
+
const waiters = this.watchWaiters.get(sessionId);
|
|
4204
|
+
if (waiters === void 0) return;
|
|
4205
|
+
for (const wake of [...waiters]) wake(view);
|
|
4206
|
+
this.watchWaiters.delete(sessionId);
|
|
4207
|
+
}
|
|
4208
|
+
/** 唤醒全部 watch 等待者(键重加等 peer 级推送:并集视图,各会话自行重同步)。 */
|
|
4209
|
+
notifyWatchAll(view) {
|
|
4210
|
+
for (const waiters of [...this.watchWaiters.values()]) for (const wake of [...waiters]) wake(view);
|
|
4141
4211
|
this.watchWaiters.clear();
|
|
4142
4212
|
}
|
|
4143
4213
|
/** 授权失效:唤醒等待者以 401 收敛(watch 循环退出,由 active 转换重启)。 */
|
|
4144
4214
|
notifyWatchInvalidated() {
|
|
4145
|
-
for (const
|
|
4215
|
+
for (const waiters of [...this.watchWaiters.values()]) for (const wake of [...waiters]) wake("invalid");
|
|
4146
4216
|
this.watchWaiters.clear();
|
|
4147
4217
|
}
|
|
4148
4218
|
/** 本地善后(撤钥/引擎关闭):中止全部在途、释放限额、停 HTTP 引擎、断 fabric 会话。 */
|
|
@@ -4163,6 +4233,8 @@ var ProviderPeerServer = class {
|
|
|
4163
4233
|
let result;
|
|
4164
4234
|
if (barePath === "/_aifly/auth" && req.method === "POST") result = await this.handleAuth(req);
|
|
4165
4235
|
else if (barePath === "/_aifly/catalog-watch" && req.method === "GET") result = await this.handleWatch(req);
|
|
4236
|
+
else if (barePath === "/_aifly/auth" || barePath === "/_aifly/catalog-watch") result = errorCarrierResponse(ERROR_CODE.forbidden_method, `method not allowed on control endpoint: ${req.method} ${barePath}`);
|
|
4237
|
+
else if (barePath.startsWith("/_aifly/")) result = errorCarrierResponse(ERROR_CODE.unauthorized, "unauthorized");
|
|
4166
4238
|
else result = await this.handleForward(req);
|
|
4167
4239
|
return result;
|
|
4168
4240
|
}
|
|
@@ -4177,7 +4249,7 @@ var ProviderPeerServer = class {
|
|
|
4177
4249
|
});
|
|
4178
4250
|
return out;
|
|
4179
4251
|
}
|
|
4180
|
-
/** AUTH(重复 AUTH 以最后一次为准:钥环变更 =
|
|
4252
|
+
/** AUTH(重复 AUTH 以最后一次为准:钥环变更 = 重授权;授权按 session_id 隔离)。 */
|
|
4181
4253
|
async handleAuth(req) {
|
|
4182
4254
|
let parsedBody;
|
|
4183
4255
|
try {
|
|
@@ -4190,18 +4262,16 @@ var ProviderPeerServer = class {
|
|
|
4190
4262
|
if (!parsed.success) return errorCarrierResponse(ERROR_CODE.protocol_error, "auth body schema rejected");
|
|
4191
4263
|
const decision = handleAuthFrame(parsed.data, this.engine.directory());
|
|
4192
4264
|
if (decision.kind === "err") {
|
|
4193
|
-
this.
|
|
4194
|
-
this.
|
|
4195
|
-
this.engine.keySessionIndex().untrack(this);
|
|
4265
|
+
this.sessionAuths.delete(req.sessionId);
|
|
4266
|
+
this.invalidateWatch(req.sessionId);
|
|
4267
|
+
if (!this.authed()) this.engine.keySessionIndex().untrack(this);
|
|
4196
4268
|
return {
|
|
4197
4269
|
status: 403,
|
|
4198
4270
|
headers: this.jsonHeaders(),
|
|
4199
4271
|
bodyChunks: [Buffer.from(JSON.stringify(decision.header))]
|
|
4200
4272
|
};
|
|
4201
4273
|
}
|
|
4202
|
-
this.
|
|
4203
|
-
this.grants = decision.valid;
|
|
4204
|
-
this.lastView = JSON.stringify(decision.header);
|
|
4274
|
+
this.putSessionAuth(req.sessionId, decision.keys, decision.valid);
|
|
4205
4275
|
this.engine.keySessionIndex().track(this);
|
|
4206
4276
|
return {
|
|
4207
4277
|
status: 200,
|
|
@@ -4211,17 +4281,18 @@ var ProviderPeerServer = class {
|
|
|
4211
4281
|
}
|
|
4212
4282
|
/** 目录刷新长轮询:since 落后即时回;未变更挂起至视图变更(200)或超时(204)。 */
|
|
4213
4283
|
async handleWatch(req) {
|
|
4214
|
-
|
|
4284
|
+
const sa = this.sessionAuthOf(req.sessionId);
|
|
4285
|
+
if (sa === void 0 || sa.grants.length === 0) return errorCarrierResponse(ERROR_CODE.unauthorized, "session is not authenticated");
|
|
4215
4286
|
const query = req.path.split("?", 2)[1] ?? "";
|
|
4216
4287
|
const sinceRaw = Number(new URLSearchParams(query).get("since") ?? "0");
|
|
4217
4288
|
const since = Number.isFinite(sinceRaw) && sinceRaw >= 0 ? Math.floor(sinceRaw) : 0;
|
|
4218
4289
|
const current = this.engine.currentCatalogSeq();
|
|
4219
4290
|
if (since < current) {
|
|
4220
4291
|
const dir = this.engine.directory();
|
|
4221
|
-
const { valid } = evaluateKeyring(
|
|
4292
|
+
const { valid } = evaluateKeyring(sa.keyring, dir);
|
|
4222
4293
|
if (valid.length === 0) return errorCarrierResponse(ERROR_CODE.unauthorized, "no valid key remains");
|
|
4223
4294
|
const header = buildAuthOk(valid, dir, { refresh: true });
|
|
4224
|
-
|
|
4295
|
+
sa.grants = valid;
|
|
4225
4296
|
return {
|
|
4226
4297
|
status: 200,
|
|
4227
4298
|
headers: this.jsonHeaders({ "x-aifly-catalog-seq": String(current) }),
|
|
@@ -4230,16 +4301,27 @@ var ProviderPeerServer = class {
|
|
|
4230
4301
|
}
|
|
4231
4302
|
const changed = await new Promise((resolve) => {
|
|
4232
4303
|
const timer = setTimeout(() => {
|
|
4233
|
-
this.watchWaiters.
|
|
4304
|
+
let waiters = this.watchWaiters.get(req.sessionId);
|
|
4305
|
+
waiters?.delete(wake);
|
|
4306
|
+
if (waiters !== void 0 && waiters.size === 0) this.watchWaiters.delete(req.sessionId);
|
|
4234
4307
|
resolve(null);
|
|
4235
4308
|
}, CATALOG_WATCH_TIMEOUT_MS);
|
|
4236
4309
|
const wake = (view) => {
|
|
4237
4310
|
clearTimeout(timer);
|
|
4238
4311
|
resolve(view);
|
|
4239
4312
|
};
|
|
4240
|
-
this.watchWaiters.
|
|
4313
|
+
let waiters = this.watchWaiters.get(req.sessionId);
|
|
4314
|
+
if (waiters === void 0) {
|
|
4315
|
+
waiters = /* @__PURE__ */ new Set();
|
|
4316
|
+
this.watchWaiters.set(req.sessionId, waiters);
|
|
4317
|
+
}
|
|
4318
|
+
waiters.add(wake);
|
|
4241
4319
|
});
|
|
4242
4320
|
if (changed === "invalid") return errorCarrierResponse(ERROR_CODE.unauthorized, "authorization invalidated");
|
|
4321
|
+
if (changed !== null) {
|
|
4322
|
+
const saAfter = this.sessionAuthOf(req.sessionId);
|
|
4323
|
+
if (saAfter === void 0 || saAfter.grants.length === 0) return errorCarrierResponse(ERROR_CODE.unauthorized, "session is not authenticated");
|
|
4324
|
+
}
|
|
4243
4325
|
if (changed === null) return {
|
|
4244
4326
|
status: 204,
|
|
4245
4327
|
headers: [{
|
|
@@ -4254,13 +4336,15 @@ var ProviderPeerServer = class {
|
|
|
4254
4336
|
};
|
|
4255
4337
|
}
|
|
4256
4338
|
async handleForward(req) {
|
|
4257
|
-
|
|
4339
|
+
const sa = this.sessionAuthOf(req.sessionId);
|
|
4340
|
+
if (sa === void 0 || sa.grants.length === 0) return errorCarrierResponse(ERROR_CODE.unauthorized, "session is not authenticated");
|
|
4341
|
+
if (req.signal.aborted) return errorCarrierResponse(ERROR_CODE.aborted, "request cancelled before dispatch");
|
|
4258
4342
|
const serviceHeader = req.headers.find((h) => h.name === AIFLY_SERVICE_HEADER)?.value;
|
|
4259
4343
|
if (serviceHeader === void 0) return errorCarrierResponse(ERROR_CODE.protocol_error, `missing ${AIFLY_SERVICE_HEADER} header`);
|
|
4260
4344
|
const method = req.method.toUpperCase();
|
|
4261
4345
|
if (!HTTP_METHODS.includes(method)) return errorCarrierResponse(ERROR_CODE.forbidden_method, `http method not allowed: ${method}`);
|
|
4262
4346
|
const service = this.engine.store.getService(serviceHeader);
|
|
4263
|
-
const grant = service === void 0 ? void 0 :
|
|
4347
|
+
const grant = service === void 0 ? void 0 : sa.grants.find((g) => this.engine.groupHasService(g.group, serviceHeader));
|
|
4264
4348
|
if (service === void 0 || service.enabled === false || grant === void 0) return errorCarrierResponse(ERROR_CODE.unknown_service, "service not available");
|
|
4265
4349
|
const acquired = this.engine.limits.acquire(grant.keyId, grant.group);
|
|
4266
4350
|
if (!acquired.ok) return errorCarrierResponse(acquired.code, acquired.code === "rate_limited" ? "group concurrency limit reached" : "daily request quota reached");
|
|
@@ -4299,6 +4383,11 @@ var ProviderPeerServer = class {
|
|
|
4299
4383
|
};
|
|
4300
4384
|
const outcome = new CarrierOutcome((status, headers) => req.respondStreaming(status, headers));
|
|
4301
4385
|
const ctrl = new AbortController();
|
|
4386
|
+
const abortUpstream = () => {
|
|
4387
|
+
ctrl.abort(new UpstreamAbortError(ERROR_CODE.aborted, true));
|
|
4388
|
+
};
|
|
4389
|
+
req.signal.addEventListener("abort", abortUpstream, { once: true });
|
|
4390
|
+
if (req.signal.aborted) abortUpstream();
|
|
4302
4391
|
const act = {
|
|
4303
4392
|
ctrl,
|
|
4304
4393
|
keyId: grant.keyId,
|
|
@@ -5144,6 +5233,10 @@ function removeKeyring(root, ref) {
|
|
|
5144
5233
|
}
|
|
5145
5234
|
};
|
|
5146
5235
|
}
|
|
5236
|
+
//#endregion
|
|
5237
|
+
//#region src/consumer/providers.ts
|
|
5238
|
+
/** fetchHttp head 等待期取消键序号(per 进程单调;abortKey 注册表关联)。 */
|
|
5239
|
+
let fetchAbortKeySeq = 0;
|
|
5147
5240
|
/**
|
|
5148
5241
|
* full jitter:delay = random() * min(cap, base * 2^attempt)。
|
|
5149
5242
|
* attempt 从 0 起(首次重连窗口 [0, base]);封顶后窗口恒为 [0, cap]。
|
|
@@ -5314,6 +5407,7 @@ var ProviderConnection = class {
|
|
|
5314
5407
|
});
|
|
5315
5408
|
let aborted = false;
|
|
5316
5409
|
let tunnel = null;
|
|
5410
|
+
const abortKey = ++fetchAbortKeySeq;
|
|
5317
5411
|
(async () => {
|
|
5318
5412
|
try {
|
|
5319
5413
|
const resp = await session.fetchHttp({
|
|
@@ -5321,7 +5415,8 @@ var ProviderConnection = class {
|
|
|
5321
5415
|
path: input.path,
|
|
5322
5416
|
headers,
|
|
5323
5417
|
...input.body.length > 0 ? { body: [input.body] } : {},
|
|
5324
|
-
...input.upgrade ? { keepOpen: true } : {}
|
|
5418
|
+
...input.upgrade ? { keepOpen: true } : {},
|
|
5419
|
+
abortKey
|
|
5325
5420
|
});
|
|
5326
5421
|
if (aborted) {
|
|
5327
5422
|
try {
|
|
@@ -5398,6 +5493,9 @@ var ProviderConnection = class {
|
|
|
5398
5493
|
id,
|
|
5399
5494
|
abort: () => {
|
|
5400
5495
|
aborted = true;
|
|
5496
|
+
try {
|
|
5497
|
+
session.abortFetch?.(abortKey);
|
|
5498
|
+
} catch {}
|
|
5401
5499
|
try {
|
|
5402
5500
|
tunnel?.abort?.();
|
|
5403
5501
|
} catch {}
|
|
@@ -99,7 +99,7 @@ async function start(startArgv, home) {
|
|
|
99
99
|
process.on("exit", () => {
|
|
100
100
|
clearPid(home);
|
|
101
101
|
});
|
|
102
|
-
const { run: serveRun } = await import("./serve-
|
|
102
|
+
const { run: serveRun } = await import("./serve-DJ8id2Qo.mjs");
|
|
103
103
|
return serveRun([...startArgv], { homedir: home });
|
|
104
104
|
}
|
|
105
105
|
/** 后台 pid(活性核验;陈旧 pidfile 就地清理返回 null)。 */
|
|
@@ -3,7 +3,7 @@ import { n as parseArgv } from "./args-CSxXW9eD.mjs";
|
|
|
3
3
|
import { n as consumersRoot, s as listKeyrings } from "./store-Bu4I1rNj.mjs";
|
|
4
4
|
import { a as ringsForRun, i as printListeners, n as ctxHomedir, o as waitForSignals, r as ctxOut, t as createSdkFabricFactory } from "./common-9KRvrs5f.mjs";
|
|
5
5
|
import { a as importLink, i as formatLinkPreview, n as decodeShareLink, r as formatKeyringSummary } from "./join-D5QUz8mh.mjs";
|
|
6
|
-
import { n as createFabricSessionFactory, t as startEngine } from "./runtime-
|
|
6
|
+
import { n as createFabricSessionFactory, t as startEngine } from "./runtime-DKmrkfNT.mjs";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
//#region src/cli/commands/consumer/import.ts
|
|
9
9
|
async function run(argv, ctx = {}) {
|
|
@@ -2,7 +2,7 @@ import { n as UsageError, r as reportCliError } from "./errors-BGtxPkM7.mjs";
|
|
|
2
2
|
import { n as parseArgv } from "./args-CSxXW9eD.mjs";
|
|
3
3
|
import { t as resolveHttpProxy } from "./proxy-D7VMUVEg.mjs";
|
|
4
4
|
import { d as resolvedRelayUrls, f as str, u as resolveDataDir } from "./common-DqSyhR6F.mjs";
|
|
5
|
-
import { t as openExistingFabric } from "./serve-
|
|
5
|
+
import { t as openExistingFabric } from "./serve-BB6-4Ixd.mjs";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
//#region src/cli/commands/provider/revoke.ts
|
|
8
8
|
const SPEC = {
|
|
@@ -2,7 +2,7 @@ import { n as UsageError, r as reportCliError, t as CliError } from "./errors-BG
|
|
|
2
2
|
import { n as parseArgv } from "./args-CSxXW9eD.mjs";
|
|
3
3
|
import { c as loadKeyring, n as consumersRoot, o as keyringDir, s as listKeyrings } from "./store-Bu4I1rNj.mjs";
|
|
4
4
|
import { a as ringsForRun, i as printListeners, n as ctxHomedir, o as waitForSignals, r as ctxOut, t as createSdkFabricFactory } from "./common-9KRvrs5f.mjs";
|
|
5
|
-
import { n as createFabricSessionFactory, t as startEngine } from "./runtime-
|
|
5
|
+
import { n as createFabricSessionFactory, t as startEngine } from "./runtime-DKmrkfNT.mjs";
|
|
6
6
|
import { a as killHard, c as terminateGracefully, i as isAlive, l as writeLastStart, n as daemonDir, o as readLastStart, r as ensureDaemonDir, s as readPid, t as clearPid, u as writePid } from "./daemon-state-CYkiW3SA.mjs";
|
|
7
7
|
import { openSync, readFileSync, watch } from "node:fs";
|
|
8
8
|
import { resolve } from "node:path";
|
|
@@ -5,6 +5,9 @@ import { n as desiredPortFor, r as listenWithFallback, t as LOOPBACK_HOST } from
|
|
|
5
5
|
import { Hono } from "hono";
|
|
6
6
|
import { createAdaptorServer } from "@hono/node-server";
|
|
7
7
|
import { WebSocketServer } from "ws";
|
|
8
|
+
//#region src/consumer/providers.ts
|
|
9
|
+
/** fetchHttp head 等待期取消键序号(per 进程单调;abortKey 注册表关联)。 */
|
|
10
|
+
let fetchAbortKeySeq = 0;
|
|
8
11
|
/**
|
|
9
12
|
* full jitter:delay = random() * min(cap, base * 2^attempt)。
|
|
10
13
|
* attempt 从 0 起(首次重连窗口 [0, base]);封顶后窗口恒为 [0, cap]。
|
|
@@ -175,6 +178,7 @@ var ProviderConnection = class {
|
|
|
175
178
|
});
|
|
176
179
|
let aborted = false;
|
|
177
180
|
let tunnel = null;
|
|
181
|
+
const abortKey = ++fetchAbortKeySeq;
|
|
178
182
|
(async () => {
|
|
179
183
|
try {
|
|
180
184
|
const resp = await session.fetchHttp({
|
|
@@ -182,7 +186,8 @@ var ProviderConnection = class {
|
|
|
182
186
|
path: input.path,
|
|
183
187
|
headers,
|
|
184
188
|
...input.body.length > 0 ? { body: [input.body] } : {},
|
|
185
|
-
...input.upgrade ? { keepOpen: true } : {}
|
|
189
|
+
...input.upgrade ? { keepOpen: true } : {},
|
|
190
|
+
abortKey
|
|
186
191
|
});
|
|
187
192
|
if (aborted) {
|
|
188
193
|
try {
|
|
@@ -259,6 +264,9 @@ var ProviderConnection = class {
|
|
|
259
264
|
id,
|
|
260
265
|
abort: () => {
|
|
261
266
|
aborted = true;
|
|
267
|
+
try {
|
|
268
|
+
session.abortFetch?.(abortKey);
|
|
269
|
+
} catch {}
|
|
262
270
|
try {
|
|
263
271
|
tunnel?.abort?.();
|
|
264
272
|
} catch {}
|