ai-fly 0.3.2 → 0.4.0-alpha.10

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.
Files changed (72) hide show
  1. package/dist/ai-fly.js +23 -6
  2. package/dist/app/main.js +9047 -0
  3. package/dist/{common-CqxqIg6D.mjs → common-C51SAE5f.mjs} +17 -6
  4. package/dist/{common-CgxCXeTt.mjs → common-CanLn0Qt.mjs} +1 -1
  5. package/dist/{daemon-ZWUSWAgM.mjs → daemon-BWgyJMyB.mjs} +3 -3
  6. package/dist/{forget-BXL2x4Wv.mjs → forget-B3KRkHho.mjs} +2 -2
  7. package/dist/{frames-BDqvhB2Y.mjs → frames-BDz8buix.mjs} +1 -0
  8. package/dist/{group-B96IKEDF.mjs → group-BdnvRD2t.mjs} +1 -1
  9. package/dist/hook-CvAMGts4.mjs +228 -0
  10. package/dist/hooks-DUrXA7PL.mjs +142 -0
  11. package/dist/{import-rpBFh_pa.mjs → import-CTkKGPsa.mjs} +4 -4
  12. package/dist/{join-u1DqTU3U.mjs → join-BRow9_3-.mjs} +3 -3
  13. package/dist/{join-CaFx5yoF.mjs → join-d-pqyf8e.mjs} +3 -3
  14. package/dist/{key-BiEp995L.mjs → key-CTL_AXOh.mjs} +24 -8
  15. package/dist/{key-KcUU3yhJ.mjs → key-D_66hrWW.mjs} +3 -3
  16. package/dist/{local-test-C3HGjGY3.mjs → local-test-BZHGo22F.mjs} +1 -1
  17. package/dist/{models-dev-2UfQoIpQ.mjs → models-dev-CDEyd2k3.mjs} +21 -1
  18. package/dist/{mux-CS1zhF4W.mjs → mux-BaIDugDq.mjs} +2 -2
  19. package/dist/{ports-BRXd5UXh.mjs → ports-TQ4tWmiJ.mjs} +2 -2
  20. package/dist/{presets-C73CAMZ6.mjs → presets-DleOUQiT.mjs} +2 -2
  21. package/dist/{revoke-CkpvK33Q.mjs → revoke-DTFW_EF1.mjs} +2 -2
  22. package/dist/{rewrite-xZHWnyJk.mjs → rewrite-CEzfYH9n.mjs} +45 -26
  23. package/dist/{route-test-Oew0CO1U.mjs → route-test-D2LQyX7c.mjs} +4 -4
  24. package/dist/{rpc-contract-5qyWzT5t.mjs → rpc-contract-cB5SuyQ_.mjs} +64 -9
  25. package/dist/{run-3pdPsD8e.mjs → run-D8kgXdId.mjs} +3 -3
  26. package/dist/{runtime-DNs9GtGp.mjs → runtime-C_pLA5F0.mjs} +3 -3
  27. package/dist/{secret-COdVHzjH.mjs → secret-P2RgHy2h.mjs} +2 -2
  28. package/dist/{secrets-BYI6ooTJ.mjs → secrets-1Hmddmwy.mjs} +2 -2
  29. package/dist/secrets-C9IYp93v.mjs +2 -0
  30. package/dist/{serve-DSBlodTF.mjs → serve-B51lmr9M.mjs} +2 -2
  31. package/dist/{serve-Be4zdKVj.mjs → serve-CY7ZFOFM.mjs} +6 -6
  32. package/dist/{service-B5uORhl3.mjs → service-D6TNolyh.mjs} +36 -9
  33. package/dist/{settings-DU0YqNDt.mjs → settings-Rq1zLoxY.mjs} +1 -1
  34. package/dist/{settings-C1Hb4zwd.mjs → settings-XCvAzDK8.mjs} +2 -2
  35. package/dist/{share-Cug0944N.mjs → share-DdtbAylA.mjs} +28 -13
  36. package/dist/{status-SkNsxGnk.mjs → status-DSVQ3YHm.mjs} +3 -3
  37. package/dist/{status-Bttb9aPj.mjs → status-uE2-kOyK.mjs} +3 -3
  38. package/dist/{store-CQShpYar.mjs → store-DOFDy1rY.mjs} +2 -2
  39. package/dist/{store-CrTUwskE.mjs → store-EGzVd-dn.mjs} +29 -4
  40. package/dist/{test-mlaZC1y5.mjs → test-DW4BF7xJ.mjs} +2 -2
  41. package/hooks/codex.cjs +12 -0
  42. package/hooks/env.cjs +10 -0
  43. package/hooks/file.cjs +27 -0
  44. package/hooks/secret.cjs +11 -0
  45. package/package.json +6 -2
  46. package/resources/app-icons/app-icon.icns +0 -0
  47. package/resources/app-icons/app-icon.ico +0 -0
  48. package/resources/app-icons/app-icon.json +84 -0
  49. package/resources/app-icons/app-icon.png +0 -0
  50. package/resources/app-icons/linux/128x128/app-icon.png +0 -0
  51. package/resources/app-icons/linux/16x16/app-icon.png +0 -0
  52. package/resources/app-icons/linux/256x256/app-icon.png +0 -0
  53. package/resources/app-icons/linux/32x32/app-icon.png +0 -0
  54. package/resources/app-icons/linux/48x48/app-icon.png +0 -0
  55. package/resources/app-icons/linux/512x512/app-icon.png +0 -0
  56. package/resources/app-icons/linux/64x64/app-icon.png +0 -0
  57. package/resources/app-icons/tray-icon.png +0 -0
  58. package/webui/dist/assets/index-8MLhHHM0.js +74 -0
  59. package/webui/dist/assets/index-ihWSPzNq.css +2 -0
  60. package/webui/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  61. package/webui/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  62. package/webui/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  63. package/webui/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  64. package/webui/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  65. package/webui/dist/assets/share-tech-mono-latin-400-normal-15MgdvEM.woff +0 -0
  66. package/webui/dist/assets/share-tech-mono-latin-400-normal-CCqdbj5z.woff2 +0 -0
  67. package/webui/dist/icons/presets/anthropic.svg +5 -0
  68. package/webui/dist/icons/presets/codex.svg +5 -0
  69. package/webui/dist/icons/presets/deepseek.svg +5 -0
  70. package/webui/dist/icons/presets/openai.svg +5 -0
  71. package/webui/dist/index.html +13 -0
  72. package/dist/secrets-u19-8EyW.mjs +0 -2
