@kaiba-cloud/orbital-harness-codex 0.0.0-stage → 0.0.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/README.md +144 -2
- package/dist/chunks/chunk-MJXEEIFC.js +28683 -0
- package/dist/plugin.js +104 -0
- package/dist/turn.js +280 -0
- package/orbital-plugin.json +5 -0
- package/package.json +27 -4
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createRequire as __orbitalCreateRequire } from 'node:module';
|
|
2
|
+
const require = __orbitalCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
servePlugin
|
|
5
|
+
} from "./chunks/chunk-MJXEEIFC.js";
|
|
6
|
+
|
|
7
|
+
// src/plugin.ts
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
// src/config.ts
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
var SERVER = "orbital";
|
|
14
|
+
function quote(value) {
|
|
15
|
+
return JSON.stringify(value);
|
|
16
|
+
}
|
|
17
|
+
function serverBlock(program, harness) {
|
|
18
|
+
const [command = "orbital", ...rest] = program;
|
|
19
|
+
const args = [...rest, "mcp", "--attach", "--harness", harness, "--agent", harness];
|
|
20
|
+
return [`[mcp_servers.${SERVER}]`, `command = ${quote(command)}`, `args = [${args.map(quote).join(", ")}]`, ""].join("\n");
|
|
21
|
+
}
|
|
22
|
+
function headerName(line) {
|
|
23
|
+
const match = /^\s*\[\[?\s*([^\]]+?)\s*\]\]?\s*(?:#.*)?$/.exec(line);
|
|
24
|
+
if (!match) return null;
|
|
25
|
+
return match[1].split(".").map((part) => part.trim().replace(/^(["'])(.*)\1$/, "$2")).join(".");
|
|
26
|
+
}
|
|
27
|
+
function splitSections(text) {
|
|
28
|
+
const sections = [{ name: null, lines: [] }];
|
|
29
|
+
let inString = null;
|
|
30
|
+
for (const line of text.split("\n")) {
|
|
31
|
+
const name = inString === null ? headerName(line) : null;
|
|
32
|
+
if (name !== null) sections.push({ name, lines: [line] });
|
|
33
|
+
else sections.at(-1).lines.push(line);
|
|
34
|
+
for (const fence of ['"""', "'''"]) {
|
|
35
|
+
const count = line.split(fence).length - 1;
|
|
36
|
+
if (count % 2 === 1) inString = inString === fence ? null : inString === null ? fence : inString;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return sections;
|
|
40
|
+
}
|
|
41
|
+
function ownsServer(name) {
|
|
42
|
+
return name === `mcp_servers.${SERVER}` || (name?.startsWith(`mcp_servers.${SERVER}.`) ?? false);
|
|
43
|
+
}
|
|
44
|
+
function definesInOtherForm(sections) {
|
|
45
|
+
return sections.some((section) => section.lines.some((line) => section.name === null && new RegExp(`^\\s*mcp_servers\\s*\\.\\s*${SERVER}\\b`).test(line) || section.name === "mcp_servers" && new RegExp(`^\\s*${SERVER}\\s*=`).test(line)));
|
|
46
|
+
}
|
|
47
|
+
function mergeServer(existing, block, force) {
|
|
48
|
+
if (existing === null || existing.trim() === "") return { text: block, outcome: "added" };
|
|
49
|
+
const sections = splitSections(existing);
|
|
50
|
+
if (definesInOtherForm(sections)) return { text: existing, outcome: "unsupported" };
|
|
51
|
+
const owned = sections.filter((section) => ownsServer(section.name));
|
|
52
|
+
if (owned.length === 0) {
|
|
53
|
+
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
54
|
+
return { text: `${existing}${separator}${block}`, outcome: "added" };
|
|
55
|
+
}
|
|
56
|
+
const current = owned.map((section) => section.lines.join("\n")).join("\n").trimEnd();
|
|
57
|
+
if (current === block.trimEnd()) return { text: existing, outcome: "same" };
|
|
58
|
+
if (!force) return { text: existing, outcome: "kept" };
|
|
59
|
+
let placed = false;
|
|
60
|
+
const kept = sections.flatMap((section) => {
|
|
61
|
+
if (section.lines.length === 0) return [];
|
|
62
|
+
if (!ownsServer(section.name)) return [section.lines.join("\n")];
|
|
63
|
+
if (placed) return [];
|
|
64
|
+
placed = true;
|
|
65
|
+
return [block];
|
|
66
|
+
});
|
|
67
|
+
const joined = kept.join("\n");
|
|
68
|
+
return { text: joined.endsWith("\n") ? joined : `${joined}
|
|
69
|
+
`, outcome: "replaced" };
|
|
70
|
+
}
|
|
71
|
+
function installServer(folder, program, harness, force) {
|
|
72
|
+
const file = join(folder, "config.toml");
|
|
73
|
+
const existing = existsSync(file) ? readFileSync(file, "utf8") : null;
|
|
74
|
+
const merged = mergeServer(existing, serverBlock(program, harness), force);
|
|
75
|
+
if (merged.outcome === "unsupported") return { written: [], notes: [`${file} defines the MCP server ${SERVER} in a form that install does not edit: it kept the file`] };
|
|
76
|
+
if (merged.outcome === "kept") return { written: [], notes: [`${file} already defines the MCP server ${SERVER} differently: it kept the file, and --force replaces the definition`] };
|
|
77
|
+
if (merged.outcome === "same") return { written: [], notes: [] };
|
|
78
|
+
mkdirSync(folder, { recursive: true });
|
|
79
|
+
writeFileSync(file, merged.text);
|
|
80
|
+
return { written: [file], notes: [`codex reaches the orbital tools through the MCP server ${SERVER} in ${file}, when it trusts this project`] };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/plugin.ts
|
|
84
|
+
var NAME = "orbital-harness-codex";
|
|
85
|
+
var HARNESS = "codex";
|
|
86
|
+
var FOLDER = { folder: ".codex", instructions: "AGENTS.md", agents: "agents", skills: "skills" };
|
|
87
|
+
var EGRESS = ["api.openai.com", "chatgpt.com", "auth.openai.com"];
|
|
88
|
+
function plugin() {
|
|
89
|
+
return {
|
|
90
|
+
name: NAME,
|
|
91
|
+
version: "0.0.1",
|
|
92
|
+
permissions: { network: [], secrets: [], models: [] },
|
|
93
|
+
ports: {
|
|
94
|
+
harnesses: {
|
|
95
|
+
harnesses: [{ id: HARNESS, description: "The OpenAI Codex CLI: its own login, its shell and file edits, and the orbital tools.", callsModel: false, browser: true, folder: FOLDER }],
|
|
96
|
+
command: async () => ({ command: [process.execPath, fileURLToPath(new URL("turn.js", import.meta.url))], egress: EGRESS }),
|
|
97
|
+
install: async (request) => installServer(request.folder, request.program, HARNESS, request.force)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/main.ts
|
|
104
|
+
servePlugin(() => plugin());
|
package/dist/turn.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { createRequire as __orbitalCreateRequire } from 'node:module';
|
|
2
|
+
const require = __orbitalCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
browserEnv,
|
|
5
|
+
browserServerBlock,
|
|
6
|
+
isBrowserServer,
|
|
7
|
+
replaceMcpServers,
|
|
8
|
+
textPart,
|
|
9
|
+
thinkingPart,
|
|
10
|
+
toolCallPart,
|
|
11
|
+
toolResultPart
|
|
12
|
+
} from "./chunks/chunk-MJXEEIFC.js";
|
|
13
|
+
|
|
14
|
+
// src/turn.ts
|
|
15
|
+
import { randomUUID } from "node:crypto";
|
|
16
|
+
import { spawn } from "node:child_process";
|
|
17
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
18
|
+
import { createInterface } from "node:readline";
|
|
19
|
+
|
|
20
|
+
// src/args.ts
|
|
21
|
+
var DEFAULT_PROVIDER = "openai";
|
|
22
|
+
var DEFAULT_MODEL = "default";
|
|
23
|
+
var MCP_SERVER = "orbital";
|
|
24
|
+
function modelOf(model) {
|
|
25
|
+
if (!model) return { provider: DEFAULT_PROVIDER, model: null };
|
|
26
|
+
const colon = model.indexOf(":");
|
|
27
|
+
if (colon === -1) return { provider: DEFAULT_PROVIDER, model };
|
|
28
|
+
return { provider: model.slice(0, colon), model: model.slice(colon + 1) };
|
|
29
|
+
}
|
|
30
|
+
function toml(value) {
|
|
31
|
+
return JSON.stringify(value);
|
|
32
|
+
}
|
|
33
|
+
function mcpOverrides(command, env) {
|
|
34
|
+
const [program, ...args] = command;
|
|
35
|
+
if (!program) return [];
|
|
36
|
+
const names = Object.keys(env);
|
|
37
|
+
return [
|
|
38
|
+
`mcp_servers.${MCP_SERVER}.command=${toml(program)}`,
|
|
39
|
+
`mcp_servers.${MCP_SERVER}.args=${toml(args)}`,
|
|
40
|
+
...names.length > 0 ? [`mcp_servers.${MCP_SERVER}.env_vars=${toml(names)}`] : []
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
function codexArgs(spec, threadId) {
|
|
44
|
+
const choice = modelOf(spec.model);
|
|
45
|
+
const overrides = [
|
|
46
|
+
...choice.provider === DEFAULT_PROVIDER ? [] : [`model_provider=${toml(choice.provider)}`],
|
|
47
|
+
`developer_instructions=${toml(spec.systemPrompt)}`,
|
|
48
|
+
...mcpOverrides(spec.tools.command, spec.tools.env)
|
|
49
|
+
];
|
|
50
|
+
return [
|
|
51
|
+
"exec",
|
|
52
|
+
...threadId ? ["resume", threadId] : [],
|
|
53
|
+
"--json",
|
|
54
|
+
"--skip-git-repo-check",
|
|
55
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
56
|
+
"--disable",
|
|
57
|
+
"multi_agent",
|
|
58
|
+
"--disable",
|
|
59
|
+
"goals",
|
|
60
|
+
...choice.model ? ["-m", choice.model] : [],
|
|
61
|
+
...overrides.flatMap((override) => ["-c", override]),
|
|
62
|
+
"--",
|
|
63
|
+
spec.prompt
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
function childEnv(spec, base, codexHome) {
|
|
67
|
+
const merged = { ...base, ...spec.env, ...spec.tools.env, ...spec.browser && browserEnv(spec.browser), CODEX_HOME: codexHome };
|
|
68
|
+
return Object.fromEntries(Object.entries(merged).filter((entry) => typeof entry[1] === "string"));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/lines.ts
|
|
72
|
+
var NO_USAGE = { input_tokens: 0, cached_input_tokens: 0, cache_write_input_tokens: 0, output_tokens: 0, reasoning_output_tokens: 0 };
|
|
73
|
+
function failureText(message) {
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(message);
|
|
76
|
+
return typeof parsed.error?.message === "string" ? parsed.error.message : message;
|
|
77
|
+
} catch {
|
|
78
|
+
return message;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function callOf(item) {
|
|
82
|
+
if (item.type === "command_execution") return toolCallPart({ id: item.id, name: "Bash", input: { command: item.command ?? "" } });
|
|
83
|
+
if (item.type === "mcp_tool_call") return toolCallPart({ id: item.id, name: `mcp__${item.server ?? "mcp"}__${item.tool ?? "tool"}`, input: item.arguments ?? {} });
|
|
84
|
+
if (item.type === "file_change") {
|
|
85
|
+
const changes = item.changes ?? [];
|
|
86
|
+
return toolCallPart({ id: item.id, name: "apply_patch", input: { path: changes[0]?.path ?? "", changes } });
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
function mcpText(item) {
|
|
91
|
+
if (item.error?.message) return item.error.message;
|
|
92
|
+
return (item.result?.content ?? []).flatMap((part) => part.type === "text" && part.text ? [part.text] : part.type === "image" ? ["[image]"] : []).join("\n");
|
|
93
|
+
}
|
|
94
|
+
function resultOf(item) {
|
|
95
|
+
if (item.type === "command_execution") return toolResultPart({ callId: item.id, name: "Bash", text: item.aggregated_output ?? "", isError: item.status !== "completed" || (item.exit_code ?? 0) !== 0 });
|
|
96
|
+
if (item.type === "mcp_tool_call") return toolResultPart({ callId: item.id, name: `mcp__${item.server ?? "mcp"}__${item.tool ?? "tool"}`, text: mcpText(item), isError: item.status !== "completed" });
|
|
97
|
+
if (item.type === "file_change") return toolResultPart({ callId: item.id, name: "apply_patch", text: (item.changes ?? []).map((change) => `${change.kind} ${change.path}`).join("\n"), isError: item.status !== "completed" });
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
var TurnLines = class {
|
|
101
|
+
threadId = null;
|
|
102
|
+
answer = null;
|
|
103
|
+
failure = null;
|
|
104
|
+
completed = false;
|
|
105
|
+
usage = null;
|
|
106
|
+
called = /* @__PURE__ */ new Set();
|
|
107
|
+
handle(event) {
|
|
108
|
+
if (event.type === "thread.started") {
|
|
109
|
+
this.threadId = event.thread_id;
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
if (event.type === "turn.completed") {
|
|
113
|
+
this.completed = true;
|
|
114
|
+
this.usage = event.usage;
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
if (event.type === "turn.failed") {
|
|
118
|
+
this.failure = failureText(event.error.message);
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
if (event.type === "item.started") return this.started(event.item);
|
|
122
|
+
if (event.type === "item.completed") return this.finished(event.item);
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
started(item) {
|
|
126
|
+
const call = callOf(item);
|
|
127
|
+
if (!call || this.called.has(item.id)) return [];
|
|
128
|
+
this.called.add(item.id);
|
|
129
|
+
return [{ role: "ROLE_AGENT", parts: [call] }];
|
|
130
|
+
}
|
|
131
|
+
finished(item) {
|
|
132
|
+
if (item.type === "agent_message" && item.text) {
|
|
133
|
+
this.answer = item.text;
|
|
134
|
+
return [{ role: "ROLE_AGENT", parts: [textPart(item.text)] }];
|
|
135
|
+
}
|
|
136
|
+
if (item.type === "reasoning" && item.text) return [{ role: "ROLE_AGENT", parts: [thinkingPart(item.text)] }];
|
|
137
|
+
const result = resultOf(item);
|
|
138
|
+
if (!result) return [];
|
|
139
|
+
return [...this.started(item), { role: "ROLE_USER", parts: [result] }];
|
|
140
|
+
}
|
|
141
|
+
usageLine(provider, model, previous) {
|
|
142
|
+
if (!this.usage) return null;
|
|
143
|
+
const total = this.usage;
|
|
144
|
+
const delta = (key) => Math.max(0, total[key] - previous[key]);
|
|
145
|
+
const cached = delta("cached_input_tokens");
|
|
146
|
+
return { type: "usage", models: [{ provider, model, inputTokens: Math.max(0, delta("input_tokens") - cached), outputTokens: delta("output_tokens"), cacheReadTokens: cached, cacheWriteTokens: delta("cache_write_input_tokens"), costUsd: 0, llmCalls: 1 }] };
|
|
147
|
+
}
|
|
148
|
+
totalUsage() {
|
|
149
|
+
return this.usage;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// src/session.ts
|
|
154
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
155
|
+
import { homedir } from "node:os";
|
|
156
|
+
import { join } from "node:path";
|
|
157
|
+
var CONFIG = "config.toml";
|
|
158
|
+
function sourceHome(env) {
|
|
159
|
+
return env.CODEX_HOME ? env.CODEX_HOME : join(env.HOME ?? homedir(), ".codex");
|
|
160
|
+
}
|
|
161
|
+
function stateFile(sessionDir) {
|
|
162
|
+
return join(sessionDir, "codex", "state.json");
|
|
163
|
+
}
|
|
164
|
+
function readState(sessionDir) {
|
|
165
|
+
const file = stateFile(sessionDir);
|
|
166
|
+
if (!existsSync(file)) return { threadId: null, usage: NO_USAGE };
|
|
167
|
+
try {
|
|
168
|
+
const raw = JSON.parse(readFileSync(file, "utf8"));
|
|
169
|
+
return { threadId: typeof raw.threadId === "string" ? raw.threadId : null, usage: { ...NO_USAGE, ...raw.usage } };
|
|
170
|
+
} catch {
|
|
171
|
+
return { threadId: null, usage: NO_USAGE };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function writeState(sessionDir, state) {
|
|
175
|
+
mkdirSync(join(sessionDir, "codex"), { recursive: true });
|
|
176
|
+
writeFileSync(stateFile(sessionDir), `${JSON.stringify(state)}
|
|
177
|
+
`);
|
|
178
|
+
}
|
|
179
|
+
function linked(target) {
|
|
180
|
+
try {
|
|
181
|
+
return lstatSync(target).isSymbolicLink();
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function browserConfig(config, browser) {
|
|
187
|
+
const swapped = replaceMcpServers(config, (server) => isBrowserServer(server.command, server.args) ? browserServerBlock(server.name, browser) : null);
|
|
188
|
+
return swapped.replaced.length > 0 ? swapped.text : null;
|
|
189
|
+
}
|
|
190
|
+
function share(home, source, name) {
|
|
191
|
+
const from = join(source, name);
|
|
192
|
+
const target = join(home, name);
|
|
193
|
+
if (!existsSync(from)) {
|
|
194
|
+
if (linked(target)) rmSync(target);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (linked(target) && readlinkSync(target) === from) return;
|
|
198
|
+
if (linked(target)) rmSync(target);
|
|
199
|
+
if (!existsSync(target)) symlinkSync(from, target);
|
|
200
|
+
}
|
|
201
|
+
function prepareHome(sessionDir, source, browser) {
|
|
202
|
+
const home = join(sessionDir, "codex", "home");
|
|
203
|
+
mkdirSync(home, { recursive: true });
|
|
204
|
+
share(home, source, "auth.json");
|
|
205
|
+
const config = join(source, CONFIG);
|
|
206
|
+
const generated = browser && existsSync(config) ? browserConfig(readFileSync(config, "utf8"), browser) : null;
|
|
207
|
+
rmSync(join(home, CONFIG), { force: true });
|
|
208
|
+
if (generated === null) share(home, source, CONFIG);
|
|
209
|
+
else writeFileSync(join(home, CONFIG), generated, { mode: 384 });
|
|
210
|
+
return home;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/turn.ts
|
|
214
|
+
var STOP_GRACE_MS = 3e3;
|
|
215
|
+
var NOISE = /^(WARNING: proceeding|Reading additional input from stdin)/;
|
|
216
|
+
var line = (value) => process.stdout.write(`${JSON.stringify(value)}
|
|
217
|
+
`);
|
|
218
|
+
function fail(message) {
|
|
219
|
+
line({ type: "error", message });
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
function stop(child) {
|
|
223
|
+
if (child.exitCode !== null || child.signalCode !== null) process.exit(143);
|
|
224
|
+
child.kill("SIGTERM");
|
|
225
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), STOP_GRACE_MS);
|
|
226
|
+
child.once("exit", () => {
|
|
227
|
+
clearTimeout(timer);
|
|
228
|
+
process.exit(143);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
function exitOf(child) {
|
|
232
|
+
return new Promise((resolve) => {
|
|
233
|
+
child.once("error", () => resolve(127));
|
|
234
|
+
child.once("close", (code, signal) => resolve(code ?? (signal ? 128 : 1)));
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function closed(reader) {
|
|
238
|
+
return new Promise((resolve) => reader.once("close", resolve));
|
|
239
|
+
}
|
|
240
|
+
async function main() {
|
|
241
|
+
const file = process.env.ORBITAL_HARNESS_TURN;
|
|
242
|
+
if (!file) fail("ORBITAL_HARNESS_TURN names no turn spec.");
|
|
243
|
+
const spec = JSON.parse(readFileSync2(file, "utf8"));
|
|
244
|
+
const state = readState(spec.sessionDir);
|
|
245
|
+
const resumed = spec.resume && state.threadId !== null;
|
|
246
|
+
const previous = resumed ? state.usage : NO_USAGE;
|
|
247
|
+
const home = prepareHome(spec.sessionDir, sourceHome(process.env), spec.browser);
|
|
248
|
+
const child = spawn("codex", codexArgs(spec, resumed ? state.threadId : null), { cwd: spec.workspaceDir, env: childEnv(spec, process.env, home), stdio: ["ignore", "pipe", "pipe"] });
|
|
249
|
+
process.once("SIGTERM", () => stop(child));
|
|
250
|
+
process.once("SIGINT", () => stop(child));
|
|
251
|
+
const turn = new TurnLines();
|
|
252
|
+
const noise = [];
|
|
253
|
+
const errors = createInterface({ input: child.stderr });
|
|
254
|
+
errors.on("line", (text) => {
|
|
255
|
+
if (text.trim() !== "" && !NOISE.test(text)) noise.push(text);
|
|
256
|
+
});
|
|
257
|
+
const events = createInterface({ input: child.stdout });
|
|
258
|
+
events.on("line", (text) => {
|
|
259
|
+
let event;
|
|
260
|
+
try {
|
|
261
|
+
event = JSON.parse(text);
|
|
262
|
+
} catch {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
for (const entry of turn.handle(event)) line({ type: "message", message: { messageId: randomUUID(), role: entry.role, parts: entry.parts } });
|
|
266
|
+
if (event.type === "thread.started") writeState(spec.sessionDir, { threadId: event.thread_id, usage: previous });
|
|
267
|
+
});
|
|
268
|
+
const [code] = await Promise.all([exitOf(child), closed(events), closed(errors)]);
|
|
269
|
+
const { provider, model } = modelOf(spec.model);
|
|
270
|
+
const usage = turn.usageLine(provider, model ?? DEFAULT_MODEL, previous);
|
|
271
|
+
if (usage) line(usage);
|
|
272
|
+
const total = turn.totalUsage();
|
|
273
|
+
if (turn.threadId && total) writeState(spec.sessionDir, { threadId: turn.threadId, usage: total });
|
|
274
|
+
if (turn.failure) fail(turn.failure);
|
|
275
|
+
if (code !== 0) fail(`codex exited with code ${code}${noise.length > 0 ? `: ${noise.slice(-5).join(" ")}` : ""}.`);
|
|
276
|
+
if (!turn.completed || !turn.answer) fail("Codex answered nothing.");
|
|
277
|
+
line({ type: "result", text: turn.answer });
|
|
278
|
+
process.exit(0);
|
|
279
|
+
}
|
|
280
|
+
main().catch((error) => fail(error instanceof Error ? error.message : String(error)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaiba-cloud/orbital-harness-codex",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"description": "The adapter of the harness codex for orbital: its folder .codex, its install, and a turn program on the OpenAI Codex CLI",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.18"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "node scripts/build.mjs",
|
|
12
|
+
"check-types": "tsc -p tsconfig.json",
|
|
13
|
+
"test": "vitest run"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@kaiba-cloud/orbital-sdk": "workspace:*",
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
18
|
+
"esbuild": "^0.25.0",
|
|
19
|
+
"typescript": "~5.9.3",
|
|
20
|
+
"zod": "^4.6.7"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"orbital-plugin.json",
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
}
|
|
29
|
+
}
|