ai-fly 0.1.1 → 0.3.0-alpha.2
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 +48 -22
- package/dist/args-CSxXW9eD.mjs +69 -0
- package/dist/common-CgxCXeTt.mjs +83 -0
- package/dist/common-CqxqIg6D.mjs +86 -0
- package/dist/config-BlZnFqx7.mjs +59 -0
- package/dist/daemon-DklsEniO.mjs +291 -0
- package/dist/{forget-BEbNwPip.mjs → forget-B17zGwj4.mjs} +3 -2
- package/dist/{sdk-C6xDRbzg.mjs → frames-BDqvhB2Y.mjs} +1 -10
- package/dist/{group-Csy9VmFb.mjs → group-B96IKEDF.mjs} +24 -4
- package/dist/{import-BVAFw5Tx.mjs → import-tilxdbTH.mjs} +8 -6
- package/dist/{join-E1_-Rt-7.mjs → join-DsKUAibH.mjs} +7 -5
- package/dist/{join-B9op0juF.mjs → join-hyqiwEek.mjs} +3 -3
- package/dist/{key-CK77IYXp.mjs → key-BiEp995L.mjs} +2 -2
- package/dist/{key-Bt4EWiuO.mjs → key-CNufbB__.mjs} +4 -3
- package/dist/local-test-C3HGjGY3.mjs +101 -0
- package/dist/models-dev-2UfQoIpQ.mjs +244 -0
- package/dist/{mux-xHl9Jrbi.mjs → mux-CS1zhF4W.mjs} +2 -2
- package/dist/{ports-BQukx7wS.mjs → ports-rRppuBgo.mjs} +3 -2
- package/dist/presets-C73CAMZ6.mjs +55 -0
- package/dist/proxy-D7VMUVEg.mjs +21 -0
- package/dist/{revoke-CyrOzc8t.mjs → revoke-CkpvK33Q.mjs} +5 -4
- package/dist/rewrite-xZHWnyJk.mjs +313 -0
- package/dist/route-test-Oew0CO1U.mjs +60 -0
- package/dist/rpc-contract-5qyWzT5t.mjs +638 -0
- package/dist/{run-Be8H-IRf.mjs → run-DbPXAm6h.mjs} +8 -5
- package/dist/{runtime-Dg4en1D8.mjs → runtime-D_nxUEjd.mjs} +3 -3
- package/dist/secret-SA37VND5.mjs +95 -0
- package/dist/secrets-BYI6ooTJ.mjs +113 -0
- package/dist/secrets-u19-8EyW.mjs +2 -0
- package/dist/{serve-DuyQMeJh.mjs → serve-Be4zdKVj.mjs} +14 -425
- package/dist/{serve-R03mDBwd.mjs → serve-DSBlodTF.mjs} +18 -6
- package/dist/service-B5uORhl3.mjs +232 -0
- package/dist/settings-C1Hb4zwd.mjs +34 -0
- package/dist/settings-DU0YqNDt.mjs +95 -0
- package/dist/{share-DGM2TOft.mjs → share-Cug0944N.mjs} +8 -5
- package/dist/{status-DRaKNvqz.mjs → status-B-j454u2.mjs} +7 -5
- package/dist/{status-W2OS7WB6.mjs → status-SkNsxGnk.mjs} +6 -4
- package/dist/{common-4VeA_3wo.mjs → store-BiTS2FTl.mjs} +3 -73
- package/dist/store-CrTUwskE.mjs +629 -0
- package/dist/test-Bc0OKBAi.mjs +64 -0
- package/package.json +1 -1
- package/dist/common-BULbPtYn.mjs +0 -1295
- package/dist/config-CO3WyW2H.mjs +0 -173
- package/dist/service-Cd4R47n4.mjs +0 -94
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { n as UsageError, r as reportCliError, t as CliError } from "./errors-BGtxPkM7.mjs";
|
|
2
|
+
import { n as parseArgv } from "./args-CSxXW9eD.mjs";
|
|
3
|
+
import { r as settingsPath, t as loadSettings } from "./settings-C1Hb4zwd.mjs";
|
|
4
|
+
import { r as openStore } from "./common-CqxqIg6D.mjs";
|
|
5
|
+
import { existsSync, mkdirSync, openSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
8
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
9
|
+
import process from "node:process";
|
|
10
|
+
//#region src/cli/daemon-state.ts
|
|
11
|
+
function daemonDir(base = homedir()) {
|
|
12
|
+
const root = join(base, ".aifly", "daemon");
|
|
13
|
+
return {
|
|
14
|
+
root,
|
|
15
|
+
pidFile: join(root, "pid"),
|
|
16
|
+
lastStartFile: join(root, "last-start.json"),
|
|
17
|
+
logFile: join(root, "daemon.log")
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function ensureDaemonDir(base = homedir()) {
|
|
21
|
+
const dir = daemonDir(base);
|
|
22
|
+
mkdirSync(dir.root, { recursive: true });
|
|
23
|
+
return dir;
|
|
24
|
+
}
|
|
25
|
+
function writePid(pid, base = homedir()) {
|
|
26
|
+
const dir = ensureDaemonDir(base);
|
|
27
|
+
writeFileSync(dir.pidFile, `${pid}\n`);
|
|
28
|
+
}
|
|
29
|
+
function readPid(base = homedir()) {
|
|
30
|
+
try {
|
|
31
|
+
const raw = readFileSync(daemonDir(base).pidFile, "utf8").trim();
|
|
32
|
+
const pid = Number.parseInt(raw, 10);
|
|
33
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function clearPid(base = homedir()) {
|
|
39
|
+
const { pidFile } = daemonDir(base);
|
|
40
|
+
if (existsSync(pidFile)) writeFileSync(pidFile, "");
|
|
41
|
+
}
|
|
42
|
+
function writeLastStart(last, base = homedir()) {
|
|
43
|
+
const dir = ensureDaemonDir(base);
|
|
44
|
+
writeFileSync(dir.lastStartFile, `${JSON.stringify(last, null, 2)}\n`);
|
|
45
|
+
}
|
|
46
|
+
function readLastStart(base = homedir()) {
|
|
47
|
+
try {
|
|
48
|
+
const raw = JSON.parse(readFileSync(daemonDir(base).lastStartFile, "utf8"));
|
|
49
|
+
if (typeof raw.entry === "string" && Array.isArray(raw.args)) return {
|
|
50
|
+
entry: raw.entry,
|
|
51
|
+
args: raw.args,
|
|
52
|
+
startedAt: Number(raw.startedAt) || Date.now()
|
|
53
|
+
};
|
|
54
|
+
return null;
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** 进程活性(ESRCH = 不存在)。 */
|
|
60
|
+
function isAlive(pid) {
|
|
61
|
+
try {
|
|
62
|
+
process.kill(pid, 0);
|
|
63
|
+
return true;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return error.code === "EPERM";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** 优雅终止:SIGTERM → 轮询 ≤timeoutMs → 仍未退返回 false(调用方决定硬杀)。 */
|
|
69
|
+
async function terminateGracefully(pid, timeoutMs = 1e4) {
|
|
70
|
+
process.kill(pid, "SIGTERM");
|
|
71
|
+
const deadline = Date.now() + timeoutMs;
|
|
72
|
+
while (Date.now() < deadline) {
|
|
73
|
+
if (!isAlive(pid)) return true;
|
|
74
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
/** 硬杀:posix SIGKILL;win32 taskkill(无优雅信号语义,USAGE 明示)。 */
|
|
79
|
+
function killHard(pid) {
|
|
80
|
+
if (process.platform === "win32") execFileSync("taskkill", [
|
|
81
|
+
"/PID",
|
|
82
|
+
String(pid),
|
|
83
|
+
"/F",
|
|
84
|
+
"/T"
|
|
85
|
+
], { stdio: "ignore" });
|
|
86
|
+
else process.kill(pid, "SIGKILL");
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/cli/commands/provider/daemon.ts
|
|
90
|
+
const SPEC = {
|
|
91
|
+
detach: { type: "boolean" },
|
|
92
|
+
force: { type: "boolean" },
|
|
93
|
+
data: {
|
|
94
|
+
type: "string",
|
|
95
|
+
tilde: true
|
|
96
|
+
},
|
|
97
|
+
relay: { type: "multi" },
|
|
98
|
+
alias: { type: "string" },
|
|
99
|
+
"log-usage": { type: "boolean" },
|
|
100
|
+
proxy: { type: "string" },
|
|
101
|
+
lines: { type: "string" }
|
|
102
|
+
};
|
|
103
|
+
const USAGE = `usage:
|
|
104
|
+
ai-fly daemon start [--detach] [--data <dir>] [--relay <url>]... [--proxy <url|env|none>]
|
|
105
|
+
ai-fly daemon stop [--force]
|
|
106
|
+
ai-fly daemon info
|
|
107
|
+
ai-fly daemon restart [--detach] [--data <dir>] [--relay <url>]... [--proxy <url|env|none>]
|
|
108
|
+
ai-fly daemon log [--lines <n>]
|
|
109
|
+
|
|
110
|
+
--proxy: relay 控制面 HTTP 代理(QUIC 数据面永不过代理);等价 env AIFLY_PROXY。
|
|
111
|
+
'env' = 读进程环境变量(HTTP_PROXY/HTTPS_PROXY);'none' = 显式禁用。`;
|
|
112
|
+
async function run(argv, ctx = {}) {
|
|
113
|
+
const home = ctx.homedir ?? homedir();
|
|
114
|
+
const sub = argv[0];
|
|
115
|
+
const rest = argv.slice(1);
|
|
116
|
+
try {
|
|
117
|
+
switch (sub) {
|
|
118
|
+
case void 0:
|
|
119
|
+
case "start": return await start(rest, home);
|
|
120
|
+
case "stop": return await stop(rest, home);
|
|
121
|
+
case "info": return info(rest, home);
|
|
122
|
+
case "restart": return await restart(rest, home);
|
|
123
|
+
case "log": return log(rest, home);
|
|
124
|
+
default: throw new UsageError(`error: unknown daemon subcommand '${sub}'\n${USAGE}`);
|
|
125
|
+
}
|
|
126
|
+
} catch (err) {
|
|
127
|
+
return reportCliError(err);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** --detach 复活参数:本次 argv 原样(含 start 子命令语义由调用方拼)。 */
|
|
131
|
+
function lastStartOf(startArgv) {
|
|
132
|
+
const entry = process.argv[1] === void 0 ? void 0 : resolve(process.argv[1]);
|
|
133
|
+
if (entry === void 0) throw new CliError("error: cannot resolve the CLI entry for daemon respawn");
|
|
134
|
+
return {
|
|
135
|
+
entry,
|
|
136
|
+
args: ["start", ...startArgv],
|
|
137
|
+
startedAt: Date.now()
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async function start(startArgv, home) {
|
|
141
|
+
const { options, positionals } = parseArgv([...startArgv], SPEC, { homedir: home });
|
|
142
|
+
if (positionals.length > 0) throw new UsageError(`error: unexpected argument '${positionals[0]}'`);
|
|
143
|
+
const running = currentPid(home);
|
|
144
|
+
if (running !== null && running !== process.pid) throw new CliError(`error: daemon already running (pid ${running}); try 'ai-fly daemon restart'`);
|
|
145
|
+
if (options.detach === true) {
|
|
146
|
+
const dir = ensureDaemonDir(home);
|
|
147
|
+
const respawnArgs = [
|
|
148
|
+
"daemon",
|
|
149
|
+
"start",
|
|
150
|
+
...startArgv.filter((a) => a !== "--detach")
|
|
151
|
+
];
|
|
152
|
+
const logFd = openSync(dir.logFile, "a");
|
|
153
|
+
const child = spawn(process.execPath, [
|
|
154
|
+
...process.execArgv,
|
|
155
|
+
process.argv[1],
|
|
156
|
+
...respawnArgs
|
|
157
|
+
], {
|
|
158
|
+
detached: true,
|
|
159
|
+
stdio: [
|
|
160
|
+
"ignore",
|
|
161
|
+
logFd,
|
|
162
|
+
logFd
|
|
163
|
+
],
|
|
164
|
+
env: process.env,
|
|
165
|
+
cwd: process.cwd()
|
|
166
|
+
});
|
|
167
|
+
child.unref();
|
|
168
|
+
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
169
|
+
if (child.exitCode !== null) throw new CliError("error: daemon exited immediately - see ~/.aifly/daemon/daemon.log");
|
|
170
|
+
writePid(child.pid, home);
|
|
171
|
+
writeLastStart(lastStartOf(respawnArgs.slice(2)), home);
|
|
172
|
+
process.stdout.write(`daemon started in background (pid ${child.pid}) - log: ${dir.logFile}\n`);
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
writePid(process.pid, home);
|
|
176
|
+
writeLastStart(lastStartOf(startArgv), home);
|
|
177
|
+
process.on("exit", () => {
|
|
178
|
+
clearPid(home);
|
|
179
|
+
});
|
|
180
|
+
const { run: serveRun } = await import("./serve-DSBlodTF.mjs");
|
|
181
|
+
return serveRun([...startArgv], { homedir: home });
|
|
182
|
+
}
|
|
183
|
+
/** 后台 pid(活性核验;陈旧 pidfile 就地清理返回 null)。 */
|
|
184
|
+
function currentPid(home) {
|
|
185
|
+
const pid = readPid(home);
|
|
186
|
+
if (pid === null) return null;
|
|
187
|
+
if (!isAlive(pid)) {
|
|
188
|
+
clearPid(home);
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
return pid;
|
|
192
|
+
}
|
|
193
|
+
async function stop(stopArgv, home) {
|
|
194
|
+
const { options, positionals } = parseArgv([...stopArgv], SPEC, { homedir: home });
|
|
195
|
+
if (positionals.length > 0) throw new UsageError(`error: unexpected argument '${positionals[0]}'`);
|
|
196
|
+
const pid = currentPid(home);
|
|
197
|
+
if (pid === null) {
|
|
198
|
+
process.stdout.write("daemon is not running\n");
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
201
|
+
if (process.platform === "win32") {
|
|
202
|
+
killHard(pid);
|
|
203
|
+
clearPid(home);
|
|
204
|
+
process.stdout.write(`daemon stopped (pid ${pid}, forced - windows has no cross-process graceful signal)\n`);
|
|
205
|
+
return 0;
|
|
206
|
+
}
|
|
207
|
+
const graceful = await terminateGracefully(pid);
|
|
208
|
+
if (!graceful && options.force !== true) throw new CliError(`error: daemon (pid ${pid}) did not exit in time - retry with --force`);
|
|
209
|
+
if (!graceful) killHard(pid);
|
|
210
|
+
clearPid(home);
|
|
211
|
+
process.stdout.write(`daemon stopped (pid ${pid}${graceful ? "" : ", forced"})\n`);
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
function info(infoArgv, home) {
|
|
215
|
+
const { positionals } = parseArgv([...infoArgv], SPEC, { homedir: home });
|
|
216
|
+
if (positionals.length > 0) throw new UsageError(`error: unexpected argument '${positionals[0]}'`);
|
|
217
|
+
const pid = currentPid(home);
|
|
218
|
+
const last = readLastStart(home);
|
|
219
|
+
const out = (line) => {
|
|
220
|
+
process.stdout.write(`${line}\n`);
|
|
221
|
+
};
|
|
222
|
+
out(pid !== null ? `running : yes (pid ${pid})` : "running : no");
|
|
223
|
+
if (pid !== null && last !== null) {
|
|
224
|
+
const up = Math.max(0, Math.round((Date.now() - last.startedAt) / 1e3));
|
|
225
|
+
out(`uptime : ${Math.floor(up / 60)}m ${up % 60}s`);
|
|
226
|
+
}
|
|
227
|
+
if (last !== null) {
|
|
228
|
+
out(`entry : ${last.entry}`);
|
|
229
|
+
out(`arguments : ${last.args.join(" ")}`);
|
|
230
|
+
}
|
|
231
|
+
out(`state dir : ${daemonDir(home).root}`);
|
|
232
|
+
out(`log file : ${daemonDir(home).logFile}`);
|
|
233
|
+
const dataDir = last?.args.includes("--data") ? resolve(home, last.args[last.args.indexOf("--data") + 1] ?? "") : resolve(home, ".aifly", "provider");
|
|
234
|
+
try {
|
|
235
|
+
const store = openStore(isAbsolute(dataDir) ? dataDir : resolve(process.cwd(), dataDir));
|
|
236
|
+
const services = store.listServices();
|
|
237
|
+
const groups = store.listGroups();
|
|
238
|
+
const keys = store.listKeys();
|
|
239
|
+
const active = keys.filter((k) => k.revokedAt === void 0).length;
|
|
240
|
+
out(`data dir : ${dataDir}`);
|
|
241
|
+
out(`services : ${services.length} (${services.map((s) => s.name).join(", ") || "-"})`);
|
|
242
|
+
out(`groups : ${groups.length} | keys: ${active} active, ${keys.length - active} revoked`);
|
|
243
|
+
} catch {
|
|
244
|
+
out(`data dir : ${dataDir} (store not readable)`);
|
|
245
|
+
}
|
|
246
|
+
out(`settings : ${settingsPath(home)}`);
|
|
247
|
+
const settings = loadSettings(home);
|
|
248
|
+
out(` theme : ${settings.theme} | models.dev: ${settings.modelsDevEnabled ? "on" : "off"}`);
|
|
249
|
+
out(` relay : ${settings.relayUrls === null ? "SDK defaults (n0)" : settings.relayUrls.join(", ")}`);
|
|
250
|
+
return 0;
|
|
251
|
+
}
|
|
252
|
+
async function restart(restartArgv, home) {
|
|
253
|
+
parseArgv([...restartArgv], SPEC, { homedir: home });
|
|
254
|
+
const pid = currentPid(home);
|
|
255
|
+
if (pid !== null) {
|
|
256
|
+
const graceful = process.platform === "win32" ? false : await terminateGracefully(pid);
|
|
257
|
+
if (!graceful) killHard(pid);
|
|
258
|
+
clearPid(home);
|
|
259
|
+
process.stdout.write(`daemon stopped (pid ${pid}${graceful ? "" : ", forced"})\n`);
|
|
260
|
+
} else process.stdout.write("daemon was not running\n");
|
|
261
|
+
if (restartArgv.some((a) => !a.startsWith("--detach"))) return await start(restartArgv, home);
|
|
262
|
+
const last = readLastStart(home);
|
|
263
|
+
const detachWanted = restartArgv.includes("--detach");
|
|
264
|
+
if (last === null) return await start(detachWanted ? ["--detach"] : [], home);
|
|
265
|
+
return await start((detachWanted ? [
|
|
266
|
+
"--detach",
|
|
267
|
+
...last.args.filter((a) => a !== "--detach" && a !== "start"),
|
|
268
|
+
"start"
|
|
269
|
+
] : last.args).filter((a) => a !== "start"), home);
|
|
270
|
+
}
|
|
271
|
+
function log(logArgv, home) {
|
|
272
|
+
const { options, positionals } = parseArgv([...logArgv], SPEC, { homedir: home });
|
|
273
|
+
if (positionals.length > 0) throw new UsageError(`error: unexpected argument '${positionals[0]}'`);
|
|
274
|
+
const lines = Math.max(1, Number.parseInt(String(options.lines ?? "50"), 10) || 50);
|
|
275
|
+
const { logFile } = daemonDir(home);
|
|
276
|
+
try {
|
|
277
|
+
const content = readFileSync(logFile, "utf8").trimEnd();
|
|
278
|
+
if (content === "") {
|
|
279
|
+
process.stdout.write("(daemon.log is empty)\n");
|
|
280
|
+
return 0;
|
|
281
|
+
}
|
|
282
|
+
const tail = content.split("\n").slice(-lines).join("\n");
|
|
283
|
+
process.stdout.write(`${tail}\n`);
|
|
284
|
+
return 0;
|
|
285
|
+
} catch {
|
|
286
|
+
process.stdout.write(`no daemon log yet (${logFile})\n`);
|
|
287
|
+
return 0;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
//#endregion
|
|
291
|
+
export { run };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { n as UsageError } from "./errors-BGtxPkM7.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { n as parseArgv } from "./args-CSxXW9eD.mjs";
|
|
3
|
+
import { d as removeKeyring, n as consumersRoot } from "./store-BiTS2FTl.mjs";
|
|
4
|
+
import { n as ctxHomedir, r as ctxOut } from "./common-CgxCXeTt.mjs";
|
|
4
5
|
import { homedir } from "node:os";
|
|
5
6
|
//#region src/cli/commands/consumer/forget.ts
|
|
6
7
|
async function run(argv, ctx = {}) {
|
|
@@ -382,13 +382,4 @@ function withId(fail, id) {
|
|
|
382
382
|
return fail;
|
|
383
383
|
}
|
|
384
384
|
//#endregion
|
|
385
|
-
|
|
386
|
-
async function loadSdk() {
|
|
387
|
-
const candidate = await import("@jixo/opendweb-client-sdk");
|
|
388
|
-
if (typeof candidate.Fabric === "function") return candidate;
|
|
389
|
-
const def = candidate.default;
|
|
390
|
-
if (def !== void 0 && typeof def.Fabric === "function") return def;
|
|
391
|
-
throw new Error("error: @jixo/opendweb-client-sdk export shape unexpected (no Fabric via named or default interop)");
|
|
392
|
-
}
|
|
393
|
-
//#endregion
|
|
394
|
-
export { FRAME_TYPE as a, REJECTED_CODE as c, classifySchemaFailure as d, isKnownFrameType as f, FRAME_HEADER_SCHEMAS as i, RESP_META_HEADER_WHITELIST as l, ERROR_CODE as n, FRAME_TYPE_NAME as o, FORBIDDEN_REQ_HEADER_NAMES as r, HTTP_METHODS as s, loadSdk as t, SERVICE_ENTRY_SCHEMA as u };
|
|
385
|
+
export { FRAME_TYPE_NAME as a, RESP_META_HEADER_WHITELIST as c, isKnownFrameType as d, FRAME_TYPE as i, SERVICE_ENTRY_SCHEMA as l, FORBIDDEN_REQ_HEADER_NAMES as n, HTTP_METHODS as o, FRAME_HEADER_SCHEMAS as r, REJECTED_CODE as s, ERROR_CODE as t, classifySchemaFailure as u };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as UsageError, r as reportCliError } from "./errors-BGtxPkM7.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { d as str, l as resolveDataDir, n as multi, r as openStore, s as parsePositiveInt } from "./common-
|
|
2
|
+
import { n as parseArgv } from "./args-CSxXW9eD.mjs";
|
|
3
|
+
import { d as str, l as resolveDataDir, n as multi, r as openStore, s as parsePositiveInt } from "./common-CqxqIg6D.mjs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
//#region src/cli/commands/provider/group.ts
|
|
6
6
|
const SPEC = {
|
|
@@ -10,11 +10,13 @@ const SPEC = {
|
|
|
10
10
|
},
|
|
11
11
|
service: { type: "multi" },
|
|
12
12
|
"max-concurrency": { type: "string" },
|
|
13
|
-
"daily-requests": { type: "string" }
|
|
13
|
+
"daily-requests": { type: "string" },
|
|
14
|
+
unlimited: { type: "boolean" }
|
|
14
15
|
};
|
|
15
16
|
const USAGE = `usage:
|
|
16
17
|
ai-fly group add <name> [--service <serviceName>]... [--max-concurrency <n>] [--daily-requests <n>] [--data <dir>]
|
|
17
18
|
ai-fly group set-services <name> [--service <serviceName>]... [--data <dir>]
|
|
19
|
+
ai-fly group set-limits <name> [--max-concurrency <n>] [--daily-requests <n>] [--unlimited] [--data <dir>]
|
|
18
20
|
ai-fly group remove <name> [--data <dir>]
|
|
19
21
|
ai-fly group list [--data <dir>]`;
|
|
20
22
|
async function run(argv, ctx = {}) {
|
|
@@ -24,10 +26,11 @@ async function run(argv, ctx = {}) {
|
|
|
24
26
|
const sub = positionals[0];
|
|
25
27
|
if (sub === "add") return add(options, positionals, home);
|
|
26
28
|
if (sub === "set-services") return setServices(options, positionals, home);
|
|
29
|
+
if (sub === "set-limits") return setLimits(options, positionals, home);
|
|
27
30
|
if (sub === "remove") return remove(options, positionals, home);
|
|
28
31
|
if (sub === "list") return list(options, home);
|
|
29
32
|
if (sub === void 0) throw new UsageError(USAGE);
|
|
30
|
-
throw new UsageError(`error: unknown group subcommand '${sub}' (known: add, set-services, remove, list)`);
|
|
33
|
+
throw new UsageError(`error: unknown group subcommand '${sub}' (known: add, set-services, set-limits, remove, list)`);
|
|
31
34
|
} catch (err) {
|
|
32
35
|
return reportCliError(err);
|
|
33
36
|
}
|
|
@@ -51,6 +54,23 @@ function add(options, positionals, home) {
|
|
|
51
54
|
].join("\n"));
|
|
52
55
|
return 0;
|
|
53
56
|
}
|
|
57
|
+
function setLimits(options, positionals, home) {
|
|
58
|
+
const name = positionals[1];
|
|
59
|
+
if (name === void 0) throw new UsageError("error: group set-limits requires a <name> argument");
|
|
60
|
+
const store = openStore(resolveDataDir(str(options.data), home));
|
|
61
|
+
if (store.listGroups().every((g) => g.name !== name)) throw new UsageError(`error: group '${name}' not found`);
|
|
62
|
+
const maxConcurrency = options["max-concurrency"] === void 0 ? void 0 : parsePositiveInt(str(options["max-concurrency"]), "max-concurrency");
|
|
63
|
+
const dailyRequests = options["daily-requests"] === void 0 ? void 0 : parsePositiveInt(str(options["daily-requests"]), "daily-requests");
|
|
64
|
+
if (options.unlimited === true && (maxConcurrency !== void 0 || dailyRequests !== void 0)) throw new UsageError("error: --unlimited cannot combine with --max-concurrency/--daily-requests");
|
|
65
|
+
const limits = options.unlimited === true || maxConcurrency === void 0 && dailyRequests === void 0 ? void 0 : {
|
|
66
|
+
...maxConcurrency !== void 0 ? { maxConcurrency } : {},
|
|
67
|
+
...dailyRequests !== void 0 ? { dailyRequests } : {}
|
|
68
|
+
};
|
|
69
|
+
const group = store.setGroupLimits(name, limits);
|
|
70
|
+
const summary = group.limits === void 0 ? "unlimited" : `max-concurrency ${group.limits.maxConcurrency ?? "-"}, daily-requests ${group.limits.dailyRequests ?? "-"}`;
|
|
71
|
+
process.stdout.write(`group limits: ${name} -> ${summary}\n`);
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
54
74
|
function setServices(options, positionals, home) {
|
|
55
75
|
const name = positionals[1];
|
|
56
76
|
if (name === void 0) throw new UsageError("error: group set-services requires a <name> argument");
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { n as UsageError } from "./errors-BGtxPkM7.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { a as
|
|
5
|
-
import { n as
|
|
2
|
+
import { n as parseArgv } from "./args-CSxXW9eD.mjs";
|
|
3
|
+
import { n as consumersRoot, s as listKeyrings } from "./store-BiTS2FTl.mjs";
|
|
4
|
+
import { a as ringsForRun, i as printListeners, n as ctxHomedir, o as waitForSignals, r as ctxOut, t as createSdkFabricFactory } from "./common-CgxCXeTt.mjs";
|
|
5
|
+
import { a as importLink, i as formatLinkPreview, n as decodeShareLink, r as formatKeyringSummary } from "./join-hyqiwEek.mjs";
|
|
6
|
+
import { n as createFabricProviderTransport, t as startEngine } from "./runtime-D_nxUEjd.mjs";
|
|
6
7
|
import { homedir } from "node:os";
|
|
7
8
|
//#region src/cli/commands/consumer/import.ts
|
|
8
9
|
async function run(argv, ctx = {}) {
|
|
@@ -14,7 +15,8 @@ async function run(argv, ctx = {}) {
|
|
|
14
15
|
preview: { type: "boolean" },
|
|
15
16
|
run: { type: "boolean" },
|
|
16
17
|
"strict-ports": { type: "boolean" },
|
|
17
|
-
relay: { type: "multi" }
|
|
18
|
+
relay: { type: "multi" },
|
|
19
|
+
proxy: { type: "string" }
|
|
18
20
|
}, { homedir: ctx.homedir ?? homedir() });
|
|
19
21
|
const link = positionals[0];
|
|
20
22
|
if (link === void 0) throw new UsageError("error: import requires a share link argument\nusage: ai-fly import <aifly1-link> [--data <dir>] [--preview] [--run]");
|
|
@@ -30,7 +32,7 @@ async function run(argv, ctx = {}) {
|
|
|
30
32
|
if (flagRelays !== void 0 && flagRelays.length > 0 && linkRelays.length > 0) {
|
|
31
33
|
if (!(flagRelays.length === linkRelays.length && flagRelays.every((u) => linkRelays.includes(u)))) out("warning: --relay overrides the share link's relay entries; if the provider stays unreachable, drop the flag to use the link's entries");
|
|
32
34
|
} else if (linkRelays.length > 0) out(`relay: using ${linkRelays.length} entry URL(s) from the share link (override with --relay)`);
|
|
33
|
-
const factory = await createSdkFabricFactory(flagRelays, ctx, linkRelays);
|
|
35
|
+
const factory = await createSdkFabricFactory(flagRelays, ctx, linkRelays, options.proxy);
|
|
34
36
|
const result = await importLink(link, {
|
|
35
37
|
consumersRoot: root,
|
|
36
38
|
fabric: factory
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { n as UsageError } from "./errors-BGtxPkM7.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { n as parseArgv } from "./args-CSxXW9eD.mjs";
|
|
3
|
+
import { n as consumersRoot } from "./store-BiTS2FTl.mjs";
|
|
4
|
+
import { n as ctxHomedir, r as ctxOut, t as createSdkFabricFactory } from "./common-CgxCXeTt.mjs";
|
|
5
|
+
import { o as joinDevice } from "./join-hyqiwEek.mjs";
|
|
5
6
|
import { homedir } from "node:os";
|
|
6
7
|
//#region src/cli/commands/consumer/join.ts
|
|
7
8
|
async function run(argv, ctx = {}) {
|
|
@@ -10,13 +11,14 @@ async function run(argv, ctx = {}) {
|
|
|
10
11
|
type: "string",
|
|
11
12
|
tilde: true
|
|
12
13
|
},
|
|
13
|
-
relay: { type: "multi" }
|
|
14
|
+
relay: { type: "multi" },
|
|
15
|
+
proxy: { type: "string" }
|
|
14
16
|
}, { homedir: ctx.homedir ?? homedir() });
|
|
15
17
|
const token = positionals[0];
|
|
16
18
|
if (token === void 0) throw new UsageError("error: join requires an invite token argument\nusage: ai-fly join <dweb1-token> [--data <dir>]");
|
|
17
19
|
const out = ctxOut(ctx);
|
|
18
20
|
const root = consumersRoot(options.data, ctxHomedir(ctx));
|
|
19
|
-
const factory = await createSdkFabricFactory(options.relay, ctx);
|
|
21
|
+
const factory = await createSdkFabricFactory(options.relay, ctx, void 0, options.proxy);
|
|
20
22
|
const result = await joinDevice(token, root, { fabric: factory });
|
|
21
23
|
if (result.alreadyJoined) {
|
|
22
24
|
out(`already joined to provider '${result.ring.alias}' (${result.ring.endpointId})`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as CliError } from "./errors-BGtxPkM7.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { o as decodeZ32, s as randomZ32 } from "./rpc-contract-5qyWzT5t.mjs";
|
|
3
|
+
import { l as SERVICE_ENTRY_SCHEMA } from "./frames-BDqvhB2Y.mjs";
|
|
4
|
+
import { a as hasFabricIdentity, c as loadKeyring, f as saveKeyring, i as findKeyringDir, l as mergeImportView, m as upsertKey, o as keyringDir, r as fabricDir } from "./store-BiTS2FTl.mjs";
|
|
5
5
|
import { existsSync, mkdirSync, renameSync, rmSync } from "node:fs";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { z } from "zod";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as UsageError, r as reportCliError } from "./errors-BGtxPkM7.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { c as requireString, d as str, l as resolveDataDir, r as openStore } from "./common-
|
|
2
|
+
import { n as parseArgv } from "./args-CSxXW9eD.mjs";
|
|
3
|
+
import { c as requireString, d as str, l as resolveDataDir, r as openStore } from "./common-CqxqIg6D.mjs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
//#region src/cli/commands/provider/key.ts
|
|
6
6
|
const SPEC = {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { n as UsageError } from "./errors-BGtxPkM7.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { n as parseArgv } from "./args-CSxXW9eD.mjs";
|
|
3
|
+
import { n as consumersRoot } from "./store-BiTS2FTl.mjs";
|
|
4
|
+
import { n as ctxHomedir, r as ctxOut } from "./common-CgxCXeTt.mjs";
|
|
5
|
+
import { r as formatKeyringSummary, t as addKey } from "./join-hyqiwEek.mjs";
|
|
5
6
|
import { homedir } from "node:os";
|
|
6
7
|
//#region src/cli/commands/consumer/key.ts
|
|
7
8
|
async function run(argv, ctx = {}) {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { n as ROUTE_LOCAL_PREFIX } from "./rpc-contract-5qyWzT5t.mjs";
|
|
2
|
+
//#region src/consumer/local-test.ts
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 2e4;
|
|
4
|
+
const BODY_EXCERPT_MAX = 2e3;
|
|
5
|
+
const ERROR_SUMMARY_MAX = 200;
|
|
6
|
+
/** 各标准的探测端点与最小请求形状(M3-r6:localPrefix 已含版本段——openai
|
|
7
|
+
* 家族追加版本后缀;anthropic 剥 localPrefix 尾部版本段后由 client 惯例
|
|
8
|
+
* 补 /v1/messages)。 */
|
|
9
|
+
/**
|
|
10
|
+
* 各标准的探测端点与最小请求形状(M3-r6:localPrefix 已含版本段——openai
|
|
11
|
+
* 家族追加版本后缀;anthropic 剥 localPrefix 尾部版本段后由 client 惯例
|
|
12
|
+
* 补 /v1/messages)。path 形式导出(provider 侧 route-test 复用同一构造)。
|
|
13
|
+
*/
|
|
14
|
+
function formProbe(form, localPrefix, model, content) {
|
|
15
|
+
const headers = { "content-type": "application/json" };
|
|
16
|
+
switch (form) {
|
|
17
|
+
case "openai-chat": return {
|
|
18
|
+
path: `${localPrefix}/chat/completions`,
|
|
19
|
+
headers,
|
|
20
|
+
body: {
|
|
21
|
+
...model !== void 0 ? { model } : {},
|
|
22
|
+
messages: [{
|
|
23
|
+
role: "user",
|
|
24
|
+
content
|
|
25
|
+
}]
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
case "openai-responses": return {
|
|
29
|
+
path: `${localPrefix}/responses`,
|
|
30
|
+
headers,
|
|
31
|
+
body: {
|
|
32
|
+
...model !== void 0 ? { model } : {},
|
|
33
|
+
input: content
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
case "anthropic": return {
|
|
37
|
+
path: `${localPrefix.replace(/\/v\d+$/, "")}/v1/messages`,
|
|
38
|
+
headers: {
|
|
39
|
+
...headers,
|
|
40
|
+
"anthropic-version": "2023-06-01"
|
|
41
|
+
},
|
|
42
|
+
body: {
|
|
43
|
+
...model !== void 0 ? { model } : {},
|
|
44
|
+
max_tokens: 1024,
|
|
45
|
+
messages: [{
|
|
46
|
+
role: "user",
|
|
47
|
+
content
|
|
48
|
+
}]
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function formRequest(port, form, localPrefix, model, content) {
|
|
54
|
+
const probe = formProbe(form, localPrefix, model, content);
|
|
55
|
+
return {
|
|
56
|
+
url: `http://127.0.0.1:${port}${probe.path}`,
|
|
57
|
+
body: probe.body,
|
|
58
|
+
headers: probe.headers
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async function testLocalService(input) {
|
|
62
|
+
const fetchImpl = input.fetchImpl ?? fetch;
|
|
63
|
+
const now = input.now ?? Date.now;
|
|
64
|
+
const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
65
|
+
const started = now();
|
|
66
|
+
const { url, body, headers } = formRequest(input.port, input.form, input.localPrefix ?? ROUTE_LOCAL_PREFIX[input.form], input.model, input.content?.trim() !== "" && input.content !== void 0 ? input.content : "ping");
|
|
67
|
+
const request = {
|
|
68
|
+
method: "POST",
|
|
69
|
+
url,
|
|
70
|
+
...input.model !== void 0 ? { model: input.model } : {}
|
|
71
|
+
};
|
|
72
|
+
try {
|
|
73
|
+
const response = await fetchImpl(url, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
headers,
|
|
76
|
+
body: JSON.stringify(body),
|
|
77
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
78
|
+
});
|
|
79
|
+
const latencyMs = Math.max(0, now() - started);
|
|
80
|
+
const ok = response.status >= 200 && response.status < 300;
|
|
81
|
+
const text = await response.text().catch(() => "");
|
|
82
|
+
const bodyExcerpt = text === "" ? void 0 : text.slice(0, BODY_EXCERPT_MAX);
|
|
83
|
+
return {
|
|
84
|
+
ok,
|
|
85
|
+
latencyMs,
|
|
86
|
+
request,
|
|
87
|
+
httpStatus: response.status,
|
|
88
|
+
...!ok && response.status >= 500 ? { error: `upstream/gateway error (HTTP ${response.status})` } : {},
|
|
89
|
+
...bodyExcerpt !== void 0 ? { bodyExcerpt } : {}
|
|
90
|
+
};
|
|
91
|
+
} catch (error) {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
latencyMs: Math.max(0, now() - started),
|
|
95
|
+
request,
|
|
96
|
+
error: (error instanceof Error ? error.message : String(error)).slice(0, ERROR_SUMMARY_MAX) || "request failed"
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { testLocalService as n, formProbe as t };
|