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.
Files changed (51) hide show
  1. package/README.md +101 -6
  2. package/bin/codex-snapshot.mjs +1 -6326
  3. package/deploy/aliyun/README.md +311 -0
  4. package/deploy/aliyun/backup-share-data.sh +109 -0
  5. package/deploy/aliyun/check-ecs-status.sh +149 -0
  6. package/deploy/aliyun/codex-snapshot-share.env.example +29 -0
  7. package/deploy/aliyun/codex-snapshot-share.service +26 -0
  8. package/deploy/aliyun/configure-github-pages-api.sh +141 -0
  9. package/deploy/aliyun/configure-local-publisher.sh +197 -0
  10. package/deploy/aliyun/deploy-to-ecs.sh +669 -0
  11. package/deploy/aliyun/deploy.env.example +52 -0
  12. package/deploy/aliyun/doctor.mjs +398 -0
  13. package/deploy/aliyun/install-share-api.sh +252 -0
  14. package/deploy/aliyun/install-system-deps.sh +84 -0
  15. package/deploy/aliyun/nginx-codex-snapshots.bootstrap.conf +34 -0
  16. package/deploy/aliyun/nginx-codex-snapshots.conf +52 -0
  17. package/deploy/aliyun/preflight.mjs +321 -0
  18. package/deploy/aliyun/restore-share-data.sh +141 -0
  19. package/deploy/aliyun/verify-public-share.mjs +404 -0
  20. package/dist/cli/codex-snapshot.mjs +2654 -0
  21. package/dist/core/privacy.js +81 -0
  22. package/dist/core/snapshot.js +1 -0
  23. package/dist/renderers/markdown.mjs +81 -0
  24. package/dist/renderers/transcript.js +195 -0
  25. package/dist/server/http.js +10 -0
  26. package/dist/server/local-security.js +66 -0
  27. package/dist/server/local-viewer-app.mjs +1670 -0
  28. package/dist/server/local-viewer.mjs +210 -0
  29. package/dist/server/share-api.mjs +1149 -0
  30. package/dist/server/share-store.js +136 -0
  31. package/dist/shared/sanitize.js +126 -0
  32. package/dist/shared/transcript.js +1 -0
  33. package/dist/sources/index.mjs +2 -0
  34. package/dist/sources/local-history.mjs +2221 -0
  35. package/package.json +42 -14
  36. package/scripts/build-site.mjs +71 -0
  37. package/scripts/launch-agent.mjs +19 -227
  38. package/scripts/serve-site.mjs +2 -2
  39. package/scripts/test-aliyun-deploy-config.sh +230 -0
  40. package/scripts/test-share-api.mjs +967 -0
  41. package/scripts/test-site-config.mjs +100 -0
  42. package/scripts/test-static-site.mjs +403 -0
  43. package/scripts/write-site-config.mjs +161 -0
  44. package/server/share-api.mjs +1 -771
  45. package/site/assets/config.js +3 -0
  46. package/site/assets/share.js +43 -106
  47. package/site/assets/site.css +3 -605
  48. package/site/assets/site.js +15 -92
  49. package/site/favicon.svg +7 -0
  50. package/site/index.html +3 -83
  51. 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