@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.
Files changed (82) hide show
  1. package/CHANGELOG.md +230 -0
  2. package/LICENSE +21 -0
  3. package/README.md +539 -0
  4. package/SECURITY.md +48 -0
  5. package/dist/api.d.ts +2 -0
  6. package/dist/api.js +360 -0
  7. package/dist/audit.d.ts +70 -0
  8. package/dist/audit.js +102 -0
  9. package/dist/capabilities.d.ts +98 -0
  10. package/dist/capabilities.js +718 -0
  11. package/dist/capability-policy.d.ts +22 -0
  12. package/dist/capability-policy.js +103 -0
  13. package/dist/chatgpt.d.ts +167 -0
  14. package/dist/chatgpt.js +561 -0
  15. package/dist/cli.d.ts +2 -0
  16. package/dist/cli.js +4621 -0
  17. package/dist/client-smoke.d.ts +44 -0
  18. package/dist/client-smoke.js +639 -0
  19. package/dist/client.d.ts +217 -0
  20. package/dist/client.js +357 -0
  21. package/dist/codex-runs.d.ts +35 -0
  22. package/dist/codex-runs.js +66 -0
  23. package/dist/computer-contract.d.ts +33 -0
  24. package/dist/computer-contract.js +384 -0
  25. package/dist/computer-operation-registry.d.ts +45 -0
  26. package/dist/computer-operation-registry.js +179 -0
  27. package/dist/config-diagnostics.d.ts +11 -0
  28. package/dist/config-diagnostics.js +185 -0
  29. package/dist/config.d.ts +10 -0
  30. package/dist/config.js +69 -0
  31. package/dist/history-insights.d.ts +132 -0
  32. package/dist/history-insights.js +457 -0
  33. package/dist/http-auth.d.ts +3 -0
  34. package/dist/http-auth.js +15 -0
  35. package/dist/mcp-surface.d.ts +5 -0
  36. package/dist/mcp-surface.js +25 -0
  37. package/dist/oauth-provider.d.ts +52 -0
  38. package/dist/oauth-provider.js +325 -0
  39. package/dist/package-metadata.d.ts +7 -0
  40. package/dist/package-metadata.js +24 -0
  41. package/dist/permissions.d.ts +43 -0
  42. package/dist/permissions.js +150 -0
  43. package/dist/platform-shell.d.ts +28 -0
  44. package/dist/platform-shell.js +124 -0
  45. package/dist/processes.d.ts +50 -0
  46. package/dist/processes.js +178 -0
  47. package/dist/profile.d.ts +159 -0
  48. package/dist/profile.js +416 -0
  49. package/dist/screenshot.d.ts +47 -0
  50. package/dist/screenshot.js +302 -0
  51. package/dist/search.d.ts +34 -0
  52. package/dist/search.js +340 -0
  53. package/dist/security.d.ts +10 -0
  54. package/dist/security.js +108 -0
  55. package/dist/sensitive-files.d.ts +4 -0
  56. package/dist/sensitive-files.js +96 -0
  57. package/dist/server.d.ts +9 -0
  58. package/dist/server.js +713 -0
  59. package/dist/service.d.ts +125 -0
  60. package/dist/service.js +486 -0
  61. package/dist/sessions.d.ts +26 -0
  62. package/dist/sessions.js +34 -0
  63. package/dist/tunnels.d.ts +161 -0
  64. package/dist/tunnels.js +1243 -0
  65. package/dist/workspace-operations.d.ts +170 -0
  66. package/dist/workspace-operations.js +3219 -0
  67. package/dist/workspaces.d.ts +61 -0
  68. package/dist/workspaces.js +353 -0
  69. package/docs/agent-instructions.md +65 -0
  70. package/docs/alpha-evidence.example.json +54 -0
  71. package/docs/api-compatibility.md +56 -0
  72. package/docs/architecture.md +561 -0
  73. package/docs/chatgpt-setup.md +397 -0
  74. package/docs/client-recipes.md +98 -0
  75. package/docs/client-sdk.md +163 -0
  76. package/docs/computer-operation-v1.schema.json +143 -0
  77. package/docs/manual-test-plan.md +322 -0
  78. package/docs/product-spec.md +911 -0
  79. package/docs/release-checklist.md +285 -0
  80. package/docs/service-mode.md +99 -0
  81. package/examples/minimal-mcp-client.mjs +114 -0
  82. package/package.json +87 -0
