overleaf-codex 0.1.0-rc.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.
Potentially problematic release.
This version of overleaf-codex might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/NOTICE.md +25 -0
- package/README.md +217 -0
- package/assets/olcx-mark.svg +22 -0
- package/dist/auth/projectAuth.d.ts +19 -0
- package/dist/auth/projectAuth.js +163 -0
- package/dist/auth/projectAuth.js.map +1 -0
- package/dist/auth/redact.d.ts +3 -0
- package/dist/auth/redact.js +7 -0
- package/dist/auth/redact.js.map +1 -0
- package/dist/auth/types.d.ts +10 -0
- package/dist/auth/types.js +4 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/backend/index.d.ts +6 -0
- package/dist/backend/index.js +2 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/olcli/client.d.ts +329 -0
- package/dist/backend/olcli/client.js +1757 -0
- package/dist/backend/olcli/client.js.map +1 -0
- package/dist/backend/olcli/index.d.ts +2 -0
- package/dist/backend/olcli/index.js +2 -0
- package/dist/backend/olcli/index.js.map +1 -0
- package/dist/backend/overleafBackend.d.ts +41 -0
- package/dist/backend/overleafBackend.js +200 -0
- package/dist/backend/overleafBackend.js.map +1 -0
- package/dist/backend/types.d.ts +73 -0
- package/dist/backend/types.js +2 -0
- package/dist/backend/types.js.map +1 -0
- package/dist/cli-behavior.d.ts +14 -0
- package/dist/cli-behavior.js +59 -0
- package/dist/cli-behavior.js.map +1 -0
- package/dist/cli.d.ts +30 -0
- package/dist/cli.js +441 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/auth.d.ts +21 -0
- package/dist/commands/auth.js +104 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/compile.d.ts +7 -0
- package/dist/commands/compile.js +73 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.js +9 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/endpoint.d.ts +23 -0
- package/dist/commands/endpoint.js +69 -0
- package/dist/commands/endpoint.js.map +1 -0
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.js +48 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.js +5 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +26 -0
- package/dist/commands/sync.js +139 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/watch.d.ts +28 -0
- package/dist/commands/watch.js +124 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/compile/compileFlow.d.ts +32 -0
- package/dist/compile/compileFlow.js +290 -0
- package/dist/compile/compileFlow.js.map +1 -0
- package/dist/compile/pdfOutput.d.ts +12 -0
- package/dist/compile/pdfOutput.js +64 -0
- package/dist/compile/pdfOutput.js.map +1 -0
- package/dist/config/ignoreRules.d.ts +5 -0
- package/dist/config/ignoreRules.js +53 -0
- package/dist/config/ignoreRules.js.map +1 -0
- package/dist/config/overleafProject.d.ts +9 -0
- package/dist/config/overleafProject.js +61 -0
- package/dist/config/overleafProject.js.map +1 -0
- package/dist/config/projectConfig.d.ts +6 -0
- package/dist/config/projectConfig.js +180 -0
- package/dist/config/projectConfig.js.map +1 -0
- package/dist/config/projectRoot.d.ts +1 -0
- package/dist/config/projectRoot.js +36 -0
- package/dist/config/projectRoot.js.map +1 -0
- package/dist/config/types.d.ts +50 -0
- package/dist/config/types.js +34 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/vscode.d.ts +10 -0
- package/dist/config/vscode.js +134 -0
- package/dist/config/vscode.js.map +1 -0
- package/dist/diagnostics/doctor.d.ts +8 -0
- package/dist/diagnostics/doctor.js +209 -0
- package/dist/diagnostics/doctor.js.map +1 -0
- package/dist/diagnostics/status.d.ts +6 -0
- package/dist/diagnostics/status.js +110 -0
- package/dist/diagnostics/status.js.map +1 -0
- package/dist/diagnostics/types.d.ts +33 -0
- package/dist/diagnostics/types.js +2 -0
- package/dist/diagnostics/types.js.map +1 -0
- package/dist/endpoint/overleafEndpoint.d.ts +36 -0
- package/dist/endpoint/overleafEndpoint.js +105 -0
- package/dist/endpoint/overleafEndpoint.js.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.js +53 -0
- package/dist/errors.js.map +1 -0
- package/dist/sync/apply.d.ts +14 -0
- package/dist/sync/apply.js +92 -0
- package/dist/sync/apply.js.map +1 -0
- package/dist/sync/conflicts.d.ts +7 -0
- package/dist/sync/conflicts.js +59 -0
- package/dist/sync/conflicts.js.map +1 -0
- package/dist/sync/ignore.d.ts +5 -0
- package/dist/sync/ignore.js +74 -0
- package/dist/sync/ignore.js.map +1 -0
- package/dist/sync/plan.d.ts +3 -0
- package/dist/sync/plan.js +197 -0
- package/dist/sync/plan.js.map +1 -0
- package/dist/sync/snapshot.d.ts +13 -0
- package/dist/sync/snapshot.js +82 -0
- package/dist/sync/snapshot.js.map +1 -0
- package/dist/sync/state.d.ts +16 -0
- package/dist/sync/state.js +214 -0
- package/dist/sync/state.js.map +1 -0
- package/dist/sync/types.d.ts +113 -0
- package/dist/sync/types.js +4 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/testing/fakeBackend.d.ts +27 -0
- package/dist/testing/fakeBackend.js +213 -0
- package/dist/testing/fakeBackend.js.map +1 -0
- package/dist/watch/queue.d.ts +2 -0
- package/dist/watch/queue.js +91 -0
- package/dist/watch/queue.js.map +1 -0
- package/dist/watch/types.d.ts +52 -0
- package/dist/watch/types.js +2 -0
- package/dist/watch/types.js.map +1 -0
- package/dist/watch/watcher.d.ts +6 -0
- package/dist/watch/watcher.js +58 -0
- package/dist/watch/watcher.js.map +1 -0
- package/dist/watch/workflow.d.ts +30 -0
- package/dist/watch/workflow.js +62 -0
- package/dist/watch/workflow.js.map +1 -0
- package/docs/architecture.md +603 -0
- package/docs/auth.md +65 -0
- package/docs/cli-behavior.md +95 -0
- package/docs/compile.md +51 -0
- package/docs/design.md +82 -0
- package/docs/endpoint.md +84 -0
- package/docs/npm-packaging.md +148 -0
- package/docs/quickdev-queue-audit.md +193 -0
- package/docs/release-gates.md +119 -0
- package/docs/release-notes-v1.md +97 -0
- package/docs/security.md +61 -0
- package/docs/sync-state.md +305 -0
- package/docs/sync.md +50 -0
- package/docs/troubleshooting.md +124 -0
- package/docs/usage.md +184 -0
- package/examples/minimal-paper/.olcx/auth.local.example.json +7 -0
- package/examples/minimal-paper/.olcx/config.json +23 -0
- package/examples/minimal-paper/README.md +88 -0
- package/examples/minimal-paper/main.tex +23 -0
- package/package.json +66 -0
- package/src/backend/olcli/LICENSE +21 -0
- package/src/backend/olcli/README.md +26 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Release Gates
|
|
2
|
+
|
|
3
|
+
Run this before publishing:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm run prepublish:check
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The command runs build, typecheck, the deterministic test suite, a high-severity
|
|
10
|
+
npm audit, dependency license validation, olcli notice validation, a
|
|
11
|
+
sensitive-value scan over release-relevant files, and an npm pack dry-run
|
|
12
|
+
package-content check.
|
|
13
|
+
|
|
14
|
+
The release checklist is:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm run build
|
|
18
|
+
npm run typecheck
|
|
19
|
+
npm test
|
|
20
|
+
OLCX_E2E_IGNORE_LOCAL_ENV=1 OLCX_E2E_ENABLE_REAL=0 npm run test:e2e:real
|
|
21
|
+
npm audit --audit-level=high
|
|
22
|
+
npm pack --dry-run --json --ignore-scripts
|
|
23
|
+
npm run prepublish:check
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Before freezing a v1 RC, update [docs/release-notes-v1.md](release-notes-v1.md)
|
|
27
|
+
with the final gate results and stable-release decision. Stable release is not approved until a sanitized disposable real Overleaf E2E pass is recorded. The default agent-safe gate may only run the forced skip smoke:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
OLCX_E2E_IGNORE_LOCAL_ENV=1 OLCX_E2E_ENABLE_REAL=0 npm run test:e2e:real
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The forced real E2E skip smoke must use `OLCX_E2E_IGNORE_LOCAL_ENV=1` so it does
|
|
34
|
+
not read a contributor's ignored `.env.e2e.local` file. Do not run real Overleaf
|
|
35
|
+
E2E unless you intentionally set `OLCX_E2E_ENABLE_REAL=1` with a disposable,
|
|
36
|
+
sanitized test project.
|
|
37
|
+
|
|
38
|
+
## npm Trusted Publishing Gate
|
|
39
|
+
|
|
40
|
+
The npm publishing workflow is `.github/workflows/npm-publish.yml`. It publishes
|
|
41
|
+
only from explicit GitHub release publication events and uses the protected
|
|
42
|
+
GitHub environment `npm-publish`. Configure that environment with manual
|
|
43
|
+
reviewer protection before enabling real publication.
|
|
44
|
+
|
|
45
|
+
GitHub release tags must match the package version:
|
|
46
|
+
|
|
47
|
+
- Stable releases use tags like `vX.Y.Z`, non-prerelease GitHub releases, and
|
|
48
|
+
npm dist-tag `latest`.
|
|
49
|
+
- Prereleases use tags like `vX.Y.Z-rc.1`, GitHub prereleases, and npm dist-tag
|
|
50
|
+
`next`.
|
|
51
|
+
|
|
52
|
+
Stable npm publish is blocked until both conditions are true:
|
|
53
|
+
|
|
54
|
+
- `docs/release-notes-v1.md` records stable release approval.
|
|
55
|
+
- A sanitized disposable real Overleaf E2E artifact is recorded and reviewed
|
|
56
|
+
with this concrete reference format:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
Sanitized real E2E artifact: gh-release://umiskky/overleaf-codex/vX.Y.Z/sanitized-real-e2e.md
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Concrete sanitized real E2E artifact reference means the referenced release
|
|
63
|
+
artifact has been reviewed and contains no raw cookie, session value, account
|
|
64
|
+
label, private project id, or private paper content.
|
|
65
|
+
|
|
66
|
+
The forced skip smoke is allowed but is not a stable substitute. It remains
|
|
67
|
+
required because it proves the CI E2E command is safely disabled and does not
|
|
68
|
+
read ignored local environment files.
|
|
69
|
+
|
|
70
|
+
## Package Contents
|
|
71
|
+
|
|
72
|
+
The package allowlist is intentionally small: `assets/`, `dist/`, `docs/`,
|
|
73
|
+
`examples/`, root `README.md`, root `LICENSE`, root `NOTICE.md`,
|
|
74
|
+
`package.json`, `src/backend/olcli/LICENSE`, and
|
|
75
|
+
`src/backend/olcli/README.md`.
|
|
76
|
+
|
|
77
|
+
Inspect the package surface directly with:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm pack --dry-run --json --ignore-scripts
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The gate fails if the dry-run package contains `node_modules/`, tests, scripts,
|
|
84
|
+
real `.olcx/` local state, local or secret JSON, local environment files,
|
|
85
|
+
`tmp/`, generated Overleaf output, E2E output, or logs. The only tracked
|
|
86
|
+
`.olcx` files allowed in the package are
|
|
87
|
+
`examples/minimal-paper/.olcx/config.json` and
|
|
88
|
+
`examples/minimal-paper/.olcx/auth.local.example.json`; both must stay
|
|
89
|
+
sanitized placeholders.
|
|
90
|
+
|
|
91
|
+
## Dependency Licenses
|
|
92
|
+
|
|
93
|
+
The current lockfile uses these compatible license families: `0BSD`,
|
|
94
|
+
`Apache-2.0`, `BSD-2-Clause`, `BSD-3-Clause`, `ISC`, `MIT`, and `MPL-2.0`.
|
|
95
|
+
|
|
96
|
+
`MPL-2.0` appears only as dependency/dev-tool package metadata and does not
|
|
97
|
+
relicense `olcx` source. Do not add GPL, AGPL, LGPL, unknown, or missing-license
|
|
98
|
+
dependencies without a documented legal review and an explicit gate update.
|
|
99
|
+
|
|
100
|
+
## npm Audit
|
|
101
|
+
|
|
102
|
+
The release gate runs:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm audit --audit-level=high
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
There are no active audit exceptions. If a future high-severity advisory cannot
|
|
109
|
+
be fixed before release, document the package, advisory id, severity, runtime
|
|
110
|
+
reachability, mitigation, owner, and expiration date in this file before
|
|
111
|
+
changing the gate.
|
|
112
|
+
|
|
113
|
+
## Third-Party Source Notices
|
|
114
|
+
|
|
115
|
+
`olcx` vendors backend-private source copied or adapted from
|
|
116
|
+
`@aloth/olcli@0.5.0`. Preserve the MIT license text in
|
|
117
|
+
`src/backend/olcli/LICENSE`, the source metadata in `NOTICE.md`, `README.md`,
|
|
118
|
+
and `src/backend/olcli/README.md`, and the attribution header in
|
|
119
|
+
`src/backend/olcli/client.ts`.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# v1 Release Notes
|
|
2
|
+
|
|
3
|
+
Release candidate status: Local release-candidate gates passed; stable release remains blocked until sanitized disposable real Overleaf E2E is recorded.
|
|
4
|
+
|
|
5
|
+
Stable release decision: Not approved for stable release until a sanitized disposable real Overleaf E2E pass is recorded.
|
|
6
|
+
|
|
7
|
+
`olcx` is not an official Overleaf project and is not an official `olcli` project. It is not affiliated with, endorsed by, or maintained by Overleaf or `olcli`.
|
|
8
|
+
|
|
9
|
+
## What v1 Includes
|
|
10
|
+
|
|
11
|
+
- CLI-first paper workflow through `olcx auth`, `olcx init`, `olcx endpoint status`, `olcx endpoint test`, `olcx status`, `olcx doctor`, `olcx sync`, `olcx compile`, and `olcx watch`.
|
|
12
|
+
- One local paper repository binds to one Overleaf project by default.
|
|
13
|
+
- Manual Overleaf endpoint management for `https://www.overleaf.com` and
|
|
14
|
+
`https://cn.overleaf.com`, with read-only probing and explicit
|
|
15
|
+
`olcx endpoint test --apply` selection.
|
|
16
|
+
- Project-local auth stored in `.olcx/auth.local.json`, which must remain ignored by Git.
|
|
17
|
+
- Safe sync planning and conflict stop behavior with `SYNC_CONFLICT`; sync must not silently overwrite local or remote changes.
|
|
18
|
+
- Remote Overleaf-backed compile that writes the PDF to `build/overleaf/main.pdf` by default and does not require local LaTeX.
|
|
19
|
+
- VS Code settings/tasks generated by `olcx init` by default; v1 does not include a VS Code extension.
|
|
20
|
+
- Backend-private code copied or adapted from `@aloth/olcli@0.5.0` with MIT attribution preserved.
|
|
21
|
+
|
|
22
|
+
## Final Gate Checklist
|
|
23
|
+
|
|
24
|
+
These gates must pass before an RC can be considered locally verified:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm run build
|
|
28
|
+
npm run typecheck
|
|
29
|
+
npm test
|
|
30
|
+
OLCX_E2E_IGNORE_LOCAL_ENV=1 OLCX_E2E_ENABLE_REAL=0 npm run test:e2e:real
|
|
31
|
+
npm audit --audit-level=high
|
|
32
|
+
npm pack --dry-run --json --ignore-scripts
|
|
33
|
+
npm run prepublish:check
|
|
34
|
+
npm run dev -- --help
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`npm pack --dry-run --json --ignore-scripts` must match the `package.json` `files` allowlist and must not include local auth, local or secret JSON, environment files, `tmp/`, tests, scripts, generated PDFs, logs, `node_modules/`, or real E2E output.
|
|
38
|
+
|
|
39
|
+
## npm Publishing Status
|
|
40
|
+
|
|
41
|
+
Stable npm publish is blocked. The repository contains
|
|
42
|
+
`.github/workflows/npm-publish.yml` for GitHub Actions Trusted Publishing through
|
|
43
|
+
the protected `npm-publish` environment, but the current v1 notes do not approve
|
|
44
|
+
a stable npm release.
|
|
45
|
+
|
|
46
|
+
Release tags must match `package.json`:
|
|
47
|
+
|
|
48
|
+
- `vX.Y.Z-rc.1` style prerelease versions must use GitHub prereleases and npm
|
|
49
|
+
dist-tag `next`.
|
|
50
|
+
- `vX.Y.Z` stable versions must use non-prerelease GitHub releases and npm
|
|
51
|
+
dist-tag `latest` only after stable approval.
|
|
52
|
+
|
|
53
|
+
Sanitized real E2E artifact: not recorded for this release candidate. This line
|
|
54
|
+
is a placeholder for the future sanitized artifact reference and is not evidence
|
|
55
|
+
of a completed real E2E run.
|
|
56
|
+
|
|
57
|
+
Future stable approval must replace the placeholder with this concrete format:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
Sanitized real E2E artifact: gh-release://umiskky/overleaf-codex/vX.Y.Z/sanitized-real-e2e.md
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The forced skip smoke is allowed but is not a stable substitute. It only proves
|
|
64
|
+
the CI command is safely disabled for agent-safe release-candidate checks.
|
|
65
|
+
|
|
66
|
+
## Real Overleaf E2E Policy
|
|
67
|
+
|
|
68
|
+
The default release-candidate verification runs only the forced skip smoke:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
OLCX_E2E_IGNORE_LOCAL_ENV=1 OLCX_E2E_ENABLE_REAL=0 npm run test:e2e:real
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This command must not read `.env.e2e.local` and must not contact Overleaf.
|
|
75
|
+
|
|
76
|
+
A stable release requires a separate disposable real Overleaf E2E pass. The recorded artifact must be sanitized and must state:
|
|
77
|
+
|
|
78
|
+
- the command completed successfully against a disposable project;
|
|
79
|
+
- no raw cookie, session value, account label, private project id, or private paper content is recorded;
|
|
80
|
+
- the artifact path is a sanitized handoff or release-management artifact, not a packaged npm file.
|
|
81
|
+
|
|
82
|
+
No raw real-E2E credentials, project IDs, cookies, or private paper contents may be committed.
|
|
83
|
+
|
|
84
|
+
## Known limitations
|
|
85
|
+
|
|
86
|
+
- Overleaf access depends on Overleaf private interfaces and may break when Overleaf changes its web or compile behavior.
|
|
87
|
+
- Authentication uses a session cookie or environment-provided token-like value; v1 must not store Overleaf passwords.
|
|
88
|
+
- JSON output mode is reserved for a future release.
|
|
89
|
+
- v1 is a CLI release. A VS Code extension is intentionally out of scope.
|
|
90
|
+
- Real E2E is gated because it needs disposable Overleaf credentials and a disposable Overleaf project.
|
|
91
|
+
|
|
92
|
+
## Post-v1 roadmap
|
|
93
|
+
|
|
94
|
+
- Add a stable release process after sanitized disposable real E2E is recorded.
|
|
95
|
+
- Improve structured output for automation.
|
|
96
|
+
- Expand compatibility coverage as Overleaf behavior changes.
|
|
97
|
+
- Consider a VS Code extension only after the CLI workflow is stable.
|
package/docs/security.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
`olcx` handles Overleaf authorization data. Treat that data as secret.
|
|
4
|
+
|
|
5
|
+
## Storage model
|
|
6
|
+
|
|
7
|
+
Authorization is project-local by default:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
.olcx/auth.local.json
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This supports the common case where different paper repositories use different
|
|
14
|
+
Overleaf accounts.
|
|
15
|
+
|
|
16
|
+
Project binding and workflow settings live in:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
.olcx/config.json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`config.json` is designed to be shareable, but it may still reveal an Overleaf
|
|
23
|
+
project id. Users should decide whether their paper repository is private.
|
|
24
|
+
|
|
25
|
+
## Git rules
|
|
26
|
+
|
|
27
|
+
The following files must never be committed:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
.olcx/auth.local.json
|
|
31
|
+
.olcx/*.local.json
|
|
32
|
+
.olcx/*.secret.json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Generated PDFs and LaTeX build artifacts are ignored by default:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
build/overleaf/
|
|
39
|
+
*.aux
|
|
40
|
+
*.log
|
|
41
|
+
*.synctex.gz
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Passwords
|
|
45
|
+
|
|
46
|
+
The first version must not store Overleaf passwords. Auth should use a session
|
|
47
|
+
cookie or environment-provided token-like value.
|
|
48
|
+
|
|
49
|
+
## Headless usage
|
|
50
|
+
|
|
51
|
+
If a server has no browser, the expected flow is:
|
|
52
|
+
|
|
53
|
+
1. Log in to Overleaf from any browser.
|
|
54
|
+
2. Copy the required session value.
|
|
55
|
+
3. Provide it to `olcx auth` or an environment variable on the server.
|
|
56
|
+
4. Store it in the paper repository's ignored local auth file.
|
|
57
|
+
|
|
58
|
+
## Reporting
|
|
59
|
+
|
|
60
|
+
`olcx status` may show the account label or email when available, but it must not
|
|
61
|
+
print the raw session cookie.
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Sync State
|
|
2
|
+
|
|
3
|
+
## Purpose And Scope
|
|
4
|
+
|
|
5
|
+
`olcx sync` and `olcx watch` use one shared state machine for file comparison,
|
|
6
|
+
ignore handling, delete safety, conflict semantics, and recovery guidance. Watch
|
|
7
|
+
does not define a second conflict model; it pauses on the same conflicts that a
|
|
8
|
+
manual sync reports.
|
|
9
|
+
|
|
10
|
+
This document defines planning, local snapshots, and conflict reporting. It does
|
|
11
|
+
not implement real Overleaf calls, backend adapters, filesystem reads, filesystem
|
|
12
|
+
writes, or command wiring.
|
|
13
|
+
|
|
14
|
+
## Local State Files
|
|
15
|
+
|
|
16
|
+
The sync state file is:
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
.olcx/state/sync.json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
It stores the last successful bidirectional sync baseline used by sync and watch.
|
|
23
|
+
It is local-only state, must remain ignored by Git, and is not shareable project
|
|
24
|
+
configuration.
|
|
25
|
+
|
|
26
|
+
The conflict report file is:
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
.olcx/state/conflicts.json
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
It stores the latest local diagnostic report for a paused sync/watch flow. It is
|
|
33
|
+
not a source of file truth and must not be committed.
|
|
34
|
+
|
|
35
|
+
Neither file may contain authorization data, credentials, session values,
|
|
36
|
+
passwords, cookies, file contents, private compile logs, raw backend responses,
|
|
37
|
+
or private paper content.
|
|
38
|
+
|
|
39
|
+
## Content Digest
|
|
40
|
+
|
|
41
|
+
Content digests use SHA-256 and are encoded as lowercase hexadecimal strings.
|
|
42
|
+
The digest is computed over exact bytes. If a caller has a string, it hashes
|
|
43
|
+
`Buffer.from(value)` with the default UTF-8 encoding. If a caller has bytes, it
|
|
44
|
+
hashes those bytes directly.
|
|
45
|
+
|
|
46
|
+
Line endings are not normalized before hashing. This keeps the baseline honest:
|
|
47
|
+
if local bytes differ from remote bytes, the hashes differ.
|
|
48
|
+
|
|
49
|
+
Remote digests may use backend-provided hashes only when the backend hash is
|
|
50
|
+
semantically the same content digest. If not, a later backend task must download
|
|
51
|
+
the bytes and hash them before planning.
|
|
52
|
+
|
|
53
|
+
## Path Normalization
|
|
54
|
+
|
|
55
|
+
All sync paths are repository-relative POSIX-style paths with forward slashes.
|
|
56
|
+
Inputs such as `./main.tex` normalize to `main.tex`, and Windows separators are
|
|
57
|
+
converted to `/`.
|
|
58
|
+
|
|
59
|
+
Absolute paths and parent traversal are not valid sync targets. Paths such as
|
|
60
|
+
`/tmp/main.tex`, `../main.tex`, and `sections/../main.tex` must not escape the
|
|
61
|
+
paper repository. A pure planner may treat them as safe ignored inputs or as
|
|
62
|
+
unsupported conflicts, but it must never upload, download, delete, or watch them.
|
|
63
|
+
|
|
64
|
+
## State Schema
|
|
65
|
+
|
|
66
|
+
The persisted state shape is:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"schemaVersion": 1,
|
|
71
|
+
"hashAlgorithm": "sha256",
|
|
72
|
+
"updatedAt": "2026-06-25T08:00:00.000Z",
|
|
73
|
+
"files": {
|
|
74
|
+
"main.tex": {
|
|
75
|
+
"path": "main.tex",
|
|
76
|
+
"contentHash": "<sha256-hex>",
|
|
77
|
+
"size": 1234,
|
|
78
|
+
"localModifiedAt": "2026-06-25T07:59:00.000Z",
|
|
79
|
+
"remoteModifiedAt": "2026-06-25T07:58:00.000Z",
|
|
80
|
+
"remoteId": "<remote-file-id>",
|
|
81
|
+
"remoteRevision": "<remote-revision>",
|
|
82
|
+
"syncedAt": "2026-06-25T08:00:00.000Z"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`schemaVersion` is `1` for the v1 state file. `hashAlgorithm` is `sha256`.
|
|
89
|
+
`updatedAt` is the time the state file was last replaced after a successful
|
|
90
|
+
non-dry-run sync.
|
|
91
|
+
|
|
92
|
+
Each file entry stores the repository-relative path, content hash, optional size,
|
|
93
|
+
optional local and remote modified times, optional remote identity metadata, and
|
|
94
|
+
the time that path was last known to be synchronized.
|
|
95
|
+
|
|
96
|
+
## Remote And Local Snapshot Fields
|
|
97
|
+
|
|
98
|
+
Local snapshots use these fields:
|
|
99
|
+
|
|
100
|
+
| Field | Meaning |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `path` | Repository-relative POSIX path. |
|
|
103
|
+
| `exists` | Whether the local file exists at snapshot time. |
|
|
104
|
+
| `contentHash` | SHA-256 digest when the file exists. |
|
|
105
|
+
| `size` | File size in bytes when available. |
|
|
106
|
+
| `modifiedAt` | Local modified timestamp when available. |
|
|
107
|
+
| `ignored` | Whether the path was already classified as ignored by the caller. |
|
|
108
|
+
|
|
109
|
+
Remote snapshots use these fields:
|
|
110
|
+
|
|
111
|
+
| Field | Meaning |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `path` | Repository-relative POSIX path. |
|
|
114
|
+
| `exists` | Whether the remote file exists at snapshot time. |
|
|
115
|
+
| `contentHash` | SHA-256 digest or equivalent backend content digest. |
|
|
116
|
+
| `size` | Remote file size in bytes when available. |
|
|
117
|
+
| `modifiedAt` | Remote modified timestamp when available. |
|
|
118
|
+
| `remoteId` | Backend file identifier for later operations. |
|
|
119
|
+
| `revision` | Backend revision marker for conflict diagnostics. |
|
|
120
|
+
| `binary` | Whether the remote entry should be treated as binary. |
|
|
121
|
+
|
|
122
|
+
Snapshots contain metadata only. They do not contain file bodies.
|
|
123
|
+
|
|
124
|
+
## Ignore Precedence
|
|
125
|
+
|
|
126
|
+
Ignore handling is deterministic and shared by sync and watch.
|
|
127
|
+
|
|
128
|
+
1. Normalize paths to repository-relative POSIX paths.
|
|
129
|
+
2. Reject or safe-ignore absolute paths and paths containing `..`.
|
|
130
|
+
3. Apply built-in safety ignores first.
|
|
131
|
+
4. Apply user-configured ignores second.
|
|
132
|
+
5. Do not support negated unignore rules such as `!path` in v1.
|
|
133
|
+
|
|
134
|
+
The built-in safety ignores are:
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
.git/
|
|
138
|
+
node_modules/
|
|
139
|
+
.olcx/auth.local.json
|
|
140
|
+
.olcx/*.local.json
|
|
141
|
+
.olcx/*.secret.json
|
|
142
|
+
.olcx/state/
|
|
143
|
+
build/overleaf/
|
|
144
|
+
*.aux
|
|
145
|
+
*.bbl
|
|
146
|
+
*.bcf
|
|
147
|
+
*.blg
|
|
148
|
+
*.fdb_latexmk
|
|
149
|
+
*.fls
|
|
150
|
+
*.log
|
|
151
|
+
*.out
|
|
152
|
+
*.run.xml
|
|
153
|
+
*.synctex.gz
|
|
154
|
+
*.toc
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
User ignores are appended after the built-in set, so they can exclude more files
|
|
158
|
+
but cannot re-include a built-in safety ignore.
|
|
159
|
+
|
|
160
|
+
Ignored paths become `ignored` operations. They never upload, download, delete,
|
|
161
|
+
watch-trigger, or become blocking conflicts.
|
|
162
|
+
|
|
163
|
+
## SyncPlan Operations
|
|
164
|
+
|
|
165
|
+
`SyncPlan` operations are:
|
|
166
|
+
|
|
167
|
+
| Operation | Meaning |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| `upload` | Local content is the only safe change and should be sent to remote during apply. |
|
|
170
|
+
| `download` | Remote content is the only safe change and should be written locally during apply. |
|
|
171
|
+
| `deleteLocal` | A remote deletion may remove the local file only in an explicit delete-allowed flow. |
|
|
172
|
+
| `deleteRemote` | A local deletion may remove the remote file only in an explicit delete-allowed flow. |
|
|
173
|
+
| `unchanged` | No apply action is needed for this path. |
|
|
174
|
+
| `conflict` | Automatic apply must pause for this path. |
|
|
175
|
+
| `ignored` | The path is excluded and must not be applied or reported as a conflict. |
|
|
176
|
+
|
|
177
|
+
The v1 default matrix is:
|
|
178
|
+
|
|
179
|
+
| Baseline | Local now | Remote now | Result |
|
|
180
|
+
| --- | --- | --- | --- |
|
|
181
|
+
| absent | present | absent | `upload` |
|
|
182
|
+
| absent | absent | present | `download` |
|
|
183
|
+
| absent | present hash A | present hash A | `unchanged` |
|
|
184
|
+
| absent | present hash A | present hash B | `conflict`, `both-modified` |
|
|
185
|
+
| present hash A | present hash A | present hash A | `unchanged` |
|
|
186
|
+
| present hash A | present hash B | present hash A | `upload` |
|
|
187
|
+
| present hash A | present hash A | present hash B | `download` |
|
|
188
|
+
| present hash A | present hash B | present hash B | `unchanged` |
|
|
189
|
+
| present hash A | present hash B | present hash C | `conflict`, `both-modified` |
|
|
190
|
+
| present hash A | present hash B | absent | `conflict`, `local-modified-remote-deleted` |
|
|
191
|
+
| present hash A | absent | present hash B | `conflict`, `remote-modified-local-deleted` |
|
|
192
|
+
| present hash A | present hash A | absent | default `conflict`, `unsafe-delete`; with `allowDeletes: true`, `deleteLocal` |
|
|
193
|
+
| present hash A | absent | present hash A | default `conflict`, `unsafe-delete`; with `allowDeletes: true`, `deleteRemote` |
|
|
194
|
+
| present hash A | absent | absent | `unchanged`, `both-deleted` |
|
|
195
|
+
| any | ignored | any | `ignored` |
|
|
196
|
+
| any | any | ignored | `ignored` |
|
|
197
|
+
|
|
198
|
+
Any non-empty `conflicts` list pauses automatic sync and watch application.
|
|
199
|
+
|
|
200
|
+
## V1 Delete Policy
|
|
201
|
+
|
|
202
|
+
Automatic deletes are disabled by default in CLI v1. A missing file on one side
|
|
203
|
+
can mean a deliberate delete, a stale listing, a backend issue, or a local
|
|
204
|
+
mistake. The default planner therefore downgrades risky or implicit deletes to a
|
|
205
|
+
`conflict` with reason `unsafe-delete`.
|
|
206
|
+
|
|
207
|
+
`deleteLocal` and `deleteRemote` remain in the type contract for a future
|
|
208
|
+
explicit user-confirmed flow or a carefully gated internal `allowDeletes` mode.
|
|
209
|
+
The default CLI path must not pass `allowDeletes: true`.
|
|
210
|
+
|
|
211
|
+
## Conflict Report Format
|
|
212
|
+
|
|
213
|
+
The conflict report path is:
|
|
214
|
+
|
|
215
|
+
```text
|
|
216
|
+
.olcx/state/conflicts.json
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The report contains paths, content hashes, sizes, timestamps, known remote
|
|
220
|
+
metadata, suggested commands, watch pause state, and manual steps. It does not
|
|
221
|
+
include the raw project id because resolving conflicts does not require it.
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"schemaVersion": 1,
|
|
228
|
+
"generatedAt": "2026-06-25T08:00:00.000Z",
|
|
229
|
+
"reportPath": ".olcx/state/conflicts.json",
|
|
230
|
+
"syncStatePath": ".olcx/state/sync.json",
|
|
231
|
+
"watch": {
|
|
232
|
+
"paused": true,
|
|
233
|
+
"reason": "sync-conflict",
|
|
234
|
+
"resumeCommand": "olcx watch"
|
|
235
|
+
},
|
|
236
|
+
"conflicts": [
|
|
237
|
+
{
|
|
238
|
+
"path": "main.tex",
|
|
239
|
+
"reason": "both-modified",
|
|
240
|
+
"local": {
|
|
241
|
+
"contentHash": "<local-sha256-hex>",
|
|
242
|
+
"size": 1234,
|
|
243
|
+
"modifiedAt": "2026-06-25T08:00:00.000Z"
|
|
244
|
+
},
|
|
245
|
+
"remote": {
|
|
246
|
+
"contentHash": "<remote-sha256-hex>",
|
|
247
|
+
"size": 1250,
|
|
248
|
+
"modifiedAt": "2026-06-25T08:01:00.000Z",
|
|
249
|
+
"remoteId": "<remote-file-id>",
|
|
250
|
+
"revision": "<remote-revision>"
|
|
251
|
+
},
|
|
252
|
+
"base": {
|
|
253
|
+
"contentHash": "<base-sha256-hex>",
|
|
254
|
+
"size": 1200,
|
|
255
|
+
"syncedAt": "2026-06-25T07:50:00.000Z"
|
|
256
|
+
},
|
|
257
|
+
"suggestedCommands": ["olcx sync --dry-run", "olcx sync"],
|
|
258
|
+
"manualSteps": [
|
|
259
|
+
"Review main.tex locally and in Overleaf.",
|
|
260
|
+
"Review both versions, merge manually, then run olcx sync --dry-run.",
|
|
261
|
+
"Run olcx sync --dry-run before applying changes."
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
],
|
|
265
|
+
"manualSteps": [
|
|
266
|
+
"Open each conflict path locally and in Overleaf.",
|
|
267
|
+
"Choose local, remote, or a manual merge.",
|
|
268
|
+
"Run olcx sync --dry-run.",
|
|
269
|
+
"Run olcx sync after the dry run is clean.",
|
|
270
|
+
"Restart olcx watch if you use the watcher."
|
|
271
|
+
]
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Conflict reports must copy only known metadata fields. They must exclude full
|
|
276
|
+
file contents, cookies, passwords, session values, authorization data, CSRF
|
|
277
|
+
values, raw private logs, and raw backend responses. Formatting must pass
|
|
278
|
+
serialized output through the shared redaction helper before display or storage.
|
|
279
|
+
|
|
280
|
+
## Snapshot Update Rules
|
|
281
|
+
|
|
282
|
+
- Do not update `.olcx/state/sync.json` during `--dry-run`.
|
|
283
|
+
- Do not update the snapshot when the plan contains any `conflict`.
|
|
284
|
+
- Do not update the snapshot when any apply step fails.
|
|
285
|
+
- Update the snapshot only after a complete successful non-dry-run sync apply.
|
|
286
|
+
- For `upload` or `download`, the new baseline digest is the resulting content
|
|
287
|
+
hash shared by both sides.
|
|
288
|
+
- For `unchanged`, keep or refresh metadata when local and remote hashes match.
|
|
289
|
+
- For `ignored`, do not persist ignored path entries.
|
|
290
|
+
- For successful user-confirmed deletes in a future flow, remove the deleted
|
|
291
|
+
path from the state.
|
|
292
|
+
- Never write auth data, cookies, session values, file contents, private logs, or
|
|
293
|
+
full remote responses into the state file.
|
|
294
|
+
|
|
295
|
+
## Testing Contract
|
|
296
|
+
|
|
297
|
+
Sync state-machine tests cover pure functions with in-memory snapshots. They do
|
|
298
|
+
not use real Overleaf, local LaTeX, filesystem writes, backend calls, network
|
|
299
|
+
calls, Commander imports, or CLI wiring.
|
|
300
|
+
|
|
301
|
+
Required coverage includes local-only upload, remote-only download, unchanged
|
|
302
|
+
hashes, built-in ignores, user ignores, both-modified conflicts, local-modified
|
|
303
|
+
versus remote-deleted conflicts, remote-modified versus local-deleted conflicts,
|
|
304
|
+
default unsafe delete downgrades, explicit `allowDeletes` mode, dry-run flag
|
|
305
|
+
preservation, summary counts, and conflict report redaction.
|
package/docs/sync.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Sync
|
|
2
|
+
|
|
3
|
+
`olcx sync` synchronizes the local paper repository with the bound Overleaf
|
|
4
|
+
project. It must not silently overwrite local or remote changes.
|
|
5
|
+
|
|
6
|
+
## Dry Run First
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
olcx sync --dry-run
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The dry run prints planned uploads, downloads, and deletes without changing
|
|
13
|
+
files. Use it before any manual sync in a repository that may have local or
|
|
14
|
+
remote edits.
|
|
15
|
+
|
|
16
|
+
## Apply A Clean Plan
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
olcx sync
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`olcx sync` applies the plan only when it can do so safely. The sync state is
|
|
23
|
+
stored under `.olcx/state/` and generated reports are local-only.
|
|
24
|
+
|
|
25
|
+
## Conflict Handling
|
|
26
|
+
|
|
27
|
+
If the same path changed locally and on Overleaf, `olcx` exits with
|
|
28
|
+
`SYNC_CONFLICT` and writes:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
.olcx/state/conflicts.json
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Review each listed path locally and in Overleaf, choose the correct content or
|
|
35
|
+
perform a manual merge, then rerun:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
olcx sync --dry-run
|
|
39
|
+
cat .olcx/state/conflicts.json
|
|
40
|
+
olcx sync
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Do not delete the conflict report until you understand why the conflict
|
|
44
|
+
happened.
|
|
45
|
+
Do not commit `.olcx/state/` files. Conflict reports contain metadata and paths only; they must not contain cookies, auth values, raw backend responses, private logs, or paper content.
|
|
46
|
+
|
|
47
|
+
## Watch Integration
|
|
48
|
+
|
|
49
|
+
`olcx watch` pauses when sync reports a conflict. Resolve the conflict manually,
|
|
50
|
+
confirm the dry run is clean, then restart the watcher.
|