ai-fly 0.4.0-alpha.8 → 0.4.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.
Files changed (48) hide show
  1. package/dist/ai-fly.js +6 -6
  2. package/dist/app/main.js +60 -17
  3. package/dist/{common-B6U1vLFw.mjs → common-C51SAE5f.mjs} +2 -2
  4. package/dist/{common-CwlJNk3L.mjs → common-CanLn0Qt.mjs} +1 -1
  5. package/dist/{daemon-iqO29D7K.mjs → daemon-BWgyJMyB.mjs} +3 -3
  6. package/dist/{forget-BIorc8Rx.mjs → forget-B3KRkHho.mjs} +2 -2
  7. package/dist/{group-C13I2bCR.mjs → group-BdnvRD2t.mjs} +1 -1
  8. package/dist/{hooks-BeBzkBL-.mjs → hooks-DUrXA7PL.mjs} +2 -2
  9. package/dist/{import-BhasJTGS.mjs → import-CTkKGPsa.mjs} +4 -4
  10. package/dist/{join-DiVTFi59.mjs → join-BRow9_3-.mjs} +2 -2
  11. package/dist/{join-BHhCLh9B.mjs → join-d-pqyf8e.mjs} +3 -3
  12. package/dist/{key-DYeTZ7MI.mjs → key-CTL_AXOh.mjs} +24 -8
  13. package/dist/{key-DdZsNmo_.mjs → key-D_66hrWW.mjs} +3 -3
  14. package/dist/{local-test-B1ys1pNo.mjs → local-test-BZHGo22F.mjs} +1 -1
  15. package/dist/{models-dev-DPT_BK_V.mjs → models-dev-CDEyd2k3.mjs} +1 -1
  16. package/dist/{mux-DlwEK4Tt.mjs → mux-BaIDugDq.mjs} +1 -1
  17. package/dist/{ports-DDM5bbJg.mjs → ports-TQ4tWmiJ.mjs} +2 -2
  18. package/dist/{presets-Do5DWXfQ.mjs → presets-DleOUQiT.mjs} +2 -2
  19. package/dist/{revoke-6lrZkjN4.mjs → revoke-DTFW_EF1.mjs} +2 -2
  20. package/dist/{rewrite-vGrkDZx8.mjs → rewrite-CEzfYH9n.mjs} +2 -2
  21. package/dist/{route-test-DfGzFjKw.mjs → route-test-D2LQyX7c.mjs} +3 -3
  22. package/dist/{rpc-contract-BXviZUtF.mjs → rpc-contract-cB5SuyQ_.mjs} +16 -6
  23. package/dist/{run-BLxeLngt.mjs → run-D8kgXdId.mjs} +3 -3
  24. package/dist/{runtime-D8P29hIl.mjs → runtime-C_pLA5F0.mjs} +2 -2
  25. package/dist/{secret-Ds6pIoLP.mjs → secret-P2RgHy2h.mjs} +2 -2
  26. package/dist/{secrets-bHJBznMX.mjs → secrets-1Hmddmwy.mjs} +2 -2
  27. package/dist/secrets-C9IYp93v.mjs +2 -0
  28. package/dist/{serve-B4f79Eqq.mjs → serve-B51lmr9M.mjs} +2 -2
  29. package/dist/{serve-C8VRRJrt.mjs → serve-CY7ZFOFM.mjs} +4 -4
  30. package/dist/{service-CYkpuJuc.mjs → service-D6TNolyh.mjs} +4 -4
  31. package/dist/{settings-CSran7QD.mjs → settings-Rq1zLoxY.mjs} +1 -1
  32. package/dist/{settings-CY-bC0MJ.mjs → settings-XCvAzDK8.mjs} +2 -2
  33. package/dist/{share-B94HhX_b.mjs → share-DdtbAylA.mjs} +27 -12
  34. package/dist/{status-BFpr-LHB.mjs → status-DSVQ3YHm.mjs} +3 -3
  35. package/dist/{status-C_EI8-b9.mjs → status-uE2-kOyK.mjs} +3 -3
  36. package/dist/{store-DCLn0Df-.mjs → store-DOFDy1rY.mjs} +1 -1
  37. package/dist/{store-DxU0bd_u.mjs → store-EGzVd-dn.mjs} +21 -3
  38. package/dist/{test-BERWIlFb.mjs → test-DW4BF7xJ.mjs} +2 -2
  39. package/hooks/codex.cjs +12 -0
  40. package/hooks/env.cjs +10 -0
  41. package/hooks/file.cjs +27 -0
  42. package/hooks/secret.cjs +11 -0
  43. package/package.json +2 -1
  44. package/webui/dist/assets/index-B5lnC-QV.js +71 -0
  45. package/webui/dist/assets/{index-CufH1z8j.css → index-DqLIOzIY.css} +1 -1
  46. package/webui/dist/index.html +2 -2
  47. package/dist/secrets-G2HTT66T.mjs +0 -2
  48. package/webui/dist/assets/index-B-lLxXvx.js +0 -77
