@byokit/herdr 0.1.0-next.dbe7142
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 +17 -0
- package/LICENSE +202 -0
- package/README.md +18 -0
- package/dist/agents.d.ts +7 -0
- package/dist/agents.js +141 -0
- package/dist/approvals.d.ts +24 -0
- package/dist/approvals.js +81 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +89 -0
- package/dist/close.d.ts +4 -0
- package/dist/close.js +85 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +5 -0
- package/dist/device.d.ts +40 -0
- package/dist/device.js +128 -0
- package/dist/generated/events.d.ts +371 -0
- package/dist/generated/events.js +3 -0
- package/dist/generated/methods.d.ts +2299 -0
- package/dist/generated/methods.js +3 -0
- package/dist/generated/report.json +50 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/kit.d.ts +73 -0
- package/dist/kit.js +209 -0
- package/dist/link.d.ts +24 -0
- package/dist/link.js +324 -0
- package/dist/notices.d.ts +14 -0
- package/dist/notices.js +88 -0
- package/dist/socket.d.ts +2 -0
- package/dist/socket.js +137 -0
- package/dist/supervise.d.ts +17 -0
- package/dist/supervise.js +137 -0
- package/dist/terminal.d.ts +6 -0
- package/dist/terminal.js +105 -0
- package/dist/testing/contract.d.ts +9 -0
- package/dist/testing/contract.js +308 -0
- package/dist/testing/fake-herdr/bin.d.ts +5 -0
- package/dist/testing/fake-herdr/bin.js +210 -0
- package/dist/testing/fake-herdr/server.d.ts +21 -0
- package/dist/testing/fake-herdr/server.js +604 -0
- package/dist/testing/fake-herdr/world.d.ts +84 -0
- package/dist/testing/fake-herdr/world.js +85 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +7 -0
- package/dist/types.d.ts +115 -0
- package/dist/types.js +3 -0
- package/dist/words.d.ts +10 -0
- package/dist/words.js +18 -0
- package/dist/words.json +19 -0
- package/package.json +60 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// The HERDR_BIN shim (docs/runtime-kits.md 6.8): a Node script the kit execs for `--version`,
|
|
2
|
+
// `api schema`, the `server` verb, JSON CLI verbs and `terminal session`. World state lives on the
|
|
3
|
+
// control socket; this process never speaks JSON-RPC except to forward a CLI verb. Ported from muxr
|
|
4
|
+
// `perf/fake-herdr/bin.mjs` with the perf byte rates and graphics probes removed.
|
|
5
|
+
import { chmodSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { createConnection } from 'node:net';
|
|
7
|
+
import { dirname, resolve } from 'node:path';
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
|
+
import { HERDR_PROTOCOL, HERDR_VERSION } from "../../constants.js";
|
|
10
|
+
const SELF = fileURLToPath(import.meta.url);
|
|
11
|
+
export function writeBinShim({ dir, socketPath }) {
|
|
12
|
+
mkdirSync(dir, { recursive: true });
|
|
13
|
+
const binPath = `${dir}/herdr`;
|
|
14
|
+
// The kit execs the shim; the shim re-imports this module, so the fake's behavior always matches
|
|
15
|
+
// the source the tests type-check. Extensionless → CommonJS, where dynamic `import()` works.
|
|
16
|
+
// The shebang pins the running Node binary: the kit passes the env verbatim, so the child's PATH
|
|
17
|
+
// may not contain node — /usr/bin/env node would exit 127 there.
|
|
18
|
+
writeFileSync(binPath, `#!${process.execPath}\nimport(${JSON.stringify(pathToFileURL(SELF).href)}).then((m) => m.main(process.argv.slice(2)), (e) => { console.error(e); process.exit(1); });\n`, { encoding: 'utf8', mode: 0o700 });
|
|
19
|
+
chmodSync(binPath, 0o700);
|
|
20
|
+
return binPath;
|
|
21
|
+
}
|
|
22
|
+
function flag(args, name) {
|
|
23
|
+
const index = args.indexOf(name);
|
|
24
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
25
|
+
}
|
|
26
|
+
function rpc(socketPath, method, params) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const socket = createConnection(socketPath);
|
|
29
|
+
const id = `fake_bin_${process.pid}_${Math.random().toString(16).slice(2)}`;
|
|
30
|
+
let buffer = '';
|
|
31
|
+
const finish = (error, result) => {
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
socket.removeAllListeners();
|
|
34
|
+
socket.destroy();
|
|
35
|
+
if (error !== null)
|
|
36
|
+
reject(error);
|
|
37
|
+
else
|
|
38
|
+
resolve(result ?? {});
|
|
39
|
+
};
|
|
40
|
+
const timer = setTimeout(() => finish(new Error('fake-herdr bin: socket timeout')), 5000);
|
|
41
|
+
socket.on('connect', () => socket.write(`${JSON.stringify({ id, method, params })}\n`));
|
|
42
|
+
socket.on('data', (chunk) => {
|
|
43
|
+
buffer += chunk.toString('utf8');
|
|
44
|
+
const lines = buffer.split('\n');
|
|
45
|
+
buffer = lines.pop() ?? '';
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
if (line.trim() === '')
|
|
48
|
+
continue;
|
|
49
|
+
let message;
|
|
50
|
+
try {
|
|
51
|
+
message = JSON.parse(line);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (message.id !== id)
|
|
57
|
+
continue;
|
|
58
|
+
if (message.error !== undefined && message.error !== null) {
|
|
59
|
+
finish(new Error(`herdr: ${message.error.code ?? 'error'}: ${message.error.message ?? ''}`));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
finish(null, message.result ?? {});
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
socket.on('error', (error) => finish(error));
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function runServer() {
|
|
71
|
+
const socketPath = process.env.HERDR_SOCKET_PATH;
|
|
72
|
+
if (!socketPath) {
|
|
73
|
+
process.stderr.write('fake-herdr bin: server needs HERDR_SOCKET_PATH\n');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const { startFakeHerdr } = await import("./server.js");
|
|
77
|
+
// One startup line: a harness reads the socket it must expect, and the recorded env lets a test
|
|
78
|
+
// compare the exact environment the kit spawned us with (docs/runtime-kits.md 6.3).
|
|
79
|
+
const handle = await startFakeHerdr({ dir: dirname(socketPath), socketPath, onStop: () => process.exit(0) });
|
|
80
|
+
process.stdout.write(`${JSON.stringify({ socketPath: handle.socketPath, pid: process.pid, env: process.env })}\n`);
|
|
81
|
+
const stop = () => { void handle.stop(); };
|
|
82
|
+
process.on('SIGTERM', stop);
|
|
83
|
+
process.on('SIGINT', stop);
|
|
84
|
+
}
|
|
85
|
+
// `{"type":"fake.stream","count":n,"size":b,"progress":file}` on stdin: n frames with data `<i>:` plus b bytes,
|
|
86
|
+
// written as fast as the pipe takes them. After each write the frames-written count lands in `progress`, so a
|
|
87
|
+
// test can see how far a paused reader let the stream run.
|
|
88
|
+
async function stream(paneId, o) {
|
|
89
|
+
const pad = 'x'.repeat(o.size ?? 1024);
|
|
90
|
+
for (let i = 0; i < (o.count ?? 0); i += 1) {
|
|
91
|
+
const ok = process.stdout.write(`${JSON.stringify({ type: 'terminal.frame', pane_id: paneId, data: `${i}:${pad}` })}\n`);
|
|
92
|
+
if (o.progress)
|
|
93
|
+
writeFileSync(o.progress, String(i + 1));
|
|
94
|
+
if (!ok)
|
|
95
|
+
await new Promise((resolve) => process.stdout.once('drain', resolve));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function runTerminal(args) {
|
|
99
|
+
// `herdr terminal session <control|observe> <pane> [--takeover] --cols <n> --rows <n>`.
|
|
100
|
+
// The fake needs no server for this: one ready frame, then every send line echoes back as an
|
|
101
|
+
// output frame (docs/runtime-kits.md 6.8), except `fake.stream` below.
|
|
102
|
+
const mode = args[0] === 'observe' ? 'observe' : 'control';
|
|
103
|
+
const rest = args[0] === 'control' || args[0] === 'observe' ? args.slice(1) : args;
|
|
104
|
+
const paneId = rest[0] ?? 'w1:p1';
|
|
105
|
+
const cols = Number(flag(args, '--cols')) || 80;
|
|
106
|
+
const rows = Number(flag(args, '--rows')) || 24;
|
|
107
|
+
const takeover = args.includes('--takeover');
|
|
108
|
+
process.stdout.write(`${JSON.stringify({ type: 'terminal.ready', pane_id: paneId, mode, takeover, cols, rows })}\n`);
|
|
109
|
+
let buffer = '';
|
|
110
|
+
process.stdin.setEncoding('utf8');
|
|
111
|
+
process.stdin.on('data', (chunk) => {
|
|
112
|
+
buffer += chunk;
|
|
113
|
+
const lines = buffer.split('\n');
|
|
114
|
+
buffer = lines.pop() ?? '';
|
|
115
|
+
for (const line of lines) {
|
|
116
|
+
if (line.trim() === '')
|
|
117
|
+
continue;
|
|
118
|
+
let data = line;
|
|
119
|
+
try {
|
|
120
|
+
const message = JSON.parse(line);
|
|
121
|
+
if (message.type === 'terminal.release')
|
|
122
|
+
process.exit(0);
|
|
123
|
+
if (message.type === 'fake.stream') {
|
|
124
|
+
void stream(paneId, message);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (typeof message.data === 'string')
|
|
128
|
+
data = message.data;
|
|
129
|
+
}
|
|
130
|
+
catch { /* a raw line echoes as-is */ }
|
|
131
|
+
process.stdout.write(`${JSON.stringify({ type: 'terminal.frame', pane_id: paneId, data })}\n`);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
process.stdin.on('end', () => process.exit(0));
|
|
135
|
+
process.stdin.resume();
|
|
136
|
+
}
|
|
137
|
+
async function runCliVerb(argv) {
|
|
138
|
+
const positional = [];
|
|
139
|
+
const params = {};
|
|
140
|
+
let socketPath = process.env.HERDR_SOCKET_PATH;
|
|
141
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
142
|
+
const arg = argv[index];
|
|
143
|
+
if (arg === '--socket') {
|
|
144
|
+
socketPath = argv[index + 1];
|
|
145
|
+
index += 1;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (arg.startsWith('--')) {
|
|
149
|
+
const key = arg.slice(2);
|
|
150
|
+
const value = argv[index + 1];
|
|
151
|
+
if (value !== undefined && !value.startsWith('--')) {
|
|
152
|
+
index += 1;
|
|
153
|
+
try {
|
|
154
|
+
params[key] = JSON.parse(value);
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
params[key] = value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
params[key] = true;
|
|
162
|
+
}
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
positional.push(arg);
|
|
166
|
+
}
|
|
167
|
+
if (positional.length < 2 || !socketPath) {
|
|
168
|
+
process.stderr.write(`fake-herdr bin: unhandled ${argv.join(' ')}\n`);
|
|
169
|
+
process.stdout.write('{}\n');
|
|
170
|
+
process.exitCode = 1;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const result = await rpc(socketPath, `${positional[0]}.${positional[1]}`, params);
|
|
175
|
+
process.stdout.write(`${JSON.stringify({ result })}\n`);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
process.stderr.write(`${error.message}\n`);
|
|
179
|
+
process.exitCode = 1;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export async function main(argv) {
|
|
183
|
+
const [cmd, sub] = argv;
|
|
184
|
+
if (cmd === '--version' || cmd === 'version') {
|
|
185
|
+
process.stdout.write(`herdr ${HERDR_VERSION}\n`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (cmd === 'api' && sub === 'schema') {
|
|
189
|
+
// Reports the pinned v0.9.1 snapshot identity (schema/herdr-api-0.9.1.json, docs/runtime-kits.md 6.7).
|
|
190
|
+
process.stdout.write(`${JSON.stringify({ title: 'herdr api schema', protocol: HERDR_PROTOCOL, version: HERDR_VERSION })}\n`);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (cmd === 'server') {
|
|
194
|
+
await runServer();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (cmd === 'terminal' && sub === 'session') {
|
|
198
|
+
runTerminal(argv.slice(2)); // args start at the mode token
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (cmd !== undefined) {
|
|
202
|
+
await runCliVerb(argv);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
process.stderr.write('fake-herdr bin: nothing to do\n');
|
|
206
|
+
process.exitCode = 1;
|
|
207
|
+
}
|
|
208
|
+
const invoked = process.argv[1] !== undefined && resolve(process.argv[1]) === SELF;
|
|
209
|
+
if (invoked)
|
|
210
|
+
await main(process.argv.slice(2));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type FakeWorld } from './world.ts';
|
|
2
|
+
export type FakeHerdrOptions = {
|
|
3
|
+
dir: string;
|
|
4
|
+
socketPath?: string;
|
|
5
|
+
world?: {
|
|
6
|
+
cwd?: string;
|
|
7
|
+
kinds?: readonly string[];
|
|
8
|
+
};
|
|
9
|
+
onStop?: () => void;
|
|
10
|
+
};
|
|
11
|
+
export type FakeHerdr = {
|
|
12
|
+
socketPath: string;
|
|
13
|
+
bin: string;
|
|
14
|
+
world: FakeWorld;
|
|
15
|
+
emit(event: {
|
|
16
|
+
type: string;
|
|
17
|
+
} & Record<string, unknown>): void;
|
|
18
|
+
setStatus(paneId: string, status: string): void;
|
|
19
|
+
stop(): Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
export declare function startFakeHerdr(options: FakeHerdrOptions): Promise<FakeHerdr>;
|