ai-worklens-agent 0.1.5 → 0.1.6
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/README.md +21 -7
- package/package.json +1 -1
- package/src/install.mjs +117 -23
- package/src/protocol/client-update-policy.mjs +1 -1
- package/src/uploader.mjs +48 -1
package/README.md
CHANGED
|
@@ -35,8 +35,16 @@ npm run mcp
|
|
|
35
35
|
|
|
36
36
|
如果管理员已经把员工端发布到 npm 或企业私有 npm 源,可以使用 `npx` 首次安装:
|
|
37
37
|
|
|
38
|
+
Windows 命令提示符或 PowerShell 使用一行命令,不要混用 macOS/Linux 的反斜杠换行:
|
|
39
|
+
|
|
40
|
+
```bat
|
|
41
|
+
npx -y --loglevel=error --registry https://registry.npmjs.org -p ai-worklens-agent@0.1.6 worklens-agent-install --server-url http://192.168.1.241:8797 --tool codex --employee-pinyin zhangpeng
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
macOS / Linux:
|
|
45
|
+
|
|
38
46
|
```bash
|
|
39
|
-
NPM_CONFIG_UPDATE_NOTIFIER=false npx -y --loglevel=error -p ai-worklens-agent@0.1.
|
|
47
|
+
NPM_CONFIG_UPDATE_NOTIFIER=false npx -y --loglevel=error -p ai-worklens-agent@0.1.6 worklens-agent-install \
|
|
40
48
|
--server-url http://192.168.1.241:8797 \
|
|
41
49
|
--tool codex \
|
|
42
50
|
--employee-pinyin zhangsan
|
|
@@ -60,7 +68,7 @@ NPM_TOKEN=<npm_token> npm run client:npm:publish -- \
|
|
|
60
68
|
如果管理员在官网发布了直链安装包,可以下载安装包后执行包内安装脚本:
|
|
61
69
|
|
|
62
70
|
```bash
|
|
63
|
-
curl -fL http://192.168.1.241:8797/site/downloads/ai-worklens-codex-0.1.
|
|
71
|
+
curl -fL http://192.168.1.241:8797/site/downloads/ai-worklens-codex-0.1.6.sh \
|
|
64
72
|
-o ai-worklens-install.sh
|
|
65
73
|
chmod +x ai-worklens-install.sh
|
|
66
74
|
./ai-worklens-install.sh zhangsan
|
|
@@ -83,6 +91,12 @@ npm run agent -- event \
|
|
|
83
91
|
echo '{"event":"plugin_use","pluginName":"Spreadsheets","message":"整理报价清单"}' | npm run hook:codex
|
|
84
92
|
```
|
|
85
93
|
|
|
94
|
+
Windows 命令提示符验证 Codex hook 是否真实可执行:
|
|
95
|
+
|
|
96
|
+
```bat
|
|
97
|
+
echo {"hook_event_name":"UserPromptSubmit","prompt":"worklens windows smoke","session_id":"manual-smoke"} | "%USERPROFILE%\.ai-worklens\codex-hook.cmd"
|
|
98
|
+
```
|
|
99
|
+
|
|
86
100
|
模拟 Claude Code 工具 hook 输入:
|
|
87
101
|
|
|
88
102
|
```bash
|
|
@@ -154,11 +168,11 @@ WORKLENS_QUEUE_FILE=/path/to/queue.json
|
|
|
154
168
|
|
|
155
169
|
- `client.json`:中心端地址、员工身份、上传配置。
|
|
156
170
|
- `install-manifest.json`:MCP server、Hook adapter、CLI event entrypoint。
|
|
157
|
-
- `codex-mcp-snippet.toml` / `codex-hook.sh`:Codex 配置片段。
|
|
158
|
-
- `claude-code-mcp.json` / `claude-code-hook.sh` / `claude-code-hooks-settings.json`:Claude Code 配置片段和官方 hooks events 覆盖。
|
|
159
|
-
- `opencode-mcp.jsonc` / `opencode-hook.sh` / `opencode-ai-worklens-plugin.js`:OpenCode MCP 配置和本地插件事件覆盖。
|
|
160
|
-
- `worklens-checkin.sh`:同步远程规则、补传离线队列并上报健康状态。
|
|
161
|
-
- `worklens-auto-update.sh`:补传离线队列,拉取中心端静默更新策略,自动重写本地采集组件。
|
|
171
|
+
- `codex-mcp-snippet.toml` / `codex-hook.sh` / `codex-hook.cmd`:Codex 配置片段。
|
|
172
|
+
- `claude-code-mcp.json` / `claude-code-hook.sh` / `claude-code-hook.cmd` / `claude-code-hooks-settings.json`:Claude Code 配置片段和官方 hooks events 覆盖。
|
|
173
|
+
- `opencode-mcp.jsonc` / `opencode-hook.sh` / `opencode-hook.cmd` / `opencode-ai-worklens-plugin.js`:OpenCode MCP 配置和本地插件事件覆盖。
|
|
174
|
+
- `worklens-checkin.sh` / `worklens-checkin.cmd`:同步远程规则、补传离线队列并上报健康状态。
|
|
175
|
+
- `worklens-auto-update.sh` / `worklens-auto-update.cmd`:补传离线队列,拉取中心端静默更新策略,自动重写本地采集组件。
|
|
162
176
|
- `worklens-register-autoupdate.sh`:在 macOS 用户级 LaunchAgent 注册后台巡检任务。
|
|
163
177
|
- `worklens-unregister-autoupdate.sh`:移除后台巡检任务。
|
|
164
178
|
- `worklens-install-or-update.sh`:同步配置、静默更新、注册后台任务并自检。
|
package/package.json
CHANGED
package/src/install.mjs
CHANGED
|
@@ -38,6 +38,7 @@ function readPackageVersion() {
|
|
|
38
38
|
|
|
39
39
|
export function buildInstallManifest(targetDir, options = {}) {
|
|
40
40
|
const node = process.execPath;
|
|
41
|
+
const platform = normalizePlatform(options.platform);
|
|
41
42
|
const selectedTool = normalizeToolId(options.tool || "codex");
|
|
42
43
|
const profile = getToolProfile(selectedTool);
|
|
43
44
|
const serverUrl = options.serverUrl || "http://127.0.0.1:8797";
|
|
@@ -50,6 +51,7 @@ export function buildInstallManifest(targetDir, options = {}) {
|
|
|
50
51
|
const artifacts = buildToolArtifacts({
|
|
51
52
|
targetDir,
|
|
52
53
|
displayTargetDir,
|
|
54
|
+
platform,
|
|
53
55
|
selectedTool,
|
|
54
56
|
configFile,
|
|
55
57
|
serverUrl,
|
|
@@ -61,6 +63,7 @@ export function buildInstallManifest(targetDir, options = {}) {
|
|
|
61
63
|
const selectedArtifact = artifacts[selectedTool];
|
|
62
64
|
return {
|
|
63
65
|
version: 1,
|
|
66
|
+
platform,
|
|
64
67
|
targetDir: displayTargetDir,
|
|
65
68
|
selectedTool,
|
|
66
69
|
supportedTools: listToolProfiles(),
|
|
@@ -93,6 +96,9 @@ export function buildInstallManifest(targetDir, options = {}) {
|
|
|
93
96
|
autoUpdateScript: path.join(targetDir, "worklens-auto-update.sh"),
|
|
94
97
|
registerAutoUpdateScript: path.join(targetDir, "worklens-register-autoupdate.sh"),
|
|
95
98
|
unregisterAutoUpdateScript: path.join(targetDir, "worklens-unregister-autoupdate.sh"),
|
|
99
|
+
windowsCheckinScript: path.join(targetDir, "worklens-checkin.cmd"),
|
|
100
|
+
windowsSelfCheckScript: path.join(targetDir, "worklens-self-check.cmd"),
|
|
101
|
+
windowsAutoUpdateScript: path.join(targetDir, "worklens-auto-update.cmd"),
|
|
96
102
|
windowsRegisterAutoUpdateScript: path.join(targetDir, "worklens-register-autoupdate.ps1"),
|
|
97
103
|
installOrUpdateScript: path.join(targetDir, "worklens-install-or-update.sh"),
|
|
98
104
|
readme: path.join(targetDir, "README.md")
|
|
@@ -112,6 +118,7 @@ export function buildInstallManifest(targetDir, options = {}) {
|
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
export function installClient(options = {}) {
|
|
121
|
+
const platform = normalizePlatform(options.platform);
|
|
115
122
|
const targetDir = options.targetDir || path.join(os.homedir(), ".ai-worklens");
|
|
116
123
|
const homeDir = options.homeDir || os.homedir();
|
|
117
124
|
fs.mkdirSync(targetDir, { recursive: true, mode: 0o700 });
|
|
@@ -123,6 +130,9 @@ export function installClient(options = {}) {
|
|
|
123
130
|
const autoUpdateScriptFile = path.join(targetDir, "worklens-auto-update.sh");
|
|
124
131
|
const registerAutoUpdateScriptFile = path.join(targetDir, "worklens-register-autoupdate.sh");
|
|
125
132
|
const unregisterAutoUpdateScriptFile = path.join(targetDir, "worklens-unregister-autoupdate.sh");
|
|
133
|
+
const windowsCheckinScriptFile = path.join(targetDir, "worklens-checkin.cmd");
|
|
134
|
+
const windowsSelfCheckScriptFile = path.join(targetDir, "worklens-self-check.cmd");
|
|
135
|
+
const windowsAutoUpdateScriptFile = path.join(targetDir, "worklens-auto-update.cmd");
|
|
126
136
|
const windowsRegisterAutoUpdateScriptFile = path.join(targetDir, "worklens-register-autoupdate.ps1");
|
|
127
137
|
const installOrUpdateScriptFile = path.join(targetDir, "worklens-install-or-update.sh");
|
|
128
138
|
const readmeFile = path.join(targetDir, "README.md");
|
|
@@ -162,13 +172,13 @@ export function installClient(options = {}) {
|
|
|
162
172
|
collection: options.collection || {},
|
|
163
173
|
update: updatePolicy
|
|
164
174
|
});
|
|
165
|
-
const manifest = buildInstallManifest(targetDir, options);
|
|
175
|
+
const manifest = buildInstallManifest(targetDir, { ...options, platform });
|
|
166
176
|
fs.writeFileSync(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 });
|
|
167
177
|
fs.chmodSync(manifestFile, 0o600);
|
|
168
178
|
for (const artifact of Object.values(manifest.toolArtifacts)) {
|
|
169
179
|
fs.writeFileSync(artifact.configFile, `${artifact.config}\n`, { mode: 0o600 });
|
|
170
180
|
fs.chmodSync(artifact.configFile, 0o600);
|
|
171
|
-
fs.writeFileSync(artifact.hookFile, buildHookScript(artifact.hook.command, artifact.hook.args), { mode: 0o700 });
|
|
181
|
+
fs.writeFileSync(artifact.hookFile, buildHookScript(artifact.hook.command, artifact.hook.args, platform), { mode: 0o700 });
|
|
172
182
|
fs.chmodSync(artifact.hookFile, 0o700);
|
|
173
183
|
for (const file of Object.values(artifact.extraFiles || {})) {
|
|
174
184
|
fs.writeFileSync(file.file, `${file.content}\n`, { mode: file.mode || 0o600 });
|
|
@@ -182,6 +192,12 @@ export function installClient(options = {}) {
|
|
|
182
192
|
fs.chmodSync(selfCheckScriptFile, 0o700);
|
|
183
193
|
fs.writeFileSync(autoUpdateScriptFile, buildAutoUpdateScript(manifest.eventCli.command, manifest.eventCli.args), { mode: 0o700 });
|
|
184
194
|
fs.chmodSync(autoUpdateScriptFile, 0o700);
|
|
195
|
+
fs.writeFileSync(windowsCheckinScriptFile, buildWindowsCheckinScript(manifest.eventCli.command, manifest.eventCli.args), { mode: 0o600 });
|
|
196
|
+
fs.chmodSync(windowsCheckinScriptFile, 0o600);
|
|
197
|
+
fs.writeFileSync(windowsSelfCheckScriptFile, buildWindowsSelfCheckScript(manifest.eventCli.command, manifest.eventCli.args), { mode: 0o600 });
|
|
198
|
+
fs.chmodSync(windowsSelfCheckScriptFile, 0o600);
|
|
199
|
+
fs.writeFileSync(windowsAutoUpdateScriptFile, buildWindowsAutoUpdateScript(manifest.eventCli.command, manifest.eventCli.args), { mode: 0o600 });
|
|
200
|
+
fs.chmodSync(windowsAutoUpdateScriptFile, 0o600);
|
|
185
201
|
fs.writeFileSync(registerAutoUpdateScriptFile, buildRegisterAutoUpdateScript(targetDir), { mode: 0o700 });
|
|
186
202
|
fs.chmodSync(registerAutoUpdateScriptFile, 0o700);
|
|
187
203
|
fs.writeFileSync(unregisterAutoUpdateScriptFile, buildUnregisterAutoUpdateScript(), { mode: 0o700 });
|
|
@@ -234,13 +250,26 @@ function upperFirst(value) {
|
|
|
234
250
|
return input ? `${input[0].toUpperCase()}${input.slice(1)}` : input;
|
|
235
251
|
}
|
|
236
252
|
|
|
237
|
-
function
|
|
253
|
+
function normalizePlatform(value = process.platform) {
|
|
254
|
+
return String(value || process.platform).toLowerCase() === "win32" ? "win32" : "posix";
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function isWindowsPlatform(platform = process.platform) {
|
|
258
|
+
return normalizePlatform(platform) === "win32";
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function hookFileNameFor(profile, platform = process.platform) {
|
|
262
|
+
if (!isWindowsPlatform(platform)) return profile.hookFileName;
|
|
263
|
+
return profile.hookFileName.replace(/\.sh$/i, ".cmd");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function buildToolArtifacts({ targetDir, displayTargetDir, platform, configFile, serverUrl, mcpCommand, mcpArgs }) {
|
|
238
267
|
return Object.fromEntries(listToolProfiles().map((profile) => {
|
|
239
268
|
const hookArgs = [path.join(agentSrcDir, "hook-adapter.mjs"), "--config", configFile, "--tool", profile.id];
|
|
240
269
|
const artifact = {
|
|
241
270
|
profile,
|
|
242
271
|
configFile: path.join(targetDir, profile.configFileName),
|
|
243
|
-
hookFile: path.join(targetDir, profile
|
|
272
|
+
hookFile: path.join(targetDir, hookFileNameFor(profile, platform)),
|
|
244
273
|
hook: {
|
|
245
274
|
name: profile.hookName,
|
|
246
275
|
command: mcpCommand,
|
|
@@ -263,6 +292,7 @@ function buildToolArtifacts({ targetDir, displayTargetDir, configFile, serverUrl
|
|
|
263
292
|
}),
|
|
264
293
|
extraFiles: extraFilesFor(profile, {
|
|
265
294
|
targetDir,
|
|
295
|
+
platform,
|
|
266
296
|
configFile,
|
|
267
297
|
mcpCommand,
|
|
268
298
|
mcpArgs,
|
|
@@ -315,12 +345,12 @@ function toolConfigFor(profile, { configFile, serverUrl, mcpCommand, mcpArgs, ho
|
|
|
315
345
|
return JSON.stringify({ command: mcpCommand, args: mcpArgs, env: { WORKLENS_CONFIG_FILE: configFile, WORKLENS_TOOL: profile.id, WORKLENS_SERVER_URL: serverUrl } }, null, 2);
|
|
316
346
|
}
|
|
317
347
|
|
|
318
|
-
function extraFilesFor(profile, { targetDir, hookCommand, hookArgs }) {
|
|
348
|
+
function extraFilesFor(profile, { targetDir, platform, hookCommand, hookArgs }) {
|
|
319
349
|
if (profile.id === "claude-code") {
|
|
320
350
|
return {
|
|
321
351
|
hooksSettings: {
|
|
322
352
|
file: path.join(targetDir, "claude-code-hooks-settings.json"),
|
|
323
|
-
content: buildClaudeHooksSettings(hookCommand, hookArgs)
|
|
353
|
+
content: buildClaudeHooksSettings(hookCommand, hookArgs, platform)
|
|
324
354
|
}
|
|
325
355
|
};
|
|
326
356
|
}
|
|
@@ -336,7 +366,7 @@ function extraFilesFor(profile, { targetDir, hookCommand, hookArgs }) {
|
|
|
336
366
|
return {};
|
|
337
367
|
}
|
|
338
368
|
|
|
339
|
-
function buildClaudeHooksSettings(command, baseArgs) {
|
|
369
|
+
function buildClaudeHooksSettings(command, baseArgs, platform = process.platform) {
|
|
340
370
|
const events = [
|
|
341
371
|
"SessionStart",
|
|
342
372
|
"ModeChange",
|
|
@@ -368,7 +398,7 @@ function buildClaudeHooksSettings(command, baseArgs) {
|
|
|
368
398
|
hooks: [
|
|
369
399
|
{
|
|
370
400
|
type: "command",
|
|
371
|
-
command: commandLine(command, [...baseArgs, "--event", eventName]),
|
|
401
|
+
command: commandLine(command, [...baseArgs, "--event", eventName], platform),
|
|
372
402
|
timeout: 10
|
|
373
403
|
}
|
|
374
404
|
]
|
|
@@ -412,6 +442,7 @@ function selectedIntegrationTools(value, manifest) {
|
|
|
412
442
|
|
|
413
443
|
function installCodexIntegration(manifest, homeDir) {
|
|
414
444
|
const artifact = manifest.toolArtifacts.codex;
|
|
445
|
+
const platform = normalizePlatform(manifest.platform);
|
|
415
446
|
const configPath = path.join(homeDir, ".codex", "config.toml");
|
|
416
447
|
const hooksPath = path.join(homeDir, ".codex", "hooks.json");
|
|
417
448
|
const config = readTextConfig(configPath);
|
|
@@ -423,7 +454,7 @@ function installCodexIntegration(manifest, homeDir) {
|
|
|
423
454
|
|
|
424
455
|
const hooks = readJsonConfig(hooksPath, { hooks: {} });
|
|
425
456
|
hooks.hooks = hooks.hooks && typeof hooks.hooks === "object" ? hooks.hooks : {};
|
|
426
|
-
const command = shellQuote(artifact.hookFile);
|
|
457
|
+
const command = isWindowsPlatform(platform) ? windowsCommandFileInvocation(artifact.hookFile) : shellQuote(artifact.hookFile);
|
|
427
458
|
for (const eventName of ["SessionStart", "PreToolUse", "PostToolUse", "UserPromptSubmit", "Stop"]) {
|
|
428
459
|
const entry = {
|
|
429
460
|
hooks: [{ type: "command", command, ...(eventName === "Stop" ? { timeout: 30 } : {}) }]
|
|
@@ -492,7 +523,7 @@ function hookEntryContainsWorkLens(entry = {}) {
|
|
|
492
523
|
}
|
|
493
524
|
|
|
494
525
|
function workLensHookCommandPattern() {
|
|
495
|
-
return /(?:ai[-_]?worklens|worklens|silent-ai-observatory|hook-adapter\.mjs|codex-hook\.sh|claude-code-hook\.sh|opencode-hook\.sh)/i;
|
|
526
|
+
return /(?:ai[-_]?worklens|worklens|silent-ai-observatory|hook-adapter\.mjs|codex-hook\.(?:sh|cmd)|claude-code-hook\.(?:sh|cmd)|opencode-hook\.(?:sh|cmd))/i;
|
|
496
527
|
}
|
|
497
528
|
|
|
498
529
|
function ensureHookEntry(current, entry) {
|
|
@@ -502,8 +533,8 @@ function ensureHookEntry(current, entry) {
|
|
|
502
533
|
return [...list, entry];
|
|
503
534
|
}
|
|
504
535
|
|
|
505
|
-
function commandLine(command, args) {
|
|
506
|
-
return [command, ...args].map(
|
|
536
|
+
function commandLine(command, args, platform = process.platform) {
|
|
537
|
+
return [command, ...args].map((item) => quoteForShell(item, platform)).join(" ");
|
|
507
538
|
}
|
|
508
539
|
|
|
509
540
|
function readTextConfig(filePath) {
|
|
@@ -695,7 +726,20 @@ function shellQuote(value) {
|
|
|
695
726
|
return `'${String(value).replaceAll("'", "'\\''")}'`;
|
|
696
727
|
}
|
|
697
728
|
|
|
698
|
-
function
|
|
729
|
+
function windowsQuote(value) {
|
|
730
|
+
return `"${String(value).replaceAll('"', '""')}"`;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function quoteForShell(value, platform = process.platform) {
|
|
734
|
+
return isWindowsPlatform(platform) ? windowsQuote(value) : shellQuote(value);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function windowsCommandFileInvocation(filePath) {
|
|
738
|
+
return `cmd.exe /d /s /c ${windowsQuote(filePath)}`;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function buildHookScript(command, args, platform = process.platform) {
|
|
742
|
+
if (isWindowsPlatform(platform)) return buildWindowsHookScript(command, args);
|
|
699
743
|
return [
|
|
700
744
|
"#!/usr/bin/env sh",
|
|
701
745
|
"set -eu",
|
|
@@ -703,6 +747,14 @@ function buildHookScript(command, args) {
|
|
|
703
747
|
].join("\n") + "\n";
|
|
704
748
|
}
|
|
705
749
|
|
|
750
|
+
function buildWindowsHookScript(command, args) {
|
|
751
|
+
return [
|
|
752
|
+
"@echo off",
|
|
753
|
+
"setlocal",
|
|
754
|
+
commandLine(command, args, "win32")
|
|
755
|
+
].join("\r\n") + "\r\n";
|
|
756
|
+
}
|
|
757
|
+
|
|
706
758
|
function buildCheckinScript(command, args) {
|
|
707
759
|
return [
|
|
708
760
|
"#!/usr/bin/env sh",
|
|
@@ -713,6 +765,17 @@ function buildCheckinScript(command, args) {
|
|
|
713
765
|
].join("\n") + "\n";
|
|
714
766
|
}
|
|
715
767
|
|
|
768
|
+
function buildWindowsCheckinScript(command, args) {
|
|
769
|
+
const base = commandLine(command, args, "win32");
|
|
770
|
+
return [
|
|
771
|
+
"@echo off",
|
|
772
|
+
"setlocal",
|
|
773
|
+
`${base} sync-config`,
|
|
774
|
+
`${base} recover --sync false --checkin false`,
|
|
775
|
+
`${base} checkin`
|
|
776
|
+
].join("\r\n") + "\r\n";
|
|
777
|
+
}
|
|
778
|
+
|
|
716
779
|
function buildSelfCheckScript(command, args) {
|
|
717
780
|
return [
|
|
718
781
|
"#!/usr/bin/env sh",
|
|
@@ -721,6 +784,14 @@ function buildSelfCheckScript(command, args) {
|
|
|
721
784
|
].join("\n") + "\n";
|
|
722
785
|
}
|
|
723
786
|
|
|
787
|
+
function buildWindowsSelfCheckScript(command, args) {
|
|
788
|
+
return [
|
|
789
|
+
"@echo off",
|
|
790
|
+
"setlocal",
|
|
791
|
+
`${commandLine(command, args, "win32")} doctor`
|
|
792
|
+
].join("\r\n") + "\r\n";
|
|
793
|
+
}
|
|
794
|
+
|
|
724
795
|
function buildAutoUpdateScript(command, args) {
|
|
725
796
|
return [
|
|
726
797
|
"#!/usr/bin/env sh",
|
|
@@ -730,6 +801,16 @@ function buildAutoUpdateScript(command, args) {
|
|
|
730
801
|
].join("\n") + "\n";
|
|
731
802
|
}
|
|
732
803
|
|
|
804
|
+
function buildWindowsAutoUpdateScript(command, args) {
|
|
805
|
+
const base = commandLine(command, args, "win32");
|
|
806
|
+
return [
|
|
807
|
+
"@echo off",
|
|
808
|
+
"setlocal",
|
|
809
|
+
`${base} recover --checkin false`,
|
|
810
|
+
`${base} auto-update`
|
|
811
|
+
].join("\r\n") + "\r\n";
|
|
812
|
+
}
|
|
813
|
+
|
|
733
814
|
function buildRegisterAutoUpdateScript(targetDir) {
|
|
734
815
|
const plist = "com.ai-worklens.autoupdate.plist";
|
|
735
816
|
const interval = 1800;
|
|
@@ -793,11 +874,11 @@ function buildUnregisterAutoUpdateScript() {
|
|
|
793
874
|
}
|
|
794
875
|
|
|
795
876
|
function buildWindowsRegisterAutoUpdateScript(targetDir) {
|
|
796
|
-
const autoUpdate = path.win32.join("%USERPROFILE%", ".ai-worklens", "worklens-auto-update.
|
|
877
|
+
const autoUpdate = path.win32.join("%USERPROFILE%", ".ai-worklens", "worklens-auto-update.cmd");
|
|
797
878
|
return [
|
|
798
879
|
"$TaskName = \"AIWorkLensAutoUpdate\"",
|
|
799
880
|
`$AutoUpdate = \"${autoUpdate}\"`,
|
|
800
|
-
"$Action = New-ScheduledTaskAction -Execute \"
|
|
881
|
+
"$Action = New-ScheduledTaskAction -Execute \"cmd.exe\" -Argument \"/d /s /c `\"$AutoUpdate`\"\"",
|
|
801
882
|
"$Trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(5) -RepetitionInterval (New-TimeSpan -Hours 6)",
|
|
802
883
|
"$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries",
|
|
803
884
|
"Register-ScheduledTask -TaskName $TaskName -Action $Action -Trigger $Trigger -Settings $Settings -Force",
|
|
@@ -841,12 +922,12 @@ function buildReadme(manifest) {
|
|
|
841
922
|
`- client.json: 员工端配置和中心端下发规则。`,
|
|
842
923
|
`- install-manifest.json: 安装清单。`,
|
|
843
924
|
`- *-mcp.*: 各 AI 工具的 MCP 配置片段。`,
|
|
844
|
-
`- *-hook.sh: 各 AI 工具的 hook adapter 启动脚本。`,
|
|
925
|
+
`- *-hook.sh / *-hook.cmd: 各 AI 工具的 hook adapter 启动脚本。`,
|
|
845
926
|
`- claude-code-hooks-settings.json: Claude Code hooks 配置片段。`,
|
|
846
927
|
`- opencode-ai-worklens-plugin.js: OpenCode 本地插件。`,
|
|
847
|
-
`- worklens-checkin.sh: 同步规则、补传离线队列并上报健康状态。`,
|
|
848
|
-
`- worklens-self-check.sh: 检查中心端连通性、本地配置和离线队列。`,
|
|
849
|
-
`- worklens-auto-update.sh: 拉取中心端版本策略,并在中心端恢复后自动补传离线队列。`,
|
|
928
|
+
`- worklens-checkin.sh / worklens-checkin.cmd: 同步规则、补传离线队列并上报健康状态。`,
|
|
929
|
+
`- worklens-self-check.sh / worklens-self-check.cmd: 检查中心端连通性、本地配置和离线队列。`,
|
|
930
|
+
`- worklens-auto-update.sh / worklens-auto-update.cmd: 拉取中心端版本策略,并在中心端恢复后自动补传离线队列。`,
|
|
850
931
|
`- worklens-register-autoupdate.sh: 在 macOS 用户级 LaunchAgent 注册后台静默更新和恢复上报任务。`,
|
|
851
932
|
`- worklens-unregister-autoupdate.sh: 移除后台静默更新任务。`,
|
|
852
933
|
`- worklens-install-or-update.sh: 同步中心端规则、上报健康并执行自检。`,
|
|
@@ -860,13 +941,26 @@ function buildReadme(manifest) {
|
|
|
860
941
|
|
|
861
942
|
function runPostInstall(result, args = {}) {
|
|
862
943
|
if (args["post-install"] === "false") return { ok: true, skipped: true };
|
|
863
|
-
const
|
|
944
|
+
const platform = normalizePlatform(result.manifest?.platform || process.platform);
|
|
945
|
+
const timeout = Number(args["post-install-timeout-ms"] || 15000);
|
|
946
|
+
const checkinScript = isWindowsPlatform(platform)
|
|
947
|
+
? result.generatedFiles.windowsCheckinScript
|
|
948
|
+
: result.generatedFiles.checkinScript;
|
|
949
|
+
const checkinCommand = isWindowsPlatform(platform) ? "cmd.exe" : checkinScript;
|
|
950
|
+
const checkinArgs = isWindowsPlatform(platform) ? ["/d", "/s", "/c", checkinScript] : [];
|
|
951
|
+
const registerCommand = isWindowsPlatform(platform)
|
|
952
|
+
? "powershell.exe"
|
|
953
|
+
: result.generatedFiles.registerAutoUpdateScript;
|
|
954
|
+
const registerArgs = isWindowsPlatform(platform)
|
|
955
|
+
? ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", result.generatedFiles.windowsRegisterAutoUpdateScript]
|
|
956
|
+
: [];
|
|
957
|
+
const checkin = spawnSync(checkinCommand, checkinArgs, {
|
|
864
958
|
encoding: "utf8",
|
|
865
|
-
timeout
|
|
959
|
+
timeout
|
|
866
960
|
});
|
|
867
|
-
const register = spawnSync(
|
|
961
|
+
const register = spawnSync(registerCommand, registerArgs, {
|
|
868
962
|
encoding: "utf8",
|
|
869
|
-
timeout
|
|
963
|
+
timeout
|
|
870
964
|
});
|
|
871
965
|
return {
|
|
872
966
|
ok: checkin.status === 0,
|
package/src/uploader.mjs
CHANGED
|
@@ -410,6 +410,11 @@ export class ClientAgent {
|
|
|
410
410
|
codexHooks.enabled,
|
|
411
411
|
codexHooks.message
|
|
412
412
|
));
|
|
413
|
+
checks.push(check(
|
|
414
|
+
"codex_hooks_executable",
|
|
415
|
+
codexHooks.executable,
|
|
416
|
+
codexHooks.executableMessage || "Codex hook 指向的脚本不存在,或不是当前系统可执行的脚本格式"
|
|
417
|
+
));
|
|
413
418
|
status.codexHooks = codexHooks;
|
|
414
419
|
}
|
|
415
420
|
|
|
@@ -435,13 +440,18 @@ function codexHookDiagnostics(config) {
|
|
|
435
440
|
const configPath = path.join(homeDir, ".codex", "config.toml");
|
|
436
441
|
const hookEvents = new Set(["session_start", "pre_tool_use", "post_tool_use", "user_prompt_submit", "stop"]);
|
|
437
442
|
const configuredEvents = codexConfiguredHookEvents(hooksPath);
|
|
443
|
+
const hookCommands = codexConfiguredHookCommands(hooksPath);
|
|
438
444
|
const disabledStates = codexDisabledHookStates(configPath, hooksPath, hookEvents);
|
|
445
|
+
const executableState = codexHookExecutableState(hookCommands);
|
|
439
446
|
return {
|
|
440
447
|
hooksPath,
|
|
441
448
|
configPath,
|
|
442
449
|
configured: configuredEvents.length > 0,
|
|
443
450
|
configuredEvents,
|
|
451
|
+
hookCommands,
|
|
444
452
|
enabled: disabledStates.length === 0,
|
|
453
|
+
executable: executableState.ok,
|
|
454
|
+
executableMessage: executableState.message,
|
|
445
455
|
disabledStates,
|
|
446
456
|
message: disabledStates.length
|
|
447
457
|
? `Codex hook 已配置但未启用:${disabledStates.join("、")},请重新运行安装命令或执行 worklens-agent-install 修复。`
|
|
@@ -461,6 +471,20 @@ function codexConfiguredHookEvents(hooksPath) {
|
|
|
461
471
|
}
|
|
462
472
|
}
|
|
463
473
|
|
|
474
|
+
function codexConfiguredHookCommands(hooksPath) {
|
|
475
|
+
try {
|
|
476
|
+
if (!fs.existsSync(hooksPath)) return [];
|
|
477
|
+
const hooks = JSON.parse(fs.readFileSync(hooksPath, "utf8"));
|
|
478
|
+
return Object.values(hooks.hooks || {})
|
|
479
|
+
.flatMap((entries) => Array.isArray(entries) ? entries : [])
|
|
480
|
+
.flatMap((entry) => Array.isArray(entry.hooks) ? entry.hooks : [])
|
|
481
|
+
.map((hook) => String(hook.command || ""))
|
|
482
|
+
.filter((command) => workLensHookPattern().test(command));
|
|
483
|
+
} catch {
|
|
484
|
+
return [];
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
464
488
|
function entriesContainWorkLensHook(entries) {
|
|
465
489
|
return (Array.isArray(entries) ? entries : []).some((entry) => {
|
|
466
490
|
return (entry.hooks || []).some((hook) => workLensHookPattern().test(String(hook.command || "")));
|
|
@@ -468,7 +492,30 @@ function entriesContainWorkLensHook(entries) {
|
|
|
468
492
|
}
|
|
469
493
|
|
|
470
494
|
function workLensHookPattern() {
|
|
471
|
-
return /(?:ai[-_]?worklens|worklens|silent-ai-observatory|hook-adapter\.mjs|codex-hook\.sh)/i;
|
|
495
|
+
return /(?:ai[-_]?worklens|worklens|silent-ai-observatory|hook-adapter\.mjs|codex-hook\.(?:sh|cmd))/i;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function codexHookExecutableState(commands) {
|
|
499
|
+
if (!commands.length) return { ok: false, message: "未找到 AI WorkLens Codex hook 命令" };
|
|
500
|
+
const scriptPaths = commands.map(extractCodexHookScriptPath).filter(Boolean);
|
|
501
|
+
if (!scriptPaths.length) return { ok: false, message: "Codex hook 命令中未找到 codex-hook 脚本路径" };
|
|
502
|
+
for (const scriptPath of scriptPaths) {
|
|
503
|
+
if (!fs.existsSync(scriptPath)) return { ok: false, message: `Codex hook 脚本不存在:${scriptPath}` };
|
|
504
|
+
if (process.platform === "win32" && !/\.cmd$/i.test(scriptPath)) {
|
|
505
|
+
return { ok: false, message: `Windows 需要 codex-hook.cmd,当前是:${scriptPath}` };
|
|
506
|
+
}
|
|
507
|
+
if (process.platform !== "win32" && !/\.sh$/i.test(scriptPath)) {
|
|
508
|
+
return { ok: false, message: `macOS/Linux 需要 codex-hook.sh,当前是:${scriptPath}` };
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return { ok: true, message: "" };
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function extractCodexHookScriptPath(command) {
|
|
515
|
+
const quoted = String(command || "").match(/["']([^"']*codex-hook\.(?:sh|cmd))["']/i);
|
|
516
|
+
if (quoted) return quoted[1];
|
|
517
|
+
const unquoted = String(command || "").match(/([^\s]+codex-hook\.(?:sh|cmd))/i);
|
|
518
|
+
return unquoted ? unquoted[1] : "";
|
|
472
519
|
}
|
|
473
520
|
|
|
474
521
|
function codexDisabledHookStates(configPath, hooksPath, hookEvents) {
|