@dsh-plus/access-gate 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -18000,16 +18000,16 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
18000
18000
  init_shellscript();
18001
18001
  init_json();
18002
18002
  var DiffBlock_module_default = {
18003
- "add": "obLtqa_add",
18004
- "block": "obLtqa_block",
18005
- "body": "obLtqa_body",
18006
- "copyButton": "obLtqa_copyButton",
18007
- "del": "obLtqa_del",
18008
- "expand": "obLtqa_expand",
18009
- "footer": "obLtqa_footer",
18010
- "gap": "obLtqa_gap",
18011
- "line": "obLtqa_line",
18012
- "path": "obLtqa_path"
18003
+ "add": "nVxsMW_add",
18004
+ "block": "nVxsMW_block",
18005
+ "body": "nVxsMW_body",
18006
+ "copyButton": "nVxsMW_copyButton",
18007
+ "del": "nVxsMW_del",
18008
+ "expand": "nVxsMW_expand",
18009
+ "footer": "nVxsMW_footer",
18010
+ "gap": "nVxsMW_gap",
18011
+ "line": "nVxsMW_line",
18012
+ "path": "nVxsMW_path"
18013
18013
  };
18014
18014
  //#endregion
18015
18015
  //#region ../../node_modules/.pnpm/mdast-util-to-string@4.0.0/node_modules/mdast-util-to-string/lib/index.js
@@ -59244,27 +59244,27 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
59244
59244
  chunkString: "chunkString"
59245
59245
  };
59246
59246
  //#endregion
59247
- //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-ui-primitives@0.1.3-alpha.2_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-ui-primitives/lib/markdown/CodeBlock.module.css
59247
+ //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-ui-primitives@0.1.5-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-ui-primitives/lib/markdown/CodeBlock.module.css
59248
59248
  var CodeBlock_module_default = {
59249
- "action": "folzqq_action",
59250
- "banner": "folzqq_banner",
59251
- "bannerWrap": "folzqq_bannerWrap",
59252
- "block": "folzqq_block",
59253
- "copyButton": "folzqq_copyButton",
59254
- "infostring": "folzqq_infostring",
59255
- "plain": "folzqq_plain",
59256
- "shiki": "folzqq_shiki"
59249
+ "action": "Hbqh2W_action",
59250
+ "banner": "Hbqh2W_banner",
59251
+ "bannerWrap": "Hbqh2W_bannerWrap",
59252
+ "block": "Hbqh2W_block",
59253
+ "copyButton": "Hbqh2W_copyButton",
59254
+ "infostring": "Hbqh2W_infostring",
59255
+ "plain": "Hbqh2W_plain",
59256
+ "shiki": "Hbqh2W_shiki"
59257
59257
  };
59258
59258
  //#endregion
59259
- //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-ui-primitives@0.1.3-alpha.2_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-ui-primitives/lib/markdown/MarkdownText.module.css
59259
+ //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-ui-primitives@0.1.5-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-ui-primitives/lib/markdown/MarkdownText.module.css
59260
59260
  var MarkdownText_module_default = {
59261
- "fileMention": "vrxa6G_fileMention",
59262
- "image": "vrxa6G_image",
59263
- "imageAlt": "vrxa6G_imageAlt",
59264
- "linkIcon": "vrxa6G_linkIcon",
59265
- "markdown": "vrxa6G_markdown",
59266
- "tableFill": "vrxa6G_tableFill",
59267
- "tableScroll": "vrxa6G_tableScroll"
59261
+ "fileMention": "BL43XG_fileMention",
59262
+ "image": "BL43XG_image",
59263
+ "imageAlt": "BL43XG_imageAlt",
59264
+ "linkIcon": "BL43XG_linkIcon",
59265
+ "markdown": "BL43XG_markdown",
59266
+ "tableFill": "BL43XG_tableFill",
59267
+ "tableScroll": "BL43XG_tableScroll"
59268
59268
  };
59269
59269
  //#endregion
59270
59270
  //#region ../../node_modules/.pnpm/@shikijs+langs@4.4.3/node_modules/@shikijs/langs/dist/python.mjs
@@ -59584,7 +59584,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
59584
59584
  less_default = [lang];
59585
59585
  }));
59586
59586
  //#endregion