@@ -1,9 +1,9 @@
1
1
  import { c as RESP_META_HEADER_WHITELIST, i as FRAME_TYPE, s as REJECTED_CODE, t as ERROR_CODE } from "./frames-BDz8buix.mjs";
2
2
  import { n as loadSdk } from "./proxy-D7VMUVEg.mjs";
3
- import { i as ensurePrivateDir, r as atomicWriteFileSync, t as ProviderStore } from "./store-DxU0bd_u.mjs";
4
- import { n as FabricWireAdapter, r as DEFAULT_BODY_CHUNK_BYTES, t as WireSession } from "./mux-DlwEK4Tt.mjs";
5
- import { t as SecretsStore } from "./secrets-bHJBznMX.mjs";
6
- import { a as collectEnvVarNames, i as buildUpstreamRequest, n as RewriteError, r as SecretMissingError, s as buildServiceEntry, t as PathNotOfferedError } from "./rewrite-vGrkDZx8.mjs";
3
+ import { i as ensurePrivateDir, r as atomicWriteFileSync, t as ProviderStore } from "./store-EGzVd-dn.mjs";
4
+ import { n as FabricWireAdapter, r as DEFAULT_BODY_CHUNK_BYTES, t as WireSession } from "./mux-BaIDugDq.mjs";
5
+ import { t as SecretsStore } from "./secrets-1Hmddmwy.mjs";
6
+ import { a as collectEnvVarNames, i as buildUpstreamRequest, n as RewriteError, r as SecretMissingError, s as buildServiceEntry, t as PathNotOfferedError } from "./rewrite-CEzfYH9n.mjs";
7
7
  import { appendFileSync, existsSync, readFileSync, readdirSync, watch } from "node:fs";
8
8
  import "node:os";
9
9
  import { dirname, join } from "node:path";
@@ -1,7 +1,7 @@
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 { a as parseMatchSpec, d as str, i as parseHeaderSetSpec, l as resolveDataDir, n as multi, o as parsePortNumber, r as openStore, t as buildRewrite } from "./common-B6U1vLFw.mjs";
4
- import { n as loadCuratedPresets } from "./models-dev-DPT_BK_V.mjs";
3
+ import { a as parseMatchSpec, d as str, i as parseHeaderSetSpec, l as resolveDataDir, n as multi, o as parsePortNumber, r as openStore, t as buildRewrite } from "./common-C51SAE5f.mjs";
4
+ import { n as loadCuratedPresets } from "./models-dev-CDEyd2k3.mjs";
5
5
  import { homedir } from "node:os";
6
6
  //#region src/cli/commands/provider/service.ts
