@tikoci/centrs 0.1.0
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/CHANGELOG.md +207 -0
- package/GLOSSARY.txt +420 -0
- package/README.md +196 -0
- package/SECURITY.md +30 -0
- package/commands/AGENTS.md +50 -0
- package/commands/btest/README.md +257 -0
- package/commands/btest/examples.md +190 -0
- package/commands/check/README.md +59 -0
- package/commands/devices/README.md +405 -0
- package/commands/devices/examples.md +488 -0
- package/commands/discover/README.md +170 -0
- package/commands/discover/examples.md +98 -0
- package/commands/execute/README.md +208 -0
- package/commands/execute/examples.md +353 -0
- package/commands/mcp/README.md +260 -0
- package/commands/mcp/examples.md +158 -0
- package/commands/retrieve/README.md +209 -0
- package/commands/retrieve/examples.md +306 -0
- package/commands/settings/README.md +69 -0
- package/commands/stream/README.md +70 -0
- package/commands/terminal/README.md +177 -0
- package/commands/terminal/examples.md +144 -0
- package/commands/transfer/README.md +315 -0
- package/commands/transfer/examples.md +368 -0
- package/docs/CONSTITUTION.md +417 -0
- package/docs/MATRIX.md +224 -0
- package/docs/errors/README.md +74 -0
- package/docs/errors/auth/failed.md +9 -0
- package/docs/errors/cdb/already-exists.md +9 -0
- package/docs/errors/cdb/created.md +9 -0
- package/docs/errors/cdb/decrypt-failed.md +9 -0
- package/docs/errors/cdb/empty-group.md +9 -0
- package/docs/errors/cdb/invalid-option.md +9 -0
- package/docs/errors/cdb/invalid-record.md +9 -0
- package/docs/errors/cdb/not-found-target.md +9 -0
- package/docs/errors/cdb/not-found.md +9 -0
- package/docs/errors/cdb/override-applied.md +9 -0
- package/docs/errors/cdb/parse-failed.md +9 -0
- package/docs/errors/cdb/password-not-needed.md +9 -0
- package/docs/errors/cdb/password-required.md +9 -0
- package/docs/errors/cdb/reserved-key.md +9 -0
- package/docs/errors/cdb/reserved-option.md +9 -0
- package/docs/errors/cdb/target-not-registered.md +9 -0
- package/docs/errors/cdb/unknown-field.md +9 -0
- package/docs/errors/cdb/unknown-option.md +9 -0
- package/docs/errors/cdb/write-not-permitted.md +9 -0
- package/docs/errors/discover/broadcast-unavailable.md +9 -0
- package/docs/errors/discover/failed.md +9 -0
- package/docs/errors/identity/ambiguous.md +9 -0
- package/docs/errors/identity/no-match.md +9 -0
- package/docs/errors/input/invalid-command.md +9 -0
- package/docs/errors/input/invalid-match.md +9 -0
- package/docs/errors/input/invalid-path.md +9 -0
- package/docs/errors/input/invalid-routeros-path.md +9 -0
- package/docs/errors/input/invalid-target.md +9 -0
- package/docs/errors/input/local-file-not-found.md +9 -0
- package/docs/errors/input/mac-address.md +9 -0
- package/docs/errors/input/max-results-exceeded.md +9 -0
- package/docs/errors/internal/devices-failed.md +9 -0
- package/docs/errors/internal/mcp-start.md +9 -0
- package/docs/errors/internal/mcp-tool.md +9 -0
- package/docs/errors/internal/native-api-length.md +9 -0
- package/docs/errors/internal/unhandled.md +9 -0
- package/docs/errors/internal/unreachable.md +9 -0
- package/docs/errors/mndp/encode-failed.md +9 -0
- package/docs/errors/mndp/listen-failed.md +9 -0
- package/docs/errors/mndp/malformed.md +9 -0
- package/docs/errors/routeros/api-fatal.md +9 -0
- package/docs/errors/routeros/api-protocol.md +9 -0
- package/docs/errors/routeros/api-trap.md +9 -0
- package/docs/errors/routeros/btest-protocol.md +12 -0
- package/docs/errors/routeros/btest-too-many-sessions.md +9 -0
- package/docs/errors/routeros/command-failed.md +14 -0
- package/docs/errors/routeros/ec-srp5-protocol.md +11 -0
- package/docs/errors/routeros/error.md +9 -0
- package/docs/errors/routeros/invalid-value.md +14 -0
- package/docs/errors/routeros/mac-telnet-error.md +9 -0
- package/docs/errors/routeros/mac-telnet-not-ready.md +9 -0
- package/docs/errors/routeros/mac-telnet-protocol.md +9 -0
- package/docs/errors/routeros/mac-telnet-unsupported-auth.md +9 -0
- package/docs/errors/routeros/protocol-not-implemented.md +9 -0
- package/docs/errors/routeros/request-failed.md +9 -0
- package/docs/errors/routeros/session-closed.md +13 -0
- package/docs/errors/routeros/unknown-attribute.md +13 -0
- package/docs/errors/routeros/unknown-path.md +15 -0
- package/docs/errors/routeros/unsupported-capability.md +9 -0
- package/docs/errors/settings/invalid-boolean.md +9 -0
- package/docs/errors/settings/invalid-format.md +9 -0
- package/docs/errors/settings/invalid-integer.md +9 -0
- package/docs/errors/settings/invalid-timeout.md +9 -0
- package/docs/errors/settings/invalid-via.md +9 -0
- package/docs/errors/settings/unsafe-protocol-blocked.md +9 -0
- package/docs/errors/target/mac-not-in-arp.md +14 -0
- package/docs/errors/target/mac-required.md +23 -0
- package/docs/errors/target/mac-unresolved.md +17 -0
- package/docs/errors/target/unresolved.md +9 -0
- package/docs/errors/transport/auth-failed.md +9 -0
- package/docs/errors/transport/auto-method.md +9 -0
- package/docs/errors/transport/capability-unsupported.md +22 -0
- package/docs/errors/transport/checksum-unavailable.md +9 -0
- package/docs/errors/transport/connection-closed.md +9 -0
- package/docs/errors/transport/connection-refused.md +9 -0
- package/docs/errors/transport/dns.md +9 -0
- package/docs/errors/transport/host-key-mismatch.md +9 -0
- package/docs/errors/transport/incomplete-transfer.md +9 -0
- package/docs/errors/transport/insecure-trust.md +9 -0
- package/docs/errors/transport/local-tool-missing.md +11 -0
- package/docs/errors/transport/network.md +9 -0
- package/docs/errors/transport/timeout.md +9 -0
- package/docs/errors/transport/tls-certificate.md +9 -0
- package/docs/errors/transport/unreachable.md +9 -0
- package/docs/errors/transport/unsupported-operation.md +9 -0
- package/docs/errors/usage/confirmation-required.md +9 -0
- package/docs/errors/usage/conflicting-flags.md +9 -0
- package/docs/errors/usage/invalid-concurrency.md +9 -0
- package/docs/errors/usage/missing-group.md +9 -0
- package/docs/errors/usage/not-implemented.md +9 -0
- package/docs/errors/usage/target-exists.md +9 -0
- package/docs/errors/usage/timeout-out-of-range.md +9 -0
- package/docs/errors/validation/not-implemented.md +9 -0
- package/docs/errors/validation/option.md +9 -0
- package/docs/errors/validation/syntax.md +9 -0
- package/docs/errors/validation/unknown-attribute.md +9 -0
- package/docs/errors/validation/unknown-path.md +9 -0
- package/package.json +96 -0
- package/src/AGENTS.md +7 -0
- package/src/btest.ts +906 -0
- package/src/cli/btest.ts +455 -0
- package/src/cli/common.ts +53 -0
- package/src/cli/devices.ts +527 -0
- package/src/cli/discover.ts +186 -0
- package/src/cli/execute.ts +333 -0
- package/src/cli/mcp.ts +109 -0
- package/src/cli/retrieve.ts +383 -0
- package/src/cli/terminal.ts +303 -0
- package/src/cli/transfer.ts +471 -0
- package/src/cli.ts +115 -0
- package/src/core/envelope.ts +221 -0
- package/src/core/error-catalog.ts +469 -0
- package/src/core/routeros-errors.ts +268 -0
- package/src/data/AGENTS.md +11 -0
- package/src/data/dude-db.ts +0 -0
- package/src/data/mndp-cache.ts +132 -0
- package/src/data/mndp.ts +405 -0
- package/src/data/winbox-cdb-write.ts +200 -0
- package/src/data/winbox-cdb.ts +913 -0
- package/src/devices-schema.ts +107 -0
- package/src/devices.ts +1717 -0
- package/src/discover.ts +802 -0
- package/src/errors.ts +189 -0
- package/src/execute.ts +1350 -0
- package/src/index.ts +353 -0
- package/src/mcp/config.ts +66 -0
- package/src/mcp/resources.ts +98 -0
- package/src/mcp/safety.ts +119 -0
- package/src/mcp/server.ts +225 -0
- package/src/mcp/tools.ts +657 -0
- package/src/mcp.ts +33 -0
- package/src/protocols/AGENTS.md +14 -0
- package/src/protocols/adapter.ts +866 -0
- package/src/protocols/btest-session.ts +1493 -0
- package/src/protocols/btest.ts +447 -0
- package/src/protocols/ec-srp5.ts +466 -0
- package/src/protocols/index.ts +118 -0
- package/src/protocols/mac-telnet-console.ts +655 -0
- package/src/protocols/mac-telnet.ts +1299 -0
- package/src/protocols/mtwei.ts +47 -0
- package/src/protocols/native-api.ts +796 -0
- package/src/protocols/sftp.ts +427 -0
- package/src/protocols/ssh.ts +297 -0
- package/src/resolver/cdb.ts +580 -0
- package/src/resolver/comment-kv.ts +360 -0
- package/src/resolver/index.ts +74 -0
- package/src/resolver/mac.ts +227 -0
- package/src/resolver/settings.ts +228 -0
- package/src/resolver/target.ts +442 -0
- package/src/retrieve-fanout.ts +533 -0
- package/src/retrieve.ts +1215 -0
- package/src/terminal.ts +444 -0
- package/src/transfer.ts +1554 -0
- package/src/tui.ts +22 -0
- package/src/webproxy.ts +16 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# transfer — examples
|
|
2
|
+
|
|
3
|
+
Each numbered example is an executable spec. The integration test under
|
|
4
|
+
`test/integration/transfer.test.ts` runs every example against a CHR booted by
|
|
5
|
+
`@tikoci/quickchr`. Examples 8–10 (stdin/stdout/default-local) run through the
|
|
6
|
+
subprocess harness (`test/integration/cli-process.ts`), which drives the real CLI
|
|
7
|
+
binary so piped stdin, raw stdout bytes, and cwd are exercised — the in-process
|
|
8
|
+
`runCli` console capture cannot see those. If a line here is not exercised by a
|
|
9
|
+
test, the test file is wrong; if a line passes only with `validate=false`, the
|
|
10
|
+
**implementation** is wrong (see `docs/CONSTITUTION.md`).
|
|
11
|
+
|
|
12
|
+
Conventions (provided by the harness):
|
|
13
|
+
|
|
14
|
+
- `$R` is `<host>:<rest-port>`; `$U` / `$P` are CHR credentials.
|
|
15
|
+
- `$A` is `<host>` and `$API_PORT` is the native-API port (`chr.ports.api`) for
|
|
16
|
+
`--via native-api`.
|
|
17
|
+
- `$SRC` is a small local text file (~2 KB) the harness creates; `$TMP` is a
|
|
18
|
+
scratch directory for downloads. `$BIG` is a local file **larger than 60 KB**.
|
|
19
|
+
- Device-side paths are **bare** (a CHR has no `flash` disk, so everything
|
|
20
|
+
persists for the session); the harness removes `centrs-*` test files between
|
|
21
|
+
runs. The CHR free license caps throughput at 1 Mb/s, so fixtures stay small —
|
|
22
|
+
the proof is correctness (a byte-exact round-trip), not throughput.
|
|
23
|
+
|
|
24
|
+
The first coded pass is `rest`/`native` only (no SSH dependency). `sftp`/`scp`/
|
|
25
|
+
`fetch`/`ftp` examples assert their *current* contract (not-implemented / gated)
|
|
26
|
+
and flip to real round-trips when their transport lands — exactly as
|
|
27
|
+
`retrieve`'s `--query` examples assert `validation/not-implemented` today.
|
|
28
|
+
|
|
29
|
+
## REST round-trip (the CI proof)
|
|
30
|
+
|
|
31
|
+
### 1. Upload a small file (auto → rest)
|
|
32
|
+
|
|
33
|
+
A ≤60 KB upload needs no `--via`; auto-selection uses the REST family.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
centrs transfer $R upload $SRC centrs-up.txt --username $U --password $P --json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Envelope: `ok: true`, `data.op="upload"`, `data.remote="centrs-up.txt"`,
|
|
40
|
+
`data.bytes` equals the size of `$SRC`, `data.verified="size"`,
|
|
41
|
+
`meta.via="rest-api"`.
|
|
42
|
+
|
|
43
|
+
### 2. Download it back (round-trip)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
centrs transfer $R download centrs-up.txt $TMP/down.txt --username $U --password $P --json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Envelope: `ok: true`, `data.op="download"`. The byte content of `$TMP/down.txt`
|
|
50
|
+
is **identical** to `$SRC` — this is the small-file round-trip that gates
|
|
51
|
+
`CHR-passed`.
|
|
52
|
+
|
|
53
|
+
## List (sugar over `retrieve /file`)
|
|
54
|
+
|
|
55
|
+
### 3. List files
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
centrs transfer $R list --username $U --password $P --json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Envelope: `data` is the `/file` row array (same shape as `retrieve /file`) and
|
|
62
|
+
includes a row with `name="centrs-up.txt"`.
|
|
63
|
+
|
|
64
|
+
### 4. Filter by type
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
centrs transfer $R list --type file --username $U --password $P --json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Envelope: every returned row has a file `type` (no `directory`/`disk` rows).
|
|
71
|
+
|
|
72
|
+
### 5. Filter by name glob
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
centrs transfer $R list --name 'centrs-*' --username $U --password $P --json
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Envelope: every row's `name` matches the glob; `centrs-up.txt` is present.
|
|
79
|
+
|
|
80
|
+
## Validate before write
|
|
81
|
+
|
|
82
|
+
### 6. Refuse overwrite without `--force`
|
|
83
|
+
|
|
84
|
+
Re-uploading onto an existing remote path fails the precondition probe (centrs
|
|
85
|
+
`stat`s the target via `/file/print`) **before** writing.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
centrs transfer $R upload $SRC centrs-up.txt --username $U --password $P --json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Envelope: `ok: false`, `error.code="usage/target-exists"`. The existing file is
|
|
92
|
+
unchanged.
|
|
93
|
+
|
|
94
|
+
### 7. `--force` overwrites
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
centrs transfer $R upload $SRC centrs-up.txt --force --username $U --password $P --json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Envelope: `ok: true`, `data.op="upload"`.
|
|
101
|
+
|
|
102
|
+
## Defaults and piping
|
|
103
|
+
|
|
104
|
+
### 8. Omitted local on download → basename in the current directory
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
centrs transfer $R download centrs-up.txt --username $U --password $P --json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Writes `./centrs-up.txt`; envelope `data.local` ends in `centrs-up.txt`.
|
|
111
|
+
|
|
112
|
+
### 9. Download to stdout (`-`)
|
|
113
|
+
|
|
114
|
+
Payload bytes go to **stdout**; the human/JSON summary goes to stderr, so the
|
|
115
|
+
two never interleave.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
centrs transfer $R download centrs-up.txt - --username $U --password $P
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
stdout is byte-identical to `$SRC`.
|
|
122
|
+
|
|
123
|
+
### 10. Upload from stdin (`-`)
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
printf 'hello-centrs' | centrs transfer $R upload - centrs-stdin.txt --username $U --password $P --json
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Envelope: `ok: true`, `data.bytes=12`. A follow-up `download centrs-stdin.txt -`
|
|
130
|
+
emits `hello-centrs`.
|
|
131
|
+
|
|
132
|
+
## Device file management
|
|
133
|
+
|
|
134
|
+
### 11. mkdir
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
centrs transfer $R mkdir centrs-dir --username $U --password $P --json
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Envelope: `ok: true`. A subsequent `list --type directory` includes
|
|
141
|
+
`name="centrs-dir"`.
|
|
142
|
+
|
|
143
|
+
### 12. Upload into a directory
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
centrs transfer $R upload $SRC centrs-dir/nested.txt --username $U --password $P --json
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Envelope: `ok: true`, `data.remote="centrs-dir/nested.txt"`.
|
|
150
|
+
|
|
151
|
+
### 13. On-device copy
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
centrs transfer $R copy centrs-dir/nested.txt centrs-dir/copy.txt --username $U --password $P --json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Envelope: `ok: true`, `data.op="copy"`. `list --name 'centrs-dir/*'` shows both
|
|
158
|
+
`nested.txt` and `copy.txt`.
|
|
159
|
+
|
|
160
|
+
### 14. Remove
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
centrs transfer $R remove centrs-dir/copy.txt --username $U --password $P --json
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Envelope: `ok: true`, `data.op="remove"`. The file no longer appears in `list`.
|
|
167
|
+
|
|
168
|
+
## Path handling
|
|
169
|
+
|
|
170
|
+
### 15. Leading-slash normalization
|
|
171
|
+
|
|
172
|
+
A leading `/` is accepted and normalized away, so `/centrs-up.txt` and
|
|
173
|
+
`centrs-up.txt` are the same target.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
centrs transfer $R download /centrs-up.txt $TMP/slash.txt --username $U --password $P --json
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Envelope: `ok: true`; `$TMP/slash.txt` is identical to `$SRC`, and
|
|
180
|
+
`data.remote="centrs-up.txt"` (no leading slash on the wire).
|
|
181
|
+
|
|
182
|
+
## Integrity
|
|
183
|
+
|
|
184
|
+
### 16. `--no-verify` skips the size check
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
centrs transfer $R upload $SRC centrs-no-verify.txt --no-verify --username $U --password $P --json
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Envelope: `ok: true`, `data.verified="off"`.
|
|
191
|
+
|
|
192
|
+
## Large file over REST (chunked read scales)
|
|
193
|
+
|
|
194
|
+
The harness seeds the >60 KB device file `centrs-big.bin` with an **sftp upload**
|
|
195
|
+
(see S3 below) — REST cannot write past 60 KB, so sftp is the natural seeder and
|
|
196
|
+
no host-HTTP `fetch` hack is needed.
|
|
197
|
+
|
|
198
|
+
### 17. Download >60 KB via chunked `/file/read` (auto → rest)
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
centrs transfer $R download centrs-big.bin $TMP/big.bin --username $U --password $P --json
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Envelope: `ok: true`, `meta.via="rest-api"`, `data.bytes` equals the seeded size
|
|
205
|
+
(>60 KB). `$TMP/big.bin` is byte-identical to the seed — proving the chunked read
|
|
206
|
+
reassembles correctly across `/file/read` calls.
|
|
207
|
+
|
|
208
|
+
## Error / transport contract
|
|
209
|
+
|
|
210
|
+
### 18. `--via rest` upload over 60 KB is rejected up front
|
|
211
|
+
|
|
212
|
+
REST cannot write past 60 KB, and an explicit `--via` is never silently
|
|
213
|
+
downgraded.
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
centrs transfer $R upload $BIG centrs-too-big.txt --via rest --username $U --password $P --json
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Envelope: `ok: false`, `error.code="transport/unsupported-operation"`. No partial
|
|
220
|
+
file is created on the device.
|
|
221
|
+
|
|
222
|
+
### 19. Download a missing remote file
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
centrs transfer $R download centrs-does-not-exist.txt $TMP/x --username $U --password $P --json
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Envelope: `ok: false`, `error.code` in the `routeros/*` family (RouterOS's own
|
|
229
|
+
"no such item" surfaced through the menu).
|
|
230
|
+
|
|
231
|
+
### 20. Bad credentials
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
centrs transfer $R list --username wrong --password wrong --json
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Envelope: `ok: false`, `error.code="transport/auth-failed"`, `details_url`
|
|
238
|
+
populated.
|
|
239
|
+
|
|
240
|
+
### 21. Conflicting flags
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
centrs transfer $R upload $SRC centrs-up.txt --verify size --no-verify --username $U --password $P --json
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Rejected before any network call with `usage/conflicting-flags`.
|
|
247
|
+
|
|
248
|
+
## native-api (`--via native-api`)
|
|
249
|
+
|
|
250
|
+
The same contract over the RouterOS binary API. Exercised by
|
|
251
|
+
`test/integration/transfer.test.ts` against the native-API port.
|
|
252
|
+
|
|
253
|
+
### N1. Upload small over native-api
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
centrs transfer $A upload $SRC centrs-nv.txt --via native-api --port $API_PORT --username $U --password $P --json
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Envelope: `ok: true`, `meta.via="native-api"`.
|
|
260
|
+
|
|
261
|
+
### N2. Download small over native-api (round-trip)
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
centrs transfer $A download centrs-nv.txt $TMP/nv.txt --via native-api --port $API_PORT --username $U --password $P --json
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`$TMP/nv.txt` is identical to `$SRC`.
|
|
268
|
+
|
|
269
|
+
### N3. List over native-api
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
centrs transfer $A list --via native-api --port $API_PORT --username $U --password $P --json
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Envelope: `data` is the `/file` row array; includes `centrs-nv.txt`.
|
|
276
|
+
|
|
277
|
+
### N4. native-api upload over 60 KB is rejected
|
|
278
|
+
|
|
279
|
+
Same 60 KB write cap as REST (it is a `/file` menu limit, not REST-specific).
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
centrs transfer $A upload $BIG centrs-too-big.txt --via native-api --port $API_PORT --username $U --password $P --json
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Envelope: `ok: false`, `error.code="transport/unsupported-operation"`.
|
|
286
|
+
|
|
287
|
+
## ssh (`--via sftp`)
|
|
288
|
+
|
|
289
|
+
The first SSH consumer: a key-authenticated SFTP round-trip over the host OpenSSH
|
|
290
|
+
`sftp` subsystem. The harness generates an ed25519 keypair, imports the public
|
|
291
|
+
half to the CHR user (`/user ssh-keys import`), and drives `--via sftp --ssh-key`
|
|
292
|
+
against the CHR's forwarded SSH port; `--insecure` accepts the ephemeral host key
|
|
293
|
+
(default is `accept-new` trust-on-first-use). RouterOS refuses password login once
|
|
294
|
+
a key is set, and centrs's sftp runs `BatchMode=yes`, so key auth is the path.
|
|
295
|
+
|
|
296
|
+
### S1. sftp upload (host → device)
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $U --ssh-key $KEY --insecure upload $SRC centrs-sftp.txt --json
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Envelope: `ok: true`, `meta.via="ssh"`, `data.op="upload"`.
|
|
303
|
+
|
|
304
|
+
### S2. sftp download (device → host, round-trip)
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $U --ssh-key $KEY --insecure download centrs-sftp.txt $TMP/sftp-down.txt --json
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`$TMP/sftp-down.txt` is byte-identical to `$SRC`.
|
|
311
|
+
|
|
312
|
+
### S3. sftp carries a >60 KB file (the gap REST cannot write)
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $U --ssh-key $KEY --insecure upload $BIG centrs-big.bin --json
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Envelope: `ok: true`. This is also the seed for example 17 (the same file then
|
|
319
|
+
reads back over REST via chunked `/file/read`).
|
|
320
|
+
|
|
321
|
+
### S4. list over sftp
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $U --ssh-key $KEY --insecure list --json
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Envelope: `data` is the file array; includes `centrs-sftp.txt`.
|
|
328
|
+
|
|
329
|
+
### S5. mkdir + remove over sftp
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $U --ssh-key $KEY --insecure mkdir centrs-sftp-dir --json
|
|
333
|
+
centrs transfer 127.0.0.1 --via sftp --port $SSH_PORT --username $U --ssh-key $KEY --insecure remove centrs-big.bin --json
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Both `ok: true`. On-device `copy` has no SFTP primitive — `--via sftp copy` returns
|
|
337
|
+
`transport/unsupported-operation` (it stays on rest/native).
|
|
338
|
+
|
|
339
|
+
## Pending transports (contract defined; flips when the transport lands)
|
|
340
|
+
|
|
341
|
+
### P2. `--via scp` (follow-on pass after sftp)
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
centrs transfer $R upload $SRC centrs-up.txt --via scp --username $U --password $P --json
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Envelope: `ok: false`, `error.code="usage/not-implemented"`.
|
|
348
|
+
|
|
349
|
+
### P3. `--via fetch` (deferred, explicit-only)
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
centrs transfer $R upload $BIG centrs-up.bin --via fetch --username $U --password $P --json
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Envelope: `ok: false`, `error.code="usage/not-implemented"`.
|
|
356
|
+
|
|
357
|
+
### P4. `--via ftp` without the unsafe-protocol opt-in
|
|
358
|
+
|
|
359
|
+
`ftp` is cleartext, so it is gated behind `ALLOW_UNSAFE_PROTOCOLS=ftp` and never
|
|
360
|
+
chosen silently.
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
centrs transfer $R upload $SRC centrs-up.txt --via ftp --username $U --password $P --json
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Envelope: `ok: false`, the error reports the protocol is gated (the cleartext-FTP
|
|
367
|
+
opt-in is absent). When `ALLOW_UNSAFE_PROTOCOLS=ftp` is set but the method is not
|
|
368
|
+
yet built, it is `usage/not-implemented` instead.
|