@@ -1,7 +1,7 @@
1
1
  import { n as UsageError } from "./errors-BGtxPkM7.mjs";
2
2
  import { n as resolveRelayUrls, t as loadConfig } from "./config-BlZnFqx7.mjs";
3
- import { t as ProviderStore } from "./store-CrTUwskE.mjs";
4
- import { t as loadSettings } from "./settings-C1Hb4zwd.mjs";
3
+ import { t as ProviderStore } from "./store-EGzVd-dn.mjs";
4
+ import { t as loadSettings } from "./settings-XCvAzDK8.mjs";
5
5
  import { homedir } from "node:os";
6
6
  import { join } from "node:path";
7
7
  //#region src/cli/commands/provider/common.ts
@@ -56,13 +56,24 @@ function parseMatchSpec(raw) {
56
56
  value
57
57
  };
58
58
  }
59
- /** "Name=value"(首个 = 分割;Name 小写化)。 */
60
59
  function parseHeaderSetSpec(raw) {
61
60
  const idx = raw.indexOf("=");
62
- if (idx <= 0) throw new UsageError(`error: invalid --header-set value: ${raw} (expected <name>=<value>, value may be $env:VAR)`);
61
+ if (idx <= 0) throw new UsageError(`error: invalid --header-set value: ${raw} (expected <name>=<value> or <name>={"hook":...}; value is a literal or hook invocation JSON)`);
62
+ const name = raw.slice(0, idx).trim().toLowerCase();
63
+ const rawValue = raw.slice(idx + 1);
64
+ if (rawValue.startsWith("{")) try {
65
+ const parsed = JSON.parse(rawValue);
66
+ if (parsed === null || typeof parsed !== "object" || typeof parsed.hook !== "string") throw new Error("not a hook invocation");
67
+ return {
68
+ name,
69
+ value: parsed
70
+ };
71
+ } catch {
72
+ throw new UsageError(`error: invalid --header-set hook JSON for '${name}' (expected {"hook":"authHeader",...})`);
73
+ }
63
74
  return {
64
- name: raw.slice(0, idx).trim().toLowerCase(),
65
- value: raw.slice(idx + 1)
75
+ name,
76
+ value: rawValue
66
77
  };
67
78
  }
68
79
  /** 各命令的 rewrite 组装(仅在存在任一重写选项时构造)。 */
@@ -1,6 +1,6 @@
1
1
  import { n as resolveRelayUrls, t as loadConfig } from "./config-BlZnFqx7.mjs";
2
2
  import { n as loadSdk, t as resolveHttpProxy } from "./proxy-D7VMUVEg.mjs";
3
- import { t as loadSettings } from "./settings-C1Hb4zwd.mjs";
3
+ import { t as loadSettings } from "./settings-XCvAzDK8.mjs";
4
4
  import { homedir } from "node:os";
5
5
  //#region src/cli/commands/consumer/common.ts
6
6
  function ctxOut(ctx) {
@@ -1,8 +1,8 @@
1
1
  import { n as UsageError, r as reportCliError, t as CliError } from "./errors-BGtxPkM7.mjs";
2
2
  import { n as parseArgv } from "./args-CSxXW9eD.mjs";
3
- import { r as settingsPath, t as loadSettings } from "./settings-C1Hb4zwd.mjs";
3
+ import { r as settingsPath, t as loadSettings } from "./settings-XCvAzDK8.mjs";
4
4
  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";
5
- import { r as openStore } from "./common-CqxqIg6D.mjs";
5
+ import { r as openStore } from "./common-C51SAE5f.mjs";
6
6
  import { openSync, readFileSync } from "node:fs";
7
7
  import { homedir } from "node:os";
8
8
  import { isAbsolute, resolve } from "node:path";
@@ -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-DSBlodTF.mjs");
102
+ const { run: serveRun } = await import("./serve-B51lmr9M.mjs");
103
103
  return serveRun([...startArgv], { homedir: home });
104
104
  }
105
105
  /** 后台 pid(活性核验;陈旧 pidfile 就地清理返回 null)。 */
