@zhang6hk/dsh-codex 0.0.0-stage → 0.1.0
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/LICENSE +201 -0
- package/NOTICE +18 -0
- package/README.md +75 -2
- package/README.zh.md +78 -0
- package/cordis.patch.yml +7 -0
- package/lib/args.js +118 -0
- package/lib/codex-path.js +31 -0
- package/lib/commands.js +242 -0
- package/lib/companion.js +177 -0
- package/lib/index.js +105 -0
- package/lib/render.js +114 -0
- package/lib/tool.js +103 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +49 -4
- package/vendor/codex/.claude-plugin/plugin.json +8 -0
- package/vendor/codex/prompts/adversarial-review.md +84 -0
- package/vendor/codex/prompts/stop-review-gate.md +36 -0
- package/vendor/codex/schemas/review-output.schema.json +87 -0
- package/vendor/codex/scripts/app-server-broker.mjs +252 -0
- package/vendor/codex/scripts/codex-companion.mjs +1073 -0
- package/vendor/codex/scripts/lib/app-server-protocol.d.ts +75 -0
- package/vendor/codex/scripts/lib/app-server.mjs +354 -0
- package/vendor/codex/scripts/lib/args.mjs +128 -0
- package/vendor/codex/scripts/lib/broker-endpoint.mjs +41 -0
- package/vendor/codex/scripts/lib/broker-lifecycle.mjs +209 -0
- package/vendor/codex/scripts/lib/claude-session-transfer.mjs +44 -0
- package/vendor/codex/scripts/lib/codex.mjs +1219 -0
- package/vendor/codex/scripts/lib/fs.mjs +40 -0
- package/vendor/codex/scripts/lib/git.mjs +347 -0
- package/vendor/codex/scripts/lib/job-control.mjs +308 -0
- package/vendor/codex/scripts/lib/process.mjs +135 -0
- package/vendor/codex/scripts/lib/prompts.mjs +13 -0
- package/vendor/codex/scripts/lib/render.mjs +465 -0
- package/vendor/codex/scripts/lib/state.mjs +191 -0
- package/vendor/codex/scripts/lib/tracked-jobs.mjs +204 -0
- package/vendor/codex/scripts/lib/workspace.mjs +9 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import net from "node:net";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { createBrokerEndpoint, parseBrokerEndpoint } from "./broker-endpoint.mjs";
|
|
9
|
+
import { resolveStateDir } from "./state.mjs";
|
|
10
|
+
|
|
11
|
+
export const PID_FILE_ENV = "CODEX_COMPANION_APP_SERVER_PID_FILE";
|
|
12
|
+
export const LOG_FILE_ENV = "CODEX_COMPANION_APP_SERVER_LOG_FILE";
|
|
13
|
+
const BROKER_STATE_FILE = "broker.json";
|
|
14
|
+
|
|
15
|
+
export function createBrokerSessionDir(prefix = "cxc-") {
|
|
16
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function connectToEndpoint(endpoint) {
|
|
20
|
+
const target = parseBrokerEndpoint(endpoint);
|
|
21
|
+
return net.createConnection({ path: target.path });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function waitForBrokerEndpoint(endpoint, timeoutMs = 2000) {
|
|
25
|
+
const start = Date.now();
|
|
26
|
+
while (Date.now() - start < timeoutMs) {
|
|
27
|
+
const ready = await new Promise((resolve) => {
|
|
28
|
+
const socket = connectToEndpoint(endpoint);
|
|
29
|
+
socket.on("connect", () => {
|
|
30
|
+
socket.end();
|
|
31
|
+
resolve(true);
|
|
32
|
+
});
|
|
33
|
+
socket.on("error", () => resolve(false));
|
|
34
|
+
});
|
|
35
|
+
if (ready) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function sendBrokerShutdown(endpoint) {
|
|
44
|
+
await new Promise((resolve) => {
|
|
45
|
+
const socket = connectToEndpoint(endpoint);
|
|
46
|
+
socket.setEncoding("utf8");
|
|
47
|
+
socket.on("connect", () => {
|
|
48
|
+
socket.write(`${JSON.stringify({ id: 1, method: "broker/shutdown", params: {} })}\n`);
|
|
49
|
+
});
|
|
50
|
+
socket.on("data", () => {
|
|
51
|
+
socket.end();
|
|
52
|
+
resolve();
|
|
53
|
+
});
|
|
54
|
+
socket.on("error", resolve);
|
|
55
|
+
socket.on("close", resolve);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function spawnBrokerProcess({ scriptPath, cwd, endpoint, pidFile, logFile, env = process.env }) {
|
|
60
|
+
const logFd = fs.openSync(logFile, "a");
|
|
61
|
+
const child = spawn(process.execPath, [scriptPath, "serve", "--endpoint", endpoint, "--cwd", cwd, "--pid-file", pidFile], {
|
|
62
|
+
cwd,
|
|
63
|
+
env,
|
|
64
|
+
detached: true,
|
|
65
|
+
stdio: ["ignore", logFd, logFd]
|
|
66
|
+
});
|
|
67
|
+
child.unref();
|
|
68
|
+
fs.closeSync(logFd);
|
|
69
|
+
return child;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function resolveBrokerStateFile(cwd) {
|
|
73
|
+
return path.join(resolveStateDir(cwd), BROKER_STATE_FILE);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function loadBrokerSession(cwd) {
|
|
77
|
+
const stateFile = resolveBrokerStateFile(cwd);
|
|
78
|
+
if (!fs.existsSync(stateFile)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
return JSON.parse(fs.readFileSync(stateFile, "utf8"));
|
|
84
|
+
} catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function saveBrokerSession(cwd, session) {
|
|
90
|
+
const stateDir = resolveStateDir(cwd);
|
|
91
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
92
|
+
fs.writeFileSync(resolveBrokerStateFile(cwd), `${JSON.stringify(session, null, 2)}\n`, "utf8");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function clearBrokerSession(cwd) {
|
|
96
|
+
const stateFile = resolveBrokerStateFile(cwd);
|
|
97
|
+
if (fs.existsSync(stateFile)) {
|
|
98
|
+
fs.unlinkSync(stateFile);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function isBrokerEndpointReady(endpoint) {
|
|
103
|
+
if (!endpoint) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
return await waitForBrokerEndpoint(endpoint, 150);
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export async function ensureBrokerSession(cwd, options = {}) {
|
|
114
|
+
const existing = loadBrokerSession(cwd);
|
|
115
|
+
if (existing && (await isBrokerEndpointReady(existing.endpoint))) {
|
|
116
|
+
return existing;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (existing) {
|
|
120
|
+
teardownBrokerSession({
|
|
121
|
+
endpoint: existing.endpoint ?? null,
|
|
122
|
+
pidFile: existing.pidFile ?? null,
|
|
123
|
+
logFile: existing.logFile ?? null,
|
|
124
|
+
sessionDir: existing.sessionDir ?? null,
|
|
125
|
+
pid: existing.pid ?? null,
|
|
126
|
+
killProcess: options.killProcess ?? null
|
|
127
|
+
});
|
|
128
|
+
clearBrokerSession(cwd);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const sessionDir = createBrokerSessionDir();
|
|
132
|
+
const endpointFactory = options.createBrokerEndpoint ?? createBrokerEndpoint;
|
|
133
|
+
const endpoint = endpointFactory(sessionDir, options.platform);
|
|
134
|
+
const pidFile = path.join(sessionDir, "broker.pid");
|
|
135
|
+
const logFile = path.join(sessionDir, "broker.log");
|
|
136
|
+
const scriptPath =
|
|
137
|
+
options.scriptPath ??
|
|
138
|
+
fileURLToPath(new URL("../app-server-broker.mjs", import.meta.url));
|
|
139
|
+
|
|
140
|
+
const child = spawnBrokerProcess({
|
|
141
|
+
scriptPath,
|
|
142
|
+
cwd,
|
|
143
|
+
endpoint,
|
|
144
|
+
pidFile,
|
|
145
|
+
logFile,
|
|
146
|
+
env: options.env ?? process.env
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const ready = await waitForBrokerEndpoint(endpoint, options.timeoutMs ?? 2000);
|
|
150
|
+
if (!ready) {
|
|
151
|
+
teardownBrokerSession({
|
|
152
|
+
endpoint,
|
|
153
|
+
pidFile,
|
|
154
|
+
logFile,
|
|
155
|
+
sessionDir,
|
|
156
|
+
pid: child.pid ?? null,
|
|
157
|
+
killProcess: options.killProcess ?? null
|
|
158
|
+
});
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const session = {
|
|
163
|
+
endpoint,
|
|
164
|
+
pidFile,
|
|
165
|
+
logFile,
|
|
166
|
+
sessionDir,
|
|
167
|
+
pid: child.pid ?? null
|
|
168
|
+
};
|
|
169
|
+
saveBrokerSession(cwd, session);
|
|
170
|
+
return session;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function teardownBrokerSession({ endpoint = null, pidFile, logFile, sessionDir = null, pid = null, killProcess = null }) {
|
|
174
|
+
if (Number.isFinite(pid) && killProcess) {
|
|
175
|
+
try {
|
|
176
|
+
killProcess(pid);
|
|
177
|
+
} catch {
|
|
178
|
+
// Ignore missing or already-exited broker processes.
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (pidFile && fs.existsSync(pidFile)) {
|
|
183
|
+
fs.unlinkSync(pidFile);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (logFile && fs.existsSync(logFile)) {
|
|
187
|
+
fs.unlinkSync(logFile);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (endpoint) {
|
|
191
|
+
try {
|
|
192
|
+
const target = parseBrokerEndpoint(endpoint);
|
|
193
|
+
if (target.kind === "unix" && fs.existsSync(target.path)) {
|
|
194
|
+
fs.unlinkSync(target.path);
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
// Ignore malformed or already-removed broker endpoints during teardown.
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const resolvedSessionDir = sessionDir ?? (pidFile ? path.dirname(pidFile) : logFile ? path.dirname(logFile) : null);
|
|
202
|
+
if (resolvedSessionDir && fs.existsSync(resolvedSessionDir)) {
|
|
203
|
+
try {
|
|
204
|
+
fs.rmdirSync(resolvedSessionDir);
|
|
205
|
+
} catch {
|
|
206
|
+
// Ignore non-empty or missing directories.
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { ensureAbsolutePath } from "./fs.mjs";
|
|
6
|
+
|
|
7
|
+
export const TRANSCRIPT_PATH_ENV = "CODEX_COMPANION_TRANSCRIPT_PATH";
|
|
8
|
+
const CLAUDE_PROJECTS_DIR = path.join(os.homedir(), ".claude", "projects");
|
|
9
|
+
|
|
10
|
+
function resolveUserPath(cwd, value) {
|
|
11
|
+
if (value === "~") {
|
|
12
|
+
return os.homedir();
|
|
13
|
+
}
|
|
14
|
+
if (String(value).startsWith("~/")) {
|
|
15
|
+
return path.join(os.homedir(), String(value).slice(2));
|
|
16
|
+
}
|
|
17
|
+
return ensureAbsolutePath(cwd, value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function resolveClaudeSessionPath(cwd, options = {}) {
|
|
21
|
+
const requestedPath = options.source || process.env[TRANSCRIPT_PATH_ENV];
|
|
22
|
+
if (!requestedPath) {
|
|
23
|
+
throw new Error("Could not identify the current Claude transcript. Retry with --source <path-to-claude-jsonl>.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const sourcePath = resolveUserPath(cwd, requestedPath);
|
|
27
|
+
if (path.extname(sourcePath) !== ".jsonl") {
|
|
28
|
+
throw new Error(`Claude session source must be a JSONL file: ${sourcePath}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let source;
|
|
32
|
+
let projects;
|
|
33
|
+
try {
|
|
34
|
+
source = fs.realpathSync(sourcePath);
|
|
35
|
+
projects = fs.realpathSync(CLAUDE_PROJECTS_DIR);
|
|
36
|
+
} catch {
|
|
37
|
+
throw new Error(`Claude session file not found: ${sourcePath}`);
|
|
38
|
+
}
|
|
39
|
+
const relative = path.relative(projects, source);
|
|
40
|
+
if (relative === "" || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
41
|
+
throw new Error(`Codex can import Claude sessions only from ${CLAUDE_PROJECTS_DIR}: ${source}`);
|
|
42
|
+
}
|
|
43
|
+
return source;
|
|
44
|
+
}
|