codex-snapshots 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -6
- package/bin/codex-snapshot.mjs +1 -6326
- package/deploy/aliyun/README.md +311 -0
- package/deploy/aliyun/backup-share-data.sh +109 -0
- package/deploy/aliyun/check-ecs-status.sh +149 -0
- package/deploy/aliyun/codex-snapshot-share.env.example +29 -0
- package/deploy/aliyun/codex-snapshot-share.service +26 -0
- package/deploy/aliyun/configure-github-pages-api.sh +141 -0
- package/deploy/aliyun/configure-local-publisher.sh +197 -0
- package/deploy/aliyun/deploy-to-ecs.sh +669 -0
- package/deploy/aliyun/deploy.env.example +52 -0
- package/deploy/aliyun/doctor.mjs +398 -0
- package/deploy/aliyun/install-share-api.sh +252 -0
- package/deploy/aliyun/install-system-deps.sh +84 -0
- package/deploy/aliyun/nginx-codex-snapshots.bootstrap.conf +34 -0
- package/deploy/aliyun/nginx-codex-snapshots.conf +52 -0
- package/deploy/aliyun/preflight.mjs +321 -0
- package/deploy/aliyun/restore-share-data.sh +141 -0
- package/deploy/aliyun/verify-public-share.mjs +404 -0
- package/dist/cli/codex-snapshot.mjs +2654 -0
- package/dist/core/privacy.js +81 -0
- package/dist/core/snapshot.js +1 -0
- package/dist/renderers/markdown.mjs +81 -0
- package/dist/renderers/transcript.js +195 -0
- package/dist/server/http.js +10 -0
- package/dist/server/local-security.js +66 -0
- package/dist/server/local-viewer-app.mjs +1670 -0
- package/dist/server/local-viewer.mjs +210 -0
- package/dist/server/share-api.mjs +1149 -0
- package/dist/server/share-store.js +136 -0
- package/dist/shared/sanitize.js +126 -0
- package/dist/shared/transcript.js +1 -0
- package/dist/sources/index.mjs +2 -0
- package/dist/sources/local-history.mjs +2221 -0
- package/package.json +42 -14
- package/scripts/build-site.mjs +71 -0
- package/scripts/launch-agent.mjs +19 -227
- package/scripts/serve-site.mjs +2 -2
- package/scripts/test-aliyun-deploy-config.sh +230 -0
- package/scripts/test-share-api.mjs +967 -0
- package/scripts/test-site-config.mjs +100 -0
- package/scripts/test-static-site.mjs +403 -0
- package/scripts/write-site-config.mjs +161 -0
- package/server/share-api.mjs +1 -771
- package/site/assets/config.js +3 -0
- package/site/assets/share.js +43 -106
- package/site/assets/site.css +3 -605
- package/site/assets/site.js +15 -92
- package/site/favicon.svg +7 -0
- package/site/index.html +3 -83
- package/site/share/index.html +3 -8
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { send, sendJson } from "./http.js";
|
|
4
|
+
import { allowMutationRequest, createMutationCsrfToken, isAllowedSnapshotServerRequest, setSnapshotServerCorsHeaders, } from "./local-security.js";
|
|
5
|
+
import { renderServerApp } from "./local-viewer-app.mjs";
|
|
6
|
+
export async function serveLocalViewer({ codexHome, claudeHome, traeHome, traeAppHome, traeRecordingsDir, host, port, defaultServerLimit, snapshotLogoSvg, shareConfig, listSessions, loadSnapshot, applySafetyChecksOption, snapshotApiResponse, publishAllSnapshots, publishSnapshot, createShareRequestPayload, stableSnapshotShareId, renderMarkdown, renderHtml, readPositiveInteger, readNonNegativeInteger, safeFileName, }) {
|
|
7
|
+
const csrfToken = createMutationCsrfToken();
|
|
8
|
+
const server = http.createServer(async (request, response) => {
|
|
9
|
+
try {
|
|
10
|
+
setSnapshotServerCorsHeaders(request, response);
|
|
11
|
+
if (request.method === "OPTIONS") {
|
|
12
|
+
response.writeHead(204);
|
|
13
|
+
response.end();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!isAllowedSnapshotServerRequest(request)) {
|
|
17
|
+
sendJson(response, { error: "origin is not allowed to access this local snapshot server" }, 403);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const url = new URL(request.url || "/", `http://${request.headers.host || `${host}:${port}`}`);
|
|
21
|
+
if (url.pathname === "/") {
|
|
22
|
+
send(response, 200, "text/html; charset=utf-8", renderServerApp(csrfToken, shareConfig));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (url.pathname === "/favicon.svg" || url.pathname === "/favicon.ico") {
|
|
26
|
+
send(response, 200, "image/svg+xml; charset=utf-8", snapshotLogoSvg);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (url.pathname === "/api/sessions") {
|
|
30
|
+
const limit = url.searchParams.get("all") === "1"
|
|
31
|
+
? Number.POSITIVE_INFINITY
|
|
32
|
+
: readPositiveInteger(url.searchParams.get("limit") || String(defaultServerLimit), "limit");
|
|
33
|
+
const offset = readNonNegativeInteger(url.searchParams.get("offset") || "0", "offset");
|
|
34
|
+
const scanLimit = Number.isFinite(limit) ? limit + offset : Number.POSITIVE_INFINITY;
|
|
35
|
+
const sessions = await listSessions({
|
|
36
|
+
codexHome,
|
|
37
|
+
claudeHome,
|
|
38
|
+
traeHome,
|
|
39
|
+
traeAppHome,
|
|
40
|
+
traeRecordingsDir,
|
|
41
|
+
limit: scanLimit,
|
|
42
|
+
cwd: url.searchParams.get("cwd") || "",
|
|
43
|
+
includeArchived: url.searchParams.get("liveOnly") !== "1",
|
|
44
|
+
source: url.searchParams.get("source") || "codex",
|
|
45
|
+
completeOnly: url.searchParams.get("completeOnly") !== "0",
|
|
46
|
+
});
|
|
47
|
+
sendJson(response, Number.isFinite(limit) ? sessions.slice(offset, offset + limit) : sessions.slice(offset));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (url.pathname === "/api/snapshot") {
|
|
51
|
+
const id = url.searchParams.get("id");
|
|
52
|
+
if (!id) {
|
|
53
|
+
sendJson(response, { error: "missing id" }, 400);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const snapshot = await loadSnapshot(id, {
|
|
57
|
+
codexHome,
|
|
58
|
+
claudeHome,
|
|
59
|
+
traeHome,
|
|
60
|
+
traeAppHome,
|
|
61
|
+
traeRecordingsDir,
|
|
62
|
+
includeTools: url.searchParams.get("includeTools") === "1" || url.searchParams.get("includeToolOutput") === "1",
|
|
63
|
+
includeToolOutput: url.searchParams.get("includeToolOutput") === "1",
|
|
64
|
+
redact: url.searchParams.get("redact") !== "0",
|
|
65
|
+
});
|
|
66
|
+
applySafetyChecksOption(snapshot, url.searchParams.get("safety") !== "0");
|
|
67
|
+
sendJson(response, snapshotApiResponse(snapshot));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (url.pathname === "/api/publish-all") {
|
|
71
|
+
if (!allowMutationRequest(request, response, csrfToken)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (url.searchParams.get("redact") === "0") {
|
|
75
|
+
sendJson(response, { error: "Cloud publish requires Redact enabled in the local viewer." }, 400);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const result = await publishAllSnapshots({
|
|
79
|
+
codexHome,
|
|
80
|
+
claudeHome,
|
|
81
|
+
traeHome,
|
|
82
|
+
traeAppHome,
|
|
83
|
+
traeRecordingsDir,
|
|
84
|
+
cwd: url.searchParams.get("cwd") || "",
|
|
85
|
+
includeArchived: url.searchParams.get("liveOnly") !== "1",
|
|
86
|
+
source: "all",
|
|
87
|
+
completeOnly: url.searchParams.get("completeOnly") !== "0",
|
|
88
|
+
limit: url.searchParams.get("limit")
|
|
89
|
+
? readPositiveInteger(url.searchParams.get("limit"), "limit")
|
|
90
|
+
: Number.POSITIVE_INFINITY,
|
|
91
|
+
includeTools: url.searchParams.get("includeTools") === "1" || url.searchParams.get("includeToolOutput") === "1",
|
|
92
|
+
includeToolOutput: url.searchParams.get("includeToolOutput") === "1",
|
|
93
|
+
safety: url.searchParams.get("safety") === "1",
|
|
94
|
+
});
|
|
95
|
+
sendJson(response, result);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (url.pathname === "/api/publish") {
|
|
99
|
+
if (!allowMutationRequest(request, response, csrfToken)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const id = url.searchParams.get("id");
|
|
103
|
+
if (!id) {
|
|
104
|
+
sendJson(response, { error: "missing id" }, 400);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (url.searchParams.get("redact") === "0") {
|
|
108
|
+
sendJson(response, { error: "Cloud publish requires Redact enabled in the local viewer." }, 400);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const snapshot = await loadSnapshot(id, {
|
|
112
|
+
codexHome,
|
|
113
|
+
claudeHome,
|
|
114
|
+
traeHome,
|
|
115
|
+
traeAppHome,
|
|
116
|
+
traeRecordingsDir,
|
|
117
|
+
includeTools: url.searchParams.get("includeTools") === "1" || url.searchParams.get("includeToolOutput") === "1",
|
|
118
|
+
includeToolOutput: url.searchParams.get("includeToolOutput") === "1",
|
|
119
|
+
redact: true,
|
|
120
|
+
});
|
|
121
|
+
applySafetyChecksOption(snapshot, url.searchParams.get("safety") === "1");
|
|
122
|
+
const result = await publishSnapshot(snapshot, {
|
|
123
|
+
apiUrl: "",
|
|
124
|
+
token: "",
|
|
125
|
+
siteUrl: "",
|
|
126
|
+
expiresInDays: 0,
|
|
127
|
+
shareId: stableSnapshotShareId(snapshot),
|
|
128
|
+
});
|
|
129
|
+
sendJson(response, result);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (url.pathname === "/api/share-payload") {
|
|
133
|
+
if (!allowMutationRequest(request, response, csrfToken)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const id = url.searchParams.get("id");
|
|
137
|
+
if (!id) {
|
|
138
|
+
sendJson(response, { error: "missing id" }, 400);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (url.searchParams.get("redact") === "0") {
|
|
142
|
+
sendJson(response, { error: "Cloud publish requires Redact enabled in the local viewer." }, 400);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const snapshot = await loadSnapshot(id, {
|
|
146
|
+
codexHome,
|
|
147
|
+
claudeHome,
|
|
148
|
+
traeHome,
|
|
149
|
+
traeAppHome,
|
|
150
|
+
traeRecordingsDir,
|
|
151
|
+
includeTools: url.searchParams.get("includeTools") === "1" || url.searchParams.get("includeToolOutput") === "1",
|
|
152
|
+
includeToolOutput: url.searchParams.get("includeToolOutput") === "1",
|
|
153
|
+
redact: true,
|
|
154
|
+
});
|
|
155
|
+
applySafetyChecksOption(snapshot, url.searchParams.get("safety") === "1");
|
|
156
|
+
const result = createShareRequestPayload(snapshot, {
|
|
157
|
+
apiUrl: "",
|
|
158
|
+
siteUrl: "",
|
|
159
|
+
expiresInDays: 0,
|
|
160
|
+
shareId: stableSnapshotShareId(snapshot),
|
|
161
|
+
});
|
|
162
|
+
sendJson(response, result);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (url.pathname === "/export") {
|
|
166
|
+
const id = url.searchParams.get("id");
|
|
167
|
+
const format = url.searchParams.get("format") === "md" ? "md" : "html";
|
|
168
|
+
if (!id) {
|
|
169
|
+
send(response, 400, "text/plain; charset=utf-8", "missing id");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const snapshot = await loadSnapshot(id, {
|
|
173
|
+
codexHome,
|
|
174
|
+
claudeHome,
|
|
175
|
+
traeHome,
|
|
176
|
+
traeAppHome,
|
|
177
|
+
traeRecordingsDir,
|
|
178
|
+
includeTools: url.searchParams.get("includeTools") === "1" || url.searchParams.get("includeToolOutput") === "1",
|
|
179
|
+
includeToolOutput: url.searchParams.get("includeToolOutput") === "1",
|
|
180
|
+
redact: url.searchParams.get("redact") !== "0",
|
|
181
|
+
});
|
|
182
|
+
applySafetyChecksOption(snapshot, url.searchParams.get("safety") !== "0");
|
|
183
|
+
const body = format === "md" ? renderMarkdown(snapshot) : renderHtml(snapshot);
|
|
184
|
+
const fileName = `${safeFileName(snapshot.title || snapshot.id)}.${format === "md" ? "md" : "html"}`;
|
|
185
|
+
response.writeHead(200, {
|
|
186
|
+
"content-type": format === "md" ? "text/markdown; charset=utf-8" : "text/html; charset=utf-8",
|
|
187
|
+
"content-disposition": `attachment; filename="${fileName}"`,
|
|
188
|
+
"cache-control": "no-store",
|
|
189
|
+
});
|
|
190
|
+
response.end(body);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
send(response, 404, "text/plain; charset=utf-8", "not found");
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
sendJson(response, { error: error instanceof Error ? error.message : String(error) }, 500);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
await new Promise((resolve, reject) => {
|
|
200
|
+
server.once("error", reject);
|
|
201
|
+
server.listen(port, host, resolve);
|
|
202
|
+
});
|
|
203
|
+
const url = `http://${host}:${port}`;
|
|
204
|
+
console.log(`Codex Snapshot is running at ${url}`);
|
|
205
|
+
console.log(`Codex home: ${codexHome}`);
|
|
206
|
+
console.log(`Claude Code home: ${claudeHome}`);
|
|
207
|
+
console.log(`Trae home: ${traeHome}`);
|
|
208
|
+
console.log(`Trae app home: ${traeAppHome}`);
|
|
209
|
+
console.log(`Trae recordings: ${traeRecordingsDir}`);
|
|
210
|
+
}
|