59587
- //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-ui-primitives@0.1.3-alpha.2_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-ui-primitives/lib/index.js
59587
+ //#region ../../node_modules/.pnpm/@deepseek-ai+dsh-client-ui-primitives@0.1.5-alpha.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-client-ui-primitives/lib/index.js
59588
59588
  init_typescript();
59589
59589
  init_shellscript();
59590
59590
  init_json();
@@ -61284,6 +61284,30 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
61284
61284
  return;
61285
61285
  }
61286
61286
  }
61287
+ /** Protocols a vocabulary-rewritten image destination may carry. */
61288
+ function vocabularyImageUrl(url) {
61289
+ try {
61290
+ const protocol = new URL(url).protocol;
61291
+ return protocol === "http:" || protocol === "https:" || protocol === "blob:" || protocol === "data:" ? url : void 0;
61292
+ } catch {
61293
+ return;
61294
+ }
61295
+ }
61296
+ /**
61297
+ * The displayable source for one image destination: absolute HTTP(S) as
61298
+ * authored, otherwise the context's local-path vocabulary when it vouches for
61299
+ * the destination. Either miss leaves the authored fallback (alt text) to the
61300
+ * caller.
61301
+ * @param url - The authored markdown destination.
61302
+ * @param pathImages - Rewriting vocabulary, when the render pass has one.
61303
+ * @returns The displayable image URL, or undefined.
61304
+ */
61305
+ function imageSource(url, pathImages) {
61306
+ const remote = remoteImageUrl(sanitizeUrl(normalizeUri(url)));
61307
+ if (remote !== void 0) return remote;
61308
+ const rewritten = pathImages?.resolve(url);
61309
+ return rewritten === void 0 ? void 0 : vocabularyImageUrl(rewritten);
61310
+ }
61287
61311
  /**
61288
61312
  * Create an empty {@link ReferenceTargets}.
61289
61313
  * @returns Fresh empty maps.
@@ -61398,7 +61422,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
61398
61422
  inLink: true
61399
61423
  }), key, !anchorWrapsOnlyImages(node.children));
61400
61424
  case "linkReference": return renderLinkReference(node, key, context);
61401
- case "image": return renderImage(node.url, node.alt ?? "", key);
61425
+ case "image": return renderImage(node.url, node.alt ?? "", key, context);
61402
61426
  case "imageReference": return renderImageReference(node, key, context);
61403
61427
  case "footnoteReference": return renderFootnoteReference(node, key, context);
61404
61428
  case "definition":
@@ -61532,20 +61556,36 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
61532
61556
  return;
61533
61557
  }
61534
61558
  }
61535
- function renderImage(url, alt, key) {
61536
- const imageSrc = remoteImageUrl(sanitizeUrl(normalizeUri(url)));
61559
+ function renderImage(url, alt, key, context) {
61560
+ const imageSrc = imageSource(url, context.pathImages);
61537
61561
  if (imageSrc === void 0) return (0, react_jsx_runtime.jsx)("span", {
61538
61562
  className: MarkdownText_module_default.imageAlt,
61539
61563
  children: alt
61540
61564
  }, key);
61565
+ return (0, react_jsx_runtime.jsx)(MarkdownImage, {
61566
+ src: imageSrc,
61567
+ alt,
61568
+ destination: url
61569
+ }, `${key}:${imageSrc}`);
61570
+ }
61571
+ /** Failed loads retain the authored alt or destination; a new source remounts the image. */
61572
+ function MarkdownImage({ src, alt, destination }) {
61573
+ const [failed, setFailed] = (0, react.useState)(false);
61574
+ if (failed) return (0, react_jsx_runtime.jsx)("span", {
61575
+ className: MarkdownText_module_default.imageAlt,
61576
+ children: alt || destination
61577
+ });
61541
61578
  return (0, react_jsx_runtime.jsx)("img", {
61542
61579
  className: MarkdownText_module_default.image,
61543
- src: imageSrc,
61580
+ src,
61544
61581
  alt,
61582
+ onError: () => {
61583
+ setFailed(true);
61584
+ },
61545
61585
  loading: "lazy",
61546
61586
  decoding: "async",
61547
61587
  referrerPolicy: "no-referrer"
61548
- }, key);
61588
+ });
61549
61589
  }
61550
61590
  /** The bracketed source text a reference reverts to when its definition is missing. */
