@quecue/dev-notify 0.1.1 → 0.1.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/CHANGELOG.md +13 -0
- package/README.md +5 -2
- package/SECURITY.md +1 -1
- package/dist/claude-code.js +3 -1
- package/dist/cli.js +13 -7
- package/dist/maintenance.js +100 -0
- package/dist/update.js +22 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.1.3 — 2026-11-03
|
|
4
|
+
|
|
5
|
+
### 改进
|
|
6
|
+
|
|
7
|
+
- `dev-notify update` 即使已经是最新版本也会执行 hooks 和桥接维护,方便从旧版升级后用同一条命令完成收尾;`--check` 仍然只检查,`--no-post` 跳过维护。
|
|
8
|
+
|
|
9
|
+
## 0.1.2 — 2026-11-03
|
|
10
|
+
|
|
11
|
+
### 改进
|
|
12
|
+
|
|
13
|
+
- `dev-notify update` 安装成功后自动重新安装绑定当前配置的 Claude Code hooks,并重启正在运行的回复桥接;新增 `--no-post` 跳过这些维护步骤。
|
|
14
|
+
- `integration claude-code install` / `remove` 支持 `--yes`,供自动化或 `update` 的维护流程使用。
|
|
15
|
+
|
|
3
16
|
## 0.1.1 — 2026-11-03
|
|
4
17
|
|
|
5
18
|
### 新增
|
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.3` 提供 Claude Code 回复、原生 PC 与 Telegram 双端答题及结果反馈。** 版本变更与升级说明见 [CHANGELOG.md](CHANGELOG.md);公开版本以 npm registry 为准。
|
|
6
6
|
|
|
7
7
|
## 安装与使用
|
|
8
8
|
|
|
@@ -46,9 +46,10 @@ node dist/cli.js send --title "分段预览" --content-file result.txt --dry-run
|
|
|
46
46
|
dev-notify update # 检查并在确认后运行 npm install -g
|
|
47
47
|
dev-notify update --check # 只检查,不安装
|
|
48
48
|
dev-notify update --yes # 跳过确认(适合脚本)
|
|
49
|
+
dev-notify update --no-post # 只更新包,不执行 hooks/桥接维护
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
实际的完整性校验、registry 配置、权限处理和原生依赖解析仍由 npm
|
|
52
|
+
实际的完整性校验、registry 配置、权限处理和原生依赖解析仍由 npm 完成。安装成功后,`update` 会自动重新安装绑定当前配置的 Claude Code hooks(已存在且配置匹配时),并重启正在运行的回复桥接;不需要时可用 `--no-post` 跳过。在 0.1.3 及以后,即使已经是最新版本,`update` 也会执行同样的维护,因此可以用同一条命令完成升级后的收尾。
|
|
52
53
|
|
|
53
54
|
## 已提供的功能
|
|
54
55
|
|
|
@@ -102,6 +103,8 @@ dev-notify integration claude-code status
|
|
|
102
103
|
dev-notify integration claude-code remove
|
|
103
104
|
```
|
|
104
105
|
|
|
106
|
+
`install` / `remove` 可加 `--yes` 跳过确认,供自动化或 `update` 的维护流程使用。
|
|
107
|
+
|
|
105
108
|
源码验证时将上述 `dev-notify` 替换为 `node dist/cli.js`。安装可用 `--config <绝对或相对路径>` 绑定指定通知配置;执行时将其解析为绝对路径并保存到 hook 参数。自定义 `DEV_NOTIFY_HOME` 的使用者应在安装时设置该变量。
|
|
106
109
|
|
|
107
110
|
- **配置范围**:默认用户级 `~/.claude/settings.json`,设置 `CLAUDE_CONFIG_DIR` 时使用该绝对目录下的 `settings.json`,覆盖该 Claude Code 配置目录下的全部项目。不会扫描或修改每个项目的设置。
|
package/SECURITY.md
CHANGED
package/dist/claude-code.js
CHANGED
|
@@ -61,11 +61,13 @@ function owned(hook) {
|
|
|
61
61
|
&& hook.args[4] === '--config' && typeof hook.args[5] === 'string';
|
|
62
62
|
}
|
|
63
63
|
function describe(file, data) {
|
|
64
|
-
const result = { file, stop: 0, notification: 0, lifecycle: 0, interaction: 0, others: 0, disabled: data.disableAllHooks === true };
|
|
64
|
+
const result = { file, stop: 0, notification: 0, lifecycle: 0, interaction: 0, others: 0, disabled: data.disableAllHooks === true, configs: [] };
|
|
65
65
|
for (const event of EVENTS)
|
|
66
66
|
for (const group of data.hooks?.[event] ?? [])
|
|
67
67
|
for (const hook of group.hooks) {
|
|
68
68
|
if (owned(hook)) {
|
|
69
|
+
if (!result.configs.includes(hook.args[5]))
|
|
70
|
+
result.configs.push(hook.args[5]);
|
|
69
71
|
if (event === 'Stop')
|
|
70
72
|
result.stop++;
|
|
71
73
|
else if (event === 'Notification')
|
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,8 @@ 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
|
-
|
|
17
|
+
import { postUpdate } from './maintenance.js';
|
|
18
|
+
export const VERSION = '0.1.3';
|
|
18
19
|
const HELP = `dev-notify ${VERSION} — 跨平台开发任务通知
|
|
19
20
|
|
|
20
21
|
命令:
|
|
@@ -28,10 +29,11 @@ const HELP = `dev-notify ${VERSION} — 跨平台开发任务通知
|
|
|
28
29
|
credentials clear 移除当前 Node 凭据引用和条目(需要确认)
|
|
29
30
|
paths 显示配置路径(不会写入文件)
|
|
30
31
|
doctor / status 离线检查(不会发送消息)
|
|
31
|
-
update [--check] [--yes]
|
|
32
|
+
update [--check] [--yes] [--no-post]
|
|
33
|
+
检查并更新到 npm 最新版本,并维护 hooks 和桥接
|
|
32
34
|
integration list 列出可用集成
|
|
33
35
|
integration claude-code install / status / remove
|
|
34
|
-
单独安装、查询或移除用户级 hooks
|
|
36
|
+
单独安装、查询或移除用户级 hooks(install/remove 可用 --yes)
|
|
35
37
|
reply setup 授权可信的 Telegram 私聊用户
|
|
36
38
|
reply start / status / stop
|
|
37
39
|
启动、查询或停止回复桥接
|
|
@@ -67,7 +69,7 @@ export async function main(argv = process.argv.slice(2), overrides = {}) {
|
|
|
67
69
|
title: { type: 'string' }, content: { type: 'string' }, 'content-file': { type: 'string' }, stdin: { type: 'boolean' },
|
|
68
70
|
channel: { type: 'string' }, level: { type: 'string' }, footer: { type: 'string' }, 'dry-run': { type: 'boolean' },
|
|
69
71
|
template: { type: 'boolean' }, yes: { type: 'boolean' },
|
|
70
|
-
'no-test': { type: 'boolean' }, 'no-hooks': { type: 'boolean' }, check: { type: 'boolean' },
|
|
72
|
+
'no-test': { type: 'boolean' }, 'no-hooks': { type: 'boolean' }, check: { type: 'boolean' }, 'no-post': { type: 'boolean' },
|
|
71
73
|
} });
|
|
72
74
|
}
|
|
73
75
|
catch {
|
|
@@ -85,7 +87,7 @@ export async function main(argv = process.argv.slice(2), overrides = {}) {
|
|
|
85
87
|
const [command, action, integrationAction] = positionals;
|
|
86
88
|
const allowed = {
|
|
87
89
|
send: ['title', 'content', 'content-file', 'stdin', 'channel', 'level', 'footer', 'dry-run'],
|
|
88
|
-
setup: ['template', 'yes', 'no-test', 'no-hooks'], paths: [], doctor: [], status: [], integration: [], credentials: [], reply: [], update: ['check', 'yes'],
|
|
90
|
+
setup: ['template', 'yes', 'no-test', 'no-hooks'], paths: [], doctor: [], status: [], integration: ['yes'], credentials: [], reply: [], update: ['check', 'yes', 'no-post'],
|
|
89
91
|
};
|
|
90
92
|
if (!command || !Object.hasOwn(allowed, command) || positionals.length > (command === 'integration' ? 3 : command === 'credentials' || command === 'reply' ? 2 : 1)
|
|
91
93
|
|| Object.keys(values).some(key => key !== 'config' && !allowed[command].includes(key)))
|
|
@@ -100,6 +102,8 @@ export async function main(argv = process.argv.slice(2), overrides = {}) {
|
|
|
100
102
|
return await updatePackage(VERSION, {
|
|
101
103
|
out: context.out, prompts: context.prompts,
|
|
102
104
|
check: !!values.check, yes: !!values.yes,
|
|
105
|
+
post: values['no-post'] ? undefined
|
|
106
|
+
: () => postUpdate({ config: file, env: context.env, out: context.out, err: context.err }),
|
|
103
107
|
});
|
|
104
108
|
}
|
|
105
109
|
if (command === 'integration') {
|
|
@@ -121,13 +125,15 @@ export async function main(argv = process.argv.slice(2), overrides = {}) {
|
|
|
121
125
|
: '这里只检查用户级文件。是否实际加载还受项目和托管策略影响,请在 Claude Code 的 /hooks 中确认。');
|
|
122
126
|
return status.stop === 1 && status.notification === 1 && status.lifecycle === 3 && status.interaction === 5 && !status.disabled ? 0 : 1;
|
|
123
127
|
}
|
|
124
|
-
const prompts = context.prompts ??
|
|
128
|
+
const prompts = context.prompts ?? (values.yes
|
|
129
|
+
? { out: context.out, text: async () => '', secret: async () => '' }
|
|
130
|
+
: terminalPrompts());
|
|
125
131
|
if (integrationAction === 'install') {
|
|
126
132
|
const config = await loadConfig(file, true, context.env);
|
|
127
133
|
if (!ready(config, 'telegram') && !ready(config, 'feishu'))
|
|
128
134
|
throw new ConfigError('请先运行 setup 配置至少一个通知渠道,再安装 Claude Code hooks。');
|
|
129
135
|
}
|
|
130
|
-
return await configureClaudeCode(file, integrationAction, prompts, { env: context.env }) ? 0 : 130;
|
|
136
|
+
return await configureClaudeCode(file, integrationAction, prompts, { env: context.env, confirm: values.yes ? false : undefined }) ? 0 : 130;
|
|
131
137
|
}
|
|
132
138
|
if (command === 'reply') {
|
|
133
139
|
if (!['setup', 'start', 'status', 'stop', 'disable', 'run'].includes(action))
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
5
|
+
import { ConfigError } from './config.js';
|
|
6
|
+
import { claudeCodeStatus } from './claude-code.js';
|
|
7
|
+
import { bridgeStatus } from './reply-bridge.js';
|
|
8
|
+
function runCli(args) {
|
|
9
|
+
const cli = fileURLToPath(new URL('./cli.js', import.meta.url));
|
|
10
|
+
const result = spawnSync(process.execPath, [cli, ...args], { stdio: 'inherit', timeout: 300000 });
|
|
11
|
+
if (result.error)
|
|
12
|
+
throw new ConfigError('无法运行更新后的 dev-notify。请重新打开终端后运行 dev-notify --version 检查。');
|
|
13
|
+
return result.status ?? 1;
|
|
14
|
+
}
|
|
15
|
+
function reason(error) {
|
|
16
|
+
return error instanceof ConfigError ? error.message : '请运行 dev-notify integration claude-code status 或 dev-notify reply status 检查。';
|
|
17
|
+
}
|
|
18
|
+
const normalize = (file) => process.platform === 'win32' ? path.resolve(file).toLowerCase() : path.resolve(file);
|
|
19
|
+
export async function postUpdate(options) {
|
|
20
|
+
const run = options.run ?? runCli;
|
|
21
|
+
const hooks = options.hooks ?? (() => claudeCodeStatus({ env: options.env }));
|
|
22
|
+
const bridge = options.bridge ?? (() => bridgeStatus(options.config));
|
|
23
|
+
const wait = options.wait ?? delay;
|
|
24
|
+
let failed = false;
|
|
25
|
+
let hooksStatus = null;
|
|
26
|
+
try {
|
|
27
|
+
hooksStatus = await hooks();
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
options.err(`无法检查 Claude Code hooks:${reason(error)}`);
|
|
31
|
+
failed = true;
|
|
32
|
+
}
|
|
33
|
+
if (hooksStatus) {
|
|
34
|
+
const count = hooksStatus.stop + hooksStatus.notification + hooksStatus.lifecycle + hooksStatus.interaction;
|
|
35
|
+
if (!count)
|
|
36
|
+
options.out('未检测到本工具的 Claude Code hooks,跳过。');
|
|
37
|
+
else if (!hooksStatus.configs.some(file => normalize(file) === normalize(options.config))) {
|
|
38
|
+
options.out('Claude Code hooks 绑定的是其他通知配置,跳过自动更新;如需更新,请用原来的 --config 运行 integration claude-code install。');
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
options.out('更新 Claude Code hooks ...');
|
|
42
|
+
try {
|
|
43
|
+
if (run(['integration', 'claude-code', 'install', '--yes', '--config', options.config]) === 0)
|
|
44
|
+
options.out('Claude Code hooks 已更新。');
|
|
45
|
+
else {
|
|
46
|
+
options.err('Claude Code hooks 更新失败。');
|
|
47
|
+
failed = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
options.err(`Claude Code hooks 更新失败:${reason(error)}`);
|
|
52
|
+
failed = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
let bridgeState = null;
|
|
57
|
+
try {
|
|
58
|
+
bridgeState = await bridge();
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
options.err(`无法检查回复桥接:${reason(error)}`);
|
|
62
|
+
failed = true;
|
|
63
|
+
}
|
|
64
|
+
if (bridgeState?.running) {
|
|
65
|
+
options.out('重启回复桥接 ...');
|
|
66
|
+
try {
|
|
67
|
+
// 0 表示已停止或本来未运行;1 表示停止请求已登记,下面等待进程释放锁。
|
|
68
|
+
run(['reply', 'stop', '--config', options.config]);
|
|
69
|
+
let stillRunning = true;
|
|
70
|
+
for (let i = 0; i < 20; i++) {
|
|
71
|
+
try {
|
|
72
|
+
stillRunning = (await bridge()).running;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
if (!stillRunning)
|
|
78
|
+
break;
|
|
79
|
+
await wait(500);
|
|
80
|
+
}
|
|
81
|
+
if (stillRunning) {
|
|
82
|
+
options.err('回复桥接仍在退出,请稍后运行 dev-notify reply start。');
|
|
83
|
+
failed = true;
|
|
84
|
+
}
|
|
85
|
+
else if (run(['reply', 'start', '--config', options.config]) === 0)
|
|
86
|
+
options.out('回复桥接已重启。');
|
|
87
|
+
else {
|
|
88
|
+
options.err('回复桥接启动失败。');
|
|
89
|
+
failed = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
options.err(`回复桥接重启失败:${reason(error)}`);
|
|
94
|
+
failed = true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (bridgeState)
|
|
98
|
+
options.out('未检测到运行中的回复桥接,跳过。');
|
|
99
|
+
return failed ? 1 : 0;
|
|
100
|
+
}
|
package/dist/update.js
CHANGED
|
@@ -63,6 +63,20 @@ async function confirmUpdate(latest, options) {
|
|
|
63
63
|
const prompts = options.prompts ?? terminalPrompts();
|
|
64
64
|
return /^(y|yes)$/i.test(await prompts.text(`更新到 ${latest}?[y/N] `));
|
|
65
65
|
}
|
|
66
|
+
async function runMaintenance(options, updated) {
|
|
67
|
+
if (!options.post) {
|
|
68
|
+
if (updated)
|
|
69
|
+
options.out('如有 Claude Code hooks,请运行 dev-notify integration claude-code install;如使用远程回复,请重启桥接。');
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
const code = await options.post();
|
|
73
|
+
if (code !== 0) {
|
|
74
|
+
options.out(updated ? '更新已完成,但后续维护步骤没有全部成功;请按上面的提示检查。' : '维护步骤没有全部成功;请按上面的提示检查。');
|
|
75
|
+
return code;
|
|
76
|
+
}
|
|
77
|
+
options.out(updated ? '更新后的维护步骤已完成。' : '维护步骤已完成。');
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
66
80
|
export async function updatePackage(current, options) {
|
|
67
81
|
if (!VERSION.test(current))
|
|
68
82
|
throw new ConfigError('当前版本号格式不受支持,无法检查更新。');
|
|
@@ -71,13 +85,19 @@ export async function updatePackage(current, options) {
|
|
|
71
85
|
throw new ConfigError('npm registry 返回的版本号格式不受支持。');
|
|
72
86
|
if (compareVersions(latest, current) <= 0) {
|
|
73
87
|
options.out(`已是最新版本 ${current}。`);
|
|
74
|
-
|
|
88
|
+
if (options.check)
|
|
89
|
+
return 0;
|
|
90
|
+
if (options.post)
|
|
91
|
+
options.out('将执行 Claude Code hooks 和回复桥接维护。');
|
|
92
|
+
return await runMaintenance(options, false);
|
|
75
93
|
}
|
|
76
94
|
options.out(`当前版本 ${current},npm 最新版本 ${latest}。`);
|
|
77
95
|
if (options.check) {
|
|
78
96
|
options.out('运行 dev-notify update 更新。');
|
|
79
97
|
return 0;
|
|
80
98
|
}
|
|
99
|
+
if (options.post)
|
|
100
|
+
options.out('更新完成后会自动重新安装绑定当前配置的 Claude Code hooks,并重启正在运行的回复桥接;可用 --no-post 跳过。');
|
|
81
101
|
if (!await confirmUpdate(latest, options)) {
|
|
82
102
|
options.out('已取消,没有更新。');
|
|
83
103
|
return 130;
|
|
@@ -87,6 +107,5 @@ export async function updatePackage(current, options) {
|
|
|
87
107
|
if (result.status !== 0)
|
|
88
108
|
throw new ConfigError(`更新没有完成。请检查 npm 权限,或手动运行 npm install -g ${NAME}@latest。`);
|
|
89
109
|
options.out(`已更新到 ${latest}。请重新打开终端后运行 dev-notify --version 确认。`);
|
|
90
|
-
|
|
91
|
-
return 0;
|
|
110
|
+
return await runMaintenance(options, true);
|
|
92
111
|
}
|