@@ -1,7 +1,7 @@
1
1
  import { n as UsageError } from "./errors-BGtxPkM7.mjs";
2
2
  import { n as parseArgv } from "./args-CSxXW9eD.mjs";
3
- import { d as removeKeyring, n as consumersRoot } from "./store-CQShpYar.mjs";
4
- import { n as ctxHomedir, r as ctxOut } from "./common-CgxCXeTt.mjs";
3
+ import { d as removeKeyring, n as consumersRoot } from "./store-DOFDy1rY.mjs";
4
+ import { n as ctxHomedir, r as ctxOut } from "./common-CanLn0Qt.mjs";
5
5
  import { homedir } from "node:os";
6
6
  //#region src/cli/commands/consumer/forget.ts
7
7
  async function run(argv, ctx = {}) {
@@ -235,6 +235,7 @@ const SERVICE_ENTRY_SCHEMA = z.strictObject({
235
235
  name: z.string().min(1).max(256),
236
236
  match: z.array(SERVICE_MATCH_SCHEMA).max(64),
237
237
  defaultPort: z.number().int().min(1).max(65535),
238
+ hooks: z.string().optional(),
238
239
  detail: SERVICE_DETAIL_SCHEMA.optional()
239
240
  });
240
241
  const GROUP_ENTRY_SCHEMA = z.strictObject({
@@ -1,6 +1,6 @@
1
1
  import { n as UsageError, r as reportCliError } from "./errors-BGtxPkM7.mjs";
2
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";
3
+ import { d as str, l as resolveDataDir, n as multi, r as openStore, s as parsePositiveInt } from "./common-C51SAE5f.mjs";
4
4
  import { homedir } from "node:os";
5
5
  //#region src/cli/commands/provider/group.ts
6
6
  const SPEC = {
@@ -0,0 +1,228 @@
1
+ import { createRequire } from "node:module";
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ //#region src/provider/hook.ts
7
+ var HookMissingError = class extends Error {
8
+ constructor() {
9
+ super("credential source missing");
10
+ this.name = "HookMissingError";
11
+ }
12
+ };
13
+ /** 包根(向上找 name=ai-fly 的 package.json;dev 仓库与安装布局皆成立)。 */
14
+ function packageRoot(start = dirname(fileURLToPath(import.meta.url))) {
15
+ let dir = resolve(start);
16
+ for (let hops = 0; hops < 12; hops += 1) {
17
+ const pkg = join(dir, "package.json");
18
+ if (existsSync(pkg)) try {
19
+ const parsed = JSON.parse(readFileSync(pkg, "utf8"));
20
+ if (parsed !== null && typeof parsed === "object" && parsed.name === "ai-fly") return dir;
21
+ } catch {}
22
+ const parent = dirname(dir);
23
+ if (parent === dir) break;
24
+ dir = parent;
25
+ }
26
+ return process.cwd();
27
+ }
28
+ function hookScriptPaths(home = homedir()) {
29
+ return {
30
+ userDir: join(home, ".aifly", "hooks"),
31
+ builtinDir: join(packageRoot(), "hooks")
32
+ };
33
+ }
34
+ function scriptCandidates(name, paths) {
35
+ const bases = [join(paths.userDir, name), join(paths.builtinDir, name)];
36
+ const out = [];
37
+ for (const b of bases) out.push(`${b}.cjs`, b.endsWith(".cjs") || b.endsWith(".js") ? b : `${b}.js`);
38
+ return out;
39
+ }
40
+ /** 加载 hooks 脚本模块(用户库优先于内建库;缺席返回 undefined)。 */
41
+ function loadHookScript(name, home = homedir()) {
42
+ if (name === "" || name.includes("/") || name.includes("..")) return void 0;
43
+ const req = createRequire(import.meta.url);
44
+ for (const candidate of scriptCandidates(name, hookScriptPaths(home))) {
45
+ if (!existsSync(candidate)) continue;
46
+ try {
47
+ const mod = req(candidate);
48
+ if (mod !== null && typeof mod === "object") return mod;
49
+ } catch {}
50
+ }
51
+ }
52
+ function dirScriptNames(dir) {
53
+ try {
54
+ return readdirSync(dir).filter((f) => f.endsWith(".cjs")).map((f) => f.slice(0, -4));
55
+ } catch {
56
+ return [];
57
+ }
58
+ }
59
+ /** 枚举可用 hooks 脚本及其钩子清单(用户库优先;内建同名被覆盖不重复列出)。 */
60
+ function discoverHooks(home = homedir()) {
61
+ const paths = hookScriptPaths(home);
62
+ const seen = /* @__PURE__ */ new Map();
63
+ for (const name of dirScriptNames(paths.userDir)) {
64
+ const mod = loadHookScript(name, home);
65
+ if (mod === void 0) continue;
66
+ seen.set(name, {
67
+ name,
68
+ source: "user",
69
+ fns: exportedHookFns(mod)
70
+ });
71
+ }
72
+ for (const name of dirScriptNames(paths.builtinDir)) {
73
+ if (seen.has(name)) continue;
74
+ const mod = loadHookScript(name, home);
75
+ if (mod === void 0) continue;
76
+ seen.set(name, {
77
+ name,
78
+ source: "builtin",
79
+ fns: exportedHookFns(mod)
80
+ });
81
+ }
82
+ return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
83
+ }
84
+ /** 模块导出面的钩子函数名(typeof function 且非下划线开头)。 */
85
+ function exportedHookFns(mod) {
86
+ return Object.keys(mod).filter((k) => !k.startsWith("_") && typeof mod[k] === "function").sort();
87
+ }
88
+ function isAsyncIterable(v) {
89
+ return v !== null && typeof v === "object" && Symbol.asyncIterator in v;
90
+ }
91
+ const subscriptions = /* @__PURE__ */ new Map();
92
+ /** 建立订阅并等待首个 yield:返回首值(后续值由后台循环更新 latest)。 */
93
+ function startSubscription(key, stream, sub) {
94
+ sub.iterator = stream[Symbol.asyncIterator]();
95
+ return new Promise((resolveFirst) => {
96
+ let firstSettled = false;
97
+ (async () => {
98
+ try {
99
+ const it = sub.iterator;
100
+ for (;;) {
101
+ const r = await it.next();
102
+ if (r.done === true) break;
103
+ if (typeof r.value === "string" && r.value !== "") {
104
+ sub.latest = r.value;
105
+ if (firstSettled === false) {
106
+ firstSettled = true;
107
+ resolveFirst(r.value);
108
+ }
109
+ }
110
+ }
111
+ } catch {}
112
+ if (firstSettled === false) {
113
+ firstSettled = true;
114
+ resolveFirst(void 0);
115
+ }
116
+ })();
117
+ });
118
+ }
119
+ /** 回收全部订阅(daemon stop / 测试收尾)。return() 对挂起在内部 await 的
120
+ * 生成器(如等待 watch 事件)可能永不结算——300ms 竞速后放弃该迭代器。 */
121
+ async function disposeHookSubscriptions() {
122
+ const subs = [...subscriptions.entries()];
123
+ subscriptions.clear();
124
+ await Promise.all(subs.map(async ([, sub]) => {
125
+ try {
126
+ await Promise.race([sub.iterator?.return?.(void 0) ?? Promise.resolve(), new Promise((r) => setTimeout(r, 300))]);
127
+ } catch {}
128
+ }));
129
+ }
130
+ /**
131
+ * 解析一次钩子取值(headerSet 的 {hook,...} 协议消费点)。
132
+ * 订阅键 = script|fn —— 同键首个请求建立订阅,后续读 latest。
133
+ * 任何未命中(脚本/函数缺席、调用抛错、非字符串、空串、订阅未出首值)
134
+ * 抛 HookMissingError(零上游请求;信息不含脚本路径与值)。
135
+ */
136
+ async function resolveHookValue(fnName, opts) {
137
+ const home = opts.home ?? homedir();
138
+ const raw = (opts.loader ?? loadHookScript)(opts.script, home)?.[fnName];
139
+ if (typeof raw !== "function") throw new HookMissingError();
140
+ let result;
141
+ try {
142
+ result = raw({
143
+ homedir: home,
144
+ args: opts.args ?? {},
145
+ secrets: (n) => opts.secrets?.(n),
146
+ env: (n) => opts.env?.(n) ?? process.env[n]
147
+ });
148
+ } catch {
149
+ throw new HookMissingError();
150
+ }
151
+ const key = `${opts.script}|${fnName}`;
152
+ if (isAsyncIterable(result)) {
153
+ let sub = subscriptions.get(key);
154
+ if (sub === void 0) {
155
+ sub = {};
156
+ subscriptions.set(key, sub);
157
+ const first = await startSubscription(key, result, sub);
158
+ if (first === void 0) throw new HookMissingError();
159
+ return first;
160
+ }
161
+ if (sub.latest === void 0) throw new HookMissingError();
162
+ return sub.latest;
163
+ }
164
+ const awaited = await result;
165
+ if (typeof awaited !== "string" || awaited === "") throw new HookMissingError();
166
+ return awaited;
167
+ }
168
+ /** 脚本名合法性(资源标识符;防路径逃逸)。 */
169
+ function validHookName(name) {
170
+ return /^[a-z][a-z0-9_-]{0,63}$/.test(name);
171
+ }
172
+ function userHookPath(name, home = homedir()) {
173
+ return join(hookScriptPaths(home).userDir, `${name}.cjs`);
174
+ }
175
+ /**
176
+ * 安装用户 hooks 脚本(写入 ~/.aifly/hooks/<name>.cjs 并校验可加载、
177
+ * 至少导出一个钩子函数)。内建同名允许覆盖(用户库优先语义)。
178
+ */
179
+ function installUserHook(name, content, home = homedir()) {
180
+ if (!validHookName(name)) throw new Error(`invalid hook script name '${name}' (lowercase identifier, 1-64 chars)`);
181
+ const paths = hookScriptPaths(home);
182
+ mkdirSync(paths.userDir, { recursive: true });
183
+ const path = userHookPath(name, home);
184
+ writeFileSync(path, content, { mode: 384 });
185
+ const mod = loadHookScript(name, home);
186
+ if (mod === void 0) {
187
+ rmSync(path, { force: true });
188
+ throw new Error("hook script failed to load (syntax error or non-object export)");
189
+ }
190
+ const fns = exportedHookFns(mod);
191
+ if (fns.length === 0) {
192
+ rmSync(path, { force: true });
193
+ throw new Error("hook script exports no hook functions (named function exports required)");
194
+ }
195
+ return {
196
+ name,
197
+ path,
198
+ fns
199
+ };
200
+ }
201
+ /** 删除用户脚本(内建库不可删;未命中报错)。 */
202
+ function removeUserHook(name, home = homedir()) {
203
+ const path = userHookPath(name, home);
204
+ if (!existsSync(path)) {
205
+ const builtin = join(hookScriptPaths(home).builtinDir, `${name}.cjs`);
206
+ if (existsSync(builtin)) throw new Error(`'${name}' is a builtin hook script (cannot be removed)`);
207
+ throw new Error(`hook script '${name}' not found`);
208
+ }
209
+ rmSync(path, { force: true });
210
+ return { path };
211
+ }
212
+ /** 读取用户脚本内容(get/show;内建脚本返回包内内容)。 */
213
+ function readHookScript(name, home = homedir()) {
214
+ const user = userHookPath(name, home);
215
+ if (existsSync(user)) return {
216
+ path: user,
217
+ source: "user",
218
+ content: readFileSync(user, "utf8")
219
+ };
220
+ const builtin = join(hookScriptPaths(home).builtinDir, `${name}.cjs`);
221
+ if (existsSync(builtin)) return {
222
+ path: builtin,
223
+ source: "builtin",
224
+ content: readFileSync(builtin, "utf8")
225
+ };
226
+ }
227
+ //#endregion
228
+ export { removeUserHook as a, readHookScript as i, disposeHookSubscriptions as n, resolveHookValue as o, installUserHook as r, discoverHooks as t };
@@ -0,0 +1,142 @@
1
+ import { n as UsageError, r as reportCliError } from "./errors-BGtxPkM7.mjs";
2
+ import { n as parseArgv } from "./args-CSxXW9eD.mjs";
3
+ import { d as str, l as resolveDataDir } from "./common-C51SAE5f.mjs";
4
+ import { a as removeUserHook, i as readHookScript, n as disposeHookSubscriptions, o as resolveHookValue, r as installUserHook, t as discoverHooks } from "./hook-CvAMGts4.mjs";
5
+ import { t as SecretsStore } from "./secrets-1Hmddmwy.mjs";
6
+ import { readFileSync } from "node:fs";
7
+ import { homedir } from "node:os";
8
+ //#region src/cli/commands/provider/hooks.ts
9
+ const SPEC = {
10
+ data: {
11
+ type: "string",
12
+ tilde: true
13
+ },
14
+ file: {
15
+ type: "string",
16
+ tilde: true
17
+ },
18
+ stdin: { type: "boolean" },
19
+ "arg": { type: "multi" },
20
+ bearer: { type: "boolean" }
21
+ };
22
+ const USAGE = `usage:
23
+ ai-fly hooks list
24
+ ai-fly hooks get <name>
25
+ ai-fly hooks add <name> [--file <path> | --stdin]
26
+ ai-fly hooks remove <name>
27
+ ai-fly hooks run <name> <fn> [--arg k=v]... [--bearer]
28
+ hooks 脚本:内建库 + ~/.aifly/hooks/<name>.cjs(用户覆盖内建);
29
+ 导出函数名 = 钩子清单(authHeader 为 HTTP 认证头钩子)。`;
30
+ async function run(argv, ctx = {}) {
31
+ const home = ctx.homedir ?? homedir();
32
+ try {
33
+ const sub = argv[0];
34
+ const rest = argv.slice(1);
35
+ switch (sub) {
36
+ case void 0:
37
+ case "list": return list(rest, home);
38
+ case "get": return get(rest, home);
39
+ case "add": return add(rest, home);
40
+ case "remove": return remove(rest, home);
41
+ case "run": return await runHook(rest, home);
42
+ default: throw new UsageError(`error: unknown hooks subcommand '${sub}'\n${USAGE}`);
43
+ }
44
+ } catch (err) {
45
+ return reportCliError(err);
46
+ }
47
+ }
48
+ function parse(rest, home) {
49
+ const { options, positionals } = parseArgv([...rest], SPEC, { homedir: home });
50
+ if (positionals.length > 2) throw new UsageError(`error: unexpected argument '${positionals[2]}'`);
51
+ return {
52
+ options,
53
+ positionals
54
+ };
55
+ }
56
+ function list(rest, home) {
57
+ parse(rest, home);
58
+ const scripts = discoverHooks(home);
59
+ const out = (line) => {
60
+ process.stdout.write(`${line}\n`);
61
+ };
62
+ out(`hooks scripts (${scripts.length})`);
63
+ for (const s of scripts) out(` ${s.name.padEnd(16)} [${s.source}] ${s.fns.join(", ")}`);
64
+ if (scripts.length > 0) out(`use: ai-fly service add <name> --preset <id> --hooks <script> | ai-fly hooks run <name> <fn>`);
65
+ else out("(install one: ai-fly hooks add <name> --file <path.cjs>)");
66
+ return 0;
67
+ }
68
+ function get(rest, home) {
69
+ const { positionals } = parse(rest, home);
70
+ const name = positionals[0];
71
+ if (name === void 0) throw new UsageError("error: hooks get requires a <name> argument");
72
+ const found = readHookScript(name, home);
73
+ if (found === void 0) throw new UsageError(`error: hook script '${name}' not found (see: ai-fly hooks list)`);
74
+ process.stdout.write(`hooks script '${name}' [${found.source}]\n path: ${found.path}\n`);
75
+ process.stdout.write(found.content);
76
+ if (!found.content.endsWith("\n")) process.stdout.write("\n");
77
+ return 0;
78
+ }
79
+ function add(rest, home) {
80
+ const { options, positionals } = parse(rest, home);
81
+ const name = positionals[0];
82
+ if (name === void 0) throw new UsageError("error: hooks add requires a <name> argument");
83
+ let content = options.stdin === true ? void 0 : str(options.file) === void 0 ? void 0 : readContent(str(options.file));
84
+ if (options.stdin === true) content = readStdin();
85
+ if (content === void 0) throw new UsageError("error: hooks add requires --file <path> or --stdin for the script content");
86
+ const installed = installUserHook(name, content, home);
87
+ process.stdout.write(`hooks script installed: ${installed.name}\n path: ${installed.path}\n fns : ${installed.fns.join(", ")}\n`);
88
+ return 0;
89
+ }
90
+ function readContent(path) {
91
+ return readFileSync(path, "utf8");
92
+ }
93
+ function readStdin() {
94
+ try {
95
+ return readFileSync(0, "utf8");
96
+ } catch {
97
+ throw new UsageError("error: --stdin requires piped input (cat script.cjs | ai-fly hooks add <name> --stdin)");
98
+ }
99
+ }
100
+ function remove(rest, home) {
101
+ const { positionals } = parse(rest, home);
102
+ const name = positionals[0];
103
+ if (name === void 0) throw new UsageError("error: hooks remove requires a <name> argument");
104
+ const removed = removeUserHook(name, home);
105
+ process.stdout.write(`hooks script removed: ${name}\n path: ${removed.path}\n`);
106
+ return 0;
107
+ }
108
+ async function runHook(rest, home) {
109
+ const { options, positionals } = parse(rest, home);
110
+ const [name, fn] = positionals;
111
+ if (name === void 0 || fn === void 0) throw new UsageError("error: hooks run requires <name> <fn> arguments (see: ai-fly hooks list)");
112
+ const args = {};
113
+ for (const raw of options.arg ?? []) {
114
+ const idx = raw.indexOf("=");
115
+ if (idx <= 0) throw new UsageError(`error: --arg expects k=v (got '${raw}')`);
116
+ args[raw.slice(0, idx)] = raw.slice(idx + 1);
117
+ }
118
+ const store = SecretsStore.open(resolveDataDir(str(options.data), home));
119
+ let value;
120
+ try {
121
+ value = await resolveHookValue(fn, {
122
+ script: name,
123
+ home,
124
+ args,
125
+ secrets: (n) => store.resolve(n)?.headerValue
126
+ });
127
+ } catch {
128
+ value = void 0;
129
+ } finally {
130
+ await disposeHookSubscriptions();
131
+ }
132
+ if (value === void 0) {
133
+ process.stderr.write(`error: hook '${name}.${fn}' did not yield a value (script/fn missing, args invalid, or credential absent)\n`);
134
+ return 1;
135
+ }
136
+ if (options.bearer === true) value = `Bearer ${value}`;
137
+ const masked = value.length <= 6 ? "*".repeat(value.length) : `${value.slice(0, 2)}${"*".repeat(Math.min(value.length - 4, 12))}${value.slice(-2)} (len ${value.length})`;
138
+ process.stdout.write(`ok: ${name}.${fn} -> ${masked}\n`);
139
+ process.exit(0);
140
+ }
141
+ //#endregion
142
+ export { run };
@@ -1,9 +1,9 @@
1
1
  import { n as UsageError } from "./errors-BGtxPkM7.mjs";
2
2
  import { n as parseArgv } from "./args-CSxXW9eD.mjs";
3
- import { n as consumersRoot, s as listKeyrings } from "./store-CQShpYar.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-u1DqTU3U.mjs";
6
- import { n as createFabricProviderTransport, t as startEngine } from "./runtime-DNs9GtGp.mjs";
3
+ import { n as consumersRoot, s as listKeyrings } from "./store-DOFDy1rY.mjs";
4
+ import { a as ringsForRun, i as printListeners, n as ctxHomedir, o as waitForSignals, r as ctxOut, t as createSdkFabricFactory } from "./common-CanLn0Qt.mjs";
5
+ import { a as importLink, i as formatLinkPreview, n as decodeShareLink, r as formatKeyringSummary } from "./join-BRow9_3-.mjs";
6
+ import { n as createFabricProviderTransport, t as startEngine } from "./runtime-C_pLA5F0.mjs";
7
7
  import { homedir } from "node:os";
8
8
  //#region src/cli/commands/consumer/import.ts
9
9
  async function run(argv, ctx = {}) {
@@ -1,7 +1,7 @@
1
1
  import { t as CliError } from "./errors-BGtxPkM7.mjs";
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, h as upsertKey, i as findKeyringDir, l as mergeImportView, o as keyringDir, r as fabricDir } from "./store-CQShpYar.mjs";
2
+ import { o as decodeZ32, s as randomZ32 } from "./rpc-contract-cB5SuyQ_.mjs";
3
+ import { l as SERVICE_ENTRY_SCHEMA } from "./frames-BDz8buix.mjs";
4
+ import { a as hasFabricIdentity, c as loadKeyring, f as saveKeyring, h as upsertKey, i as findKeyringDir, l as mergeImportView, o as keyringDir, r as fabricDir } from "./store-DOFDy1rY.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,8 +1,8 @@
1
1
  import { n as UsageError } from "./errors-BGtxPkM7.mjs";
2
2
  import { n as parseArgv } from "./args-CSxXW9eD.mjs";
3
- import { n as consumersRoot } from "./store-CQShpYar.mjs";
4
- import { n as ctxHomedir, r as ctxOut, t as createSdkFabricFactory } from "./common-CgxCXeTt.mjs";
5
- import { o as joinDevice } from "./join-u1DqTU3U.mjs";
3
+ import { n as consumersRoot } from "./store-DOFDy1rY.mjs";
4
+ import { n as ctxHomedir, r as ctxOut, t as createSdkFabricFactory } from "./common-CanLn0Qt.mjs";
5
+ import { o as joinDevice } from "./join-BRow9_3-.mjs";
6
6
  import { homedir } from "node:os";
7
7
  //#region src/cli/commands/consumer/join.ts
8
8
  async function run(argv, ctx = {}) {
@@ -1,6 +1,6 @@
1
1
  import { n as UsageError, r as reportCliError } from "./errors-BGtxPkM7.mjs";
2
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";
3
+ import { c as requireString, d as str, l as resolveDataDir, r as openStore } from "./common-C51SAE5f.mjs";
4
4
  import { homedir } from "node:os";
5
5
  //#region src/cli/commands/provider/key.ts
6
6
  const SPEC = {
@@ -8,11 +8,13 @@ const SPEC = {
8
8
  type: "string",
9
9
  tilde: true
10
10
  },
11
- group: { type: "string" }
11
+ group: { type: "string" },
12
+ name: { type: "string" }
12
13
  };
13
14
  const USAGE = `usage:
14
- ai-fly key issue --group <name> [--data <dir>]
15
+ ai-fly key issue --group <name> [--name <keyName>] [--data <dir>]
15
16
  ai-fly key list [--data <dir>]
17
+ ai-fly key show <keyId> [--data <dir>]
16
18
  ai-fly key revoke <keyId> [--data <dir>]`;
17
19
  async function run(argv, ctx = {}) {
18
20
  try {
@@ -21,22 +23,24 @@ async function run(argv, ctx = {}) {
21
23
  const sub = positionals[0];
22
24
  if (sub === "issue") return issue(options, home);
23
25
  if (sub === "list") return list(options, home);
26
+ if (sub === "show") return show(options, positionals, home);
24
27
  if (sub === "revoke") return revoke(options, positionals, home);
25
28
  if (sub === void 0) throw new UsageError(USAGE);
26
- throw new UsageError(`error: unknown key subcommand '${sub}' (known: issue, list, revoke)`);
29
+ throw new UsageError(`error: unknown key subcommand '${sub}' (known: issue, list, show, revoke)`);
27
30
  } catch (err) {
28
31
  return reportCliError(err);
29
32
  }
30
33
  }
31
34
  function issue(options, home) {
32
35
  const group = requireString(str(options.group), "group");
33
- const issued = openStore(resolveDataDir(str(options.data), home)).issueKey(group);
36
+ const name = str(options.name) ?? "default";
37
+ const issued = openStore(resolveDataDir(str(options.data), home)).issueKey(group, name);
34
38
  process.stdout.write([
35
- `key issued for group '${group}'`,
39
+ `key '${name}' issued for group '${group}'`,
36
40
  ` keyId: ${issued.keyId}`,
37
41
  ` key : ${issued.key}`,
38
42
  "",
39
- "This secret is shown only once. Store it now (password manager or share link).",
43
+ "The raw key is stored locally - re-show it anytime with: ai-fly key show " + issued.keyId,
40
44
  "Revoke with: ai-fly key revoke " + issued.keyId,
41
45
  ""
42
46
  ].join("\n"));
@@ -51,11 +55,23 @@ function list(options, home) {
51
55
  const lines = [];
52
56
  for (const k of keys) {
53
57
  const status = k.revokedAt === void 0 ? "active" : `revoked at ${new Date(k.revokedAt).toISOString()}`;
54
- lines.push(`${k.keyId} group=${k.group} created=${new Date(k.createdAt).toISOString()} ${status}`);
58
+ const name = k.name ?? "(unnamed)";
59
+ lines.push(`${k.keyId} name=${name} group=${k.group} created=${new Date(k.createdAt).toISOString()} ${status}`);
55
60
  }
56
61
  process.stdout.write(`${lines.join("\n")}\n`);
57
62
  return 0;
58
63
  }
64
+ function show(options, positionals, home) {
65
+ const keyId = positionals[1];
66
+ if (keyId === void 0) throw new UsageError("error: key show requires a <keyId> argument");
67
+ const material = openStore(resolveDataDir(str(options.data), home)).getKeyMaterial(keyId);
68
+ if (material === void 0) {
69
+ process.stdout.write(`key '${keyId}' has no retrievable material (missing, revoked, or issued before raw storage)\n`);
70
+ return 1;
71
+ }
72
+ process.stdout.write(`${material}\n`);
73
+ return 0;
74
+ }
59
75
  function revoke(options, positionals, home) {
60
76
  const keyId = positionals[1];
61
77
  if (keyId === void 0) throw new UsageError("error: key revoke requires a <keyId> argument");
@@ -1,8 +1,8 @@
1
1
  import { n as UsageError } from "./errors-BGtxPkM7.mjs";
2
2
  import { n as parseArgv } from "./args-CSxXW9eD.mjs";
3
- import { n as consumersRoot } from "./store-CQShpYar.mjs";
4
- import { n as ctxHomedir, r as ctxOut } from "./common-CgxCXeTt.mjs";
5
- import { r as formatKeyringSummary, t as addKey } from "./join-u1DqTU3U.mjs";
3
+ import { n as consumersRoot } from "./store-DOFDy1rY.mjs";
4
+ import { n as ctxHomedir, r as ctxOut } from "./common-CanLn0Qt.mjs";
5
+ import { r as formatKeyringSummary, t as addKey } from "./join-BRow9_3-.mjs";
6
6
  import { homedir } from "node:os";
7
7
  //#region src/cli/commands/consumer/key.ts
8
8
  async function run(argv, ctx = {}) {
@@ -1,4 +1,4 @@
1
- import { n as ROUTE_LOCAL_PREFIX } from "./rpc-contract-5qyWzT5t.mjs";
1
+ import { n as ROUTE_LOCAL_PREFIX } from "./rpc-contract-cB5SuyQ_.mjs";
2
2
  //#region src/consumer/local-test.ts
3
3
  const DEFAULT_TIMEOUT_MS = 2e4;
4
4
  const BODY_EXCERPT_MAX = 2e3;
@@ -1,4 +1,4 @@
1
- import { t as PRESET_SCHEMA } from "./rpc-contract-5qyWzT5t.mjs";
1
+ import { t as PRESET_SCHEMA } from "./rpc-contract-cB5SuyQ_.mjs";
2
2
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { dirname, join } from "node:path";
@@ -58,6 +58,26 @@ var providers_default = {
58
58
  }],
59
59
  "notes": "OpenAI-compatible at /v1/* (chat completions + responses, codex-ready); Anthropic-compatible surface at /anthropic/* (per vendor docs).",
60
60
  "source": "https://models.dev/api.json + https://api-docs.deepseek.com/guides/anthropic_api"
61
+ },
62
+ {
63
+ "id": "codex",
64
+ "label": "Codex (ChatGPT backend)",
65
+ "apiForm": "openai-responses",
66
+ "baseUrl": "https://chatgpt.com",
67
+ "defaultPort": 4306,
68
+ "matchDomains": ["chatgpt.com"],
69
+ "routes": [{
70
+ "forms": ["openai-responses"],
71
+ "localPrefix": "/codex",
72
+ "upstreamPrefix": "/backend-api/codex"
73
+ }],
74
+ "authHeader": {
75
+ "hook": "authHeader",
76
+ "bearer": true
77
+ },
78
+ "notes": "ChatGPT Codex backend (Responses API). Auth reads ~/.codex/auth.json per request (codex CLI login state; no key needed). Point agents at OPENAI_BASE_URL=http://127.0.0.1:<port>/codex with wire_api=responses. WS clients: ws://127.0.0.1:<port>/codex/responses upgrades to wss upstream.",
79
+ "source": "https://github.com/openai/codex (~/.codex/auth.json login state)",
80
+ "hooks": "codex"
61
81
  }
62
82
  ]
63
83
  };
@@ -1,5 +1,5 @@
1
- import { s as randomZ32 } from "./rpc-contract-5qyWzT5t.mjs";
2
- import { a as FRAME_TYPE_NAME, d as isKnownFrameType, i as FRAME_TYPE, r as FRAME_HEADER_SCHEMAS, t as ERROR_CODE, u as classifySchemaFailure } from "./frames-BDqvhB2Y.mjs";
1
+ import { s as randomZ32 } from "./rpc-contract-cB5SuyQ_.mjs";
2
+ import { a as FRAME_TYPE_NAME, d as isKnownFrameType, i as FRAME_TYPE, r as FRAME_HEADER_SCHEMAS, t as ERROR_CODE, u as classifySchemaFailure } from "./frames-BDz8buix.mjs";
3
3
  //#region src/wire/codec.ts
4
4
  const WIRE_MAGIC_PREFIX = "aifly";
5
5
  const WIRE_MAGIC = "aifly1";
@@ -1,7 +1,7 @@
1
1
  import { n as UsageError } from "./errors-BGtxPkM7.mjs";
2
2
  import { n as parseArgv } from "./args-CSxXW9eD.mjs";
3
- import { m as setPort, n as consumersRoot, s as listKeyrings } from "./store-CQShpYar.mjs";
4
- import { n as ctxHomedir, r as ctxOut } from "./common-CgxCXeTt.mjs";
3
+ import { m as setPort, n as consumersRoot, s as listKeyrings } from "./store-DOFDy1rY.mjs";
4
+ import { n as ctxHomedir, r as ctxOut } from "./common-CanLn0Qt.mjs";
5
5
  import { i as validatePortArg, n as desiredPortFor } from "./ports-pI59MtmI.mjs";
6
6
  import { homedir } from "node:os";
7
7
  //#region src/cli/commands/consumer/ports.ts