@quecue/dev-notify 0.1.0 → 0.1.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/CHANGELOG.md +6 -0
- package/README.md +11 -1
- package/SECURITY.md +1 -1
- package/dist/cli.js +13 -4
- package/dist/update.js +92 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
跨平台开发任务通知工具,使用 **TypeScript / Node.js**,通过 npm 分发,不需要独立可执行文件或其他脚本运行时。通用通知不依赖 Claude Code、Unity 或项目目录。
|
|
4
4
|
|
|
5
|
-
**npm 包名为 `@quecue/dev-notify`。`0.1.
|
|
5
|
+
**npm 包名为 `@quecue/dev-notify`。`0.1.1` 提供 Claude Code 回复、原生 PC 与 Telegram 双端答题及结果反馈。** 版本变更与升级说明见 [CHANGELOG.md](CHANGELOG.md);公开版本以 npm registry 为准。
|
|
6
6
|
|
|
7
7
|
## 安装与使用
|
|
8
8
|
|
|
@@ -40,6 +40,16 @@ node dist/cli.js send --title "分段预览" --content-file result.txt --dry-run
|
|
|
40
40
|
|
|
41
41
|
正式包的安装、升级和卸载由 npm 负责。**卸载前建议先清理凭据**:npm 卸载不会自动删除用户凭据。请先交互执行 `dev-notify credentials clear`,再卸载。该命令只删除当前配置引用的 Node 凭据,不会动其他账号、其他配置或旧版原型凭据。
|
|
42
42
|
|
|
43
|
+
已安装的版本可以用 `update` 升级;它只负责检查版本并调用 npm,不会自己下载或替换文件:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
dev-notify update # 检查并在确认后运行 npm install -g
|
|
47
|
+
dev-notify update --check # 只检查,不安装
|
|
48
|
+
dev-notify update --yes # 跳过确认(适合脚本)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
实际的完整性校验、registry 配置、权限处理和原生依赖解析仍由 npm 完成。更新后请重新运行 `dev-notify integration claude-code install`;如使用远程回复,还需重启桥接。
|
|
52
|
+
|
|
43
53
|
## 已提供的功能
|
|
44
54
|
|
|
45
55
|
- Telegram、飞书双渠道通知,按渲染后的长度分段,不会悄悄截断内容。
|
package/SECURITY.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,9 @@ import { claudeCodeStatus, claudeSettingsPath, configureClaudeCode } from './cla
|
|
|
13
13
|
import { dispatchClaudeHook, readHookInput } from './claude-hook.js';
|
|
14
14
|
import { accessReplies, configureReplies, disableReplies } from './reply-state.js';
|
|
15
15
|
import { startBridge, stopBridge, bridgeStatus, runBridge } from './reply-bridge.js';
|
|
16
|
-
|
|
16
|
+
import { updatePackage } from './update.js';
|
|
17
|
+
export const VERSION = '0.1.1';
|
|
18
|
+
const HELP = `dev-notify ${VERSION} — 跨平台开发任务通知
|
|
17
19
|
|
|
18
20
|
命令:
|
|
19
21
|
send --title 标题 [--content 正文 | --content-file 文件 | --stdin]
|
|
@@ -26,6 +28,7 @@ const HELP = `dev-notify 0.1.0 — 跨平台开发任务通知
|
|
|
26
28
|
credentials clear 移除当前 Node 凭据引用和条目(需要确认)
|
|
27
29
|
paths 显示配置路径(不会写入文件)
|
|
28
30
|
doctor / status 离线检查(不会发送消息)
|
|
31
|
+
update [--check] [--yes] 检查并更新到 npm 最新版本(确认后调用 npm)
|
|
29
32
|
integration list 列出可用集成
|
|
30
33
|
integration claude-code install / status / remove
|
|
31
34
|
单独安装、查询或移除用户级 hooks
|
|
@@ -64,7 +67,7 @@ export async function main(argv = process.argv.slice(2), overrides = {}) {
|
|
|
64
67
|
title: { type: 'string' }, content: { type: 'string' }, 'content-file': { type: 'string' }, stdin: { type: 'boolean' },
|
|
65
68
|
channel: { type: 'string' }, level: { type: 'string' }, footer: { type: 'string' }, 'dry-run': { type: 'boolean' },
|
|
66
69
|
template: { type: 'boolean' }, yes: { type: 'boolean' },
|
|
67
|
-
'no-test': { type: 'boolean' }, 'no-hooks': { type: 'boolean' },
|
|
70
|
+
'no-test': { type: 'boolean' }, 'no-hooks': { type: 'boolean' }, check: { type: 'boolean' },
|
|
68
71
|
} });
|
|
69
72
|
}
|
|
70
73
|
catch {
|
|
@@ -76,13 +79,13 @@ export async function main(argv = process.argv.slice(2), overrides = {}) {
|
|
|
76
79
|
return 0;
|
|
77
80
|
}
|
|
78
81
|
if (values.version) {
|
|
79
|
-
context.out(
|
|
82
|
+
context.out(`dev-notify ${VERSION}`);
|
|
80
83
|
return 0;
|
|
81
84
|
}
|
|
82
85
|
const [command, action, integrationAction] = positionals;
|
|
83
86
|
const allowed = {
|
|
84
87
|
send: ['title', 'content', 'content-file', 'stdin', 'channel', 'level', 'footer', 'dry-run'],
|
|
85
|
-
setup: ['template', 'yes', 'no-test', 'no-hooks'], paths: [], doctor: [], status: [], integration: [], credentials: [], reply: [],
|
|
88
|
+
setup: ['template', 'yes', 'no-test', 'no-hooks'], paths: [], doctor: [], status: [], integration: [], credentials: [], reply: [], update: ['check', 'yes'],
|
|
86
89
|
};
|
|
87
90
|
if (!command || !Object.hasOwn(allowed, command) || positionals.length > (command === 'integration' ? 3 : command === 'credentials' || command === 'reply' ? 2 : 1)
|
|
88
91
|
|| Object.keys(values).some(key => key !== 'config' && !allowed[command].includes(key)))
|
|
@@ -93,6 +96,12 @@ export async function main(argv = process.argv.slice(2), overrides = {}) {
|
|
|
93
96
|
context.out(JSON.stringify({ config: file, data: paths.data }, null, 2));
|
|
94
97
|
return 0;
|
|
95
98
|
}
|
|
99
|
+
if (command === 'update') {
|
|
100
|
+
return await updatePackage(VERSION, {
|
|
101
|
+
out: context.out, prompts: context.prompts,
|
|
102
|
+
check: !!values.check, yes: !!values.yes,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
96
105
|
if (command === 'integration') {
|
|
97
106
|
if (action === 'list' && !integrationAction) {
|
|
98
107
|
context.out('claude-code:支持通知、Telegram 回复和结构化问题交互。通知 hooks 与回复权限分开,默认只发送通知,不接收远程输入。');
|
package/dist/update.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { ConfigError } from './config.js';
|
|
3
|
+
import { terminalPrompts } from './prompts.js';
|
|
4
|
+
const NAME = '@quecue/dev-notify';
|
|
5
|
+
const VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(alpha|beta|rc)\.(0|[1-9]\d*))?$/;
|
|
6
|
+
function npmRunner(args, capture) {
|
|
7
|
+
// Windows 的 npm 是 .cmd;Node 不能直接 spawn,需要经过 shell。参数均为固定值。
|
|
8
|
+
const result = spawnSync('npm', args, {
|
|
9
|
+
encoding: 'utf8',
|
|
10
|
+
stdio: capture ? 'pipe' : 'inherit',
|
|
11
|
+
timeout: capture ? 30000 : 300000,
|
|
12
|
+
shell: process.platform === 'win32',
|
|
13
|
+
});
|
|
14
|
+
if (result.error)
|
|
15
|
+
throw new ConfigError('无法运行 npm。请确认已安装 npm,并且它在 PATH 中。');
|
|
16
|
+
return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
|
|
17
|
+
}
|
|
18
|
+
async function fetchLatest() {
|
|
19
|
+
let response;
|
|
20
|
+
try {
|
|
21
|
+
response = await fetch(`https://registry.npmjs.org/${NAME.replace('/', '%2F')}/latest?t=${Date.now()}`, {
|
|
22
|
+
redirect: 'error',
|
|
23
|
+
signal: AbortSignal.timeout(10000),
|
|
24
|
+
headers: { 'Cache-Control': 'no-cache, no-store', Accept: 'application/json' },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new ConfigError('无法检查最新版本。请检查网络后重试。');
|
|
29
|
+
}
|
|
30
|
+
if (!response.ok)
|
|
31
|
+
throw new ConfigError(`无法检查最新版本(HTTP ${response.status})。请检查网络后重试。`);
|
|
32
|
+
let version;
|
|
33
|
+
try {
|
|
34
|
+
version = (await response.json())?.version;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
throw new ConfigError('npm registry 返回的版本信息无法解析。');
|
|
38
|
+
}
|
|
39
|
+
if (typeof version !== 'string' || !VERSION.test(version))
|
|
40
|
+
throw new ConfigError('npm registry 返回的版本号格式不受支持。');
|
|
41
|
+
return version;
|
|
42
|
+
}
|
|
43
|
+
function parts(version) {
|
|
44
|
+
const match = VERSION.exec(version);
|
|
45
|
+
if (!match)
|
|
46
|
+
throw new ConfigError('版本号格式不受支持。');
|
|
47
|
+
const rank = match[4] === 'alpha' ? 1 : match[4] === 'beta' ? 2 : match[4] === 'rc' ? 3 : 4;
|
|
48
|
+
return [Number(match[1]), Number(match[2]), Number(match[3]), rank, Number(match[5] ?? 0)];
|
|
49
|
+
}
|
|
50
|
+
export function compareVersions(left, right) {
|
|
51
|
+
const a = parts(left), b = parts(right);
|
|
52
|
+
for (let i = 0; i < a.length; i++)
|
|
53
|
+
if (a[i] !== b[i])
|
|
54
|
+
return a[i] > b[i] ? 1 : -1;
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
async function confirmUpdate(latest, options) {
|
|
58
|
+
if (options.yes)
|
|
59
|
+
return true;
|
|
60
|
+
if (!options.prompts && (!process.stdin.isTTY || !process.stdout.isTTY)) {
|
|
61
|
+
throw new ConfigError('更新需要在交互终端中确认;非交互场景请使用 dev-notify update --yes。');
|
|
62
|
+
}
|
|
63
|
+
const prompts = options.prompts ?? terminalPrompts();
|
|
64
|
+
return /^(y|yes)$/i.test(await prompts.text(`更新到 ${latest}?[y/N] `));
|
|
65
|
+
}
|
|
66
|
+
export async function updatePackage(current, options) {
|
|
67
|
+
if (!VERSION.test(current))
|
|
68
|
+
throw new ConfigError('当前版本号格式不受支持,无法检查更新。');
|
|
69
|
+
const latest = await (options.latest ?? fetchLatest)();
|
|
70
|
+
if (!VERSION.test(latest))
|
|
71
|
+
throw new ConfigError('npm registry 返回的版本号格式不受支持。');
|
|
72
|
+
if (compareVersions(latest, current) <= 0) {
|
|
73
|
+
options.out(`已是最新版本 ${current}。`);
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
options.out(`当前版本 ${current},npm 最新版本 ${latest}。`);
|
|
77
|
+
if (options.check) {
|
|
78
|
+
options.out('运行 dev-notify update 更新。');
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
if (!await confirmUpdate(latest, options)) {
|
|
82
|
+
options.out('已取消,没有更新。');
|
|
83
|
+
return 130;
|
|
84
|
+
}
|
|
85
|
+
options.out(`运行 npm install -g ${NAME}@${latest} ...`);
|
|
86
|
+
const result = (options.runner ?? npmRunner)(['install', '-g', `${NAME}@${latest}`], false);
|
|
87
|
+
if (result.status !== 0)
|
|
88
|
+
throw new ConfigError(`更新没有完成。请检查 npm 权限,或手动运行 npm install -g ${NAME}@latest。`);
|
|
89
|
+
options.out(`已更新到 ${latest}。请重新打开终端后运行 dev-notify --version 确认。`);
|
|
90
|
+
options.out('如有 Claude Code hooks,请运行 dev-notify integration claude-code install;如使用远程回复,请重启桥接。');
|
|
91
|
+
return 0;
|
|
92
|
+
}
|