dsh-lighthouse 0.0.0-stage → 0.3.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 7gzgn (operated by DSH)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,168 @@
1
- # Temporary Holding Version
1
+ # dsh-lighthouse(灯塔)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **一屏看清你发出去的每个作品现在怎么样了 —— 而且能点开看。**
4
+
5
+ ## 界面观察区(v0.2 新增)
6
+
7
+ DSH 侧栏多一个**灯塔图标**,点开就是一块观察面板 —— 每个作品一张卡:
8
+
9
+ | 看什么 | 怎么显示 |
10
+ | --- | --- |
11
+ | 发布了没有 | ✅ 已发布 / ❌ 仓库不存在 / ❌ 读不到 |
12
+ | 市场收录 | ✅ 已收录(带下载量)/ ⏳ 收录中 / ❌ 读不到 |
13
+ | 收录 PR | ✅ 已合并 / ⏳ 等 N 小时(天)/ —— 没有 |
14
+ | 星 · 关注 · 留言 | ⭐ / 👁 / 💬 三个数字(GitHub stars / watchers / open issues) |
15
+ | 本地与推送 | 本地版本 · 文件数 · 最后推送时间 |
16
+
17
+ 右上角一个「刷新」,打开面板时自动拉一次;数据来自 host 那条只读接口 `/dsh-lighthouse/status` ——
18
+ 同一个数字在**面板、工具、卡片**里必然一致(走的是同一条采集路径)。
19
+
20
+ 本地拿的是什么版本、GitHub 有没有动静、**市场收没收录、被下载多少次**、给收录 PR 等了多久(对比上游中位数)——
21
+ 全部来自 API,**一个数字都不经模型生成**,读不到就写"读不到"。
22
+
23
+ > 名字的来由:船下水之后就看不见了。灯塔是岸上那盏灯 ——
24
+ > 专门盯着"我的作品现在在哪一步、有没有人上船"。
25
+
26
+ ---
27
+
28
+ ## 它回答什么问题
29
+
30
+ - 我发出去的东西,**市场收了吗**?("未收录" vs "已收录")
31
+ - 有没有人用?**下载多少、在同类里排第几**?
32
+ - 收录 PR 等了多久?——**和上游的中位数比,算不算久**?
33
+ - 本地那份和线上那份,是同一条船吗?(版本、最后推送时间)
34
+ - npm 上有没有?(默认查,可关)
35
+
36
+ ## 作品多了怎么办(v0.2.5)
37
+
38
+ ≤ 12 个作品时是一作品一张卡(信息最全)。**超过 12 个自动切紧凑视图**:
39
+
40
+ - 文本回执:一行一个作品(`名字|v版本|star|市场|PR 等待`),不再每个占两行;
41
+ - HTML 卡片:换成**一张排序表格**(作品 / 分类 / 版本 / GitHub / 市场 / 收录 PR),汇总条与表头**滚动吸顶**,
42
+ 不用滑到底再回来对表头;
43
+ - **排序按"要不要人管"**:①需要处理(仓库读不到 / 没有 PR / 市场读不到)②等收录(等得久的在前)③已收录。
44
+ 50 个作品时,你第一眼看到的就是那几个有问题的,而不是按名册顺序从上翻。
45
+
46
+ > 表格是纯静态 HTML(内联样式,无脚本),所以不能搜索/过滤 —— 需要筛选就走名册(`lighthouse_fleet`)把范围缩到一组。
47
+
48
+ ## 中文名与直达链接(v0.3.0)
49
+
50
+ 每个作品显示为 `dsh-lighthouse(灯塔)` —— **英文名 + 中文名**,名字本身就是**超链接**,点开直达仓库;
51
+ 卡片里另有一行「直达」,把 GitHub / npm / 市场条目页分别列出来(哪个地址能确定才给,绝不编)。
52
+
53
+ 中文名从哪来:名册的 `nameZh` 优先,其次本地 `package.json` 的 `dsh.displayName`。
54
+ **滑道发过布(尤其改名)之后**跑一次 `lighthouse_fleet action="sync"`:
55
+ 它会读每个作品本地的 `package.json`,把**空着**的中文名与链接回填进名册,
56
+ 并列出「名册包名与仓库对不上」的情况(只报告,不擅自改名)。这样滑道更新了,灯塔也就跟上了。
57
+
58
+ ## 多来源状态
59
+
60
+ **适用范围(一句话):数据源只有 GitHub(API)+ 市场登记表 + npm。**
61
+ 如果作品只放在 Gitee / GitLab,灯塔**读不到那边的 star 与推送时间** —— 名册里只能给它写一条
62
+ `mirrors` 链接(点了能跳过去),不会假装有数据。
63
+
64
+ 每个作品都按来源分开报:**本地**(版本/文件数/有没有自测)、**GitHub**(仓库在不在、star、最后推送)、
65
+ **npm**(有没有包)、**市场**(收录/下载/同类排名)、**收录 PR**(等待时长 vs 上游中位数)。
66
+ 某一源读不到就写"读不到",不会拿另一源的数凑。
67
+
68
+ ## 只读承诺
69
+
70
+ - **全部是 GET 请求**:不动仓库、不开 PR、不写任何远端。
71
+ - **不编数字**:任何一个数据源读不到,就标 `读不到(原因)`,不会用估算值糊过去。
72
+ - **名册显式**:灯塔照着名册看,**不自动发现**作品 —— 从目录名或包名前缀去猜"哪些是你的作品",
73
+ 猜错了会把别人的仓库报进你的看板。
74
+
75
+ ## 名册(fleet)
76
+
77
+ 默认路径 `~/.dsh/lighthouse-fleet.json`:
78
+
79
+ ```json
80
+ {
81
+ "version": 1,
82
+ "owner": "7gzgn",
83
+ "upstream": { "owner": "awesome-dsh-plugin", "repo": "awesome-dsh-plugin", "branch": "main" },
84
+ "works": [
85
+ {
86
+ "name": "dsh-slipway",
87
+ "repo": "dsh-slipway",
88
+ "category": "tools",
89
+ "localDir": "D:\\dsh\\projects\\dsh-slipway"
90
+ }
91
+ ]
92
+ }
93
+ ```
94
+
95
+ - `owner` 与 `works` **必填**(缺了直接报错,不猜)。
96
+ - `localDir` 可省:省了就只看线上那一半。
97
+ - 用工具或命令写它:
98
+
99
+ ```
100
+ lighthouse_fleet action=init owner=7gzgn works='[{"name":"dsh-slipway","repo":"dsh-slipway","category":"tools","localDir":"D:\\dsh\\projects\\dsh-slipway"}]'
101
+ dsh-lighthouse init --owner 7gzgn --works '[…]'
102
+ ```
103
+
104
+ ## 安装
105
+
106
+ 市场收录后在 DSH 里搜 `dsh-lighthouse` 安装。手动安装:
107
+
108
+ ```powershell
109
+ New-Item -ItemType Junction -Path "$env:USERPROFILE\.dsh\profiles\desktop\node_modules\dsh-lighthouse" -Target "D:\dsh\projects\dsh-lighthouse"
110
+ # profile 的 package.json:dependencies 加 "dsh-lighthouse": "link:D:/dsh/projects/dsh-lighthouse"
111
+ # 并把 "dsh-lighthouse" 加进 dsh.profile.bundles(漏了不加载)→ 重启 DSH
112
+ ```
113
+
114
+ ## 用法
115
+
116
+ ### 工具
117
+
118
+ ```
119
+ lighthouse_report [fleetFile] [html] [includeNpm] [marketUrl] [reportDir]
120
+ lighthouse_fleet action=show|init …
121
+ ```
122
+
123
+ `lighthouse_report` 返回一段汇总文本,并把一张**单文件 HTML 卡片**落到
124
+ `~/.dsh/lighthouse-reports/lighthouse-<时间戳>.html` —— 无外链、无脚本依赖,可直接打开或截图。
125
+
126
+ ### 命令
127
+
128
+ ```powershell
129
+ dsh-lighthouse report [--fleet <路径>] [--dir <卡片目录>] [--no-npm] [--no-html] [--json]
130
+ dsh-lighthouse fleet [--fleet <路径>]
131
+ ```
132
+
133
+ ## 数据从哪来
134
+
135
+ | 来源 | 取什么 | 备注 |
136
+ | --- | --- | --- |
137
+ | 本地工程 | `package.json` 的版本、文件数、有没有 `test/smoke.mjs` | 只读文件 |
138
+ | GitHub API | 仓库是否存在、star/fork、最后推送、**我开的收录 PR 状态** | 需要 PAT(`repo` scope)时更全 |
139
+ | 市场接口 | `GET <marketUrl>/dsh-market/registry`(**要同源头**,否则被拒) | **默认取宿主 web 服务的 origin**(`ctx.get('webServer').getOrigin()`,比如 `http://127.0.0.1:56497`);登记表只拉一次、进程内缓存 5 分钟 |
140
+ | npm registry | 包在不在 | 默认开,`--no-npm` / `includeNpm:false` 可关 |
141
+
142
+ > **市场地址为什么不能再写死端口**:登记表路由挂在宿主自己的 web 服务上,端口每次启动都可能变;
143
+ > 写死 `19387` 的后果不是报错,而是报告里一片 **0 收录 / 0 下载** —— 看起来像"没人用",
144
+ > 其实是"没读到"。v0.2.4 起:默认向 `webServer.getOrigin()` 要地址;**读不到时整行不报数**,
145
+ > 并明确写"市场数据读不到(不显示 0,免得被当成没人用)"。
146
+
147
+ PAT 依次找:`tokenFile` → 环境变量 `DSH_SLIPWAY_TOKEN_FILE` → `~/.dsh/.gh-token` → `D:\dsh\.gh-token`。
148
+
149
+ ## 它还会替你盯一件事
150
+
151
+ **哪些收录 PR 已经等了超过上游中位数**。报告里单独一行点名(`#编号(等了多久)`),
152
+ 没有超的也明说"都还在正常等待区间" —— 这样"要不要去催"不用自己算。
153
+
154
+ ## 兼容性
155
+
156
+ - 运行时:**DSH 0.2.1-rc.2**(实测使用的版本)。
157
+ - `peerDependencies` 用显式范围;拿不到 `@deepseek-ai/dsh-tools` 时 `defineTool` 退化成恒等(CLI/测试照常)。
158
+ - 插件只注册工具与一段提示词,不挂任何前台服务、不轮询、不占资源(只在被调用时跑一次)。
159
+
160
+ ## 自测
161
+
162
+ ```powershell
163
+ node test/smoke.mjs # 全假网络(GitHub/市场/npm 都是假的),不碰真宿主、不碰外网
164
+ ```
165
+
166
+ ## License
167
+
168
+ MIT
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * dsh-lighthouse · CLI —— 不开会话也能看板。
4
+ *
5
+ * dsh-lighthouse report [--fleet <路径>] [--dir <卡片目录>] [--no-npm] [--no-html] [--json]
6
+ * dsh-lighthouse fleet [--fleet <路径>]
7
+ * dsh-lighthouse init --owner <账号> --works <JSON 数组> [--fleet <路径>]
8
+ *
9
+ * 退出码:0 成功;1 名册读不到或有问题;2 用法错误。
10
+ *
11
+ * @module dsh-lighthouse/bin
12
+ */
13
+
14
+ import { defaultFleetPath, readFleet, validateFleet, writeFleet } from '../lib/fleet.js';
15
+ import { defaultMarketUrl, defaultReportDir, runReport } from '../lib/index.js';
16
+ import { LIGHTHOUSE_GUIDANCE } from '../lib/index.js';
17
+
18
+ /** 极简参数解析。 */
19
+ function parseArgv(argv) {
20
+ const out = { _: [] };
21
+ for (let i = 0; i < argv.length; i += 1) {
22
+ const token = argv[i];
23
+ if (token.startsWith('--')) {
24
+ const key = token.slice(2);
25
+ const next = argv[i + 1];
26
+ if (next === undefined || next.startsWith('--')) out[key] = true;
27
+ else { out[key] = next; i += 1; }
28
+ } else {
29
+ out._.push(token);
30
+ }
31
+ }
32
+ return out;
33
+ }
34
+
35
+ const USAGE = `dsh-lighthouse —— 作品看板
36
+
37
+ 用法:
38
+ dsh-lighthouse report [--fleet <名册路径>] [--dir <卡片目录>] [--no-npm] [--no-html] [--json]
39
+ dsh-lighthouse fleet [--fleet <名册路径>]
40
+ dsh-lighthouse init --owner <GitHub 账号> --works '<JSON 数组>' [--fleet <名册路径>]
41
+
42
+ 名册默认:${defaultFleetPath()}
43
+ 卡片默认目录:${defaultReportDir()}
44
+ 市场接口默认:${defaultMarketUrl()}
45
+
46
+ ${LIGHTHOUSE_GUIDANCE}
47
+ `;
48
+
49
+ async function main() {
50
+ const argv = parseArgv(process.argv.slice(2));
51
+ const cmd = argv._[0] ?? 'report';
52
+ const fleetPath = argv.fleet ?? defaultFleetPath();
53
+
54
+ if (cmd === 'help' || argv.help === true) {
55
+ console.log(USAGE);
56
+ return 0;
57
+ }
58
+
59
+ if (cmd === 'fleet') {
60
+ const read = await readFleet(fleetPath);
61
+ console.log(`名册:${fleetPath}`);
62
+ if (read.ok !== true) {
63
+ for (const e of read.errors) console.log(` ❌ ${e}`);
64
+ return 1;
65
+ }
66
+ console.log(` owner:${read.fleet.owner}`);
67
+ console.log(` 上游:${read.fleet.upstream.owner}/${read.fleet.upstream.repo}@${read.fleet.upstream.branch}`);
68
+ console.log(` 作品(${read.fleet.works.length}):`);
69
+ for (const work of read.fleet.works) {
70
+ console.log(` · ${work.name}${work.category ? `(${work.category})` : ''}${work.localDir ? ` ← ${work.localDir}` : ''}`);
71
+ }
72
+ return 0;
73
+ }
74
+
75
+ if (cmd === 'init') {
76
+ let works;
77
+ try {
78
+ works = JSON.parse(String(argv.works ?? ''));
79
+ } catch {
80
+ console.error('❌ --works 不是合法 JSON 数组');
81
+ return 2;
82
+ }
83
+ const checked = validateFleet({ owner: argv.owner, upstream: { owner: argv['upstream-owner'], repo: argv['upstream-repo'] }, works });
84
+ if (checked.ok !== true) {
85
+ console.error(`❌ ${checked.errors.join(';')}`);
86
+ return 2;
87
+ }
88
+ const written = await writeFleet(fleetPath, checked.fleet);
89
+ console.log(`✅ 名册已写入:${written}(${checked.fleet.works.length} 个作品)`);
90
+ return 0;
91
+ }
92
+
93
+ if (cmd === 'report') {
94
+ const result = await runReport({
95
+ fleetFile: fleetPath,
96
+ reportDir: argv.dir,
97
+ includeNpm: argv['no-npm'] !== true,
98
+ html: argv['no-html'] !== true,
99
+ json: argv.json === true,
100
+ });
101
+ if (result.ok !== true) {
102
+ for (const e of result.errors) console.error(`❌ ${e}`);
103
+ return 1;
104
+ }
105
+ const s = result.report.summary;
106
+ console.log(`📡 作品看板|${result.report.generatedAt}`);
107
+ console.log(` 作品 ${s.works} · 已发布 ${s.published} · 已收录 ${s.listed} · 总下载 ${s.totalDownloads} · 待合 PR ${s.openPrs}`);
108
+ for (const item of result.report.works) {
109
+ const { work, local, github, market } = item;
110
+ const bits = [];
111
+ bits.push(local.available === true ? `本地 ${local.version ?? '?'}` : '本地 读不到');
112
+ bits.push(github.exists === true ? `star ${github.stars}` : 'GitHub 无/读不到');
113
+ bits.push(market.listed === true ? `市场已收录(${market.downloads} 下载)` : (market.available === true ? '市场未收录' : '市场读不到'));
114
+ if (github.pr) bits.push(`PR #${github.pr.number} ${github.pr.mergedAt ? '已合并' : github.pr.state}`);
115
+ console.log(` · ${work.name}:${bits.join('|')}`);
116
+ }
117
+ if (result.htmlPath) console.log(`\n卡片:${result.htmlPath}`);
118
+ if (result.jsonPath) console.log(`JSON:${result.jsonPath}`);
119
+ return 0;
120
+ }
121
+
122
+ console.error(`未知子命令:${cmd}\n\n${USAGE}`);
123
+ return 2;
124
+ }
125
+
126
+ main()
127
+ .then((code) => { process.exitCode = code; })
128
+ .catch((err) => {
129
+ console.error(`❌ ${err instanceof Error ? err.message : String(err)}`);
130
+ process.exitCode = 1;
131
+ });