61551
61591
  function referenceSuffix(node) {
@@ -61569,7 +61609,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
61569
61609
  function renderImageReference(node, key, context) {
61570
61610
  const definition = context.targets.definitions.get(node.identifier.toUpperCase());
61571
61611
  if (definition === void 0) return `![${node.alt ?? ""}${referenceSuffix(node)}`;
61572
- return renderImage(definition.url, node.alt ?? "", key);
61612
+ return renderImage(definition.url, node.alt ?? "", key, context);
61573
61613
  }
61574
61614
  function renderFootnoteReference(node, key, context) {
61575
61615
  const id = node.identifier.toUpperCase();
@@ -61630,7 +61670,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
61630
61670
  * full parse self-heals it.
61631
61671
  */
61632
61672
  /** One settled full render: parse with math, resolve references, append the footnote section. */
61633
- function renderSettled(text, labels, fileMentions) {
61673
+ function renderSettled(text, labels, fileMentions, pathImages) {
61634
61674
  const root = parseGfmWithMath(text);
61635
61675
  const targets = createReferenceTargets();
61636
61676
  collectReferenceTargets(root.children, targets);
@@ -61638,6 +61678,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
61638
61678
  streaming: false,
61639
61679
  labels,
61640
61680
  fileMentions,
61681
+ pathImages,
61641
61682
  targets,
61642
61683
  footnoteOrder: [],
61643
61684
  footnoteCounts: /* @__PURE__ */ new Map()
@@ -61704,6 +61745,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
61704
61745
  streaming: true,
61705
61746
  labels: this.labels,
61706
61747
  fileMentions: void 0,
61748
+ pathImages: void 0,
61707
61749
  targets: frameTargets,
61708
61750
  footnoteOrder: this.frozenFootnoteOrder,
61709
61751
  footnoteCounts: this.frozenFootnoteCounts
@@ -61720,6 +61762,7 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
61720
61762
  streaming: true,
61721
61763
  labels: this.labels,
61722
61764
  fileMentions: void 0,
61765
+ pathImages: void 0,
61723
61766
  targets: frameTargets,
61724
61767
  footnoteOrder: [...this.frozenFootnoteOrder],
61725
61768
  footnoteCounts: new Map(this.frozenFootnoteCounts)
@@ -61736,13 +61779,13 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
61736
61779
  return this.lastRendered;
61737
61780
  }
61738
61781
  };
61739
- (0, react.memo)(function MarkdownText({ text, streaming = false, labels, fileMentions }) {
61782
+ (0, react.memo)(function MarkdownText({ text, streaming = false, labels, fileMentions, pathImages }) {
61740
61783
  const streamRef = (0, react.useRef)(null);
61741
61784
  const streamLabelsRef = (0, react.useRef)(labels);
61742
61785
  const children = (0, react.useMemo)(() => {
61743
61786
  if (!streaming) {
61744
61787
  streamRef.current = null;
61745
- return renderSettled(text, labels, fileMentions);
61788
+ return renderSettled(text, labels, fileMentions, pathImages);
61746
61789
  }
61747
61790
  if (streamRef.current === null || streamLabelsRef.current !== labels) {
61748
61791
  streamRef.current = new StreamingRenderer(labels);
@@ -61753,7 +61796,8 @@ XID_Start XIDS`.split(/\s/).map((p) => [w(p), p]));
61753
61796
  text,
61754
61797
  streaming,
61755
61798
  labels,
61756
- fileMentions
61799
+ fileMentions,
61800
+ pathImages
61757
61801
  ]);
61758
61802
  return (0, react_jsx_runtime.jsx)("div", {
61759
61803
  className: MarkdownText_module_default.markdown,
@@ -62111,25 +62155,29 @@ ${extra}
62111
62155
  return {
62112
62156
  enabled: value.enabled,
62113
62157
  allowedIpsText: value.allowedIps.join("\n"),
62114
- trustForwardedFor: value.trustForwardedFor
62158
+ trustForwardedFor: value.trustForwardedFor,
62159
+ autoLoginTrustedIps: value.autoLoginTrustedIps
62115
62160
  };
62116
62161
  }
62117
62162
  function toPatch(draft) {
62118
62163
  return {
62119
62164
  enabled: draft.enabled,
62120
62165
  allowedIps: draft.allowedIpsText.split("\n").map((s) => s.trim()).filter((s) => s.length > 0),
62121
- trustForwardedFor: draft.trustForwardedFor
62166
+ trustForwardedFor: draft.trustForwardedFor,
62167
+ autoLoginTrustedIps: draft.autoLoginTrustedIps
62122
62168
  };
62123
62169
  }
62124
62170
  /** reason 枚举 → 文案 key。 */
62125
62171
  const REASON_KEYS = {
62126
62172
  local: "diagLocal",
62127
- cookie: "diagCookie"
62173
+ cookie: "diagCookie",
62174
+ "trusted-ip": "diagTrustedIp"
62128
62175
  };
62129
62176
  function verdictLabel(t, diag) {
62130
62177
  if (!diag.enabled) return t("diagOff");
62131
62178
  if (diag.verdict === "pass") return t("diagPass");
62132
62179
  if (diag.verdict === "token-page") return t("diagTokenPage");
62180
+ if (diag.verdict === "autologin") return t("diagAutoLogin");
62133
62181
  return t("diagBlock");
62134
62182
  }
62135
62183
  function AccessGateCard(props) {
@@ -62254,6 +62302,14 @@ ${extra}
62254
62302
  })
62255
62303
  ]
62256
62304
  }),
62305
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckRow, {
62306
+ prefix: "dag",
62307
+ id: "dag-autologin",
62308
+ label: t("autoLoginHint"),
62309
+ checked: draft.autoLoginTrustedIps,
62310
+ disabled,
62311
+ onEdit: (v) => edit("autoLoginTrustedIps", v)
62312
+ }),
62257
62313
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CheckRow, {
62258
62314
  prefix: "dag",
62259
62315
  id: "dag-xff",
@@ -62309,6 +62365,16 @@ ${extra}
62309
62365
  children: diag.officialAuthed ? t("diagOfficialYes") : t("diagOfficialNo")
62310
62366
  })]
62311
62367
  }),
62368
+ diag.autoLoginActive ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
62369
+ className: "dag-diagRow",
62370
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
62371
+ className: "dag-diagKey",
62372
+ children: t("diagAutoLogin")
62373
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
62374
+ className: "dag-diagVal",
62375
+ children: diag.autoLoginReady ? t("diagAutoLoginReady") : t("diagAutoLoginNoSecret")
62376
+ })]
62377
+ }) : null,
62312
62378
  diag.invalidEntries.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
62313
62379
  className: "dag-diagRow",
62314
62380
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -62340,6 +62406,7 @@ ${extra}
62340
62406
  mergedHint: "登录凭据由官方认证持有(启动令牌换 30 天 cookie)。获取当前令牌:服务器执行 dshctl url,或本机访问 /dsh-plus/gate/launch-url。",
62341
62407
  allowedIps: "附加 IP 围栏白名单",
62342
62408
  allowedIpsHint: "每行一个:精确 IP 或 CIDR(IPv4/IPv6 皆可)。非空时仅白名单来源可通行与登录;留空 = 不限制来源 IP(仅官方登录保护)。",
62409
+ autoLoginHint: "IP 信任自动登录:白名单来源未认证时自动签发官方登录 cookie,免输启动令牌。开启即视为信任白名单内全部设备(tailnet 内),请确保白名单准确。",
62343
62410
  trustForwardedFor: "信任 x-forwarded-for 还原客户端 IP",
62344
62411
  trustForwardedForHint: "仅当入口代理强制覆盖该头时开启(tailscale serve 满足);直连暴露场景必须关闭。",
62345
62412
  diagTitle: "当前页面诊断",
@@ -62348,12 +62415,17 @@ ${extra}
62348
62415
  diagReason: "放行原因",
62349
62416
  diagLocal: "本机直连",
62350
62417
  diagCookie: "官方 cookie",
62418
+ diagTrustedIp: "IP 信任白名单",
62351
62419
  diagOfficial: "官方登录状态",
62352
62420
  diagOfficialYes: "已登录",
62353
62421
  diagOfficialNo: "未登录",
62354
62422
  diagPass: "放行",
62355
62423
  diagBlock: "拦截",
62356
62424
  diagTokenPage: "令牌输入页",
62425
+ diagAutoLogin: "自动登录",
62426
+ diagAutoLoginVerdict: "自动登录(免 token 进入)",
62427
+ diagAutoLoginReady: "已就绪(签名密钥可读)",
62428
+ diagAutoLoginNoSecret: "不可用(签名密钥读取失败,已降级)",
62357
62429
  diagOff: "围栏未启用",
62358
62430
  diagInvalid: "白名单存在无法解析的条目:"
62359
62431
  };
@@ -62365,6 +62437,7 @@ ${extra}
62365
62437
  mergedHint: "Credentials are owned by official auth (launch token exchange, 30-day cookie). Get the current token: run dshctl url on the server, or visit /dsh-plus/gate/launch-url locally.",
62366
62438
  allowedIps: "Additional IP fence allowlist",
62367
62439
  allowedIpsHint: "One per line: exact IP or CIDR (IPv4/IPv6). When non-empty only listed sources may pass or log in; empty = no IP restriction (official login only).",
62440
+ autoLoginHint: "Trusted-IP auto login: mint the official login cookie automatically for unauthenticated allowlisted sources, no launch token needed. Enabling trusts every allowlisted device (inside the tailnet); keep the allowlist accurate.",
62368
62441
  trustForwardedFor: "Trust x-forwarded-for for client IP",
62369
62442
  trustForwardedForHint: "Enable only when the entry proxy overwrites that header (tailscale serve does); must be off for direct exposure.",
62370
62443
  diagTitle: "This page",
@@ -62373,12 +62446,17 @@ ${extra}
62373
62446
  diagReason: "Reason",
62374
62447
  diagLocal: "loopback",
62375
62448
  diagCookie: "official cookie",
62449
+ diagTrustedIp: "trusted IP allowlist",
62376
62450
  diagOfficial: "Official login",
62377
62451
  diagOfficialYes: "authenticated",
62378
62452
  diagOfficialNo: "not authenticated",
62379
62453
  diagPass: "pass",
62380
62454
  diagBlock: "blocked",
62381
62455
  diagTokenPage: "token page",
62456
+ diagAutoLogin: "Auto login",
62457
+ diagAutoLoginVerdict: "auto login (token-free entry)",
62458
+ diagAutoLoginReady: "ready (signing secret readable)",
62459
+ diagAutoLoginNoSecret: "unavailable (signing secret unreadable, degraded)",
62382
62460
  diagOff: "gate disabled",
62383
62461
  diagInvalid: "Allowlist entries that failed to parse:"
62384
62462
  };
package/lib/index.d.ts CHANGED
@@ -5,10 +5,12 @@ declare const Config: z<Schemastery.ObjectS<{
5
5
  enabled: z<boolean, boolean>;
6
6
  allowedIps: z<string[], string[]>;
7
7
  trustForwardedFor: z<boolean, boolean>;
8
+ autoLoginTrustedIps: z<boolean, boolean>;
8
9
  }>, Schemastery.ObjectT<{
9
10
  enabled: z<boolean, boolean>;
10
11
  allowedIps: z<string[], string[]>;
11
12
  trustForwardedFor: z<boolean, boolean>;
13
+ autoLoginTrustedIps: z<boolean, boolean>;
12
14
  }>>;
13
15
  type AccessGateConfig = Schemastery.TypeT<typeof Config>;
14
16
  //#endregion
@@ -23,9 +25,17 @@ declare module '@deepseek-ai/cordis' {
23
25
  /** 围栏服务:持有当前配置读取器(settings 用户层热更新经 setSource 注入)。 */
24
26
  declare class AccessGateService extends Service {
25
27
  private current;
28
+ /** 官方 browser-auth 签名密钥(惰性读取缓存;读取失败 = null,autoLogin fail-safe 降级)。 */
29
+ private signingSecret;
26
30
  constructor(ctx: Context, config: AccessGateConfig);
27
31
  /** 当前生效配置(诊断用)。 */
28
32
  policy(): AccessGateConfig;
33
+ /** 官方签名密钥(惰性缓存;失败返回 null 并记录,autoLogin 降级 fail-safe)。 */
34
+ private readSigningSecret;
35
+ /** IP 信任自动登录可用性(诊断用):签名密钥可读即为可用。 */
36
+ autoLoginSecretReady(logger: {
37
+ warn(message: string): void;
38
+ }): boolean;
29
39
  }
30
40
  declare function apply(ctx: Context, config: AccessGateConfig): void;
31
41
  //#endregion