@dsh-plugins/dsh-llm-hub 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.en.md +6 -2
- package/README.md +11 -6
- package/lib/client.js +180 -703
- package/lib/index.js +110 -56
- package/package.json +6 -3
- package/scripts/gate-lib.mjs +38 -0
- package/scripts/prepublish-gate.mjs +2 -2
package/lib/index.js
CHANGED
|
@@ -33,12 +33,14 @@
|
|
|
33
33
|
/** 官方直连适配器拥有的 settings 命名空间。 */
|
|
34
34
|
import { readFileSync } from 'node:fs'
|
|
35
35
|
import { fileURLToPath } from 'node:url'
|
|
36
|
+
import z from '@deepseek-ai/schemastery'
|
|
36
37
|
import { applyHarness, harnessReport } from './harness.js'
|
|
37
38
|
import { detectBillingMode, sortProviders } from './sorter.js'
|
|
38
39
|
import { AvailabilityStore, isDropDeadHidden, classifyFailure } from './availability-store.js'
|
|
39
40
|
|
|
40
41
|
export { detectBillingMode, sortProviders } from './sorter.js'
|
|
41
42
|
export { AvailabilityStore, isDropDeadHidden, classifyFailure } from './availability-store.js'
|
|
43
|
+
export { Config }
|
|
42
44
|
|
|
43
45
|
/**
|
|
44
46
|
* 用量统计 domain 的 spec。
|
|
@@ -657,66 +659,70 @@ export const name = 'dsh-llm-hub'
|
|
|
657
659
|
* 用量统计走的是**局部注入**:`storageDomain` 缺席时不报错,本插件其余功能照常工作
|
|
658
660
|
* —— 没有 storageDomain 也能用余额/可用性/harness,只是没法跨会话记 token 用量。
|
|
659
661
|
*/
|
|
662
|
+
/**
|
|
663
|
+
* 本插件的配置形态(DSH 0.1.7 的 settings 契约)。
|
|
664
|
+
*
|
|
665
|
+
* 0.1.7 起配置不再运行时 `settings.register()` 注册,而是由 Loader 按**条目 id**
|
|
666
|
+
* 从 profile 的 `cordis.patch.yml` 读取;插件导出这个 `Config`,`apply(ctx, config)`
|
|
667
|
+
* 收下 `config`,页面的表单与 `settings.update(ns, patch)` 也都按它生成。
|
|
668
|
+
* 字段与 1.5.0 起的段结构一一对应,所以老 settings.yaml 里的段能原样迁进 patch。
|
|
669
|
+
*
|
|
670
|
+
* note / notes:页面上写的备注。YAML 注释在页面保存时会被整段替换冲掉,
|
|
671
|
+
* 所以说明要落成字段才留得住。notes 以 'provider/model' 为键。
|
|
672
|
+
*/
|
|
673
|
+
const groupItem = z.object({
|
|
674
|
+
id: z.string().required(),
|
|
675
|
+
label: z.string(),
|
|
676
|
+
note: z.string(),
|
|
677
|
+
primary: z.string(),
|
|
678
|
+
fallbacks: z.array(z.string()),
|
|
679
|
+
notes: z.dict(z.string())
|
|
680
|
+
})
|
|
681
|
+
// ⚠️ 三个字段都必须 .volatile():0.1.7 的 settings.update() 拒绝「没有 volatile
|
|
682
|
+
// 字段的条目」(原话:Plugin entry "dsh-llm-hub" has no volatile fields)。
|
|
683
|
+
// volatile 的语义正是本插件要的行为 —— 配置改动只换值、不重跑 apply:路由组、预警
|
|
684
|
+
// 阈值、别名全是**每次请求惰性重读**的(见 hubSectionOf 的注释),让切一次路由就去
|
|
685
|
+
// 重新 apply 整个插件是白付代价。官方 dsh-llm-pi-ai 的 providers 段同样标了 volatile。
|
|
686
|
+
const Config = z.object({
|
|
687
|
+
routing: z.object({
|
|
688
|
+
mode: z.union(['manual', 'smart']).default('manual'),
|
|
689
|
+
activeGroup: z.string().default(''),
|
|
690
|
+
groups: z.array(groupItem).default([])
|
|
691
|
+
}).default({}).volatile(),
|
|
692
|
+
warning: z.object({ cashCNY: z.number().default(10), planPercent: z.number().default(10) }).default({}).volatile(),
|
|
693
|
+
aliases: z.dict(z.string()).default({}).volatile()
|
|
694
|
+
})
|
|
695
|
+
|
|
660
696
|
export const inject = ['llm', 'webServer']
|
|
661
697
|
|
|
662
698
|
/**
|
|
663
699
|
* 注册模型发现与余额路由。
|
|
664
700
|
* @param ctx - 插件上下文。
|
|
665
701
|
*/
|
|
666
|
-
export function apply(ctx) {
|
|
702
|
+
export function apply(ctx, config) {
|
|
667
703
|
|
|
668
704
|
// ── 插件自有 settings 命名空间(dsh-llm-hub)────────────────────────────
|
|
669
705
|
//
|
|
670
|
-
// 1.
|
|
671
|
-
//
|
|
672
|
-
//
|
|
673
|
-
//
|
|
674
|
-
//
|
|
675
|
-
//
|
|
676
|
-
// (schemastery 接受原生构造器作为 schema)。注册失败不 fatal:配置读取退
|
|
677
|
-
// 回 legacy 路径,智能路由降级为「只建议」。
|
|
678
|
-
let hubRegistered = false
|
|
679
|
-
ctx.inject(['settings'], (settingsCtx) => {
|
|
680
|
-
void (async () => {
|
|
681
|
-
let schema
|
|
682
|
-
try {
|
|
683
|
-
const mod = await import('@deepseek-ai/schemastery')
|
|
684
|
-
const z = (mod && typeof mod === 'object' ? mod.default ?? mod : mod)
|
|
685
|
-
// note / notes(1.7.0):页面上写的备注。YAML 注释在页面保存时会被整段替换冲掉,
|
|
686
|
-
// 所以说明要落成字段才留得住。notes 以 'provider/model' 为键。
|
|
687
|
-
const groupItem = z.object({ id: z.string().required(), label: z.string(), note: z.string(), primary: z.string(), fallbacks: z.array(z.string()), notes: z.dict(z.string()) })
|
|
688
|
-
schema = z.object({
|
|
689
|
-
routing: z.object({
|
|
690
|
-
mode: z.union(['manual', 'smart']).default('manual'),
|
|
691
|
-
activeGroup: z.string().default(''),
|
|
692
|
-
groups: z.array(groupItem).default([])
|
|
693
|
-
}).default({}),
|
|
694
|
-
warning: z.object({ cashCNY: z.number().default(10), planPercent: z.number().default(10) }).default({}),
|
|
695
|
-
aliases: z.dict(z.string()).default({})
|
|
696
|
-
})
|
|
697
|
-
} catch {
|
|
698
|
-
// 零依赖环境:schemastery 没解析到 —— 用原生构造器兜底。
|
|
699
|
-
schema = Object
|
|
700
|
-
}
|
|
701
|
-
try {
|
|
702
|
-
settingsCtx.settings.register(HUB_NS, schema)
|
|
703
|
-
hubRegistered = true
|
|
704
|
-
} catch (error) {
|
|
705
|
-
ctx.logger.warn(`dsh-llm-hub: settings ns "dsh-llm-hub" register failed: ${error?.message ?? error}`)
|
|
706
|
-
}
|
|
707
|
-
})()
|
|
708
|
-
})
|
|
706
|
+
// DSH 0.1.7 把 settings 架构整个换了:`settings.register(ns, schema)` 与
|
|
707
|
+
// `settings.get(ns)` 双双消失,配置改由 Loader 按**条目 id**从 profile 的
|
|
708
|
+
// cordis.patch.yml 读,插件自己导出 `Config`,再由 `apply(ctx, config)` 收下。
|
|
709
|
+
// 老写法在 0.1.7 上是 `TypeError: settings.register is not a function`,
|
|
710
|
+
// 被下面那个 catch 吞掉 → hubRegistered 恒 false → 整个 dsh-llm-hub 段读写
|
|
711
|
+
// 全死。对外表现是「路由组凭空消失」:数据好好躺在 patch 里,只是没人读。
|
|
709
712
|
|
|
710
713
|
/**
|
|
711
714
|
* 读 `dsh-llm-hub` 设置段(1.5.0 起的真源)。
|
|
712
715
|
* @returns 已解析设置段;未注册 / 未配置时为 `undefined`。
|
|
713
716
|
*/
|
|
714
717
|
const hubSectionOf = () => {
|
|
715
|
-
|
|
716
|
-
|
|
718
|
+
// 0.1.7:配置由 `apply(ctx, config)` 注入,惰性 .get() 每次拿当前修订。
|
|
719
|
+
// 不再走 settings.get(HUB_NS) —— 那个方法在 0.1.7 已被移除。
|
|
720
|
+
if (config === undefined || config === null) return undefined
|
|
717
721
|
try {
|
|
718
|
-
const
|
|
719
|
-
|
|
722
|
+
const routing = config.routing?.get?.()
|
|
723
|
+
const warning = config.warning?.get?.()
|
|
724
|
+
const aliases = config.aliases?.get?.()
|
|
725
|
+
return { routing, warning, aliases }
|
|
720
726
|
} catch {
|
|
721
727
|
return undefined
|
|
722
728
|
}
|
|
@@ -729,13 +735,31 @@ export function apply(ctx) {
|
|
|
729
735
|
* @returns 已解析的设置段;不可用时为 `undefined`。
|
|
730
736
|
*/
|
|
731
737
|
const sectionOf = () => {
|
|
738
|
+
// 0.1.7:读**别的插件**的配置不再有 settings.get(ns)。改走
|
|
739
|
+
// settings.describe() —— 它返回所有条目的 ns + 实时 value。
|
|
732
740
|
const settings = ctx.get('settings')
|
|
733
741
|
if (settings === undefined) return undefined
|
|
734
742
|
try {
|
|
735
|
-
const
|
|
743
|
+
const row = settings.describe().find((item) => item.ns === NS)
|
|
744
|
+
const value = row?.value
|
|
745
|
+
return value !== null && typeof value === 'object' ? value : undefined
|
|
746
|
+
} catch {
|
|
747
|
+
return undefined
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* 读任意命名空间(跨插件)在 0.1.7 的唯一途径。
|
|
753
|
+
* @param {string} ns - profile 条目 id。
|
|
754
|
+
* @returns {object | undefined} 该条目的实时配置值。
|
|
755
|
+
*/
|
|
756
|
+
const sectionOfNs = (ns) => {
|
|
757
|
+
const settings = ctx.get('settings')
|
|
758
|
+
if (settings === undefined) return undefined
|
|
759
|
+
try {
|
|
760
|
+
const value = settings.describe().find((item) => item.ns === ns)?.value
|
|
736
761
|
return value !== null && typeof value === 'object' ? value : undefined
|
|
737
762
|
} catch {
|
|
738
|
-
// 命名空间尚未注册时会抛 —— 那是正常启动竞态,不是失败。
|
|
739
763
|
return undefined
|
|
740
764
|
}
|
|
741
765
|
}
|
|
@@ -1025,13 +1049,13 @@ export function apply(ctx) {
|
|
|
1025
1049
|
try {
|
|
1026
1050
|
const raw = await getJson('/user/balance', { apiKey })
|
|
1027
1051
|
const shaped = shapeBalance(raw)
|
|
1028
|
-
const
|
|
1052
|
+
const activeBalance = shaped.balances?.find((b) => Number(b.total) > 0) ?? shaped.balances?.[0]
|
|
1029
1053
|
return {
|
|
1030
1054
|
ref,
|
|
1031
1055
|
keyMasked: maskKey(apiKey),
|
|
1032
1056
|
isAvailable: shaped.isAvailable,
|
|
1033
|
-
total:
|
|
1034
|
-
currency:
|
|
1057
|
+
total: activeBalance?.total ?? '0.00',
|
|
1058
|
+
currency: activeBalance?.currency ?? 'CNY',
|
|
1035
1059
|
ok: true
|
|
1036
1060
|
}
|
|
1037
1061
|
} catch (e) {
|
|
@@ -1125,7 +1149,7 @@ export function apply(ctx) {
|
|
|
1125
1149
|
const settings = ctx.get('settings')
|
|
1126
1150
|
if (settings === undefined) return []
|
|
1127
1151
|
try {
|
|
1128
|
-
const section =
|
|
1152
|
+
const section = sectionOfNs(PIAI_NS)
|
|
1129
1153
|
const providers = section !== null && typeof section === 'object' ? section.providers : undefined
|
|
1130
1154
|
if (providers === null || typeof providers !== 'object') return []
|
|
1131
1155
|
return Object.entries(providers).filter(([, profile]) => profile !== null && typeof profile === 'object')
|
|
@@ -1145,7 +1169,7 @@ export function apply(ctx) {
|
|
|
1145
1169
|
const settings = ctx.get('settings')
|
|
1146
1170
|
if (settings === undefined) return undefined
|
|
1147
1171
|
try {
|
|
1148
|
-
const section =
|
|
1172
|
+
const section = sectionOfNs(PIAI_NS)
|
|
1149
1173
|
const providers = section !== null && typeof section === 'object' ? section.providers : undefined
|
|
1150
1174
|
const profile = providers !== null && typeof providers === 'object' ? providers[provider] : undefined
|
|
1151
1175
|
return profile !== null && typeof profile === 'object' ? profile : undefined
|
|
@@ -1849,7 +1873,25 @@ export function apply(ctx) {
|
|
|
1849
1873
|
}
|
|
1850
1874
|
// 已经被新一轮取代:这份结论过期了,丢掉。
|
|
1851
1875
|
if (generation !== probeGeneration) return
|
|
1852
|
-
|
|
1876
|
+
let modelIds = []
|
|
1877
|
+
if (target.ns === NS) {
|
|
1878
|
+
const deepseek = sectionOf()
|
|
1879
|
+
const models = Array.isArray(deepseek?.models) ? deepseek.models : []
|
|
1880
|
+
modelIds = models.map((m) => (m !== null && typeof m === 'object' ? m.id : m)).filter((id) => typeof id === 'string')
|
|
1881
|
+
} else {
|
|
1882
|
+
const profile = piaiProfileOf(target.id)
|
|
1883
|
+
const models = Array.isArray(profile?.models) ? profile.models : []
|
|
1884
|
+
modelIds = models.map((m) => (m !== null && typeof m === 'object' ? m.id : m)).filter((id) => typeof id === 'string')
|
|
1885
|
+
}
|
|
1886
|
+
baseVerdicts.set(target.id, {
|
|
1887
|
+
provider: target.id,
|
|
1888
|
+
displayName: target.displayName,
|
|
1889
|
+
ns: target.ns,
|
|
1890
|
+
checkedAt: Date.now(),
|
|
1891
|
+
modelCount: modelIds.length,
|
|
1892
|
+
modelIds,
|
|
1893
|
+
...outcome
|
|
1894
|
+
})
|
|
1853
1895
|
}))
|
|
1854
1896
|
if (generation !== probeGeneration) return
|
|
1855
1897
|
// 清掉真正从路由表里消失的 provider。判据必须是**原始路由表**,不是本轮的
|
|
@@ -2204,10 +2246,10 @@ export function apply(ctx) {
|
|
|
2204
2246
|
// modelgo 这种 pi-ai 风格 provider 在 'llm-pi-ai' namespace 下;
|
|
2205
2247
|
// dsh-llm-hub 自己的 NS 是 'llm-deepseek',但写回属于跨 namespace 兜底:
|
|
2206
2248
|
// plugin 不是 pi-ai 的 owner,只是借用 settings 服务给 pi-ai 段补一份目录。
|
|
2207
|
-
const source =
|
|
2249
|
+
const source = sectionOfNs(PIAI_NS)
|
|
2208
2250
|
const updates = await populateEmptyProviderModels(source)
|
|
2209
2251
|
if (Object.keys(updates).length === 0) return
|
|
2210
|
-
const current =
|
|
2252
|
+
const current = sectionOfNs(PIAI_NS)
|
|
2211
2253
|
for (const provider of Object.keys(updates)) {
|
|
2212
2254
|
const live = current?.providers?.[provider]
|
|
2213
2255
|
if (Array.isArray(live?.models) && live.models.length > 0) {
|
|
@@ -2812,7 +2854,7 @@ export function apply(ctx) {
|
|
|
2812
2854
|
/** 读 settings 的 routing 段并按需覆写 mode / activeGroup,持久化。 */
|
|
2813
2855
|
const applyRoutingSelect = async (patch) => {
|
|
2814
2856
|
const settings = ctx.get('settings')
|
|
2815
|
-
if (settings === undefined
|
|
2857
|
+
if (settings === undefined) {
|
|
2816
2858
|
return { ok: false, error: 'settings 命名空间 dsh-llm-hub 尚未就绪 —— 稍后重试' }
|
|
2817
2859
|
}
|
|
2818
2860
|
const current = routingObjOf()
|
|
@@ -2930,7 +2972,7 @@ export function apply(ctx) {
|
|
|
2930
2972
|
/** 写回整份路由组(设置页编辑器保存路径),并返回最新状态。 */
|
|
2931
2973
|
const applyRoutingConfig = async (payload) => {
|
|
2932
2974
|
const settings = ctx.get('settings')
|
|
2933
|
-
if (settings === undefined
|
|
2975
|
+
if (settings === undefined) {
|
|
2934
2976
|
return { ok: false, error: 'settings 命名空间 dsh-llm-hub 尚未就绪 —— 稍后重试' }
|
|
2935
2977
|
}
|
|
2936
2978
|
const normalized = normalizeRoutingGroupsInput(payload?.groups)
|
|
@@ -3173,6 +3215,18 @@ export function apply(ctx) {
|
|
|
3173
3215
|
|
|
3174
3216
|
// ── harness 子代理 ────────────────────────────────────────────────────────
|
|
3175
3217
|
|
|
3218
|
+
// 把本机装好的外部 agent CLI(codex / claude / agy)注册成子代理提供方,详见
|
|
3219
|
+
// `./harness.js`。
|
|
3220
|
+
//
|
|
3221
|
+
// 为什么用**局部注入**而不是加进模块顶部的 `inject` 数组:`inject` 是硬依赖,
|
|
3222
|
+
// 少一个服务整个插件就不加载 —— 那会让「宿主没装子代理栈」的部署连余额和模型
|
|
3223
|
+
// 发现一起失去。局部注入只让 harness 这一段跟着服务走,缺了就不启用。
|
|
3224
|
+
ctx.inject(['subagents', 'subprocess'], (scope) => {
|
|
3225
|
+
applyHarness(scope).catch((error) => {
|
|
3226
|
+
scope.logger?.warn?.(`dsh-llm-hub: harness 探测失败:${error?.message ?? error}`)
|
|
3227
|
+
})
|
|
3228
|
+
})
|
|
3229
|
+
|
|
3176
3230
|
// 自检路由:`GET /api/dsh-llm-hub/harness` 报告探到了什么、注册了几个。
|
|
3177
3231
|
//
|
|
3178
3232
|
// 为什么需要它:宿主的 `info` 级日志**不落盘**(实测 launchd.log 里只有
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-plugins/dsh-llm-hub",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "DSH 模型管理层:网关可达性探测、模型目录拉取、DeepSeek + MiniMax/智谱/Moonshot/StepFun 余额与配额、本月用量统计、余额预警、Provider 健康看板,再把本机装好的 codex / claude / agy 注册成子代理(装了才出现)。零运行时依赖,不改动 DSH 安装。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"scripts": {
|
|
31
|
-
"deploy": "rsync -av --delete lib/ ~/.dsh/profiles/
|
|
31
|
+
"deploy": "rsync -av --delete lib/ ~/.dsh/profiles/${DSH_PROFILE:-desktop}/node_modules/@dsh-plugins/dsh-llm-hub/lib/ && rsync -av types package.json cordis.patch.yml README.md README.en.md LICENSE ~/.dsh/profiles/${DSH_PROFILE:-desktop}/node_modules/@dsh-plugins/dsh-llm-hub/",
|
|
32
32
|
"preview": "npm run deploy && open http://127.0.0.1:3080",
|
|
33
33
|
"check": "for f in lib/*.js; do node --check \"$f\" || exit 1; done",
|
|
34
34
|
"test": "node --test",
|
|
@@ -79,5 +79,8 @@
|
|
|
79
79
|
"lib",
|
|
80
80
|
"scripts",
|
|
81
81
|
"types"
|
|
82
|
-
]
|
|
82
|
+
],
|
|
83
|
+
"devDependencies": {
|
|
84
|
+
"@deepseek-ai/schemastery": "3.18.4"
|
|
85
|
+
}
|
|
83
86
|
}
|
package/scripts/gate-lib.mjs
CHANGED
|
@@ -86,3 +86,41 @@ export function detectChangedRuntime(prevRoot, curRoot, curFiles) {
|
|
|
86
86
|
}
|
|
87
87
|
return changed
|
|
88
88
|
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 取 `npm pack --json` 的那一条结果 —— 两种 npm 大版本形状都认。
|
|
92
|
+
*
|
|
93
|
+
* **npm 11 → 12 的破坏性变更**:11 回数组 `[{ files: [...] }]`,
|
|
94
|
+
* 12 回以包名为键的对象 `{ "<name>": { files: [...] } }`。
|
|
95
|
+
* 原先写死 `JSON.parse(out)[0].files`,在 npm 12 上 `[0]` 是 undefined,
|
|
96
|
+
* 报出来的是 `Cannot read properties of undefined (reading 'files')`
|
|
97
|
+
* —— 从这句话完全看不出跟 npm 版本有任何关系。
|
|
98
|
+
*
|
|
99
|
+
* 2026-10-02 同一天咬了两次(museav-cli v3.9.2、museav-mcp 2.2.0):各仓的
|
|
100
|
+
* publish workflow 里写着 `npm install -g npm@latest`,于是 npm 12 一发版,
|
|
101
|
+
* **所有仓的 CI 发版同时挂掉**,而本地(还是 npm 11)怎么跑都是绿的。
|
|
102
|
+
*
|
|
103
|
+
* 抽进 lib 是为了能测(门禁脚本顶层会跑 pack/连 registry,import 不进来)。
|
|
104
|
+
* 形状认不出时**抛一句能看懂的话** —— 这正是今天最缺的东西:当时那句
|
|
105
|
+
* `Cannot read properties of undefined` 让人以为是代码问题,实际是上游换了形状。
|
|
106
|
+
*/
|
|
107
|
+
export function packEntry(parsed) {
|
|
108
|
+
// 形状先记下来再判空:报错里要能看出 npm 回了什么,而不是只说「认不出」。
|
|
109
|
+
const shape = Array.isArray(parsed) ? 'array' : parsed === null ? 'null' : typeof parsed
|
|
110
|
+
// 先判空再 Object.values —— 否则 parsed 为 null/undefined 时
|
|
111
|
+
// Object.values 先抛「Cannot convert undefined or null to object」,
|
|
112
|
+
// 又回到那个「看不出是 npm 换了形状」的老问题上。
|
|
113
|
+
let entry
|
|
114
|
+
if (Array.isArray(parsed)) {
|
|
115
|
+
entry = parsed[0]
|
|
116
|
+
} else if (parsed && typeof parsed === 'object') {
|
|
117
|
+
entry = Object.values(parsed)[0]
|
|
118
|
+
}
|
|
119
|
+
if (!entry || !Array.isArray(entry.files)) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`npm pack --json 的返回形状认不出(顶层是 ${shape})` +
|
|
122
|
+
`,npm 可能又换了形状 —— 见 gate-lib.mjs 的 packEntry 注释`,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
return entry
|
|
126
|
+
}
|
|
@@ -31,7 +31,7 @@ import { dirname, join, relative } from 'node:path'
|
|
|
31
31
|
import { fileURLToPath } from 'node:url'
|
|
32
32
|
import { tmpdir } from 'node:os'
|
|
33
33
|
import { createHash } from 'node:crypto'
|
|
34
|
-
import { EXCLUDE, isRuntime, collectAllFiles, detectChangedRuntime } from './gate-lib.mjs'
|
|
34
|
+
import { EXCLUDE, isRuntime, collectAllFiles, detectChangedRuntime, packEntry } from './gate-lib.mjs'
|
|
35
35
|
|
|
36
36
|
const GATE_VERSION = 1
|
|
37
37
|
|
|
@@ -48,7 +48,7 @@ try {
|
|
|
48
48
|
const out = execFileSync('npm', ['pack', '--dry-run', '--json'], {
|
|
49
49
|
cwd: pkgRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'inherit']
|
|
50
50
|
})
|
|
51
|
-
packFiles = JSON.parse(out)
|
|
51
|
+
packFiles = packEntry(JSON.parse(out)).files.map(f => f.path)
|
|
52
52
|
} catch (err) {
|
|
53
53
|
console.error('[prepublish-gate] npm pack --dry-run 失败:', err.message)
|
|
54
54
|
process.exit(2)
|