@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,306 @@
|
|
|
1
|
+
# retrieve — examples
|
|
2
|
+
|
|
3
|
+
Each numbered example is an executable spec. The integration test under
|
|
4
|
+
`test/integration/rest-retrieve.test.ts` runs every example against a CHR
|
|
5
|
+
booted by `@tikoci/quickchr`. If a line here is not exercised by a test, the
|
|
6
|
+
test file is wrong; if a line passes only with `validate=false`, the
|
|
7
|
+
**implementation** is wrong (see `docs/CONSTITUTION.md`).
|
|
8
|
+
|
|
9
|
+
`$R` is `<host>:<rest-port>` resolved by quickchr.
|
|
10
|
+
`$U` / `$P` are CHR credentials provided by the test harness.
|
|
11
|
+
Examples that assert envelope fields pass `--json`; plain text is the default
|
|
12
|
+
for human CLI use.
|
|
13
|
+
|
|
14
|
+
## Singletons (read a single record)
|
|
15
|
+
|
|
16
|
+
### 1. /system/resource
|
|
17
|
+
|
|
18
|
+
Must succeed with `validate=true` (default).
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
centrs retrieve $R /system/resource --username $U --password $P --json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Envelope: `ok: true`, `data` is an object, `meta.via=rest-api`,
|
|
25
|
+
`meta.validation.source=/console/inspect`.
|
|
26
|
+
|
|
27
|
+
### 2. /system/identity
|
|
28
|
+
|
|
29
|
+
Same shape, different menu.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
centrs retrieve $R /system/identity --username $U --password $P --json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Lists (read an array of rows)
|
|
36
|
+
|
|
37
|
+
### 3. /ip/address
|
|
38
|
+
|
|
39
|
+
Empty or populated, both must succeed with `validate=true`.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
centrs retrieve $R /ip/address --username $U --password $P --json
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Envelope: `ok: true`, `data` is an array.
|
|
46
|
+
|
|
47
|
+
### 4. /interface
|
|
48
|
+
|
|
49
|
+
List of interfaces present on a default CHR.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
centrs retrieve $R /interface --username $U --password $P --json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Attribute projection
|
|
56
|
+
|
|
57
|
+
### 5. Single --attribute on a singleton
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
centrs retrieve $R /system/resource --attribute uptime --username $U --password $P --json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Envelope: `data` is the bare value, not an object.
|
|
64
|
+
|
|
65
|
+
### 6. Comma-list --attributes on a list
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
centrs retrieve $R /interface --attributes name,type --username $U --password $P --json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Envelope: `data` is an array of objects each containing only `name` and `type`.
|
|
72
|
+
|
|
73
|
+
### 7. --all-attributes (RouterOS details=true)
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
centrs retrieve $R /system/resource --all-attributes --username $U --password $P --json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 8. Conflict: --attribute + --all-attributes
|
|
80
|
+
|
|
81
|
+
Rejected before any network call, with `usage/conflicting-flags`.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
centrs retrieve $R /system/resource --attribute uptime --all-attributes --username $U --password $P --json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## --list-attributes
|
|
88
|
+
|
|
89
|
+
### 9. List attributes for a path
|
|
90
|
+
|
|
91
|
+
Without running `print`/`get`.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
centrs retrieve $R /system/resource --list-attributes --username $U --password $P --json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Envelope: `data` is `string[]`. No `meta.timing.request` (no transport call).
|
|
98
|
+
|
|
99
|
+
## Validation surface
|
|
100
|
+
|
|
101
|
+
### 10. Unknown path
|
|
102
|
+
|
|
103
|
+
Must fail with `validation/unknown-path` (not `routeros/unsupported-path`
|
|
104
|
+
from a server round-trip; the validator catches it first). The error
|
|
105
|
+
envelope must include suggested alternatives from `/console/inspect` when
|
|
106
|
+
available.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
centrs retrieve $R /not/a/real/path --username $U --password $P --json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 11. Unknown attribute
|
|
113
|
+
|
|
114
|
+
`validation/unknown-attribute` with suggestions.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
centrs retrieve $R /system/resource --attribute bogus --username $U --password $P --json
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Transport / error contract
|
|
121
|
+
|
|
122
|
+
### 12. Bad credentials
|
|
123
|
+
|
|
124
|
+
`transport/auth-failed`, `details_url` populated.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
centrs retrieve $R /system/resource --username wrong --password wrong --json
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 13. Unreachable host
|
|
131
|
+
|
|
132
|
+
`transport/connection-refused` (not the more general `transport/network`).
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
centrs retrieve 127.0.0.1:1 /system/resource --username $U --password $P --json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 14. REST timeout ceiling
|
|
139
|
+
|
|
140
|
+
`--timeout 70000` against `--via rest-api` is rejected up front with
|
|
141
|
+
`usage/timeout-out-of-range` (REST hard cap is 60s).
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
centrs retrieve $R /system/resource --via rest-api --timeout 70000 --username $U --password $P --json
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## CDB resolution
|
|
148
|
+
|
|
149
|
+
### 15. Name resolution
|
|
150
|
+
|
|
151
|
+
`<router>` matches a CDB-stored target; user/password are filled from CDB.
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
centrs retrieve $R /system/resource --cdb-file $CDB --json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 16. Unused --cdb-password on an unencrypted CDB
|
|
158
|
+
|
|
159
|
+
Call succeeds with a `cdb/password-not-needed` warning in `meta.warnings`.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
centrs retrieve $R /system/resource --cdb-file $CDB --cdb-password ignored --json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Group fanout
|
|
166
|
+
|
|
167
|
+
These are exercised by `test/integration/fanout-retrieve.test.ts` when CHR
|
|
168
|
+
integration is enabled.
|
|
169
|
+
|
|
170
|
+
### F1. Group fanout with one inner success and one inner failure
|
|
171
|
+
|
|
172
|
+
`$CDB` contains two records in group `fanout-chr`: record 0 is the live CHR and
|
|
173
|
+
record 1 is an unreachable REST URL.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
centrs retrieve --group fanout-chr /system/resource --cdb-file $CDB --json
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Outer envelope: `ok: true`, `data.summary = { total: 2, ok: 1, failed: 1 }`,
|
|
180
|
+
`data.targets` is ordered by CDB `recordIndex`, and `meta.operation.kind` is
|
|
181
|
+
`fanout`. The unreachable target is an inner `ok: false` envelope with
|
|
182
|
+
`transport/connection-refused`.
|
|
183
|
+
|
|
184
|
+
### F2. Empty / unknown group
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
centrs retrieve --group no-such-group /system/resource --cdb-file $CDB --json
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Envelope: `ok: true`, `data.summary = { total: 0, ok: 0, failed: 0 }`,
|
|
191
|
+
`data.targets = []`, and warnings include `cdb/empty-group`.
|
|
192
|
+
|
|
193
|
+
## Format
|
|
194
|
+
|
|
195
|
+
### 17. --format yaml
|
|
196
|
+
|
|
197
|
+
`text` is the default; `--json` (or `--format json`) emits the structured
|
|
198
|
+
envelope, and `--format yaml` renders the same envelope. The two structured
|
|
199
|
+
outputs round-trip to the same JS value.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
centrs retrieve $R /system/resource --format yaml --username $U --password $P
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Out-of-scope (must surface as not-implemented)
|
|
206
|
+
|
|
207
|
+
### 18. --query
|
|
208
|
+
|
|
209
|
+
Returns `validation/not-implemented` immediately.
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
centrs retrieve $R /ip/address --query 'address~"192"' --username $U --password $P --json
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 19. --filter
|
|
216
|
+
|
|
217
|
+
Same handling as `--query`.
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
centrs retrieve $R /ip/address --filter 'disabled=no' --username $U --password $P --json
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## native-api (`--via native-api`)
|
|
224
|
+
|
|
225
|
+
The same retrieve contract over the RouterOS binary API (TCP 8728, or TLS 8729
|
|
226
|
+
when `--port 8729`). `$A` is `<host>` and `--port` is the resolved api port
|
|
227
|
+
from quickchr (`chr.ports.api`). Validation still runs through
|
|
228
|
+
`/console/inspect`, issued as a native-API command rather than over REST.
|
|
229
|
+
|
|
230
|
+
These are exercised by `test/integration/native-api-retrieve.test.ts`.
|
|
231
|
+
|
|
232
|
+
One transport-specific note: native-API attribute values are **strings**
|
|
233
|
+
(the binary API does not carry JSON scalar types), so `data` scalars are
|
|
234
|
+
strings even where the REST path returns a number/boolean. The envelope shape
|
|
235
|
+
(object vs array vs bare value, projection, validation source) is identical.
|
|
236
|
+
|
|
237
|
+
### N1. Singleton over native-api
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
centrs retrieve $A /system/resource --via native-api --port $API_PORT --username $U --password $P
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Envelope: `ok: true`, `data` is an object, `meta.via=native-api`,
|
|
244
|
+
`meta.validation.source` contains `/console/inspect`.
|
|
245
|
+
|
|
246
|
+
### N2. Second singleton
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
centrs retrieve $A /system/identity --via native-api --port $API_PORT --username $U --password $P
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### N3. List menu → array
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
centrs retrieve $A /interface --via native-api --port $API_PORT --username $U --password $P
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### N4. Possibly-empty list
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
centrs retrieve $A /ip/address --via native-api --port $API_PORT --username $U --password $P
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### N5. Singleton single `--attribute` → bare value
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
centrs retrieve $A /system/resource --attribute uptime --via native-api --port $API_PORT --username $U --password $P
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### N6. List `--attributes` projection
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
centrs retrieve $A /interface --attributes name,type --via native-api --port $API_PORT --username $U --password $P
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`data` is an array of objects each containing only `name` and `type`.
|
|
277
|
+
|
|
278
|
+
### N7. `--all-attributes` (native `print detail`)
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
centrs retrieve $A /system/resource --all-attributes --via native-api --port $API_PORT --username $U --password $P
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### N8. `--list-attributes` (inspect only, no data call)
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
centrs retrieve $A /system/resource --list-attributes --via native-api --port $API_PORT --username $U --password $P
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### N9. Unknown path → `validation/unknown-path`
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
centrs retrieve $A /not/a/real/path --via native-api --port $API_PORT --username $U --password $P
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### N10. Unknown attribute → `validation/unknown-attribute`
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
centrs retrieve $A /system/resource --attribute bogus-attr --via native-api --port $API_PORT --username $U --password $P
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### N11. Bad credentials → `transport/auth-failed`
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
centrs retrieve $A /system/resource --via native-api --port $API_PORT --username wrong --password wrong
|
|
306
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# settings
|
|
2
|
+
|
|
3
|
+
Read and write centrs's own settings — the global `centrs.env` preferences and
|
|
4
|
+
the `__default__` fallback record — and run first-time setup.
|
|
5
|
+
|
|
6
|
+
Status: `designed`. This file describes intent and flags; no implementation yet.
|
|
7
|
+
See `docs/MATRIX.md` for the cell state. `settings` is the front-end to the
|
|
8
|
+
settings the constitution defines (Settings precedence: the `centrs.env` config
|
|
9
|
+
file at tier 2; `__default__` fallback creds): it does not invent new settings,
|
|
10
|
+
only edits the ones already specified.
|
|
11
|
+
|
|
12
|
+
## Why
|
|
13
|
+
|
|
14
|
+
centrs deliberately has no store of its own for *inventory* — that is the CDB.
|
|
15
|
+
But it does have **global, centrs-only preferences** (default discovery window,
|
|
16
|
+
default `--format`, concurrency, …) that have no per-device home, and a
|
|
17
|
+
**`__default__` fallback record** for credentials. Today both are hand-edited
|
|
18
|
+
(`centrs.env`, or `devices add __default__`). `settings` makes that ergonomic and
|
|
19
|
+
is where onboarding flows steer the user.
|
|
20
|
+
|
|
21
|
+
## Surface
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
centrs settings # interactive setup (TTY)
|
|
25
|
+
centrs settings print [<attr>] # show resolved settings + sources
|
|
26
|
+
centrs settings get <attr> # read one value
|
|
27
|
+
centrs settings set <attr> <value> # write one value to centrs.env
|
|
28
|
+
centrs settings reset [<attr>] # clear one/all back to built-in default
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- **Interactive (`centrs settings`, TTY):** prompts (clack-style, like
|
|
32
|
+
`@tikoci/quickchr`) to optionally collect a default `user`/`password` (written
|
|
33
|
+
to the `__default__` record so a fresh CDB "starts useful"), confirm/update
|
|
34
|
+
global preferences, and report where the `centrs.env` lives. Non-TTY runs of
|
|
35
|
+
bare `settings` print the resolved settings instead of prompting.
|
|
36
|
+
- **Non-interactive (`set`/`get`/`reset`/`print`):** scriptable edits to
|
|
37
|
+
`centrs.env`. `print` shows each resolved setting with its winning source
|
|
38
|
+
(`default` / `centrs.env` / `env` / `cli`), mirroring `meta.settings`.
|
|
39
|
+
- Credentials collected for `__default__` route through the `devices` write
|
|
40
|
+
layer (the only CDB writer) and are redacted from any structured output.
|
|
41
|
+
|
|
42
|
+
## Boundaries
|
|
43
|
+
|
|
44
|
+
- `settings` edits **global** preferences (`centrs.env`) and the **`__default__`**
|
|
45
|
+
record only. Per-device settings stay in `devices` (CDB comment-kv).
|
|
46
|
+
- It writes no RouterOS device; it is local-only, like `devices`.
|
|
47
|
+
- `--skip-env-file` / `CENTRS_SKIP_ENV_FILE=1` makes a run ignore `centrs.env`
|
|
48
|
+
(tests, dev-agent usage); `settings` still reports what *would* apply.
|
|
49
|
+
|
|
50
|
+
## Onboarding
|
|
51
|
+
|
|
52
|
+
`settings` is the steering target for "centrs starts useful": an empty CDB plus a
|
|
53
|
+
first `retrieve`/`devices` should tip toward `centrs settings` (collect default
|
|
54
|
+
creds) and `centrs devices discover` (populate the registry). See
|
|
55
|
+
`commands/discover/README.md` (save-implied `devices discover`) and
|
|
56
|
+
`docs/CONSTITUTION.md` (Default-device record).
|
|
57
|
+
|
|
58
|
+
## Settings keys
|
|
59
|
+
|
|
60
|
+
`centrs.env` holds **canonical `CENTRS_*=value` lines** — identical to the
|
|
61
|
+
environment-variable spellings, prefix included (`CENTRS_FORMAT=json`,
|
|
62
|
+
`CENTRS_DISCOVER_TIMEOUT=15s`, `CENTRS_CONCURRENCY=8`). One vocabulary across
|
|
63
|
+
file / env / CLI: the file *is* "just default env vars," so `set -a && source
|
|
64
|
+
centrs.env` works and the loader applies each key as a default **only when the
|
|
65
|
+
same `CENTRS_*` is not already in the process environment** (it never injects
|
|
66
|
+
into `process.env`). `settings set <attr>` accepts the setting name with or
|
|
67
|
+
without the prefix for convenience but always writes the canonical prefixed
|
|
68
|
+
line. It is **user-global only** — the single XDG path above; there is no
|
|
69
|
+
project-local config file discovered from the working directory.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# stream
|
|
2
|
+
|
|
3
|
+
Follow live RouterOS output — `print follow`, `monitor`-style commands, and
|
|
4
|
+
sniffer feeds — as a stream of envelopes.
|
|
5
|
+
|
|
6
|
+
Status: `designed`. This file describes intent and flags; no implementation yet.
|
|
7
|
+
See `docs/MATRIX.md` for the cell states. `stream` lands with a transport that
|
|
8
|
+
can hold an open read (native-api first, then ssh); it is **not** a REST cell.
|
|
9
|
+
|
|
10
|
+
## Why a separate verb
|
|
11
|
+
|
|
12
|
+
`stream` exists because open-ended follow is a different I/O contract than a
|
|
13
|
+
one-shot read, and conflating it into `retrieve` would force every retrieve
|
|
14
|
+
consumer to handle "maybe this is a stream." A caller that picks `stream` knows
|
|
15
|
+
it is getting a sequence of frames, not a single envelope. Bounded, finite
|
|
16
|
+
captures stay on `retrieve` (`--once`); interactive PTY stays on `terminal`
|
|
17
|
+
(constitution: protocol selection).
|
|
18
|
+
|
|
19
|
+
## Intent
|
|
20
|
+
|
|
21
|
+
- **Read-only**, validated like `retrieve` (path-shaped commands gate through
|
|
22
|
+
`/console/inspect`). `stream` never writes.
|
|
23
|
+
- Wraps RouterOS follow semantics: `once`, `follow`, `duration=`,
|
|
24
|
+
`freeze-frame-interval=`. Targets are the `monitor`/`print follow`/sniffer
|
|
25
|
+
family of menus.
|
|
26
|
+
- **Transport:** true follow cannot ride REST (RouterOS REST has a 60s hard
|
|
27
|
+
cap), so `stream` is **native-api / ssh-first**. `--via rest-api` is
|
|
28
|
+
bounded-or-rejected, never a silent open-ended poll.
|
|
29
|
+
- Not a fan-out surface in single-session mode: a single `stream` rejects N>1
|
|
30
|
+
targets with `usage/fanout-not-supported` (constitution: target selection).
|
|
31
|
+
|
|
32
|
+
## Output contract (NDJSON stream of envelopes)
|
|
33
|
+
|
|
34
|
+
- Each frame is **one envelope** (`{ ok, data?, warnings?, error?, meta }`),
|
|
35
|
+
emitted as one NDJSON line under `--format json`. Text mode renders
|
|
36
|
+
human-readable frames.
|
|
37
|
+
- The stream is terminated by a final **summary envelope**: frame count,
|
|
38
|
+
duration, and stop reason (`duration-elapsed`, `interrupted`, `eof`,
|
|
39
|
+
`transport-error`).
|
|
40
|
+
- A mid-stream RouterOS or transport error is itself a frame with `ok: false`;
|
|
41
|
+
the **process exit code** reflects whether the stream *started* cleanly, not
|
|
42
|
+
whether every frame was `ok`.
|
|
43
|
+
- `--duration` / `--count` bound an otherwise open-ended follow; Ctrl-C stops it
|
|
44
|
+
and still emits the summary envelope.
|
|
45
|
+
|
|
46
|
+
## Flags (planned)
|
|
47
|
+
|
|
48
|
+
| Flag | Behavior |
|
|
49
|
+
| ---- | -------- |
|
|
50
|
+
| `--via <protocol>` | Pin transport (`native-api` or `ssh`). REST is bounded-or-rejected. |
|
|
51
|
+
| `--duration <dur>` | Stop after this wall-clock window (maps to RouterOS `duration=`). |
|
|
52
|
+
| `--count <n>` | Stop after N frames. |
|
|
53
|
+
| `--freeze-frame-interval <dur>` | RouterOS `freeze-frame-interval=` passthrough. |
|
|
54
|
+
| `--format <text\|json>` | `json` is NDJSON (one envelope per line); `text` is human frames. |
|
|
55
|
+
|
|
56
|
+
`tail` is a log-scoped alias: `centrs tail <router> /log` ≈ `tail -f` over
|
|
57
|
+
`/log print follow`.
|
|
58
|
+
|
|
59
|
+
## Open questions
|
|
60
|
+
|
|
61
|
+
- Whether `stream` over ssh shells out to the RouterOS CLI follow or rides a
|
|
62
|
+
structured channel; settle during the SSH transport work.
|
|
63
|
+
- Backpressure / max-buffered-frames policy for a fast producer.
|
|
64
|
+
|
|
65
|
+
Sequencing (decided): do **not** start `stream` until (1) a native-api
|
|
66
|
+
*streaming* spike confirms the reader is solid as a long-lived follow channel
|
|
67
|
+
(not just request/response), and (2) `retrieve` is further along and re-reviewed
|
|
68
|
+
for UX — `stream` should inherit a settled retrieve shape, not race it. The
|
|
69
|
+
native-api streaming reader already exists (`src/protocols/native-api.ts`) and is
|
|
70
|
+
the first follow consumer.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# terminal
|
|
2
|
+
|
|
3
|
+
Open an interactive RouterOS console.
|
|
4
|
+
|
|
5
|
+
Status: `terminal / mac-telnet` **and** `terminal / ssh` are both **`CHR-passed`**
|
|
6
|
+
(see `docs/MATRIX.md` and `examples.md` T1–T3 / TS1–TS2). The two transports take
|
|
7
|
+
different paths: **mac-telnet** is the in-process raw passthrough over
|
|
8
|
+
`MacTelnetConsole.attachInteractive`; **ssh** execs the host `ssh` with inherited
|
|
9
|
+
stdio (RouterOS grants no PTY, but `ssh user@host` opens the console and the OS
|
|
10
|
+
relays the already-clean stream — no screen emulation; a host target defaults to
|
|
11
|
+
ssh, a MAC target to mac-telnet). The mac-telnet path is described below;
|
|
12
|
+
`src/terminal.ts` is the raw passthrough over `MacTelnetConsole.attachInteractive`,
|
|
13
|
+
driven through an injectable `TerminalIo` (real stdin/stdout/`SIGWINCH` in
|
|
14
|
+
`src/cli/terminal.ts`). Two input modes by whether stdin is a TTY: interactive
|
|
15
|
+
(raw-mode keystrokes, real size) and batch (pipe a command, close on EOF after an
|
|
16
|
+
output drain). The pipe integration test does not exercise raw-mode/real-TTY-size
|
|
17
|
+
(a piped subprocess stdin is not a TTY); a full-TTY test would need a PTY and is
|
|
18
|
+
deferred.
|
|
19
|
+
|
|
20
|
+
`terminal` is also the documentation home for SSH as a centrs transport. **Re-scoped
|
|
21
|
+
(decided with the user): SSH lands transfer-first, not as one monolithic unit.**
|
|
22
|
+
The SSH transport *base* shipped as the **SFTP transfer client** (`transfer / ssh`,
|
|
23
|
+
`src/protocols/sftp.ts` over the host OpenSSH `sftp` subsystem); the `ssh-key` and
|
|
24
|
+
`insecure` settings landed **with** it (so no setting is half-wired without a
|
|
25
|
+
working transport — the rule still holds). `execute / ssh` and `terminal / ssh`
|
|
26
|
+
then followed and are both `CHR-passed`. **A CHR spike corrected the earlier "no
|
|
27
|
+
exec channel, needs an interactive-shell reader" framing:** RouterOS's SSH server
|
|
28
|
+
has **no pseudo-tty**, but a single-line `ssh user@host "<command>"` runs on the
|
|
29
|
+
console and returns clean output — so `execute / ssh` is a per-command batch client
|
|
30
|
+
and `terminal / ssh` is a thin inherited-stdio exec of `ssh`. Neither needed a
|
|
31
|
+
`MacTelnetConsole`-style screen-emulating reader. The one no-PTY limitation is the
|
|
32
|
+
device's, not centrs's: multi-line brace blocks (`{ … }`) do not work over
|
|
33
|
+
`terminal / ssh` — reach for `mac-telnet` when you need them.
|
|
34
|
+
|
|
35
|
+
## Intent
|
|
36
|
+
|
|
37
|
+
- `ssh` is the default. It honors system `ssh` config and the user's SSH agent;
|
|
38
|
+
`--ssh-key` overrides with an explicit private key path. CDB metadata may carry
|
|
39
|
+
the path to a per-router private key.
|
|
40
|
+
- `mac-telnet` is the right transport when `<router>` is a MAC address, or
|
|
41
|
+
when SSH is unreachable but a MAC is on file (Layer-2 emergency access).
|
|
42
|
+
- REST and native API have no terminal capability; `--via rest-api` for
|
|
43
|
+
`terminal` must error with `transport/capability-unsupported`.
|
|
44
|
+
- RoMON and WinBox Terminal are execute protocol surfaces in centrs, not
|
|
45
|
+
terminal routes, unless future matrix cells explicitly add terminal support.
|
|
46
|
+
|
|
47
|
+
## MAC target over SSH (`--resolve`)
|
|
48
|
+
|
|
49
|
+
A MAC `<router>` defaults to `mac-telnet`, which addresses the device by MAC —
|
|
50
|
+
no IP, no resolution. Pinning `--via ssh` asks for the IP-level console, so the
|
|
51
|
+
MAC must first resolve to an IP. That resolution follows the shared `--resolve`
|
|
52
|
+
policy (CDB record → `--resolve arp` host-ARP opt-in → error; never a silent ARP
|
|
53
|
+
or transport swap) — see `docs/CONSTITUTION.md`, *Target selection grammar* and
|
|
54
|
+
*Protocol selection*.
|
|
55
|
+
|
|
56
|
+
Terminal-specific: when no IP resolves, the `target/mac-unresolved` (and
|
|
57
|
+
`target/mac-not-in-arp`) tip **leads with `--via mac-telnet`**, since for terminal
|
|
58
|
+
reaching the device over L2 needs no IP at all — it is the first thing to try.
|
|
59
|
+
`--resolve` is a no-op for the `mac-telnet` default (the MAC is the target).
|
|
60
|
+
|
|
61
|
+
## SSH key selection
|
|
62
|
+
|
|
63
|
+
Signed off (settings names): terminal uses the same `sshKey` setting as
|
|
64
|
+
`execute`: `--ssh-key <path>`, `CENTRS_SSH_KEY`, and CDB comment-kv
|
|
65
|
+
`ssh-key=<path>`. Precedence is defaults → config → CDB comment-kv → env →
|
|
66
|
+
CLI/API. When no key is set, terminal delegates identity selection to system
|
|
67
|
+
`ssh` config and the SSH agent; `--ssh-key` is an explicit override and must not
|
|
68
|
+
silently merge with a conflicting CDB/env key.
|
|
69
|
+
|
|
70
|
+
Unlike the batch sftp/execute clients, the **interactive** `terminal / ssh` argv
|
|
71
|
+
omits `BatchMode=yes` (`interactive: true` in `sshCommonOptions`), so the host
|
|
72
|
+
`ssh` can prompt on the inherited TTY for an encrypted key's passphrase or a
|
|
73
|
+
password. centrs does not forward `--password` to ssh — OpenSSH accepts no
|
|
74
|
+
password on the argv — so password auth is satisfied by that interactive prompt,
|
|
75
|
+
not by the centrs-resolved credential.
|
|
76
|
+
|
|
77
|
+
Resolved by the sftp transfer client (`src/protocols/sftp.ts`), so they are no
|
|
78
|
+
longer open for the file path: **host-key verification** rides the unified
|
|
79
|
+
`--insecure` knob (default `StrictHostKeyChecking=accept-new` trust-on-first-use;
|
|
80
|
+
a changed key → `transport/host-key-mismatch`; `--insecure` →
|
|
81
|
+
`StrictHostKeyChecking=no` — see `docs/CONSTITUTION.md`, Transport trust);
|
|
82
|
+
**agent vs explicit-key** interplay and **algorithm negotiation** are delegated to
|
|
83
|
+
the host OpenSSH (`-i <ssh-key>` when set, else the agent / `~/.ssh/config`). The
|
|
84
|
+
MAC-on-file-but-SSH-pinned case is settled (see *MAC target over SSH* above):
|
|
85
|
+
centrs resolves a MAC for `--via ssh` CDB-first, then tips `--via mac-telnet` —
|
|
86
|
+
it does **not** silently fall back to L2 (constitution: a pinned `--via` is never
|
|
87
|
+
swapped). The no-pseudo-tty console reader is likewise resolved — a CHR spike
|
|
88
|
+
proved `ssh user@host` opens a clean console with no screen emulation.
|
|
89
|
+
|
|
90
|
+
The setting stores a key path only. Private key material, passphrases, and agent
|
|
91
|
+
contents are always sensitive and belong in `error.redactable_fields` if an
|
|
92
|
+
error carries them; the selected key path may appear in `meta.settings.sshKey`
|
|
93
|
+
with its source unless the caller marks paths sensitive.
|
|
94
|
+
|
|
95
|
+
## RouterOS SSH surface (device-side option alignment)
|
|
96
|
+
|
|
97
|
+
What RouterOS's SSH server exposes (`/ip/ssh`, grounded on the
|
|
98
|
+
[SSH](https://help.mikrotik.com/docs/spaces/ROS/pages/132350014/SSH) and
|
|
99
|
+
[User](https://help.mikrotik.com/docs/spaces/ROS/pages/8978504/User) pages,
|
|
100
|
+
RouterOS 7.x) and how the centrs client (host OpenSSH) lines up — so there are no
|
|
101
|
+
gaps between what the device offers and what centrs can negotiate:
|
|
102
|
+
|
|
103
|
+
| RouterOS `/ip/ssh` | Values (default) | How centrs aligns |
|
|
104
|
+
| ------------------ | ---------------- | ----------------- |
|
|
105
|
+
| `host-key-type` | `ed25519` \| `rsa` (**rsa**) | Host OpenSSH accepts both; the default device host key is RSA, so the client must offer `rsa-sha2-256/512` and `ssh-ed25519`. TOFU pins whichever the device presents. |
|
|
106
|
+
| `strong-crypto` | `yes` \| `no` (**no**) | `yes` disables ssh-rsa/SHA1, null ciphers, and MD5, and uses sha256 MACs + a 2048-bit DH prime. OpenSSH already prefers `rsa-sha2-*` / curve25519, so a strong-crypto device negotiates cleanly with no client change. |
|
|
107
|
+
| `ciphers` | `3des-cbc`\|`aes-cbc`\|`aes-ctr`\|`aes-gcm`\|`auto`\|`null` (**auto**) | Per-cipher control added ~7.17. OpenSSH's default cipher set covers `aes-ctr`/`aes-gcm`; `null` (no encryption) is never offered by the client. |
|
|
108
|
+
| `password-authentication` | `yes-if-no-key` \| `yes` \| `no` (**yes-if-no-key**) | The default refuses password login once a user has an imported SSH key, so centrs's normal path is key auth (`--ssh-key` / agent). No password is ever placed on the `sftp` argv. |
|
|
109
|
+
| `publickey-authentication-options` | `none`\|`touch-required`\|`verify-required` (**none**) | FIDO touch/verify is handled by the host ssh-agent when the key is a FIDO key; transparent to centrs. |
|
|
110
|
+
| `forwarding-enabled` | `both`\|`local`\|`remote`\|`no` (**no**) | centrs does not use SSH forwarding; no dependency. |
|
|
111
|
+
| — (subsystem) | SFTP subsystem | The reliable **file** channel (no pseudo-tty; a single-line `ssh host "cmd"` exec runs, but it's for `execute`, not files); `transfer --via sftp` speaks it. scp would ride the same subsystem on modern OpenSSH. **CHR-confirmed (7.23.1):** `put`/`get`/`ls`/`mkdir`/`rm` all work over the subsystem. |
|
|
112
|
+
| `/user` group policy | `ftp` (+ `ssh`, `read`/`write`) | SFTP file access needs the user group's `ftp` policy. **CHR-confirmed:** the `full`-group admin (which includes `ftp`) authenticates and transfers; a restricted user must be granted `ftp`. |
|
|
113
|
+
|
|
114
|
+
**CHR finding (7.23.1):** RouterOS's sftp `ls -l` does **not** report a reliable
|
|
115
|
+
byte size (the long-name size column is unpopulated / server-format-specific). So
|
|
116
|
+
centrs's sftp client treats `ls` as an existence + name probe only, and
|
|
117
|
+
`transfer --verify size` over sftp trusts the **SFTP transfer guarantee** (a
|
|
118
|
+
partial `put`/`get` errors) rather than re-reading a size. REST/native keep their
|
|
119
|
+
JSON `/file` size cross-check. See `src/protocols/sftp.ts` (`parseLsOutput`) and
|
|
120
|
+
`src/transfer.ts` (`SftpFileBackend.findFile`).
|
|
121
|
+
|
|
122
|
+
These are device facts the host OpenSSH negotiates for us; the alignment table is
|
|
123
|
+
the audit trail that the centrs file path has no hole versus what RouterOS offers.
|
|
124
|
+
|
|
125
|
+
## mac-telnet L2 validation
|
|
126
|
+
|
|
127
|
+
Decided (2026-06-06): terminal/mac-telnet uses the same `@tikoci/quickchr`
|
|
128
|
+
host-side L2 capture path as execute/mac-telnet — a host TCP server with the CHR
|
|
129
|
+
on a `socket-connect` NIC (QEMU streams guest frames length-prefixed; a frame
|
|
130
|
+
written back injects L2). Loopback-only, cross-platform, no root. Prefer
|
|
131
|
+
`socket-connect` over `socket-mcast` (macOS-broken — `SO_REUSEPORT`). That
|
|
132
|
+
harness is wired and `execute / mac-telnet` is **`CHR-passed`** over it
|
|
133
|
+
(`test/integration/mac-telnet-console.test.ts`): MTWEI login, the console reader,
|
|
134
|
+
clean command output, writes, and the `:parse` validation gate all work end to
|
|
135
|
+
end. The bridge also relays UDP both ways (`udpPort`), so the production UDP
|
|
136
|
+
transport is exercised, not just the in-process callbacks. See
|
|
137
|
+
`commands/execute/README.md` (mac-telnet L2 validation) for the console wiring and
|
|
138
|
+
auth findings (MTWEI required, MD5 refused, `END_AUTH` ≠ success).
|
|
139
|
+
|
|
140
|
+
The unresolved-MAC default remains load-bearing: a MAC target chooses
|
|
141
|
+
mac-telnet for terminal unless an IP-level resolution path is explicitly
|
|
142
|
+
requested. Covered with resolver/selection tests plus packet/session coverage in
|
|
143
|
+
`test/unit/mac-telnet.test.ts` and EC-SRP coverage in `test/unit/mtwei.test.ts`.
|
|
144
|
+
|
|
145
|
+
## Interactive-console work (advances terminal/mac-telnet to CHR-passed)
|
|
146
|
+
|
|
147
|
+
The console reader landed with `execute / mac-telnet` (`CHR-passed`): it answers
|
|
148
|
+
the login terminal-size probe, auto-clears the first-login license, syncs on the
|
|
149
|
+
prompt, and emulates the CR/LF screen — grounded on CHR 7.23.1 (the login probe
|
|
150
|
+
is actually a multi-step ANSI cursor probe; `ESC[6n` DSR is the operative query,
|
|
151
|
+
answered with `ESC[rows;colsR`). `terminal / mac-telnet` reuses that reader; what
|
|
152
|
+
it adds on top is **interactive relay**, not protocol work:
|
|
153
|
+
|
|
154
|
+
- **PTY relay**: wire local stdin → `session.sendInput` and console output →
|
|
155
|
+
stdout in raw mode, instead of the `run(cli)` capture path `execute` uses.
|
|
156
|
+
- **Real terminal size**: report the actual PTY rows/cols to the probe (and on
|
|
157
|
+
`SIGWINCH`), where `execute` reports an oversized screen to avoid paging/wrap.
|
|
158
|
+
- **Idle keepalive + retransmit (already landed):** `MacTelnetSession.tick(now)`
|
|
159
|
+
(driven by `MacTelnetConsole` on a ~200ms interval) sends an empty-ACK keepalive
|
|
160
|
+
after ~8s idle and retransmits the last unacked frame on the reference backoff
|
|
161
|
+
schedule. So a held-open `terminal` is already kept alive; the relay just needs
|
|
162
|
+
to keep `tick` running (it does, via the console reader).
|
|
163
|
+
- **~10s prime stall (optional polish):** every mac-telnet login waits ~10s for
|
|
164
|
+
terminal negotiation before the prompt; answering the DSR sets the width but
|
|
165
|
+
does not remove the stall. A cursor-tracking emulator (track row/col, answer
|
|
166
|
+
each `ESC[6n` with the real clamped position) would likely kill it. Latency
|
|
167
|
+
only — not a blocker.
|
|
168
|
+
|
|
169
|
+
Protocol facts to honor: **MTWEI does not encrypt the terminal stream** — treat
|
|
170
|
+
it as management-plane traffic on a trusted L2 segment.
|
|
171
|
+
|
|
172
|
+
## Open questions
|
|
173
|
+
|
|
174
|
+
- How to surface keystroke recording / replay for tests.
|
|
175
|
+
|
|
176
|
+
The transport, auth, console reader, and data path are all grounded; only the
|
|
177
|
+
interactive-relay layer above remains for terminal/mac-telnet.
|