@wangnanshan/dsh-nanshan-publish-manager 1.0.4 → 1.0.5

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 (4) hide show
  1. package/README.md +3 -1
  2. package/client.js +165 -1
  3. package/index.js +74 -0
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  把本机开发的 DSH 组件,按 DeepSeek Harness 官方推荐的 npm 包形式发布给其他人安装。
4
4
 
5
5
  - **一个组件清单页**:卡片形式展示本机所有可发布的 DSH 组件(`dsh.bundle` / `dsh.client`),包含版本、发布状态、预检查结果和最近一次发布。
6
- - **一个组件详情页**:点击卡片进入,用「流程化板块」完成发布——版本与标签 → 发布目标与选项 → 发布预检查 → 确认与执行,右侧常驻该组件的**发布记录列表**。
6
+ - **一个组件详情页**:点击卡片进入,用「流程化板块」完成发布——版本与标签 → 发布目标与选项 → 发布预检查 → 确认与执行,右侧常驻该组件的 **NPM信息查询**、**发布记录列表**。
7
7
 
8
8
  界面采用现代极简的开发者工具控制台风格(Linear / Vercel / Raycast 一类的浅色克制审美),跟随 Harness 的浅色 / 深色主题。插件图标、侧栏入口和页面标识使用 3D 液态玻璃黑色调;搜索、勾选、关闭等功能图标保持线性,以保证小尺寸下的可读性。
9
9
 
@@ -86,6 +86,7 @@ DSH 组件就是一个 npm 包:
86
86
  3. **发布预检查** —— 运行发布预检查;有阻断项时正式发布按钮不可用。
87
87
  4. **确认与执行** —— 选择发布验证方式,勾选确认,先「仅演练」(`npm publish --dry-run`,临时写入的目标版本会在演练后还原)或「正式发布」(带最终确认弹窗);本机会话方式的最终弹窗按钮为:左侧「再想想」,右侧「采用终端发布(推荐)」和「采用 OTP 发布」。
88
88
  5. **发布记录** —— 每次演练 / 发布 / 失败都会写入本机记录,包含版本、tag、access、验证方式、耗时、预检查摘要和命令输出;终端发布会在记录里标记「终端」。
89
+ 6. **NPM信息查询** —— 右侧栏显示当前包名、近 7 天每日下载量柱状图(数据来自 npm downloads API)和「NPM查看该组件」入口。
89
90
 
90
91
  ## 发布验证方式
91
92
 
@@ -225,5 +226,6 @@ HTTP 接口(仅本机、同源写入):
225
226
  - 发布使用本机 npm 配置,不支持在页面里输入 token;OTP 只用于单次发布,不落盘。
226
227
  - 系统终端发布支持 macOS Terminal、Windows `cmd.exe` 和 Linux 常见终端;Windows 的 `npm.cmd` 已做启动适配,但当前环境主要在 macOS 验证。
227
228
  - 终端发布最多等待 15 分钟;关闭终端或未完成验证会超时,不会写入成功记录;终端输出不写入插件记录。
229
+ - 近 7 天下载量来自 npm downloads API,统计通常滞后约 1 天;新包或未发布包可能暂无数据。
228
230
  - 页面直接执行 `npm publish`,请在发布前用「仅演练」确认打包内容。
229
231
  - `npm publish` 会执行组件 `package.json` 中的 publish 生命周期脚本;请只发布你信任的组件。