@@ -0,0 +1,124 @@
1
+ import { accessSync, constants } from "node:fs";
2
+ import { delimiter, extname, join } from "node:path";
3
+ export function shellCommand(command, options = {}) {
4
+ const platform = options.platform ?? process.platform;
5
+ if (platform === "win32") {
6
+ return {
7
+ command: options.comSpec ?? process.env.ComSpec ?? "cmd.exe",
8
+ args: ["/d", "/s", "/c", command],
9
+ };
10
+ }
11
+ return {
12
+ command: options.shell ?? process.env.SHELL ?? "/bin/sh",
13
+ args: ["-lc", command],
14
+ };
15
+ }
16
+ export function resolveExecutableCommand(command, options = {}) {
17
+ const platform = options.platform ?? process.platform;
18
+ if (platform !== "win32")
19
+ return command;
20
+ const env = options.env ?? process.env;
21
+ const pathEntries = (env.PATH ?? env.Path ?? env.path ?? "")
22
+ .split(delimiter)
23
+ .filter(Boolean);
24
+ const extensions = windowsExecutableExtensions(env);
25
+ const hasDirectory = command.includes("/") || command.includes("\\");
26
+ const hasExtension = Boolean(extname(command));
27
+ const commandCandidates = hasExtension
28
+ ? [command]
29
+ : extensions.map((extension) => `${command}${extension}`);
30
+ const candidates = hasDirectory
31
+ ? commandCandidates
32
+ : pathEntries.flatMap((entry) => commandCandidates.map((candidate) => join(entry, candidate)));
33
+ for (const candidate of candidates) {
34
+ try {
35
+ accessSync(candidate, constants.X_OK);
36
+ return candidate;
37
+ }
38
+ catch {
39
+ // Try the next PATH/PATHEXT candidate.
40
+ }
41
+ }
42
+ return command;
43
+ }
44
+ export function executableCommand(command, args, options = {}) {
45
+ const platform = options.platform ?? process.platform;
46
+ const executable = resolveExecutableCommand(command, {
47
+ platform,
48
+ env: options.env,
49
+ });
50
+ if (!shouldRunExecutableThroughShell(executable, { platform })) {
51
+ return { command: executable, args };
52
+ }
53
+ const line = commandLine([executable, ...args], platform);
54
+ if (platform === "win32") {
55
+ return {
56
+ command: options.comSpec ?? process.env.ComSpec ?? "cmd.exe",
57
+ args: ["/d", "/s", "/c", `"${line}"`],
58
+ windowsVerbatimArguments: true,
59
+ };
60
+ }
61
+ return shellCommand(line, {
62
+ platform,
63
+ shell: options.shell,
64
+ comSpec: options.comSpec,
65
+ });
66
+ }
67
+ export function windowsVerbatimArgumentsOption(command) {
68
+ return command.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {};
69
+ }
70
+ export function findExecutableCommand(command, options = {}) {
71
+ const platform = options.platform ?? process.platform;
72
+ const env = options.env ?? process.env;
73
+ const pathEntries = (env.PATH ?? env.Path ?? env.path ?? "")
74
+ .split(delimiter)
75
+ .filter(Boolean);
76
+ const extensions = platform === "win32" ? windowsExecutableExtensions(env) : [""];
77
+ const hasDirectory = command.includes("/") || command.includes("\\");
78
+ const hasExtension = Boolean(extname(command));
79
+ const commandCandidates = platform === "win32" && !hasExtension
80
+ ? extensions.map((extension) => `${command}${extension}`)
81
+ : [command];
82
+ const candidates = hasDirectory
83
+ ? commandCandidates
84
+ : pathEntries.flatMap((entry) => commandCandidates.map((candidate) => join(entry, candidate)));
85
+ for (const candidate of candidates) {
86
+ try {
87
+ accessSync(candidate, constants.X_OK);
88
+ return candidate;
89
+ }
90
+ catch {
91
+ // Try the next PATH/PATHEXT candidate.
92
+ }
93
+ }
94
+ return undefined;
95
+ }
96
+ export function shouldRunExecutableThroughShell(command, options = {}) {
97
+ const platform = options.platform ?? process.platform;
98
+ return platform === "win32" && [".bat", ".cmd"].includes(extname(command).toLowerCase());
99
+ }
100
+ function commandLine(parts, platform) {
101
+ return parts.map((part) => quoteCommandPart(part, platform)).join(" ");
102
+ }
103
+ function quoteCommandPart(part, platform) {
104
+ if (part === "")
105
+ return "\"\"";
106
+ if (platform === "win32")
107
+ return quoteWindowsCommandPart(part);
108
+ if (!/[\s"'\\$`!&|;<>(){}[\]*?]/.test(part))
109
+ return part;
110
+ return `'${part.replaceAll("'", "'\\''")}'`;
111
+ }
112
+ function quoteWindowsCommandPart(part) {
113
+ if (!/[\s"&|<>^()%!:\\/]/.test(part))
114
+ return part;
115
+ return `"${part.replaceAll("\"", "\"\"")}"`;
116
+ }
117
+ function windowsExecutableExtensions(env) {
118
+ const raw = env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD";
119
+ return raw
120
+ .split(";")
121
+ .map((extension) => extension.trim())
122
+ .filter(Boolean)
123
+ .map((extension) => (extension.startsWith(".") ? extension : `.${extension}`).toLowerCase());
124
+ }
@@ -0,0 +1,50 @@
1
+ export interface ManagedProcessSnapshot {
2
+ processId: string;
3
+ kind: "shell" | "codex";
4
+ workspaceId: string;
5
+ workspaceRoot: string;
6
+ cwd: string;
7
+ commandPreview: string;
8
+ pid?: number;
9
+ startedAt: string;
10
+ endedAt?: string;
11
+ status: "running" | "exited";
12
+ exitCode: number | null;
13
+ signal?: string;
14
+ timedOut: boolean;
15
+ stdout: string;
16
+ stderr: string;
17
+ }
18
+ export declare function startManagedProcess(input: {
19
+ kind: ManagedProcessSnapshot["kind"];
20
+ workspaceId: string;
21
+ workspaceRoot: string;
22
+ cwd: string;
23
+ command: string;
24
+ args?: string[];
25
+ commandPreview: string;
26
+ timeoutMs?: number;
27
+ maxOutputBytes?: number;
28
+ stdin?: string;
29
+ }): ManagedProcessSnapshot;
30
+ export declare function listManagedProcesses(input: {
31
+ workspaceId: string;
32
+ workspaceRoot: string;
33
+ kinds?: ManagedProcessSnapshot["kind"][];
34
+ }): ManagedProcessSnapshot[];
35
+ export declare function listAllManagedProcesses(): ManagedProcessSnapshot[];
36
+ export declare function readManagedProcess(input: {
37
+ processId: string;
38
+ workspaceId: string;
39
+ workspaceRoot: string;
40
+ kinds?: ManagedProcessSnapshot["kind"][];
41
+ }): ManagedProcessSnapshot;
42
+ export declare function stopManagedProcess(input: {
43
+ processId: string;
44
+ workspaceId: string;
45
+ workspaceRoot: string;
46
+ signal?: string;
47
+ kinds?: ManagedProcessSnapshot["kind"][];
48
+ }): Promise<ManagedProcessSnapshot>;
49
+ export declare function stopManagedProcessById(processId: string, signal?: string): Promise<ManagedProcessSnapshot>;
50
+ export declare function stopAllManagedProcesses(signal?: string): Promise<ManagedProcessSnapshot[]>;
@@ -0,0 +1,178 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { spawn, spawnSync } from "node:child_process";
3
+ import { executableCommand, shellCommand } from "./platform-shell.js";
4
+ const defaultMaxOutputBytes = 128 * 1024;
5
+ const processes = new Map();
6
+ export function startManagedProcess(input) {
7
+ const processId = `proc_${randomUUID()}`;
8
+ const detached = process.platform !== "win32";
9
+ const command = input.args
10
+ ? executableCommand(input.command, input.args)
11
+ : shellCommand(input.command);
12
+ const child = spawn(command.command, command.args, {
13
+ cwd: input.cwd,
14
+ stdio: ["pipe", "pipe", "pipe"],
15
+ detached,
16
+ windowsVerbatimArguments: command.windowsVerbatimArguments,
17
+ });
18
+ const managed = {
19
+ child,
20
+ processId,
21
+ kind: input.kind,
22
+ workspaceId: input.workspaceId,
23
+ workspaceRoot: input.workspaceRoot,
24
+ cwd: input.cwd,
25
+ commandPreview: input.commandPreview,
26
+ pid: child.pid,
27
+ startedAt: new Date().toISOString(),
28
+ status: "running",
29
+ exitCode: null,
30
+ timedOut: false,
31
+ stdout: "",
32
+ stderr: "",
33
+ maxOutputBytes: input.maxOutputBytes ?? defaultMaxOutputBytes,
34
+ };
35
+ processes.set(processId, managed);
36
+ child.stdin.end(input.stdin ?? "");
37
+ child.stdout.on("data", (chunk) => {
38
+ managed.stdout = appendBounded(managed.stdout, chunk.toString("utf8"), managed.maxOutputBytes);
39
+ });
40
+ child.stderr.on("data", (chunk) => {
41
+ managed.stderr = appendBounded(managed.stderr, chunk.toString("utf8"), managed.maxOutputBytes);
42
+ });
43
+ child.on("exit", (code, signal) => {
44
+ if (managed.timer)
45
+ clearTimeout(managed.timer);
46
+ managed.status = "exited";
47
+ managed.exitCode = code;
48
+ managed.signal = signal ?? undefined;
49
+ managed.endedAt = new Date().toISOString();
50
+ });
51
+ if (input.timeoutMs && input.timeoutMs > 0) {
52
+ managed.timer = setTimeout(() => {
53
+ if (managed.status !== "running")
54
+ return;
55
+ managed.timedOut = true;
56
+ void stopProcess(managed, "SIGTERM");
57
+ }, input.timeoutMs);
58
+ managed.timer.unref();
59
+ }
60
+ return snapshot(managed);
61
+ }
62
+ export function listManagedProcesses(input) {
63
+ return [...processes.values()]
64
+ .filter((process) => process.workspaceId === input.workspaceId && process.workspaceRoot === input.workspaceRoot)
65
+ .filter((process) => !input.kinds || input.kinds.includes(process.kind))
66
+ .map(snapshot)
67
+ .sort((a, b) => b.startedAt.localeCompare(a.startedAt));
68
+ }
69
+ export function listAllManagedProcesses() {
70
+ return [...processes.values()]
71
+ .map(snapshot)
72
+ .sort((a, b) => b.startedAt.localeCompare(a.startedAt));
73
+ }
74
+ export function readManagedProcess(input) {
75
+ return snapshot(getProcessForWorkspace(input));
76
+ }
77
+ export async function stopManagedProcess(input) {
78
+ const process = getProcessForWorkspace(input);
79
+ return stopProcess(process, normalizeSignal(input.signal));
80
+ }
81
+ export async function stopManagedProcessById(processId, signal) {
82
+ const process = processes.get(processId);
83
+ if (!process)
84
+ throw new Error(`Unknown process: ${processId}`);
85
+ return stopProcess(process, normalizeSignal(signal));
86
+ }
87
+ export async function stopAllManagedProcesses(signal = "SIGTERM") {
88
+ const normalizedSignal = normalizeSignal(signal);
89
+ return Promise.all([...processes.values()].map((process) => stopProcess(process, normalizedSignal)));
90
+ }
91
+ function getProcessForWorkspace(input) {
92
+ const process = processes.get(input.processId);
93
+ if (!process ||
94
+ process.workspaceId !== input.workspaceId ||
95
+ process.workspaceRoot !== input.workspaceRoot ||
96
+ (input.kinds && !input.kinds.includes(process.kind))) {
97
+ throw new Error(`Unknown process for workspace: ${input.processId}`);
98
+ }
99
+ return process;
100
+ }
101
+ async function stopProcess(process, signal) {
102
+ if (process.status !== "running")
103
+ return snapshot(process);
104
+ terminateProcessGroup(process, signal);
105
+ await waitForExit(process, 500);
106
+ if (signal !== "SIGKILL" && (process.status === "running" || isUnixProcessGroup(process))) {
107
+ terminateProcessGroup(process, "SIGKILL");
108
+ await waitForExit(process, 500);
109
+ }
110
+ return snapshot(process);
111
+ }
112
+ function snapshot(process) {
113
+ return {
114
+ processId: process.processId,
115
+ kind: process.kind,
116
+ workspaceId: process.workspaceId,
117
+ workspaceRoot: process.workspaceRoot,
118
+ cwd: process.cwd,
119
+ commandPreview: process.commandPreview,
120
+ pid: process.pid,
121
+ startedAt: process.startedAt,
122
+ endedAt: process.endedAt,
123
+ status: process.status,
124
+ exitCode: process.exitCode,
125
+ signal: process.signal,
126
+ timedOut: process.timedOut,
127
+ stdout: process.stdout,
128
+ stderr: process.stderr,
129
+ };
130
+ }
131
+ function appendBounded(current, next, maxOutputBytes) {
132
+ let output = current + next;
133
+ while (Buffer.byteLength(output, "utf8") > maxOutputBytes) {
134
+ output = output.slice(Math.max(1, output.length - maxOutputBytes));
135
+ }
136
+ return output;
137
+ }
138
+ function normalizeSignal(signal) {
139
+ if (signal === "SIGKILL" || signal === "SIGINT" || signal === "SIGTERM")
140
+ return signal;
141
+ return "SIGTERM";
142
+ }
143
+ function terminateProcessGroup(process, signal) {
144
+ if (globalThis.process.platform === "win32" && process.pid) {
145
+ const result = spawnSync("taskkill", ["/pid", String(process.pid), "/t", "/f"], { stdio: "ignore" });
146
+ if (result.status === 0)
147
+ return;
148
+ }
149
+ if (isUnixProcessGroup(process)) {
150
+ try {
151
+ globalThis.process.kill(-process.pid, signal);
152
+ return;
153
+ }
154
+ catch {
155
+ // Fall back to killing the shell process below.
156
+ }
157
+ }
158
+ process.child.kill(signal);
159
+ }
160
+ function isUnixProcessGroup(process) {
161
+ return Boolean(process.pid && process.child.spawnargs.length && globalThis.process.platform !== "win32");
162
+ }
163
+ async function waitForExit(process, timeoutMs) {
164
+ if (process.status !== "running")
165
+ return true;
166
+ return new Promise((resolve) => {
167
+ const onExit = () => {
168
+ clearTimeout(timeout);
169
+ resolve(true);
170
+ };
171
+ const timeout = setTimeout(() => {
172
+ process.child.off("exit", onExit);
173
+ resolve(false);
174
+ }, timeoutMs);
175
+ process.child.once("exit", onExit);
176
+ timeout.unref();
177
+ });
178
+ }
@@ -0,0 +1,159 @@
1
+ import type { LocalPortConfig } from "./permissions.js";
2
+ export type ChatGptProfileMode = "safe" | "coding" | "full";
3
+ export interface ChatGptProfileOptions {
4
+ publicBaseUrl?: string;
5
+ }
6
+ export interface ChatGptModelGuide {
7
+ summary: string;
8
+ mcpEntrypoint: "computer_operation";
9
+ jsonApiEntrypoint: {
10
+ endpoint: "POST /api/v1/control";
11
+ action: "computer_operation";
12
+ availability: "local-or-trusted-private-only";
13
+ publicTunnelDefault: "blocked-when-publicMcpOnly";
14
+ };
15
+ startupChecklist: string[];
16
+ operationSelection: Array<{
17
+ intent: string;
18
+ op: string;
19
+ when: string;
20
+ }>;
21
+ guardrails: string[];
22
+ }
23
+ export interface ChatGptWorkflowRecipe {
24
+ name: string;
25
+ purpose: string;
26
+ steps: Array<{
27
+ tool: "get_computer_info" | "computer_operation" | "get_operation_history";
28
+ input?: Record<string, unknown>;
29
+ why: string;
30
+ }>;
31
+ }
32
+ export interface ConnectionProfile {
33
+ name: "computer-linker";
34
+ machineId?: string;
35
+ machineName: string;
36
+ configPath: string;
37
+ stdio: {
38
+ command: string;
39
+ args: string[];
40
+ };
41
+ http: {
42
+ localMcpUrl: string;
43
+ publicMcpUrl: string;
44
+ localApiUrl: string;
45
+ publicApiUrl: string | null;
46
+ publicApiAvailable: boolean;
47
+ auth: {
48
+ mode: "loopback-only" | "owner-token-or-oauth";
49
+ header?: string;
50
+ bearerToken?: string;
51
+ };
52
+ };
53
+ }
54
+ export interface ChatGptConnectProfile {
55
+ kind: "chatgpt-mcp-app";
56
+ schemaVersion: 1;
57
+ mode: ChatGptProfileMode;
58
+ name: string;
59
+ description: string;
60
+ machineId?: string;
61
+ machineName: string;
62
+ configPath: string;
63
+ mcpServerUrl: string;
64
+ publicBaseUrl: string | null;
65
+ localMcpUrl: string;
66
+ auth: {
67
+ preferred: "oauth";
68
+ fallback: "bearer";
69
+ oauth: {
70
+ discovery: string;
71
+ scopes: string[];
72
+ };
73
+ bearer: {
74
+ header: string | null;
75
+ token?: string;
76
+ alternateHeader: string | null;
77
+ };
78
+ notes: string[];
79
+ };
80
+ appManifest: {
81
+ appName: string;
82
+ appType: "remote-mcp";
83
+ serverUrl: string;
84
+ authType: "oauth-or-bearer";
85
+ };
86
+ setup: {
87
+ developerMode: true;
88
+ requiredReachability: "public-https";
89
+ connectionType: "Remote MCP";
90
+ mode: ChatGptProfileMode;
91
+ firstPrompt: string;
92
+ verifyWith: string[];
93
+ };
94
+ tools: string[];
95
+ operationShape: {
96
+ recommendedTool: "computer_operation";
97
+ envelope: {
98
+ scope: string;
99
+ op: string;
100
+ target: string;
101
+ input: Record<string, unknown>;
102
+ options: Record<string, unknown>;
103
+ };
104
+ notes: string[];
105
+ };
106
+ recommendedFlow: Array<{
107
+ step: number;
108
+ tool: string;
109
+ purpose: string;
110
+ input?: Record<string, unknown>;
111
+ }>;
112
+ modelGuide: ChatGptModelGuide;
113
+ workflowRecipes: ChatGptWorkflowRecipe[];
114
+ gptInstructions: string[];
115
+ warnings: string[];
116
+ }
117
+ export interface ChatGptAppManifest {
118
+ kind: "chatgpt-app-manifest";
119
+ schemaVersion: 1;
120
+ mode: ChatGptProfileMode;
121
+ appName: string;
122
+ description: string;
123
+ appType: "remote-mcp";
124
+ mcpServerUrl: string;
125
+ auth: {
126
+ preferred: "oauth";
127
+ fallback: "bearer";
128
+ scopes: string[];
129
+ };
130
+ tools: string[];
131
+ firstPrompt: string;
132
+ warnings: string[];
133
+ }
134
+ export interface ChatGptConnectorConfig {
135
+ kind: "chatgpt-connector-config";
136
+ schemaVersion: 1;
137
+ mode: ChatGptProfileMode;
138
+ displayName: string;
139
+ mcpServerUrl: string;
140
+ connectionType: "Remote MCP";
141
+ auth: {
142
+ type: "oauth-or-bearer";
143
+ oauthScopes: string[];
144
+ bearerHeader: string | null;
145
+ alternateBearerHeader: string | null;
146
+ };
147
+ setup: ChatGptConnectProfile["setup"];
148
+ recommendedFlow: ChatGptConnectProfile["recommendedFlow"];
149
+ modelGuide: ChatGptConnectProfile["modelGuide"];
150
+ workflowRecipes: ChatGptConnectProfile["workflowRecipes"];
151
+ gptInstructions: string[];
152
+ warnings: string[];
153
+ }
154
+ export declare function connectionProfile(config: LocalPortConfig, includeSecrets?: boolean): ConnectionProfile;
155
+ export declare function parseChatGptProfileMode(value: string | undefined, command?: string): ChatGptProfileMode;
156
+ export declare function chatGptConnectProfile(config: LocalPortConfig, includeSecrets?: boolean, mode?: ChatGptProfileMode, options?: ChatGptProfileOptions): ChatGptConnectProfile;
157
+ export declare function chatGptAppManifest(config: LocalPortConfig, mode?: ChatGptProfileMode, options?: ChatGptProfileOptions): ChatGptAppManifest;
158
+ export declare function chatGptConnectorConfig(config: LocalPortConfig, includeSecrets?: boolean, mode?: ChatGptProfileMode, options?: ChatGptProfileOptions): ChatGptConnectorConfig;
159
+ export declare function localPublicBaseUrl(host: string, port: number): string;