sessiongraph-app-darwin-x64 1.0.7 → 1.0.9
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/package.json +2 -2
- package/payload/SessionGraph.app/Contents/Info.plist +2 -2
- package/payload/SessionGraph.app/Contents/MacOS/sessiongraph +0 -0
- package/payload/SessionGraph.app/Contents/Resources/resources/server.mjs +392 -31
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{index-x7CFY4c0.js → index-B5TXKeav.js} +1 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{index-CSlKklgv.js → index-BPR2p22J.js} +1 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{index-CQQQn4As.js → index-CWphmptD.js} +1 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{index-iA3u_0lX.js → index-D-Ue5tsr.js} +30 -28
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{index-vX8x5oFC.css → index-fjc8n25E.css} +1 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/publishing-D6ALMt6w.js +12 -0
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/publishing-DG0jrGoB.css +1 -0
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{scene-BGmgWBAw.js → scene-C2kTsRX_.js} +1 -1
- package/payload/SessionGraph.app/Contents/Resources/resources/web/assets/{share-CLYxMZI8.js → share-BhzLwRnE.js} +3 -3
- package/payload/SessionGraph.app/Contents/Resources/resources/web/index.html +2 -2
- package/payload/SessionGraph.app/Contents/_CodeSignature/CodeResources +54 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sessiongraph-app-darwin-x64",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Explore & visualize all your LLM sessions — darwin/x64",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"access": "public",
|
|
11
11
|
"registry": "https://registry.npmjs.org/"
|
|
12
12
|
},
|
|
13
|
-
"sessionGraphCommit": "
|
|
13
|
+
"sessionGraphCommit": "477079f037f7772fb9c1a55032d887d90a48333f",
|
|
14
14
|
"os": [
|
|
15
15
|
"darwin"
|
|
16
16
|
],
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
<key>CFBundlePackageType</key>
|
|
18
18
|
<string>APPL</string>
|
|
19
19
|
<key>CFBundleShortVersionString</key>
|
|
20
|
-
<string>1.0.
|
|
20
|
+
<string>1.0.9</string>
|
|
21
21
|
<key>CFBundleVersion</key>
|
|
22
|
-
<string>1.0.
|
|
22
|
+
<string>1.0.9</string>
|
|
23
23
|
<key>CSResourcesFileMapped</key>
|
|
24
24
|
<true/>
|
|
25
25
|
<key>LSApplicationCategoryType</key>
|
|
Binary file
|
|
@@ -2140,7 +2140,7 @@ import { createReadStream } from "node:fs";
|
|
|
2140
2140
|
import { stat as stat5 } from "node:fs/promises";
|
|
2141
2141
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
2142
2142
|
import { resolve as resolve2, extname, sep as sep4 } from "node:path";
|
|
2143
|
-
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
2143
|
+
import { randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
2144
2144
|
|
|
2145
2145
|
// node_modules/.pnpm/@opencode+client@2.2.1_effect@4.0.1/node_modules/@opencode/client/dist/chunks/mcp-ve4aws36.js
|
|
2146
2146
|
import { spawn } from "node:child_process";
|
|
@@ -2784,7 +2784,7 @@ function extractContent(messages) {
|
|
|
2784
2784
|
for (const raw of messages) {
|
|
2785
2785
|
if (!raw || typeof raw !== "object") continue;
|
|
2786
2786
|
const message2 = raw;
|
|
2787
|
-
const
|
|
2787
|
+
const time4 = Number(message2.time?.created) || 0;
|
|
2788
2788
|
const seen = /* @__PURE__ */ new Set();
|
|
2789
2789
|
const add = (value, kind) => {
|
|
2790
2790
|
if (typeof value !== "string" || !value.trim() || value.startsWith("data:") || seen.has(value)) return;
|
|
@@ -2794,7 +2794,7 @@ function extractContent(messages) {
|
|
|
2794
2794
|
if (size <= 0) return;
|
|
2795
2795
|
const text2 = value.slice(0, size);
|
|
2796
2796
|
remaining -= text2.length;
|
|
2797
|
-
fragments.push({ text: text2, kind, time:
|
|
2797
|
+
fragments.push({ text: text2, kind, time: time4 });
|
|
2798
2798
|
};
|
|
2799
2799
|
const strings = (value, depth = 0) => {
|
|
2800
2800
|
if (depth > 5 || remaining <= 0) {
|
|
@@ -2873,7 +2873,7 @@ var blankSession = (source, nativeID, directory2 = "") => ({
|
|
|
2873
2873
|
cost: null,
|
|
2874
2874
|
tokens: null
|
|
2875
2875
|
});
|
|
2876
|
-
var blankMessage = (id2,
|
|
2876
|
+
var blankMessage = (id2, time4, role) => ({ id: id2, time: time4, role, fragments: [], files: [], tools: [] });
|
|
2877
2877
|
function addText(message2, value, kind) {
|
|
2878
2878
|
if (typeof value !== "string" || !value.trim() || value.startsWith("data:")) return;
|
|
2879
2879
|
const remaining = Math.max(0, 128e3 - message2.fragments.reduce((n, f) => n + f.text.length, 0));
|
|
@@ -2897,7 +2897,7 @@ function toolText(message2, value, depth = 0) {
|
|
|
2897
2897
|
}
|
|
2898
2898
|
|
|
2899
2899
|
// server/activity.ts
|
|
2900
|
-
var messageTime = (
|
|
2900
|
+
var messageTime = (time4) => Number.isFinite(time4) && time4 > 0 && time4 <= 864e13 ? time4 : null;
|
|
2901
2901
|
var isUserMessage = (message2) => message2.role === "user" && message2.fragments.some((f) => f.kind === "You" && f.text.trim());
|
|
2902
2902
|
var activitySQL = `SELECT min(time) AS firstMessageAt, max(time) AS lastMessageAt,
|
|
2903
2903
|
coalesce(sum(is_user), 0) AS userMessages,
|
|
@@ -2928,8 +2928,8 @@ function eventText(value, limit = 32e3) {
|
|
|
2928
2928
|
}
|
|
2929
2929
|
}
|
|
2930
2930
|
function eventFor(message2, id2, kind, name, text2 = "", fields2 = {}) {
|
|
2931
|
-
const
|
|
2932
|
-
const event = { id: id2, kind, name, role: message2.role, time:
|
|
2931
|
+
const time4 = messageTime(message2.time);
|
|
2932
|
+
const event = { id: id2, kind, name, role: message2.role, time: time4, timeSource: time4 ? "message" : "unknown", text: eventText(text2, 64e3), ...fields2 };
|
|
2933
2933
|
event.partial ||= [event.text, event.input, event.output].some((value) => /\[(shortened|omitted|unavailable)\]/.test(value || "")) || text2.length > 64e3;
|
|
2934
2934
|
return event;
|
|
2935
2935
|
}
|
|
@@ -2997,7 +2997,7 @@ function normalizedEvents(message2, sessionID) {
|
|
|
2997
2997
|
const files = message2.files.map((file, i) => eventFor(message2, `file:${i}`, "file_change", file.path, `${file.source} \xB7 ${file.steps} recorded step(s)`, { nativeType: file.source }));
|
|
2998
2998
|
return [...events, ...files].slice(0, 200).map((event) => {
|
|
2999
2999
|
const text2 = String(event.text || "").slice(0, 64e3), input = event.input?.slice(0, 32e3), output = event.output?.slice(0, 64e3);
|
|
3000
|
-
const
|
|
3000
|
+
const time4 = messageTime(event.time || 0);
|
|
3001
3001
|
return {
|
|
3002
3002
|
...event,
|
|
3003
3003
|
id: digest([message2.id, event.id]),
|
|
@@ -3009,8 +3009,8 @@ function normalizedEvents(message2, sessionID) {
|
|
|
3009
3009
|
callID: event.callID ? String(event.callID).slice(0, 512) : void 0,
|
|
3010
3010
|
parentID: event.parentID ? String(event.parentID).slice(0, 512) : void 0,
|
|
3011
3011
|
endTime: messageTime(event.endTime || 0),
|
|
3012
|
-
time:
|
|
3013
|
-
timeSource:
|
|
3012
|
+
time: time4,
|
|
3013
|
+
timeSource: time4 ? event.timeSource : "unknown",
|
|
3014
3014
|
text: text2,
|
|
3015
3015
|
input,
|
|
3016
3016
|
output,
|
|
@@ -3031,8 +3031,8 @@ function opencodeEvents(raw, message2) {
|
|
|
3031
3031
|
const events = [];
|
|
3032
3032
|
for (const [i, part] of (Array.isArray(raw.content) ? raw.content.slice(0, 200) : []).entries()) {
|
|
3033
3033
|
const id2 = `${part.type}:${part.id || i}`, native = { nativeID: part.id, nativeType: String(part.type) };
|
|
3034
|
-
const
|
|
3035
|
-
const timing =
|
|
3034
|
+
const time4 = messageTime(part.time?.created);
|
|
3035
|
+
const timing = time4 ? { time: time4, timeSource: "recorded" } : {};
|
|
3036
3036
|
if (part.type === "text") events.push(eventFor(message2, id2, "message", "Assistant message", part.text || "", { ...native, ...timing }));
|
|
3037
3037
|
else if (part.type === "reasoning") events.push(eventFor(message2, id2, "reasoning", "Recorded reasoning", part.text || "", { ...native, ...timing }));
|
|
3038
3038
|
else if (part.type === "tool") {
|
|
@@ -30449,8 +30449,8 @@ async function claudeTranscript(path, revision2) {
|
|
|
30449
30449
|
}
|
|
30450
30450
|
entries2.push(row);
|
|
30451
30451
|
if (typeof row.uuid === "string") {
|
|
30452
|
-
const
|
|
30453
|
-
times.set(row.uuid, Number.isFinite(
|
|
30452
|
+
const time4 = typeof row.timestamp === "string" ? Date.parse(row.timestamp) : NaN;
|
|
30453
|
+
times.set(row.uuid, Number.isFinite(time4) && time4 > 0 ? time4 : 0);
|
|
30454
30454
|
}
|
|
30455
30455
|
} catch {
|
|
30456
30456
|
partial = true;
|
|
@@ -30482,7 +30482,7 @@ function claudeTranscriptInfo(transcript, sessionId) {
|
|
|
30482
30482
|
}).find(Boolean);
|
|
30483
30483
|
const title = rows2.findLast((row) => ["custom-title", "ai-title", "summary"].includes(String(row.type)) && typeof (row.customTitle || row.aiTitle || row.summary) === "string");
|
|
30484
30484
|
const cwd2 = rows2.findLast((row) => row.type === "relocated" && typeof row.relocatedCwd === "string")?.relocatedCwd || messages.find((row) => typeof row.cwd === "string")?.cwd;
|
|
30485
|
-
const createdAt = messages.map((row) => transcript.times.get(String(row.uuid))).find((
|
|
30485
|
+
const createdAt = messages.map((row) => transcript.times.get(String(row.uuid))).find((time4) => time4 && time4 > 0);
|
|
30486
30486
|
return { sessionId, summary: String(title?.customTitle || title?.aiTitle || title?.summary || firstPrompt || "Claude Code session"), firstPrompt, cwd: typeof cwd2 === "string" ? cwd2 : void 0, createdAt, lastModified: transcript.modified, fileSize: transcript.size };
|
|
30487
30487
|
}
|
|
30488
30488
|
function claudeTranscriptStore(transcript, sessionId, agent) {
|
|
@@ -31908,8 +31908,8 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
31908
31908
|
|
|
31909
31909
|
// server/adapters/cursor-message.ts
|
|
31910
31910
|
function cursorTime(value) {
|
|
31911
|
-
const
|
|
31912
|
-
return Number.isFinite(
|
|
31911
|
+
const time4 = typeof value === "number" ? value : typeof value === "string" && /(?:Z|[+-]\d\d:\d\d)$/.test(value) ? Date.parse(value) : 0;
|
|
31912
|
+
return Number.isFinite(time4) && time4 > 0 && time4 <= 864e13 ? time4 : 0;
|
|
31913
31913
|
}
|
|
31914
31914
|
function cursorEnvelopeTime(text2) {
|
|
31915
31915
|
const value = text2.match(/^\s*<timestamp>([^<]+)<\/timestamp>/)?.[1];
|
|
@@ -31937,9 +31937,9 @@ function cursorMessage(raw, fallbackID) {
|
|
|
31937
31937
|
if (!["user", "assistant", "tool", "system"].includes(role)) return;
|
|
31938
31938
|
const content = body2.content;
|
|
31939
31939
|
const firstText = typeof content === "string" ? content : Array.isArray(content) && content[0]?.type === "text" ? content[0].text : "";
|
|
31940
|
-
const
|
|
31940
|
+
const time4 = cursorTime(raw.timestamp) || cursorTime(body2.timestamp) || cursorTime(body2.createdAt) || (role === "user" && typeof firstText === "string" ? cursorEnvelopeTime(firstText) : 0);
|
|
31941
31941
|
const native = [raw.uuid, raw.id, body2.uuid, body2.id].find((id2) => typeof id2 === "string" && id2.length && id2.length <= 512);
|
|
31942
|
-
const msg = blankMessage(native || fallbackID,
|
|
31942
|
+
const msg = blankMessage(native || fallbackID, time4, role);
|
|
31943
31943
|
msg.events = role === "tool" ? [eventFor(msg, "result", "tool_result", body2.name || "Tool result", "", { callID: body2.tool_call_id, output: eventText(content, 64e3), status: "unknown" })] : blockEvents(msg, content);
|
|
31944
31944
|
const kind = role === "user" ? "You" : role === "assistant" ? "Assistant" : "Tool";
|
|
31945
31945
|
const text2 = (value) => addText(msg, role === "user" && typeof value === "string" ? cursorUserText(value) : value, kind);
|
|
@@ -33675,7 +33675,7 @@ function readEvents(db2, session, options = {}) {
|
|
|
33675
33675
|
};
|
|
33676
33676
|
}
|
|
33677
33677
|
|
|
33678
|
-
// src/preferences.ts
|
|
33678
|
+
// packages/core/src/preferences.ts
|
|
33679
33679
|
var DEFAULT_PREFERENCES = Object.freeze({
|
|
33680
33680
|
autoImport: true,
|
|
33681
33681
|
muted: false,
|
|
@@ -33717,7 +33717,7 @@ function normalizePreferences(value) {
|
|
|
33717
33717
|
return result;
|
|
33718
33718
|
}
|
|
33719
33719
|
|
|
33720
|
-
// src/view-state.ts
|
|
33720
|
+
// packages/core/src/view-state.ts
|
|
33721
33721
|
var DEFAULT_VIEW_STATE = Object.freeze({
|
|
33722
33722
|
sourceID: "",
|
|
33723
33723
|
projectID: "",
|
|
@@ -33913,12 +33913,12 @@ var SessionGraphStore = class {
|
|
|
33913
33913
|
const count = this.db.prepare("SELECT COUNT(*) AS n FROM sessions WHERE source=?").get(id2);
|
|
33914
33914
|
return { sessions: Number(count.n), lastImport: row?.last_import ? Number(row.last_import) : void 0, error: row?.error ? String(row.error) : void 0 };
|
|
33915
33915
|
}
|
|
33916
|
-
library(
|
|
33916
|
+
library(sources2, importing) {
|
|
33917
33917
|
return {
|
|
33918
33918
|
sourceID: this.setting("libraryID"),
|
|
33919
33919
|
onboarding: this.setting("selected") === void 0,
|
|
33920
33920
|
autoImport: this.autoImportEnabled(),
|
|
33921
|
-
sources,
|
|
33921
|
+
sources: sources2,
|
|
33922
33922
|
importing,
|
|
33923
33923
|
preferences: this.setting("preferences") ? { ...normalizePreferences(this.setting("preferences")), autoImport: this.autoImportEnabled() } : void 0,
|
|
33924
33924
|
viewState: normalizeViewState(this.setting("viewState")),
|
|
@@ -34305,14 +34305,14 @@ function handoffDocument(snapshot2) {
|
|
|
34305
34305
|
${text3.slice(-half)}`;
|
|
34306
34306
|
};
|
|
34307
34307
|
const block = (message2, limit) => {
|
|
34308
|
-
const
|
|
34308
|
+
const time4 = messageTime(message2.time) ? new Date(message2.time).toISOString() : "time unavailable";
|
|
34309
34309
|
const content = message2.fragments.map((f) => `[${f.kind}]
|
|
34310
34310
|
${clip(f.text, f.kind === "Tool" ? 2e3 : 12e3)}`).join("\n\n");
|
|
34311
34311
|
const files = message2.files.length ? `
|
|
34312
34312
|
|
|
34313
34313
|
Recorded file changes: ${message2.files.slice(0, 60).map((f) => JSON.stringify(f.path)).join(", ")}` : "";
|
|
34314
34314
|
shortened ||= message2.files.length > 60 || Boolean(message2.partial);
|
|
34315
|
-
return `## ${message2.role} \xB7 ${
|
|
34315
|
+
return `## ${message2.role} \xB7 ${time4}
|
|
34316
34316
|
|
|
34317
34317
|
${clip(content + files, limit)}
|
|
34318
34318
|
`;
|
|
@@ -34441,7 +34441,349 @@ async function launchTerminal(session, prepared) {
|
|
|
34441
34441
|
} else await exec(invocation.file, invocation.args, { timeout: 15e3, maxBuffer: 16384 });
|
|
34442
34442
|
}
|
|
34443
34443
|
|
|
34444
|
+
// server/publishing.ts
|
|
34445
|
+
import { createHash as createHash5, randomBytes } from "node:crypto";
|
|
34446
|
+
import { chmod, mkdir as mkdir2, readFile as readFile5, rename as rename3, rm as rm5, writeFile as writeFile3 } from "node:fs/promises";
|
|
34447
|
+
import { dirname as dirname10, join as join25 } from "node:path";
|
|
34448
|
+
|
|
34449
|
+
// packages/core/src/layout.ts
|
|
34450
|
+
var directoryPath = (directory2) => (/^(?:[a-z]:[\\/]|\\\\)/i.test(directory2) ? directory2.replace(/\\/g, "/") : directory2).replace(/\/+$/, "") || (directory2.startsWith("/") ? "/" : "");
|
|
34451
|
+
var laneKey = (session) => session.directory ? JSON.stringify(["directory", directoryPath(session.directory)]) : JSON.stringify(["project", session.source || "opencode", session.projectID || session.id]);
|
|
34452
|
+
|
|
34453
|
+
// packages/core/src/sources.ts
|
|
34454
|
+
var sources = {
|
|
34455
|
+
opencode: { name: "OpenCode", icon: "opencode" },
|
|
34456
|
+
"claude-code": { name: "Claude Code", icon: "claude" },
|
|
34457
|
+
codex: { name: "Codex", icon: "openai" },
|
|
34458
|
+
"t3-code": { name: "T3 Code", icon: "t3" },
|
|
34459
|
+
cursor: { name: "Cursor", icon: "cursor" },
|
|
34460
|
+
gemini: { name: "Gemini CLI", icon: "gemini" },
|
|
34461
|
+
"qwen-code": { name: "Qwen Code", icon: "qwen" },
|
|
34462
|
+
pi: { name: "Pi", icon: "pi" },
|
|
34463
|
+
copilot: { name: "GitHub Copilot CLI", icon: "copilot" },
|
|
34464
|
+
"copilot-chat": { name: "GitHub Copilot Chat (VS Code)", icon: "copilot" },
|
|
34465
|
+
cline: { name: "Cline", icon: "cline" },
|
|
34466
|
+
"roo-code": { name: "Roo Code", icon: "roo" },
|
|
34467
|
+
"kilo-code": { name: "Kilo Code", icon: "kilo" },
|
|
34468
|
+
continue: { name: "Continue", icon: "continue" },
|
|
34469
|
+
"prime-agent": { name: "Prime Agent", icon: "prime" },
|
|
34470
|
+
omp: { name: "Oh My Pi", icon: "pi" },
|
|
34471
|
+
senpi: { name: "Senpi / OmO Native", icon: "senpi" },
|
|
34472
|
+
kimchi: { name: "Kimchi", icon: "kimchi" },
|
|
34473
|
+
gajae: { name: "Gajae-Code", icon: "gajae" },
|
|
34474
|
+
openclaw: { name: "OpenClaw", icon: "openclaw" },
|
|
34475
|
+
hermes: { name: "Hermes Agent", icon: "hermes" },
|
|
34476
|
+
amp: { name: "Amp", icon: "amp" },
|
|
34477
|
+
droid: { name: "Factory Droid", icon: "droid" },
|
|
34478
|
+
codebuff: { name: "Codebuff", icon: "codebuff" },
|
|
34479
|
+
freebuff: { name: "Freebuff", icon: "freebuff" },
|
|
34480
|
+
kimi: { name: "Kimi CLI / Code", icon: "kimi" },
|
|
34481
|
+
grok: { name: "Grok Build", icon: "grok" },
|
|
34482
|
+
jcode: { name: "Jcode", icon: "jcode" },
|
|
34483
|
+
"cherry-studio": { name: "Cherry Studio", icon: "cherry" },
|
|
34484
|
+
"command-code": { name: "Command Code", icon: "commandcode" },
|
|
34485
|
+
zcode: { name: "ZCode", icon: "zcode" },
|
|
34486
|
+
codebuddy: { name: "CodeBuddy", icon: "codebuddy" },
|
|
34487
|
+
workbuddy: { name: "WorkBuddy", icon: "codebuddy" },
|
|
34488
|
+
proma: { name: "Proma", icon: "proma" },
|
|
34489
|
+
"deepseek-harness": { name: "DeepSeek Harness", icon: "deepseek" },
|
|
34490
|
+
aside: { name: "Aside", icon: "aside" },
|
|
34491
|
+
vibe: { name: "Mistral Vibe", icon: "mistral" },
|
|
34492
|
+
muse: { name: "Muse Code", icon: "muse" },
|
|
34493
|
+
junie: { name: "Junie", icon: "junie" },
|
|
34494
|
+
augment: { name: "Augment Code", icon: "augment" },
|
|
34495
|
+
fx: { name: "Fx", icon: "fx" },
|
|
34496
|
+
"minimax-code": { name: "MiniMax Code", icon: "minimax" },
|
|
34497
|
+
opencodereview: { name: "OpenCodeReview", icon: "opencode" },
|
|
34498
|
+
goose: { name: "Goose", icon: "goose" },
|
|
34499
|
+
zed: { name: "Zed Agent", icon: "zed" },
|
|
34500
|
+
devin: { name: "Devin CLI", icon: "devin" },
|
|
34501
|
+
"devin-desktop": { name: "Devin Desktop", icon: "devin" },
|
|
34502
|
+
crush: { name: "Crush", icon: "crush" },
|
|
34503
|
+
octofriend: { name: "Octofriend", icon: "octofriend" },
|
|
34504
|
+
"kilo-cli": { name: "Kilo CLI", icon: "kilo" },
|
|
34505
|
+
"mimo-code": { name: "MiMo Code", icon: "mimo" },
|
|
34506
|
+
"mimo-desktop": { name: "Xiaomi MiMo AI", icon: "mimo" },
|
|
34507
|
+
unsloth: { name: "Unsloth Studio", icon: "unsloth" },
|
|
34508
|
+
kiro: { name: "Kiro", icon: "kiro" },
|
|
34509
|
+
mux: { name: "Mux", icon: "mux" },
|
|
34510
|
+
antigravity: { name: "Antigravity", icon: "antigravity" },
|
|
34511
|
+
"antigravity-cli": { name: "Antigravity CLI", icon: "antigravity" },
|
|
34512
|
+
"antigravity-extension": { name: "Antigravity IDE Extension", icon: "antigravity" },
|
|
34513
|
+
reasonix: { name: "Reasonix", icon: "reasonix" },
|
|
34514
|
+
trae: { name: "Trae / Trae Solo", icon: "trae" },
|
|
34515
|
+
warp: { name: "Warp / Oz", icon: "warp" },
|
|
34516
|
+
"lm-studio": { name: "LM Studio", icon: "lmstudio" },
|
|
34517
|
+
hindsight: { name: "Hindsight", icon: "hindsight" }
|
|
34518
|
+
};
|
|
34519
|
+
var isKnownSource = (id2) => typeof id2 === "string" && Object.hasOwn(sources, id2);
|
|
34520
|
+
|
|
34521
|
+
// packages/core/src/public-graph.ts
|
|
34522
|
+
var DEFAULT_PUBLISHING = { tokens: true, timestamps: true, messageCounts: true, messageTimestamps: true, model: true, harness: true };
|
|
34523
|
+
function publishingSettings(value) {
|
|
34524
|
+
const result = { ...DEFAULT_PUBLISHING };
|
|
34525
|
+
if (object2(value)) {
|
|
34526
|
+
for (const key of Object.keys(result)) if (typeof value[key] === "boolean") result[key] = value[key];
|
|
34527
|
+
}
|
|
34528
|
+
if (!result.timestamps || !result.messageCounts) result.messageTimestamps = false;
|
|
34529
|
+
return result;
|
|
34530
|
+
}
|
|
34531
|
+
var PUBLIC_GRAPH_MAX_SESSIONS = 1e4;
|
|
34532
|
+
var PUBLIC_GRAPH_MAX_BYTES = 2e6;
|
|
34533
|
+
var PUBLIC_GRAPH_MAX_MESSAGE_TIMES = 1e5;
|
|
34534
|
+
var modelPattern = /^(?:gpt-[3-6](?:\.[0-9])?(?:-(?:turbo|mini|nano|pro|codex|astra|instant|thinking|chat|latest|preview|[0-9]{4,8}))*|o[1-4](?:-(?:mini|pro|preview|[0-9]{4,8}))*|claude-(?:(?:sonnet|opus|haiku)-[3-5](?:[.-][0-9])?|[3-5](?:-[0-9])?-(?:sonnet|opus|haiku))(?:-(?:latest|thinking|[0-9]{8}))*|gemini-[1-4](?:\.[0-9])?-(?:pro|flash)(?:-(?:lite|preview|exp|latest|[0-9]{2,4}))*|deepseek-(?:chat|reasoner|v[3-4](?:\.[0-9])?|r1)|grok-[2-5](?:\.[0-9])?(?:-(?:fast|code|reasoning|non-reasoning|[0-9]))*|kimi-k[1-3](?:\.[0-9])?(?:-thinking)?|minimax-m[1-3](?:\.[0-9])?)$/i;
|
|
34535
|
+
var publicModel = (value) => {
|
|
34536
|
+
const name = value.split("/").at(-1) || "";
|
|
34537
|
+
return name.length <= 100 && modelPattern.test(name) ? name.toLowerCase() : null;
|
|
34538
|
+
};
|
|
34539
|
+
function publishableGraph(sessions, settings, messages = /* @__PURE__ */ new Map()) {
|
|
34540
|
+
const fields2 = publishingSettings(settings);
|
|
34541
|
+
const ordered = [...sessions].sort((a, b) => a.created - b.created || a.id.localeCompare(b.id));
|
|
34542
|
+
const ids = new Map(ordered.map((session, index) => [session.id, index]));
|
|
34543
|
+
const groups = /* @__PURE__ */ new Map();
|
|
34544
|
+
return validatePublicGraph({ version: 1, settings: fields2, sessions: ordered.map((session) => {
|
|
34545
|
+
const key = laneKey(session);
|
|
34546
|
+
if (!groups.has(key)) groups.set(key, groups.size);
|
|
34547
|
+
const result = {
|
|
34548
|
+
id: ids.get(session.id),
|
|
34549
|
+
group: groups.get(key),
|
|
34550
|
+
parent: session.parentID ? ids.get(session.parentID) ?? null : null,
|
|
34551
|
+
fork: session.forkID ? ids.get(session.forkID) ?? null : null
|
|
34552
|
+
};
|
|
34553
|
+
if (fields2.timestamps) result.timestamps = { created: session.created, lastMessageAt: session.activity?.lastMessageAt ?? null };
|
|
34554
|
+
if (fields2.tokens) result.tokens = session.tokens;
|
|
34555
|
+
const stats = messages.get(session.id);
|
|
34556
|
+
if (fields2.messageCounts) result.messages = { count: stats?.count ?? null, untimed: stats?.untimed ?? null, partial: stats?.partial ?? true };
|
|
34557
|
+
if (fields2.messageTimestamps) result.messageTimes = { values: stats?.times ?? [], limited: stats?.timesLimited ?? true };
|
|
34558
|
+
if (fields2.model) result.model = publicModel(session.model);
|
|
34559
|
+
if (fields2.harness) result.harness = isKnownSource(session.source) ? session.source : null;
|
|
34560
|
+
return result;
|
|
34561
|
+
}) });
|
|
34562
|
+
}
|
|
34563
|
+
var object2 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
34564
|
+
var exact = (value, keys) => object2(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
34565
|
+
var integer = (value, max = Number.MAX_SAFE_INTEGER) => Number.isSafeInteger(value) && value >= 0 && value <= max;
|
|
34566
|
+
var time3 = (value) => integer(value, Date.UTC(2100, 0, 1));
|
|
34567
|
+
var nullableCount = (value) => value === null || integer(value);
|
|
34568
|
+
function validatePublicGraph(value) {
|
|
34569
|
+
const fail2 = () => {
|
|
34570
|
+
throw new Error("Invalid anonymous graph. Reopen the publishing preview and try again.");
|
|
34571
|
+
};
|
|
34572
|
+
if (!exact(value, ["version", "settings", "sessions"]) || value.version !== 1 || !Array.isArray(value.sessions)) return fail2();
|
|
34573
|
+
if (!exact(value.settings, Object.keys(DEFAULT_PUBLISHING)) || Object.values(value.settings).some((v3) => typeof v3 !== "boolean")) return fail2();
|
|
34574
|
+
const settings = value.settings;
|
|
34575
|
+
if (settings.messageTimestamps && (!settings.timestamps || !settings.messageCounts)) return fail2();
|
|
34576
|
+
if (value.sessions.length < 1 || value.sessions.length > PUBLIC_GRAPH_MAX_SESSIONS) throw new Error(`Choose between 1 and ${PUBLIC_GRAPH_MAX_SESSIONS.toLocaleString("en")} sessions to publish.`);
|
|
34577
|
+
const keys = ["id", "group", "parent", "fork", ...settings.timestamps ? ["timestamps"] : [], ...settings.tokens ? ["tokens"] : [], ...settings.messageCounts ? ["messages"] : [], ...settings.messageTimestamps ? ["messageTimes"] : [], ...settings.model ? ["model"] : [], ...settings.harness ? ["harness"] : []];
|
|
34578
|
+
let times = 0;
|
|
34579
|
+
for (let index = 0; index < value.sessions.length; index++) {
|
|
34580
|
+
const s = value.sessions[index];
|
|
34581
|
+
if (!exact(s, keys) || s.id !== index || !integer(s.group, value.sessions.length - 1)) return fail2();
|
|
34582
|
+
for (const key of ["parent", "fork"]) if (s[key] !== null && (!integer(s[key], value.sessions.length - 1) || s[key] === index)) return fail2();
|
|
34583
|
+
if (settings.timestamps && (!exact(s.timestamps, ["created", "lastMessageAt"]) || !time3(s.timestamps.created) || s.timestamps.lastMessageAt !== null && !time3(s.timestamps.lastMessageAt))) return fail2();
|
|
34584
|
+
if (settings.tokens && !nullableCount(s.tokens)) return fail2();
|
|
34585
|
+
if (settings.messageCounts && (!exact(s.messages, ["count", "untimed", "partial"]) || !nullableCount(s.messages.count) || !nullableCount(s.messages.untimed) || typeof s.messages.partial !== "boolean")) return fail2();
|
|
34586
|
+
if (settings.messageTimestamps) {
|
|
34587
|
+
if (!exact(s.messageTimes, ["values", "limited"]) || !Array.isArray(s.messageTimes.values) || s.messageTimes.values.length > 2e3 || typeof s.messageTimes.limited !== "boolean" || s.messageTimes.values.some((v3) => !time3(v3))) return fail2();
|
|
34588
|
+
times += s.messageTimes.values.length;
|
|
34589
|
+
if (times > PUBLIC_GRAPH_MAX_MESSAGE_TIMES) return fail2();
|
|
34590
|
+
}
|
|
34591
|
+
if (settings.model && s.model !== null && (typeof s.model !== "string" || publicModel(s.model) !== s.model)) return fail2();
|
|
34592
|
+
if (settings.harness && s.harness !== null && (typeof s.harness !== "string" || !isKnownSource(s.harness))) return fail2();
|
|
34593
|
+
}
|
|
34594
|
+
const sessions = value.sessions;
|
|
34595
|
+
const colors = new Uint8Array(sessions.length);
|
|
34596
|
+
for (let i = 0; i < sessions.length; i++) {
|
|
34597
|
+
if (colors[i]) continue;
|
|
34598
|
+
const stack = [[i, false]];
|
|
34599
|
+
while (stack.length) {
|
|
34600
|
+
const [id2, exit] = stack.pop();
|
|
34601
|
+
if (exit) {
|
|
34602
|
+
colors[id2] = 2;
|
|
34603
|
+
continue;
|
|
34604
|
+
}
|
|
34605
|
+
if (colors[id2] === 1) return fail2();
|
|
34606
|
+
if (colors[id2] === 2) continue;
|
|
34607
|
+
colors[id2] = 1;
|
|
34608
|
+
stack.push([id2, true]);
|
|
34609
|
+
for (const parent2 of [sessions[id2].parent, sessions[id2].fork]) if (parent2 !== null) stack.push([parent2, false]);
|
|
34610
|
+
}
|
|
34611
|
+
}
|
|
34612
|
+
if (new TextEncoder().encode(JSON.stringify(value)).length > PUBLIC_GRAPH_MAX_BYTES) throw new Error("This graph is too large. Choose a smaller date range.");
|
|
34613
|
+
return value;
|
|
34614
|
+
}
|
|
34615
|
+
|
|
34616
|
+
// server/publishing.ts
|
|
34617
|
+
var PublishingError = class extends Error {
|
|
34618
|
+
constructor(message2, status2 = 400) {
|
|
34619
|
+
super(message2);
|
|
34620
|
+
this.status = status2;
|
|
34621
|
+
}
|
|
34622
|
+
status;
|
|
34623
|
+
};
|
|
34624
|
+
var SITE = "https://sessiongraph.ai";
|
|
34625
|
+
var hash = (value) => createHash5("sha256").update(value).digest("hex");
|
|
34626
|
+
var token = () => randomBytes(32).toString("base64url");
|
|
34627
|
+
function preparePublicGraph(store, ids, inputSettings) {
|
|
34628
|
+
if (!Array.isArray(ids) || !ids.length || ids.length > PUBLIC_GRAPH_MAX_SESSIONS || ids.some((id2) => typeof id2 !== "string") || new Set(ids).size !== ids.length) throw new PublishingError("Choose a valid set of sessions.");
|
|
34629
|
+
const settings = publishingSettings(inputSettings);
|
|
34630
|
+
const stats = /* @__PURE__ */ new Map();
|
|
34631
|
+
const count = store.db.prepare("SELECT count(*) AS count, count(*) FILTER (WHERE time IS NULL) AS untimed FROM timeline_items WHERE session_id=? AND kind='message' AND role IN ('user','assistant')");
|
|
34632
|
+
const times = store.db.prepare("SELECT time FROM timeline_items WHERE session_id=? AND kind='message' AND role IN ('user','assistant') AND time IS NOT NULL ORDER BY time,ordinal LIMIT ?");
|
|
34633
|
+
const coverage2 = store.db.prepare("SELECT partial FROM timeline_coverage WHERE session_id=?");
|
|
34634
|
+
let remaining = PUBLIC_GRAPH_MAX_MESSAGE_TIMES;
|
|
34635
|
+
store.db.exec("BEGIN");
|
|
34636
|
+
try {
|
|
34637
|
+
const sessions = ids.map((id2) => {
|
|
34638
|
+
const session = store.getSession(id2);
|
|
34639
|
+
if (!session) throw new PublishingError("Your session selection changed. Reopen publishing.");
|
|
34640
|
+
if (settings.messageCounts) {
|
|
34641
|
+
const known = session.coverage?.history !== "pending" && !!coverage2.get(id2);
|
|
34642
|
+
const totals = count.get(id2);
|
|
34643
|
+
const rows2 = settings.messageTimestamps ? times.all(id2, Math.min(remaining, 2e3)) : [];
|
|
34644
|
+
remaining -= rows2.length;
|
|
34645
|
+
const values = rows2.map((row) => Number(row.time));
|
|
34646
|
+
stats.set(id2, {
|
|
34647
|
+
count: known ? Number(totals.count) : null,
|
|
34648
|
+
untimed: known ? Number(totals.untimed) : null,
|
|
34649
|
+
partial: !known || session.coverage?.history !== "complete" || Boolean(coverage2.get(id2)?.partial),
|
|
34650
|
+
times: values,
|
|
34651
|
+
timesLimited: !known || Number(totals.count) - Number(totals.untimed) > values.length
|
|
34652
|
+
});
|
|
34653
|
+
}
|
|
34654
|
+
return session;
|
|
34655
|
+
});
|
|
34656
|
+
const result = publishableGraph(sessions, settings, stats);
|
|
34657
|
+
store.db.exec("COMMIT");
|
|
34658
|
+
return result;
|
|
34659
|
+
} catch (error) {
|
|
34660
|
+
store.db.exec("ROLLBACK");
|
|
34661
|
+
throw error;
|
|
34662
|
+
}
|
|
34663
|
+
}
|
|
34664
|
+
var PublishingClient = class {
|
|
34665
|
+
constructor(credentialPath = join25(dirname10(databasePath()), "account.json"), fetcher = fetch) {
|
|
34666
|
+
this.credentialPath = credentialPath;
|
|
34667
|
+
this.fetcher = fetcher;
|
|
34668
|
+
}
|
|
34669
|
+
credentialPath;
|
|
34670
|
+
fetcher;
|
|
34671
|
+
pending;
|
|
34672
|
+
preview;
|
|
34673
|
+
busy = false;
|
|
34674
|
+
async exclusive(work) {
|
|
34675
|
+
if (this.busy) throw new PublishingError("Wait for the current account operation to finish.", 409);
|
|
34676
|
+
this.busy = true;
|
|
34677
|
+
try {
|
|
34678
|
+
return await work();
|
|
34679
|
+
} finally {
|
|
34680
|
+
this.busy = false;
|
|
34681
|
+
}
|
|
34682
|
+
}
|
|
34683
|
+
async credential() {
|
|
34684
|
+
try {
|
|
34685
|
+
const saved = JSON.parse(await readFile5(this.credentialPath, "utf8"));
|
|
34686
|
+
if (typeof saved.token !== "string" || !/^[\w-]{43}$/.test(saved.token) || typeof saved.username !== "string" || !Number.isFinite(Date.parse(saved.expiresAt)) || Date.parse(saved.expiresAt) <= Date.now()) return null;
|
|
34687
|
+
return saved;
|
|
34688
|
+
} catch (error) {
|
|
34689
|
+
if (error.code === "ENOENT") return null;
|
|
34690
|
+
throw new PublishingError("Could not read your saved login. Sign out and try again.");
|
|
34691
|
+
}
|
|
34692
|
+
}
|
|
34693
|
+
async save(value) {
|
|
34694
|
+
await mkdir2(dirname10(this.credentialPath), { recursive: true, mode: 448 });
|
|
34695
|
+
const temporary = `${this.credentialPath}.${randomBytes(8).toString("hex")}`;
|
|
34696
|
+
try {
|
|
34697
|
+
await writeFile3(temporary, JSON.stringify(value), { mode: 384, flag: "wx" });
|
|
34698
|
+
await rename3(temporary, this.credentialPath);
|
|
34699
|
+
await chmod(this.credentialPath, 384);
|
|
34700
|
+
} finally {
|
|
34701
|
+
await rm5(temporary, { force: true });
|
|
34702
|
+
}
|
|
34703
|
+
}
|
|
34704
|
+
async call(path, method = "GET", value, credential) {
|
|
34705
|
+
const response = await this.fetcher(`${SITE}/api/${path}`, { method, redirect: "error", signal: AbortSignal.timeout(25e3), headers: { "Content-Type": "application/json", "x-sessiongraph-action": "1", ...credential ? { Authorization: `Bearer ${credential.token}` } : {} }, ...value === void 0 ? {} : { body: JSON.stringify(value) } });
|
|
34706
|
+
const data = await response.json().catch(() => ({}));
|
|
34707
|
+
if (!response.ok) {
|
|
34708
|
+
if (response.status === 401) await rm5(this.credentialPath, { force: true });
|
|
34709
|
+
throw new PublishingError(typeof data.error === "string" ? data.error : "The publishing service is unavailable.", response.status);
|
|
34710
|
+
}
|
|
34711
|
+
return data;
|
|
34712
|
+
}
|
|
34713
|
+
async status() {
|
|
34714
|
+
const credential = await this.credential();
|
|
34715
|
+
if (!credential) return { username: null, publication: null };
|
|
34716
|
+
return this.call("account", "GET", void 0, credential);
|
|
34717
|
+
}
|
|
34718
|
+
login() {
|
|
34719
|
+
if (this.busy) throw new PublishingError("Wait for the current account operation to finish.", 409);
|
|
34720
|
+
const verifier = token(), challenge = hash(verifier);
|
|
34721
|
+
this.pending = { verifier, expires: Date.now() + 6e5, nextPoll: 0 };
|
|
34722
|
+
return { url: `${SITE}/account?device=${challenge}`, code: challenge.slice(0, 8).toUpperCase() };
|
|
34723
|
+
}
|
|
34724
|
+
async poll() {
|
|
34725
|
+
const pending2 = this.pending;
|
|
34726
|
+
if (!pending2 || pending2.expires <= Date.now()) {
|
|
34727
|
+
this.pending = void 0;
|
|
34728
|
+
throw new PublishingError("Login expired. Start again.");
|
|
34729
|
+
}
|
|
34730
|
+
if (this.busy || pending2.nextPoll > Date.now()) return { pending: true };
|
|
34731
|
+
pending2.nextPoll = Date.now() + 3e3;
|
|
34732
|
+
return this.exclusive(async () => {
|
|
34733
|
+
const result = await this.call("auth/device", "POST", { verifier: pending2.verifier });
|
|
34734
|
+
if (this.pending !== pending2 || result.pending) return { pending: true };
|
|
34735
|
+
if (typeof result.token !== "string" || !/^[\w-]{43}$/.test(result.token) || typeof result.username !== "string" || !Number.isFinite(Date.parse(result.expiresAt))) throw new PublishingError("Invalid login response.");
|
|
34736
|
+
await this.save(result);
|
|
34737
|
+
this.pending = void 0;
|
|
34738
|
+
return { username: result.username };
|
|
34739
|
+
});
|
|
34740
|
+
}
|
|
34741
|
+
prepare(store, ids, settings) {
|
|
34742
|
+
this.preview = void 0;
|
|
34743
|
+
let graph;
|
|
34744
|
+
try {
|
|
34745
|
+
graph = preparePublicGraph(store, ids, settings);
|
|
34746
|
+
} catch (error) {
|
|
34747
|
+
if (error instanceof PublishingError) throw error;
|
|
34748
|
+
throw new PublishingError(error instanceof Error ? error.message : "Could not prepare the public graph.");
|
|
34749
|
+
}
|
|
34750
|
+
const id2 = token();
|
|
34751
|
+
this.preview = { id: id2, graph, expires: Date.now() + 18e5 };
|
|
34752
|
+
store.setting("publishing", graph.settings);
|
|
34753
|
+
return { id: id2, graph };
|
|
34754
|
+
}
|
|
34755
|
+
async publish(previewID, confirmed) {
|
|
34756
|
+
return this.exclusive(async () => {
|
|
34757
|
+
const preview = this.preview;
|
|
34758
|
+
if (confirmed !== true || !preview || preview.id !== previewID || preview.expires < Date.now()) throw new PublishingError("Review a fresh preview before publishing.");
|
|
34759
|
+
const credential = await this.credential();
|
|
34760
|
+
if (!credential) throw new PublishingError("Sign in before publishing.", 401);
|
|
34761
|
+
const result = await this.call("publication", "PUT", { confirmed: true, graph: preview.graph }, credential);
|
|
34762
|
+
if (this.preview === preview) this.preview = void 0;
|
|
34763
|
+
return result;
|
|
34764
|
+
});
|
|
34765
|
+
}
|
|
34766
|
+
async unpublish() {
|
|
34767
|
+
return this.exclusive(async () => {
|
|
34768
|
+
const credential = await this.credential();
|
|
34769
|
+
if (!credential) throw new PublishingError("Sign in before unpublishing.", 401);
|
|
34770
|
+
return this.call("publication", "DELETE", void 0, credential);
|
|
34771
|
+
});
|
|
34772
|
+
}
|
|
34773
|
+
async logout() {
|
|
34774
|
+
return this.exclusive(async () => {
|
|
34775
|
+
this.pending = void 0;
|
|
34776
|
+
this.preview = void 0;
|
|
34777
|
+
const credential = await this.credential();
|
|
34778
|
+
if (credential) await this.call("auth/logout", "POST", {}, credential);
|
|
34779
|
+
await rm5(this.credentialPath, { force: true });
|
|
34780
|
+
return { signedOut: true };
|
|
34781
|
+
});
|
|
34782
|
+
}
|
|
34783
|
+
};
|
|
34784
|
+
|
|
34444
34785
|
// server/middleware.ts
|
|
34786
|
+
var publishing = new PublishingClient();
|
|
34445
34787
|
var generation = /* @__PURE__ */ Symbol("sessiongraph-backend");
|
|
34446
34788
|
var runtime = globalThis;
|
|
34447
34789
|
function library() {
|
|
@@ -34494,6 +34836,24 @@ async function sessionGraphMiddleware(req, res, next) {
|
|
|
34494
34836
|
if (req.method !== "GET" && typeof req.headers["x-sessiongraph-action"] !== "string") return json4(403, { error: "Use the SessionGraph application." });
|
|
34495
34837
|
try {
|
|
34496
34838
|
const { store, importer } = library();
|
|
34839
|
+
if (url.pathname.startsWith("/api/sessiongraph/publishing")) {
|
|
34840
|
+
const action = url.pathname.slice("/api/sessiongraph/publishing".length);
|
|
34841
|
+
if (action === "/settings" && req.method === "GET") return json4(200, publishingSettings(store.setting("publishing")));
|
|
34842
|
+
if (action === "/status" && req.method === "GET") return json4(200, await publishing.status());
|
|
34843
|
+
if (action === "/login" && req.method === "POST") return json4(200, publishing.login());
|
|
34844
|
+
if (action === "/poll" && req.method === "POST") return json4(200, await publishing.poll());
|
|
34845
|
+
if (action === "/logout" && req.method === "POST") return json4(200, await publishing.logout());
|
|
34846
|
+
if (action === "/preview" && req.method === "POST") {
|
|
34847
|
+
const value = await body(req);
|
|
34848
|
+
return json4(200, publishing.prepare(store, value?.ids, value?.settings));
|
|
34849
|
+
}
|
|
34850
|
+
if (action === "/publish" && req.method === "POST") {
|
|
34851
|
+
const value = await body(req);
|
|
34852
|
+
return json4(200, await publishing.publish(value?.id, value?.confirmed));
|
|
34853
|
+
}
|
|
34854
|
+
if (action === "/unpublish" && req.method === "POST") return json4(200, await publishing.unpublish());
|
|
34855
|
+
return json4(405, { error: "Method not allowed." });
|
|
34856
|
+
}
|
|
34497
34857
|
if (url.pathname === "/api/sessiongraph/settings" && req.method === "POST") {
|
|
34498
34858
|
const value = await body(req);
|
|
34499
34859
|
if (!value || typeof value !== "object" || Array.isArray(value) || !["autoImport", "preferences", "viewState"].some((key) => value[key] !== void 0)) return json4(400, { error: "Choose valid settings." });
|
|
@@ -34535,8 +34895,8 @@ async function sessionGraphMiddleware(req, res, next) {
|
|
|
34535
34895
|
}
|
|
34536
34896
|
}
|
|
34537
34897
|
if (url.pathname === "/api/sessiongraph" && req.method === "GET") {
|
|
34538
|
-
const
|
|
34539
|
-
return json4(200, store.library(
|
|
34898
|
+
const sources2 = adapters.map((a) => ({ id: a.id, name: a.name, description: a.description, capabilities: a.capabilities, selected: store.selected().includes(a.id), available: false, ...store.sourceInfo(a.id) }));
|
|
34899
|
+
return json4(200, store.library(sources2, importer.status.running));
|
|
34540
34900
|
}
|
|
34541
34901
|
const match2 = /^\/api\/sessiongraph\/session\/([^/]+)(?:\/(content|timeline|events|event-markers|connection|prepare-launch|open-terminal))?$/.exec(url.pathname);
|
|
34542
34902
|
if (match2) {
|
|
@@ -34568,6 +34928,7 @@ async function sessionGraphMiddleware(req, res, next) {
|
|
|
34568
34928
|
}
|
|
34569
34929
|
return json4(404, { error: "Not found." });
|
|
34570
34930
|
} catch (error) {
|
|
34931
|
+
if (error instanceof PublishingError) return json4(error.status, { error: error.message });
|
|
34571
34932
|
if (error instanceof EventReadError) return json4(error.status, { error: error.message });
|
|
34572
34933
|
console.error("[sessiongraph] Local operation failed:", error instanceof Error ? error.name : "Unknown error");
|
|
34573
34934
|
return json4(500, { error: "Local operation failed. Check the import status or retry." });
|
|
@@ -34580,9 +34941,9 @@ var arg = (name) => {
|
|
|
34580
34941
|
return at2 >= 0 ? process.argv[at2 + 1] : void 0;
|
|
34581
34942
|
};
|
|
34582
34943
|
var assets = resolve2(arg("--assets") || fileURLToPath5(new URL("../dist", import.meta.url)));
|
|
34583
|
-
var
|
|
34944
|
+
var token2 = randomBytes2(32).toString("hex");
|
|
34584
34945
|
var ipc = process.argv.includes("--desktop") ? " ipc: http://ipc.localhost" : "";
|
|
34585
|
-
var equals = (value) => /^[a-f0-9]{64}$/.test(value) && timingSafeEqual(Buffer.from(value), Buffer.from(
|
|
34946
|
+
var equals = (value) => /^[a-f0-9]{64}$/.test(value) && timingSafeEqual(Buffer.from(value), Buffer.from(token2));
|
|
34586
34947
|
var types = { ".html": "text/html; charset=utf-8", ".js": "text/javascript", ".css": "text/css", ".svg": "image/svg+xml", ".png": "image/png", ".woff": "font/woff", ".woff2": "font/woff2", ".json": "application/json" };
|
|
34587
34948
|
var server = createServer(async (req, res) => {
|
|
34588
34949
|
if (req.headers.host !== `127.0.0.1:${server.address().port}`) {
|
|
@@ -34595,7 +34956,7 @@ var server = createServer(async (req, res) => {
|
|
|
34595
34956
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
34596
34957
|
res.setHeader("Content-Security-Policy", `default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; media-src 'self' blob:; font-src 'self'; connect-src 'self'${ipc}; worker-src 'self' blob:; object-src 'none'; frame-ancestors 'none'; base-uri 'self'`);
|
|
34597
34958
|
if (url.pathname === "/__sessiongraph" && equals(url.searchParams.get("token") || "")) {
|
|
34598
|
-
res.setHeader("Set-Cookie", `sessiongraph=${
|
|
34959
|
+
res.setHeader("Set-Cookie", `sessiongraph=${token2}; HttpOnly; SameSite=Strict; Path=/`);
|
|
34599
34960
|
res.setHeader("Cache-Control", "no-store");
|
|
34600
34961
|
res.writeHead(303, { Location: "/" });
|
|
34601
34962
|
res.end();
|
|
@@ -34636,7 +34997,7 @@ var server = createServer(async (req, res) => {
|
|
|
34636
34997
|
});
|
|
34637
34998
|
server.listen(0, "127.0.0.1", () => {
|
|
34638
34999
|
const port = server.address().port;
|
|
34639
|
-
process.stdout.write(JSON.stringify({ url: `http://127.0.0.1:${port}/__sessiongraph?token=${
|
|
35000
|
+
process.stdout.write(JSON.stringify({ url: `http://127.0.0.1:${port}/__sessiongraph?token=${token2}` }) + "\n");
|
|
34640
35001
|
});
|
|
34641
35002
|
var parent = Number(arg("--parent-pid"));
|
|
34642
35003
|
if (parent > 0) setInterval(() => {
|