package/client.js CHANGED
@@ -66,6 +66,15 @@ window.__ModuleLoader__.load({
66
66
  'badge.host': 'Host',
67
67
  'badge.bundlePatch': '含 patch',
68
68
  'detail.records': '发布记录',
69
+ 'npmInfo.title': 'NPM信息查询',
70
+ 'npmInfo.open': 'NPM查看该组件',
71
+ 'npmInfo.hint': '在 npm 网站查看版本、README 与安装信息;未发布时可能显示 404。',
72
+ 'npmInfo.last7': '近 7 天下载',
73
+ 'npmInfo.chartLabel': '近 7 天每日下载量柱形图',
74
+ 'npmInfo.loading': '查询中…',
75
+ 'npmInfo.empty': '暂无下载数据(新包或未发布)',
76
+ 'npmInfo.unavailable': '下载数据暂时不可用',
77
+ 'npmInfo.source': '数据来源:npm downloads API',
69
78
  'detail.synopsis': '发布状态',
70
79
  'detail.noRecords': '还没有发布记录',
71
80
  'detail.noRecordsHint': '完成第一次演练或正式发布后,记录会按时间出现在这里。',
@@ -393,6 +402,15 @@ window.__ModuleLoader__.load({
393
402
  'badge.host': 'Host',
394
403
  'badge.bundlePatch': 'patch',
395
404
  'detail.records': 'Publish history',
405
+ 'npmInfo.title': 'npm information',
406
+ 'npmInfo.open': 'View this component on npm',
407
+ 'npmInfo.hint': 'View versions, README and install info on npm; a 404 means it is not published yet.',
408
+ 'npmInfo.last7': 'Downloads · last 7 days',
409
+ 'npmInfo.chartLabel': 'Daily downloads over the last 7 days',
410
+ 'npmInfo.loading': 'Loading…',
411
+ 'npmInfo.empty': 'No download data yet (new or unpublished package)',
412
+ 'npmInfo.unavailable': 'Download data is temporarily unavailable',
413
+ 'npmInfo.source': 'Source: npm downloads API',
396
414
  'detail.synopsis': 'Release status',
397
415
  'detail.noRecords': 'No publish records yet',
398
416
  'detail.noRecordsHint': 'After the first dry run or publish, entries appear here in order.',
@@ -697,7 +715,7 @@ window.__ModuleLoader__.load({
697
715
  .np__title{margin:0;font-size:23px;font-weight:600;line-height:1.25;letter-spacing:-.02em}
698
716
  .np__subtitle{margin:4px 0 0;max-width:680px;font-size:13px;line-height:1.6;color:var(--np-ink-soft)}
699
717
  .np__actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
700
- .np-btn{appearance:none;display:inline-flex;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 12px;border:1px solid var(--np-line-strong);border-radius:8px;background:var(--np-sheet);color:var(--np-ink);font:inherit;font-size:13px;font-weight:500;line-height:1;cursor:pointer;box-shadow:var(--np-shadow);transition:background-color .14s ease,border-color .14s ease,color .14s ease,opacity .14s ease;white-space:nowrap}
718
+ .np-btn{appearance:none;display:inline-flex;text-decoration:none;align-items:center;justify-content:center;gap:6px;height:32px;padding:0 12px;border:1px solid var(--np-line-strong);border-radius:8px;background:var(--np-sheet);color:var(--np-ink);font:inherit;font-size:13px;font-weight:500;line-height:1;cursor:pointer;box-shadow:var(--np-shadow);transition:background-color .14s ease,border-color .14s ease,color .14s ease,opacity .14s ease;white-space:nowrap}
701
719
  .np-btn:hover:not(:disabled){background:var(--np-sheet2);border-color:var(--np-line-strong)}
702
720
  .np-btn:active:not(:disabled){background:var(--np-sheet2)}
703
721
  .np-btn:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}
@@ -856,7 +874,24 @@ window.__ModuleLoader__.load({
856
874
  .np-result__close:focus-visible{outline:2px solid var(--np-bad);outline-offset:2px}
857
875
  .np-result__cmd{padding:7px 11px;border-bottom:1px solid var(--np-line);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;line-height:1.55;color:var(--np-ink-soft);word-break:break-all}
858
876
  .np-log{margin:0;padding:10px 12px;max-height:260px;overflow:auto;white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;line-height:1.55;color:var(--np-ink-soft)}
877
+ .np-downloads{display:flex;flex-direction:column;gap:8px;padding-top:2px}
878
+ .np-downloads__head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;font-size:11.5px;color:var(--np-ink-soft)}
879
+ .np-downloads__head strong{font-size:14px;font-weight:600;color:var(--np-ink);font-variant-numeric:tabular-nums}
880
+ .np-downloads__loading{font-size:11px;color:var(--np-ink-soft)}
881
+ .np-bars{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));align-items:end;gap:5px;height:96px;padding-top:14px}
882
+ .np-bar{display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:3px;min-width:0}
883
+ .np-bar__value{font-size:9px;line-height:1;color:var(--np-ink-soft);font-variant-numeric:tabular-nums;white-space:nowrap}
884
+ .np-bar__track{display:flex;align-items:flex-end;width:100%;max-width:18px;height:62px;overflow:hidden;border-radius:3px;background:var(--np-sheet2)}
885
+ .np-bar__fill{display:block;width:100%;min-height:2px;border-radius:3px 3px 0 0;background:var(--np-accent);opacity:.9}
886
+ .np-bar__label{font-size:9px;line-height:1;color:var(--np-ink-soft);white-space:nowrap}
887
+ .np-downloads__hint{font-size:10px;line-height:1.5;color:var(--np-ink-soft)}
888
+ .np-bars--skeleton{height:96px;padding-top:14px}
889
+ .np-bar__skeleton{display:block;width:100%;max-width:18px;height:62px;align-self:end;justify-self:center;border-radius:3px;background:linear-gradient(100deg,var(--np-sheet2) 20%,var(--np-line) 45%,var(--np-sheet2) 70%);background-size:200% 100%;animation:np-shimmer 1.5s linear infinite}
859
890
  .np-railcard{position:relative;padding:16px;background:var(--np-sheet);border:1px solid var(--np-line);border-radius:12px;box-shadow:var(--np-shadow)}
891
+ .np-railcard__body{display:flex;flex-direction:column;gap:8px}
892
+ .np-railcard__pkg{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11.5px;color:var(--np-ink-soft);word-break:break-all}
893
+ .np-railcard__hint{font-size:11px;line-height:1.55;color:var(--np-ink-soft)}
894
+ .np-railcard__action{width:100%;justify-content:center}
860
895
  .np-railcard__title{display:flex;align-items:center;justify-content:space-between;gap:8px;margin:0 0 12px;font-size:13px;font-weight:600;line-height:1.3}
861
896
  .np-dl{display:grid;grid-template-columns:auto minmax(0,1fr);gap:8px 12px;margin:0;font-size:12px;line-height:1.5}
862
897
  .np-dl dt{color:var(--np-ink-soft)}
@@ -977,6 +1012,19 @@ window.__ModuleLoader__.load({
977
1012
  return text
978
1013
  }
979
1014
 
1015
+ function formatCount(value) {
1016
+ const n = Number(value) || 0
1017
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(n >= 10_000_000 ? 0 : 1)}M`
1018
+ if (n >= 10_000) return `${Math.round(n / 1000)}k`
1019
+ if (n >= 1000) return `${(n / 1000).toFixed(1)}k`
1020
+ return String(n)
1021
+ }
1022
+
1023
+ function shortDay(value) {
1024
+ const text = String(value || '')
1025
+ return text.length >= 10 ? text.slice(5, 10) : text
1026
+ }
1027
+
980
1028
  function formatDateTime(value) {
981
1029
  const date = new Date(value)
982
1030
  if (Number.isNaN(date.getTime())) return '—'
@@ -1260,6 +1308,10 @@ window.__ModuleLoader__.load({
1260
1308
  ['rect', { x: 2.2, y: 3.2, width: 11.6, height: 9.6, rx: 1.6 }],
1261
1309
  ['path', { d: 'm4.8 6.1 2.4 2.2-2.4 2.2M8.6 10.5h2.8' }],
1262
1310
  ],
1311
+ external: [
1312
+ ['path', { d: 'M9.4 2.8H13v3.6M13 2.8 8.2 7.6' }],
1313
+ ['path', { d: 'M12 9.2v2.2a1.6 1.6 0 0 1-1.6 1.6H3.8A1.6 1.6 0 0 1 2.2 11.4V4.8a1.6 1.6 0 0 1 1.6-1.6h2.4' }],
1314
+ ],
1263
1315
  pencil: [
1264
1316
  ['path', { d: 'M3 13l.9-3.1 6.3-6.3 2.2 2.2-6.3 6.3L3 13Z' }],
1265
1317
  ['path', { d: 'M9.6 4.2 11.8 6.4' }],
@@ -1375,6 +1427,69 @@ window.__ModuleLoader__.load({
1375
1427
  )
1376
1428
  }
1377
1429
 
1430
+ function DownloadBars(props) {
1431
+ const [t] = useText()
1432
+ const status = props.status || 'loading'
1433
+ const points = Array.isArray(props.points) ? props.points : []
1434
+ const total = Number(props.total) || 0
1435
+ if (status === 'loading') {
1436
+ return h('div', { className: 'np-downloads' },
1437
+ h('div', { className: 'np-downloads__head' },
1438
+ h('span', null, t('npmInfo.last7')),
1439
+ h('span', { className: 'np-downloads__loading' }, t('npmInfo.loading')),
1440
+ ),
1441
+ h('div', { className: 'np-bars np-bars--skeleton', 'aria-hidden': 'true' },
1442
+ Array.from({ length: 7 }, (_, index) => h('span', { key: index, className: 'np-bar__skeleton' })),
1443
+ ),
1444
+ )
1445
+ }
1446
+ if (status === 'unavailable' || status === 'error') {
1447
+ return h('div', { className: 'np-downloads' },
1448
+ h('div', { className: 'np-downloads__head' }, h('span', null, t('npmInfo.last7'))),
1449
+ h('span', { className: 'np-downloads__hint', title: props.error || undefined }, t('npmInfo.unavailable')),
1450
+ )
1451
+ }
1452
+ if (status === 'empty' || points.length === 0) {
1453
+ return h('div', { className: 'np-downloads' },
1454
+ h('div', { className: 'np-downloads__head' },
1455
+ h('span', null, t('npmInfo.last7')),
1456
+ h('strong', null, '0'),
1457
+ ),
1458
+ h('span', { className: 'np-downloads__hint' }, t('npmInfo.empty')),
1459
+ )
1460
+ }
1461
+ const max = Math.max(1, ...points.map((point) => Number(point.downloads) || 0))
1462
+ return h('div', { className: 'np-downloads' },
1463
+ h('div', { className: 'np-downloads__head' },
1464
+ h('span', null, t('npmInfo.last7')),
1465
+ h('strong', null, formatCount(total)),
1466
+ ),
1467
+ h('div', {
1468
+ className: 'np-bars',
1469
+ role: 'img',
1470
+ 'aria-label': t('npmInfo.chartLabel'),
1471
+ style: { gridTemplateColumns: `repeat(${Math.max(points.length, 1)},minmax(0,1fr))` },
1472
+ },
1473
+ points.map((point, index) => {
1474
+ const value = Number(point.downloads) || 0
1475
+ const height = Math.max(2, Math.round((value / max) * 100))
1476
+ return h('div', {
1477
+ key: point.day || index,
1478
+ className: 'np-bar',
1479
+ title: `${point.day}: ${formatCount(value)}`,
1480
+ },
1481
+ h('span', { className: 'np-bar__value' }, value > 0 ? formatCount(value) : ''),
1482
+ h('span', { className: 'np-bar__track' },
1483
+ h('span', { className: 'np-bar__fill', style: { height: `${height}%` } }),
1484
+ ),
1485
+ h('span', { className: 'np-bar__label' }, shortDay(point.day)),
1486
+ )
1487
+ }),
1488
+ ),
1489
+ h('span', { className: 'np-downloads__hint' }, t('npmInfo.source')),
1490
+ )
1491
+ }
1492
+
1378
1493
  function Toast(props) {
1379
1494
  if (!props.toast) return null
1380
1495
  return h('div', { className: `np-toast np-toast--${props.toast.kind}`, role: 'status', 'aria-live': 'polite' },
@@ -1665,6 +1780,7 @@ window.__ModuleLoader__.load({
1665
1780
  const [showToken, setShowToken] = React.useState(false)
1666
1781
  const [toast, showToast] = useToast()
1667
1782
  const [errorDialog, setErrorDialog] = React.useState(null)
1783
+ const [downloads, setDownloads] = React.useState({ status: 'loading', points: [], total: 0, error: '' })
1668
1784
  const okButtonRef = React.useRef(null)
1669
1785
  const otpInputRef = React.useRef(null)
1670
1786
  const tokenInputRef = React.useRef(null)
@@ -1753,6 +1869,32 @@ window.__ModuleLoader__.load({
1753
1869
  const effectiveAccess = isScopedPackage ? (settings ? settings.access : 'public') : 'public'
1754
1870
  const isPrerelease = typeof targetVersion === 'string' && targetVersion.includes('-')
1755
1871
 
1872
+ React.useEffect(() => {
1873
+ if (!packageName) return undefined
1874
+ const controller = new AbortController()
1875
+ setDownloads({ status: 'loading', points: [], total: 0, error: '' })
1876
+ apiGet(`/downloads?dir=${encodeURIComponent(dir)}`, controller.signal)
1877
+ .then((data) => {
1878
+ const points = Array.isArray(data.points) ? data.points : []
1879
+ setDownloads({
1880
+ status: data.unavailable ? 'unavailable' : points.length > 0 ? 'ready' : 'empty',
1881
+ points,
1882
+ total: Number(data.total) || 0,
1883
+ error: data.error || '',
1884
+ })
1885
+ })
1886
+ .catch((failure) => {
1887
+ if (failure && failure.name === 'AbortError') return
1888
+ setDownloads({
1889
+ status: 'error',
1890
+ points: [],
1891
+ total: 0,
1892
+ error: failure && failure.message ? failure.message : '',
1893
+ })
1894
+ })
1895
+ return () => controller.abort()
1896
+ }, [dir, packageName])
1897
+
1756
1898
  const closeError = React.useCallback(() => setErrorDialog(null), [])
1757
1899
  const showError = React.useCallback((title, message, detail, hint) => {
1758
1900
  setModalOpen(false)
@@ -2025,6 +2167,7 @@ window.__ModuleLoader__.load({
2025
2167
  if (!component || !settings) return null
2026
2168
 
2027
2169
  const recordSummary = component.lastPublish || null
2170
+ const npmUrl = packageName ? `https://www.npmjs.com/package/${packageName}` : ''
2028
2171
 
2029
2172
  return h('div', { className: 'np__inner' },
2030
2173
  h('div', { className: 'np-detail__head' },
@@ -2419,6 +2562,27 @@ window.__ModuleLoader__.load({
2419
2562
  h('dd', null, npmInfo ? (npmInfo.username ? tr('synopsis.npmUser', { name: npmInfo.username }) : t('synopsis.npmUnknown')) : t('synopsis.npmIdle')),
2420
2563
  ),
2421
2564
  ),
2565
+ h('section', { className: 'np-railcard' },
2566
+ h('h2', { className: 'np-railcard__title' }, t('npmInfo.title')),
2567
+ h('div', { className: 'np-railcard__body' },
2568
+ h('code', { className: 'np-railcard__pkg' }, packageName || '—'),
2569
+ h('span', { className: 'np-railcard__hint' }, t('npmInfo.hint')),
2570
+ h(DownloadBars, {
2571
+ status: downloads.status,
2572
+ points: downloads.points,
2573
+ total: downloads.total,
2574
+ error: downloads.error,
2575
+ }),
2576
+ npmUrl
2577
+ ? h('a', {
2578
+ className: 'np-btn np-btn--small np-railcard__action',
2579
+ href: npmUrl,
2580
+ target: '_blank',
2581
+ rel: 'noreferrer noopener',
2582
+ }, h(Glyph, { name: 'external', size: 13 }), t('npmInfo.open'))
2583
+ : h('button', { type: 'button', className: 'np-btn np-btn--small np-railcard__action', disabled: true }, h(Glyph, { name: 'external', size: 13 }), t('npmInfo.open')),
2584
+ ),
2585
+ ),
2422
2586
  h('section', { className: 'np-railcard' },
2423
2587
  h('h2', { className: 'np-railcard__title' },
2424
2588
  t('detail.records'),
package/index.js CHANGED
@@ -84,6 +84,7 @@ export function apply(ctx, config = {}) {
84
84
  let discoveryCache = { at: 0, value: null }
85
85
  let overviewCache = { at: 0, value: null }
86
86
  let npmCache = { at: 0, value: null }
87
+ const downloadsCache = new Map()
87
88
  let npmrcRegistry = null
88
89
  const publishing = new Set()
89
90
 
@@ -1725,6 +1726,76 @@ export function apply(ctx, config = {}) {
1725
1726
  }
1726
1727
  }
1727
1728
 
1729
+ async function getDownloads(rawDir) {
1730
+ const { pkg } = await requireComponentDir(rawDir)
1731
+ const name = typeof pkg.name === 'string' ? pkg.name : ''
1732
+ if (!name) throw httpError(400, '该组件没有可查询的 npm 包名。')
1733
+ const cached = downloadsCache.get(name)
1734
+ if (cached && Date.now() - cached.at < cached.ttl) return cached.value
1735
+ const remember = (value, ttl) => {
1736
+ downloadsCache.set(name, { at: Date.now(), ttl, value })
1737
+ return value
1738
+ }
1739
+ const unavailable = (message) => remember({
1740
+ package: name,
1741
+ points: [],
1742
+ total: 0,
1743
+ start: null,
1744
+ end: null,
1745
+ unavailable: true,
1746
+ error: message,
1747
+ fetchedAt: new Date().toISOString(),
1748
+ }, 2 * 60_000)
1749
+ const controller = new AbortController()
1750
+ const timer = setTimeout(() => controller.abort(), 10_000)
1751
+ let response
1752
+ try {
1753
+ response = await fetch(`https://api.npmjs.org/downloads/range/last-week/${encodeURIComponent(name)}`, {
1754
+ signal: controller.signal,
1755
+ headers: { accept: 'application/json' },
1756
+ })
1757
+ } catch (error) {
1758
+ return unavailable(error && error.name === 'AbortError' ? '查询下载量超时。' : '无法连接 npm 下载统计接口。')
1759
+ } finally {
1760
+ clearTimeout(timer)
1761
+ }
1762
+ if (response.status === 404) {
1763
+ return remember({
1764
+ package: name,
1765
+ points: [],
1766
+ total: 0,
1767
+ start: null,
1768
+ end: null,
1769
+ unavailable: false,
1770
+ empty: true,
1771
+ fetchedAt: new Date().toISOString(),
1772
+ }, 10 * 60_000)
1773
+ }
1774
+ if (!response.ok) return unavailable(`npm 下载统计接口返回 HTTP ${response.status}。`)
1775
+ let data
1776
+ try {
1777
+ data = await response.json()
1778
+ } catch {
1779
+ return unavailable('npm 下载统计返回了无法解析的数据。')
1780
+ }
1781
+ const points = Array.isArray(data.downloads)
1782
+ ? data.downloads.map((point) => ({
1783
+ day: String(point && point.day ? point.day : ''),
1784
+ downloads: Number(point && point.downloads) || 0,
1785
+ }))
1786
+ : []
1787
+ return remember({
1788
+ package: name,
1789
+ start: data.start || (points.length > 0 ? points[0].day : null),
1790
+ end: data.end || (points.length > 0 ? points[points.length - 1].day : null),
1791
+ points,
1792
+ total: points.reduce((sum, point) => sum + point.downloads, 0),
1793
+ unavailable: false,
1794
+ empty: points.length === 0,
1795
+ fetchedAt: new Date().toISOString(),
1796
+ }, 10 * 60_000)
1797
+ }
1798
+
1728
1799
  async function addTarget(rawDir) {
1729
1800
  await ensureState()
1730
1801
  const { dir } = await requireComponentDir(rawDir)
@@ -1765,6 +1836,9 @@ export function apply(ctx, config = {}) {
1765
1836
  const { dir } = await requireComponentDir(url.searchParams.get('dir'))
1766
1837
  return sendJson(response, 200, { ok: true, ...(await buildDetail(dir)) })
1767
1838
  }
1839
+ if (pathname === `${API}/downloads`) {
1840
+ return sendJson(response, 200, { ok: true, ...(await getDownloads(url.searchParams.get('dir'))) })
1841
+ }
1768
1842
  return sendJson(response, 404, { ok: false, code: 'not-found', message: '未知的发布管理接口。' })
1769
1843
  }
1770
1844
  if (request.method !== 'POST') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wangnanshan/dsh-nanshan-publish-manager",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "type": "module",
5
5
  "description": "【南山】DSH发布管理:以 npm 包(dsh.bundle + dsh.client)的官方形式,管理本机 DSH 组件的发布设置、发布预检查、npm 发布与发布记录。",
6
6
  "license": "MIT",