ai-worklens-agent 0.1.2 → 0.1.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.
package/README.md CHANGED
@@ -36,8 +36,8 @@ npm run mcp
36
36
  如果管理员已经把员工端发布到 npm 或企业私有 npm 源,可以使用 `npx` 首次安装:
37
37
 
38
38
  ```bash
39
- NPM_CONFIG_UPDATE_NOTIFIER=false npx -y --loglevel=error -p ai-worklens-agent@0.1.2 worklens-agent-install \
40
- --server-url http://127.0.0.1:8797 \
39
+ NPM_CONFIG_UPDATE_NOTIFIER=false npx -y --loglevel=error -p ai-worklens-agent@0.1.5 worklens-agent-install \
40
+ --server-url http://192.168.1.241:8797 \
41
41
  --tool codex \
42
42
  --employee-pinyin zhangsan
43
43
  ```
@@ -60,7 +60,7 @@ NPM_TOKEN=<npm_token> npm run client:npm:publish -- \
60
60
  如果管理员在官网发布了直链安装包,可以下载安装包后执行包内安装脚本:
61
61
 
62
62
  ```bash
63
- curl -fL http://127.0.0.1:8797/site/downloads/ai-worklens-codex-0.1.0.sh \
63
+ curl -fL http://192.168.1.241:8797/site/downloads/ai-worklens-codex-0.1.5.sh \
64
64
  -o ai-worklens-install.sh
65
65
  chmod +x ai-worklens-install.sh
66
66
  ./ai-worklens-install.sh zhangsan
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-worklens-agent",
3
- "version": "0.1.2",
3
+ "version": "0.1.5",
4
4
  "description": "Employee-side collector agent for AI WorkLens.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/config.mjs CHANGED
@@ -22,6 +22,92 @@ function readJson(filePath) {
22
22
  }
23
23
  }
24
24
 
25
+ function readText(filePath) {
26
+ try {
27
+ if (!fs.existsSync(filePath)) return "";
28
+ return fs.readFileSync(filePath, "utf8");
29
+ } catch {
30
+ return "";
31
+ }
32
+ }
33
+
34
+ function firstValue(...values) {
35
+ return values.map((value) => String(value || "").trim()).find(Boolean) || "";
36
+ }
37
+
38
+ function knownModel(model) {
39
+ return Boolean(model?.label && model.label !== "unknown");
40
+ }
41
+
42
+ function rootTomlString(content, key) {
43
+ const keyPattern = new RegExp(`^${key}\\s*=\\s*["']([^"']+)["']`);
44
+ for (const line of String(content || "").split(/\r?\n/)) {
45
+ const trimmed = line.trim();
46
+ if (!trimmed || trimmed.startsWith("#")) continue;
47
+ if (trimmed.startsWith("[")) return "";
48
+ const match = trimmed.match(keyPattern);
49
+ if (match) return match[1].trim();
50
+ }
51
+ return "";
52
+ }
53
+
54
+ function jsonishString(content, key) {
55
+ const match = String(content || "").match(new RegExp(`["']?${key}["']?\\s*[:=]\\s*["']([^"']+)["']`));
56
+ return match?.[1]?.trim() || "";
57
+ }
58
+
59
+ function inferCodexModel(homeDir, env) {
60
+ const codexConfig = readText(path.join(homeDir, ".codex", "config.toml"));
61
+ return normalizeModelInfo({
62
+ provider: firstValue(env.OPENAI_PROVIDER, "openai"),
63
+ name: firstValue(
64
+ env.CODEX_MODEL,
65
+ env.OPENAI_MODEL,
66
+ rootTomlString(codexConfig, "model"),
67
+ jsonishString(codexConfig, "model")
68
+ )
69
+ });
70
+ }
71
+
72
+ function inferClaudeModel(homeDir, env) {
73
+ const claudeSettings = readJson(path.join(homeDir, ".claude", "settings.json"));
74
+ const claudeJson = readJson(path.join(homeDir, ".claude.json"));
75
+ return normalizeModelInfo({
76
+ provider: firstValue(env.ANTHROPIC_PROVIDER, "anthropic"),
77
+ name: firstValue(
78
+ env.CLAUDE_CODE_MODEL,
79
+ env.CLAUDE_MODEL,
80
+ env.ANTHROPIC_MODEL,
81
+ claudeSettings.model,
82
+ claudeSettings.modelName,
83
+ claudeSettings.env?.ANTHROPIC_MODEL,
84
+ claudeJson.model,
85
+ claudeJson.modelName
86
+ )
87
+ });
88
+ }
89
+
90
+ function inferOpenCodeModel(homeDir, env) {
91
+ const candidates = [
92
+ path.join(homeDir, ".config", "opencode", "opencode.json"),
93
+ path.join(homeDir, ".config", "opencode", "opencode.jsonc"),
94
+ path.join(homeDir, ".opencode", "config.json"),
95
+ path.join(homeDir, ".opencode", "config.jsonc")
96
+ ];
97
+ const configText = candidates.map(readText).find(Boolean) || "";
98
+ return normalizeModelInfo({
99
+ name: firstValue(env.OPENCODE_MODEL, env.OPENAI_MODEL, jsonishString(configText, "model")),
100
+ provider: firstValue(env.OPENCODE_PROVIDER, jsonishString(configText, "provider"))
101
+ });
102
+ }
103
+
104
+ function inferToolModel({ tool, homeDir, env }) {
105
+ const normalizedTool = normalizeToolId(tool);
106
+ if (normalizedTool === "claude-code") return inferClaudeModel(homeDir, env);
107
+ if (normalizedTool === "opencode") return inferOpenCodeModel(homeDir, env);
108
+ return inferCodexModel(homeDir, env);
109
+ }
110
+
25
111
  function defaultClientId() {
26
112
  const seed = `${os.hostname()}:${os.userInfo().username}`;
27
113
  return `device_${crypto.createHash("sha256").update(seed).digest("hex").slice(0, 12)}`;
@@ -38,31 +124,38 @@ export function defaultQueueFile(configFile = defaultConfigFile()) {
38
124
  export function loadClientConfig(options = {}) {
39
125
  const configFile = options.configFile || defaultConfigFile();
40
126
  const fileConfig = readJson(configFile);
41
- const env = process.env;
127
+ const env = options.env || process.env;
128
+ const homeDir = options.homeDir || envValue(env, "WORKLENS_HOME_DIR") || fileConfig.homeDir || os.homedir();
129
+ const tool = normalizeToolId(options.tool || envValue(env, "WORKLENS_TOOL") || fileConfig.tool || "codex");
42
130
  const serverUrl = options.serverUrl || envValue(env, "WORKLENS_SERVER_URL") || fileConfig.serverUrl || "http://127.0.0.1:8797";
43
131
  const collection = normalizeCollectionSettings(options.collection || fileConfig.collection || {});
132
+ const configuredModel = normalizeModelInfo({
133
+ ...fileConfig.model,
134
+ provider: options.modelProvider || envValue(env, "WORKLENS_MODEL_PROVIDER") || fileConfig.model?.provider,
135
+ name: options.modelName || envValue(env, "WORKLENS_MODEL_NAME") || fileConfig.model?.name,
136
+ version: options.modelVersion || envValue(env, "WORKLENS_MODEL_VERSION") || fileConfig.model?.version,
137
+ family: options.modelFamily || envValue(env, "WORKLENS_MODEL_FAMILY") || fileConfig.model?.family
138
+ });
139
+ const inferredModel = inferToolModel({ tool, homeDir, env });
140
+ const employeePinyin = options.employeePinyin || options.pinyinName || envValue(env, "WORKLENS_EMPLOYEE_PINYIN") || fileConfig.employee?.pinyinName || "";
141
+ const employeeId = options.employeeId || envValue(env, "WORKLENS_EMPLOYEE_ID") || fileConfig.employee?.id || employeePinyin || os.userInfo().username;
44
142
  const employee = {
45
- id: options.employeeId || envValue(env, "WORKLENS_EMPLOYEE_ID") || fileConfig.employee?.id || os.userInfo().username,
46
- name: options.employeeName || envValue(env, "WORKLENS_EMPLOYEE_NAME") || fileConfig.employee?.name || os.userInfo().username,
47
- pinyinName: options.employeePinyin || options.pinyinName || envValue(env, "WORKLENS_EMPLOYEE_PINYIN") || fileConfig.employee?.pinyinName || "",
143
+ id: employeeId,
144
+ name: options.employeeName || envValue(env, "WORKLENS_EMPLOYEE_NAME") || fileConfig.employee?.name || employeeId || os.userInfo().username,
145
+ pinyinName: employeePinyin,
48
146
  department: options.department || envValue(env, "WORKLENS_DEPARTMENT") || fileConfig.employee?.department || "",
49
147
  role: options.role || envValue(env, "WORKLENS_ROLE") || fileConfig.employee?.role || ""
50
148
  };
51
149
  return {
52
150
  configFile,
151
+ homeDir,
53
152
  queueFile: options.queueFile || defaultQueueFile(configFile),
54
153
  serverUrl,
55
154
  collectorToken: options.collectorToken || envValue(env, "WORKLENS_COLLECTOR_TOKEN") || fileConfig.collectorToken || "",
56
155
  clientId: options.clientId || envValue(env, "WORKLENS_CLIENT_ID") || fileConfig.clientId || defaultClientId(),
57
156
  clientVersion: options.clientVersion || fileConfig.clientVersion || "0.1.0",
58
- tool: normalizeToolId(options.tool || envValue(env, "WORKLENS_TOOL") || fileConfig.tool || "codex"),
59
- model: normalizeModelInfo({
60
- ...fileConfig.model,
61
- provider: options.modelProvider || envValue(env, "WORKLENS_MODEL_PROVIDER") || fileConfig.model?.provider,
62
- name: options.modelName || envValue(env, "WORKLENS_MODEL_NAME") || fileConfig.model?.name,
63
- version: options.modelVersion || envValue(env, "WORKLENS_MODEL_VERSION") || fileConfig.model?.version,
64
- family: options.modelFamily || envValue(env, "WORKLENS_MODEL_FAMILY") || fileConfig.model?.family
65
- }),
157
+ tool,
158
+ model: knownModel(configuredModel) ? configuredModel : inferredModel,
66
159
  workspaceRoot: options.workspaceRoot || envValue(env, "WORKLENS_WORKSPACE_ROOT") || process.cwd(),
67
160
  repoName: options.repoName || envValue(env, "WORKLENS_REPO_NAME") || path.basename(options.workspaceRoot || envValue(env, "WORKLENS_WORKSPACE_ROOT") || process.cwd()),
68
161
  branch: options.branch || envValue(env, "WORKLENS_BRANCH") || fileConfig.branch || "",
@@ -70,11 +70,17 @@ function eventTypeFromHook(name, payload) {
70
70
  if (["pretooluse", "toolcall", "tooluse", "toolusebefore", "toolexecutebefore"].includes(compact)) {
71
71
  const toolName = toolNameFromPayload(payload);
72
72
  const command = commandFromPayload(payload);
73
+ if (skillNameFromPayload(payload)) return "skill_use";
74
+ if (mcpServerFromPayload(payload)) return "mcp_tool_call";
75
+ if (pluginNameFromPayload(payload)) return "plugin_use";
73
76
  return /bash|shell|terminal|command/i.test(toolName) ? commandEventType(command) : "tool_call";
74
77
  }
75
78
  if (["posttooluse", "posttoolbatch", "tooluseafter", "toolexecuteafter"].includes(compact)) {
76
79
  const toolName = toolNameFromPayload(payload);
77
80
  const command = commandFromPayload(payload);
81
+ if (skillNameFromPayload(payload)) return "skill_use";
82
+ if (mcpServerFromPayload(payload)) return "mcp_tool_call";
83
+ if (pluginNameFromPayload(payload)) return "plugin_use";
78
84
  return /bash|shell|terminal|command/i.test(toolName) ? commandEventType(command) : "tool_result";
79
85
  }
80
86
  if (["commandexecuted", "tuicommandexecute"].includes(compact)) return commandEventType(commandFromPayload(payload));
@@ -96,13 +102,32 @@ function commandEventType(command) {
96
102
  }
97
103
 
98
104
  function toolNameFromPayload(payload) {
99
- return payload.toolName ||
100
- payload.tool_name ||
101
- payload.tool ||
102
- payload.input?.tool ||
103
- payload.output?.tool ||
104
- payload.request?.tool ||
105
- "";
105
+ return firstText(
106
+ payload.toolName,
107
+ payload.tool_name,
108
+ payload.tool,
109
+ payload.name,
110
+ payload.input?.toolName,
111
+ payload.input?.tool_name,
112
+ payload.input?.tool,
113
+ payload.input?.name,
114
+ payload.output?.toolName,
115
+ payload.output?.tool_name,
116
+ payload.output?.tool,
117
+ payload.output?.name,
118
+ payload.request?.toolName,
119
+ payload.request?.tool_name,
120
+ payload.request?.tool,
121
+ payload.tool_input?.toolName,
122
+ payload.tool_input?.tool_name,
123
+ payload.tool_input?.tool,
124
+ payload.toolInput?.toolName,
125
+ payload.toolInput?.tool_name,
126
+ payload.toolInput?.tool,
127
+ payload.tool_args?.toolName,
128
+ payload.tool_args?.tool_name,
129
+ payload.tool_args?.tool
130
+ );
106
131
  }
107
132
 
108
133
  function firstText(...values) {
@@ -127,6 +152,111 @@ function modeFromPayload(payload) {
127
152
  );
128
153
  }
129
154
 
155
+ function skillNameFromPayload(payload) {
156
+ return firstText(
157
+ payload.skillName,
158
+ payload.skill_name,
159
+ payload.skill,
160
+ payload.metadata?.skillName,
161
+ payload.metadata?.skill_name,
162
+ payload.metadata?.skill,
163
+ payload.context?.skillName,
164
+ payload.context?.skill_name,
165
+ payload.context?.skill,
166
+ payload.input?.skillName,
167
+ payload.input?.skill_name,
168
+ payload.input?.skill,
169
+ payload.output?.skillName,
170
+ payload.output?.skill_name,
171
+ payload.output?.skill
172
+ );
173
+ }
174
+
175
+ function pluginNameFromPayload(payload) {
176
+ const explicit = firstText(
177
+ payload.pluginName,
178
+ payload.plugin_name,
179
+ payload.plugin,
180
+ payload.metadata?.pluginName,
181
+ payload.metadata?.plugin_name,
182
+ payload.metadata?.plugin,
183
+ payload.context?.pluginName,
184
+ payload.context?.plugin_name,
185
+ payload.context?.plugin,
186
+ payload.input?.pluginName,
187
+ payload.input?.plugin_name,
188
+ payload.input?.plugin,
189
+ payload.output?.pluginName,
190
+ payload.output?.plugin_name,
191
+ payload.output?.plugin
192
+ );
193
+ if (explicit) return explicit;
194
+ return pluginNameFromMcpNamespace(mcpNamespaceFromToolName(toolNameFromPayload(payload)));
195
+ }
196
+
197
+ function mcpServerFromPayload(payload) {
198
+ const explicit = firstText(
199
+ payload.mcpServer,
200
+ payload.mcp_server,
201
+ payload.server,
202
+ payload.metadata?.mcpServer,
203
+ payload.metadata?.mcp_server,
204
+ payload.metadata?.server,
205
+ payload.context?.mcpServer,
206
+ payload.context?.mcp_server,
207
+ payload.context?.server,
208
+ payload.input?.mcpServer,
209
+ payload.input?.mcp_server,
210
+ payload.input?.server,
211
+ payload.output?.mcpServer,
212
+ payload.output?.mcp_server,
213
+ payload.output?.server
214
+ );
215
+ if (explicit) return normalizeMcpServerName(explicit);
216
+ return normalizeMcpServerName(mcpNamespaceFromToolName(toolNameFromPayload(payload)));
217
+ }
218
+
219
+ function mcpNamespaceFromToolName(value) {
220
+ const toolName = String(value || "");
221
+ const match = toolName.match(/^mcp__(.+?)(?:[.:].*)?$/);
222
+ return match ? match[1] : "";
223
+ }
224
+
225
+ function normalizeMcpServerName(value) {
226
+ const raw = String(value || "").trim();
227
+ if (!raw) return "";
228
+ if (raw.startsWith("codex_apps__")) return raw.slice("codex_apps__".length).replaceAll("_", "-");
229
+ return raw.replaceAll("_", "-");
230
+ }
231
+
232
+ function pluginNameFromMcpNamespace(value) {
233
+ const raw = String(value || "").trim();
234
+ if (!raw) return "";
235
+ const normalized = normalizeMcpServerName(raw);
236
+ const key = normalized.toLowerCase();
237
+ const rawKey = raw.toLowerCase();
238
+ const mapping = {
239
+ playwright: "Browser",
240
+ browser: "Browser",
241
+ chrome: "Chrome",
242
+ "computer-use": "Computer Use",
243
+ figma: "Figma",
244
+ "outlook-email": "Outlook Email",
245
+ presentations: "Presentations",
246
+ spreadsheets: "Spreadsheets"
247
+ };
248
+ if (rawKey.startsWith("codex_apps__")) return mapping[key] || upperPluginName(normalized);
249
+ return mapping[key] || "";
250
+ }
251
+
252
+ function upperPluginName(value) {
253
+ return String(value || "")
254
+ .split(/[-_\s]+/)
255
+ .filter(Boolean)
256
+ .map((part) => `${part[0]?.toUpperCase() || ""}${part.slice(1)}`)
257
+ .join(" ");
258
+ }
259
+
130
260
  function previousModeFromPayload(payload) {
131
261
  return firstText(
132
262
  payload.previousMode,
@@ -268,6 +398,10 @@ export function normalizeHookPayload(payload, args, config) {
268
398
  const eventType = eventTypeFromHook(name, payload);
269
399
  const summary = pickSummary(payload, name, eventType);
270
400
  const metadata = payload.metadata && typeof payload.metadata === "object" ? payload.metadata : {};
401
+ const skillName = skillNameFromPayload(payload);
402
+ const pluginName = pluginNameFromPayload(payload);
403
+ const mcpServer = mcpServerFromPayload(payload);
404
+ const toolName = toolNameFromPayload(payload);
271
405
  return buildEvent({
272
406
  ...payload,
273
407
  eventType,
@@ -277,9 +411,9 @@ export function normalizeHookPayload(payload, args, config) {
277
411
  hookName: name,
278
412
  localSessionId: payload.sessionId || payload.session_id || payload.localSessionId,
279
413
  turnIndex: payload.turnIndex || payload.turn_index,
280
- skillName: payload.skillName || payload.skill_name || payload.skill,
281
- pluginName: payload.pluginName || payload.plugin_name || payload.plugin,
282
- mcpServer: payload.mcpServer || payload.mcp_server || payload.server,
414
+ skillName,
415
+ pluginName,
416
+ mcpServer,
283
417
  files: fileRefsFromPayload(payload),
284
418
  commands: commandFromPayload(payload),
285
419
  durationSeconds: payload.durationSeconds || payload.duration_seconds || payload.duration_ms && Number(payload.duration_ms) / 1000,
@@ -289,7 +423,11 @@ export function normalizeHookPayload(payload, args, config) {
289
423
  sourceTool: config.tool,
290
424
  rawHookEvent: name,
291
425
  hookEventName: payload.hook_event_name || payload.hookEventName || payload.event || payload.type || name,
292
- toolName: toolNameFromPayload(payload),
426
+ toolName,
427
+ skillName,
428
+ pluginName,
429
+ mcpServer,
430
+ mcpNamespace: mcpNamespaceFromToolName(toolName),
293
431
  codexMode: nextModeFromPayload(payload),
294
432
  interactionType: eventType,
295
433
  cwd: payload.cwd || payload.directory || payload.project?.directory,
@@ -305,7 +443,7 @@ export function normalizeHookPayload(payload, args, config) {
305
443
  previousMode: previousModeFromPayload(payload),
306
444
  nextMode: nextModeFromPayload(payload),
307
445
  phase: payload.phase || payload.input?.phase || payload.output?.phase,
308
- toolName: toolNameFromPayload(payload),
446
+ toolName,
309
447
  toolStatus: statusFromPayload(payload),
310
448
  exitCode: payload.exitCode ?? payload.exit_code ?? payload.output?.exitCode ?? payload.output?.exit_code,
311
449
  permissionDecision: permissionDecisionFromPayload(payload),