function11 1.0.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/.claude/settings.local.json +7 -0
- package/CHANGELOG.md +56 -0
- package/INSTRUCTIONS.md +1123 -0
- package/README.md +366 -0
- package/fn.js +177 -0
- package/fs/bin/host.exec.js +22 -0
- package/fs/bin/ls.js +14 -0
- package/fs/bin/man.js +21 -0
- package/fs/man/ls.md +26 -0
- package/fs/man/man.md +31 -0
- package/fs/proc/self/approve.js +33 -0
- package/fs/proc/self/kill.js +5 -0
- package/fs/proc/self/status.js +8 -0
- package/fs/proc/sys/loglevel.js +15 -0
- package/fs/proc/sys/sessions.js +5 -0
- package/fs/proc/sys/uptime.js +11 -0
- package/package.json +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# The Function11 Manual
|
|
2
|
+
|
|
3
|
+
**fn — The Completed U*IX**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Overview
|
|
8
|
+
|
|
9
|
+
**Function11 (fn)** is a minimalist operating shell and control system designed for:
|
|
10
|
+
|
|
11
|
+
* secure system administration,
|
|
12
|
+
* human–AI collaboration,
|
|
13
|
+
* elimination of supply-chain risk,
|
|
14
|
+
* complete transparency of execution.
|
|
15
|
+
|
|
16
|
+
fn replaces:
|
|
17
|
+
|
|
18
|
+
* traditional shells,
|
|
19
|
+
* Model Context Protocol (MCP),
|
|
20
|
+
* opaque AI tool-calling systems,
|
|
21
|
+
|
|
22
|
+
with a **single, file-oriented, command-driven environment** inspired by UNIX, Plan 9, and early X systems.
|
|
23
|
+
|
|
24
|
+
Function11 is not a framework.
|
|
25
|
+
It is not a package ecosystem.
|
|
26
|
+
It is a **system substrate**.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. Core Principles
|
|
31
|
+
|
|
32
|
+
Function11 obeys five axioms:
|
|
33
|
+
|
|
34
|
+
1. **Everything is a command**
|
|
35
|
+
2. **Every interface is a filesystem**
|
|
36
|
+
3. **State is inspectable**
|
|
37
|
+
4. **Execution requires approval**
|
|
38
|
+
5. **Discovery replaces configuration**
|
|
39
|
+
|
|
40
|
+
If a feature violates these axioms, it does not belong in fn.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 3. System Model
|
|
45
|
+
|
|
46
|
+
### 3.1 The Kernel
|
|
47
|
+
|
|
48
|
+
The fn kernel is a long-lived Node.js process that:
|
|
49
|
+
|
|
50
|
+
* maintains sessions,
|
|
51
|
+
* enforces approval,
|
|
52
|
+
* dispatches commands,
|
|
53
|
+
* exposes a virtual filesystem.
|
|
54
|
+
|
|
55
|
+
The kernel does **not**:
|
|
56
|
+
|
|
57
|
+
* manage users,
|
|
58
|
+
* enforce permissions,
|
|
59
|
+
* auto-execute AI output,
|
|
60
|
+
* hide state.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### 3.2 Sessions
|
|
65
|
+
|
|
66
|
+
Each connection to fn creates a **session**.
|
|
67
|
+
|
|
68
|
+
A session has:
|
|
69
|
+
|
|
70
|
+
* its own namespace,
|
|
71
|
+
* its own `/proc`,
|
|
72
|
+
* its own approval state,
|
|
73
|
+
* isolated command execution.
|
|
74
|
+
|
|
75
|
+
Sessions are **namespaces**, not users or processes.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 4. Filesystem Layout
|
|
80
|
+
|
|
81
|
+
Function11 exposes a virtual filesystem rooted at `/`.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
/
|
|
85
|
+
├─ bin/ Executable commands
|
|
86
|
+
├─ man/ Command manuals
|
|
87
|
+
└─ proc/ Runtime control and state
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Nothing exists outside this structure.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 5. `/bin` — Commands
|
|
95
|
+
|
|
96
|
+
### 5.1 Command Definition
|
|
97
|
+
|
|
98
|
+
Each command is a JavaScript ESM module exporting:
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
export const meta = {
|
|
102
|
+
name,
|
|
103
|
+
description,
|
|
104
|
+
approval
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export async function run(ctx) {}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Commands:
|
|
111
|
+
|
|
112
|
+
* are discoverable via `ls /bin`
|
|
113
|
+
* document themselves via `man`
|
|
114
|
+
* may request approval
|
|
115
|
+
* may expose streams or filesystems
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### 5.2 Command Composition
|
|
120
|
+
|
|
121
|
+
Commands may:
|
|
122
|
+
|
|
123
|
+
* pipe output to other commands,
|
|
124
|
+
* mount filesystems,
|
|
125
|
+
* expose streams,
|
|
126
|
+
* request privileged execution.
|
|
127
|
+
|
|
128
|
+
There are **no hidden APIs**.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 6. `/man` — Documentation
|
|
133
|
+
|
|
134
|
+
Each command has a corresponding manual:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
/man/<command>.md
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The `man` command renders these files.
|
|
141
|
+
|
|
142
|
+
Manuals are:
|
|
143
|
+
|
|
144
|
+
* human-readable,
|
|
145
|
+
* AI-readable,
|
|
146
|
+
* authoritative.
|
|
147
|
+
|
|
148
|
+
If a command lacks a manual, it is incomplete.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 7. `/proc` — Control Surfaces
|
|
153
|
+
|
|
154
|
+
`/proc` is the primary control and introspection interface.
|
|
155
|
+
|
|
156
|
+
It is **bidirectional**.
|
|
157
|
+
|
|
158
|
+
### 7.1 `/proc/self`
|
|
159
|
+
|
|
160
|
+
Session-scoped control.
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
/proc/self/
|
|
164
|
+
├─ status Session state
|
|
165
|
+
├─ approve Approval gate
|
|
166
|
+
├─ stdin Input stream
|
|
167
|
+
├─ stdout Output stream
|
|
168
|
+
└─ kill Terminate session
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
A session always has access to its own `/proc/self`.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
### 7.2 `/proc/sys`
|
|
176
|
+
|
|
177
|
+
Global system state.
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
/proc/sys/
|
|
181
|
+
├─ uptime
|
|
182
|
+
├─ loglevel
|
|
183
|
+
└─ sessions
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Writes to `/proc/sys` affect the kernel globally.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 8. Approval System
|
|
191
|
+
|
|
192
|
+
### 8.1 Purpose
|
|
193
|
+
|
|
194
|
+
No command may:
|
|
195
|
+
|
|
196
|
+
* execute host commands,
|
|
197
|
+
* run containers,
|
|
198
|
+
* modify the system,
|
|
199
|
+
|
|
200
|
+
without **explicit approval**.
|
|
201
|
+
|
|
202
|
+
Approval is not a prompt.
|
|
203
|
+
It is **state**.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
### 8.2 `/proc/self/approve`
|
|
208
|
+
|
|
209
|
+
#### Read
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
cat /proc/self/approve
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Outputs:
|
|
216
|
+
|
|
217
|
+
* `idle` if no request is pending
|
|
218
|
+
* otherwise, a structured approval request
|
|
219
|
+
|
|
220
|
+
#### Write
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
echo yes > /proc/self/approve
|
|
224
|
+
echo no > /proc/self/approve
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Resolves the request.
|
|
228
|
+
|
|
229
|
+
Approval is:
|
|
230
|
+
|
|
231
|
+
* session-scoped,
|
|
232
|
+
* blocking,
|
|
233
|
+
* auditable,
|
|
234
|
+
* impossible to bypass.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 9. Human Interaction
|
|
239
|
+
|
|
240
|
+
### 9.1 Interactive Shell
|
|
241
|
+
|
|
242
|
+
Humans interact with fn via:
|
|
243
|
+
|
|
244
|
+
```sh
|
|
245
|
+
./fn --interactive
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The shell:
|
|
249
|
+
|
|
250
|
+
* uses readline,
|
|
251
|
+
* supports pipelines,
|
|
252
|
+
* resolves commands via `/bin`,
|
|
253
|
+
* reads state via `/proc`.
|
|
254
|
+
|
|
255
|
+
The shell is **not special**.
|
|
256
|
+
It is just a client.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 10. AI Interaction
|
|
261
|
+
|
|
262
|
+
### 10.1 AI as a Client
|
|
263
|
+
|
|
264
|
+
AI systems interact with fn exactly as humans do.
|
|
265
|
+
|
|
266
|
+
They may:
|
|
267
|
+
|
|
268
|
+
* list commands,
|
|
269
|
+
* read manuals,
|
|
270
|
+
* inspect `/proc`,
|
|
271
|
+
* propose actions.
|
|
272
|
+
|
|
273
|
+
They may **not**:
|
|
274
|
+
|
|
275
|
+
* self-approve,
|
|
276
|
+
* bypass `/proc/self/approve`,
|
|
277
|
+
* invent commands.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
### 10.2 AI Discovery Model
|
|
282
|
+
|
|
283
|
+
AI learns capabilities by:
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
ls /bin
|
|
287
|
+
man <command>
|
|
288
|
+
ls /proc
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
No tool schemas.
|
|
292
|
+
No MCP.
|
|
293
|
+
No prompt engineering.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## 11. Security Model
|
|
298
|
+
|
|
299
|
+
Function11 achieves security by:
|
|
300
|
+
|
|
301
|
+
* eliminating dependency chains,
|
|
302
|
+
* removing hidden execution paths,
|
|
303
|
+
* enforcing approval structurally,
|
|
304
|
+
* making state visible.
|
|
305
|
+
|
|
306
|
+
There are:
|
|
307
|
+
|
|
308
|
+
* no background privileges,
|
|
309
|
+
* no implicit trust,
|
|
310
|
+
* no invisible side effects.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## 12. Design Lineage
|
|
315
|
+
|
|
316
|
+
Function11 draws directly from:
|
|
317
|
+
|
|
318
|
+
* **UNIX** — composability and text streams
|
|
319
|
+
* **Plan 9** — namespaces and file interfaces
|
|
320
|
+
* **Inferno** — supervised concurrency
|
|
321
|
+
* **Early X** — minimalism and pixel clarity
|
|
322
|
+
|
|
323
|
+
It intentionally rejects:
|
|
324
|
+
|
|
325
|
+
* package ecosystems,
|
|
326
|
+
* opaque tooling,
|
|
327
|
+
* global mutable state,
|
|
328
|
+
* cloud-dependent AI systems.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 13. What Function11 Is Not
|
|
333
|
+
|
|
334
|
+
* Not a package manager
|
|
335
|
+
* Not an AI agent framework
|
|
336
|
+
* Not a container platform
|
|
337
|
+
* Not a desktop environment (yet)
|
|
338
|
+
|
|
339
|
+
It is the **substrate** those things may rest upon.
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 14. Status
|
|
344
|
+
|
|
345
|
+
Function11 is:
|
|
346
|
+
|
|
347
|
+
* conceptually complete,
|
|
348
|
+
* incrementally implementable,
|
|
349
|
+
* hostile to supply-chain attacks,
|
|
350
|
+
* legible to both humans and AI.
|
|
351
|
+
|
|
352
|
+
Everything beyond this manual is **refinement**, not reinvention.
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Closing Note
|
|
357
|
+
|
|
358
|
+
Function11 does not attempt to be clever.
|
|
359
|
+
|
|
360
|
+
It attempts to be **honest**.
|
|
361
|
+
|
|
362
|
+
Honest systems are inspectable.
|
|
363
|
+
Inspectable systems are secure.
|
|
364
|
+
Secure systems can trust AI.
|
|
365
|
+
|
|
366
|
+
That is the point of fn.
|
package/fn.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import readline from "node:readline";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
|
|
10
|
+
const FS_ROOT = path.join(__dirname, "fs");
|
|
11
|
+
|
|
12
|
+
/* -------------------------
|
|
13
|
+
* Namespace
|
|
14
|
+
* ------------------------- */
|
|
15
|
+
class Namespace {
|
|
16
|
+
constructor(root = FS_ROOT) {
|
|
17
|
+
this.root = root;
|
|
18
|
+
this.cwd = "/";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
resolve(p) {
|
|
22
|
+
if (!p.startsWith("/")) p = path.join(this.cwd, p);
|
|
23
|
+
return path.join(this.root, p);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* -------------------------
|
|
28
|
+
* Kernel
|
|
29
|
+
* ------------------------- */
|
|
30
|
+
class Kernel {
|
|
31
|
+
constructor() {
|
|
32
|
+
this.sessions = new Map();
|
|
33
|
+
this.nextSessionId = 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
createSession(io = {}) {
|
|
37
|
+
const id = this.nextSessionId++;
|
|
38
|
+
const session = {
|
|
39
|
+
id,
|
|
40
|
+
created: Date.now(),
|
|
41
|
+
alive: true,
|
|
42
|
+
stdin: io.stdin || process.stdin,
|
|
43
|
+
stdout: io.stdout || process.stdout,
|
|
44
|
+
namespace: new Namespace(),
|
|
45
|
+
approval: null
|
|
46
|
+
};
|
|
47
|
+
this.sessions.set(id, session);
|
|
48
|
+
return session;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
listBin() {
|
|
52
|
+
return fs.readdirSync(path.join(FS_ROOT, "bin"))
|
|
53
|
+
.filter(f => f.endsWith(".js"))
|
|
54
|
+
.map(f => f.replace(/\.js$/, ""));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async runCommand(session, name, args = []) {
|
|
58
|
+
const cmdPath = path.join(FS_ROOT, "bin", `${name}.js`);
|
|
59
|
+
if (!fs.existsSync(cmdPath)) {
|
|
60
|
+
session.stdout.write(`fn: command not found: ${name}\n`);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const cmd = await import(cmdPath);
|
|
65
|
+
const ctx = {
|
|
66
|
+
args,
|
|
67
|
+
session,
|
|
68
|
+
kernel: this,
|
|
69
|
+
ns: session.namespace,
|
|
70
|
+
stdout: session.stdout,
|
|
71
|
+
stderr: process.stderr,
|
|
72
|
+
approve: async () => true
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
await cmd.run(ctx);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async accessProc(session, pathParts, mode, data) {
|
|
79
|
+
const [scope, name] = pathParts;
|
|
80
|
+
|
|
81
|
+
if (scope === "self") {
|
|
82
|
+
const modPath = path.join(FS_ROOT, "proc", "self", `${name}.js`);
|
|
83
|
+
if (!fs.existsSync(modPath)) {
|
|
84
|
+
session.stdout.write(`fn: /proc/self/${name}: no such entry\n`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const mod = await import(modPath);
|
|
89
|
+
const ctx = { session, kernel: this, stdout: session.stdout };
|
|
90
|
+
|
|
91
|
+
if (mode === "read" && mod.read) await mod.read(ctx);
|
|
92
|
+
if (mode === "write" && mod.write) await mod.write(ctx, data);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (scope === "sys") {
|
|
97
|
+
const modPath = path.join(FS_ROOT, "proc", "sys", `${name}.js`);
|
|
98
|
+
if (!fs.existsSync(modPath)) {
|
|
99
|
+
session.stdout.write(`fn: /proc/sys/${name}: no such entry\n`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const mod = await import(modPath);
|
|
104
|
+
const ctx = { kernel: this, stdout: session.stdout };
|
|
105
|
+
|
|
106
|
+
if (mode === "read" && mod.read) await mod.read(ctx);
|
|
107
|
+
if (mode === "write" && mod.write) await mod.write(ctx, data);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
session.stdout.write(`fn: /proc/${scope}: invalid scope\n`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async requestApproval(session, request) {
|
|
115
|
+
if (session.approval) {
|
|
116
|
+
throw new Error("approval already pending");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let resolve;
|
|
120
|
+
const promise = new Promise(r => (resolve = r));
|
|
121
|
+
|
|
122
|
+
session.approval = {
|
|
123
|
+
request,
|
|
124
|
+
resolve
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return promise;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* -------------------------
|
|
132
|
+
* Interactive Shell
|
|
133
|
+
* ------------------------- */
|
|
134
|
+
const kernel = new Kernel();
|
|
135
|
+
const session = kernel.createSession({
|
|
136
|
+
stdin: process.stdin,
|
|
137
|
+
stdout: process.stdout
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const rl = readline.createInterface({
|
|
141
|
+
input: process.stdin,
|
|
142
|
+
output: process.stdout,
|
|
143
|
+
prompt: "fn> "
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
rl.prompt();
|
|
147
|
+
|
|
148
|
+
rl.on("line", async line => {
|
|
149
|
+
line = line.trim();
|
|
150
|
+
|
|
151
|
+
// proc write: echo X > /proc/name
|
|
152
|
+
const writeMatch = line.match(/^echo\s+(.+)\s+>\s+\/proc\/(.+)/);
|
|
153
|
+
if (writeMatch) {
|
|
154
|
+
const [, data, p] = writeMatch;
|
|
155
|
+
await kernel.accessProc(session, p.split("/"), "write", data);
|
|
156
|
+
rl.prompt();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// proc read: cat /proc/name
|
|
161
|
+
const readMatch = line.match(/^cat\s+\/proc\/(.+)/);
|
|
162
|
+
if (readMatch) {
|
|
163
|
+
const [, p] = readMatch;
|
|
164
|
+
await kernel.accessProc(session, p.split("/"), "read");
|
|
165
|
+
rl.prompt();
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const [cmd, ...args] = line.split(/\s+/);
|
|
170
|
+
if (cmd) await kernel.runCommand(session, cmd, args);
|
|
171
|
+
rl.prompt();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
rl.on("close", () => {
|
|
175
|
+
console.log("fn: session ended");
|
|
176
|
+
process.exit(0);
|
|
177
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: "host.exec",
|
|
3
|
+
description: "Execute host command (requires approval)",
|
|
4
|
+
approval: "host"
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export async function run(ctx) {
|
|
8
|
+
const cmd = ctx.args.join(" ");
|
|
9
|
+
|
|
10
|
+
const ok = await ctx.kernel.requestApproval(ctx.session, {
|
|
11
|
+
action: "host-exec",
|
|
12
|
+
command: cmd,
|
|
13
|
+
reason: "execute host command"
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
if (!ok) {
|
|
17
|
+
ctx.stdout.write("execution denied\n");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
ctx.stdout.write(`(pretend) running: ${cmd}\n`);
|
|
22
|
+
}
|
package/fs/bin/ls.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: "ls",
|
|
3
|
+
description: "List commands",
|
|
4
|
+
approval: "none"
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export async function run(ctx) {
|
|
8
|
+
const bin = ctx.ns.resolve("/bin");
|
|
9
|
+
const files = (await import("node:fs")).default
|
|
10
|
+
.readdirSync(bin)
|
|
11
|
+
.filter(f => f.endsWith(".js"))
|
|
12
|
+
.map(f => f.replace(/\.js$/, ""));
|
|
13
|
+
ctx.stdout.write(files.join("\n") + "\n");
|
|
14
|
+
}
|
package/fs/bin/man.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export const meta = {
|
|
5
|
+
name: "man",
|
|
6
|
+
description: "Show manual",
|
|
7
|
+
approval: "none"
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export async function run(ctx) {
|
|
11
|
+
const [cmd] = ctx.args;
|
|
12
|
+
if (!cmd) return ctx.stdout.write("usage: man <command>\n");
|
|
13
|
+
|
|
14
|
+
const manPath = ctx.ns.resolve(`/man/${cmd}.md`);
|
|
15
|
+
if (!fs.existsSync(manPath)) {
|
|
16
|
+
ctx.stdout.write(`man: no entry for ${cmd}\n`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
ctx.stdout.write(fs.readFileSync(manPath, "utf8"));
|
|
21
|
+
}
|
package/fs/man/ls.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# ls
|
|
2
|
+
|
|
3
|
+
List all available commands in `/bin`.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
ls
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Description
|
|
12
|
+
|
|
13
|
+
Lists all executable commands available in the Function11 environment. Each command is a JavaScript module in `/bin` that exports a `meta` object and a `run` function.
|
|
14
|
+
|
|
15
|
+
## Examples
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
fn> ls
|
|
19
|
+
ls
|
|
20
|
+
man
|
|
21
|
+
host.exec
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## See Also
|
|
25
|
+
|
|
26
|
+
- man(1) - read command documentation
|
package/fs/man/man.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# man
|
|
2
|
+
|
|
3
|
+
Display command documentation.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
man <command>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Description
|
|
12
|
+
|
|
13
|
+
Reads and displays the manual page for the specified command. Manual pages are stored in `/man` as Markdown files.
|
|
14
|
+
|
|
15
|
+
This is the primary discovery mechanism for understanding what commands do and how to use them.
|
|
16
|
+
|
|
17
|
+
## Examples
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
fn> man ls
|
|
21
|
+
# ls
|
|
22
|
+
List all available commands...
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Discoverable by AI
|
|
26
|
+
|
|
27
|
+
AI systems can use `man` to learn about available commands without requiring external schemas or tool definitions. The filesystem itself is the API.
|
|
28
|
+
|
|
29
|
+
## See Also
|
|
30
|
+
|
|
31
|
+
- ls(1) - list available commands
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export async function read(ctx) {
|
|
2
|
+
const a = ctx.session.approval;
|
|
3
|
+
if (!a) {
|
|
4
|
+
ctx.stdout.write("idle\n");
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
ctx.stdout.write(
|
|
9
|
+
"PENDING APPROVAL\n" +
|
|
10
|
+
`action: ${a.request.action}\n` +
|
|
11
|
+
`command: ${a.request.command}\n` +
|
|
12
|
+
`reason: ${a.request.reason}\n`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function write(ctx, data) {
|
|
17
|
+
const a = ctx.session.approval;
|
|
18
|
+
if (!a) {
|
|
19
|
+
ctx.stdout.write("no pending approval\n");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const v = data.trim().toLowerCase();
|
|
24
|
+
if (v !== "yes" && v !== "no") {
|
|
25
|
+
ctx.stdout.write("write 'yes' or 'no'\n");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
ctx.session.approval = null;
|
|
30
|
+
a.resolve(v === "yes");
|
|
31
|
+
|
|
32
|
+
ctx.stdout.write(`approval ${v}\n`);
|
|
33
|
+
}
|