u1s1-cli 0.3.0 → 0.4.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/README.md +1 -0
- package/dist/brand.js +56 -20
- package/dist/import/claude.js +372 -0
- package/dist/import/codex.js +362 -0
- package/dist/import/index.js +354 -0
- package/dist/import/types.js +1 -0
- package/dist/import/util.js +231 -0
- package/dist/import/write.js +95 -0
- package/dist/index.js +14 -0
- package/dist/login.js +6 -3
- package/dist/style.js +6 -284
- package/dist/themes.js +6 -4
- package/package.json +5 -4
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { createReadStream, existsSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
|
+
import { createInterface } from "node:readline";
|
|
5
|
+
import { asRecord, asString, fileMtimeMs, firstMeaningfulLine, isProbablyInjection, MAX_TEXT_CHARS, MAX_TOOL_RESULT_CHARS, oneLine, parseArgsJson, parseJsonLine, parseTime, readFirstJsonObject, sessionBelongsToCwd, truncateText, uniqueExistingDirs, } from "./util.js";
|
|
6
|
+
const CODEX_TOOL_MAP = {
|
|
7
|
+
exec_command: "bash",
|
|
8
|
+
shell: "bash",
|
|
9
|
+
bash: "bash",
|
|
10
|
+
local_shell: "bash",
|
|
11
|
+
apply_patch: "edit",
|
|
12
|
+
read_file: "read",
|
|
13
|
+
write_file: "write",
|
|
14
|
+
grep_files: "grep",
|
|
15
|
+
grep: "grep",
|
|
16
|
+
};
|
|
17
|
+
function codexHomes() {
|
|
18
|
+
return uniqueExistingDirs([process.env["CODEX_HOME"], join(homedir(), ".codex")]);
|
|
19
|
+
}
|
|
20
|
+
function walkJsonl(root) {
|
|
21
|
+
const out = [];
|
|
22
|
+
const stack = [root];
|
|
23
|
+
while (stack.length) {
|
|
24
|
+
const dir = stack.pop();
|
|
25
|
+
if (!dir)
|
|
26
|
+
continue;
|
|
27
|
+
let entries = [];
|
|
28
|
+
try {
|
|
29
|
+
entries = readdirSync(dir);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
for (const name of entries) {
|
|
35
|
+
const path = join(dir, name);
|
|
36
|
+
let st;
|
|
37
|
+
try {
|
|
38
|
+
st = statSync(path);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (st.isDirectory())
|
|
44
|
+
stack.push(path);
|
|
45
|
+
else if (name.endsWith(".jsonl"))
|
|
46
|
+
out.push(path);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
function sessionIdFromMeta(meta, file) {
|
|
52
|
+
const payload = asRecord(meta["payload"]) ?? meta;
|
|
53
|
+
return (asString(payload["session_id"]) ??
|
|
54
|
+
asString(payload["id"]) ??
|
|
55
|
+
basename(file, ".jsonl").replace(/^rollout-/, ""));
|
|
56
|
+
}
|
|
57
|
+
function cwdFromMeta(meta) {
|
|
58
|
+
const payload = asRecord(meta["payload"]) ?? meta;
|
|
59
|
+
return asString(payload["cwd"]);
|
|
60
|
+
}
|
|
61
|
+
function isSubagent(meta) {
|
|
62
|
+
const payload = asRecord(meta["payload"]) ?? meta;
|
|
63
|
+
const source = payload["source"];
|
|
64
|
+
if (source && typeof source === "object" && !Array.isArray(source) && "subagent" in source)
|
|
65
|
+
return true;
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
function flattenCodexContent(content) {
|
|
69
|
+
if (typeof content === "string")
|
|
70
|
+
return content;
|
|
71
|
+
if (!Array.isArray(content))
|
|
72
|
+
return "";
|
|
73
|
+
const parts = [];
|
|
74
|
+
for (const raw of content) {
|
|
75
|
+
if (typeof raw === "string") {
|
|
76
|
+
parts.push(raw);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const block = asRecord(raw);
|
|
80
|
+
if (!block)
|
|
81
|
+
continue;
|
|
82
|
+
const text = asString(block["text"]) ?? asString(block["input_text"]) ?? asString(block["output_text"]);
|
|
83
|
+
if (text)
|
|
84
|
+
parts.push(text);
|
|
85
|
+
}
|
|
86
|
+
return parts.join("\n");
|
|
87
|
+
}
|
|
88
|
+
function mapToolName(name) {
|
|
89
|
+
return CODEX_TOOL_MAP[name] ?? name;
|
|
90
|
+
}
|
|
91
|
+
function argsFromCall(payload) {
|
|
92
|
+
const rawArgs = payload["arguments"];
|
|
93
|
+
if (typeof rawArgs === "string")
|
|
94
|
+
return parseArgsJson(rawArgs);
|
|
95
|
+
const rec = asRecord(rawArgs);
|
|
96
|
+
if (rec)
|
|
97
|
+
return rec;
|
|
98
|
+
const input = payload["input"];
|
|
99
|
+
if (typeof input === "string")
|
|
100
|
+
return { raw: input };
|
|
101
|
+
const recInput = asRecord(input);
|
|
102
|
+
return recInput ?? {};
|
|
103
|
+
}
|
|
104
|
+
function remapToolArgs(name, args) {
|
|
105
|
+
const mapped = mapToolName(name);
|
|
106
|
+
if (mapped === "bash") {
|
|
107
|
+
const command = asString(args["cmd"]) ?? asString(args["command"]) ?? asString(args["script"]) ?? asString(args["raw"]);
|
|
108
|
+
return command ? { command } : args;
|
|
109
|
+
}
|
|
110
|
+
if (mapped === "read") {
|
|
111
|
+
const path = asString(args["path"]) ?? asString(args["file_path"]) ?? asString(args["file"]);
|
|
112
|
+
return path ? { path } : args;
|
|
113
|
+
}
|
|
114
|
+
if (mapped === "write") {
|
|
115
|
+
const path = asString(args["path"]) ?? asString(args["file_path"]);
|
|
116
|
+
const content = asString(args["content"]) ?? asString(args["contents"]);
|
|
117
|
+
return {
|
|
118
|
+
...(path ? { path } : {}),
|
|
119
|
+
...(content !== undefined ? { content } : {}),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (mapped === "edit" && name === "apply_patch") {
|
|
123
|
+
const patch = asString(args["raw"]) ?? asString(args["input"]) ?? asString(args["patch"]);
|
|
124
|
+
return patch ? { patch } : args;
|
|
125
|
+
}
|
|
126
|
+
return args;
|
|
127
|
+
}
|
|
128
|
+
function emptyUsage() {
|
|
129
|
+
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
130
|
+
}
|
|
131
|
+
export async function hydrateCodexSession(session) {
|
|
132
|
+
if (session.title && session.cwd)
|
|
133
|
+
return session;
|
|
134
|
+
const stream = createReadStream(session.sourcePath, { encoding: "utf8" });
|
|
135
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
136
|
+
let cwd = session.cwd;
|
|
137
|
+
let title = session.title;
|
|
138
|
+
let lines = 0;
|
|
139
|
+
try {
|
|
140
|
+
for await (const line of rl) {
|
|
141
|
+
lines += 1;
|
|
142
|
+
const obj = parseJsonLine(line);
|
|
143
|
+
if (!obj)
|
|
144
|
+
continue;
|
|
145
|
+
const type = asString(obj["type"]);
|
|
146
|
+
const payload = asRecord(obj["payload"]) ?? {};
|
|
147
|
+
if (!cwd && type === "session_meta")
|
|
148
|
+
cwd = asString(payload["cwd"]) ?? cwd;
|
|
149
|
+
if (!title && type === "event_msg" && asString(payload["type"]) === "user_message") {
|
|
150
|
+
const text = (asString(payload["message"]) ?? "").trim();
|
|
151
|
+
if (text && !isProbablyInjection(text))
|
|
152
|
+
title = oneLine(firstMeaningfulLine(text) || text);
|
|
153
|
+
}
|
|
154
|
+
if (cwd && title)
|
|
155
|
+
break;
|
|
156
|
+
if (lines > 80)
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
rl.close();
|
|
162
|
+
stream.destroy();
|
|
163
|
+
}
|
|
164
|
+
return { ...session, cwd: cwd || session.cwd, title: title || session.title };
|
|
165
|
+
}
|
|
166
|
+
function flushAssistant(messages, pending, model, ts) {
|
|
167
|
+
if (pending.length === 0)
|
|
168
|
+
return;
|
|
169
|
+
const sawTool = pending.some((b) => b.type === "toolCall");
|
|
170
|
+
messages.push({
|
|
171
|
+
role: "assistant",
|
|
172
|
+
content: pending.splice(0),
|
|
173
|
+
provider: "openai",
|
|
174
|
+
model,
|
|
175
|
+
api: "openai-codex-responses",
|
|
176
|
+
stopReason: sawTool ? "toolUse" : "stop",
|
|
177
|
+
usage: emptyUsage(),
|
|
178
|
+
timestamp: ts,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
export const codexAdapter = {
|
|
182
|
+
id: "codex",
|
|
183
|
+
label: "Codex",
|
|
184
|
+
discover(opts) {
|
|
185
|
+
const wanted = opts.cwd;
|
|
186
|
+
const seen = new Set();
|
|
187
|
+
const found = [];
|
|
188
|
+
for (const home of codexHomes()) {
|
|
189
|
+
const sessionsRoot = join(home, "sessions");
|
|
190
|
+
if (!existsSync(sessionsRoot))
|
|
191
|
+
continue;
|
|
192
|
+
for (const file of walkJsonl(sessionsRoot)) {
|
|
193
|
+
let real = file;
|
|
194
|
+
try {
|
|
195
|
+
real = realpathSync(file);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// keep
|
|
199
|
+
}
|
|
200
|
+
if (seen.has(real))
|
|
201
|
+
continue;
|
|
202
|
+
seen.add(real);
|
|
203
|
+
const first = readFirstJsonObject(real);
|
|
204
|
+
if (!first)
|
|
205
|
+
continue;
|
|
206
|
+
if (isSubagent(first))
|
|
207
|
+
continue;
|
|
208
|
+
const cwd = cwdFromMeta(first) ?? "";
|
|
209
|
+
if (wanted && (!cwd || !sessionBelongsToCwd(cwd, wanted)))
|
|
210
|
+
continue;
|
|
211
|
+
found.push({
|
|
212
|
+
source: "codex",
|
|
213
|
+
sourceId: sessionIdFromMeta(first, real),
|
|
214
|
+
sourcePath: real,
|
|
215
|
+
cwd,
|
|
216
|
+
startedAt: parseTime(asRecord(first["payload"])?.["timestamp"] ?? first["timestamp"]),
|
|
217
|
+
mtimeMs: fileMtimeMs(real),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return found;
|
|
222
|
+
},
|
|
223
|
+
convert(session) {
|
|
224
|
+
let text = "";
|
|
225
|
+
try {
|
|
226
|
+
text = readFileSync(session.sourcePath, "utf8");
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return { cwd: session.cwd || process.cwd(), title: session.title, messages: [] };
|
|
230
|
+
}
|
|
231
|
+
// A few Codex rollouts are huge (100MB+). Keep the latest ~8MB of text so import stays usable.
|
|
232
|
+
const maxChars = 8_000_000;
|
|
233
|
+
if (text.length > maxChars) {
|
|
234
|
+
const cut = text.length - maxChars;
|
|
235
|
+
const nl = text.indexOf("\n", cut);
|
|
236
|
+
text = text.slice(nl === -1 ? cut : nl + 1);
|
|
237
|
+
}
|
|
238
|
+
const messages = [];
|
|
239
|
+
let cwd = session.cwd;
|
|
240
|
+
let title = session.title;
|
|
241
|
+
let model = "codex";
|
|
242
|
+
const pending = [];
|
|
243
|
+
const pendingTools = new Map();
|
|
244
|
+
let lastTs = session.startedAt ?? Date.now();
|
|
245
|
+
let lastUserText = "";
|
|
246
|
+
for (const line of text.split(/\r?\n/)) {
|
|
247
|
+
const obj = parseJsonLine(line);
|
|
248
|
+
if (!obj)
|
|
249
|
+
continue;
|
|
250
|
+
const type = asString(obj["type"]);
|
|
251
|
+
const payload = asRecord(obj["payload"]) ?? {};
|
|
252
|
+
const ts = parseTime(obj["timestamp"]) ?? lastTs;
|
|
253
|
+
lastTs = ts;
|
|
254
|
+
if (type === "session_meta") {
|
|
255
|
+
cwd = asString(payload["cwd"]) ?? cwd;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (type === "turn_context") {
|
|
259
|
+
model = asString(payload["model"]) ?? model;
|
|
260
|
+
cwd = asString(payload["cwd"]) ?? cwd;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (type === "event_msg") {
|
|
264
|
+
const et = asString(payload["type"]);
|
|
265
|
+
if (et === "user_message") {
|
|
266
|
+
flushAssistant(messages, pending, model, ts);
|
|
267
|
+
const userText = truncateText(asString(payload["message"]) ?? "", MAX_TEXT_CHARS).trim();
|
|
268
|
+
if (!userText || isProbablyInjection(userText))
|
|
269
|
+
continue;
|
|
270
|
+
if (userText === lastUserText)
|
|
271
|
+
continue;
|
|
272
|
+
lastUserText = userText;
|
|
273
|
+
if (!title)
|
|
274
|
+
title = oneLine(firstMeaningfulLine(userText) || userText);
|
|
275
|
+
messages.push({ role: "user", text: userText, timestamp: ts });
|
|
276
|
+
}
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (type !== "response_item")
|
|
280
|
+
continue;
|
|
281
|
+
const pt = asString(payload["type"]);
|
|
282
|
+
const role = asString(payload["role"]);
|
|
283
|
+
if (pt === "message" && role === "user") {
|
|
284
|
+
flushAssistant(messages, pending, model, ts);
|
|
285
|
+
const userText = flattenCodexContent(payload["content"]).trim();
|
|
286
|
+
if (!userText || isProbablyInjection(userText) || userText === lastUserText)
|
|
287
|
+
continue;
|
|
288
|
+
lastUserText = userText;
|
|
289
|
+
if (!title)
|
|
290
|
+
title = oneLine(firstMeaningfulLine(userText) || userText);
|
|
291
|
+
messages.push({ role: "user", text: userText, timestamp: ts });
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (pt === "message" && role === "assistant") {
|
|
295
|
+
const textOut = flattenCodexContent(payload["content"]).trim();
|
|
296
|
+
if (textOut)
|
|
297
|
+
pending.push({ type: "text", text: truncateText(textOut, MAX_TEXT_CHARS) });
|
|
298
|
+
flushAssistant(messages, pending, model, ts);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (pt === "reasoning") {
|
|
302
|
+
const summary = payload["summary"];
|
|
303
|
+
const bits = [];
|
|
304
|
+
if (Array.isArray(summary)) {
|
|
305
|
+
for (const item of summary) {
|
|
306
|
+
if (typeof item === "string")
|
|
307
|
+
bits.push(item);
|
|
308
|
+
else {
|
|
309
|
+
const rec = asRecord(item);
|
|
310
|
+
const t = rec ? asString(rec["text"]) : undefined;
|
|
311
|
+
if (t)
|
|
312
|
+
bits.push(t);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const thinking = bits.join("\n").trim();
|
|
317
|
+
if (thinking)
|
|
318
|
+
pending.push({ type: "thinking", thinking: truncateText(thinking, MAX_TEXT_CHARS) });
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (pt === "function_call" || pt === "custom_tool_call") {
|
|
322
|
+
const id = asString(payload["call_id"]) ?? asString(payload["id"]) ?? "";
|
|
323
|
+
const name = asString(payload["name"]) ?? "unknown";
|
|
324
|
+
if (!id)
|
|
325
|
+
continue;
|
|
326
|
+
const mapped = mapToolName(name);
|
|
327
|
+
pendingTools.set(id, mapped);
|
|
328
|
+
pending.push({
|
|
329
|
+
type: "toolCall",
|
|
330
|
+
id,
|
|
331
|
+
name: mapped,
|
|
332
|
+
arguments: remapToolArgs(name, argsFromCall(payload)),
|
|
333
|
+
});
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
if (pt === "function_call_output" || pt === "custom_tool_call_output") {
|
|
337
|
+
flushAssistant(messages, pending, model, ts);
|
|
338
|
+
const id = asString(payload["call_id"]) ?? asString(payload["id"]) ?? "";
|
|
339
|
+
if (!id)
|
|
340
|
+
continue;
|
|
341
|
+
const output = payload["output"];
|
|
342
|
+
let textOut = "";
|
|
343
|
+
if (typeof output === "string")
|
|
344
|
+
textOut = output;
|
|
345
|
+
else {
|
|
346
|
+
const rec = asRecord(output);
|
|
347
|
+
textOut = rec ? (asString(rec["output"]) ?? JSON.stringify(output)) : String(output ?? "");
|
|
348
|
+
}
|
|
349
|
+
messages.push({
|
|
350
|
+
role: "toolResult",
|
|
351
|
+
toolCallId: id,
|
|
352
|
+
toolName: pendingTools.get(id) ?? "unknown",
|
|
353
|
+
text: truncateText(textOut, MAX_TOOL_RESULT_CHARS),
|
|
354
|
+
isError: false,
|
|
355
|
+
timestamp: ts,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
flushAssistant(messages, pending, model, lastTs);
|
|
360
|
+
return { cwd: cwd || session.cwd || process.cwd(), title, messages };
|
|
361
|
+
},
|
|
362
|
+
};
|