@easonwumac/computer-linker 0.1.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/CHANGELOG.md +230 -0
- package/LICENSE +21 -0
- package/README.md +539 -0
- package/SECURITY.md +48 -0
- package/dist/api.d.ts +2 -0
- package/dist/api.js +360 -0
- package/dist/audit.d.ts +70 -0
- package/dist/audit.js +102 -0
- package/dist/capabilities.d.ts +98 -0
- package/dist/capabilities.js +718 -0
- package/dist/capability-policy.d.ts +22 -0
- package/dist/capability-policy.js +103 -0
- package/dist/chatgpt.d.ts +167 -0
- package/dist/chatgpt.js +561 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +4621 -0
- package/dist/client-smoke.d.ts +44 -0
- package/dist/client-smoke.js +639 -0
- package/dist/client.d.ts +217 -0
- package/dist/client.js +357 -0
- package/dist/codex-runs.d.ts +35 -0
- package/dist/codex-runs.js +66 -0
- package/dist/computer-contract.d.ts +33 -0
- package/dist/computer-contract.js +384 -0
- package/dist/computer-operation-registry.d.ts +45 -0
- package/dist/computer-operation-registry.js +179 -0
- package/dist/config-diagnostics.d.ts +11 -0
- package/dist/config-diagnostics.js +185 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +69 -0
- package/dist/history-insights.d.ts +132 -0
- package/dist/history-insights.js +457 -0
- package/dist/http-auth.d.ts +3 -0
- package/dist/http-auth.js +15 -0
- package/dist/mcp-surface.d.ts +5 -0
- package/dist/mcp-surface.js +25 -0
- package/dist/oauth-provider.d.ts +52 -0
- package/dist/oauth-provider.js +325 -0
- package/dist/package-metadata.d.ts +7 -0
- package/dist/package-metadata.js +24 -0
- package/dist/permissions.d.ts +43 -0
- package/dist/permissions.js +150 -0
- package/dist/platform-shell.d.ts +28 -0
- package/dist/platform-shell.js +124 -0
- package/dist/processes.d.ts +50 -0
- package/dist/processes.js +178 -0
- package/dist/profile.d.ts +159 -0
- package/dist/profile.js +416 -0
- package/dist/screenshot.d.ts +47 -0
- package/dist/screenshot.js +302 -0
- package/dist/search.d.ts +34 -0
- package/dist/search.js +340 -0
- package/dist/security.d.ts +10 -0
- package/dist/security.js +108 -0
- package/dist/sensitive-files.d.ts +4 -0
- package/dist/sensitive-files.js +96 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +713 -0
- package/dist/service.d.ts +125 -0
- package/dist/service.js +486 -0
- package/dist/sessions.d.ts +26 -0
- package/dist/sessions.js +34 -0
- package/dist/tunnels.d.ts +161 -0
- package/dist/tunnels.js +1243 -0
- package/dist/workspace-operations.d.ts +170 -0
- package/dist/workspace-operations.js +3219 -0
- package/dist/workspaces.d.ts +61 -0
- package/dist/workspaces.js +353 -0
- package/docs/agent-instructions.md +65 -0
- package/docs/alpha-evidence.example.json +54 -0
- package/docs/api-compatibility.md +56 -0
- package/docs/architecture.md +561 -0
- package/docs/chatgpt-setup.md +397 -0
- package/docs/client-recipes.md +98 -0
- package/docs/client-sdk.md +163 -0
- package/docs/computer-operation-v1.schema.json +143 -0
- package/docs/manual-test-plan.md +322 -0
- package/docs/product-spec.md +911 -0
- package/docs/release-checklist.md +285 -0
- package/docs/service-mode.md +99 -0
- package/examples/minimal-mcp-client.mjs +114 -0
- package/package.json +87 -0
package/dist/profile.js
ADDED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { configPath } from "./config.js";
|
|
2
|
+
import { genericMcpTools } from "./mcp-surface.js";
|
|
3
|
+
export function connectionProfile(config, includeSecrets = false) {
|
|
4
|
+
const host = config.host ?? "127.0.0.1";
|
|
5
|
+
const port = config.port ?? 3939;
|
|
6
|
+
const publicBaseUrl = config.publicBaseUrl ?? localPublicBaseUrl(host, port);
|
|
7
|
+
const mcpUrl = new URL("/mcp", publicBaseUrl);
|
|
8
|
+
const publicApiAvailable = !config.publicMcpOnly;
|
|
9
|
+
const apiUrl = publicApiAvailable ? new URL("/api/v1", publicBaseUrl) : undefined;
|
|
10
|
+
const localMcpUrl = `http://${host}:${port}/mcp`;
|
|
11
|
+
const localApiUrl = `http://${host}:${port}/api/v1`;
|
|
12
|
+
return {
|
|
13
|
+
name: "computer-linker",
|
|
14
|
+
machineId: config.machineId,
|
|
15
|
+
machineName: config.machineName,
|
|
16
|
+
configPath: configPath(),
|
|
17
|
+
stdio: {
|
|
18
|
+
command: "computer-linker",
|
|
19
|
+
args: ["serve"],
|
|
20
|
+
},
|
|
21
|
+
http: {
|
|
22
|
+
localMcpUrl,
|
|
23
|
+
publicMcpUrl: mcpUrl.href,
|
|
24
|
+
localApiUrl,
|
|
25
|
+
publicApiUrl: apiUrl?.href ?? null,
|
|
26
|
+
publicApiAvailable,
|
|
27
|
+
auth: config.ownerToken
|
|
28
|
+
? {
|
|
29
|
+
mode: "owner-token-or-oauth",
|
|
30
|
+
header: includeSecrets ? `Authorization: Bearer ${config.ownerToken}` : "Authorization: Bearer <ownerToken>",
|
|
31
|
+
bearerToken: includeSecrets ? config.ownerToken : undefined,
|
|
32
|
+
}
|
|
33
|
+
: {
|
|
34
|
+
mode: "loopback-only",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function parseChatGptProfileMode(value, command = "chatgpt --mode") {
|
|
40
|
+
if (!value)
|
|
41
|
+
return "coding";
|
|
42
|
+
if (value === "safe" || value === "coding" || value === "full")
|
|
43
|
+
return value;
|
|
44
|
+
throw new Error(`${command} must be one of: safe, coding, full`);
|
|
45
|
+
}
|
|
46
|
+
export function chatGptConnectProfile(config, includeSecrets = false, mode = "coding", options = {}) {
|
|
47
|
+
const effectiveConfig = options.publicBaseUrl ? { ...config, publicBaseUrl: options.publicBaseUrl } : config;
|
|
48
|
+
const profile = connectionProfile(effectiveConfig, includeSecrets);
|
|
49
|
+
const publicBaseUrl = effectiveConfig.publicBaseUrl ?? null;
|
|
50
|
+
const modeSpec = chatGptModeSpec(mode);
|
|
51
|
+
const warnings = chatGptWarnings(config, profile.http.publicMcpUrl, mode, options);
|
|
52
|
+
const bearerHeader = config.ownerToken
|
|
53
|
+
? includeSecrets
|
|
54
|
+
? `Authorization: Bearer ${config.ownerToken}`
|
|
55
|
+
: "Authorization: Bearer <ownerToken>"
|
|
56
|
+
: null;
|
|
57
|
+
return {
|
|
58
|
+
kind: "chatgpt-mcp-app",
|
|
59
|
+
schemaVersion: 1,
|
|
60
|
+
mode,
|
|
61
|
+
name: `Computer Linker (${config.machineName})`,
|
|
62
|
+
description: "Permissioned MCP access to predefined local coding workspaces on this computer.",
|
|
63
|
+
machineId: config.machineId,
|
|
64
|
+
machineName: config.machineName,
|
|
65
|
+
configPath: profile.configPath,
|
|
66
|
+
mcpServerUrl: profile.http.publicMcpUrl,
|
|
67
|
+
publicBaseUrl,
|
|
68
|
+
localMcpUrl: profile.http.localMcpUrl,
|
|
69
|
+
auth: {
|
|
70
|
+
preferred: "oauth",
|
|
71
|
+
fallback: "bearer",
|
|
72
|
+
oauth: {
|
|
73
|
+
discovery: "Use MCP OAuth discovery from the MCP server URL when the client supports it.",
|
|
74
|
+
scopes: ["computer-linker"],
|
|
75
|
+
},
|
|
76
|
+
bearer: {
|
|
77
|
+
header: bearerHeader,
|
|
78
|
+
token: includeSecrets ? config.ownerToken : undefined,
|
|
79
|
+
alternateHeader: config.ownerToken
|
|
80
|
+
? includeSecrets
|
|
81
|
+
? `x-computer-linker-token: ${config.ownerToken}`
|
|
82
|
+
: "x-computer-linker-token: <ownerToken>"
|
|
83
|
+
: null,
|
|
84
|
+
},
|
|
85
|
+
notes: [
|
|
86
|
+
"Prefer OAuth for ChatGPT custom MCP apps.",
|
|
87
|
+
"Use bearer auth only in clients that support custom headers.",
|
|
88
|
+
"Do not paste the owner token into untrusted clients or shared chats.",
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
appManifest: {
|
|
92
|
+
appName: `Computer Linker (${config.machineName})`,
|
|
93
|
+
appType: "remote-mcp",
|
|
94
|
+
serverUrl: profile.http.publicMcpUrl,
|
|
95
|
+
authType: "oauth-or-bearer",
|
|
96
|
+
},
|
|
97
|
+
setup: {
|
|
98
|
+
developerMode: true,
|
|
99
|
+
requiredReachability: "public-https",
|
|
100
|
+
connectionType: "Remote MCP",
|
|
101
|
+
mode,
|
|
102
|
+
firstPrompt: modeSpec.firstPrompt,
|
|
103
|
+
verifyWith: modeSpec.verifyWith,
|
|
104
|
+
},
|
|
105
|
+
tools: [...genericMcpTools],
|
|
106
|
+
operationShape: {
|
|
107
|
+
recommendedTool: "computer_operation",
|
|
108
|
+
envelope: modeSpec.envelope,
|
|
109
|
+
notes: [
|
|
110
|
+
"Always use the stable envelope: scope, op, target, input, options.",
|
|
111
|
+
"For public ChatGPT connections, use MCP tools; the JSON API is for local or trusted private automation.",
|
|
112
|
+
"Use get_computer_info, operationRegistry, allowedOperations, and scope capabilityPolicy before write, shell, process, git write, package, or codex operations.",
|
|
113
|
+
"target usually maps to path; for command, process_start, codex, and codex_start it maps to workingDirectory.",
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
recommendedFlow: [
|
|
117
|
+
{
|
|
118
|
+
step: 1,
|
|
119
|
+
tool: "get_computer_info",
|
|
120
|
+
purpose: "Learn machine identity, scopes, operationRegistry, permissions, tunnel/auth state, and safety boundaries.",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
step: 2,
|
|
124
|
+
tool: "computer_operation",
|
|
125
|
+
purpose: modeSpec.operationPurpose,
|
|
126
|
+
input: modeSpec.flowInput,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
step: 3,
|
|
130
|
+
tool: "get_operation_history",
|
|
131
|
+
purpose: "Inspect the last action or connection/debug history when needed.",
|
|
132
|
+
input: { scope: "app", view: "last", limit: 20 },
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
modelGuide: chatGptModelGuide(mode),
|
|
136
|
+
workflowRecipes: chatGptWorkflowRecipes(mode),
|
|
137
|
+
gptInstructions: [
|
|
138
|
+
"Do not invent file paths outside listed scopes.",
|
|
139
|
+
"Choose a scope returned by get_computer_info before calling computer_operation.",
|
|
140
|
+
"Do not call shell, process, package, git write, or codex operations unless allowedOperations includes the operation.",
|
|
141
|
+
"Prefer code.context, file.search, file.read, git.status, git.diff, and history.last before editing.",
|
|
142
|
+
"Use get_operation_history for last-operation summaries, session/connection summaries, workspace timelines, failed replay templates, or debug bundles.",
|
|
143
|
+
"Do not use /api/v1/control through public tunnel URLs unless the operator explicitly exposed the JSON API through a trusted private route.",
|
|
144
|
+
"If an operation is blocked, inspect get_computer_info.scopes[].allowedOperations and get_computer_info.operationRegistry before retrying.",
|
|
145
|
+
...modeSpec.instructions,
|
|
146
|
+
],
|
|
147
|
+
warnings,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
export function chatGptAppManifest(config, mode = "coding", options = {}) {
|
|
151
|
+
const profile = chatGptConnectProfile(config, false, mode, options);
|
|
152
|
+
return {
|
|
153
|
+
kind: "chatgpt-app-manifest",
|
|
154
|
+
schemaVersion: 1,
|
|
155
|
+
mode,
|
|
156
|
+
appName: profile.appManifest.appName,
|
|
157
|
+
description: profile.description,
|
|
158
|
+
appType: "remote-mcp",
|
|
159
|
+
mcpServerUrl: profile.mcpServerUrl,
|
|
160
|
+
auth: {
|
|
161
|
+
preferred: "oauth",
|
|
162
|
+
fallback: "bearer",
|
|
163
|
+
scopes: profile.auth.oauth.scopes,
|
|
164
|
+
},
|
|
165
|
+
tools: profile.tools,
|
|
166
|
+
firstPrompt: profile.setup.firstPrompt,
|
|
167
|
+
warnings: profile.warnings,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
export function chatGptConnectorConfig(config, includeSecrets = false, mode = "coding", options = {}) {
|
|
171
|
+
const profile = chatGptConnectProfile(config, includeSecrets, mode, options);
|
|
172
|
+
return {
|
|
173
|
+
kind: "chatgpt-connector-config",
|
|
174
|
+
schemaVersion: 1,
|
|
175
|
+
mode,
|
|
176
|
+
displayName: profile.name,
|
|
177
|
+
mcpServerUrl: profile.mcpServerUrl,
|
|
178
|
+
connectionType: "Remote MCP",
|
|
179
|
+
auth: {
|
|
180
|
+
type: "oauth-or-bearer",
|
|
181
|
+
oauthScopes: profile.auth.oauth.scopes,
|
|
182
|
+
bearerHeader: profile.auth.bearer.header,
|
|
183
|
+
alternateBearerHeader: profile.auth.bearer.alternateHeader,
|
|
184
|
+
},
|
|
185
|
+
setup: profile.setup,
|
|
186
|
+
recommendedFlow: profile.recommendedFlow,
|
|
187
|
+
modelGuide: profile.modelGuide,
|
|
188
|
+
workflowRecipes: profile.workflowRecipes,
|
|
189
|
+
gptInstructions: profile.gptInstructions,
|
|
190
|
+
warnings: profile.warnings,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
export function localPublicBaseUrl(host, port) {
|
|
194
|
+
const publicHost = host === "0.0.0.0" || host === "::" ? "127.0.0.1" : host;
|
|
195
|
+
const formattedHost = publicHost.includes(":") && !publicHost.startsWith("[")
|
|
196
|
+
? `[${publicHost}]`
|
|
197
|
+
: publicHost;
|
|
198
|
+
return `http://${formattedHost}:${port}`;
|
|
199
|
+
}
|
|
200
|
+
function chatGptWarnings(config, mcpServerUrl, mode, options = {}) {
|
|
201
|
+
const warnings = [];
|
|
202
|
+
if (!config.publicBaseUrl && !options.publicBaseUrl) {
|
|
203
|
+
warnings.push("publicBaseUrl is not configured; ChatGPT cloud clients cannot reach the local fallback URL.");
|
|
204
|
+
}
|
|
205
|
+
if (options.publicBaseUrl && options.publicBaseUrl !== config.publicBaseUrl) {
|
|
206
|
+
warnings.push("publicBaseUrl is overridden for this profile only; save it with `computer-linker config set-public-url` before relying on OAuth discovery.");
|
|
207
|
+
}
|
|
208
|
+
let parsed;
|
|
209
|
+
try {
|
|
210
|
+
parsed = new URL(mcpServerUrl);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
warnings.push("mcpServerUrl is not a valid URL.");
|
|
214
|
+
}
|
|
215
|
+
if (parsed && parsed.protocol !== "https:") {
|
|
216
|
+
warnings.push("mcpServerUrl must use https:// for ChatGPT cloud clients.");
|
|
217
|
+
}
|
|
218
|
+
if (!config.ownerToken) {
|
|
219
|
+
warnings.push("ownerToken is not configured; HTTP MCP is loopback-only and cannot be exposed safely.");
|
|
220
|
+
}
|
|
221
|
+
if (config.workspaces.length === 0) {
|
|
222
|
+
warnings.push("No workspaces are configured.");
|
|
223
|
+
}
|
|
224
|
+
if (config.workspaces.some((workspace) => workspace.permissions.shell || workspace.permissions.codex)) {
|
|
225
|
+
warnings.push("At least one workspace allows shell or Codex execution; run doctor and review security findings before public exposure.");
|
|
226
|
+
}
|
|
227
|
+
if (mode === "safe" && config.workspaces.some((workspace) => workspace.permissions.write || workspace.permissions.shell || workspace.permissions.codex)) {
|
|
228
|
+
warnings.push("Safe mode was selected, but at least one workspace exposes write, shell, or Codex permissions; reduce workspace permissions for strict read-only use.");
|
|
229
|
+
}
|
|
230
|
+
if (mode === "full" && config.workspaces.some((workspace) => workspace.permissions.write || workspace.permissions.shell || workspace.permissions.codex)) {
|
|
231
|
+
warnings.push("Full mode can expose write and local execution operations to ChatGPT; use it only for trusted private setups.");
|
|
232
|
+
}
|
|
233
|
+
return warnings;
|
|
234
|
+
}
|
|
235
|
+
function chatGptModelGuide(mode) {
|
|
236
|
+
const operationSelection = [
|
|
237
|
+
{ intent: "understand repository", op: "code.context", when: "before planning or editing" },
|
|
238
|
+
{ intent: "find text quickly", op: "file.search", when: "for broad code or content search; backed by fast local search when available" },
|
|
239
|
+
{ intent: "find code symbols", op: "code.search_symbols", when: "for functions, classes, exports, or definitions" },
|
|
240
|
+
{ intent: "read files", op: "file.read", when: "when bounded file content is needed" },
|
|
241
|
+
{ intent: "inspect git state", op: "git.status", when: "before and after changes" },
|
|
242
|
+
{ intent: "inspect diff", op: "git.diff", when: "before review, summary, or commit" },
|
|
243
|
+
{ intent: "review history", op: "history.last", when: "for what just happened; use get_operation_history for timeline, sessions, connections, failed_replay, or debug_bundle" },
|
|
244
|
+
];
|
|
245
|
+
const guardrails = [
|
|
246
|
+
"Only operate inside scopes returned by get_computer_info.",
|
|
247
|
+
"Check allowedOperations and capabilityPolicy before write, shell, package, process, git write, or Codex operations.",
|
|
248
|
+
"Prefer code.context, file.search, file.read, git.status, git.diff, and history.last before editing.",
|
|
249
|
+
"Use get_operation_history view=last when the user asks what just happened, connections for tunnel/session correlation, and failed_replay or debug_bundle when a command failed.",
|
|
250
|
+
];
|
|
251
|
+
if (mode === "safe") {
|
|
252
|
+
guardrails.push("Stay read-only even if elevated operations appear in the registry.");
|
|
253
|
+
}
|
|
254
|
+
else if (mode === "full") {
|
|
255
|
+
operationSelection.push({ intent: "create a new file safely", op: "file.create", when: "for new files only; it fails instead of overwriting existing paths" }, { intent: "apply patches", op: "file.patch", when: "for targeted multi-line edits" }, { intent: "run package scripts", op: "package.run", when: "for package.json scripts when package execution is allowed" }, { intent: "run commands", op: "command.run", when: "for local verification when shell is allowed" }, { intent: "start managed processes", op: "process.start", when: "for dev servers or watchers that should be inspected later" }, { intent: "delegate coding to Codex", op: "codex.run", when: "for a bounded local Codex task when codex is allowed" });
|
|
256
|
+
guardrails.push("Ask before destructive deletes, broad moves, dependency installs, publishing, deployment, or external service changes.");
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
operationSelection.push({ intent: "create a new file safely", op: "file.create", when: "for new files only; it fails instead of overwriting existing paths" }, { intent: "apply patches", op: "file.patch", when: "for targeted multi-line edits" }, { intent: "delegate coding to Codex", op: "codex.run", when: "for a bounded local Codex task when codex is allowed" });
|
|
260
|
+
guardrails.push("Treat shell, package, process, and Codex operations as higher risk; prefer direct file/search/git inspection first.");
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
summary: "Computer Linker exposes predefined scopes. Use computer_operation for file, search, git, shell, Codex, screen, and history work through the stable envelope: scope, op, target, input, options.",
|
|
264
|
+
mcpEntrypoint: "computer_operation",
|
|
265
|
+
jsonApiEntrypoint: {
|
|
266
|
+
endpoint: "POST /api/v1/control",
|
|
267
|
+
action: "computer_operation",
|
|
268
|
+
availability: "local-or-trusted-private-only",
|
|
269
|
+
publicTunnelDefault: "blocked-when-publicMcpOnly",
|
|
270
|
+
},
|
|
271
|
+
startupChecklist: [
|
|
272
|
+
"Call get_computer_info.",
|
|
273
|
+
"Choose a configured scope id.",
|
|
274
|
+
"Call computer_operation with op=code.context, then get_operation_history with view=last.",
|
|
275
|
+
],
|
|
276
|
+
operationSelection,
|
|
277
|
+
guardrails,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function chatGptWorkflowRecipes(mode) {
|
|
281
|
+
const base = [
|
|
282
|
+
{
|
|
283
|
+
name: "connect_and_orient",
|
|
284
|
+
purpose: "Verify the connector and build working context before answering.",
|
|
285
|
+
steps: [
|
|
286
|
+
{ tool: "get_computer_info", why: "Read machine identity, scopes, operation registry, policy, and tunnel/auth state." },
|
|
287
|
+
{ tool: "computer_operation", input: { scope: "app", op: "code.context", target: ".", input: {}, options: { maxDepth: 2, maxEntries: 100 } }, why: "Get a bounded code-oriented map." },
|
|
288
|
+
{ tool: "get_operation_history", input: { scope: "app", view: "last", limit: 20 }, why: "See the last action, recent failure, and suggested next step." },
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: "search_and_read",
|
|
293
|
+
purpose: "Find relevant code without guessing paths.",
|
|
294
|
+
steps: [
|
|
295
|
+
{ tool: "computer_operation", input: { scope: "app", op: "file.search", target: ".", input: { query: "TODO" }, options: { maxResults: 20 } }, why: "Use fast text search first." },
|
|
296
|
+
{ tool: "computer_operation", input: { scope: "app", op: "file.read", target: "README.md", input: {}, options: { maxBytes: 65536 } }, why: "Read bounded source context." },
|
|
297
|
+
],
|
|
298
|
+
},
|
|
299
|
+
];
|
|
300
|
+
if (mode !== "safe") {
|
|
301
|
+
base.push({
|
|
302
|
+
name: "implement_and_verify",
|
|
303
|
+
purpose: "Make a scoped coding change and verify it.",
|
|
304
|
+
steps: [
|
|
305
|
+
{ tool: "computer_operation", input: { scope: "app", op: "code.context", target: ".", input: {}, options: { maxDepth: 2, maxEntries: 100 } }, why: "Check scoped context before editing." },
|
|
306
|
+
{ tool: "computer_operation", input: { scope: "app", op: "file.patch", target: ".", input: { patch: "<valid patch generated from the planned edit>" }, options: {} }, why: "Apply targeted edits when allowed." },
|
|
307
|
+
{ tool: "computer_operation", input: { scope: "app", op: "package.run", target: ".", input: { script: "test" }, options: { timeoutSeconds: 600 } }, why: "Run the package verification command when shell is allowed." },
|
|
308
|
+
{ tool: "computer_operation", input: { scope: "app", op: "git.diff", target: ".", input: {}, options: { maxBytes: 65536 } }, why: "Review the final diff before summarizing." },
|
|
309
|
+
],
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
if (mode === "coding" || mode === "full") {
|
|
313
|
+
base.push({
|
|
314
|
+
name: "codex_assisted_change",
|
|
315
|
+
purpose: "Use Codex as a local coding agent when the workspace permits it.",
|
|
316
|
+
steps: [
|
|
317
|
+
{ tool: "get_computer_info", why: "Confirm Codex operations are permitted before invoking local Codex." },
|
|
318
|
+
{ tool: "computer_operation", input: { scope: "app", op: "codex.run", target: ".", input: { prompt: "Inspect this workspace and propose the smallest safe implementation plan." }, options: { timeoutSeconds: 1800 } }, why: "Ask Codex for a bounded task." },
|
|
319
|
+
{ tool: "computer_operation", input: { scope: "app", op: "codex.list", target: ".", input: {}, options: { maxResults: 5 } }, why: "Inspect recent Codex run records when needed." },
|
|
320
|
+
],
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
return base;
|
|
324
|
+
}
|
|
325
|
+
function chatGptModeSpec(mode) {
|
|
326
|
+
if (mode === "safe") {
|
|
327
|
+
return {
|
|
328
|
+
firstPrompt: "Call get_computer_info, choose the app scope if present, run computer_operation op=code.context, then call get_operation_history with view=last. Stay read-only.",
|
|
329
|
+
verifyWith: [
|
|
330
|
+
"get_computer_info",
|
|
331
|
+
"computer_operation op=code.context",
|
|
332
|
+
"get_operation_history view=last",
|
|
333
|
+
],
|
|
334
|
+
envelope: {
|
|
335
|
+
scope: "app",
|
|
336
|
+
op: "file.search",
|
|
337
|
+
target: ".",
|
|
338
|
+
input: { query: "TODO", glob: "*.ts" },
|
|
339
|
+
options: { maxResults: 20 },
|
|
340
|
+
},
|
|
341
|
+
operationPurpose: "Use the generic operation envelope for read-only file, search, and history actions.",
|
|
342
|
+
flowInput: {
|
|
343
|
+
scope: "app",
|
|
344
|
+
op: "code.context",
|
|
345
|
+
target: ".",
|
|
346
|
+
input: {},
|
|
347
|
+
options: { maxDepth: 2, maxEntries: 100 },
|
|
348
|
+
},
|
|
349
|
+
instructions: [
|
|
350
|
+
"Stay read-only in safe mode: use code.context, code.search_symbols, file.list, file.read, file.search, git.status, git.diff, history.last, and get_operation_history.",
|
|
351
|
+
"Do not call write, patch, delete, move, command, process, package, git write, or Codex operations in safe mode even when they appear in allowedOperations.",
|
|
352
|
+
],
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
if (mode === "full") {
|
|
356
|
+
return {
|
|
357
|
+
firstPrompt: "Call get_computer_info, choose the app scope if present, run computer_operation op=code.context, then call get_operation_history with view=last. Use allowed write, shell, process, and Codex operations only when needed.",
|
|
358
|
+
verifyWith: [
|
|
359
|
+
"get_computer_info",
|
|
360
|
+
"computer_operation op=code.context",
|
|
361
|
+
"get_operation_history view=last",
|
|
362
|
+
],
|
|
363
|
+
envelope: {
|
|
364
|
+
scope: "app",
|
|
365
|
+
op: "codex.run",
|
|
366
|
+
target: ".",
|
|
367
|
+
input: { prompt: "Inspect this workspace and propose the smallest safe implementation plan." },
|
|
368
|
+
options: {},
|
|
369
|
+
},
|
|
370
|
+
operationPurpose: "Use the generic operation envelope for file, search, command, process, history, screen, and Codex workflows when allowed.",
|
|
371
|
+
flowInput: {
|
|
372
|
+
scope: "app",
|
|
373
|
+
op: "code.context",
|
|
374
|
+
target: ".",
|
|
375
|
+
input: {},
|
|
376
|
+
options: { maxDepth: 2, maxEntries: 100 },
|
|
377
|
+
},
|
|
378
|
+
instructions: [
|
|
379
|
+
"Full mode may use write, command, process, and Codex operations only when allowedOperations includes the mapped operation.",
|
|
380
|
+
"Ask before destructive file deletes, broad moves, git commits, long-running processes, or commands that install, publish, deploy, or modify external services.",
|
|
381
|
+
"Use codex.run or codex.start only for trusted local coding tasks.",
|
|
382
|
+
"After a Codex workflow, use codex.read or codex.list when you need stdout/stderr previews, exit metadata, or the stored change summary later.",
|
|
383
|
+
"Use file.patch for edits when possible.",
|
|
384
|
+
],
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
firstPrompt: "Call get_computer_info, choose the app scope if present, run computer_operation op=code.context, then call get_operation_history with view=last.",
|
|
389
|
+
verifyWith: [
|
|
390
|
+
"get_computer_info",
|
|
391
|
+
"computer_operation op=code.context",
|
|
392
|
+
"get_operation_history view=last",
|
|
393
|
+
],
|
|
394
|
+
envelope: {
|
|
395
|
+
scope: "app",
|
|
396
|
+
op: "file.search",
|
|
397
|
+
target: ".",
|
|
398
|
+
input: { query: "TODO", glob: "*.ts" },
|
|
399
|
+
options: { maxResults: 20 },
|
|
400
|
+
},
|
|
401
|
+
operationPurpose: "Use the generic operation envelope for file, search, command, history, and Codex actions.",
|
|
402
|
+
flowInput: {
|
|
403
|
+
scope: "app",
|
|
404
|
+
op: "code.context",
|
|
405
|
+
target: ".",
|
|
406
|
+
input: {},
|
|
407
|
+
options: { maxDepth: 2, maxEntries: 100 },
|
|
408
|
+
},
|
|
409
|
+
instructions: [
|
|
410
|
+
"Coding mode may edit files, but should treat command, process, and Codex operations as higher-risk actions.",
|
|
411
|
+
"Use codex.run or codex.start only for trusted local coding tasks.",
|
|
412
|
+
"After a Codex workflow, use codex.read or codex.list when you need stdout/stderr previews, exit metadata, or the stored change summary later.",
|
|
413
|
+
"Use file.patch for edits when possible.",
|
|
414
|
+
],
|
|
415
|
+
};
|
|
416
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface ScreenshotPermission {
|
|
2
|
+
status: "granted" | "unknown" | "unsupported" | "os_permission_required";
|
|
3
|
+
detail: string | null;
|
|
4
|
+
}
|
|
5
|
+
export interface ScreenshotListResult {
|
|
6
|
+
permission: ScreenshotPermission;
|
|
7
|
+
provider: string;
|
|
8
|
+
supported: boolean;
|
|
9
|
+
modes: string[];
|
|
10
|
+
displays: Array<{
|
|
11
|
+
id: string;
|
|
12
|
+
primary: boolean;
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
}>;
|
|
16
|
+
windows: Array<{
|
|
17
|
+
id: string;
|
|
18
|
+
title?: string;
|
|
19
|
+
processId?: number;
|
|
20
|
+
processName?: string;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export interface ScreenshotCaptureOptions {
|
|
24
|
+
source: "display" | "window" | "process";
|
|
25
|
+
target?: string;
|
|
26
|
+
format?: string;
|
|
27
|
+
returnMode?: string;
|
|
28
|
+
maxWidth?: number;
|
|
29
|
+
maxHeight?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface ScreenshotCaptureResult {
|
|
32
|
+
format: "png";
|
|
33
|
+
width?: number;
|
|
34
|
+
height?: number;
|
|
35
|
+
bytesBase64?: string;
|
|
36
|
+
fileRef?: string;
|
|
37
|
+
sizeBytes: number;
|
|
38
|
+
source: {
|
|
39
|
+
type: "display" | "window" | "process";
|
|
40
|
+
id: string;
|
|
41
|
+
};
|
|
42
|
+
permission: ScreenshotPermission;
|
|
43
|
+
provider: string;
|
|
44
|
+
}
|
|
45
|
+
export declare function screenshotCapability(): ScreenshotListResult;
|
|
46
|
+
export declare function listScreenshotTargets(): ScreenshotListResult;
|
|
47
|
+
export declare function captureScreenshot(options: ScreenshotCaptureOptions): Promise<ScreenshotCaptureResult>;
|