@tokscale/cli 1.0.6 → 1.0.7

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.
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Native Runner - Subprocess for non-blocking native Rust calls
4
+ *
5
+ * This script runs in a separate process to keep the main event loop free
6
+ * for UI rendering (e.g., spinner animation).
7
+ *
8
+ * Communication: stdin (JSON input) -> stdout (JSON output)
9
+ * No temp files needed - pure Unix IPC pattern.
10
+ */
11
+
12
+ import nativeCore from "@tokscale/core";
13
+
14
+ const MAX_INPUT_SIZE = 50 * 1024 * 1024; // 50MB
15
+ const STDIN_TIMEOUT_MS = 30_000; // 30s
16
+
17
+ interface NativeRunnerRequest {
18
+ method: string;
19
+ args: unknown[];
20
+ }
21
+
22
+ async function readStdinWithLimits(): Promise<string> {
23
+ const chunks: Buffer[] = [];
24
+ let totalSize = 0;
25
+ let timeoutId: ReturnType<typeof setTimeout> | null = null;
26
+
27
+ const timeoutPromise = new Promise<never>((_, reject) => {
28
+ timeoutId = setTimeout(() => reject(new Error(`Stdin read timed out after ${STDIN_TIMEOUT_MS}ms`)), STDIN_TIMEOUT_MS);
29
+ });
30
+
31
+ const readPromise = (async () => {
32
+ for await (const chunk of process.stdin) {
33
+ const buf = Buffer.from(chunk as ArrayBuffer);
34
+ totalSize += buf.length;
35
+ if (totalSize > MAX_INPUT_SIZE) {
36
+ throw new Error(`Input exceeds maximum size of ${MAX_INPUT_SIZE} bytes (${Math.round(MAX_INPUT_SIZE / 1024 / 1024)}MB)`);
37
+ }
38
+ chunks.push(buf);
39
+ }
40
+ return Buffer.concat(chunks).toString("utf-8");
41
+ })();
42
+
43
+ try {
44
+ const result = await Promise.race([readPromise, timeoutPromise]);
45
+ return result;
46
+ } finally {
47
+ if (timeoutId) clearTimeout(timeoutId);
48
+ }
49
+ }
50
+
51
+ async function main() {
52
+ const input = await readStdinWithLimits();
53
+
54
+ if (!input.trim()) {
55
+ process.stderr.write(JSON.stringify({ error: "No input received" }));
56
+ process.exit(1);
57
+ }
58
+
59
+ let request: NativeRunnerRequest;
60
+ try {
61
+ request = JSON.parse(input) as NativeRunnerRequest;
62
+ } catch (e) {
63
+ throw new Error(`Malformed JSON input: ${(e as Error).message}`);
64
+ }
65
+
66
+ const { method, args } = request;
67
+
68
+ if (!Array.isArray(args) || args.length === 0) {
69
+ throw new Error(`Invalid args for method '${method}': expected at least 1 argument`);
70
+ }
71
+
72
+ let result: unknown;
73
+
74
+ switch (method) {
75
+ case "parseLocalSources":
76
+ result = nativeCore.parseLocalSources(args[0] as Parameters<typeof nativeCore.parseLocalSources>[0]);
77
+ break;
78
+ case "finalizeReport":
79
+ result = nativeCore.finalizeReport(args[0] as Parameters<typeof nativeCore.finalizeReport>[0]);
80
+ break;
81
+ case "finalizeMonthlyReport":
82
+ result = nativeCore.finalizeMonthlyReport(args[0] as Parameters<typeof nativeCore.finalizeMonthlyReport>[0]);
83
+ break;
84
+ case "finalizeGraph":
85
+ result = nativeCore.finalizeGraph(args[0] as Parameters<typeof nativeCore.finalizeGraph>[0]);
86
+ break;
87
+ case "generateGraphWithPricing":
88
+ result = nativeCore.generateGraphWithPricing(args[0] as Parameters<typeof nativeCore.generateGraphWithPricing>[0]);
89
+ break;
90
+ default:
91
+ throw new Error(`Unknown method: ${method}`);
92
+ }
93
+
94
+ // Write result to stdout (no newline - pure JSON)
95
+ process.stdout.write(JSON.stringify(result));
96
+ }
97
+
98
+ main().catch((e) => {
99
+ const error = e as Error;
100
+ process.stderr.write(JSON.stringify({
101
+ error: error.message,
102
+ stack: error.stack,
103
+ }));
104
+ process.exit(1);
105
+ });