1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! **The operator's document** (yog's `docs/REMOTE.md` §5.2, §5.4; DESIGN
//! §3.4): what this box offers, and — by dropping half of it — what it says it
//! offers.
//!
//! ```json
//! [{"name": "Bash",
//! "description": "run a command in a shell",
//! "input_schema": {"type": "object",
//! "properties": {"command": {"type": "string"}},
//! "required": ["command"]},
//! "command": ["/usr/local/libexec/thrall-tools/bash-tool"],
//! "cwd": "/srv/work"}]
//! ```
//!
//! **One document, two readings.** The first three keys *are* REMOTE §5.1's
//! advertised element, verbatim, and [`advertisement`] is the whole of the
//! derivation: drop `command` and `cwd`. So what this box offers and what it
//! can actually run cannot drift, which is the entire reason the config is not
//! a pair of lists an operator has to keep in step.
//!
//! **Local config gates what is enabled, and this document is the gate.** A
//! tool absent from it is a tool this box does not have. Server-side
//! adjudication is unchanged, stacks on top, and fails closed — but it stacks
//! on *this*, so removing a capability from this machine is deleting an entry
//! here rather than editing anything, on the box whose operator is the one
//! entitled to decide (DESIGN §3.4).
//!
//! **`command` is an argv, spawned directly.** There is no shell and no
//! interpolation of the invocation's input into it. A shell would make the
//! declared `input_schema` advisory and turn an operator's config file into a
//! command-injection surface for anything a model can type; the input reaches
//! the command the way a local tool contract already delivers one — the JSON on
//! stdin, bytes on stdout, the exit code the verdict (bl-4cda).
//!
//! **It sits beside the channel material, not inside anything generated**
//! (`<thrall-data-root>/tools.json`, the sibling of `wire/`), for the material's
//! reason exactly: it describes *this machine*, it is written by the operator's
//! hand, and nothing thrall generates may sit where a rebuild would take it.
use ;
use Value;
use crate;
use crate;
/// The document's leaf under the data root.
pub const TOOLS: &str = "tools.json";
/// One tool this box offers: the advertised half, and the local half that is
/// never presented to anyone.
/// This box's document, under a data root.
/// Read it, or say why it is not one.
///
/// **An absent document is a refusal rather than the empty set.** A foot with
/// nothing to offer has nothing to do, and starting one is an explicit act that
/// deserves an explicit answer — the same posture absent channel material takes
/// (`channel::material`), and the opposite of the *presented* set, where an
/// empty array is a legitimate thing to say.
/// One element: the advertised three read by the **same** decoder the wire
/// spends, then the local two.
///
/// **`cwd` must be absolute, and that is decided here** (bl-3c93). Whether a
/// path is absolute is a property of the document — static, knowable at the
/// read, and never changing afterwards — so it belongs with the empty argv and
/// the unusable name rather than at the first invocation. It is the refusal
/// `paths::root` already makes about the data root, in this operator's other
/// document: a relative path resolves against whatever directory the
/// supervisor happened to start this process in, which nobody wrote down and
/// which changes when the unit file does. Whether the directory *exists* is a
/// fact about the box rather than about the file, can change between the read
/// and the run, and is answered by the spawn (`exec::child`).
/// **The advertisement, derived** (REMOTE §5.2): the same document with the
/// local half dropped. The one derivation, so this box cannot offer what it
/// cannot run.
/// Which element an invocation names, by position — an index rather than a
/// borrow, so the caller resolves it against the very list it passed in.