@jamiexiongr/panda-hub 0.1.23 → 0.1.24
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/{chunk-RGBXF5RH.mjs → chunk-KWFBUSH3.mjs} +657 -34
- package/dist/{chunk-YUQA2JBP.mjs → chunk-YKEZXJPZ.mjs} +2 -2
- package/dist/cli.mjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{src-SI4UGXXJ.mjs → src-ILWWSZFE.mjs} +1 -1
- package/dist/web/assets/{diagnostics-page-CFg-0ets.js → diagnostics-page-CUUs5mBW.js} +1 -1
- package/dist/web/assets/index-BV97xtnA.js +148 -0
- package/dist/web/assets/index-DBWUmekP.css +1 -0
- package/dist/web/assets/session-diff-preview-BXSETWfx.js +1 -0
- package/dist/web/assets/session-file-markdown-preview-CNB0_vuB.js +8 -0
- package/dist/web/assets/session-file-preview-page-DZKIRsxQ.js +53 -0
- package/dist/web/assets/{web-ZoXxkmDx.js → web-BiRQ43B_.js} +1 -1
- package/dist/web/assets/{web-BDPOp9W1.js → web-BufOpPB2.js} +1 -1
- package/dist/web/assets/{web-BrfuIvmO.js → web-CVdkFm8V.js} +1 -1
- package/dist/web/assets/{web-nYC-CRnl.js → web-D7DF4ZhP.js} +1 -1
- package/dist/web/assets/{web-C7W0xBux.js → web-c9a9eQ4-.js} +1 -1
- package/dist/web/assets/xml-C8Jwua6u.js +3 -0
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/dist/web/assets/index-BMq39XAH.js +0 -143
- package/dist/web/assets/index-CsbfkzdQ.css +0 -1
- package/dist/web/assets/session-diff-preview-Ct6TOWze.js +0 -3
|
@@ -2273,6 +2273,58 @@ var sessionGitActionResponseSchema = external_exports.object({
|
|
|
2273
2273
|
ok: external_exports.literal(true),
|
|
2274
2274
|
workspace: sessionGitWorkspaceSchema
|
|
2275
2275
|
});
|
|
2276
|
+
var sessionTurnActionSchema = external_exports.enum([
|
|
2277
|
+
"rollback"
|
|
2278
|
+
]);
|
|
2279
|
+
var sessionTurnActionResponseSchema = external_exports.object({
|
|
2280
|
+
ok: external_exports.literal(true),
|
|
2281
|
+
turn_id: external_exports.string(),
|
|
2282
|
+
change_set_id: external_exports.string(),
|
|
2283
|
+
workspace: sessionGitWorkspaceSchema
|
|
2284
|
+
});
|
|
2285
|
+
var sessionFilePreviewNodeKindSchema = external_exports.enum([
|
|
2286
|
+
"directory",
|
|
2287
|
+
"file"
|
|
2288
|
+
]);
|
|
2289
|
+
var sessionFilePreviewFileKindSchema = external_exports.enum([
|
|
2290
|
+
"markdown",
|
|
2291
|
+
"code",
|
|
2292
|
+
"text",
|
|
2293
|
+
"image",
|
|
2294
|
+
"binary"
|
|
2295
|
+
]);
|
|
2296
|
+
var sessionFilePreviewTreeNodeSchema = external_exports.object({
|
|
2297
|
+
path: external_exports.string(),
|
|
2298
|
+
name: external_exports.string(),
|
|
2299
|
+
kind: sessionFilePreviewNodeKindSchema,
|
|
2300
|
+
has_children: external_exports.boolean(),
|
|
2301
|
+
extension: external_exports.string().nullable().default(null),
|
|
2302
|
+
file_kind: sessionFilePreviewFileKindSchema.nullable().default(null),
|
|
2303
|
+
size_bytes: external_exports.number().int().nonnegative().nullable().default(null)
|
|
2304
|
+
});
|
|
2305
|
+
var sessionFilePreviewTreeResponseSchema = external_exports.object({
|
|
2306
|
+
session_id: external_exports.string(),
|
|
2307
|
+
project_id: external_exports.string(),
|
|
2308
|
+
root_path: external_exports.string(),
|
|
2309
|
+
parent_path: external_exports.string().nullable().default(null),
|
|
2310
|
+
nodes: external_exports.array(sessionFilePreviewTreeNodeSchema),
|
|
2311
|
+
loaded_at: external_exports.string()
|
|
2312
|
+
});
|
|
2313
|
+
var sessionFilePreviewContentResponseSchema = external_exports.object({
|
|
2314
|
+
session_id: external_exports.string(),
|
|
2315
|
+
project_id: external_exports.string(),
|
|
2316
|
+
path: external_exports.string(),
|
|
2317
|
+
name: external_exports.string(),
|
|
2318
|
+
extension: external_exports.string().nullable().default(null),
|
|
2319
|
+
file_kind: sessionFilePreviewFileKindSchema,
|
|
2320
|
+
mime_type: external_exports.string().nullable().default(null),
|
|
2321
|
+
size_bytes: external_exports.number().int().nonnegative().nullable().default(null),
|
|
2322
|
+
encoding: external_exports.enum(["utf8", "base64"]).nullable().default(null),
|
|
2323
|
+
is_truncated: external_exports.boolean(),
|
|
2324
|
+
content_text: external_exports.string().nullable().default(null),
|
|
2325
|
+
content_base64: external_exports.string().nullable().default(null),
|
|
2326
|
+
loaded_at: external_exports.string()
|
|
2327
|
+
});
|
|
2276
2328
|
var sessionRunCommandShellSchema = external_exports.enum([
|
|
2277
2329
|
"auto",
|
|
2278
2330
|
"powershell",
|
|
@@ -10071,6 +10123,72 @@ var USER_OVERLAY_ENTRY_PREFIX = "overlay-user:";
|
|
|
10071
10123
|
var USER_ENTRY_MATCH_EARLY_SKEW_MS = 5e3;
|
|
10072
10124
|
var TIMELINE_TOOL_SUMMARY_LIMIT = 160;
|
|
10073
10125
|
var HTTP_COMPRESSION_MIN_BYTES = 4 * 1024;
|
|
10126
|
+
var DEFAULT_SESSION_TITLE_GENERATION_MODEL = "gpt-5.4-mini";
|
|
10127
|
+
var SESSION_TITLE_GENERATION_TIMEOUT_MS = 25e3;
|
|
10128
|
+
var SESSION_GENERATED_TITLE_MAX_LENGTH = 30;
|
|
10129
|
+
var SESSION_FILE_PREVIEW_ROOT_PATH = "/";
|
|
10130
|
+
var SESSION_FILE_PREVIEW_TEXT_BYTE_LIMIT = 256 * 1024;
|
|
10131
|
+
var SESSION_FILE_PREVIEW_IMAGE_BYTE_LIMIT = 6 * 1024 * 1024;
|
|
10132
|
+
var SESSION_FILE_PREVIEW_MARKDOWN_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown"]);
|
|
10133
|
+
var SESSION_FILE_PREVIEW_CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
10134
|
+
".ts",
|
|
10135
|
+
".tsx",
|
|
10136
|
+
".js",
|
|
10137
|
+
".jsx",
|
|
10138
|
+
".json",
|
|
10139
|
+
".css",
|
|
10140
|
+
".html",
|
|
10141
|
+
".xml",
|
|
10142
|
+
".yml",
|
|
10143
|
+
".yaml",
|
|
10144
|
+
".sh",
|
|
10145
|
+
".java"
|
|
10146
|
+
]);
|
|
10147
|
+
var SESSION_FILE_PREVIEW_TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
10148
|
+
".txt",
|
|
10149
|
+
".log"
|
|
10150
|
+
]);
|
|
10151
|
+
var SESSION_FILE_PREVIEW_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
10152
|
+
".png",
|
|
10153
|
+
".jpg",
|
|
10154
|
+
".jpeg",
|
|
10155
|
+
".webp",
|
|
10156
|
+
".gif",
|
|
10157
|
+
".svg"
|
|
10158
|
+
]);
|
|
10159
|
+
var SESSION_FILE_PREVIEW_CODE_FILENAMES = /* @__PURE__ */ new Set([
|
|
10160
|
+
"dockerfile",
|
|
10161
|
+
"makefile",
|
|
10162
|
+
"jenkinsfile"
|
|
10163
|
+
]);
|
|
10164
|
+
var SESSION_FILE_PREVIEW_CODE_NAME_PATTERNS = [
|
|
10165
|
+
/^\.(?:env(?:\..+)?)$/i,
|
|
10166
|
+
/^\.(?:gitignore|gitattributes|npmrc|yarnrc|editorconfig)$/i,
|
|
10167
|
+
/^\.(?:prettierrc|eslintrc)(?:\..+)?$/i
|
|
10168
|
+
];
|
|
10169
|
+
var SESSION_FILE_PREVIEW_MIME_TYPES = {
|
|
10170
|
+
".css": "text/css; charset=utf-8",
|
|
10171
|
+
".gif": "image/gif",
|
|
10172
|
+
".html": "text/html; charset=utf-8",
|
|
10173
|
+
".java": "text/x-java-source; charset=utf-8",
|
|
10174
|
+
".jpeg": "image/jpeg",
|
|
10175
|
+
".jpg": "image/jpeg",
|
|
10176
|
+
".js": "text/javascript; charset=utf-8",
|
|
10177
|
+
".json": "application/json; charset=utf-8",
|
|
10178
|
+
".log": "text/plain; charset=utf-8",
|
|
10179
|
+
".markdown": "text/markdown; charset=utf-8",
|
|
10180
|
+
".md": "text/markdown; charset=utf-8",
|
|
10181
|
+
".png": "image/png",
|
|
10182
|
+
".sh": "text/x-shellscript; charset=utf-8",
|
|
10183
|
+
".svg": "image/svg+xml",
|
|
10184
|
+
".ts": "text/typescript; charset=utf-8",
|
|
10185
|
+
".tsx": "text/typescript; charset=utf-8",
|
|
10186
|
+
".txt": "text/plain; charset=utf-8",
|
|
10187
|
+
".webp": "image/webp",
|
|
10188
|
+
".xml": "application/xml; charset=utf-8",
|
|
10189
|
+
".yaml": "text/yaml; charset=utf-8",
|
|
10190
|
+
".yml": "text/yaml; charset=utf-8"
|
|
10191
|
+
};
|
|
10074
10192
|
var WEB_UI_CONTENT_TYPES = {
|
|
10075
10193
|
".css": "text/css; charset=utf-8",
|
|
10076
10194
|
".html": "text/html; charset=utf-8",
|
|
@@ -10090,6 +10208,11 @@ Plan mode is enabled for this message only. Before doing substantial work, creat
|
|
|
10090
10208
|
</user_instructions>
|
|
10091
10209
|
|
|
10092
10210
|
`;
|
|
10211
|
+
var LATEST_VISIBLE_CODEX_COMMAND_CONFIG_VERSION = 2;
|
|
10212
|
+
var REVIEW_VISIBLE_CODEX_COMMAND_ENTRY = {
|
|
10213
|
+
name: "review",
|
|
10214
|
+
reason: "\u5FEB\u901F\u5BA1\u67E5\u5F53\u524D\u5DE5\u4F5C\u533A\u672A\u63D0\u4EA4\u6539\u52A8\uFF0C\u9002\u5408\u5728\u63D0\u4EA4\u524D\u6216\u7EE7\u7EED\u7F16\u7801\u524D\u5148\u81EA\u68C0\u4E00\u904D\u3002"
|
|
10215
|
+
};
|
|
10093
10216
|
var FALLBACK_CODEX_COMMAND_CATALOG = [
|
|
10094
10217
|
{ name: "compact", description: "\u538B\u7F29\u5F53\u524D\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF0C\u91CA\u653E\u7A97\u53E3\u5E76\u7EE7\u7EED\u5DE5\u4F5C\u3002", availability: "supported" },
|
|
10095
10218
|
{ name: "copy", description: "\u590D\u5236\u6700\u8FD1\u4E00\u6B21\u52A9\u624B\u8F93\u51FA\u3002", availability: "unsupported" },
|
|
@@ -10117,7 +10240,7 @@ var PANDA_SUPPORTED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
10117
10240
|
"status"
|
|
10118
10241
|
]);
|
|
10119
10242
|
var DEFAULT_VISIBLE_CODEX_COMMAND_CONFIG = {
|
|
10120
|
-
version:
|
|
10243
|
+
version: LATEST_VISIBLE_CODEX_COMMAND_CONFIG_VERSION,
|
|
10121
10244
|
visible_commands: [
|
|
10122
10245
|
{
|
|
10123
10246
|
name: "model",
|
|
@@ -10127,6 +10250,7 @@ var DEFAULT_VISIBLE_CODEX_COMMAND_CONFIG = {
|
|
|
10127
10250
|
name: "status",
|
|
10128
10251
|
reason: "\u5FEB\u901F\u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\u6A21\u578B\u3001\u4E0A\u4E0B\u6587\u5360\u7528\u548C\u6C99\u7BB1\u7B49\u5173\u952E\u4FE1\u606F\uFF0C\u9002\u5408\u5148\u81EA\u68C0\u3002"
|
|
10129
10252
|
},
|
|
10253
|
+
REVIEW_VISIBLE_CODEX_COMMAND_ENTRY,
|
|
10130
10254
|
{
|
|
10131
10255
|
name: "skills",
|
|
10132
10256
|
reason: "\u628A\u9879\u76EE\u91CC\u53EF\u7528\u6280\u80FD\u76F4\u63A5\u66B4\u9732\u51FA\u6765\uFF0C\u80FD\u5E2E\u52A9\u7528\u6237\u53D1\u73B0 Panda \u5DF2\u63A5\u5165\u7684\u80FD\u529B\u3002"
|
|
@@ -10370,6 +10494,142 @@ var startPandaSessionService = async ({
|
|
|
10370
10494
|
}
|
|
10371
10495
|
return !relativePath.startsWith("..") && !path9.isAbsolute(relativePath);
|
|
10372
10496
|
};
|
|
10497
|
+
const isAbsolutePathInsideProject = (projectPath, candidatePath) => {
|
|
10498
|
+
const resolvedProjectPath = path9.resolve(projectPath);
|
|
10499
|
+
const resolvedCandidatePath = path9.resolve(candidatePath);
|
|
10500
|
+
const relativePath = path9.relative(resolvedProjectPath, resolvedCandidatePath);
|
|
10501
|
+
if (!relativePath) {
|
|
10502
|
+
return true;
|
|
10503
|
+
}
|
|
10504
|
+
return !relativePath.startsWith("..") && !path9.isAbsolute(relativePath);
|
|
10505
|
+
};
|
|
10506
|
+
const normalizeSessionFilePreviewPath = (value) => normalizeGitWorkspacePath(value?.trim() ?? "").replace(/^\/+|\/+$/g, "");
|
|
10507
|
+
const isMissingPathError = (error) => error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
10508
|
+
const resolveSessionFilePreviewPath = async (projectPath, requestedPath) => {
|
|
10509
|
+
const normalizedPath = normalizeSessionFilePreviewPath(requestedPath);
|
|
10510
|
+
const absolutePath = normalizedPath ? path9.resolve(projectPath, normalizedPath) : path9.resolve(projectPath);
|
|
10511
|
+
if (!isAbsolutePathInsideProject(projectPath, absolutePath)) {
|
|
10512
|
+
throw new Error("\u76EE\u6807\u8DEF\u5F84\u4E0D\u5728\u5F53\u524D\u9879\u76EE\u5185\u3002");
|
|
10513
|
+
}
|
|
10514
|
+
let realPath;
|
|
10515
|
+
try {
|
|
10516
|
+
realPath = await fs8.realpath(absolutePath);
|
|
10517
|
+
} catch (error) {
|
|
10518
|
+
if (isMissingPathError(error)) {
|
|
10519
|
+
throw new Error("File not found.");
|
|
10520
|
+
}
|
|
10521
|
+
throw error;
|
|
10522
|
+
}
|
|
10523
|
+
if (!isAbsolutePathInsideProject(projectPath, realPath)) {
|
|
10524
|
+
throw new Error("\u76EE\u6807\u8DEF\u5F84\u4E0D\u5728\u5F53\u524D\u9879\u76EE\u5185\u3002");
|
|
10525
|
+
}
|
|
10526
|
+
return {
|
|
10527
|
+
normalizedPath,
|
|
10528
|
+
absolutePath,
|
|
10529
|
+
realPath
|
|
10530
|
+
};
|
|
10531
|
+
};
|
|
10532
|
+
const normalizeSessionFilePreviewExtension = (value) => {
|
|
10533
|
+
const extension = path9.extname(value).trim().toLowerCase();
|
|
10534
|
+
return extension || null;
|
|
10535
|
+
};
|
|
10536
|
+
const detectSessionFilePreviewKind = (fileName, extension) => {
|
|
10537
|
+
const normalizedName = fileName.trim().toLowerCase();
|
|
10538
|
+
if (extension && SESSION_FILE_PREVIEW_MARKDOWN_EXTENSIONS.has(extension)) {
|
|
10539
|
+
return "markdown";
|
|
10540
|
+
}
|
|
10541
|
+
if (extension && SESSION_FILE_PREVIEW_IMAGE_EXTENSIONS.has(extension)) {
|
|
10542
|
+
return "image";
|
|
10543
|
+
}
|
|
10544
|
+
if (extension && SESSION_FILE_PREVIEW_TEXT_EXTENSIONS.has(extension)) {
|
|
10545
|
+
return "text";
|
|
10546
|
+
}
|
|
10547
|
+
if (extension && SESSION_FILE_PREVIEW_CODE_EXTENSIONS.has(extension) || SESSION_FILE_PREVIEW_CODE_FILENAMES.has(normalizedName) || SESSION_FILE_PREVIEW_CODE_NAME_PATTERNS.some((pattern) => pattern.test(normalizedName))) {
|
|
10548
|
+
return "code";
|
|
10549
|
+
}
|
|
10550
|
+
return null;
|
|
10551
|
+
};
|
|
10552
|
+
const getSessionFilePreviewMimeType = (extension, fileKind) => {
|
|
10553
|
+
if (extension && SESSION_FILE_PREVIEW_MIME_TYPES[extension]) {
|
|
10554
|
+
return SESSION_FILE_PREVIEW_MIME_TYPES[extension];
|
|
10555
|
+
}
|
|
10556
|
+
if (fileKind === "markdown") {
|
|
10557
|
+
return "text/markdown; charset=utf-8";
|
|
10558
|
+
}
|
|
10559
|
+
if (fileKind === "code" || fileKind === "text") {
|
|
10560
|
+
return "text/plain; charset=utf-8";
|
|
10561
|
+
}
|
|
10562
|
+
return null;
|
|
10563
|
+
};
|
|
10564
|
+
const looksLikeTextBuffer = (buffer) => {
|
|
10565
|
+
if (buffer.length === 0) {
|
|
10566
|
+
return true;
|
|
10567
|
+
}
|
|
10568
|
+
let suspiciousControlCount = 0;
|
|
10569
|
+
for (const byte of buffer.values()) {
|
|
10570
|
+
if (byte === 0) {
|
|
10571
|
+
return false;
|
|
10572
|
+
}
|
|
10573
|
+
const isAllowedWhitespace = byte === 9 || byte === 10 || byte === 13;
|
|
10574
|
+
if (!isAllowedWhitespace && byte < 32) {
|
|
10575
|
+
suspiciousControlCount += 1;
|
|
10576
|
+
}
|
|
10577
|
+
}
|
|
10578
|
+
return suspiciousControlCount / buffer.length < 0.08;
|
|
10579
|
+
};
|
|
10580
|
+
const readPreviewBuffer = async (filePath, byteLimit) => {
|
|
10581
|
+
const handle = await fs8.open(filePath, "r");
|
|
10582
|
+
try {
|
|
10583
|
+
const buffer = Buffer.alloc(byteLimit);
|
|
10584
|
+
const { bytesRead } = await handle.read(buffer, 0, byteLimit, 0);
|
|
10585
|
+
return buffer.subarray(0, bytesRead);
|
|
10586
|
+
} finally {
|
|
10587
|
+
await handle.close();
|
|
10588
|
+
}
|
|
10589
|
+
};
|
|
10590
|
+
const readDirectoryHasChildren = async (directoryPath) => {
|
|
10591
|
+
try {
|
|
10592
|
+
const entries = await fs8.readdir(directoryPath);
|
|
10593
|
+
return entries.length > 0;
|
|
10594
|
+
} catch {
|
|
10595
|
+
return false;
|
|
10596
|
+
}
|
|
10597
|
+
};
|
|
10598
|
+
const buildSessionFilePreviewTreeNode = async (projectPath, parentRelativePath, parentRealPath, entry) => {
|
|
10599
|
+
const nextRelativePath = normalizeGitWorkspacePath(
|
|
10600
|
+
parentRelativePath ? `${parentRelativePath}/${entry.name}` : entry.name
|
|
10601
|
+
);
|
|
10602
|
+
const entryPath = path9.join(parentRealPath, entry.name);
|
|
10603
|
+
if (entry.isSymbolicLink()) {
|
|
10604
|
+
try {
|
|
10605
|
+
const realPath = await fs8.realpath(entryPath);
|
|
10606
|
+
if (!isAbsolutePathInsideProject(projectPath, realPath)) {
|
|
10607
|
+
return null;
|
|
10608
|
+
}
|
|
10609
|
+
} catch {
|
|
10610
|
+
return null;
|
|
10611
|
+
}
|
|
10612
|
+
}
|
|
10613
|
+
const stat = await fs8.stat(entryPath).catch(() => null);
|
|
10614
|
+
if (!stat) {
|
|
10615
|
+
return null;
|
|
10616
|
+
}
|
|
10617
|
+
const kind = stat.isDirectory() ? "directory" : stat.isFile() ? "file" : null;
|
|
10618
|
+
if (!kind) {
|
|
10619
|
+
return null;
|
|
10620
|
+
}
|
|
10621
|
+
const extension = kind === "file" ? normalizeSessionFilePreviewExtension(entry.name) : null;
|
|
10622
|
+
const fileKind = kind === "file" ? detectSessionFilePreviewKind(entry.name, extension) ?? "binary" : null;
|
|
10623
|
+
return {
|
|
10624
|
+
path: nextRelativePath,
|
|
10625
|
+
name: entry.name,
|
|
10626
|
+
kind,
|
|
10627
|
+
has_children: kind === "directory" ? await readDirectoryHasChildren(entryPath) : false,
|
|
10628
|
+
extension,
|
|
10629
|
+
file_kind: fileKind,
|
|
10630
|
+
size_bytes: kind === "file" ? stat.size : null
|
|
10631
|
+
};
|
|
10632
|
+
};
|
|
10373
10633
|
const runGitCommand = async (cwd, args, options) => new Promise((resolve, reject) => {
|
|
10374
10634
|
let stdout = "";
|
|
10375
10635
|
let stderr = "";
|
|
@@ -11031,17 +11291,36 @@ var startPandaSessionService = async ({
|
|
|
11031
11291
|
return null;
|
|
11032
11292
|
}
|
|
11033
11293
|
const candidate = value;
|
|
11034
|
-
if (candidate.version !== void 0 && candidate.version !== 1) {
|
|
11294
|
+
if (candidate.version !== void 0 && candidate.version !== 1 && candidate.version !== LATEST_VISIBLE_CODEX_COMMAND_CONFIG_VERSION) {
|
|
11035
11295
|
return null;
|
|
11036
11296
|
}
|
|
11037
11297
|
if (!Array.isArray(candidate.visible_commands)) {
|
|
11038
11298
|
return null;
|
|
11039
11299
|
}
|
|
11040
11300
|
return {
|
|
11041
|
-
version: 1,
|
|
11301
|
+
version: candidate.version === LATEST_VISIBLE_CODEX_COMMAND_CONFIG_VERSION ? LATEST_VISIBLE_CODEX_COMMAND_CONFIG_VERSION : 1,
|
|
11042
11302
|
visible_commands: candidate.visible_commands.map((entry) => normalizeVisibleCodexCommandConfigEntry(entry)).filter((entry) => Boolean(entry))
|
|
11043
11303
|
};
|
|
11044
11304
|
};
|
|
11305
|
+
const migrateVisibleCodexCommandConfig = (config) => {
|
|
11306
|
+
if (config.version >= LATEST_VISIBLE_CODEX_COMMAND_CONFIG_VERSION) {
|
|
11307
|
+
return config;
|
|
11308
|
+
}
|
|
11309
|
+
const visibleCommands = [...config.visible_commands];
|
|
11310
|
+
const hasReview = visibleCommands.some((entry) => entry.name === REVIEW_VISIBLE_CODEX_COMMAND_ENTRY.name);
|
|
11311
|
+
if (!hasReview) {
|
|
11312
|
+
const skillsIndex = visibleCommands.findIndex((entry) => entry.name === "skills");
|
|
11313
|
+
if (skillsIndex === -1) {
|
|
11314
|
+
visibleCommands.push(REVIEW_VISIBLE_CODEX_COMMAND_ENTRY);
|
|
11315
|
+
} else {
|
|
11316
|
+
visibleCommands.splice(skillsIndex, 0, REVIEW_VISIBLE_CODEX_COMMAND_ENTRY);
|
|
11317
|
+
}
|
|
11318
|
+
}
|
|
11319
|
+
return {
|
|
11320
|
+
version: LATEST_VISIBLE_CODEX_COMMAND_CONFIG_VERSION,
|
|
11321
|
+
visible_commands: visibleCommands
|
|
11322
|
+
};
|
|
11323
|
+
};
|
|
11045
11324
|
const writeVisibleCodexCommandConfig = async (config) => {
|
|
11046
11325
|
const configFilePath = await getCodexVisibleCommandsConfigFilePath();
|
|
11047
11326
|
await fs8.writeFile(configFilePath, `${JSON.stringify(config, null, 2)}
|
|
@@ -11053,7 +11332,11 @@ var startPandaSessionService = async ({
|
|
|
11053
11332
|
const raw = await fs8.readFile(configFilePath, "utf8");
|
|
11054
11333
|
const parsed = parseVisibleCodexCommandConfig(JSON.parse(raw));
|
|
11055
11334
|
if (parsed) {
|
|
11056
|
-
|
|
11335
|
+
const migrated = migrateVisibleCodexCommandConfig(parsed);
|
|
11336
|
+
if (migrated !== parsed) {
|
|
11337
|
+
await writeVisibleCodexCommandConfig(migrated);
|
|
11338
|
+
}
|
|
11339
|
+
return migrated;
|
|
11057
11340
|
}
|
|
11058
11341
|
} catch {
|
|
11059
11342
|
}
|
|
@@ -11310,6 +11593,36 @@ var startPandaSessionService = async ({
|
|
|
11310
11593
|
}
|
|
11311
11594
|
return `${trimmed.slice(0, maxLength - 1).trimEnd()}\u2026`;
|
|
11312
11595
|
};
|
|
11596
|
+
const sanitizeGeneratedSessionTitle = (value, maxLength = SESSION_GENERATED_TITLE_MAX_LENGTH) => {
|
|
11597
|
+
const normalized = value.replace(/\r\n/g, "\n").split("\n").map((line) => line.trim()).filter(Boolean).join(" ").replace(/^["'`\[\]【】()()\s]+|["'`\[\]【】()()\s]+$/g, "").replace(/^(标题|Title)\s*[::-]\s*/i, "").replace(/\s+/g, " ").trim();
|
|
11598
|
+
if (!normalized) {
|
|
11599
|
+
return null;
|
|
11600
|
+
}
|
|
11601
|
+
if (normalized.length <= maxLength) {
|
|
11602
|
+
return normalized;
|
|
11603
|
+
}
|
|
11604
|
+
return normalized.slice(0, maxLength).trimEnd();
|
|
11605
|
+
};
|
|
11606
|
+
const buildSessionTitleGenerationPrompt = (input) => {
|
|
11607
|
+
const message = input.message.trim();
|
|
11608
|
+
const attachmentNames = input.attachments.map((attachment) => attachment.name.trim()).filter(Boolean).slice(0, 5);
|
|
11609
|
+
const contextParts = [
|
|
11610
|
+
message ? `\u7528\u6237\u9996\u6761\u6D88\u606F\uFF1A
|
|
11611
|
+
${message}` : null,
|
|
11612
|
+
attachmentNames.length > 0 ? `\u9644\u4EF6\uFF1A
|
|
11613
|
+
${attachmentNames.map((name) => `- ${name}`).join("\n")}` : null
|
|
11614
|
+
].filter(Boolean);
|
|
11615
|
+
return [
|
|
11616
|
+
"\u8BF7\u6839\u636E\u4E0B\u9762\u7684\u65B0\u4F1A\u8BDD\u9996\u6761\u6D88\u606F\u751F\u6210\u4E00\u4E2A\u7B80\u77ED\u7684\u4E2D\u6587\u4F1A\u8BDD\u6807\u9898\u3002",
|
|
11617
|
+
"\u8981\u6C42\uFF1A",
|
|
11618
|
+
"1. \u53EA\u8F93\u51FA\u6807\u9898\u672C\u8EAB\uFF0C\u4E0D\u8981\u89E3\u91CA\uFF0C\u4E0D\u8981\u52A0\u5F15\u53F7\u3002",
|
|
11619
|
+
"2. \u6807\u9898\u8981\u51C6\u786E\u6982\u62EC\u4EFB\u52A1\u76EE\u6807\uFF0C\u4E0D\u8981\u7167\u6284\u539F\u53E5\u3002",
|
|
11620
|
+
"3. \u5C3D\u91CF\u63A7\u5236\u5728 4 \u5230 14 \u4E2A\u6C49\u5B57\uFF0C\u6216\u7B49\u4EF7\u7684\u77ED\u82F1\u6587\u957F\u5EA6\u3002",
|
|
11621
|
+
"4. \u907F\u514D\u7A7A\u6CDB\u8BCD\uFF0C\u6BD4\u5982\u201C\u6C42\u52A9\u201D\u201C\u95EE\u9898\u201D\u201C\u804A\u5929\u201D\u201C\u65B0\u4F1A\u8BDD\u201D\u3002",
|
|
11622
|
+
"",
|
|
11623
|
+
...contextParts
|
|
11624
|
+
].join("\n");
|
|
11625
|
+
};
|
|
11313
11626
|
const createDiagnosticHash2 = (value) => createHash5("sha1").update(value).digest("hex").slice(0, 16);
|
|
11314
11627
|
const summarizeTextForDiagnostics = (value, options) => {
|
|
11315
11628
|
const normalized = typeof value === "string" ? value.trim() : "";
|
|
@@ -11347,6 +11660,7 @@ var startPandaSessionService = async ({
|
|
|
11347
11660
|
dataHash: attachment.dataHash
|
|
11348
11661
|
})),
|
|
11349
11662
|
model: input.model,
|
|
11663
|
+
titleGenerationModel: input.titleGenerationModel ?? null,
|
|
11350
11664
|
reasoningEffort: input.reasoningEffort,
|
|
11351
11665
|
requestedServiceTier: input.requestedServiceTier ?? null,
|
|
11352
11666
|
normalizedRequestedServiceTier: input.normalizedRequestedServiceTier,
|
|
@@ -11360,6 +11674,7 @@ var startPandaSessionService = async ({
|
|
|
11360
11674
|
prompt: promptSummary,
|
|
11361
11675
|
attachments: attachmentsSummary,
|
|
11362
11676
|
model: input.model,
|
|
11677
|
+
titleGenerationModel: input.titleGenerationModel ?? null,
|
|
11363
11678
|
reasoningEffort: input.reasoningEffort,
|
|
11364
11679
|
requestedServiceTier: input.requestedServiceTier === "fast" || input.requestedServiceTier === "flex" ? input.requestedServiceTier : input.requestedServiceTier ?? null,
|
|
11365
11680
|
normalizedRequestedServiceTier: input.normalizedRequestedServiceTier,
|
|
@@ -11572,7 +11887,7 @@ var startPandaSessionService = async ({
|
|
|
11572
11887
|
return nextOverlayEntries;
|
|
11573
11888
|
};
|
|
11574
11889
|
const readTimelineFromRollout = async (sessionId) => {
|
|
11575
|
-
const { readCodexTimeline } = await import("./src-
|
|
11890
|
+
const { readCodexTimeline } = await import("./src-ILWWSZFE.mjs");
|
|
11576
11891
|
return readCodexTimeline(sessionId, {
|
|
11577
11892
|
codexHome,
|
|
11578
11893
|
sessionFiles: discoveredSessionFiles
|
|
@@ -12171,6 +12486,14 @@ var startPandaSessionService = async ({
|
|
|
12171
12486
|
}
|
|
12172
12487
|
return [];
|
|
12173
12488
|
};
|
|
12489
|
+
const readChangeSetForSession = async (sessionId, changeSetId) => {
|
|
12490
|
+
const changeSets = await readChangeSetsForSession(sessionId);
|
|
12491
|
+
return changeSets.find((entry) => entry.id === changeSetId) ?? null;
|
|
12492
|
+
};
|
|
12493
|
+
const readChangeSetForTurn = async (sessionId, turnId) => {
|
|
12494
|
+
const changeSets = await readChangeSetsForSession(sessionId);
|
|
12495
|
+
return changeSets.find((entry) => entry.turn_id === turnId) ?? null;
|
|
12496
|
+
};
|
|
12174
12497
|
const readPlanForSession = async (sessionId) => {
|
|
12175
12498
|
const existing = sessionPlans.get(sessionId);
|
|
12176
12499
|
if (existing !== void 0) {
|
|
@@ -12202,8 +12525,7 @@ var startPandaSessionService = async ({
|
|
|
12202
12525
|
return [];
|
|
12203
12526
|
};
|
|
12204
12527
|
const readChangeSetFileDiffForSession = async (sessionId, input) => {
|
|
12205
|
-
const
|
|
12206
|
-
const changeSet = changeSets.find((entry) => entry.id === input.changeSetId);
|
|
12528
|
+
const changeSet = await readChangeSetForSession(sessionId, input.changeSetId);
|
|
12207
12529
|
if (!changeSet) {
|
|
12208
12530
|
return null;
|
|
12209
12531
|
}
|
|
@@ -12676,7 +12998,7 @@ var startPandaSessionService = async ({
|
|
|
12676
12998
|
commands: filterCodexCommandsForDisplay(catalog.commands, visibleConfig)
|
|
12677
12999
|
};
|
|
12678
13000
|
};
|
|
12679
|
-
const
|
|
13001
|
+
const renameSession = async (sessionId, nextName) => {
|
|
12680
13002
|
if (managedSessions.has(sessionId)) {
|
|
12681
13003
|
const managedSession = managedSessions.get(sessionId);
|
|
12682
13004
|
managedSessions.set(sessionId, {
|
|
@@ -12701,6 +13023,37 @@ var startPandaSessionService = async ({
|
|
|
12701
13023
|
}
|
|
12702
13024
|
});
|
|
12703
13025
|
};
|
|
13026
|
+
const generateSessionTitleInBackground = async (input) => {
|
|
13027
|
+
const prompt = buildSessionTitleGenerationPrompt({
|
|
13028
|
+
message: input.message,
|
|
13029
|
+
attachments: input.attachments
|
|
13030
|
+
});
|
|
13031
|
+
try {
|
|
13032
|
+
const generated = await liveSessionStream.runOneShotPrompt({
|
|
13033
|
+
cwd: input.project.path,
|
|
13034
|
+
prompt,
|
|
13035
|
+
model: input.model,
|
|
13036
|
+
timeoutMs: SESSION_TITLE_GENERATION_TIMEOUT_MS
|
|
13037
|
+
});
|
|
13038
|
+
const nextTitle = sanitizeGeneratedSessionTitle(generated);
|
|
13039
|
+
if (!nextTitle || nextTitle === input.fallbackTitle) {
|
|
13040
|
+
return;
|
|
13041
|
+
}
|
|
13042
|
+
const currentSession = await findSnapshotSession(input.sessionId);
|
|
13043
|
+
if (!currentSession || currentSession.title !== input.fallbackTitle) {
|
|
13044
|
+
return;
|
|
13045
|
+
}
|
|
13046
|
+
await renameSession(input.sessionId, nextTitle);
|
|
13047
|
+
} catch (error) {
|
|
13048
|
+
diagnosticLogger.warn({
|
|
13049
|
+
sessionId: input.sessionId,
|
|
13050
|
+
projectId: input.project.id,
|
|
13051
|
+
projectPath: input.project.path,
|
|
13052
|
+
model: input.model,
|
|
13053
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
13054
|
+
}, "Failed to generate background session title.");
|
|
13055
|
+
}
|
|
13056
|
+
};
|
|
12704
13057
|
const executeSessionCommand = async (session, project, rawInput) => {
|
|
12705
13058
|
const parsed = parseSlashCommandInput(rawInput);
|
|
12706
13059
|
if (!parsed) {
|
|
@@ -12802,7 +13155,7 @@ var startPandaSessionService = async ({
|
|
|
12802
13155
|
}
|
|
12803
13156
|
if (command.name === "rename") {
|
|
12804
13157
|
if (parsed.args) {
|
|
12805
|
-
await
|
|
13158
|
+
await renameSession(session.id, parsed.args);
|
|
12806
13159
|
return createCommandPanel({
|
|
12807
13160
|
sessionId: session.id,
|
|
12808
13161
|
commandName: command.name,
|
|
@@ -12872,7 +13225,7 @@ var startPandaSessionService = async ({
|
|
|
12872
13225
|
if (!nextName) {
|
|
12873
13226
|
throw new Error("\u8BF7\u8F93\u5165\u65B0\u7684\u4F1A\u8BDD\u540D\u79F0\u3002");
|
|
12874
13227
|
}
|
|
12875
|
-
await
|
|
13228
|
+
await renameSession(input.sessionId, nextName);
|
|
12876
13229
|
clearStoredCommandPanel(input.panelId);
|
|
12877
13230
|
return {
|
|
12878
13231
|
...stored.panel,
|
|
@@ -13040,7 +13393,7 @@ var startPandaSessionService = async ({
|
|
|
13040
13393
|
lastSnapshotRefreshAt = Date.now();
|
|
13041
13394
|
return snapshot;
|
|
13042
13395
|
}
|
|
13043
|
-
const { discoverLocalCodexData } = await import("./src-
|
|
13396
|
+
const { discoverLocalCodexData } = await import("./src-ILWWSZFE.mjs");
|
|
13044
13397
|
const discovery = await discoverLocalCodexData({
|
|
13045
13398
|
agentId: localAgentId,
|
|
13046
13399
|
agentName: localAgentName,
|
|
@@ -13116,6 +13469,135 @@ var startPandaSessionService = async ({
|
|
|
13116
13469
|
return snapshot.projects.find((project) => project.id === projectId) ?? (await buildSnapshot({ force: true })).projects.find((project) => project.id === projectId) ?? null;
|
|
13117
13470
|
};
|
|
13118
13471
|
const readProjectForSession = async (session) => findSnapshotProject(session.project_id);
|
|
13472
|
+
const readSessionFilePreviewTree = async (session, project, requestedPath) => {
|
|
13473
|
+
const resolved = await resolveSessionFilePreviewPath(project.path, requestedPath);
|
|
13474
|
+
const stat = await fs8.stat(resolved.realPath).catch((error) => {
|
|
13475
|
+
if (isMissingPathError(error)) {
|
|
13476
|
+
throw new Error("File not found.");
|
|
13477
|
+
}
|
|
13478
|
+
throw error;
|
|
13479
|
+
});
|
|
13480
|
+
if (!stat.isDirectory()) {
|
|
13481
|
+
throw new Error("\u76EE\u6807\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\u3002");
|
|
13482
|
+
}
|
|
13483
|
+
const entries = await fs8.readdir(resolved.realPath, {
|
|
13484
|
+
withFileTypes: true
|
|
13485
|
+
});
|
|
13486
|
+
const nodes = (await Promise.all(
|
|
13487
|
+
entries.map(
|
|
13488
|
+
(entry) => buildSessionFilePreviewTreeNode(
|
|
13489
|
+
project.path,
|
|
13490
|
+
resolved.normalizedPath,
|
|
13491
|
+
resolved.realPath,
|
|
13492
|
+
entry
|
|
13493
|
+
)
|
|
13494
|
+
)
|
|
13495
|
+
)).filter((entry) => Boolean(entry)).sort((left, right) => {
|
|
13496
|
+
if (left.kind !== right.kind) {
|
|
13497
|
+
return left.kind === "directory" ? -1 : 1;
|
|
13498
|
+
}
|
|
13499
|
+
return left.name.localeCompare(right.name, "zh-CN");
|
|
13500
|
+
});
|
|
13501
|
+
return {
|
|
13502
|
+
session_id: session.id,
|
|
13503
|
+
project_id: project.id,
|
|
13504
|
+
root_path: SESSION_FILE_PREVIEW_ROOT_PATH,
|
|
13505
|
+
parent_path: resolved.normalizedPath || null,
|
|
13506
|
+
nodes,
|
|
13507
|
+
loaded_at: isoNow5()
|
|
13508
|
+
};
|
|
13509
|
+
};
|
|
13510
|
+
const readSessionFilePreviewContent = async (session, project, requestedPath) => {
|
|
13511
|
+
const resolved = await resolveSessionFilePreviewPath(project.path, requestedPath);
|
|
13512
|
+
const stat = await fs8.stat(resolved.realPath).catch((error) => {
|
|
13513
|
+
if (isMissingPathError(error)) {
|
|
13514
|
+
throw new Error("File not found.");
|
|
13515
|
+
}
|
|
13516
|
+
throw error;
|
|
13517
|
+
});
|
|
13518
|
+
if (!stat.isFile()) {
|
|
13519
|
+
throw new Error("\u76EE\u6807\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6\u3002");
|
|
13520
|
+
}
|
|
13521
|
+
const fileName = path9.basename(resolved.normalizedPath || resolved.realPath);
|
|
13522
|
+
const extension = normalizeSessionFilePreviewExtension(fileName);
|
|
13523
|
+
let fileKind = detectSessionFilePreviewKind(fileName, extension) ?? null;
|
|
13524
|
+
let previewBuffer = null;
|
|
13525
|
+
if (!fileKind || fileKind === "binary") {
|
|
13526
|
+
previewBuffer = await readPreviewBuffer(
|
|
13527
|
+
resolved.realPath,
|
|
13528
|
+
Math.min(SESSION_FILE_PREVIEW_TEXT_BYTE_LIMIT, Math.max(1024, stat.size))
|
|
13529
|
+
);
|
|
13530
|
+
fileKind = looksLikeTextBuffer(previewBuffer) ? "text" : "binary";
|
|
13531
|
+
}
|
|
13532
|
+
if (fileKind === "image") {
|
|
13533
|
+
if (stat.size > SESSION_FILE_PREVIEW_IMAGE_BYTE_LIMIT) {
|
|
13534
|
+
return {
|
|
13535
|
+
session_id: session.id,
|
|
13536
|
+
project_id: project.id,
|
|
13537
|
+
path: resolved.normalizedPath,
|
|
13538
|
+
name: fileName,
|
|
13539
|
+
extension,
|
|
13540
|
+
file_kind: fileKind,
|
|
13541
|
+
mime_type: getSessionFilePreviewMimeType(extension, fileKind),
|
|
13542
|
+
size_bytes: stat.size,
|
|
13543
|
+
encoding: null,
|
|
13544
|
+
is_truncated: true,
|
|
13545
|
+
content_text: null,
|
|
13546
|
+
content_base64: null,
|
|
13547
|
+
loaded_at: isoNow5()
|
|
13548
|
+
};
|
|
13549
|
+
}
|
|
13550
|
+
const buffer2 = await fs8.readFile(resolved.realPath);
|
|
13551
|
+
return {
|
|
13552
|
+
session_id: session.id,
|
|
13553
|
+
project_id: project.id,
|
|
13554
|
+
path: resolved.normalizedPath,
|
|
13555
|
+
name: fileName,
|
|
13556
|
+
extension,
|
|
13557
|
+
file_kind: fileKind,
|
|
13558
|
+
mime_type: getSessionFilePreviewMimeType(extension, fileKind),
|
|
13559
|
+
size_bytes: stat.size,
|
|
13560
|
+
encoding: "base64",
|
|
13561
|
+
is_truncated: false,
|
|
13562
|
+
content_text: null,
|
|
13563
|
+
content_base64: buffer2.toString("base64"),
|
|
13564
|
+
loaded_at: isoNow5()
|
|
13565
|
+
};
|
|
13566
|
+
}
|
|
13567
|
+
if (fileKind === "binary") {
|
|
13568
|
+
return {
|
|
13569
|
+
session_id: session.id,
|
|
13570
|
+
project_id: project.id,
|
|
13571
|
+
path: resolved.normalizedPath,
|
|
13572
|
+
name: fileName,
|
|
13573
|
+
extension,
|
|
13574
|
+
file_kind: fileKind,
|
|
13575
|
+
mime_type: getSessionFilePreviewMimeType(extension, fileKind),
|
|
13576
|
+
size_bytes: stat.size,
|
|
13577
|
+
encoding: null,
|
|
13578
|
+
is_truncated: false,
|
|
13579
|
+
content_text: null,
|
|
13580
|
+
content_base64: null,
|
|
13581
|
+
loaded_at: isoNow5()
|
|
13582
|
+
};
|
|
13583
|
+
}
|
|
13584
|
+
const buffer = previewBuffer ?? await readPreviewBuffer(resolved.realPath, SESSION_FILE_PREVIEW_TEXT_BYTE_LIMIT);
|
|
13585
|
+
return {
|
|
13586
|
+
session_id: session.id,
|
|
13587
|
+
project_id: project.id,
|
|
13588
|
+
path: resolved.normalizedPath,
|
|
13589
|
+
name: fileName,
|
|
13590
|
+
extension,
|
|
13591
|
+
file_kind: fileKind,
|
|
13592
|
+
mime_type: getSessionFilePreviewMimeType(extension, fileKind),
|
|
13593
|
+
size_bytes: stat.size,
|
|
13594
|
+
encoding: "utf8",
|
|
13595
|
+
is_truncated: stat.size > SESSION_FILE_PREVIEW_TEXT_BYTE_LIMIT,
|
|
13596
|
+
content_text: buffer.toString("utf8"),
|
|
13597
|
+
content_base64: null,
|
|
13598
|
+
loaded_at: isoNow5()
|
|
13599
|
+
};
|
|
13600
|
+
};
|
|
13119
13601
|
const validateHubControlPlaneAuth = (request) => {
|
|
13120
13602
|
const expectedApiKey = process.env.PANDA_HUB_API_KEY?.trim() ?? "";
|
|
13121
13603
|
if (!expectedApiKey) {
|
|
@@ -13147,6 +13629,58 @@ var startPandaSessionService = async ({
|
|
|
13147
13629
|
}
|
|
13148
13630
|
await runGitCommand(projectPath, ["restore", "--source=HEAD", "--staged", "--worktree", "--", ...targetPaths]);
|
|
13149
13631
|
};
|
|
13632
|
+
const normalizeRollbackPatchText = (input) => {
|
|
13633
|
+
const normalized = input.replace(/\r\n/g, "\n").trim();
|
|
13634
|
+
return normalized ? `${normalized}
|
|
13635
|
+
` : "";
|
|
13636
|
+
};
|
|
13637
|
+
const buildRollbackPatchText = (changeSet) => {
|
|
13638
|
+
if (changeSet.aggregated_diff.trim()) {
|
|
13639
|
+
return normalizeRollbackPatchText(changeSet.aggregated_diff);
|
|
13640
|
+
}
|
|
13641
|
+
if (changeSet.files.some((file) => file.diff.trim().length === 0)) {
|
|
13642
|
+
return "";
|
|
13643
|
+
}
|
|
13644
|
+
return normalizeRollbackPatchText(
|
|
13645
|
+
changeSet.files.map((file) => file.diff).filter((diff) => diff.trim().length > 0).join("\n\n")
|
|
13646
|
+
);
|
|
13647
|
+
};
|
|
13648
|
+
const ensureChangeSetPathsAreInProject = (projectPath, changeSet) => {
|
|
13649
|
+
for (const file of changeSet.files) {
|
|
13650
|
+
const candidates = [file.path, file.move_path].filter((value) => Boolean(value?.trim()));
|
|
13651
|
+
for (const candidate of candidates) {
|
|
13652
|
+
if (!isPathInsideProject(projectPath, candidate)) {
|
|
13653
|
+
throw new Error("\u8FD9\u8F6E\u6539\u52A8\u5305\u542B\u8D85\u51FA\u5F53\u524D\u5DE5\u4F5C\u533A\u7684\u8DEF\u5F84\uFF0C\u65E0\u6CD5\u5B89\u5168\u56DE\u6EDA\u3002");
|
|
13654
|
+
}
|
|
13655
|
+
}
|
|
13656
|
+
}
|
|
13657
|
+
};
|
|
13658
|
+
const rollbackSessionChangeSet = async (projectPath, changeSet) => {
|
|
13659
|
+
if (changeSet.status !== "completed") {
|
|
13660
|
+
throw new Error("\u5F53\u524D\u8FD9\u8F6E\u6539\u52A8\u5C1A\u672A\u5B8C\u6210\uFF0C\u6682\u65F6\u4E0D\u80FD\u56DE\u6EDA\u3002");
|
|
13661
|
+
}
|
|
13662
|
+
if (changeSet.files.length === 0) {
|
|
13663
|
+
throw new Error("\u8FD9\u8F6E\u5BF9\u8BDD\u6CA1\u6709\u53EF\u56DE\u6EDA\u7684\u6587\u4EF6\u6539\u52A8\u3002");
|
|
13664
|
+
}
|
|
13665
|
+
ensureChangeSetPathsAreInProject(projectPath, changeSet);
|
|
13666
|
+
const patchText = buildRollbackPatchText(changeSet);
|
|
13667
|
+
if (!patchText) {
|
|
13668
|
+
throw new Error("\u8FD9\u8F6E\u6539\u52A8\u7F3A\u5C11\u53EF\u9006\u8865\u4E01\uFF0C\u6682\u65F6\u65E0\u6CD5\u56DE\u6EDA\u3002");
|
|
13669
|
+
}
|
|
13670
|
+
const tempDir = await fs8.mkdtemp(path9.join(os6.tmpdir(), "panda-change-set-rollback-"));
|
|
13671
|
+
const patchFilePath = path9.join(tempDir, "rollback.patch");
|
|
13672
|
+
await fs8.writeFile(patchFilePath, patchText, "utf8");
|
|
13673
|
+
try {
|
|
13674
|
+
const patchArgs = ["apply", "--reverse", "--whitespace=nowarn", "--binary", patchFilePath];
|
|
13675
|
+
await runGitCommand(projectPath, [...patchArgs.slice(0, 1), "--check", ...patchArgs.slice(1)]);
|
|
13676
|
+
await runGitCommand(projectPath, patchArgs);
|
|
13677
|
+
} catch (error) {
|
|
13678
|
+
const reason = error instanceof Error && error.message.trim() ? error.message : "Git \u65E0\u6CD5\u53CD\u5411\u5E94\u7528\u8FD9\u8F6E\u6539\u52A8\u7684\u8865\u4E01\u3002";
|
|
13679
|
+
throw new Error(`\u65E0\u6CD5\u5B89\u5168\u56DE\u6EDA\u8FD9\u8F6E\u6539\u52A8\uFF1A${reason}`);
|
|
13680
|
+
} finally {
|
|
13681
|
+
await fs8.rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
|
|
13682
|
+
}
|
|
13683
|
+
};
|
|
13150
13684
|
const discardAllGitWorkspaceChanges = async (projectPath) => {
|
|
13151
13685
|
await runGitCommand(projectPath, ["restore", "--source=HEAD", "--staged", "--worktree", "--", "."]);
|
|
13152
13686
|
await runGitCommand(projectPath, ["clean", "-fd", "--", "."], {
|
|
@@ -13851,6 +14385,107 @@ var startPandaSessionService = async ({
|
|
|
13851
14385
|
}
|
|
13852
14386
|
return diff;
|
|
13853
14387
|
});
|
|
14388
|
+
app.post("/api/sessions/:sessionId/turns/:turnId/actions", async (request, reply) => {
|
|
14389
|
+
const { sessionId, turnId } = request.params;
|
|
14390
|
+
const session = await findSnapshotSession(sessionId);
|
|
14391
|
+
if (!session) {
|
|
14392
|
+
reply.code(404);
|
|
14393
|
+
return { error: "Session not found." };
|
|
14394
|
+
}
|
|
14395
|
+
if (!session.capability.can_show_git) {
|
|
14396
|
+
reply.code(409);
|
|
14397
|
+
return { error: "This session does not support git workspace actions." };
|
|
14398
|
+
}
|
|
14399
|
+
const project = await readProjectForSession(session);
|
|
14400
|
+
if (!project) {
|
|
14401
|
+
reply.code(404);
|
|
14402
|
+
return { error: "Project not found." };
|
|
14403
|
+
}
|
|
14404
|
+
if (request.body.action !== "rollback") {
|
|
14405
|
+
reply.code(409);
|
|
14406
|
+
return { error: "Unsupported turn action." };
|
|
14407
|
+
}
|
|
14408
|
+
const normalizedTurnId = turnId.trim();
|
|
14409
|
+
if (!normalizedTurnId) {
|
|
14410
|
+
reply.code(400);
|
|
14411
|
+
return { error: "Turn id is required." };
|
|
14412
|
+
}
|
|
14413
|
+
const changeSet = await readChangeSetForTurn(sessionId, normalizedTurnId);
|
|
14414
|
+
if (!changeSet) {
|
|
14415
|
+
reply.code(404);
|
|
14416
|
+
return { error: "Session turn change-set not found." };
|
|
14417
|
+
}
|
|
14418
|
+
try {
|
|
14419
|
+
await rollbackSessionChangeSet(project.path, changeSet);
|
|
14420
|
+
return {
|
|
14421
|
+
ok: true,
|
|
14422
|
+
turn_id: changeSet.turn_id,
|
|
14423
|
+
change_set_id: changeSet.id,
|
|
14424
|
+
workspace: await readSessionGitWorkspace(session, project)
|
|
14425
|
+
};
|
|
14426
|
+
} catch (error) {
|
|
14427
|
+
reply.code(409);
|
|
14428
|
+
return {
|
|
14429
|
+
error: error instanceof Error ? error.message : "Unable to execute turn action."
|
|
14430
|
+
};
|
|
14431
|
+
}
|
|
14432
|
+
});
|
|
14433
|
+
app.get("/api/sessions/:sessionId/file-preview/tree", async (request, reply) => {
|
|
14434
|
+
const { sessionId } = request.params;
|
|
14435
|
+
const { path: requestedPath } = request.query ?? {};
|
|
14436
|
+
const session = await findSnapshotSession(sessionId);
|
|
14437
|
+
if (!session) {
|
|
14438
|
+
reply.code(404);
|
|
14439
|
+
return { error: "Session not found." };
|
|
14440
|
+
}
|
|
14441
|
+
const project = await readProjectForSession(session);
|
|
14442
|
+
if (!project) {
|
|
14443
|
+
reply.code(404);
|
|
14444
|
+
return { error: "Project not found." };
|
|
14445
|
+
}
|
|
14446
|
+
try {
|
|
14447
|
+
return await readSessionFilePreviewTree(session, project, requestedPath ?? null);
|
|
14448
|
+
} catch (error) {
|
|
14449
|
+
if (error instanceof Error && error.message === "File not found.") {
|
|
14450
|
+
reply.code(404);
|
|
14451
|
+
return { error: error.message };
|
|
14452
|
+
}
|
|
14453
|
+
reply.code(409);
|
|
14454
|
+
return {
|
|
14455
|
+
error: error instanceof Error ? error.message : "Unable to read file preview tree."
|
|
14456
|
+
};
|
|
14457
|
+
}
|
|
14458
|
+
});
|
|
14459
|
+
app.get("/api/sessions/:sessionId/file-preview/content", async (request, reply) => {
|
|
14460
|
+
const { sessionId } = request.params;
|
|
14461
|
+
const { path: requestedPath } = request.query ?? {};
|
|
14462
|
+
const session = await findSnapshotSession(sessionId);
|
|
14463
|
+
if (!session) {
|
|
14464
|
+
reply.code(404);
|
|
14465
|
+
return { error: "Session not found." };
|
|
14466
|
+
}
|
|
14467
|
+
if (!requestedPath?.trim()) {
|
|
14468
|
+
reply.code(400);
|
|
14469
|
+
return { error: "path is required." };
|
|
14470
|
+
}
|
|
14471
|
+
const project = await readProjectForSession(session);
|
|
14472
|
+
if (!project) {
|
|
14473
|
+
reply.code(404);
|
|
14474
|
+
return { error: "Project not found." };
|
|
14475
|
+
}
|
|
14476
|
+
try {
|
|
14477
|
+
return await readSessionFilePreviewContent(session, project, requestedPath);
|
|
14478
|
+
} catch (error) {
|
|
14479
|
+
if (error instanceof Error && error.message === "File not found.") {
|
|
14480
|
+
reply.code(404);
|
|
14481
|
+
return { error: error.message };
|
|
14482
|
+
}
|
|
14483
|
+
reply.code(409);
|
|
14484
|
+
return {
|
|
14485
|
+
error: error instanceof Error ? error.message : "Unable to read file preview content."
|
|
14486
|
+
};
|
|
14487
|
+
}
|
|
14488
|
+
});
|
|
13854
14489
|
app.get("/api/sessions/:sessionId/git-workspace", async (request, reply) => {
|
|
13855
14490
|
const { sessionId } = request.params;
|
|
13856
14491
|
const session = await findSnapshotSession(sessionId);
|
|
@@ -14551,6 +15186,7 @@ var startPandaSessionService = async ({
|
|
|
14551
15186
|
const input = request.body.input?.trim() ?? "";
|
|
14552
15187
|
const attachments = normalizeSessionInputAttachments(request.body.attachments);
|
|
14553
15188
|
const model = request.body.model?.trim() || null;
|
|
15189
|
+
const titleGenerationModel = request.body.titleGenerationModel?.trim() || DEFAULT_SESSION_TITLE_GENERATION_MODEL;
|
|
14554
15190
|
const reasoningEffort = request.body.reasoningEffort?.trim() || null;
|
|
14555
15191
|
const requestedServiceTier = request.body.serviceTier;
|
|
14556
15192
|
const normalizedRequestedServiceTier = normalizeServiceTier(requestedServiceTier);
|
|
@@ -14579,6 +15215,7 @@ var startPandaSessionService = async ({
|
|
|
14579
15215
|
prompt,
|
|
14580
15216
|
attachments,
|
|
14581
15217
|
model,
|
|
15218
|
+
titleGenerationModel,
|
|
14582
15219
|
reasoningEffort,
|
|
14583
15220
|
requestedServiceTier,
|
|
14584
15221
|
normalizedRequestedServiceTier,
|
|
@@ -14672,6 +15309,14 @@ var startPandaSessionService = async ({
|
|
|
14672
15309
|
managedSessions.set(session.id, session);
|
|
14673
15310
|
upsertSnapshotSession(session);
|
|
14674
15311
|
broadcastSnapshotChanged();
|
|
15312
|
+
void generateSessionTitleInBackground({
|
|
15313
|
+
sessionId: session.id,
|
|
15314
|
+
project,
|
|
15315
|
+
fallbackTitle: title,
|
|
15316
|
+
message: input,
|
|
15317
|
+
attachments,
|
|
15318
|
+
model: titleGenerationModel
|
|
15319
|
+
});
|
|
14675
15320
|
reply.code(201);
|
|
14676
15321
|
return { session };
|
|
14677
15322
|
});
|
|
@@ -14695,29 +15340,7 @@ var startPandaSessionService = async ({
|
|
|
14695
15340
|
reply.code(400);
|
|
14696
15341
|
return { error: "Session name is required." };
|
|
14697
15342
|
}
|
|
14698
|
-
|
|
14699
|
-
const managedSession = managedSessions.get(sessionId);
|
|
14700
|
-
managedSessions.set(sessionId, {
|
|
14701
|
-
...managedSession,
|
|
14702
|
-
title: nextName
|
|
14703
|
-
});
|
|
14704
|
-
}
|
|
14705
|
-
await liveSessionStream.setThreadName(sessionId, nextName).catch(() => {
|
|
14706
|
-
});
|
|
14707
|
-
await appendSessionIndexUpdate(
|
|
14708
|
-
sessionId,
|
|
14709
|
-
{
|
|
14710
|
-
thread_name: nextName
|
|
14711
|
-
},
|
|
14712
|
-
codexHome
|
|
14713
|
-
);
|
|
14714
|
-
patchSnapshotSession(sessionId, { title: nextName });
|
|
14715
|
-
broadcastEvent("session.updated", {
|
|
14716
|
-
sessionId,
|
|
14717
|
-
sessionPatch: {
|
|
14718
|
-
title: nextName
|
|
14719
|
-
}
|
|
14720
|
-
});
|
|
15343
|
+
await renameSession(sessionId, nextName);
|
|
14721
15344
|
return {
|
|
14722
15345
|
ok: true,
|
|
14723
15346
|
affectedSessionIds: [sessionId],
|