@hasna/hooks 0.10.9 → 0.11.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/README.md +48 -0
- package/bin/hooks-mcp.js +352 -182
- package/bin/index.js +2314 -1607
- package/bin/native-readiness-entry.js +355 -0
- package/dist/index.js +502 -284
- package/dist/lib/codex-safety-check.d.ts +2 -1
- package/dist/lib/codex-safety-trust.d.ts +40 -8
- package/dist/lib/installer.d.ts +11 -0
- package/dist/lib/native-readiness-command.d.ts +14 -0
- package/dist/lib/native-safety-registration.d.ts +38 -7
- package/dist/lib/readiness-process.d.ts +20 -0
- package/dist/native-readiness.d.ts +68 -0
- package/dist/native-readiness.js +328 -0
- package/hooks/native-readiness-entry.ts +31 -0
- package/package.json +7 -2
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
// src/native-readiness.ts
|
|
2
|
+
var import_node_crypto = require("node:crypto");
|
|
3
|
+
|
|
4
|
+
// src/lib/readiness-process.ts
|
|
5
|
+
var import_node_child_process = require("node:child_process");
|
|
6
|
+
var supervisor = String.raw`
|
|
7
|
+
const {spawn}=require("node:child_process");
|
|
8
|
+
const spec=JSON.parse(process.argv.at(-1));
|
|
9
|
+
// This runtime was created detached and is its group's live leader. Its
|
|
10
|
+
// independent timer and parent pipe remain effective if the observer exits
|
|
11
|
+
// or its event loop stops responding. Only this freshly created tree is hit.
|
|
12
|
+
const cleanup=()=>{try{process.kill(-process.pid,"SIGKILL")}catch{process.exit(1)}};
|
|
13
|
+
setTimeout(cleanup,spec.timeout);
|
|
14
|
+
process.stdin.once("end",cleanup);
|
|
15
|
+
process.stdin.once("error",cleanup);
|
|
16
|
+
process.stdin.on("data",cleanup);
|
|
17
|
+
process.stdin.resume();
|
|
18
|
+
for(const signal of ["SIGTERM","SIGINT","SIGHUP"])process.once(signal,cleanup);
|
|
19
|
+
let done=false, out=Buffer.alloc(0), err=Buffer.alloc(0);
|
|
20
|
+
function report(status,error){
|
|
21
|
+
if(done)return; done=true;
|
|
22
|
+
process.stdout.write(JSON.stringify({status,error,stdout:out.toString("utf8"),stderr:err.toString("utf8")})+"\n");
|
|
23
|
+
}
|
|
24
|
+
const child=spawn(spec.command,spec.args,{stdio:["ignore","pipe","pipe"],env:process.env});
|
|
25
|
+
for(const [stream,name] of [[child.stdout,"stdout"],[child.stderr,"stderr"]]){
|
|
26
|
+
stream.on("data",bytes=>{
|
|
27
|
+
if(done)return;
|
|
28
|
+
const before=name==="stdout"?out:err;
|
|
29
|
+
if(before.length+bytes.length>spec.maxBuffer){report(null,{code:"ENOBUFS"});return}
|
|
30
|
+
if(name==="stdout")out=Buffer.concat([out,bytes]);else err=Buffer.concat([err,bytes]);
|
|
31
|
+
});
|
|
32
|
+
stream.on("error",()=>report(null,{code:"ECHILD"}));
|
|
33
|
+
}
|
|
34
|
+
child.on("error",()=>report(null,{code:"ECHILD"}));
|
|
35
|
+
// A successful observation includes complete streams. A nested client that
|
|
36
|
+
// keeps them open after its parent exits is a failure requiring group cleanup.
|
|
37
|
+
child.on("exit",()=>setTimeout(()=>report(null,{code:"ECHILD"}),100));
|
|
38
|
+
child.on("close",(status,signal)=>report(status,signal?{code:"ECHILD"}:undefined));
|
|
39
|
+
`;
|
|
40
|
+
function runReadinessProcess(command, args, options) {
|
|
41
|
+
if (!["darwin", "linux"].includes(process.platform)) {
|
|
42
|
+
return Promise.resolve({ status: null, error: { code: "ENOTSUP" }, stdout: "", stderr: "" });
|
|
43
|
+
}
|
|
44
|
+
if (options.signal?.aborted) {
|
|
45
|
+
return Promise.resolve({ status: null, error: { code: "ABORT_ERR" }, stdout: "", stderr: "" });
|
|
46
|
+
}
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
const child = import_node_child_process.spawn(command, ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install", "--eval", supervisor, "--", JSON.stringify({ command, args, maxBuffer: options.maxBuffer, timeout: options.timeout })], {
|
|
49
|
+
cwd: options.cwd,
|
|
50
|
+
env: options.env,
|
|
51
|
+
detached: true,
|
|
52
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
53
|
+
});
|
|
54
|
+
let status = null, error;
|
|
55
|
+
let stopping = false, exited = false, finished = false;
|
|
56
|
+
let stdout = "", stderr = "", control = "";
|
|
57
|
+
const maximumEnvelope = options.maxBuffer * 12 + 1024;
|
|
58
|
+
let cleanup;
|
|
59
|
+
const finish = () => {
|
|
60
|
+
if (finished)
|
|
61
|
+
return;
|
|
62
|
+
finished = true;
|
|
63
|
+
clearTimeout(deadline);
|
|
64
|
+
clearTimeout(cleanup);
|
|
65
|
+
options.signal?.removeEventListener("abort", abort);
|
|
66
|
+
child.stdin?.destroy();
|
|
67
|
+
child.stdout?.destroy();
|
|
68
|
+
child.stderr?.destroy();
|
|
69
|
+
resolve({ status, error, stdout, stderr });
|
|
70
|
+
};
|
|
71
|
+
const stop = (code) => {
|
|
72
|
+
if (code && !finished)
|
|
73
|
+
error ??= { code };
|
|
74
|
+
if (finished || stopping)
|
|
75
|
+
return;
|
|
76
|
+
stopping = true;
|
|
77
|
+
if (exited || !child.pid) {
|
|
78
|
+
error ??= { code: "ECHILD" };
|
|
79
|
+
} else {
|
|
80
|
+
try {
|
|
81
|
+
child.stdin.end();
|
|
82
|
+
} catch {
|
|
83
|
+
error = { code: "ECHILD" };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
cleanup = setTimeout(() => {
|
|
87
|
+
error = { code: "ECHILD" };
|
|
88
|
+
finish();
|
|
89
|
+
}, 1000);
|
|
90
|
+
};
|
|
91
|
+
const abort = () => stop("ABORT_ERR");
|
|
92
|
+
const deadline = setTimeout(() => stop("ETIMEDOUT"), options.timeout);
|
|
93
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
94
|
+
if (options.signal?.aborted)
|
|
95
|
+
abort();
|
|
96
|
+
child.stdin?.on("error", () => {
|
|
97
|
+
if (!stopping)
|
|
98
|
+
stop("ECHILD");
|
|
99
|
+
});
|
|
100
|
+
child.stdout?.on("data", (bytes) => {
|
|
101
|
+
if (stopping)
|
|
102
|
+
return;
|
|
103
|
+
control += bytes.toString("utf8");
|
|
104
|
+
if (Buffer.byteLength(control) > maximumEnvelope)
|
|
105
|
+
return stop("ENOBUFS");
|
|
106
|
+
if (!control.endsWith(`
|
|
107
|
+
`))
|
|
108
|
+
return;
|
|
109
|
+
try {
|
|
110
|
+
const value = JSON.parse(control);
|
|
111
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["status", "error", "stdout", "stderr"].includes(key)) || !(value.status === null || Number.isInteger(value.status) && value.status >= 0 && value.status <= 255) || typeof value.stdout !== "string" || typeof value.stderr !== "string" || Buffer.byteLength(value.stdout) > options.maxBuffer || Buffer.byteLength(value.stderr) > options.maxBuffer || value.error !== undefined && (Object.keys(value.error).join() !== "code" || !["ENOBUFS", "ECHILD"].includes(value.error.code))) {
|
|
112
|
+
return stop("EPROTO");
|
|
113
|
+
}
|
|
114
|
+
status = value.status;
|
|
115
|
+
stdout = value.stdout;
|
|
116
|
+
stderr = value.stderr;
|
|
117
|
+
stop(value.error?.code);
|
|
118
|
+
} catch {
|
|
119
|
+
stop("EPROTO");
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
child.stdout?.on("error", () => stop("EPROTO"));
|
|
123
|
+
child.stderr?.on("data", () => stop("ECHILD"));
|
|
124
|
+
child.stderr?.on("error", () => stop("ECHILD"));
|
|
125
|
+
child.on("error", () => {
|
|
126
|
+
error = { code: "ECHILD" };
|
|
127
|
+
stop();
|
|
128
|
+
});
|
|
129
|
+
child.on("exit", () => {
|
|
130
|
+
exited = true;
|
|
131
|
+
if (!stopping)
|
|
132
|
+
stop("ECHILD");
|
|
133
|
+
});
|
|
134
|
+
child.on("close", () => {
|
|
135
|
+
if (!stopping)
|
|
136
|
+
error = { code: "ECHILD" };
|
|
137
|
+
finish();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/native-readiness.ts
|
|
143
|
+
var import_node_fs = require("node:fs");
|
|
144
|
+
var import_node_os = require("node:os");
|
|
145
|
+
var import_node_path = require("node:path");
|
|
146
|
+
|
|
147
|
+
class ReadinessError extends Error {
|
|
148
|
+
code;
|
|
149
|
+
constructor(code) {
|
|
150
|
+
super(code);
|
|
151
|
+
this.code = code;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function need(value, code) {
|
|
155
|
+
if (!value)
|
|
156
|
+
throw new ReadinessError(code);
|
|
157
|
+
}
|
|
158
|
+
var sha = (value) => import_node_crypto.createHash("sha256").update(value).digest("hex");
|
|
159
|
+
var digest = (v) => typeof v === "string" && /^[a-f0-9]{64}$/.test(v);
|
|
160
|
+
var absolute = (v) => typeof v === "string" && v.length <= 4096 && import_node_path.isAbsolute(v) && import_node_path.resolve(v) === v && !/[\0\r\n]/.test(v);
|
|
161
|
+
var object = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
162
|
+
var keys = (v, expected) => object(v) && Object.keys(v).sort().join() === expected.sort().join();
|
|
163
|
+
var pin = (v) => keys(v, ["path", "sha256"]) && absolute(v.path) && digest(v.sha256);
|
|
164
|
+
function isReadinessProvider(value) {
|
|
165
|
+
return value === "claude" || value === "codex" || value === "sumi";
|
|
166
|
+
}
|
|
167
|
+
function inside(home, path) {
|
|
168
|
+
const part = import_node_path.relative(home, path);
|
|
169
|
+
return part !== "" && part !== ".." && !part.startsWith("../") && !import_node_path.isAbsolute(part);
|
|
170
|
+
}
|
|
171
|
+
function parseTrashReadinessBinding(input, provider, home) {
|
|
172
|
+
const b = input;
|
|
173
|
+
need(isReadinessProvider(provider) && absolute(home), "binding_invalid");
|
|
174
|
+
need(keys(b, ["schema", "provider", "home", "runtime", "trash", "plan", "timeoutMs"]) && b.schema === "hasna.hooks.trash-readiness/v1" && b.provider === provider && b.home === home && pin(b.runtime) && pin(b.plan) && keys(b.trash, ["root", "version", "cliSha256"]) && absolute(b.trash.root) && typeof b.trash.version === "string" && b.trash.version.length <= 128 && /^\d+\.\d+\.\d+$/.test(b.trash.version) && digest(b.trash.cliSha256) && Number.isInteger(b.timeoutMs) && b.timeoutMs >= 1000 && b.timeoutMs <= 15000 && inside(home, b.plan.path) && inside(home, b.trash.root), "binding_invalid");
|
|
175
|
+
return structuredClone(b);
|
|
176
|
+
}
|
|
177
|
+
function trashReadinessBindingPath(provider, home = import_node_os.homedir()) {
|
|
178
|
+
need(isReadinessProvider(provider) && absolute(home), "binding_invalid");
|
|
179
|
+
return import_node_path.join(home, ".hasna", "hooks", "native", "trash-readiness", provider + ".json");
|
|
180
|
+
}
|
|
181
|
+
function readSafe(path, maximum, executable = false) {
|
|
182
|
+
need(absolute(path) && import_node_fs.realpathSync(path) === path, "unsafe_path");
|
|
183
|
+
for (let parent = import_node_path.dirname(path);; parent = import_node_path.dirname(parent)) {
|
|
184
|
+
const stat = import_node_fs.lstatSync(parent);
|
|
185
|
+
need(stat.isDirectory() && [0, process.getuid?.()].includes(stat.uid) && !(stat.mode & 18), "unsafe_path");
|
|
186
|
+
if (parent === import_node_path.dirname(parent))
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
const fd = import_node_fs.openSync(path, import_node_fs.constants.O_RDONLY | import_node_fs.constants.O_NOFOLLOW | import_node_fs.constants.O_NONBLOCK);
|
|
190
|
+
try {
|
|
191
|
+
const stat = import_node_fs.fstatSync(fd);
|
|
192
|
+
need(stat.isFile() && [0, process.getuid?.()].includes(stat.uid) && !(stat.mode & 18) && stat.size <= maximum && (!executable || !!(stat.mode & 64)), "unsafe_path");
|
|
193
|
+
const bytes = Buffer.alloc(stat.size + 1);
|
|
194
|
+
let length = 0;
|
|
195
|
+
while (length < bytes.length) {
|
|
196
|
+
const count = import_node_fs.readSync(fd, bytes, length, bytes.length - length, null);
|
|
197
|
+
if (!count)
|
|
198
|
+
break;
|
|
199
|
+
length += count;
|
|
200
|
+
}
|
|
201
|
+
const after = import_node_fs.fstatSync(fd);
|
|
202
|
+
need(length === stat.size && after.size === stat.size && after.mtimeMs === stat.mtimeMs && after.ctimeMs === stat.ctimeMs, "file_changed");
|
|
203
|
+
return bytes.subarray(0, length);
|
|
204
|
+
} finally {
|
|
205
|
+
import_node_fs.closeSync(fd);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
var safeFailureCodes = new Set([
|
|
209
|
+
"unsupported_platform",
|
|
210
|
+
"station_detection_failed",
|
|
211
|
+
"station_mismatch",
|
|
212
|
+
"hosted_unavailable",
|
|
213
|
+
"authentication_refused",
|
|
214
|
+
"station_setup_required",
|
|
215
|
+
"invalid_status",
|
|
216
|
+
"deadline_exceeded",
|
|
217
|
+
"retention_mismatch",
|
|
218
|
+
"managed_instructions_refused",
|
|
219
|
+
"removal_instruction_missing",
|
|
220
|
+
"instructions_changed",
|
|
221
|
+
"native_config_changed",
|
|
222
|
+
"guard_refused",
|
|
223
|
+
"owner_refused"
|
|
224
|
+
]);
|
|
225
|
+
async function runTrashReadiness(provider, d = {}) {
|
|
226
|
+
const started = performance.now();
|
|
227
|
+
const result = (code, extra = {}) => ({
|
|
228
|
+
schema: "hasna.hooks.trash-readiness-result/v1",
|
|
229
|
+
provider,
|
|
230
|
+
ready: code === "ready",
|
|
231
|
+
code,
|
|
232
|
+
nativeAdoptionVerified: false,
|
|
233
|
+
...extra
|
|
234
|
+
});
|
|
235
|
+
let phase = "binding";
|
|
236
|
+
try {
|
|
237
|
+
const home = d.home ?? import_node_fs.realpathSync(import_node_os.homedir()), read = d.read ?? readSafe;
|
|
238
|
+
need(isReadinessProvider(provider), "binding_invalid");
|
|
239
|
+
const bindingPath = trashReadinessBindingPath(provider, home), bindingBytes = read(bindingPath, 65536);
|
|
240
|
+
const b = parseTrashReadinessBinding(JSON.parse(bindingBytes.toString("utf8")), provider, home);
|
|
241
|
+
phase = "consumer";
|
|
242
|
+
const checkPin = (p, maximum, executable = false) => {
|
|
243
|
+
const bytes = read(p.path, maximum, executable);
|
|
244
|
+
need(sha(bytes) === p.sha256, "file_changed");
|
|
245
|
+
return bytes;
|
|
246
|
+
};
|
|
247
|
+
checkPin(b.runtime, 200000000, true);
|
|
248
|
+
const manifestPath = import_node_path.join(b.trash.root, "package.json"), manifestBytes = read(manifestPath, 65536);
|
|
249
|
+
const manifest = JSON.parse(manifestBytes.toString("utf8"));
|
|
250
|
+
need(manifest.name === "@hasna/trash" && manifest.version === b.trash.version && manifest.bin?.trash === "dist/cli/index.js", "package_mismatch");
|
|
251
|
+
const cli = import_node_path.join(b.trash.root, "dist", "cli", "index.js");
|
|
252
|
+
checkPin({ path: cli, sha256: b.trash.cliSha256 }, 64 * 1024 * 1024, true);
|
|
253
|
+
const plan = JSON.parse(checkPin(b.plan, 65536).toString("utf8"));
|
|
254
|
+
need(plan?.schema === "hasna.trash.station-setup-plan/v1" && plan.stage === "ready" && plan.configurationReady === true && plan.nativeAdoptionVerified === false && digest(plan.planDigest) && plan.spec?.provider === provider && plan.spec.home === home && typeof plan.spec.station === "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(plan.spec.station) && plan.spec.runtime?.path === b.runtime.path && plan.spec.runtime?.sha256 === b.runtime.sha256 && absolute(plan.spec.cwd) && (plan.spec.cwd === home || inside(home, plan.spec.cwd)) && typeof plan.spec.path === "string" && plan.spec.path.length <= 8192 && plan.spec.path.split(":").every(absolute), "plan_invalid");
|
|
255
|
+
const args = ["--config=/dev/null", "--no-env-file", "--no-macros", "--no-install", cli, "station", "check", "--plan", b.plan.path];
|
|
256
|
+
const remaining = () => Math.floor(b.timeoutMs - (performance.now() - started));
|
|
257
|
+
const timeout = remaining();
|
|
258
|
+
need(timeout > 0, "deadline_exceeded");
|
|
259
|
+
const child = await (d.spawn ?? runReadinessProcess)(b.runtime.path, args, {
|
|
260
|
+
cwd: home,
|
|
261
|
+
env: { HOME: home, PATH: plan.spec.path, LANG: "C", NO_COLOR: "1" },
|
|
262
|
+
timeout,
|
|
263
|
+
maxBuffer: 16384,
|
|
264
|
+
signal: d.signal
|
|
265
|
+
});
|
|
266
|
+
need(remaining() > 0, "deadline_exceeded");
|
|
267
|
+
if (child.error && "code" in child.error && child.error.code === "ETIMEDOUT")
|
|
268
|
+
return result("deadline_exceeded");
|
|
269
|
+
if (child.error || child.signal || child.status === null)
|
|
270
|
+
return result("execution_failed");
|
|
271
|
+
need(typeof child.stdout === "string" && typeof child.stderr === "string" && Buffer.byteLength(child.stdout) <= 16384 && Buffer.byteLength(child.stderr) <= 16384, "output_invalid");
|
|
272
|
+
let value;
|
|
273
|
+
try {
|
|
274
|
+
value = JSON.parse(child.stdout);
|
|
275
|
+
} catch {
|
|
276
|
+
return result("output_invalid");
|
|
277
|
+
}
|
|
278
|
+
if (child.status !== 0 || value?.configurationReady !== true) {
|
|
279
|
+
return result(safeFailureCodes.has(value?.code) ? value.code : "check_failed");
|
|
280
|
+
}
|
|
281
|
+
need(value.schema === "hasna.trash.station-setup-result/v1" && value.station === plan.spec.station && value.provider === provider && value.planDigest === plan.planDigest && value.serviceReady === true && value.guardVerified === true && value.managedInstructionsVerified === true && value.mcpConfigurationVerified === true && value.mcpExecutionVerified === false && value.nativeAdoptionVerified === false && child.stderr.length === 0, "output_invalid");
|
|
282
|
+
checkPin(b.plan, 65536);
|
|
283
|
+
checkPin({ path: cli, sha256: b.trash.cliSha256 }, 64 * 1024 * 1024, true);
|
|
284
|
+
checkPin(b.runtime, 200000000, true);
|
|
285
|
+
need(read(bindingPath, 65536).equals(bindingBytes) && read(manifestPath, 65536).equals(manifestBytes), "file_changed");
|
|
286
|
+
need(remaining() > 0, "deadline_exceeded");
|
|
287
|
+
return result("ready", { station: value.station, planDigest: value.planDigest });
|
|
288
|
+
} catch (error) {
|
|
289
|
+
return result(error instanceof ReadinessError ? error.code : phase === "binding" && error?.code === "ENOENT" ? "binding_missing" : "binding_invalid");
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function readinessContext(result) {
|
|
293
|
+
return result.ready ? `Trash readiness verified for ${result.station} (${result.provider}). Use Trash for recoverable file removal.` : `Trash readiness needs attention (${result.provider}: ${result.code}). Preserve files; do not fall back to direct deletion. Refresh the station setup before removal.`;
|
|
294
|
+
}
|
|
295
|
+
function readinessSessionStartOutput(result) {
|
|
296
|
+
return { hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: readinessContext(result) } };
|
|
297
|
+
}
|
|
298
|
+
async function handleReadinessSessionStart(provider, event, dependencies = {}) {
|
|
299
|
+
const input = event;
|
|
300
|
+
if (!object(input) || input.hook_event_name !== "SessionStart" || !["startup", "resume", "clear", "fork", "compact"].includes(input.source)) {
|
|
301
|
+
return readinessSessionStartOutput({ schema: "hasna.hooks.trash-readiness-result/v1", provider, ready: false, code: "input_invalid", nativeAdoptionVerified: false });
|
|
302
|
+
}
|
|
303
|
+
if (input.source === "compact")
|
|
304
|
+
return {};
|
|
305
|
+
return readinessSessionStartOutput(await runTrashReadiness(provider, dependencies));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// hooks/native-readiness-entry.ts
|
|
309
|
+
async function main() {
|
|
310
|
+
const provider = process.argv[2];
|
|
311
|
+
if (!isReadinessProvider(provider)) {
|
|
312
|
+
process.stderr.write(`Trash readiness needs attention (unsupported provider).
|
|
313
|
+
`);
|
|
314
|
+
process.exitCode = 1;
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const invalid = () => readinessSessionStartOutput({
|
|
318
|
+
schema: "hasna.hooks.trash-readiness-result/v1",
|
|
319
|
+
provider,
|
|
320
|
+
ready: false,
|
|
321
|
+
code: "input_invalid",
|
|
322
|
+
nativeAdoptionVerified: false
|
|
323
|
+
});
|
|
324
|
+
try {
|
|
325
|
+
const input = await new Promise((accept, reject) => {
|
|
326
|
+
const chunks = [];
|
|
327
|
+
let size = 0;
|
|
328
|
+
const finish = (error) => {
|
|
329
|
+
clearTimeout(timer);
|
|
330
|
+
process.stdin.pause();
|
|
331
|
+
process.stdin.removeAllListeners("data");
|
|
332
|
+
process.stdin.removeAllListeners("end");
|
|
333
|
+
process.stdin.removeAllListeners("error");
|
|
334
|
+
error ? reject(error) : accept(Buffer.concat(chunks));
|
|
335
|
+
};
|
|
336
|
+
const timer = setTimeout(() => finish(new Error), 1000);
|
|
337
|
+
process.stdin.on("data", (chunk) => {
|
|
338
|
+
const bytes = Buffer.from(chunk);
|
|
339
|
+
size += bytes.length;
|
|
340
|
+
if (size > 65536)
|
|
341
|
+
finish(new Error);
|
|
342
|
+
else
|
|
343
|
+
chunks.push(bytes);
|
|
344
|
+
});
|
|
345
|
+
process.stdin.once("end", () => finish());
|
|
346
|
+
process.stdin.once("error", () => finish(new Error));
|
|
347
|
+
});
|
|
348
|
+
process.stdout.write(JSON.stringify(await handleReadinessSessionStart(provider, JSON.parse(input.toString("utf8")))) + `
|
|
349
|
+
`);
|
|
350
|
+
} catch {
|
|
351
|
+
process.stdout.write(JSON.stringify(invalid()) + `
|
|
352
|
+
`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
main();
|