@jamiexiongr/panda-hub 0.1.22 → 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-SPTBBMUL.mjs → chunk-KWFBUSH3.mjs} +667 -36
- package/dist/{chunk-VKOJD7PG.mjs → chunk-YKEZXJPZ.mjs} +30 -15
- package/dist/cli.mjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{src-M7W7LPHG.mjs → src-ILWWSZFE.mjs} +1 -1
- package/dist/web/assets/{diagnostics-page-BKp7IiZk.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-BqX5x67V.js → web-BiRQ43B_.js} +1 -1
- package/dist/web/assets/{web-CIHpZyzh.js → web-BufOpPB2.js} +1 -1
- package/dist/web/assets/{web-C3EZUi0V.js → web-CVdkFm8V.js} +1 -1
- package/dist/web/assets/{web-HzS7kaoe.js → web-D7DF4ZhP.js} +1 -1
- package/dist/web/assets/{web-Ck4th-0Z.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-DhjIbKkn.js +0 -142
- package/dist/web/assets/index-QBVYFqgo.css +0 -1
- package/dist/web/assets/session-diff-preview-BU4XO0fT.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",
|
|
@@ -3244,6 +3296,7 @@ var APP_SERVER_DIAGNOSTIC_METHODS = /* @__PURE__ */ new Set([
|
|
|
3244
3296
|
"config/read"
|
|
3245
3297
|
]);
|
|
3246
3298
|
var defaultCodexHome2 = () => path4.join(os3.homedir(), ".codex");
|
|
3299
|
+
var resolveCodexEnvironmentHome = () => process.env.CODEX_HOME?.trim() || process.env.PANDA_CODEX_HOME?.trim() || defaultCodexHome2();
|
|
3247
3300
|
var truncateText = (value, length) => {
|
|
3248
3301
|
const trimmed = value.trim();
|
|
3249
3302
|
if (trimmed.length <= length) {
|
|
@@ -5694,14 +5747,20 @@ ${nextFile.diff}`.trim();
|
|
|
5694
5747
|
const spawnAppServerChild = () => {
|
|
5695
5748
|
try {
|
|
5696
5749
|
const appServerArgs = ["app-server", "--enable", "default_mode_request_user_input"];
|
|
5750
|
+
const childEnv = {
|
|
5751
|
+
...process.env,
|
|
5752
|
+
CODEX_HOME: resolveCodexEnvironmentHome()
|
|
5753
|
+
};
|
|
5697
5754
|
if (process.platform === "win32") {
|
|
5698
5755
|
return spawn(process.env.ComSpec || "cmd.exe", ["/d", "/c", "codex", ...appServerArgs], {
|
|
5699
5756
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5700
|
-
windowsHide: true
|
|
5757
|
+
windowsHide: true,
|
|
5758
|
+
env: childEnv
|
|
5701
5759
|
});
|
|
5702
5760
|
}
|
|
5703
5761
|
return spawn("codex", appServerArgs, {
|
|
5704
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
5762
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
5763
|
+
env: childEnv
|
|
5705
5764
|
});
|
|
5706
5765
|
} catch {
|
|
5707
5766
|
return null;
|
|
@@ -5715,6 +5774,7 @@ ${nextFile.diff}`.trim();
|
|
|
5715
5774
|
logDiagnostic("info", "Starting Codex app-server bridge.", {
|
|
5716
5775
|
platform: process.platform,
|
|
5717
5776
|
execPath: process.execPath,
|
|
5777
|
+
codexHome: resolveCodexEnvironmentHome(),
|
|
5718
5778
|
codexCommandFound: codexCandidates.length > 0,
|
|
5719
5779
|
codexCommandCandidates: codexCandidates,
|
|
5720
5780
|
bridgeExecutable: process.platform === "win32" ? `${process.env.ComSpec || "cmd.exe"} /d /c codex` : "codex"
|
|
@@ -10063,6 +10123,72 @@ var USER_OVERLAY_ENTRY_PREFIX = "overlay-user:";
|
|
|
10063
10123
|
var USER_ENTRY_MATCH_EARLY_SKEW_MS = 5e3;
|
|
10064
10124
|
var TIMELINE_TOOL_SUMMARY_LIMIT = 160;
|
|
10065
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
|
+
};
|
|
10066
10192
|
var WEB_UI_CONTENT_TYPES = {
|
|
10067
10193
|
".css": "text/css; charset=utf-8",
|
|
10068
10194
|
".html": "text/html; charset=utf-8",
|
|
@@ -10082,6 +10208,11 @@ Plan mode is enabled for this message only. Before doing substantial work, creat
|
|
|
10082
10208
|
</user_instructions>
|
|
10083
10209
|
|
|
10084
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
|
+
};
|
|
10085
10216
|
var FALLBACK_CODEX_COMMAND_CATALOG = [
|
|
10086
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" },
|
|
10087
10218
|
{ name: "copy", description: "\u590D\u5236\u6700\u8FD1\u4E00\u6B21\u52A9\u624B\u8F93\u51FA\u3002", availability: "unsupported" },
|
|
@@ -10109,7 +10240,7 @@ var PANDA_SUPPORTED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
10109
10240
|
"status"
|
|
10110
10241
|
]);
|
|
10111
10242
|
var DEFAULT_VISIBLE_CODEX_COMMAND_CONFIG = {
|
|
10112
|
-
version:
|
|
10243
|
+
version: LATEST_VISIBLE_CODEX_COMMAND_CONFIG_VERSION,
|
|
10113
10244
|
visible_commands: [
|
|
10114
10245
|
{
|
|
10115
10246
|
name: "model",
|
|
@@ -10119,6 +10250,7 @@ var DEFAULT_VISIBLE_CODEX_COMMAND_CONFIG = {
|
|
|
10119
10250
|
name: "status",
|
|
10120
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"
|
|
10121
10252
|
},
|
|
10253
|
+
REVIEW_VISIBLE_CODEX_COMMAND_ENTRY,
|
|
10122
10254
|
{
|
|
10123
10255
|
name: "skills",
|
|
10124
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"
|
|
@@ -10362,6 +10494,142 @@ var startPandaSessionService = async ({
|
|
|
10362
10494
|
}
|
|
10363
10495
|
return !relativePath.startsWith("..") && !path9.isAbsolute(relativePath);
|
|
10364
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
|
+
};
|
|
10365
10633
|
const runGitCommand = async (cwd, args, options) => new Promise((resolve, reject) => {
|
|
10366
10634
|
let stdout = "";
|
|
10367
10635
|
let stderr = "";
|
|
@@ -11023,17 +11291,36 @@ var startPandaSessionService = async ({
|
|
|
11023
11291
|
return null;
|
|
11024
11292
|
}
|
|
11025
11293
|
const candidate = value;
|
|
11026
|
-
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) {
|
|
11027
11295
|
return null;
|
|
11028
11296
|
}
|
|
11029
11297
|
if (!Array.isArray(candidate.visible_commands)) {
|
|
11030
11298
|
return null;
|
|
11031
11299
|
}
|
|
11032
11300
|
return {
|
|
11033
|
-
version: 1,
|
|
11301
|
+
version: candidate.version === LATEST_VISIBLE_CODEX_COMMAND_CONFIG_VERSION ? LATEST_VISIBLE_CODEX_COMMAND_CONFIG_VERSION : 1,
|
|
11034
11302
|
visible_commands: candidate.visible_commands.map((entry) => normalizeVisibleCodexCommandConfigEntry(entry)).filter((entry) => Boolean(entry))
|
|
11035
11303
|
};
|
|
11036
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
|
+
};
|
|
11037
11324
|
const writeVisibleCodexCommandConfig = async (config) => {
|
|
11038
11325
|
const configFilePath = await getCodexVisibleCommandsConfigFilePath();
|
|
11039
11326
|
await fs8.writeFile(configFilePath, `${JSON.stringify(config, null, 2)}
|
|
@@ -11045,7 +11332,11 @@ var startPandaSessionService = async ({
|
|
|
11045
11332
|
const raw = await fs8.readFile(configFilePath, "utf8");
|
|
11046
11333
|
const parsed = parseVisibleCodexCommandConfig(JSON.parse(raw));
|
|
11047
11334
|
if (parsed) {
|
|
11048
|
-
|
|
11335
|
+
const migrated = migrateVisibleCodexCommandConfig(parsed);
|
|
11336
|
+
if (migrated !== parsed) {
|
|
11337
|
+
await writeVisibleCodexCommandConfig(migrated);
|
|
11338
|
+
}
|
|
11339
|
+
return migrated;
|
|
11049
11340
|
}
|
|
11050
11341
|
} catch {
|
|
11051
11342
|
}
|
|
@@ -11302,6 +11593,36 @@ var startPandaSessionService = async ({
|
|
|
11302
11593
|
}
|
|
11303
11594
|
return `${trimmed.slice(0, maxLength - 1).trimEnd()}\u2026`;
|
|
11304
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
|
+
};
|
|
11305
11626
|
const createDiagnosticHash2 = (value) => createHash5("sha1").update(value).digest("hex").slice(0, 16);
|
|
11306
11627
|
const summarizeTextForDiagnostics = (value, options) => {
|
|
11307
11628
|
const normalized = typeof value === "string" ? value.trim() : "";
|
|
@@ -11339,6 +11660,7 @@ var startPandaSessionService = async ({
|
|
|
11339
11660
|
dataHash: attachment.dataHash
|
|
11340
11661
|
})),
|
|
11341
11662
|
model: input.model,
|
|
11663
|
+
titleGenerationModel: input.titleGenerationModel ?? null,
|
|
11342
11664
|
reasoningEffort: input.reasoningEffort,
|
|
11343
11665
|
requestedServiceTier: input.requestedServiceTier ?? null,
|
|
11344
11666
|
normalizedRequestedServiceTier: input.normalizedRequestedServiceTier,
|
|
@@ -11352,6 +11674,7 @@ var startPandaSessionService = async ({
|
|
|
11352
11674
|
prompt: promptSummary,
|
|
11353
11675
|
attachments: attachmentsSummary,
|
|
11354
11676
|
model: input.model,
|
|
11677
|
+
titleGenerationModel: input.titleGenerationModel ?? null,
|
|
11355
11678
|
reasoningEffort: input.reasoningEffort,
|
|
11356
11679
|
requestedServiceTier: input.requestedServiceTier === "fast" || input.requestedServiceTier === "flex" ? input.requestedServiceTier : input.requestedServiceTier ?? null,
|
|
11357
11680
|
normalizedRequestedServiceTier: input.normalizedRequestedServiceTier,
|
|
@@ -11564,7 +11887,7 @@ var startPandaSessionService = async ({
|
|
|
11564
11887
|
return nextOverlayEntries;
|
|
11565
11888
|
};
|
|
11566
11889
|
const readTimelineFromRollout = async (sessionId) => {
|
|
11567
|
-
const { readCodexTimeline } = await import("./src-
|
|
11890
|
+
const { readCodexTimeline } = await import("./src-ILWWSZFE.mjs");
|
|
11568
11891
|
return readCodexTimeline(sessionId, {
|
|
11569
11892
|
codexHome,
|
|
11570
11893
|
sessionFiles: discoveredSessionFiles
|
|
@@ -12163,6 +12486,14 @@ var startPandaSessionService = async ({
|
|
|
12163
12486
|
}
|
|
12164
12487
|
return [];
|
|
12165
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
|
+
};
|
|
12166
12497
|
const readPlanForSession = async (sessionId) => {
|
|
12167
12498
|
const existing = sessionPlans.get(sessionId);
|
|
12168
12499
|
if (existing !== void 0) {
|
|
@@ -12194,8 +12525,7 @@ var startPandaSessionService = async ({
|
|
|
12194
12525
|
return [];
|
|
12195
12526
|
};
|
|
12196
12527
|
const readChangeSetFileDiffForSession = async (sessionId, input) => {
|
|
12197
|
-
const
|
|
12198
|
-
const changeSet = changeSets.find((entry) => entry.id === input.changeSetId);
|
|
12528
|
+
const changeSet = await readChangeSetForSession(sessionId, input.changeSetId);
|
|
12199
12529
|
if (!changeSet) {
|
|
12200
12530
|
return null;
|
|
12201
12531
|
}
|
|
@@ -12668,7 +12998,7 @@ var startPandaSessionService = async ({
|
|
|
12668
12998
|
commands: filterCodexCommandsForDisplay(catalog.commands, visibleConfig)
|
|
12669
12999
|
};
|
|
12670
13000
|
};
|
|
12671
|
-
const
|
|
13001
|
+
const renameSession = async (sessionId, nextName) => {
|
|
12672
13002
|
if (managedSessions.has(sessionId)) {
|
|
12673
13003
|
const managedSession = managedSessions.get(sessionId);
|
|
12674
13004
|
managedSessions.set(sessionId, {
|
|
@@ -12693,6 +13023,37 @@ var startPandaSessionService = async ({
|
|
|
12693
13023
|
}
|
|
12694
13024
|
});
|
|
12695
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
|
+
};
|
|
12696
13057
|
const executeSessionCommand = async (session, project, rawInput) => {
|
|
12697
13058
|
const parsed = parseSlashCommandInput(rawInput);
|
|
12698
13059
|
if (!parsed) {
|
|
@@ -12794,7 +13155,7 @@ var startPandaSessionService = async ({
|
|
|
12794
13155
|
}
|
|
12795
13156
|
if (command.name === "rename") {
|
|
12796
13157
|
if (parsed.args) {
|
|
12797
|
-
await
|
|
13158
|
+
await renameSession(session.id, parsed.args);
|
|
12798
13159
|
return createCommandPanel({
|
|
12799
13160
|
sessionId: session.id,
|
|
12800
13161
|
commandName: command.name,
|
|
@@ -12864,7 +13225,7 @@ var startPandaSessionService = async ({
|
|
|
12864
13225
|
if (!nextName) {
|
|
12865
13226
|
throw new Error("\u8BF7\u8F93\u5165\u65B0\u7684\u4F1A\u8BDD\u540D\u79F0\u3002");
|
|
12866
13227
|
}
|
|
12867
|
-
await
|
|
13228
|
+
await renameSession(input.sessionId, nextName);
|
|
12868
13229
|
clearStoredCommandPanel(input.panelId);
|
|
12869
13230
|
return {
|
|
12870
13231
|
...stored.panel,
|
|
@@ -13032,7 +13393,7 @@ var startPandaSessionService = async ({
|
|
|
13032
13393
|
lastSnapshotRefreshAt = Date.now();
|
|
13033
13394
|
return snapshot;
|
|
13034
13395
|
}
|
|
13035
|
-
const { discoverLocalCodexData } = await import("./src-
|
|
13396
|
+
const { discoverLocalCodexData } = await import("./src-ILWWSZFE.mjs");
|
|
13036
13397
|
const discovery = await discoverLocalCodexData({
|
|
13037
13398
|
agentId: localAgentId,
|
|
13038
13399
|
agentName: localAgentName,
|
|
@@ -13108,6 +13469,135 @@ var startPandaSessionService = async ({
|
|
|
13108
13469
|
return snapshot.projects.find((project) => project.id === projectId) ?? (await buildSnapshot({ force: true })).projects.find((project) => project.id === projectId) ?? null;
|
|
13109
13470
|
};
|
|
13110
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
|
+
};
|
|
13111
13601
|
const validateHubControlPlaneAuth = (request) => {
|
|
13112
13602
|
const expectedApiKey = process.env.PANDA_HUB_API_KEY?.trim() ?? "";
|
|
13113
13603
|
if (!expectedApiKey) {
|
|
@@ -13139,6 +13629,58 @@ var startPandaSessionService = async ({
|
|
|
13139
13629
|
}
|
|
13140
13630
|
await runGitCommand(projectPath, ["restore", "--source=HEAD", "--staged", "--worktree", "--", ...targetPaths]);
|
|
13141
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
|
+
};
|
|
13142
13684
|
const discardAllGitWorkspaceChanges = async (projectPath) => {
|
|
13143
13685
|
await runGitCommand(projectPath, ["restore", "--source=HEAD", "--staged", "--worktree", "--", "."]);
|
|
13144
13686
|
await runGitCommand(projectPath, ["clean", "-fd", "--", "."], {
|
|
@@ -13843,6 +14385,107 @@ var startPandaSessionService = async ({
|
|
|
13843
14385
|
}
|
|
13844
14386
|
return diff;
|
|
13845
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
|
+
});
|
|
13846
14489
|
app.get("/api/sessions/:sessionId/git-workspace", async (request, reply) => {
|
|
13847
14490
|
const { sessionId } = request.params;
|
|
13848
14491
|
const session = await findSnapshotSession(sessionId);
|
|
@@ -14543,6 +15186,7 @@ var startPandaSessionService = async ({
|
|
|
14543
15186
|
const input = request.body.input?.trim() ?? "";
|
|
14544
15187
|
const attachments = normalizeSessionInputAttachments(request.body.attachments);
|
|
14545
15188
|
const model = request.body.model?.trim() || null;
|
|
15189
|
+
const titleGenerationModel = request.body.titleGenerationModel?.trim() || DEFAULT_SESSION_TITLE_GENERATION_MODEL;
|
|
14546
15190
|
const reasoningEffort = request.body.reasoningEffort?.trim() || null;
|
|
14547
15191
|
const requestedServiceTier = request.body.serviceTier;
|
|
14548
15192
|
const normalizedRequestedServiceTier = normalizeServiceTier(requestedServiceTier);
|
|
@@ -14571,6 +15215,7 @@ var startPandaSessionService = async ({
|
|
|
14571
15215
|
prompt,
|
|
14572
15216
|
attachments,
|
|
14573
15217
|
model,
|
|
15218
|
+
titleGenerationModel,
|
|
14574
15219
|
reasoningEffort,
|
|
14575
15220
|
requestedServiceTier,
|
|
14576
15221
|
normalizedRequestedServiceTier,
|
|
@@ -14664,6 +15309,14 @@ var startPandaSessionService = async ({
|
|
|
14664
15309
|
managedSessions.set(session.id, session);
|
|
14665
15310
|
upsertSnapshotSession(session);
|
|
14666
15311
|
broadcastSnapshotChanged();
|
|
15312
|
+
void generateSessionTitleInBackground({
|
|
15313
|
+
sessionId: session.id,
|
|
15314
|
+
project,
|
|
15315
|
+
fallbackTitle: title,
|
|
15316
|
+
message: input,
|
|
15317
|
+
attachments,
|
|
15318
|
+
model: titleGenerationModel
|
|
15319
|
+
});
|
|
14667
15320
|
reply.code(201);
|
|
14668
15321
|
return { session };
|
|
14669
15322
|
});
|
|
@@ -14687,29 +15340,7 @@ var startPandaSessionService = async ({
|
|
|
14687
15340
|
reply.code(400);
|
|
14688
15341
|
return { error: "Session name is required." };
|
|
14689
15342
|
}
|
|
14690
|
-
|
|
14691
|
-
const managedSession = managedSessions.get(sessionId);
|
|
14692
|
-
managedSessions.set(sessionId, {
|
|
14693
|
-
...managedSession,
|
|
14694
|
-
title: nextName
|
|
14695
|
-
});
|
|
14696
|
-
}
|
|
14697
|
-
await liveSessionStream.setThreadName(sessionId, nextName).catch(() => {
|
|
14698
|
-
});
|
|
14699
|
-
await appendSessionIndexUpdate(
|
|
14700
|
-
sessionId,
|
|
14701
|
-
{
|
|
14702
|
-
thread_name: nextName
|
|
14703
|
-
},
|
|
14704
|
-
codexHome
|
|
14705
|
-
);
|
|
14706
|
-
patchSnapshotSession(sessionId, { title: nextName });
|
|
14707
|
-
broadcastEvent("session.updated", {
|
|
14708
|
-
sessionId,
|
|
14709
|
-
sessionPatch: {
|
|
14710
|
-
title: nextName
|
|
14711
|
-
}
|
|
14712
|
-
});
|
|
15343
|
+
await renameSession(sessionId, nextName);
|
|
14713
15344
|
return {
|
|
14714
15345
|
ok: true,
|
|
14715
15346
|
affectedSessionIds: [sessionId],
|