7
7
  const SPEC = {
@@ -225,8 +225,8 @@ async function test(options, positionals, home) {
225
225
  formRaw = (localPrefix !== void 0 ? routes.find((r) => r.localPrefix === localPrefix) : routes.length === 1 ? routes[0] : void 0)?.forms[0] ?? "openai-chat";
226
226
  }
227
227
  if (!ROUTE_FORMS.includes(formRaw)) throw new UsageError(`error: --form must be one of ${ROUTE_FORMS.join(", ")}`);
228
- const { testServiceRoute } = await import("./route-test-DfGzFjKw.mjs");
229
- const { SecretsStore } = await import("./secrets-G2HTT66T.mjs");
228
+ const { testServiceRoute } = await import("./route-test-D2LQyX7c.mjs");
229
+ const { SecretsStore } = await import("./secrets-C9IYp93v.mjs");
230
230
  const secretsStore = SecretsStore.open(dataDir);
231
231
  const hitLocalPrefix = options["local-prefix"] !== void 0 ? str(options["local-prefix"]) : (() => {
232
232
  const routes = (service.routes ?? []).filter((r) => r.mode !== "pattern");
@@ -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 { n as saveSettings, r as settingsPath, t as loadSettings } from "./settings-CY-bC0MJ.mjs";
3
+ import { n as saveSettings, r as settingsPath, t as loadSettings } from "./settings-XCvAzDK8.mjs";
4
4
  import { homedir } from "node:os";
5
5
  //#region src/cli/commands/settings.ts
6
6
  const SPEC = { default: { type: "boolean" } };
@@ -1,5 +1,5 @@
1
- import { i as SETTINGS_SCHEMA } from "./rpc-contract-BXviZUtF.mjs";
2
- import { i as ensurePrivateDir, r as atomicWriteFileSync } from "./store-DxU0bd_u.mjs";
1
+ import { i as SETTINGS_SCHEMA } from "./rpc-contract-cB5SuyQ_.mjs";
2
+ import { i as ensurePrivateDir, r as atomicWriteFileSync } from "./store-EGzVd-dn.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
5
  import { join } from "node:path";
@@ -2,10 +2,10 @@ import { r as reportCliError, t as CliError } from "./errors-BGtxPkM7.mjs";
2
2
  import { n as parseArgv, r as parseDurationMs, t as assertDurationRange } from "./args-CSxXW9eD.mjs";
3
3
  import { l as SERVICE_ENTRY_SCHEMA } from "./frames-BDz8buix.mjs";
4
4
  import { t as resolveHttpProxy } from "./proxy-D7VMUVEg.mjs";
5
- import { n as StoreError } from "./store-DxU0bd_u.mjs";
6
- import { c as requireString, d as str, l as resolveDataDir, r as openStore, u as resolvedRelayUrls } from "./common-B6U1vLFw.mjs";
7
- import { t as openExistingFabric } from "./serve-C8VRRJrt.mjs";
8
- import { s as buildServiceEntry } from "./rewrite-vGrkDZx8.mjs";
5
+ import { n as StoreError } from "./store-EGzVd-dn.mjs";
6
+ import { c as requireString, d as str, l as resolveDataDir, r as openStore, u as resolvedRelayUrls } from "./common-C51SAE5f.mjs";
7
+ import { t as openExistingFabric } from "./serve-CY7ZFOFM.mjs";
8
+ import { s as buildServiceEntry } from "./rewrite-CEzfYH9n.mjs";
9
9
  import { homedir } from "node:os";
10
10
  import { z } from "zod";
11
11
  //#region src/provider/link.ts
@@ -33,21 +33,32 @@ function encodeShareLink(payload) {
33
33
  return SHARE_LINK_PREFIX + Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
34
34
  }
35
35
  /**
36
- * 构造分享链接:前置检查(组存在且非空)-> 签发新钥(每次 share 一枚;旧钥不可再现)
37
- * -> 组内服务脱敏视图 -> payload + link + 警告。
36
+ * 构造分享链接:前置检查(组存在且非空)-> key(复用 keyId 指定的已存
37
+ * 原文;缺省签发新钥)-> 组内服务脱敏视图 -> payload + link + 警告。
38
38
  */
39
39
  function buildShareLink(input) {
40
40
  if (input.store.getGroup(input.group) === void 0) throw new StoreError("not-found", `error: group '${input.group}' not found`);
41
41
  const services = input.store.groupServices(input.group);
42
42
  if (services.length === 0) throw new StoreError("invalid", `error: group '${input.group}' has no services; a share link would be meaningless`);
43
- const issued = input.store.issueKey(input.group);
43
+ let key;
44
+ let keyId;
45
+ if (input.keyId !== void 0) {
46
+ const material = input.store.getKeyMaterial(input.keyId);
47
+ if (material === void 0) throw new StoreError("invalid", `error: key '${input.keyId}' cannot be shared (missing, revoked, or raw material not stored)`);
48
+ key = material;
49
+ keyId = input.keyId;
50
+ } else {
51
+ const issued = input.store.issueKey(input.group, input.keyName);
52
+ key = issued.key;
53
+ keyId = issued.keyId;
54
+ }
44
55
  const serviceEntries = services.map(buildServiceEntry);
45
56
  const relayUrls = input.relayUrls === void 0 ? [] : [...input.relayUrls];
46
57
  const payload = {
47
58
  v: 1,
48
59
  invite: input.invite,
49
- key: issued.key,
50
- keyId: issued.keyId,
60
+ key,
61
+ keyId,
51
62
  provider: {
52
63
  alias: input.alias ?? input.store.alias ?? "provider",
53
64
  endpointId: input.endpointId,
@@ -62,7 +73,7 @@ function buildShareLink(input) {
62
73
  payload,
63
74
  link: encodeShareLink(payload),
64
75
  warnings,
65
- keyId: issued.keyId
76
+ keyId
66
77
  };
67
78
  }
68
79
  //#endregion
@@ -75,7 +86,9 @@ const SPEC = {
75
86
  group: { type: "string" },
76
87
  ttl: { type: "string" },
77
88
  relay: { type: "multi" },
78
- "allow-relayless": { type: "boolean" }
89
+ "allow-relayless": { type: "boolean" },
90
+ "key-id": { type: "string" },
91
+ "key-name": { type: "string" }
79
92
  };
80
93
  async function run(argv, ctx = {}) {
81
94
  let fabric;
@@ -97,7 +110,9 @@ async function run(argv, ctx = {}) {
97
110
  group,
98
111
  invite,
99
112
  endpointId: fabric.endpointId,
100
- relayUrls: relayStatus.urls
113
+ relayUrls: relayStatus.urls,
114
+ keyId: str(options["key-id"]),
115
+ keyName: str(options["key-name"])
101
116
  });
102
117
  process.stdout.write([
103
118
  `share link for group '${group}' (keyId ${result.keyId}, ttl ${describeTtl(ttlMs)}):`,
@@ -1,9 +1,9 @@
1
1
  import { 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
- import { d as str, l as resolveDataDir, r as openStore, u as resolvedRelayUrls } from "./common-B6U1vLFw.mjs";
5
- import { t as openExistingFabric } from "./serve-C8VRRJrt.mjs";
6
- import { c as detailDisplayLines, o as buildServiceDetail } from "./rewrite-vGrkDZx8.mjs";
4
+ import { d as str, l as resolveDataDir, r as openStore, u as resolvedRelayUrls } from "./common-C51SAE5f.mjs";
5
+ import { t as openExistingFabric } from "./serve-CY7ZFOFM.mjs";
6
+ import { c as detailDisplayLines, o as buildServiceDetail } from "./rewrite-CEzfYH9n.mjs";
7
7
  import { homedir } from "node:os";
8
8
  //#region src/cli/commands/provider/status.ts
9
9
  const SPEC = {
@@ -1,7 +1,7 @@
1
1
  import { n as parseArgv } from "./args-CSxXW9eD.mjs";
2
- import { n as consumersRoot, s as listKeyrings } from "./store-DCLn0Df-.mjs";
3
- import { a as ringsForRun, n as ctxHomedir, r as ctxOut, t as createSdkFabricFactory } from "./common-CwlJNk3L.mjs";
4
- import { n as createFabricProviderTransport, t as startEngine } from "./runtime-D8P29hIl.mjs";
2
+ import { n as consumersRoot, s as listKeyrings } from "./store-DOFDy1rY.mjs";
3
+ import { a as ringsForRun, n as ctxHomedir, r as ctxOut, t as createSdkFabricFactory } from "./common-CanLn0Qt.mjs";
4
+ import { n as createFabricProviderTransport, t as startEngine } from "./runtime-C_pLA5F0.mjs";
5
5
  import { homedir } from "node:os";
6
6
  //#region src/cli/commands/consumer/status.ts
7
7
  /** 状态显示名(spec 六态)。 */
@@ -1,5 +1,5 @@
1
1
  import { t as CliError } from "./errors-BGtxPkM7.mjs";
2
- import { s as randomZ32 } from "./rpc-contract-BXviZUtF.mjs";
2
+ import { s as randomZ32 } from "./rpc-contract-cB5SuyQ_.mjs";
3
3
  import { l as SERVICE_ENTRY_SCHEMA } from "./frames-BDz8buix.mjs";
4
4
  import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
5
5
  import { homedir } from "node:os";
@@ -1,4 +1,4 @@
1
- import { n as ROUTE_LOCAL_PREFIX, s as randomZ32 } from "./rpc-contract-BXviZUtF.mjs";
1
+ import { n as ROUTE_LOCAL_PREFIX, s as randomZ32 } from "./rpc-contract-cB5SuyQ_.mjs";
2
2
  import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { z } from "zod";
@@ -235,9 +235,17 @@ const KEY_STORE_SCHEMA = z.strictObject({
235
235
  keyId: z.string().min(1).max(128),
236
236
  group: z.string().min(1).max(256),
237
237
  hash: z.string().regex(/^[0-9a-f]{64}$/, "key hash must be 64 hex chars"),
238
+ /** key 名(Owner 裁决 2026-09-13:签发时必填,GUI 分组视图按名展示;
239
+ * 旧记录无此字段——迁移容忍,GUI 显示为未命名)。 */
240
+ name: z.string().min(1).max(128).optional(),
241
+ /** key 原文(Owner 裁决 2026-09-13:可随时复制取代仅签发时可见——本机
242
+ * 个人工具与密钥库明文同一威胁模型;旧记录只存哈希,无法补录)。 */
243
+ key: z.string().min(8).max(256).optional(),
238
244
  createdAt: z.number().int().min(0),
239
245
  revokedAt: z.number().int().min(0).optional()
240
246
  });
247
+ /** key 名规则(同 SECRET_NAME_SCHEMA 词汇:小写开头,点横杠下划线)。 */
248
+ const KEY_NAME_SCHEMA = /^[a-z0-9][a-z0-9._-]*$/;
241
249
  const STORE_FILE_SCHEMA = z.strictObject({
242
250
  revision: z.number().int().min(0),
243
251
  meta: z.strictObject({ alias: z.string().min(1).max(256).optional() }).optional(),
@@ -473,9 +481,11 @@ var ProviderStore = class ProviderStore {
473
481
  listKeys() {
474
482
  return this.data.keys.map((k) => ({ ...k }));
475
483
  }
476
- /** 签发:原文仅本次返回,存储只落哈希。 */
477
- issueKey(groupName) {
484
+ /** 签发:name 缺省 "default";原文随记录落库(Owner 2026-09-13:随时
485
+ * 可复制)。name 是展示标签非身份(keyId 唯一),同组可重名。 */
486
+ issueKey(groupName, name = "default") {
478
487
  if (this.getGroup(groupName) === void 0) throw new StoreError("not-found", `error: group '${groupName}' not found`);
488
+ if (!KEY_NAME_SCHEMA.test(name)) throw new StoreError("invalid", "error: key name must match /^[a-z0-9][a-z0-9._-]*$/");
479
489
  let keyId;
480
490
  do
481
491
  keyId = randomZ32(8);
@@ -486,6 +496,8 @@ var ProviderStore = class ProviderStore {
486
496
  keyId,
487
497
  group: groupName,
488
498
  hash: hashKeyMaterial(key),
499
+ name,
500
+ key,
489
501
  createdAt
490
502
  });
491
503
  this.save();
@@ -495,6 +507,12 @@ var ProviderStore = class ProviderStore {
495
507
  createdAt
496
508
  };
497
509
  }
510
+ /** key 原文取回(旧记录只存哈希 → undefined;撤销/不存在 → undefined)。 */
511
+ getKeyMaterial(keyId) {
512
+ const found = this.data.keys.find((k) => k.keyId === keyId);
513
+ if (found === void 0 || found.revokedAt !== void 0) return void 0;
514
+ return found.key;
515
+ }
498
516
  /** 撤销(幂等:已撤销为 no-op)。 */
499
517
  revokeKey(keyId) {
500
518
  const found = this.data.keys.find((k) => k.keyId === keyId);
@@ -1,8 +1,8 @@
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 { n as consumersRoot, s as listKeyrings } from "./store-DCLn0Df-.mjs";
3
+ import { n as consumersRoot, s as listKeyrings } from "./store-DOFDy1rY.mjs";
4
4
  import { n as desiredPortFor } from "./ports-pI59MtmI.mjs";
5
- import { n as testLocalService } from "./local-test-B1ys1pNo.mjs";
5
+ import { n as testLocalService } from "./local-test-BZHGo22F.mjs";
6
6
  import { homedir } from "node:os";
7
7
  //#region src/cli/commands/consumer/test.ts
8
8
  const SPEC = {
@@ -0,0 +1,12 @@
1
+ // 内建 hooks 脚本:codex —— 读 codex CLI 登录态(~/.codex/auth.json)。
2
+ // 钩子清单(按函数名命名规范被发现):
3
+ // - authHeader(ctx) -> string:ChatGPT Codex backend 的 Bearer 认证头(去前缀裸值,
4
+ // 框架按需拼 "Bearer " 由 headerSet 配置的 bearer 决定——本脚本返回裸 token)。
5
+ module.exports.authHeader = function authHeader({ homedir }) {
6
+ const { readFileSync } = require("node:fs");
7
+ const { join } = require("node:path");
8
+ const auth = JSON.parse(readFileSync(join(homedir, ".codex", "auth.json"), "utf8"));
9
+ const token = auth && auth.tokens && auth.tokens.access_token;
10
+ if (typeof token !== "string" || token === "") throw new Error("codex auth.json missing access_token");
11
+ return token;
12
+ };
package/hooks/env.cjs ADDED
@@ -0,0 +1,10 @@
1
+ // 内建 hooks 脚本:env —— 环境变量桥。
2
+ // 钩子清单:
3
+ // - authHeader(ctx) -> string:读 args.var 指定的环境变量(OPENAI_API_KEY 等)。
4
+ module.exports.authHeader = function authHeader({ args, env: ctxEnv }) {
5
+ const name = args && args.var;
6
+ if (typeof name !== "string" || name === "") throw new Error("env hook requires args.var");
7
+ const value = (ctxEnv && ctxEnv(name)) || process.env[name];
8
+ if (value === undefined || value === "") throw new Error(`env '${name}' unset`);
9
+ return value;
10
+ };
package/hooks/file.cjs ADDED
@@ -0,0 +1,27 @@
1
+ // 内建 hooks 脚本:file —— JSON 文件取值(点径)。
2
+ // 钩子清单:
3
+ // - authHeader(ctx) -> string:读 args.path(支持 ~)+ args.jsonPath(.a.b 风格);
4
+ // args.bearer 为 "true"/"1" 时返回值拼 "Bearer " 前缀。
5
+ // watch 模式示例(返回 AsyncIterable):unwatchOnClose 实现见文档。
6
+ module.exports.authHeader = function authHeader({ homedir, args }) {
7
+ const { readFileSync } = require("node:fs");
8
+ const { join } = require("node:path");
9
+ const path = args && args.path;
10
+ const jsonPath = (args && args.jsonPath) || ".";
11
+ if (typeof path !== "string" || path === "") throw new Error("file hook requires args.path");
12
+ const abs = path.startsWith("~/") ? join(homedir, path.slice(1)) : path;
13
+ let doc;
14
+ try {
15
+ doc = JSON.parse(readFileSync(abs, "utf8"));
16
+ } catch {
17
+ throw new Error("file unreadable or invalid JSON");
18
+ }
19
+ let cur = doc;
20
+ for (const seg of jsonPath.split(".").filter(Boolean)) {
21
+ if (cur === null || cur === undefined) throw new Error("json path miss");
22
+ cur = cur[seg];
23
+ }
24
+ if (typeof cur !== "string" || cur === "") throw new Error("json path value missing");
25
+ const bearer = args && (args.bearer === "true" || args.bearer === "1");
26
+ return bearer ? `Bearer ${cur}` : cur;
27
+ };
@@ -0,0 +1,11 @@
1
+ // 内建 hooks 脚本:secret —— 密钥库(SecretsStore)桥。
2
+ // 钩子清单:
3
+ // - authHeader(ctx) -> string:读 args.name 指定的密钥(ctx.secrets 访问器;
4
+ // 值只进头不回显——$secret 法则不变)。
5
+ module.exports.authHeader = function authHeader({ args, secrets }) {
6
+ const name = args && args.name;
7
+ if (typeof name !== "string" || name === "") throw new Error("secret hook requires args.name");
8
+ const value = secrets && secrets(name);
9
+ if (value === undefined || value === "") throw new Error(`secret '${name}' not found`);
10
+ return value;
11
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-fly",
3
- "version": "0.4.0-alpha.8",
3
+ "version": "0.4.0",
4
4
  "description": "ai-fly — peer-to-peer HTTP/WebSocket bridge with AI-ready presets, over OpenDWeb fabric",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "type": "module",
@@ -60,6 +60,7 @@
60
60
  "dist/*.mjs",
61
61
  "dist/*.d.ts",
62
62
  "dist/app/**",
63
+ "hooks",
63
64
  "webui/dist",
64
65
  "resources/app-icons"
65
66
  ],