feat(cli): --stdin piped JSON payloads for structured commands (T-315)

The C client slurps stdin when it sees --stdin, strips the flag, and ships
the payload alongside the argv; the Dart unwrap folds it into the request
as a `stdin` arg (undeclared keys pass the schema untouched). icon.show and
image.show now read that payload as the peer of --file (stdin wins). Bounded
slurp + the envelope's existing size guard keep a huge payload from
corrupting the wire. C client builds clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 23:56:33 +02:00
co-authored by Claude Opus 4.8
parent 1f6d31ac73
commit 64410e8047
10 changed files with 160 additions and 21 deletions
+12 -6
View File
@@ -62,11 +62,14 @@ Future<IpcResponse> _show(IpcRequest req, MessagePublisher? Function() publisher
}
final entries = <Map<String, Object?>>[];
// The entry payload comes from a piped --stdin (T-315) or a --file; --stdin
// wins. Either is a JSON array of {icon,label,description,color}.
final stdin = _str(req.args['stdin']);
final file = _str(req.args['file']);
if (file != null) {
final raw = readFile == null ? null : await readFile(file);
if (raw == null) {
String? payload = stdin;
if (payload == null && file != null) {
payload = readFile == null ? null : await readFile(file);
if (payload == null) {
return IpcResponse.err(
id: req.id,
error: IpcError(
@@ -77,11 +80,14 @@ Future<IpcResponse> _show(IpcRequest req, MessagePublisher? Function() publisher
),
);
}
}
if (payload != null) {
Object? decoded;
try {
decoded = jsonDecode(raw);
decoded = jsonDecode(payload);
} on FormatException catch (e) {
return _userErr(req.id, 'invalid JSON in $file: ${e.message}');
return _userErr(req.id, 'invalid JSON in the icon payload: ${e.message}');
}
if (decoded is! List) return _userErr(req.id, 'icon metadata must be a JSON array of entries');
for (final item in decoded) {