codex-chatgpt-control 0.1.0-alpha.1
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 +5 -0
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/contracts/v1/fixtures/backend-capabilities.json +54 -0
- package/contracts/v1/fixtures/backend-error-event-missing-stream-input.json +10 -0
- package/contracts/v1/fixtures/backend-error-missing-run-input.json +10 -0
- package/contracts/v1/fixtures/backend-runner-plan-request.json +12 -0
- package/contracts/v1/fixtures/backend-version.json +10 -0
- package/contracts/v1/fixtures/command-descriptors.json +803 -0
- package/contracts/v1/fixtures/describe-runner-run.json +31 -0
- package/contracts/v1/fixtures/doctor-bridge-upload.json +34 -0
- package/contracts/v1/fixtures/help-root.json +6 -0
- package/contracts/v1/fixtures/named-plan-two-turn.json +39 -0
- package/contracts/v1/fixtures/output-json-parse-success.json +115 -0
- package/contracts/v1/fixtures/primitive-bootstrap-blocker.json +43 -0
- package/contracts/v1/fixtures/report-redaction-default.json +16 -0
- package/contracts/v1/fixtures/reports-create-redacted.json +16 -0
- package/contracts/v1/fixtures/reports-summarize-redacted.json +26 -0
- package/contracts/v1/fixtures/responses-basic-success.json +27 -0
- package/contracts/v1/fixtures/responses-hidden-instructions-unsupported.json +22 -0
- package/contracts/v1/fixtures/responses-unknown-field-unsupported.json +22 -0
- package/contracts/v1/fixtures/responses-unsupported-previous-response-id.json +22 -0
- package/contracts/v1/fixtures/responses-unsupported-temperature.json +22 -0
- package/contracts/v1/fixtures/run-basic-success.json +36 -0
- package/contracts/v1/fixtures/run-browser-bridge-blocker.json +180 -0
- package/contracts/v1/fixtures/run-file-attach-success.json +63 -0
- package/contracts/v1/fixtures/run-report-redacted.json +52 -0
- package/contracts/v1/fixtures/run-selector-drift-blocker.json +60 -0
- package/contracts/v1/fixtures/run-timeout-partial.json +81 -0
- package/contracts/v1/fixtures/run-two-turn-success.json +83 -0
- package/contracts/v1/fixtures/run-upload-permission-blocker.json +56 -0
- package/contracts/v1/fixtures/runner-budget-blocker.json +106 -0
- package/contracts/v1/fixtures/runner-full-agent-config.json +50 -0
- package/contracts/v1/fixtures/runner-input-items-and-files-plan.json +53 -0
- package/contracts/v1/fixtures/runner-metadata-only-plan.json +31 -0
- package/contracts/v1/fixtures/runner-visible-prefix-plan.json +28 -0
- package/contracts/v1/fixtures/runner-visible-setup-plan.json +37 -0
- package/contracts/v1/fixtures/stream-basic.ndjson +3 -0
- package/contracts/v1/fixtures/stream-blocked.ndjson +2 -0
- package/contracts/v1/fixtures/stream-submitted-completed.ndjson +3 -0
- package/contracts/v1/fixtures/workflow-ask-success.json +67 -0
- package/contracts/v1/manifest.json +210 -0
- package/contracts/v1/parity-suite.json +635 -0
- package/contracts/v1/schemas/agent.schema.json +52 -0
- package/contracts/v1/schemas/backend-event.schema.json +74 -0
- package/contracts/v1/schemas/backend-request.schema.json +59 -0
- package/contracts/v1/schemas/backend-response.schema.json +42 -0
- package/contracts/v1/schemas/capabilities.schema.json +33 -0
- package/contracts/v1/schemas/command-descriptor.schema.json +31 -0
- package/contracts/v1/schemas/command-result.schema.json +99 -0
- package/contracts/v1/schemas/manifest.schema.json +69 -0
- package/contracts/v1/schemas/response.schema.json +51 -0
- package/contracts/v1/schemas/run-result.schema.json +138 -0
- package/contracts/v1/schemas/sequence-plan.schema.json +44 -0
- package/contracts/v1/schemas/stream-event.schema.json +25 -0
- package/dist/codex-chatgpt-control-backend.mjs +5284 -0
- package/dist/codex-chatgpt-control.bundle.mjs +6013 -0
- package/dist/src/backend/client.d.ts +129 -0
- package/dist/src/backend/client.js +415 -0
- package/dist/src/backend/protocol.d.ts +74 -0
- package/dist/src/backend/protocol.js +128 -0
- package/dist/src/backend/session.d.ts +11 -0
- package/dist/src/backend/session.js +223 -0
- package/dist/src/backend/stdio-server.d.ts +9 -0
- package/dist/src/backend/stdio-server.js +88 -0
- package/dist/src/browser/attach.d.ts +8 -0
- package/dist/src/browser/attach.js +456 -0
- package/dist/src/browser/clipboard.d.ts +2 -0
- package/dist/src/browser/clipboard.js +26 -0
- package/dist/src/browser/downloads.d.ts +7 -0
- package/dist/src/browser/downloads.js +29 -0
- package/dist/src/browser/page-state.d.ts +17 -0
- package/dist/src/browser/page-state.js +66 -0
- package/dist/src/client.d.ts +167 -0
- package/dist/src/client.js +666 -0
- package/dist/src/commands/confirmations.d.ts +9 -0
- package/dist/src/commands/confirmations.js +33 -0
- package/dist/src/commands/context.d.ts +2 -0
- package/dist/src/commands/context.js +32 -0
- package/dist/src/commands/doctor.d.ts +16 -0
- package/dist/src/commands/doctor.js +120 -0
- package/dist/src/commands/files.d.ts +4 -0
- package/dist/src/commands/files.js +314 -0
- package/dist/src/commands/helpers.d.ts +16 -0
- package/dist/src/commands/helpers.js +128 -0
- package/dist/src/commands/messages.d.ts +16 -0
- package/dist/src/commands/messages.js +538 -0
- package/dist/src/commands/modes.d.ts +9 -0
- package/dist/src/commands/modes.js +323 -0
- package/dist/src/commands/output.d.ts +5 -0
- package/dist/src/commands/output.js +31 -0
- package/dist/src/commands/registry.d.ts +17 -0
- package/dist/src/commands/registry.js +275 -0
- package/dist/src/commands/reports.d.ts +14 -0
- package/dist/src/commands/reports.js +47 -0
- package/dist/src/commands/response-actions.d.ts +2 -0
- package/dist/src/commands/response-actions.js +141 -0
- package/dist/src/commands/sequence.d.ts +9 -0
- package/dist/src/commands/sequence.js +228 -0
- package/dist/src/commands/session.d.ts +2 -0
- package/dist/src/commands/session.js +25 -0
- package/dist/src/commands/threads.d.ts +6 -0
- package/dist/src/commands/threads.js +300 -0
- package/dist/src/dom/fixtures.d.ts +1 -0
- package/dist/src/dom/fixtures.js +8 -0
- package/dist/src/dom/menus.d.ts +8 -0
- package/dist/src/dom/menus.js +40 -0
- package/dist/src/dom/message-format.d.ts +30 -0
- package/dist/src/dom/message-format.js +593 -0
- package/dist/src/dom/messages.d.ts +40 -0
- package/dist/src/dom/messages.js +127 -0
- package/dist/src/dom/selectors.d.ts +20 -0
- package/dist/src/dom/selectors.js +72 -0
- package/dist/src/dom/visible-text.d.ts +5 -0
- package/dist/src/dom/visible-text.js +27 -0
- package/dist/src/errors.d.ts +32 -0
- package/dist/src/errors.js +127 -0
- package/dist/src/index.d.ts +34 -0
- package/dist/src/index.js +34 -0
- package/dist/src/logger.d.ts +15 -0
- package/dist/src/logger.js +25 -0
- package/dist/src/runner/agent.d.ts +2 -0
- package/dist/src/runner/agent.js +17 -0
- package/dist/src/runner/index.d.ts +7 -0
- package/dist/src/runner/index.js +7 -0
- package/dist/src/runner/interruptions.d.ts +3 -0
- package/dist/src/runner/interruptions.js +65 -0
- package/dist/src/runner/responses.d.ts +30 -0
- package/dist/src/runner/responses.js +171 -0
- package/dist/src/runner/result.d.ts +3 -0
- package/dist/src/runner/result.js +128 -0
- package/dist/src/runner/resume.d.ts +11 -0
- package/dist/src/runner/resume.js +26 -0
- package/dist/src/runner/stream.d.ts +13 -0
- package/dist/src/runner/stream.js +66 -0
- package/dist/src/runner/types.d.ts +264 -0
- package/dist/src/runner/types.js +1 -0
- package/dist/src/safety/blockers.d.ts +7 -0
- package/dist/src/safety/blockers.js +60 -0
- package/dist/src/safety/redaction.d.ts +2 -0
- package/dist/src/safety/redaction.js +18 -0
- package/dist/src/safety/report-redaction.d.ts +8 -0
- package/dist/src/safety/report-redaction.js +74 -0
- package/dist/src/safety/risk.d.ts +26 -0
- package/dist/src/safety/risk.js +28 -0
- package/dist/src/scripts/backend-server.d.ts +2 -0
- package/dist/src/scripts/backend-server.js +7 -0
- package/dist/src/scripts/check-parity-suite.d.ts +1 -0
- package/dist/src/scripts/check-parity-suite.js +10 -0
- package/dist/src/scripts/continue-thread.d.ts +27 -0
- package/dist/src/scripts/continue-thread.js +388 -0
- package/dist/src/scripts/live-smoke/harness.d.ts +11 -0
- package/dist/src/scripts/live-smoke/harness.js +151 -0
- package/dist/src/scripts/live-smoke/scenarios.d.ts +3 -0
- package/dist/src/scripts/live-smoke/scenarios.js +648 -0
- package/dist/src/scripts/live-smoke/types.d.ts +53 -0
- package/dist/src/scripts/live-smoke/types.js +1 -0
- package/dist/src/scripts/live-smoke-module.d.ts +3 -0
- package/dist/src/scripts/live-smoke-module.js +2 -0
- package/dist/src/scripts/live-smoke.d.ts +1 -0
- package/dist/src/scripts/live-smoke.js +54 -0
- package/dist/src/scripts/smoke-attach-file.d.ts +1 -0
- package/dist/src/scripts/smoke-attach-file.js +39 -0
- package/dist/src/scripts/smoke-hi.d.ts +1 -0
- package/dist/src/scripts/smoke-hi.js +25 -0
- package/dist/src/scripts/smoke-search-open-read.d.ts +1 -0
- package/dist/src/scripts/smoke-search-open-read.js +41 -0
- package/dist/src/testing/parity-suite.d.ts +12 -0
- package/dist/src/testing/parity-suite.js +213 -0
- package/dist/src/types.d.ts +495 -0
- package/dist/src/types.js +1 -0
- package/package.json +81 -0
- package/references/agents-runner.md +32 -0
- package/references/backend-protocol.md +238 -0
- package/references/python-parity.md +168 -0
- package/references/responses-adapter.md +28 -0
- package/references/safety.md +14 -0
- package/references/streaming.md +15 -0
- package/references/troubleshooting.md +91 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export const BACKEND_REQUEST_SCHEMA_VERSION = "chatgpt.browser_control.backend_request.v1";
|
|
2
|
+
export const BACKEND_RESPONSE_SCHEMA_VERSION = "chatgpt.browser_control.backend_response.v1";
|
|
3
|
+
export const BACKEND_EVENT_SCHEMA_VERSION = "chatgpt.browser_control.backend_event.v1";
|
|
4
|
+
export const backendCommands = [
|
|
5
|
+
"backend.version",
|
|
6
|
+
"backend.health",
|
|
7
|
+
"backend.capabilities",
|
|
8
|
+
"runner.run",
|
|
9
|
+
"runner.plan",
|
|
10
|
+
"runner.stream",
|
|
11
|
+
"responses.create",
|
|
12
|
+
"ask",
|
|
13
|
+
"askInThread",
|
|
14
|
+
"askWithFiles",
|
|
15
|
+
"askAndDownload",
|
|
16
|
+
"runMessages",
|
|
17
|
+
"openThread",
|
|
18
|
+
"readLatest",
|
|
19
|
+
"copyLatest",
|
|
20
|
+
"downloadLatest",
|
|
21
|
+
"runPlan",
|
|
22
|
+
"doctor",
|
|
23
|
+
"createReport",
|
|
24
|
+
"reports.create",
|
|
25
|
+
"reports.redact",
|
|
26
|
+
"reports.summarize",
|
|
27
|
+
"commands",
|
|
28
|
+
"describe",
|
|
29
|
+
"help",
|
|
30
|
+
"session.bootstrap",
|
|
31
|
+
"threads.new",
|
|
32
|
+
"threads.search",
|
|
33
|
+
"threads.open",
|
|
34
|
+
"messages.compose",
|
|
35
|
+
"messages.submit",
|
|
36
|
+
"messages.ask",
|
|
37
|
+
"messages.wait",
|
|
38
|
+
"messages.readLatest",
|
|
39
|
+
"messages.waitAndRead",
|
|
40
|
+
"files.attach",
|
|
41
|
+
"files.downloadLatest",
|
|
42
|
+
"modes.set",
|
|
43
|
+
"tools.select",
|
|
44
|
+
"response.copy"
|
|
45
|
+
];
|
|
46
|
+
export class ProtocolError extends Error {
|
|
47
|
+
code;
|
|
48
|
+
recoverable;
|
|
49
|
+
constructor(code, message, recoverable) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.code = code;
|
|
52
|
+
this.recoverable = recoverable;
|
|
53
|
+
this.name = "ProtocolError";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const commandSet = new Set(backendCommands);
|
|
57
|
+
export function parseBackendRequest(raw) {
|
|
58
|
+
if (!isRecord(raw)) {
|
|
59
|
+
throw new ProtocolError("invalid_request", "Backend request must be an object.", false);
|
|
60
|
+
}
|
|
61
|
+
const schemaVersion = raw.schemaVersion;
|
|
62
|
+
if (schemaVersion !== BACKEND_REQUEST_SCHEMA_VERSION) {
|
|
63
|
+
throw new ProtocolError("unsupported_schema_version", `Unsupported backend request schemaVersion: ${String(schemaVersion)}`, false);
|
|
64
|
+
}
|
|
65
|
+
const command = raw.command;
|
|
66
|
+
if (typeof command !== "string" || !commandSet.has(command)) {
|
|
67
|
+
throw new ProtocolError("unknown_command", `Unknown backend command: ${String(command)}`, false);
|
|
68
|
+
}
|
|
69
|
+
const request = {
|
|
70
|
+
schemaVersion: BACKEND_REQUEST_SCHEMA_VERSION,
|
|
71
|
+
command: command,
|
|
72
|
+
payload: normalizePayload(raw.payload)
|
|
73
|
+
};
|
|
74
|
+
if (raw.requestId !== undefined) {
|
|
75
|
+
if (typeof raw.requestId !== "string" || raw.requestId.length === 0) {
|
|
76
|
+
throw new ProtocolError("invalid_request", "Backend request requestId must be a non-empty string when provided.", false);
|
|
77
|
+
}
|
|
78
|
+
request.requestId = raw.requestId;
|
|
79
|
+
}
|
|
80
|
+
return request;
|
|
81
|
+
}
|
|
82
|
+
export function backendResponseOk(requestId, result) {
|
|
83
|
+
const response = {
|
|
84
|
+
schemaVersion: BACKEND_RESPONSE_SCHEMA_VERSION,
|
|
85
|
+
ok: true,
|
|
86
|
+
result
|
|
87
|
+
};
|
|
88
|
+
if (requestId !== undefined)
|
|
89
|
+
response.requestId = requestId;
|
|
90
|
+
return response;
|
|
91
|
+
}
|
|
92
|
+
export function backendResponseError(requestId, error) {
|
|
93
|
+
const response = {
|
|
94
|
+
schemaVersion: BACKEND_RESPONSE_SCHEMA_VERSION,
|
|
95
|
+
ok: false,
|
|
96
|
+
error: {
|
|
97
|
+
code: error instanceof ProtocolError ? error.code : "invalid_request",
|
|
98
|
+
message: error.message,
|
|
99
|
+
recoverable: error instanceof ProtocolError ? error.recoverable : false
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
if (requestId !== undefined)
|
|
103
|
+
response.requestId = requestId;
|
|
104
|
+
return response;
|
|
105
|
+
}
|
|
106
|
+
export function backendEvent(requestId, payload) {
|
|
107
|
+
const event = {
|
|
108
|
+
schemaVersion: BACKEND_EVENT_SCHEMA_VERSION,
|
|
109
|
+
...payload
|
|
110
|
+
};
|
|
111
|
+
if (requestId !== undefined)
|
|
112
|
+
event.requestId = requestId;
|
|
113
|
+
return event;
|
|
114
|
+
}
|
|
115
|
+
export function backendEventCompleted(requestId, result) {
|
|
116
|
+
return backendEvent(requestId, { type: "completed", result });
|
|
117
|
+
}
|
|
118
|
+
function normalizePayload(value) {
|
|
119
|
+
if (value === undefined)
|
|
120
|
+
return {};
|
|
121
|
+
if (!isRecord(value)) {
|
|
122
|
+
throw new ProtocolError("invalid_request", "Backend request payload must be an object when provided.", false);
|
|
123
|
+
}
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
function isRecord(value) {
|
|
127
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
128
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ChatGPTClientOptions } from "../client.js";
|
|
2
|
+
import { type BackendEvent, type BackendRequest, type BackendResponse } from "./protocol.js";
|
|
3
|
+
export type BackendSessionOptions = ChatGPTClientOptions;
|
|
4
|
+
export declare class BackendSession {
|
|
5
|
+
private readonly options;
|
|
6
|
+
private clientInstance;
|
|
7
|
+
constructor(options?: BackendSessionOptions);
|
|
8
|
+
dispatch(request: BackendRequest): Promise<BackendResponse>;
|
|
9
|
+
stream(request: BackendRequest): AsyncIterable<BackendEvent>;
|
|
10
|
+
private client;
|
|
11
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { createChatGPT } from "../client.js";
|
|
2
|
+
import { BACKEND_REQUEST_SCHEMA_VERSION, backendCommands, backendResponseError, backendEvent, backendEventCompleted, backendResponseOk, ProtocolError } from "./protocol.js";
|
|
3
|
+
export class BackendSession {
|
|
4
|
+
options;
|
|
5
|
+
clientInstance;
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
this.options = options;
|
|
8
|
+
}
|
|
9
|
+
async dispatch(request) {
|
|
10
|
+
try {
|
|
11
|
+
const result = await dispatchBackendCommand(this.client(), request);
|
|
12
|
+
return backendResponseOk(request.requestId, result);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
return backendResponseError(request.requestId, error instanceof Error ? error : new Error(String(error)));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async *stream(request) {
|
|
19
|
+
try {
|
|
20
|
+
if (request.command !== "runner.stream") {
|
|
21
|
+
const response = await this.dispatch(request);
|
|
22
|
+
if (response.ok) {
|
|
23
|
+
yield backendEventCompleted(request.requestId, response.result);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
yield backendEvent(request.requestId, { type: "error", error: response.error });
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const payload = request.payload;
|
|
31
|
+
const agent = this.client().agent(agentConfig(payload));
|
|
32
|
+
const stream = this.client().runner.run(agent, runInput(payload), { stream: true });
|
|
33
|
+
for await (const event of stream) {
|
|
34
|
+
yield backendEvent(request.requestId, {
|
|
35
|
+
type: "run_item_stream_event",
|
|
36
|
+
name: event.name,
|
|
37
|
+
item: event.item
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
yield backendEventCompleted(request.requestId, await stream.completed);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
const protocolError = error instanceof ProtocolError
|
|
44
|
+
? error
|
|
45
|
+
: new ProtocolError("invalid_request", error instanceof Error ? error.message : String(error), false);
|
|
46
|
+
yield backendEvent(request.requestId, {
|
|
47
|
+
type: "error",
|
|
48
|
+
error: {
|
|
49
|
+
code: protocolError.code,
|
|
50
|
+
message: protocolError.message,
|
|
51
|
+
recoverable: protocolError.recoverable
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
client() {
|
|
57
|
+
this.clientInstance ??= createChatGPT(this.options);
|
|
58
|
+
return this.clientInstance;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function dispatchBackendCommand(client, request) {
|
|
62
|
+
const payload = request.payload;
|
|
63
|
+
switch (request.command) {
|
|
64
|
+
case "backend.version":
|
|
65
|
+
return {
|
|
66
|
+
name: "codex-chatgpt-control-backend",
|
|
67
|
+
runtime: "node",
|
|
68
|
+
protocolVersion: BACKEND_REQUEST_SCHEMA_VERSION
|
|
69
|
+
};
|
|
70
|
+
case "backend.health":
|
|
71
|
+
return {
|
|
72
|
+
ok: true,
|
|
73
|
+
status: "ok",
|
|
74
|
+
timestamp: new Date().toISOString()
|
|
75
|
+
};
|
|
76
|
+
case "backend.capabilities":
|
|
77
|
+
return backendCapabilities();
|
|
78
|
+
case "runner.run": {
|
|
79
|
+
const agent = client.agent(agentConfig(payload));
|
|
80
|
+
return client.runner.run(agent, runInput(payload));
|
|
81
|
+
}
|
|
82
|
+
case "runner.plan": {
|
|
83
|
+
const agent = client.agent(agentConfig(payload));
|
|
84
|
+
return client.runner.plan(agent, runInput(payload));
|
|
85
|
+
}
|
|
86
|
+
case "responses.create":
|
|
87
|
+
return client.responses.create(payload);
|
|
88
|
+
case "commands":
|
|
89
|
+
return client.commands(commandFilter(payload));
|
|
90
|
+
case "describe":
|
|
91
|
+
return client.describe(requiredString(payload, "name"));
|
|
92
|
+
case "help":
|
|
93
|
+
return client.help(optionalString(payload, "topic"));
|
|
94
|
+
case "doctor":
|
|
95
|
+
return client.doctor(payload);
|
|
96
|
+
case "ask":
|
|
97
|
+
return client.ask(payload);
|
|
98
|
+
case "askInThread":
|
|
99
|
+
return client.askInThread(payload);
|
|
100
|
+
case "askWithFiles":
|
|
101
|
+
return client.askWithFiles(payload);
|
|
102
|
+
case "askAndDownload":
|
|
103
|
+
return client.askAndDownload(payload);
|
|
104
|
+
case "runMessages":
|
|
105
|
+
return client.runMessages(payload);
|
|
106
|
+
case "openThread":
|
|
107
|
+
return client.openThread(payload);
|
|
108
|
+
case "readLatest":
|
|
109
|
+
return client.readLatest(emptyToUndefined(payload));
|
|
110
|
+
case "copyLatest":
|
|
111
|
+
return client.copyLatest(emptyToUndefined(payload));
|
|
112
|
+
case "downloadLatest":
|
|
113
|
+
return client.downloadLatest(payload);
|
|
114
|
+
case "runPlan":
|
|
115
|
+
return client.runPlan(runPlanPayload(payload));
|
|
116
|
+
case "createReport":
|
|
117
|
+
return client.createReport(requiredRecord(payload, "result"), optionalRecord(payload, "args"));
|
|
118
|
+
case "reports.create":
|
|
119
|
+
return client.reports.create(requiredRecord(payload, "result"), optionalRecord(payload, "args"));
|
|
120
|
+
case "reports.redact":
|
|
121
|
+
return client.reports.redact(payload.value, optionalRecord(payload, "args"));
|
|
122
|
+
case "reports.summarize":
|
|
123
|
+
return client.reports.summarize(requiredRecord(payload, "result"), optionalRecord(payload, "args"));
|
|
124
|
+
case "session.bootstrap":
|
|
125
|
+
return client.session.bootstrap(emptyToUndefined(payload));
|
|
126
|
+
case "threads.new":
|
|
127
|
+
return client.threads.new(emptyToUndefined(payload));
|
|
128
|
+
case "threads.search":
|
|
129
|
+
return client.threads.search(payload);
|
|
130
|
+
case "threads.open":
|
|
131
|
+
return client.threads.open(payload);
|
|
132
|
+
case "messages.compose":
|
|
133
|
+
return client.messages.compose(payload);
|
|
134
|
+
case "messages.submit":
|
|
135
|
+
return client.messages.submit(emptyToUndefined(payload));
|
|
136
|
+
case "messages.ask":
|
|
137
|
+
return client.messages.ask(payload);
|
|
138
|
+
case "messages.wait":
|
|
139
|
+
return client.messages.wait(emptyToUndefined(payload));
|
|
140
|
+
case "messages.readLatest":
|
|
141
|
+
return client.messages.readLatest(emptyToUndefined(payload));
|
|
142
|
+
case "messages.waitAndRead":
|
|
143
|
+
return client.messages.waitAndRead(payload);
|
|
144
|
+
case "files.attach":
|
|
145
|
+
return client.files.attach(payload);
|
|
146
|
+
case "files.downloadLatest":
|
|
147
|
+
return client.files.downloadLatest(payload);
|
|
148
|
+
case "modes.set":
|
|
149
|
+
return client.modes.set(payload);
|
|
150
|
+
case "tools.select":
|
|
151
|
+
return client.tools.select(payload);
|
|
152
|
+
case "response.copy":
|
|
153
|
+
return client.response.copy(emptyToUndefined(payload));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function backendCapabilities() {
|
|
157
|
+
return {
|
|
158
|
+
protocolVersion: BACKEND_REQUEST_SCHEMA_VERSION,
|
|
159
|
+
commands: [...backendCommands],
|
|
160
|
+
transports: ["stdio"],
|
|
161
|
+
streaming: {
|
|
162
|
+
modes: ["ndjson"],
|
|
163
|
+
tokenDeltas: false
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function agentConfig(payload) {
|
|
168
|
+
return requiredRecord(payload, "agent");
|
|
169
|
+
}
|
|
170
|
+
function runInput(payload) {
|
|
171
|
+
if (!Object.hasOwn(payload, "input")) {
|
|
172
|
+
throw new ProtocolError("invalid_request", "Backend runner command requires payload.input.", false);
|
|
173
|
+
}
|
|
174
|
+
return payload.input;
|
|
175
|
+
}
|
|
176
|
+
function runPlanPayload(payload) {
|
|
177
|
+
if (isRecord(payload.plan))
|
|
178
|
+
return payload.plan;
|
|
179
|
+
return payload;
|
|
180
|
+
}
|
|
181
|
+
function commandFilter(payload) {
|
|
182
|
+
if (isRecord(payload.filter))
|
|
183
|
+
return payload.filter;
|
|
184
|
+
return Object.keys(payload).length === 0 ? undefined : payload;
|
|
185
|
+
}
|
|
186
|
+
function requiredString(payload, key) {
|
|
187
|
+
const value = payload[key];
|
|
188
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
189
|
+
throw new ProtocolError("invalid_request", `Backend command requires payload.${key} as a non-empty string.`, false);
|
|
190
|
+
}
|
|
191
|
+
return value;
|
|
192
|
+
}
|
|
193
|
+
function optionalString(payload, key) {
|
|
194
|
+
const value = payload[key];
|
|
195
|
+
if (value === undefined)
|
|
196
|
+
return undefined;
|
|
197
|
+
if (typeof value !== "string") {
|
|
198
|
+
throw new ProtocolError("invalid_request", `Backend command payload.${key} must be a string when provided.`, false);
|
|
199
|
+
}
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
function requiredRecord(payload, key) {
|
|
203
|
+
const value = payload[key];
|
|
204
|
+
if (!isRecord(value)) {
|
|
205
|
+
throw new ProtocolError("invalid_request", `Backend command requires payload.${key} as an object.`, false);
|
|
206
|
+
}
|
|
207
|
+
return value;
|
|
208
|
+
}
|
|
209
|
+
function optionalRecord(payload, key) {
|
|
210
|
+
const value = payload[key];
|
|
211
|
+
if (value === undefined)
|
|
212
|
+
return undefined;
|
|
213
|
+
if (!isRecord(value)) {
|
|
214
|
+
throw new ProtocolError("invalid_request", `Backend command payload.${key} must be an object when provided.`, false);
|
|
215
|
+
}
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
function emptyToUndefined(payload) {
|
|
219
|
+
return Object.keys(payload).length === 0 ? undefined : payload;
|
|
220
|
+
}
|
|
221
|
+
function isRecord(value) {
|
|
222
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
223
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
import { BackendSession } from "./session.js";
|
|
3
|
+
export type BackendStdioServerOptions = {
|
|
4
|
+
input: Readable;
|
|
5
|
+
output: Writable;
|
|
6
|
+
error?: Writable;
|
|
7
|
+
session?: BackendSession;
|
|
8
|
+
};
|
|
9
|
+
export declare function runBackendStdioServer(options: BackendStdioServerOptions): Promise<void>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createInterface } from "node:readline";
|
|
2
|
+
import { BackendSession } from "./session.js";
|
|
3
|
+
import { backendResponseError, parseBackendRequest, ProtocolError } from "./protocol.js";
|
|
4
|
+
export async function runBackendStdioServer(options) {
|
|
5
|
+
const session = options.session ?? new BackendSession();
|
|
6
|
+
const lines = createInterface({
|
|
7
|
+
input: options.input,
|
|
8
|
+
crlfDelay: Infinity
|
|
9
|
+
});
|
|
10
|
+
const writeJson = createJsonLineWriter(options.output);
|
|
11
|
+
const tasks = new Set();
|
|
12
|
+
for await (const line of lines) {
|
|
13
|
+
const trimmed = line.trim();
|
|
14
|
+
if (trimmed.length === 0)
|
|
15
|
+
continue;
|
|
16
|
+
const task = handleLine(session, trimmed, writeJson, options.error);
|
|
17
|
+
tasks.add(task);
|
|
18
|
+
void task.finally(() => {
|
|
19
|
+
tasks.delete(task);
|
|
20
|
+
}).catch(() => { });
|
|
21
|
+
}
|
|
22
|
+
await Promise.allSettled(tasks);
|
|
23
|
+
}
|
|
24
|
+
async function handleLine(session, line, writeJson, error) {
|
|
25
|
+
let request;
|
|
26
|
+
try {
|
|
27
|
+
const raw = JSON.parse(line);
|
|
28
|
+
request = parseBackendRequest(raw);
|
|
29
|
+
if (request.command === "runner.stream") {
|
|
30
|
+
for await (const event of session.stream(request)) {
|
|
31
|
+
await writeJson(event);
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await writeJson(await session.dispatch(request));
|
|
36
|
+
}
|
|
37
|
+
catch (caught) {
|
|
38
|
+
const response = backendResponseError(request?.requestId ?? requestIdFromLine(line), normalizeError(caught));
|
|
39
|
+
await writeJson(response);
|
|
40
|
+
if (!(caught instanceof ProtocolError)) {
|
|
41
|
+
await writeDiagnostic(error, caught);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function normalizeError(error) {
|
|
46
|
+
if (error instanceof SyntaxError) {
|
|
47
|
+
return new ProtocolError("invalid_request", `Invalid JSON backend request line: ${error.message}`, false);
|
|
48
|
+
}
|
|
49
|
+
if (error instanceof Error)
|
|
50
|
+
return error;
|
|
51
|
+
return new ProtocolError("invalid_request", String(error), false);
|
|
52
|
+
}
|
|
53
|
+
function requestIdFromLine(line) {
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(line);
|
|
56
|
+
if (isRecord(parsed) && typeof parsed.requestId === "string" && parsed.requestId.length > 0) {
|
|
57
|
+
return parsed.requestId;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
function createJsonLineWriter(output) {
|
|
66
|
+
let tail = Promise.resolve();
|
|
67
|
+
return value => {
|
|
68
|
+
const next = tail.then(() => writeLine(output, JSON.stringify(value)));
|
|
69
|
+
tail = next.catch(() => { });
|
|
70
|
+
return next;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async function writeDiagnostic(error, value) {
|
|
74
|
+
if (error === undefined)
|
|
75
|
+
return;
|
|
76
|
+
const message = value instanceof Error ? `${value.name}: ${value.message}` : String(value);
|
|
77
|
+
await writeLine(error, message);
|
|
78
|
+
}
|
|
79
|
+
async function writeLine(output, line) {
|
|
80
|
+
if (output.write(`${line}\n`))
|
|
81
|
+
return;
|
|
82
|
+
await new Promise(resolve => {
|
|
83
|
+
output.once("drain", resolve);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function isRecord(value) {
|
|
87
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
88
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BootstrapArgs, BrowserLike, PageLike, RuntimeEnv } from "../types.js";
|
|
2
|
+
export type AttachedBrowser = {
|
|
3
|
+
browser: BrowserLike;
|
|
4
|
+
page: PageLike;
|
|
5
|
+
browserName: string;
|
|
6
|
+
tabId?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function attachChatGPTBrowser(env: RuntimeEnv, args?: BootstrapArgs): Promise<AttachedBrowser>;
|