doer-agent 0.7.0 → 0.7.2
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/dist/agent-codex-app-rpc.js +31 -7
- package/dist/mobile-mcp-server.js +81 -0
- package/package.json +1 -1
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
import { StringCodec } from "nats";
|
|
2
2
|
const codexAppRpcCodec = StringCodec();
|
|
3
|
+
const codexAppRpcOmitRules = [
|
|
4
|
+
{
|
|
5
|
+
method: "thread/turns/list",
|
|
6
|
+
itemType: "imageGeneration",
|
|
7
|
+
keys: ["result"],
|
|
8
|
+
},
|
|
9
|
+
];
|
|
3
10
|
function recordValue(value) {
|
|
4
11
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
5
12
|
}
|
|
6
|
-
function
|
|
7
|
-
|
|
13
|
+
function omitKeysFromRecord(record, keys) {
|
|
14
|
+
const next = { ...record };
|
|
15
|
+
for (const key of keys) {
|
|
16
|
+
delete next[key];
|
|
17
|
+
}
|
|
18
|
+
return next;
|
|
19
|
+
}
|
|
20
|
+
function omitRulesForThreadTurnItem(method, item) {
|
|
21
|
+
return codexAppRpcOmitRules.filter((rule) => {
|
|
22
|
+
if (rule.method !== method) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return !rule.itemType || item.type === rule.itemType;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function applyCodexAppRpcOmitRules(method, value) {
|
|
29
|
+
if (!codexAppRpcOmitRules.some((rule) => rule.method === method)) {
|
|
8
30
|
return value;
|
|
9
31
|
}
|
|
10
32
|
const response = recordValue(value);
|
|
@@ -22,12 +44,14 @@ function stripLargeThreadTurnItemFields(method, value) {
|
|
|
22
44
|
...turn,
|
|
23
45
|
items: turn.items.map((itemValue) => {
|
|
24
46
|
const item = recordValue(itemValue);
|
|
25
|
-
if (!item
|
|
47
|
+
if (!item) {
|
|
48
|
+
return itemValue;
|
|
49
|
+
}
|
|
50
|
+
const rules = omitRulesForThreadTurnItem(method, item);
|
|
51
|
+
if (rules.length === 0) {
|
|
26
52
|
return itemValue;
|
|
27
53
|
}
|
|
28
|
-
|
|
29
|
-
delete withoutResult.result;
|
|
30
|
-
return withoutResult;
|
|
54
|
+
return rules.reduce((current, rule) => omitKeysFromRecord(current, rule.keys), item);
|
|
31
55
|
}),
|
|
32
56
|
};
|
|
33
57
|
}),
|
|
@@ -54,7 +78,7 @@ async function handleCodexAppRpcMessage(args) {
|
|
|
54
78
|
const payload = JSON.parse(codexAppRpcCodec.decode(args.msg.data));
|
|
55
79
|
const request = normalizeCodexAppRpcRequest({ request: payload, agentId: args.agentId });
|
|
56
80
|
requestId = request.requestId;
|
|
57
|
-
const result =
|
|
81
|
+
const result = applyCodexAppRpcOmitRules(request.method, await args.manager.request(request.method, request.params));
|
|
58
82
|
args.msg.respond(codexAppRpcCodec.encode(JSON.stringify({
|
|
59
83
|
requestId,
|
|
60
84
|
ok: true,
|
|
@@ -100,6 +100,29 @@ async function showMobileNotification(args) {
|
|
|
100
100
|
url: args.url,
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
|
+
async function showMobileConfirmation(args) {
|
|
104
|
+
const config = getConfig();
|
|
105
|
+
const resolvedDeviceId = await resolveDeviceId(args.deviceId);
|
|
106
|
+
return await postJson(`/api/users/${encodeURIComponent(config.userId)}/agents/${encodeURIComponent(config.agentId)}/mobile-agents/${encodeURIComponent(resolvedDeviceId)}/show-confirmation`, {
|
|
107
|
+
title: args.title,
|
|
108
|
+
text: args.text,
|
|
109
|
+
requestId: args.requestId,
|
|
110
|
+
yesLabel: args.yesLabel,
|
|
111
|
+
noLabel: args.noLabel,
|
|
112
|
+
notificationId: args.notificationId,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async function runMobileCommand(args) {
|
|
116
|
+
const config = getConfig();
|
|
117
|
+
const resolvedDeviceId = await resolveDeviceId(args.deviceId);
|
|
118
|
+
return await postJson(`/api/users/${encodeURIComponent(config.userId)}/agents/${encodeURIComponent(config.agentId)}/mobile-agents/${encodeURIComponent(resolvedDeviceId)}/run-command`, {
|
|
119
|
+
command: args.command,
|
|
120
|
+
cwd: args.cwd,
|
|
121
|
+
env: args.env,
|
|
122
|
+
timeoutMs: args.timeoutMs,
|
|
123
|
+
maxOutputBytes: args.maxOutputBytes,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
103
126
|
async function getMobileLogs(args) {
|
|
104
127
|
const config = getConfig();
|
|
105
128
|
const resolvedDeviceId = await resolveDeviceId(args.deviceId);
|
|
@@ -167,6 +190,9 @@ function extractLocationEvents(events) {
|
|
|
167
190
|
function extractNotifications(events) {
|
|
168
191
|
return events.filter((event) => event.kind.startsWith("notification."));
|
|
169
192
|
}
|
|
193
|
+
function extractConfirmations(events) {
|
|
194
|
+
return events.filter((event) => event.kind.startsWith("confirmation."));
|
|
195
|
+
}
|
|
170
196
|
async function main() {
|
|
171
197
|
const server = new McpServer({
|
|
172
198
|
name: "doer-mobile",
|
|
@@ -294,6 +320,61 @@ async function main() {
|
|
|
294
320
|
structuredContent: result,
|
|
295
321
|
};
|
|
296
322
|
});
|
|
323
|
+
server.registerTool("mobile_show_confirmation", {
|
|
324
|
+
description: "Show a high-priority mobile confirmation notification with only Yes/No actions. Responses are stored in the mobile event log as confirmation.response.",
|
|
325
|
+
inputSchema: {
|
|
326
|
+
deviceId: z.string().optional().describe("Mobile device id. Defaults to the first registered mobile agent."),
|
|
327
|
+
requestId: z.string().optional().describe("Caller-provided id for matching the eventual confirmation.response log. Generated by the server when omitted."),
|
|
328
|
+
title: z.string().min(1).describe("Confirmation title."),
|
|
329
|
+
text: z.string().min(1).describe("Confirmation body text."),
|
|
330
|
+
yesLabel: z.string().optional().describe("Label for the affirmative action. Defaults to Yes."),
|
|
331
|
+
noLabel: z.string().optional().describe("Label for the negative action. Defaults to No."),
|
|
332
|
+
notificationId: z.number().int().optional().describe("Optional Android notification id. Reusing an id updates the existing confirmation notification."),
|
|
333
|
+
},
|
|
334
|
+
}, async ({ deviceId, requestId, title, text, yesLabel, noLabel, notificationId }) => {
|
|
335
|
+
const result = await showMobileConfirmation({ deviceId, requestId, title, text, yesLabel, noLabel, notificationId });
|
|
336
|
+
return {
|
|
337
|
+
content: [{ type: "text", text: formatJson(result) }],
|
|
338
|
+
structuredContent: result,
|
|
339
|
+
};
|
|
340
|
+
});
|
|
341
|
+
server.registerTool("mobile_run_shell_command", {
|
|
342
|
+
description: "Run a shell command on an Android mobile agent using the app sandbox permissions. This is not root; command output is size-limited and timed out.",
|
|
343
|
+
inputSchema: {
|
|
344
|
+
deviceId: z.string().optional().describe("Mobile device id. Defaults to the first registered mobile agent."),
|
|
345
|
+
command: z.string().min(1).describe("Shell command passed to /system/bin/sh -c on the device."),
|
|
346
|
+
cwd: z.string().nullable().optional().describe("Optional working directory on the Android device. Defaults to the app process default directory."),
|
|
347
|
+
env: z.record(z.string(), z.string()).optional().describe("Optional environment variables for the command."),
|
|
348
|
+
timeoutMs: z.number().int().min(1000).max(60000).optional().describe("Command timeout in milliseconds. Defaults to 10000."),
|
|
349
|
+
maxOutputBytes: z.number().int().min(1024).max(65536).optional().describe("Maximum bytes to capture per stdout/stderr stream. Defaults to 16384."),
|
|
350
|
+
},
|
|
351
|
+
}, async ({ deviceId, command, cwd, env, timeoutMs, maxOutputBytes }) => {
|
|
352
|
+
const result = await runMobileCommand({ deviceId, command, cwd, env, timeoutMs, maxOutputBytes });
|
|
353
|
+
return {
|
|
354
|
+
content: [{ type: "text", text: formatJson(result) }],
|
|
355
|
+
structuredContent: result,
|
|
356
|
+
};
|
|
357
|
+
});
|
|
358
|
+
server.registerTool("mobile_search_confirmation_logs", {
|
|
359
|
+
description: "Return recent confirmation request/response/dismissed events stored in mobile logs.",
|
|
360
|
+
inputSchema: {
|
|
361
|
+
deviceId: z.string().optional().describe("Mobile device id. Defaults to the first registered mobile agent."),
|
|
362
|
+
limit: z.number().int().min(1).max(1000).optional().describe("How many recent events to scan."),
|
|
363
|
+
requestId: z.string().optional().describe("Optional confirmation request id to search for."),
|
|
364
|
+
},
|
|
365
|
+
}, async ({ deviceId, limit, requestId }) => {
|
|
366
|
+
const page = await searchMobileLogs({
|
|
367
|
+
deviceId,
|
|
368
|
+
kind: "confirmation.",
|
|
369
|
+
limit: limit ?? 200,
|
|
370
|
+
query: requestId?.trim() || undefined,
|
|
371
|
+
});
|
|
372
|
+
const events = extractConfirmations(page.events);
|
|
373
|
+
return {
|
|
374
|
+
content: [{ type: "text", text: formatJson({ events }) }],
|
|
375
|
+
structuredContent: { events },
|
|
376
|
+
};
|
|
377
|
+
});
|
|
297
378
|
server.registerTool("mobile_search_notification_logs", {
|
|
298
379
|
description: "Return recent notification events stored in mobile logs.",
|
|
299
380
|
inputSchema: {
|