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 +3 -3
- package/package.json +1 -1
- package/src/config.mjs +105 -12
- package/src/hook-adapter.mjs +150 -12
- package/src/install.mjs +338 -18
- package/src/protocol/client-update-policy.mjs +2 -2
- package/src/protocol/event-types.mjs +35 -3
- package/src/publish-npm.mjs +64 -8
- package/src/queue.mjs +158 -31
- package/src/uploader.mjs +102 -0
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.
|
|
40
|
-
--server-url http://
|
|
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://
|
|
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
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:
|
|
46
|
-
name: options.employeeName || envValue(env, "WORKLENS_EMPLOYEE_NAME") || fileConfig.employee?.name || os.userInfo().username,
|
|
47
|
-
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
|
|
59
|
-
model:
|
|
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 || "",
|
package/src/hook-adapter.mjs
CHANGED
|
@@ -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
|
|
100
|
-
payload.
|
|
101
|
-
payload.
|
|
102
|
-
payload.
|
|
103
|
-
payload.
|
|
104
|
-
payload.
|
|
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
|
|
281
|
-
pluginName
|
|
282
|
-
mcpServer
|
|
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
|
|
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
|
|
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),
|