@chalksurf/cli 0.1.0 → 0.2.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 +31 -94
- package/dist/bin/chalksurf.js +84 -10
- package/dist/commands/auth.js +43 -24
- package/dist/commands/exercise.js +428 -0
- package/dist/commands/job.js +20 -10
- package/dist/commands/org.js +20 -11
- package/dist/commands/profile.js +97 -0
- package/dist/commands/sheet.js +287 -130
- package/dist/lib/api-client.js +9 -3
- package/dist/lib/cli-error.js +37 -1
- package/dist/lib/config-store.js +188 -10
- package/dist/lib/import-files.js +120 -0
- package/dist/lib/import-output.js +67 -0
- package/dist/lib/manifest.js +183 -19
- package/dist/lib/output.js +35 -2
- package/dist/lib/prompt-secret.js +32 -0
- package/dist/lib/session.js +1 -1
- package/dist/lib/source-resolver.js +72 -10
- package/dist/lib/translation-languages.js +1 -0
- package/dist/lib/user-jobs.js +16 -1
- package/docs/agents.md +203 -0
- package/docs/examples/exercise-import-manifest.json +13 -0
- package/docs/examples/exercise-solution-import-manifest.json +13 -0
- package/docs/examples/sheet-import-manifest.json +33 -0
- package/docs/exit-codes.md +57 -0
- package/docs/manifest.md +129 -0
- package/docs/manual.md +169 -0
- package/package.json +8 -4
- package/schemas/exercise-import-manifest.schema.json +104 -0
- package/schemas/exercise-solution-import-manifest.schema.json +104 -0
- package/schemas/sheet-import-manifest.schema.json +142 -0
package/docs/agents.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Agent And Codex Guide
|
|
2
|
+
|
|
3
|
+
Use this flow when ChalkSurf is driven by Codex, CI, or another orchestration layer.
|
|
4
|
+
|
|
5
|
+
## Authentication Strategy
|
|
6
|
+
|
|
7
|
+
Agents must always use an explicit profile. The profile name must follow the
|
|
8
|
+
`ENV-AGENT_TYPE` convention, such as `prod-codex`, `staging-codex`, `dev-claude`,
|
|
9
|
+
or `prod-claude`.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
chalksurf --profile prod-codex auth status --json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Do not rely on the default profile in agent-driven runs. The default profile is
|
|
16
|
+
for the human operator's terminal workflow and may point at a different
|
|
17
|
+
environment, user token, or selected organization.
|
|
18
|
+
|
|
19
|
+
Create or refresh an agent profile explicitly:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
printf '%s' "$CHALKSURF_TOKEN" | chalksurf auth login \
|
|
23
|
+
--profile prod-codex \
|
|
24
|
+
--base-url https://chalksurf-api.fly.dev \
|
|
25
|
+
--with-token
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Use `CHALKSURF_TOKEN` only as the source for login or as a short-lived override.
|
|
29
|
+
Prefer storing the token in the explicit agent profile for repeated Codex/CI runs.
|
|
30
|
+
|
|
31
|
+
## Command Contract
|
|
32
|
+
|
|
33
|
+
For agent-driven imports, always use:
|
|
34
|
+
|
|
35
|
+
- `--manifest -` or a generated manifest file
|
|
36
|
+
- `--wait` when the next step depends on completed imports
|
|
37
|
+
- `--json` so the response stays machine-readable
|
|
38
|
+
|
|
39
|
+
Recommended invocation shape:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
chalksurf --profile prod-codex sheet import --manifest - --wait --json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
In `--json` mode:
|
|
46
|
+
|
|
47
|
+
- stdout contains a stable JSON envelope
|
|
48
|
+
- known failures still produce JSON on stdout
|
|
49
|
+
- stderr is reserved for unexpected runtime failures
|
|
50
|
+
|
|
51
|
+
Wait-style failures still include a populated `result` payload, so agents can inspect partial outcomes on exit code `6` or `7`.
|
|
52
|
+
|
|
53
|
+
## Manifest Design
|
|
54
|
+
|
|
55
|
+
Prefer manifests over large positional argument lists.
|
|
56
|
+
|
|
57
|
+
Why:
|
|
58
|
+
|
|
59
|
+
- multi-source imports stay explicit
|
|
60
|
+
- each logical source can carry a stable `sourceId`
|
|
61
|
+
- sheet imports can group multiple source files into one resulting sheet
|
|
62
|
+
- sheet imports carry `targetFolderPath`, `title`, and `translateTo` at the sheet level
|
|
63
|
+
- agents can correlate import results back to the discovered source set
|
|
64
|
+
|
|
65
|
+
Use `sourceId` whenever a browsing step or upstream scraper already has a stable identifier:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"organizationId": "org_123",
|
|
70
|
+
"wait": true,
|
|
71
|
+
"sheets": [
|
|
72
|
+
{
|
|
73
|
+
"targetFolderPath": "OKTV/2014",
|
|
74
|
+
"title": "OKTV 2014 Round 1",
|
|
75
|
+
"translateTo": ["english"],
|
|
76
|
+
"sources": [
|
|
77
|
+
{
|
|
78
|
+
"sourceId": "oktv-2014-round-1-part-a",
|
|
79
|
+
"kind": "url",
|
|
80
|
+
"url": "https://example.com/oktv-2014-round-1-part-a.pdf"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"sourceId": "oktv-2014-round-1-part-b",
|
|
84
|
+
"kind": "url",
|
|
85
|
+
"url": "https://example.com/oktv-2014-round-1-part-b.pdf"
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For sheet imports, `targetFolderPath` is the destination of the resulting sheet. Use `null` for the root folder. Source-level `relativePath` is only a filename or source-label override and should not be used as a destination path.
|
|
94
|
+
|
|
95
|
+
Further reference:
|
|
96
|
+
|
|
97
|
+
- [Manifest reference](./manifest.md)
|
|
98
|
+
- [Sheet import schema](../schemas/sheet-import-manifest.schema.json)
|
|
99
|
+
- [Exercise import schema](../schemas/exercise-import-manifest.schema.json)
|
|
100
|
+
- [Exercise solution import schema](../schemas/exercise-solution-import-manifest.schema.json)
|
|
101
|
+
|
|
102
|
+
## JSON Result Shape
|
|
103
|
+
|
|
104
|
+
Import commands normalize results around:
|
|
105
|
+
|
|
106
|
+
- `request`
|
|
107
|
+
- `sources`
|
|
108
|
+
- `jobs`
|
|
109
|
+
- `summary`
|
|
110
|
+
|
|
111
|
+
Each resolved source includes stable correlation fields:
|
|
112
|
+
|
|
113
|
+
- `sourceIndex`
|
|
114
|
+
- `sourceInputIndex`
|
|
115
|
+
- `sourceId`
|
|
116
|
+
|
|
117
|
+
Each job includes:
|
|
118
|
+
|
|
119
|
+
- `jobId`
|
|
120
|
+
- `sourceIndexes`
|
|
121
|
+
- `sourceIds`
|
|
122
|
+
- `status`
|
|
123
|
+
|
|
124
|
+
`summary` includes:
|
|
125
|
+
|
|
126
|
+
- source and job counts
|
|
127
|
+
- per-status counts
|
|
128
|
+
- `timedOut`
|
|
129
|
+
|
|
130
|
+
## Recommended Control Flow
|
|
131
|
+
|
|
132
|
+
1. Discover candidate URLs or local files.
|
|
133
|
+
2. Filter them to the target scope.
|
|
134
|
+
3. Build a manifest with stable `sourceId` values.
|
|
135
|
+
4. Pipe the manifest into the CLI with `--wait --json`.
|
|
136
|
+
5. Inspect `ok`, `result.summary`, and `result.jobs`.
|
|
137
|
+
6. Retry only the failed or timed-out source set.
|
|
138
|
+
|
|
139
|
+
For example:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
cat import.json | chalksurf --profile prod-codex sheet import --manifest - --wait --json
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
On success, the envelope looks like:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"schemaVersion": "v1",
|
|
150
|
+
"command": "sheet import",
|
|
151
|
+
"ok": true,
|
|
152
|
+
"result": {
|
|
153
|
+
"request": {},
|
|
154
|
+
"sources": [],
|
|
155
|
+
"jobs": [],
|
|
156
|
+
"summary": {
|
|
157
|
+
"timedOut": false
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"warnings": []
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
On timeout or job failure, `ok` becomes `false`, `error.code` is stable, and `result` still contains the normalized import data needed for retries.
|
|
165
|
+
|
|
166
|
+
## Codex Workflow Example
|
|
167
|
+
|
|
168
|
+
Target task:
|
|
169
|
+
|
|
170
|
+
Import all OKTV exercise sheets between 2010 and 2020 from an archive page, and translate the non-English sheets to English.
|
|
171
|
+
|
|
172
|
+
Recommended workflow:
|
|
173
|
+
|
|
174
|
+
1. Codex browses the archive page and collects the relevant links.
|
|
175
|
+
2. Codex filters the links to years `2010` through `2020`.
|
|
176
|
+
3. Codex builds a sheet-import manifest with one `sheets[]` entry per resulting sheet.
|
|
177
|
+
4. Codex groups multiple source files inside one `sources[]` array when they belong to the same resulting sheet.
|
|
178
|
+
5. Codex sets `translateTo: ["english"]` on the sheet entries that should produce an English translation.
|
|
179
|
+
6. Codex runs `chalksurf --profile prod-codex sheet import --manifest - --wait --json`.
|
|
180
|
+
7. Codex inspects `result.summary.failed`, `result.summary.timedOut`, and `jobs` to decide whether to retry or report failures.
|
|
181
|
+
|
|
182
|
+
Minimal shell shape:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
cat import.json | chalksurf --profile prod-codex sheet import --manifest - --wait --json
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The discovery step belongs outside the ChalkSurf CLI. The CLI contract starts at a concrete manifest and ends at structured import results.
|
|
189
|
+
|
|
190
|
+
## Failure Handling
|
|
191
|
+
|
|
192
|
+
Use the exit code and JSON `error.code` together:
|
|
193
|
+
|
|
194
|
+
- `2` / `usage_error`: the manifest or command input is invalid
|
|
195
|
+
- `3` / `not_authenticated`: missing or invalid token
|
|
196
|
+
- `4` / `source_resolution_failed`: local file, directory, or URL preparation failed
|
|
197
|
+
- `5` / `api_error`: the ChalkSurf API rejected the request
|
|
198
|
+
- `6` / `wait_timed_out`: jobs did not finish in time
|
|
199
|
+
- `7` / `job_failed`: one or more jobs finished with failure
|
|
200
|
+
|
|
201
|
+
Reference:
|
|
202
|
+
|
|
203
|
+
- [Exit codes and JSON errors](./exit-codes.md)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"organizationId": "org_123",
|
|
3
|
+
"exerciseSheetId": "00000000-0000-4000-8000-000000000111",
|
|
4
|
+
"wait": true,
|
|
5
|
+
"sources": [
|
|
6
|
+
{
|
|
7
|
+
"sourceId": "problem-1",
|
|
8
|
+
"kind": "local",
|
|
9
|
+
"path": "./imports/problem-1.pdf",
|
|
10
|
+
"relativePath": "Problem Set/problem-1.pdf"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"organizationId": "org_123",
|
|
3
|
+
"exerciseId": "00000000-0000-4000-8000-000000000222",
|
|
4
|
+
"wait": true,
|
|
5
|
+
"sources": [
|
|
6
|
+
{
|
|
7
|
+
"sourceId": "problem-1-solution",
|
|
8
|
+
"kind": "local",
|
|
9
|
+
"path": "./imports/problem-1-solution.pdf",
|
|
10
|
+
"relativePath": "Solutions/problem-1.pdf"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"organizationId": "org_123",
|
|
3
|
+
"wait": true,
|
|
4
|
+
"sheets": [
|
|
5
|
+
{
|
|
6
|
+
"targetFolderPath": "OKTV/2014",
|
|
7
|
+
"title": "OKTV 2014 Round 1",
|
|
8
|
+
"translateTo": ["english"],
|
|
9
|
+
"sources": [
|
|
10
|
+
{
|
|
11
|
+
"sourceId": "oktv-2014-round-1-part-a",
|
|
12
|
+
"kind": "url",
|
|
13
|
+
"url": "https://example.com/oktv-2014-round-1-part-a.pdf"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"sourceId": "oktv-2014-round-1-part-b",
|
|
17
|
+
"kind": "url",
|
|
18
|
+
"url": "https://example.com/oktv-2014-round-1-part-b.pdf"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"targetFolderPath": null,
|
|
24
|
+
"sources": [
|
|
25
|
+
{
|
|
26
|
+
"sourceId": "practice-sheet",
|
|
27
|
+
"kind": "local",
|
|
28
|
+
"path": "./practice-sheet.pdf"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Exit Codes And JSON Errors
|
|
2
|
+
|
|
3
|
+
The CLI uses stable exit codes so automation can branch without parsing human text.
|
|
4
|
+
|
|
5
|
+
## Exit Codes
|
|
6
|
+
|
|
7
|
+
| Exit code | Error code | Meaning |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `0` | n/a | Success |
|
|
10
|
+
| `1` | `unexpected_error` | Unhandled runtime failure |
|
|
11
|
+
| `2` | `usage_error` | Invalid arguments, invalid manifest, or missing required config such as `--base-url` |
|
|
12
|
+
| `3` | `not_authenticated` | Missing token or rejected CLI token |
|
|
13
|
+
| `4` | `source_resolution_failed` | Local file, directory, URL, or relative path resolution failed |
|
|
14
|
+
| `5` | `api_error` | The ChalkSurf API returned a non-auth failure |
|
|
15
|
+
| `6` | `wait_timed_out` | Waiting ended before all jobs reached a terminal state |
|
|
16
|
+
| `7` | `job_failed` | One or more jobs completed with failure |
|
|
17
|
+
|
|
18
|
+
These same codes are mirrored in `error.exitCode` and `error.code` inside the JSON envelope.
|
|
19
|
+
|
|
20
|
+
## JSON Envelope
|
|
21
|
+
|
|
22
|
+
In `--json` mode, stdout contains a stable envelope:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"schemaVersion": "v1",
|
|
27
|
+
"command": "sheet import",
|
|
28
|
+
"ok": true,
|
|
29
|
+
"result": {},
|
|
30
|
+
"warnings": []
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Known failures also return JSON on stdout:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"schemaVersion": "v1",
|
|
39
|
+
"command": "sheet import",
|
|
40
|
+
"ok": false,
|
|
41
|
+
"result": {},
|
|
42
|
+
"error": {
|
|
43
|
+
"code": "source_resolution_failed",
|
|
44
|
+
"message": "Local source \"/tmp/imports/missing.pdf\" does not exist.",
|
|
45
|
+
"exitCode": 4,
|
|
46
|
+
"retryable": false
|
|
47
|
+
},
|
|
48
|
+
"warnings": []
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Contract Notes
|
|
53
|
+
|
|
54
|
+
- `warnings` contains informational messages that would otherwise be printed for humans.
|
|
55
|
+
- In `--json` mode, stderr is reserved for unexpected runtime failures only.
|
|
56
|
+
- Wait-style commands and imports keep their `result` payload even when `ok` is `false`.
|
|
57
|
+
- Import commands normalize their `result` payloads around `request`, `sources`, `jobs`, and `summary`.
|
package/docs/manifest.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Manifest Reference
|
|
2
|
+
|
|
3
|
+
ChalkSurf uses JSON manifests for batch and agent-driven imports.
|
|
4
|
+
|
|
5
|
+
One manifest file is passed to exactly one command:
|
|
6
|
+
|
|
7
|
+
- `chalksurf sheet import --manifest <path|->`
|
|
8
|
+
- `chalksurf exercise import --manifest <path|->`
|
|
9
|
+
- `chalksurf exercise import-solution --manifest <path|->`
|
|
10
|
+
|
|
11
|
+
Use `--manifest -` to pipe JSON on stdin.
|
|
12
|
+
|
|
13
|
+
## Common Fields
|
|
14
|
+
|
|
15
|
+
Every manifest is a JSON object with these shared metadata fields:
|
|
16
|
+
|
|
17
|
+
| Field | Type | Required | Notes |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| `organizationId` | string | no | Overrides the default organization for this invocation. |
|
|
20
|
+
| `wait` | boolean | no | Behaves like `--wait`. The CLI also accepts `--wait`, which wins if set. |
|
|
21
|
+
|
|
22
|
+
Every source object supports these common fields. For `sheet import`, sources appear inside `sheets[].sources[]`. For the exercise import commands, they appear in top-level `sources[]`.
|
|
23
|
+
|
|
24
|
+
| Field | Type | Required | Notes |
|
|
25
|
+
| --- | --- | --- | --- |
|
|
26
|
+
| `sourceId` | string | no | Optional stable identifier for correlating source inputs to results. Must be unique within the manifest. |
|
|
27
|
+
| `kind` | `local` \| `directory` \| `url` | yes | Selects the source shape. |
|
|
28
|
+
|
|
29
|
+
### Source Kinds
|
|
30
|
+
|
|
31
|
+
`local`
|
|
32
|
+
|
|
33
|
+
| Field | Type | Required | Notes |
|
|
34
|
+
| --- | --- | --- | --- |
|
|
35
|
+
| `path` | string | yes | Local file path relative to `cwd` unless already absolute. |
|
|
36
|
+
| `relativePath` | string | no | Stored path shown to ChalkSurf. Defaults to the basename of `path`. |
|
|
37
|
+
|
|
38
|
+
`directory`
|
|
39
|
+
|
|
40
|
+
| Field | Type | Required | Notes |
|
|
41
|
+
| --- | --- | --- | --- |
|
|
42
|
+
| `path` | string | yes | Directory to expand recursively. |
|
|
43
|
+
| `relativeRoot` | string | no | Base directory used to compute stored relative paths. Defaults to `path`. |
|
|
44
|
+
|
|
45
|
+
`url`
|
|
46
|
+
|
|
47
|
+
| Field | Type | Required | Notes |
|
|
48
|
+
| --- | --- | --- | --- |
|
|
49
|
+
| `url` | string | yes | HTTP(S) URL to download before import. |
|
|
50
|
+
| `relativePath` | string | no | Stored path shown to ChalkSurf. Defaults to the filename derived from the URL. |
|
|
51
|
+
|
|
52
|
+
## Command-Specific Fields
|
|
53
|
+
|
|
54
|
+
### Sheet Import
|
|
55
|
+
|
|
56
|
+
Top-level fields:
|
|
57
|
+
|
|
58
|
+
| Field | Type | Required | Notes |
|
|
59
|
+
| --- | --- | --- | --- |
|
|
60
|
+
| `sheets` | array | yes | One or more sheets to import. Each sheet may contain one or more source files. |
|
|
61
|
+
|
|
62
|
+
Sheet fields:
|
|
63
|
+
|
|
64
|
+
| Field | Type | Required | Notes |
|
|
65
|
+
| --- | --- | --- | --- |
|
|
66
|
+
| `targetFolderPath` | string \| null | yes | Destination folder for the resulting sheet. Use `null` for the root folder. |
|
|
67
|
+
| `sources` | array | yes | One or more source files that belong to this sheet, in import order. |
|
|
68
|
+
| `title` | string | no | Overrides the imported sheet title. |
|
|
69
|
+
| `translateTo` | string[] | no | Target translation languages. Must be unique and non-empty when present. |
|
|
70
|
+
|
|
71
|
+
Sheet import metadata is sheet-level, not source-level. Do not put `title` or `translateTo` on individual sources inside `sheets[].sources[]`.
|
|
72
|
+
|
|
73
|
+
Source-level `relativePath` is only a source label or filename override. It does not choose the destination folder for grouped sheet imports; `targetFolderPath` is the only destination field.
|
|
74
|
+
|
|
75
|
+
Canonical example:
|
|
76
|
+
|
|
77
|
+
- [docs/examples/sheet-import-manifest.json](./examples/sheet-import-manifest.json)
|
|
78
|
+
- [schemas/sheet-import-manifest.schema.json](../schemas/sheet-import-manifest.schema.json)
|
|
79
|
+
|
|
80
|
+
### Exercise Import
|
|
81
|
+
|
|
82
|
+
Top-level fields:
|
|
83
|
+
|
|
84
|
+
| Field | Type | Required | Notes |
|
|
85
|
+
| --- | --- | --- | --- |
|
|
86
|
+
| `sources` | array | yes | One or more import sources. |
|
|
87
|
+
| `exerciseSheetId` | string | no | Default target sheet for the imported exercises. Can still be overridden by `--sheet-id`. |
|
|
88
|
+
|
|
89
|
+
Per-source fields:
|
|
90
|
+
|
|
91
|
+
- Only the common source fields are allowed.
|
|
92
|
+
- `title` and `translateTo` are rejected for this command.
|
|
93
|
+
|
|
94
|
+
Canonical example:
|
|
95
|
+
|
|
96
|
+
- [docs/examples/exercise-import-manifest.json](./examples/exercise-import-manifest.json)
|
|
97
|
+
- [schemas/exercise-import-manifest.schema.json](../schemas/exercise-import-manifest.schema.json)
|
|
98
|
+
|
|
99
|
+
### Exercise Solution Import
|
|
100
|
+
|
|
101
|
+
Top-level fields:
|
|
102
|
+
|
|
103
|
+
| Field | Type | Required | Notes |
|
|
104
|
+
| --- | --- | --- | --- |
|
|
105
|
+
| `sources` | array | yes | One or more import sources. |
|
|
106
|
+
| `exerciseId` | string | no | Default target exercise for the imported solution files. The command still requires an exercise id overall, either here or positionally. |
|
|
107
|
+
|
|
108
|
+
Per-source fields:
|
|
109
|
+
|
|
110
|
+
- Only the common source fields are allowed.
|
|
111
|
+
- `title` and `translateTo` are rejected for this command.
|
|
112
|
+
|
|
113
|
+
Canonical example:
|
|
114
|
+
|
|
115
|
+
- [docs/examples/exercise-solution-import-manifest.json](./examples/exercise-solution-import-manifest.json)
|
|
116
|
+
- [schemas/exercise-solution-import-manifest.schema.json](../schemas/exercise-solution-import-manifest.schema.json)
|
|
117
|
+
|
|
118
|
+
## Validation Notes
|
|
119
|
+
|
|
120
|
+
The CLI validates more than the JSON schema can express on its own:
|
|
121
|
+
|
|
122
|
+
- `sourceId` values must be unique within one manifest.
|
|
123
|
+
- Sheet import `translateTo` values must be unique within one sheet.
|
|
124
|
+
- `relativePath` cannot be empty or contain `..`.
|
|
125
|
+
- Sheet import `targetFolderPath` must be `null` or a normalized folder path without `.` or `..` segments.
|
|
126
|
+
- `directory` imports must resolve to at least one file.
|
|
127
|
+
- URL imports must use `http` or `https`.
|
|
128
|
+
|
|
129
|
+
Treat the schema files as a reference format and the CLI as the final validator.
|
package/docs/manual.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Manual Operator Guide
|
|
2
|
+
|
|
3
|
+
Use this flow when a person is driving the CLI directly from a terminal.
|
|
4
|
+
|
|
5
|
+
## Authentication
|
|
6
|
+
|
|
7
|
+
Interactive login stores the token and base URL in a named profile:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
chalksurf auth login --profile prod-cztamas --base-url https://chalksurf-api.fly.dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The same command also works with piped stdin:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
printf '%s' "$CHALKSURF_TOKEN" | chalksurf auth login \
|
|
17
|
+
--profile prod-cztamas \
|
|
18
|
+
--base-url https://chalksurf-api.fly.dev \
|
|
19
|
+
--with-token
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Set the profile you want to use by default in your terminal workflow:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
chalksurf profile use prod-cztamas
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Check what the CLI will use for the current session:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
chalksurf auth status
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Config resolution rules:
|
|
35
|
+
|
|
36
|
+
- `--profile` overrides `CHALKSURF_PROFILE`, which overrides the stored default profile.
|
|
37
|
+
- `--base-url` overrides `CHALKSURF_BASE_URL`, which overrides the active profile.
|
|
38
|
+
- `CHALKSURF_TOKEN` overrides the active profile token.
|
|
39
|
+
- `--organization` overrides manifest `organizationId`, which overrides `CHALKSURF_ORGANIZATION_ID`, which overrides the active profile.
|
|
40
|
+
|
|
41
|
+
## Organization Selection
|
|
42
|
+
|
|
43
|
+
List organizations visible to the current token:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
chalksurf org list
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Store a default organization for future commands:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
chalksurf org use org_123
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Store a default organization in a different profile without changing your default profile:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
chalksurf --profile prod-codex org use org_456
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Override it for one command:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
chalksurf sheet import ./fixtures/algebra.pdf --organization org_456
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Common Import Flows
|
|
68
|
+
|
|
69
|
+
Import one sheet and wait for the background job to finish:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
chalksurf sheet import ./fixtures/algebra.pdf --wait
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Import one sheet, override its title, and request an English translation during import:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
chalksurf sheet import ./fixtures/algebra.pdf \
|
|
79
|
+
--title "OKTV 2014 Round 1" \
|
|
80
|
+
--translate-to english \
|
|
81
|
+
--wait
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Import one sheet assembled from multiple source files:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
chalksurf sheet import ./fixtures/round-1-a.pdf ./fixtures/round-1-b.pdf \
|
|
88
|
+
--single-sheet \
|
|
89
|
+
--target-folder "OKTV/2014" \
|
|
90
|
+
--wait
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Import exercises into an existing sheet:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
chalksurf exercise import ./fixtures/problem-set.pdf \
|
|
97
|
+
--sheet-id 00000000-0000-4000-8000-000000000001 \
|
|
98
|
+
--wait
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Import a solution into an existing exercise:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
chalksurf exercise import-solution \
|
|
105
|
+
00000000-0000-4000-8000-000000000001 \
|
|
106
|
+
./fixtures/solution.pdf \
|
|
107
|
+
--wait
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Import from a remote URL:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
chalksurf sheet import https://example.com/trig.docx \
|
|
114
|
+
--single-sheet \
|
|
115
|
+
--target-folder Imported \
|
|
116
|
+
--wait
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Use a manifest for multi-source imports or when each resulting sheet needs explicit metadata:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
chalksurf sheet import --manifest ./import.json --wait
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Sheet import manifests use top-level `sheets[]`. Each sheet has one destination `targetFolderPath`, optional `title` and `translateTo`, and one or more ordered `sources[]`.
|
|
126
|
+
|
|
127
|
+
## Working With Jobs
|
|
128
|
+
|
|
129
|
+
Read one job:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
chalksurf job get job_123
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Wait on one or more jobs later:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
chalksurf job wait job_123 job_124
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`--wait` on an import command means the requested import work is complete before the command exits. For sheet imports, that includes requested translations.
|
|
142
|
+
|
|
143
|
+
## Troubleshooting
|
|
144
|
+
|
|
145
|
+
Missing auth:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
chalksurf auth status
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Use profiles when switching between local, staging, production, humans, and agents:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
chalksurf auth login --profile staging-cztamas --base-url https://chalksurf-api-staging.fly.dev
|
|
155
|
+
chalksurf auth login --profile prod-cztamas --base-url https://chalksurf-api.fly.dev
|
|
156
|
+
chalksurf profile use prod-cztamas
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Use JSON mode when you need exact machine-readable output:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
chalksurf job wait job_123 --json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Further reference:
|
|
166
|
+
|
|
167
|
+
- [Agent and Codex guide](./agents.md)
|
|
168
|
+
- [Manifest reference](./manifest.md)
|
|
169
|
+
- [Exit codes and JSON errors](./exit-codes.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chalksurf/cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
-
"README.md"
|
|
11
|
+
"README.md",
|
|
12
|
+
"docs",
|
|
13
|
+
"schemas"
|
|
12
14
|
],
|
|
13
15
|
"bin": {
|
|
14
16
|
"chalksurf": "dist/bin/chalksurf.js"
|
|
@@ -26,12 +28,14 @@
|
|
|
26
28
|
"build": "tsc --project ./tsconfig.build.json",
|
|
27
29
|
"lint": "eslint .",
|
|
28
30
|
"type-check": "tsc --noEmit --project ./tsconfig.json",
|
|
29
|
-
"test": "vitest --config ./vitest.config.ts --run",
|
|
31
|
+
"test": "vitest --config ./vitest.config.ts --run --maxWorkers=1",
|
|
32
|
+
"test:watch": "vitest --config ./vitest.config.ts --watch --maxWorkers=1",
|
|
30
33
|
"smoke-pack": "node --import tsx ./scripts/smoke-pack.ts"
|
|
31
34
|
},
|
|
32
35
|
"dependencies": {
|
|
33
36
|
"p-limit": "^6.2.0",
|
|
34
|
-
"yargs": "^17.7.2"
|
|
37
|
+
"yargs": "^17.7.2",
|
|
38
|
+
"zod": "^4.1.12"
|
|
35
39
|
},
|
|
36
40
|
"devDependencies": {
|
|
37
41
|
"@types/node": "^24.10.1",
|