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,141 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
API_URL="${CODEX_SNAPSHOTS_PUBLIC_API_URL:-${SNAPSHOT_SHARE_PUBLIC_API_URL:-${SNAPSHOT_SHARE_API_URL:-}}}"
|
|
5
|
+
REPO="${GITHUB_REPOSITORY:-ffffhx/codex-snapshots}"
|
|
6
|
+
WORKFLOW="pages.yml"
|
|
7
|
+
TRIGGER_WORKFLOW=1
|
|
8
|
+
WAIT_WORKFLOW=0
|
|
9
|
+
|
|
10
|
+
usage() {
|
|
11
|
+
cat <<'EOF'
|
|
12
|
+
Usage:
|
|
13
|
+
deploy/aliyun/configure-github-pages-api.sh \
|
|
14
|
+
--api-url https://snapshots.example.com \
|
|
15
|
+
[--repo ffffhx/codex-snapshots]
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--api-url URL Public share API URL to expose to GitHub Pages.
|
|
19
|
+
--repo OWNER/REPO GitHub repository. Defaults to ffffhx/codex-snapshots.
|
|
20
|
+
--workflow FILE Pages workflow file. Defaults to pages.yml.
|
|
21
|
+
--no-dispatch Set the variable but do not trigger the workflow.
|
|
22
|
+
--wait Wait for the triggered workflow to finish.
|
|
23
|
+
-h, --help Show help.
|
|
24
|
+
|
|
25
|
+
Requires:
|
|
26
|
+
gh auth login
|
|
27
|
+
EOF
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
while [[ $# -gt 0 ]]; do
|
|
31
|
+
case "$1" in
|
|
32
|
+
--api-url)
|
|
33
|
+
API_URL="${2:-}"
|
|
34
|
+
shift 2
|
|
35
|
+
;;
|
|
36
|
+
--repo)
|
|
37
|
+
REPO="${2:-}"
|
|
38
|
+
shift 2
|
|
39
|
+
;;
|
|
40
|
+
--workflow)
|
|
41
|
+
WORKFLOW="${2:-}"
|
|
42
|
+
shift 2
|
|
43
|
+
;;
|
|
44
|
+
--no-dispatch)
|
|
45
|
+
TRIGGER_WORKFLOW=0
|
|
46
|
+
shift
|
|
47
|
+
;;
|
|
48
|
+
--wait)
|
|
49
|
+
WAIT_WORKFLOW=1
|
|
50
|
+
shift
|
|
51
|
+
;;
|
|
52
|
+
-h|--help)
|
|
53
|
+
usage
|
|
54
|
+
exit 0
|
|
55
|
+
;;
|
|
56
|
+
*)
|
|
57
|
+
echo "Unknown option: $1" >&2
|
|
58
|
+
usage >&2
|
|
59
|
+
exit 1
|
|
60
|
+
;;
|
|
61
|
+
esac
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
if [[ -z "${API_URL}" ]]; then
|
|
65
|
+
echo "Missing API URL. Set CODEX_SNAPSHOTS_PUBLIC_API_URL or pass --api-url." >&2
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
lowercase() {
|
|
70
|
+
printf "%s" "$1" | tr '[:upper:]' '[:lower:]'
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
url_host() {
|
|
74
|
+
local value="${1#http://}"
|
|
75
|
+
value="${value#https://}"
|
|
76
|
+
value="${value%%/*}"
|
|
77
|
+
if [[ "${value}" == \[*\]* ]]; then
|
|
78
|
+
value="${value#\[}"
|
|
79
|
+
value="${value%%\]*}"
|
|
80
|
+
else
|
|
81
|
+
value="${value%%:*}"
|
|
82
|
+
fi
|
|
83
|
+
lowercase "${value}"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
is_http_url() {
|
|
87
|
+
[[ "$1" == http://* || "$1" == https://* ]]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
is_placeholder_domain() {
|
|
91
|
+
local value
|
|
92
|
+
value="$(lowercase "$1")"
|
|
93
|
+
[[ "${value}" == "example.com" || "${value}" == *.example.com || "${value}" == "snapshots.example.com" ]]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
api_host="$(url_host "${API_URL}")"
|
|
97
|
+
if ! is_http_url "${API_URL}"; then
|
|
98
|
+
echo "API URL must start with http:// or https://." >&2
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
if is_placeholder_domain "${api_host}"; then
|
|
102
|
+
echo "API URL still uses the placeholder https://snapshots.example.com." >&2
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
if [[ "${api_host}" == "127.0.0.1" || "${api_host}" == "localhost" || "${api_host}" == "::1" ]]; then
|
|
106
|
+
echo "API URL must be the public Aliyun API, not ${API_URL}." >&2
|
|
107
|
+
exit 1
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
if [[ "${WAIT_WORKFLOW}" -eq 1 && "${TRIGGER_WORKFLOW}" -ne 1 ]]; then
|
|
111
|
+
echo "--wait requires workflow dispatch." >&2
|
|
112
|
+
exit 1
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
116
|
+
echo "Missing gh CLI. Install it and run: gh auth login" >&2
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
gh auth status >/dev/null
|
|
121
|
+
|
|
122
|
+
gh variable set CODEX_SNAPSHOTS_PUBLIC_API_URL \
|
|
123
|
+
--repo "${REPO}" \
|
|
124
|
+
--body "${API_URL%/}"
|
|
125
|
+
|
|
126
|
+
echo "Set CODEX_SNAPSHOTS_PUBLIC_API_URL=${API_URL%/} on ${REPO}"
|
|
127
|
+
|
|
128
|
+
if [[ "${TRIGGER_WORKFLOW}" -eq 1 ]]; then
|
|
129
|
+
gh workflow run "${WORKFLOW}" --repo "${REPO}"
|
|
130
|
+
echo "Triggered workflow ${WORKFLOW} on ${REPO}"
|
|
131
|
+
|
|
132
|
+
if [[ "${WAIT_WORKFLOW}" -eq 1 ]]; then
|
|
133
|
+
sleep 5
|
|
134
|
+
RUN_ID="$(gh run list --repo "${REPO}" --workflow "${WORKFLOW}" --limit 1 --json databaseId --jq '.[0].databaseId')"
|
|
135
|
+
if [[ -z "${RUN_ID}" || "${RUN_ID}" == "null" ]]; then
|
|
136
|
+
echo "Could not find the triggered workflow run." >&2
|
|
137
|
+
exit 1
|
|
138
|
+
fi
|
|
139
|
+
gh run watch "${RUN_ID}" --repo "${REPO}" --exit-status
|
|
140
|
+
fi
|
|
141
|
+
fi
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
API_URL="${SNAPSHOT_SHARE_API_URL:-${SNAPSHOT_SHARE_PUBLIC_API_URL:-}}"
|
|
5
|
+
SITE_URL="${SNAPSHOT_SHARE_SITE_URL:-https://ffffhx.github.io/codex-snapshots/}"
|
|
6
|
+
TOKEN="${SNAPSHOT_SHARE_TOKEN:-}"
|
|
7
|
+
TOKEN_FILE="${SNAPSHOT_SHARE_TOKEN_FILE:-${HOME}/.codex-snapshots-agent.json}"
|
|
8
|
+
REINSTALL_DAEMON=0
|
|
9
|
+
CHECK_API=1
|
|
10
|
+
|
|
11
|
+
usage() {
|
|
12
|
+
cat <<'EOF'
|
|
13
|
+
Usage:
|
|
14
|
+
SNAPSHOT_SHARE_API_URL=https://snapshots.example.com \
|
|
15
|
+
deploy/aliyun/configure-local-publisher.sh [--reinstall-daemon]
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
--api-url URL Public share API URL. Defaults to SNAPSHOT_SHARE_API_URL or SNAPSHOT_SHARE_PUBLIC_API_URL.
|
|
19
|
+
--site-url URL Public static site URL. Defaults to SNAPSHOT_SHARE_SITE_URL or GitHub Pages.
|
|
20
|
+
--token TOKEN Optional publish token for legacy token auth. Defaults to SNAPSHOT_SHARE_TOKEN.
|
|
21
|
+
--token-file FILE Local publisher config file for codex-snapshot. Defaults to ~/.codex-snapshots-agent.json.
|
|
22
|
+
--reinstall-daemon Reinstall the macOS LaunchAgent with the public API/site URLs.
|
|
23
|
+
--no-check Skip the API health check.
|
|
24
|
+
-h, --help Show help.
|
|
25
|
+
EOF
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
while [[ $# -gt 0 ]]; do
|
|
29
|
+
case "$1" in
|
|
30
|
+
--api-url)
|
|
31
|
+
API_URL="${2:-}"
|
|
32
|
+
shift 2
|
|
33
|
+
;;
|
|
34
|
+
--site-url)
|
|
35
|
+
SITE_URL="${2:-}"
|
|
36
|
+
shift 2
|
|
37
|
+
;;
|
|
38
|
+
--token)
|
|
39
|
+
TOKEN="${2:-}"
|
|
40
|
+
shift 2
|
|
41
|
+
;;
|
|
42
|
+
--token-file)
|
|
43
|
+
TOKEN_FILE="${2:-}"
|
|
44
|
+
shift 2
|
|
45
|
+
;;
|
|
46
|
+
--reinstall-daemon)
|
|
47
|
+
REINSTALL_DAEMON=1
|
|
48
|
+
shift
|
|
49
|
+
;;
|
|
50
|
+
--no-check)
|
|
51
|
+
CHECK_API=0
|
|
52
|
+
shift
|
|
53
|
+
;;
|
|
54
|
+
-h|--help)
|
|
55
|
+
usage
|
|
56
|
+
exit 0
|
|
57
|
+
;;
|
|
58
|
+
*)
|
|
59
|
+
echo "Unknown option: $1" >&2
|
|
60
|
+
usage >&2
|
|
61
|
+
exit 1
|
|
62
|
+
;;
|
|
63
|
+
esac
|
|
64
|
+
done
|
|
65
|
+
|
|
66
|
+
if [[ -z "${API_URL}" ]]; then
|
|
67
|
+
echo "Missing API URL. Set SNAPSHOT_SHARE_API_URL or pass --api-url." >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
lowercase() {
|
|
72
|
+
printf "%s" "$1" | tr '[:upper:]' '[:lower:]'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
url_host() {
|
|
76
|
+
local value="${1#http://}"
|
|
77
|
+
value="${value#https://}"
|
|
78
|
+
value="${value%%/*}"
|
|
79
|
+
if [[ "${value}" == \[*\]* ]]; then
|
|
80
|
+
value="${value#\[}"
|
|
81
|
+
value="${value%%\]*}"
|
|
82
|
+
else
|
|
83
|
+
value="${value%%:*}"
|
|
84
|
+
fi
|
|
85
|
+
lowercase "${value}"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
is_http_url() {
|
|
89
|
+
[[ "$1" == http://* || "$1" == https://* ]]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
is_placeholder_domain() {
|
|
93
|
+
local value
|
|
94
|
+
value="$(lowercase "$1")"
|
|
95
|
+
[[ "${value}" == "example.com" || "${value}" == *.example.com || "${value}" == "snapshots.example.com" ]]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
validate_public_publisher_config() {
|
|
99
|
+
local errors=()
|
|
100
|
+
local api_host
|
|
101
|
+
api_host="$(url_host "${API_URL}")"
|
|
102
|
+
local site_host
|
|
103
|
+
site_host="$(url_host "${SITE_URL}")"
|
|
104
|
+
|
|
105
|
+
if ! is_http_url "${API_URL}"; then
|
|
106
|
+
errors+=("API URL must start with http:// or https://.")
|
|
107
|
+
fi
|
|
108
|
+
if ! is_http_url "${SITE_URL}"; then
|
|
109
|
+
errors+=("Site URL must start with http:// or https://.")
|
|
110
|
+
fi
|
|
111
|
+
if is_placeholder_domain "${api_host}"; then
|
|
112
|
+
errors+=("API URL still uses the placeholder https://snapshots.example.com.")
|
|
113
|
+
fi
|
|
114
|
+
if [[ "${api_host}" == "127.0.0.1" || "${api_host}" == "localhost" || "${api_host}" == "::1" ]]; then
|
|
115
|
+
errors+=("API URL must be the public Aliyun API, not ${API_URL}.")
|
|
116
|
+
fi
|
|
117
|
+
if [[ "${site_host}" == "127.0.0.1" || "${site_host}" == "localhost" || "${site_host}" == "::1" ]]; then
|
|
118
|
+
errors+=("Site URL must be the public website, not ${SITE_URL}.")
|
|
119
|
+
fi
|
|
120
|
+
if [[ -n "${TOKEN}" ]]; then
|
|
121
|
+
if [[ "${TOKEN}" == "change-me" ]]; then
|
|
122
|
+
errors+=("Token still uses the placeholder change-me.")
|
|
123
|
+
elif [[ "${#TOKEN}" -lt 16 ]]; then
|
|
124
|
+
errors+=("Token should be at least 16 characters.")
|
|
125
|
+
fi
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
if [[ "${#errors[@]}" -gt 0 ]]; then
|
|
129
|
+
echo "Local publisher config needs real public values:" >&2
|
|
130
|
+
printf " - %s\n" "${errors[@]}" >&2
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
validate_public_publisher_config
|
|
136
|
+
|
|
137
|
+
mkdir -p "$(dirname "${TOKEN_FILE}")"
|
|
138
|
+
umask 077
|
|
139
|
+
node -e '
|
|
140
|
+
const { writeFileSync } = require("node:fs");
|
|
141
|
+
const file = process.argv[1];
|
|
142
|
+
const token = process.argv[2];
|
|
143
|
+
const apiUrl = process.argv[3].replace(/\/+$/, "");
|
|
144
|
+
const siteUrl = process.argv[4].replace(/\/+$/, "");
|
|
145
|
+
const payload = {
|
|
146
|
+
snapshotShareApiUrl: apiUrl,
|
|
147
|
+
snapshotShareSiteUrl: siteUrl,
|
|
148
|
+
};
|
|
149
|
+
if (token) {
|
|
150
|
+
payload.snapshotShareToken = token;
|
|
151
|
+
}
|
|
152
|
+
writeFileSync(file, `${JSON.stringify(payload, null, 2)}\n`, { mode: 0o600 });
|
|
153
|
+
' "${TOKEN_FILE}" "${TOKEN}" "${API_URL}" "${SITE_URL}"
|
|
154
|
+
|
|
155
|
+
echo "Wrote local publisher config: ${TOKEN_FILE}"
|
|
156
|
+
echo "Public share API: ${API_URL}"
|
|
157
|
+
echo "Public site: ${SITE_URL}"
|
|
158
|
+
if [[ -n "${TOKEN}" ]]; then
|
|
159
|
+
echo "Legacy publish token: configured"
|
|
160
|
+
else
|
|
161
|
+
echo "Legacy publish token: not configured; GitHub OAuth browser publishing can still use this API/site config."
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
if [[ "${CHECK_API}" -eq 1 ]]; then
|
|
165
|
+
curl --fail --show-error --silent --max-time 8 "${API_URL%/}/api/snapshots/health" >/dev/null
|
|
166
|
+
echo "Share API health check passed."
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
if [[ "${REINSTALL_DAEMON}" -eq 1 ]]; then
|
|
170
|
+
SNAPSHOT_SHARE_API_URL="${API_URL}" \
|
|
171
|
+
SNAPSHOT_SHARE_SITE_URL="${SITE_URL}" \
|
|
172
|
+
pnpm snapshot:uninstall-daemon || true
|
|
173
|
+
|
|
174
|
+
SNAPSHOT_SHARE_API_URL="${API_URL}" \
|
|
175
|
+
SNAPSHOT_SHARE_SITE_URL="${SITE_URL}" \
|
|
176
|
+
pnpm snapshot:install-daemon
|
|
177
|
+
|
|
178
|
+
pnpm snapshot:daemon:status
|
|
179
|
+
else
|
|
180
|
+
cat <<EOF
|
|
181
|
+
To run the local viewer now:
|
|
182
|
+
|
|
183
|
+
pnpm snapshot serve --port 4321
|
|
184
|
+
|
|
185
|
+
To update the macOS LaunchAgent:
|
|
186
|
+
|
|
187
|
+
deploy/aliyun/configure-local-publisher.sh \\
|
|
188
|
+
--api-url ${API_URL} \\
|
|
189
|
+
--site-url ${SITE_URL} \\
|
|
190
|
+
--token-file ${TOKEN_FILE} \\
|
|
191
|
+
--reinstall-daemon
|
|
192
|
+
|
|
193
|
+
For legacy token auth, add:
|
|
194
|
+
|
|
195
|
+
--token <same-token>
|
|
196
|
+
EOF
|
|
197
|
+
fi
|