u1s1-cli 1.2.3 → 1.2.4
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/dist/agent-setup.js +2 -2
- package/dist/api.d.ts +5 -1
- package/dist/api.js +6 -6
- package/dist/bench-report.d.ts +22 -0
- package/dist/bench-report.js +158 -0
- package/dist/bench-scoring.d.ts +8 -0
- package/dist/bench-scoring.js +101 -0
- package/dist/bench-types.d.ts +50 -0
- package/dist/bench-types.js +1 -0
- package/dist/bench.js +101 -367
- package/dist/brand.d.ts +9 -3
- package/dist/brand.js +12 -12
- package/dist/deploy.js +72 -49
- package/dist/import/claude.d.ts +2 -1
- package/dist/import/claude.js +162 -129
- package/dist/import/codex.d.ts +2 -1
- package/dist/import/codex.js +184 -157
- package/dist/import/index.js +62 -47
- package/dist/index.js +13 -13
- package/dist/style.js +7 -1
- package/dist/subagent.d.ts +6 -1
- package/dist/subagent.js +4 -4
- package/dist/tools.d.ts +18 -12
- package/dist/tools.js +22 -16
- package/dist/usage.js +95 -74
- package/dist/workflow/runner.js +36 -26
- package/dist/workflow/tool.js +67 -55
- package/package.json +1 -1
- package/scripts/render-regression.mjs +3 -1
package/dist/deploy.js
CHANGED
|
@@ -123,13 +123,13 @@ export function parseDeployArgs(args) {
|
|
|
123
123
|
}
|
|
124
124
|
return parsed;
|
|
125
125
|
}
|
|
126
|
-
async function api(cfg,
|
|
126
|
+
async function api(cfg, request) {
|
|
127
127
|
let resp;
|
|
128
128
|
try {
|
|
129
|
-
resp = await authorizedFetch(cfg, `${cfg.baseUrl}${path}`, {
|
|
130
|
-
method,
|
|
129
|
+
resp = await authorizedFetch(cfg, `${cfg.baseUrl}${request.path}`, {
|
|
130
|
+
method: request.method,
|
|
131
131
|
headers: { "x-u1s1-version": VERSION, "content-type": "application/json" },
|
|
132
|
-
body: body === undefined ? undefined : JSON.stringify(body),
|
|
132
|
+
body: request.body === undefined ? undefined : JSON.stringify(request.body),
|
|
133
133
|
signal: AbortSignal.timeout(30_000),
|
|
134
134
|
});
|
|
135
135
|
}
|
|
@@ -208,35 +208,30 @@ async function promptVisibility() {
|
|
|
208
208
|
rl.close();
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
if (!sites.length) {
|
|
216
|
-
console.log(" 还没有部署过站点。在网页目录里跑 u1s1 deploy 试试。");
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
// 服务端给的是 UTC 时间戳,转成本地时间展示
|
|
220
|
-
const fmtTime = (ts) => {
|
|
221
|
-
const d = new Date(ts.includes("T") ? ts : `${ts.replace(" ", "T")}Z`);
|
|
222
|
-
return Number.isNaN(d.getTime()) ? ts : d.toLocaleString("zh-CN", { hour12: false });
|
|
223
|
-
};
|
|
224
|
-
console.log("");
|
|
225
|
-
for (const s of sites) {
|
|
226
|
-
const visibility = s.visibility === "private" ? "私密" : s.community_listed ? "公开(社区)" : "公开(未展示)";
|
|
227
|
-
console.log(` ${s.deployed ? "●" : "○"} ${s.url} ${visibility} · ${fmtBytes(s.total_bytes)} · ${s.updated_at} UTC`);
|
|
228
|
-
}
|
|
229
|
-
console.log("");
|
|
211
|
+
async function listDeployments(cfg) {
|
|
212
|
+
const { sites } = await api(cfg, { method: "GET", path: "/deploy/sites" });
|
|
213
|
+
if (!sites.length) {
|
|
214
|
+
console.log(" 还没有部署过站点。在网页目录里跑 u1s1 deploy 试试。");
|
|
230
215
|
return;
|
|
231
216
|
}
|
|
217
|
+
console.log("");
|
|
218
|
+
for (const site of sites) {
|
|
219
|
+
const visibility = site.visibility === "private"
|
|
220
|
+
? "私密"
|
|
221
|
+
: site.community_listed ? "公开(社区)" : "公开(未展示)";
|
|
222
|
+
console.log(` ${site.deployed ? "●" : "○"} ${site.url} ${visibility} · ${fmtBytes(site.total_bytes)} · ${site.updated_at} UTC`);
|
|
223
|
+
}
|
|
224
|
+
console.log("");
|
|
225
|
+
}
|
|
226
|
+
async function selectDeployment(args) {
|
|
232
227
|
const parsed = parseDeployArgs(args);
|
|
233
228
|
let { name, dirArg, visibility } = parsed;
|
|
234
229
|
const dir = resolveSiteDir(dirArg);
|
|
235
230
|
const files = collectFiles(dir);
|
|
236
|
-
if (!files.some((
|
|
231
|
+
if (!files.some((file) => file.path === "index.html")) {
|
|
237
232
|
throw new Error(`${dir} 里没有 index.html,网站需要一个首页`);
|
|
238
233
|
}
|
|
239
|
-
const totalBytes = files.reduce((
|
|
234
|
+
const totalBytes = files.reduce((sum, file) => sum + file.bytes, 0);
|
|
240
235
|
console.log("");
|
|
241
236
|
console.log(` 部署目录 ${dir}`);
|
|
242
237
|
console.log(` 文件 ${files.length} 个,共 ${fmtBytes(totalBytes)}`);
|
|
@@ -252,55 +247,61 @@ export async function deployCommand(cfg, args) {
|
|
|
252
247
|
// 取更安全的 private,用户仍可用 --public 明确公开。
|
|
253
248
|
if (!visibility && remembered !== name)
|
|
254
249
|
visibility = await promptVisibility();
|
|
255
|
-
|
|
256
|
-
|
|
250
|
+
return { dir, files, name, totalBytes, visibility };
|
|
251
|
+
}
|
|
252
|
+
async function startDeployment(cfg, initialName) {
|
|
253
|
+
let name = initialName;
|
|
254
|
+
for (let attempt = 0;; attempt++) {
|
|
257
255
|
try {
|
|
258
|
-
start = await api(cfg,
|
|
256
|
+
const start = await api(cfg, {
|
|
257
|
+
method: "POST",
|
|
258
|
+
path: "/deploy/start",
|
|
259
|
+
body: { site: name },
|
|
260
|
+
});
|
|
261
|
+
return { name, start };
|
|
259
262
|
}
|
|
260
|
-
catch (
|
|
261
|
-
const code =
|
|
263
|
+
catch (error) {
|
|
264
|
+
const code = error.code;
|
|
262
265
|
const retriable = code === "site_name_taken" || code === "invalid_site_name";
|
|
263
266
|
if (!retriable || attempt >= 3)
|
|
264
|
-
throw
|
|
265
|
-
console.log(` ${
|
|
267
|
+
throw error;
|
|
268
|
+
console.log(` ${error.message}`);
|
|
269
|
+
const suggested = `${name.slice(0, 25)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
266
270
|
if (!process.stdin.isTTY) {
|
|
267
271
|
// 非交互环境自动加后缀重试一次
|
|
268
272
|
if (attempt > 0)
|
|
269
|
-
throw
|
|
270
|
-
name =
|
|
273
|
+
throw error;
|
|
274
|
+
name = suggested;
|
|
271
275
|
}
|
|
272
276
|
else {
|
|
273
|
-
name = await promptSiteName(
|
|
277
|
+
name = await promptSiteName(suggested);
|
|
274
278
|
}
|
|
275
279
|
}
|
|
276
280
|
}
|
|
277
|
-
|
|
281
|
+
}
|
|
282
|
+
function validateDeploymentLimits(start, files, totalBytes) {
|
|
283
|
+
const tooBig = files.filter((file) => file.bytes > start.limits.max_file_bytes);
|
|
278
284
|
if (tooBig.length) {
|
|
279
285
|
throw new Error(`这些文件超过单文件上限 ${fmtBytes(start.limits.max_file_bytes)}:\n` +
|
|
280
|
-
tooBig.map((
|
|
286
|
+
tooBig.map((file) => ` ${file.path}(${fmtBytes(file.bytes)})`).join("\n"));
|
|
281
287
|
}
|
|
282
288
|
if (files.length > start.limits.max_files || totalBytes > start.limits.max_total_bytes) {
|
|
283
289
|
throw new Error(`超出配额:最多 ${start.limits.max_files} 个文件 / ${fmtBytes(start.limits.max_total_bytes)}。` +
|
|
284
290
|
`当前 ${files.length} 个 / ${fmtBytes(totalBytes)}`);
|
|
285
291
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
deploy_id: start.deploy_id,
|
|
290
|
-
visibility,
|
|
291
|
-
});
|
|
292
|
-
rememberSite(dir, start.slug || name || start.site);
|
|
293
|
-
console.log(` ✅ 部署完成,${fin.file_count} 个文件已上线`);
|
|
292
|
+
}
|
|
293
|
+
function printDeploymentResult(result, start) {
|
|
294
|
+
console.log(` ✅ 部署完成,${result.file_count} 个文件已上线`);
|
|
294
295
|
console.log("");
|
|
295
|
-
console.log(` ${
|
|
296
|
+
console.log(` ${result.visibility === "private" ? "🔒" : "🌐"} ${result.url}`);
|
|
296
297
|
console.log("");
|
|
297
|
-
if ((
|
|
298
|
+
if ((result.hostname || start.hostname || "").split(".").length > 3) {
|
|
298
299
|
console.log(" 新 hostname 的 HTTPS 证书会由 Total TLS 自动签发,首次访问可能需要等待几分钟。");
|
|
299
300
|
}
|
|
300
|
-
if (
|
|
301
|
+
if (result.visibility === "private") {
|
|
301
302
|
console.log(" 私密站点仅你可见,请从 https://u1s1.io/dashboard#sec-sites 打开。");
|
|
302
303
|
}
|
|
303
|
-
else if (
|
|
304
|
+
else if (result.community_listed) {
|
|
304
305
|
console.log(" 已进入 https://u1s1.io/community ,把网址发给朋友就能看。");
|
|
305
306
|
}
|
|
306
307
|
else {
|
|
@@ -309,3 +310,25 @@ export async function deployCommand(cfg, args) {
|
|
|
309
310
|
console.log(" 改完代码再跑一次 u1s1 deploy 即可更新。");
|
|
310
311
|
console.log("");
|
|
311
312
|
}
|
|
313
|
+
export async function deployCommand(cfg, args) {
|
|
314
|
+
// 参数:[dir] [--name xxx] [--public|--private];u1s1 deploy list 列出已有站点
|
|
315
|
+
if (args[0] === "list") {
|
|
316
|
+
await listDeployments(cfg);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const selection = await selectDeployment(args);
|
|
320
|
+
const { name, start } = await startDeployment(cfg, selection.name);
|
|
321
|
+
validateDeploymentLimits(start, selection.files, selection.totalBytes);
|
|
322
|
+
await uploadAll(cfg, start, selection.files);
|
|
323
|
+
const result = await api(cfg, {
|
|
324
|
+
method: "POST",
|
|
325
|
+
path: "/deploy/finish",
|
|
326
|
+
body: {
|
|
327
|
+
site: start.site,
|
|
328
|
+
deploy_id: start.deploy_id,
|
|
329
|
+
visibility: selection.visibility,
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
rememberSite(selection.dir, start.slug || name || start.site);
|
|
333
|
+
printDeploymentResult(result, start);
|
|
334
|
+
}
|
package/dist/import/claude.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type { SourceAdapter, SourceSession } from "./types.js";
|
|
1
|
+
import type { ConvertedSession, SourceAdapter, SourceSession } from "./types.js";
|
|
2
2
|
export declare function hydrateClaudeSession(session: SourceSession): Promise<SourceSession>;
|
|
3
|
+
export declare function convertClaudeSession(session: SourceSession): ConvertedSession;
|
|
3
4
|
export declare const claudeAdapter: SourceAdapter;
|
package/dist/import/claude.js
CHANGED
|
@@ -12,6 +12,7 @@ const CLAUDE_TOOL_MAP = {
|
|
|
12
12
|
grep: "grep",
|
|
13
13
|
ls: "ls",
|
|
14
14
|
};
|
|
15
|
+
const MAX_CLAUDE_LINE_CHARS = 8_000_000;
|
|
15
16
|
function claudeHomes() {
|
|
16
17
|
return uniqueExistingDirs([
|
|
17
18
|
process.env["CLAUDE_CONFIG_DIR"],
|
|
@@ -152,14 +153,25 @@ function firstUserPreview(obj) {
|
|
|
152
153
|
return undefined;
|
|
153
154
|
return oneLine(firstMeaningfulLine(text) || text);
|
|
154
155
|
}
|
|
156
|
+
function updateClaudeHydration(state, obj) {
|
|
157
|
+
state.sourceId ||= asString(obj["sessionId"]) ?? "";
|
|
158
|
+
state.cwd ||= extractCwd(obj) ?? "";
|
|
159
|
+
state.title ??= extractTitle(obj);
|
|
160
|
+
state.startedAt ??= parseTime(obj["timestamp"]);
|
|
161
|
+
state.preview ??= firstUserPreview(obj);
|
|
162
|
+
}
|
|
163
|
+
function hasClaudeHydrationIdentity(state) {
|
|
164
|
+
return Boolean(state.cwd && (state.title || state.preview) && state.sourceId);
|
|
165
|
+
}
|
|
155
166
|
export async function hydrateClaudeSession(session) {
|
|
156
167
|
const stream = createReadStream(session.sourcePath, { encoding: "utf8" });
|
|
157
168
|
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
169
|
+
const state = {
|
|
170
|
+
cwd: "",
|
|
171
|
+
title: session.title,
|
|
172
|
+
sourceId: session.sourceId,
|
|
173
|
+
startedAt: session.startedAt,
|
|
174
|
+
};
|
|
163
175
|
let lines = 0;
|
|
164
176
|
try {
|
|
165
177
|
for await (const line of rl) {
|
|
@@ -167,17 +179,8 @@ export async function hydrateClaudeSession(session) {
|
|
|
167
179
|
const obj = parseJsonLine(line);
|
|
168
180
|
if (!obj)
|
|
169
181
|
continue;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (!cwd)
|
|
173
|
-
cwd = extractCwd(obj) ?? "";
|
|
174
|
-
if (!title)
|
|
175
|
-
title = extractTitle(obj) ?? title;
|
|
176
|
-
if (!startedAt)
|
|
177
|
-
startedAt = parseTime(obj["timestamp"]);
|
|
178
|
-
if (!preview)
|
|
179
|
-
preview = firstUserPreview(obj);
|
|
180
|
-
if (cwd && (title || preview) && sourceId && lines > 80)
|
|
182
|
+
updateClaudeHydration(state, obj);
|
|
183
|
+
if (hasClaudeHydrationIdentity(state) && lines > 80)
|
|
181
184
|
break;
|
|
182
185
|
if (lines > 400)
|
|
183
186
|
break;
|
|
@@ -189,10 +192,148 @@ export async function hydrateClaudeSession(session) {
|
|
|
189
192
|
}
|
|
190
193
|
return {
|
|
191
194
|
...session,
|
|
192
|
-
sourceId: sourceId || session.sourceId,
|
|
193
|
-
cwd: cwd || session.cwd,
|
|
194
|
-
title: title || preview || session.title,
|
|
195
|
-
startedAt: startedAt ?? session.startedAt,
|
|
195
|
+
sourceId: state.sourceId || session.sourceId,
|
|
196
|
+
cwd: state.cwd || session.cwd,
|
|
197
|
+
title: state.title || state.preview || session.title,
|
|
198
|
+
startedAt: state.startedAt ?? session.startedAt,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function readClaudeTranscript(path) {
|
|
202
|
+
try {
|
|
203
|
+
return readFileSync(path, "utf8");
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function appendClaudeToolResults(state, blocks, timestamp) {
|
|
210
|
+
for (const raw of blocks) {
|
|
211
|
+
const block = asRecord(raw);
|
|
212
|
+
if (!block || block["type"] !== "tool_result")
|
|
213
|
+
continue;
|
|
214
|
+
const callId = asString(block["tool_use_id"]);
|
|
215
|
+
if (!callId)
|
|
216
|
+
continue;
|
|
217
|
+
state.messages.push({
|
|
218
|
+
role: "toolResult",
|
|
219
|
+
toolCallId: callId,
|
|
220
|
+
toolName: state.pendingTools.get(callId) ?? "unknown",
|
|
221
|
+
text: truncateText(flattenToolResult(block["content"]), MAX_TOOL_RESULT_CHARS),
|
|
222
|
+
isError: block["is_error"] === true,
|
|
223
|
+
timestamp,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function appendClaudeUser(state, obj) {
|
|
228
|
+
const message = asRecord(obj["message"]);
|
|
229
|
+
if (!message)
|
|
230
|
+
return;
|
|
231
|
+
const rawContent = message["content"];
|
|
232
|
+
const timestamp = parseTime(obj["timestamp"]) ?? Date.now();
|
|
233
|
+
if (isToolResultContent(rawContent) && Array.isArray(rawContent)) {
|
|
234
|
+
appendClaudeToolResults(state, rawContent, timestamp);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const text = truncateText(flattenClaudeContent(rawContent), MAX_TEXT_CHARS).trim();
|
|
238
|
+
if (!text)
|
|
239
|
+
return;
|
|
240
|
+
state.title ||= oneLine(firstMeaningfulLine(text) || text);
|
|
241
|
+
state.messages.push({ role: "user", text, timestamp });
|
|
242
|
+
}
|
|
243
|
+
function convertClaudeContentBlock(raw, pendingTools) {
|
|
244
|
+
const block = asRecord(raw);
|
|
245
|
+
if (!block)
|
|
246
|
+
return undefined;
|
|
247
|
+
const blockType = asString(block["type"]);
|
|
248
|
+
if (blockType === "thinking") {
|
|
249
|
+
const thinking = asString(block["thinking"]) ?? "";
|
|
250
|
+
return thinking.trim()
|
|
251
|
+
? { type: "thinking", thinking: truncateText(thinking, MAX_TEXT_CHARS) }
|
|
252
|
+
: undefined;
|
|
253
|
+
}
|
|
254
|
+
if (blockType === "text") {
|
|
255
|
+
const text = asString(block["text"]) ?? "";
|
|
256
|
+
return text.trim() ? { type: "text", text: truncateText(text, MAX_TEXT_CHARS) } : undefined;
|
|
257
|
+
}
|
|
258
|
+
if (blockType !== "tool_use")
|
|
259
|
+
return undefined;
|
|
260
|
+
const id = asString(block["id"]);
|
|
261
|
+
if (!id)
|
|
262
|
+
return undefined;
|
|
263
|
+
const name = asString(block["name"]) ?? "unknown";
|
|
264
|
+
const mappedName = mapToolName(name);
|
|
265
|
+
pendingTools.set(id, mappedName);
|
|
266
|
+
return {
|
|
267
|
+
type: "toolCall",
|
|
268
|
+
id,
|
|
269
|
+
name: mappedName,
|
|
270
|
+
arguments: remapToolArgs(name, asRecord(block["input"]) ?? {}),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function claudeStopReason(content, rawStopReason) {
|
|
274
|
+
if (content.some((block) => block.type === "toolCall") || rawStopReason === "tool_use") {
|
|
275
|
+
return "toolUse";
|
|
276
|
+
}
|
|
277
|
+
return rawStopReason === "max_tokens" ? "length" : "stop";
|
|
278
|
+
}
|
|
279
|
+
function appendClaudeAssistant(state, obj) {
|
|
280
|
+
const message = asRecord(obj["message"]);
|
|
281
|
+
if (!message)
|
|
282
|
+
return;
|
|
283
|
+
const rawBlocks = Array.isArray(message["content"]) ? message["content"] : [];
|
|
284
|
+
const content = rawBlocks
|
|
285
|
+
.map((block) => convertClaudeContentBlock(block, state.pendingTools))
|
|
286
|
+
.filter((block) => block !== undefined);
|
|
287
|
+
if (content.length === 0)
|
|
288
|
+
return;
|
|
289
|
+
state.messages.push({
|
|
290
|
+
role: "assistant",
|
|
291
|
+
content,
|
|
292
|
+
provider: "anthropic",
|
|
293
|
+
model: asString(message["model"]) ?? "claude",
|
|
294
|
+
api: "anthropic-messages",
|
|
295
|
+
stopReason: claudeStopReason(content, message["stop_reason"]),
|
|
296
|
+
usage: usageFromClaude(message["usage"]),
|
|
297
|
+
timestamp: parseTime(obj["timestamp"]) ?? Date.now(),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
function consumeClaudeEntry(state, obj) {
|
|
301
|
+
const type = asString(obj["type"]);
|
|
302
|
+
state.cwd ||= extractCwd(obj) ?? "";
|
|
303
|
+
if (!state.title && type === "ai-title") {
|
|
304
|
+
state.title = extractTitle(obj);
|
|
305
|
+
}
|
|
306
|
+
if (obj["isSidechain"] === true)
|
|
307
|
+
return;
|
|
308
|
+
if (type === "user") {
|
|
309
|
+
appendClaudeUser(state, obj);
|
|
310
|
+
}
|
|
311
|
+
else if (type === "assistant") {
|
|
312
|
+
appendClaudeAssistant(state, obj);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
export function convertClaudeSession(session) {
|
|
316
|
+
const text = readClaudeTranscript(session.sourcePath);
|
|
317
|
+
if (text === undefined) {
|
|
318
|
+
return { cwd: session.cwd || process.cwd(), title: session.title, messages: [] };
|
|
319
|
+
}
|
|
320
|
+
const state = {
|
|
321
|
+
messages: [],
|
|
322
|
+
cwd: "",
|
|
323
|
+
title: session.title,
|
|
324
|
+
pendingTools: new Map(),
|
|
325
|
+
};
|
|
326
|
+
for (const line of text.split(/\r?\n/)) {
|
|
327
|
+
if (line.length > MAX_CLAUDE_LINE_CHARS)
|
|
328
|
+
continue;
|
|
329
|
+
const obj = parseJsonLine(line);
|
|
330
|
+
if (obj)
|
|
331
|
+
consumeClaudeEntry(state, obj);
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
cwd: state.cwd || session.cwd || process.cwd(),
|
|
335
|
+
title: state.title,
|
|
336
|
+
messages: state.messages,
|
|
196
337
|
};
|
|
197
338
|
}
|
|
198
339
|
export const claudeAdapter = {
|
|
@@ -259,114 +400,6 @@ export const claudeAdapter = {
|
|
|
259
400
|
return found;
|
|
260
401
|
},
|
|
261
402
|
convert(session) {
|
|
262
|
-
|
|
263
|
-
try {
|
|
264
|
-
// Large Claude transcripts can be tens of MB; still fine as a one-shot import.
|
|
265
|
-
text = readFileSync(session.sourcePath, "utf8");
|
|
266
|
-
}
|
|
267
|
-
catch {
|
|
268
|
-
return { cwd: session.cwd || process.cwd(), title: session.title, messages: [] };
|
|
269
|
-
}
|
|
270
|
-
const messages = [];
|
|
271
|
-
let cwd = "";
|
|
272
|
-
let title = session.title;
|
|
273
|
-
const pendingTools = new Map();
|
|
274
|
-
for (const line of text.split(/\r?\n/)) {
|
|
275
|
-
if (line.length > 8_000_000)
|
|
276
|
-
continue;
|
|
277
|
-
const obj = parseJsonLine(line);
|
|
278
|
-
if (!obj)
|
|
279
|
-
continue;
|
|
280
|
-
const type = asString(obj["type"]);
|
|
281
|
-
if (!cwd)
|
|
282
|
-
cwd = extractCwd(obj) ?? cwd;
|
|
283
|
-
if (!title && type === "ai-title")
|
|
284
|
-
title = extractTitle(obj) ?? title;
|
|
285
|
-
if (obj["isSidechain"] === true)
|
|
286
|
-
continue;
|
|
287
|
-
if (type === "user") {
|
|
288
|
-
const msg = asRecord(obj["message"]);
|
|
289
|
-
if (!msg)
|
|
290
|
-
continue;
|
|
291
|
-
const content = msg["content"];
|
|
292
|
-
const ts = parseTime(obj["timestamp"]) ?? Date.now();
|
|
293
|
-
if (isToolResultContent(content) && Array.isArray(content)) {
|
|
294
|
-
for (const raw of content) {
|
|
295
|
-
const block = asRecord(raw);
|
|
296
|
-
if (!block || block["type"] !== "tool_result")
|
|
297
|
-
continue;
|
|
298
|
-
const callId = asString(block["tool_use_id"]) ?? "";
|
|
299
|
-
if (!callId)
|
|
300
|
-
continue;
|
|
301
|
-
messages.push({
|
|
302
|
-
role: "toolResult",
|
|
303
|
-
toolCallId: callId,
|
|
304
|
-
toolName: pendingTools.get(callId) ?? "unknown",
|
|
305
|
-
text: truncateText(flattenToolResult(block["content"]), MAX_TOOL_RESULT_CHARS),
|
|
306
|
-
isError: block["is_error"] === true,
|
|
307
|
-
timestamp: ts,
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
const userText = truncateText(flattenClaudeContent(content), MAX_TEXT_CHARS).trim();
|
|
313
|
-
if (!userText)
|
|
314
|
-
continue;
|
|
315
|
-
if (!title)
|
|
316
|
-
title = oneLine(firstMeaningfulLine(userText) || userText);
|
|
317
|
-
messages.push({ role: "user", text: userText, timestamp: ts });
|
|
318
|
-
continue;
|
|
319
|
-
}
|
|
320
|
-
if (type !== "assistant")
|
|
321
|
-
continue;
|
|
322
|
-
const msg = asRecord(obj["message"]);
|
|
323
|
-
if (!msg)
|
|
324
|
-
continue;
|
|
325
|
-
const ts = parseTime(obj["timestamp"]) ?? Date.now();
|
|
326
|
-
const blocks = Array.isArray(msg["content"]) ? msg["content"] : [];
|
|
327
|
-
const content = [];
|
|
328
|
-
let sawTool = false;
|
|
329
|
-
for (const raw of blocks) {
|
|
330
|
-
const block = asRecord(raw);
|
|
331
|
-
if (!block)
|
|
332
|
-
continue;
|
|
333
|
-
const btype = asString(block["type"]);
|
|
334
|
-
if (btype === "thinking") {
|
|
335
|
-
const thinking = asString(block["thinking"]) ?? "";
|
|
336
|
-
if (thinking.trim())
|
|
337
|
-
content.push({ type: "thinking", thinking: truncateText(thinking, MAX_TEXT_CHARS) });
|
|
338
|
-
}
|
|
339
|
-
else if (btype === "text") {
|
|
340
|
-
const textBlock = asString(block["text"]) ?? "";
|
|
341
|
-
if (textBlock.trim())
|
|
342
|
-
content.push({ type: "text", text: truncateText(textBlock, MAX_TEXT_CHARS) });
|
|
343
|
-
}
|
|
344
|
-
else if (btype === "tool_use") {
|
|
345
|
-
const id = asString(block["id"]) ?? "";
|
|
346
|
-
const name = asString(block["name"]) ?? "unknown";
|
|
347
|
-
if (!id)
|
|
348
|
-
continue;
|
|
349
|
-
const input = asRecord(block["input"]) ?? {};
|
|
350
|
-
const mapped = mapToolName(name);
|
|
351
|
-
pendingTools.set(id, mapped);
|
|
352
|
-
content.push({ type: "toolCall", id, name: mapped, arguments: remapToolArgs(name, input) });
|
|
353
|
-
sawTool = true;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
if (content.length === 0)
|
|
357
|
-
continue;
|
|
358
|
-
const stop = asString(msg["stop_reason"]);
|
|
359
|
-
messages.push({
|
|
360
|
-
role: "assistant",
|
|
361
|
-
content,
|
|
362
|
-
provider: "anthropic",
|
|
363
|
-
model: asString(msg["model"]) ?? "claude",
|
|
364
|
-
api: "anthropic-messages",
|
|
365
|
-
stopReason: sawTool || stop === "tool_use" ? "toolUse" : stop === "max_tokens" ? "length" : "stop",
|
|
366
|
-
usage: usageFromClaude(msg["usage"]),
|
|
367
|
-
timestamp: ts,
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
return { cwd: cwd || session.cwd || process.cwd(), title, messages };
|
|
403
|
+
return convertClaudeSession(session);
|
|
371
404
|
},
|
|
372
405
|
};
|
package/dist/import/codex.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type { SourceAdapter, SourceSession } from "./types.js";
|
|
1
|
+
import type { ConvertedSession, SourceAdapter, SourceSession } from "./types.js";
|
|
2
2
|
export declare function hydrateCodexSession(session: SourceSession): Promise<SourceSession>;
|
|
3
|
+
export declare function convertCodexSession(session: SourceSession): ConvertedSession;
|
|
3
4
|
export declare const codexAdapter: SourceAdapter;
|