@lakitu/sdk 0.1.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 +166 -0
- package/convex/_generated/api.d.ts +45 -0
- package/convex/_generated/api.js +23 -0
- package/convex/_generated/dataModel.d.ts +58 -0
- package/convex/_generated/server.d.ts +143 -0
- package/convex/_generated/server.js +93 -0
- package/convex/cloud/CLAUDE.md +238 -0
- package/convex/cloud/_generated/api.ts +84 -0
- package/convex/cloud/_generated/component.ts +861 -0
- package/convex/cloud/_generated/dataModel.ts +60 -0
- package/convex/cloud/_generated/server.ts +156 -0
- package/convex/cloud/convex.config.ts +16 -0
- package/convex/cloud/index.ts +29 -0
- package/convex/cloud/intentSchema/generate.ts +447 -0
- package/convex/cloud/intentSchema/index.ts +16 -0
- package/convex/cloud/intentSchema/types.ts +418 -0
- package/convex/cloud/ksaPolicy.ts +554 -0
- package/convex/cloud/mail.ts +92 -0
- package/convex/cloud/schema.ts +322 -0
- package/convex/cloud/utils/kanbanContext.ts +229 -0
- package/convex/cloud/workflows/agentBoard.ts +451 -0
- package/convex/cloud/workflows/agentPrompt.ts +272 -0
- package/convex/cloud/workflows/agentThread.ts +374 -0
- package/convex/cloud/workflows/compileSandbox.ts +146 -0
- package/convex/cloud/workflows/crudBoard.ts +217 -0
- package/convex/cloud/workflows/crudKSAs.ts +262 -0
- package/convex/cloud/workflows/crudLorobeads.ts +371 -0
- package/convex/cloud/workflows/crudSkills.ts +205 -0
- package/convex/cloud/workflows/crudThreads.ts +708 -0
- package/convex/cloud/workflows/lifecycleSandbox.ts +1396 -0
- package/convex/cloud/workflows/sandboxConvex.ts +1046 -0
- package/convex/sandbox/README.md +90 -0
- package/convex/sandbox/_generated/api.d.ts +2934 -0
- package/convex/sandbox/_generated/api.js +23 -0
- package/convex/sandbox/_generated/dataModel.d.ts +60 -0
- package/convex/sandbox/_generated/server.d.ts +143 -0
- package/convex/sandbox/_generated/server.js +93 -0
- package/convex/sandbox/actions/bash.ts +130 -0
- package/convex/sandbox/actions/browser.ts +282 -0
- package/convex/sandbox/actions/file.ts +336 -0
- package/convex/sandbox/actions/lsp.ts +325 -0
- package/convex/sandbox/actions/pdf.ts +119 -0
- package/convex/sandbox/agent/codeExecLoop.ts +535 -0
- package/convex/sandbox/agent/decisions.ts +284 -0
- package/convex/sandbox/agent/index.ts +515 -0
- package/convex/sandbox/agent/subagents.ts +651 -0
- package/convex/sandbox/brandResearch/index.ts +417 -0
- package/convex/sandbox/context/index.ts +7 -0
- package/convex/sandbox/context/session.ts +402 -0
- package/convex/sandbox/convex.config.ts +17 -0
- package/convex/sandbox/index.ts +51 -0
- package/convex/sandbox/nodeActions/codeExec.ts +130 -0
- package/convex/sandbox/planning/beads.ts +187 -0
- package/convex/sandbox/planning/index.ts +8 -0
- package/convex/sandbox/planning/sync.ts +194 -0
- package/convex/sandbox/prompts/codeExec.ts +852 -0
- package/convex/sandbox/prompts/modes.ts +231 -0
- package/convex/sandbox/prompts/system.ts +142 -0
- package/convex/sandbox/schema.ts +510 -0
- package/convex/sandbox/state/artifacts.ts +99 -0
- package/convex/sandbox/state/checkpoints.ts +341 -0
- package/convex/sandbox/state/files.ts +383 -0
- package/convex/sandbox/state/index.ts +10 -0
- package/convex/sandbox/state/verification.actions.ts +268 -0
- package/convex/sandbox/state/verification.ts +101 -0
- package/convex/sandbox/tsconfig.json +25 -0
- package/convex/sandbox/utils/codeExecHelpers.ts +52 -0
- package/dist/cli/commands/build.d.ts +19 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +223 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +148 -0
- package/dist/cli/commands/publish.d.ts +12 -0
- package/dist/cli/commands/publish.d.ts.map +1 -0
- package/dist/cli/commands/publish.js +33 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +40 -0
- package/dist/sdk/builders.d.ts +104 -0
- package/dist/sdk/builders.d.ts.map +1 -0
- package/dist/sdk/builders.js +214 -0
- package/dist/sdk/index.d.ts +29 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +38 -0
- package/dist/sdk/types.d.ts +107 -0
- package/dist/sdk/types.d.ts.map +1 -0
- package/dist/sdk/types.js +6 -0
- package/ksa/README.md +263 -0
- package/ksa/_generated/REFERENCE.md +2954 -0
- package/ksa/_generated/registry.ts +257 -0
- package/ksa/_shared/configReader.ts +302 -0
- package/ksa/_shared/configSchemas.ts +649 -0
- package/ksa/_shared/gateway.ts +175 -0
- package/ksa/_shared/ksaBehaviors.ts +411 -0
- package/ksa/_shared/ksaProxy.ts +248 -0
- package/ksa/_shared/localDb.ts +302 -0
- package/ksa/index.ts +134 -0
- package/package.json +93 -0
- package/runtime/browser/agent-browser.ts +330 -0
- package/runtime/entrypoint.ts +194 -0
- package/runtime/lsp/manager.ts +366 -0
- package/runtime/pdf/pdf-generator.ts +50 -0
- package/runtime/pdf/renderer.ts +357 -0
- package/runtime/pdf/schema.ts +97 -0
- package/runtime/services/file-watcher.ts +191 -0
- package/template/build.ts +307 -0
- package/template/e2b/Dockerfile +69 -0
- package/template/e2b/e2b.toml +13 -0
- package/template/e2b/prebuild.sh +68 -0
- package/template/e2b/start.sh +14 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LSP Manager - Language Server Lifecycle Management
|
|
3
|
+
*
|
|
4
|
+
* Start, stop, and communicate with language servers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawn, type ChildProcess } from "child_process";
|
|
8
|
+
import { EventEmitter } from "events";
|
|
9
|
+
|
|
10
|
+
export type Language = "typescript" | "python" | "rust";
|
|
11
|
+
|
|
12
|
+
export interface LspServer {
|
|
13
|
+
language: Language;
|
|
14
|
+
process: ChildProcess | null;
|
|
15
|
+
status: "stopped" | "starting" | "running" | "error";
|
|
16
|
+
error?: string;
|
|
17
|
+
pendingRequests: Map<number, { resolve: Function; reject: Function }>;
|
|
18
|
+
nextRequestId: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface LspRequest {
|
|
22
|
+
jsonrpc: "2.0";
|
|
23
|
+
id: number;
|
|
24
|
+
method: string;
|
|
25
|
+
params?: any;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface LspResponse {
|
|
29
|
+
jsonrpc: "2.0";
|
|
30
|
+
id: number;
|
|
31
|
+
result?: any;
|
|
32
|
+
error?: { code: number; message: string };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface LspNotification {
|
|
36
|
+
jsonrpc: "2.0";
|
|
37
|
+
method: string;
|
|
38
|
+
params?: any;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const LSP_COMMANDS: Record<Language, { cmd: string; args: string[] }> = {
|
|
42
|
+
typescript: {
|
|
43
|
+
cmd: "typescript-language-server",
|
|
44
|
+
args: ["--stdio"],
|
|
45
|
+
},
|
|
46
|
+
python: {
|
|
47
|
+
cmd: "pylsp",
|
|
48
|
+
args: [],
|
|
49
|
+
},
|
|
50
|
+
rust: {
|
|
51
|
+
cmd: "rust-analyzer",
|
|
52
|
+
args: [],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
class LspManager extends EventEmitter {
|
|
57
|
+
private servers: Map<Language, LspServer> = new Map();
|
|
58
|
+
private messageBuffer: Map<Language, string> = new Map();
|
|
59
|
+
|
|
60
|
+
async start(language: Language): Promise<void> {
|
|
61
|
+
const existing = this.servers.get(language);
|
|
62
|
+
if (existing?.status === "running") {
|
|
63
|
+
return; // Already running
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const config = LSP_COMMANDS[language];
|
|
67
|
+
if (!config) {
|
|
68
|
+
throw new Error(`Unknown language: ${language}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const server: LspServer = {
|
|
72
|
+
language,
|
|
73
|
+
process: null,
|
|
74
|
+
status: "starting",
|
|
75
|
+
pendingRequests: new Map(),
|
|
76
|
+
nextRequestId: 1,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
this.servers.set(language, server);
|
|
80
|
+
this.messageBuffer.set(language, "");
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const proc = spawn(config.cmd, config.args, {
|
|
84
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
85
|
+
env: {
|
|
86
|
+
...process.env,
|
|
87
|
+
HOME: "/home/user",
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
server.process = proc;
|
|
92
|
+
|
|
93
|
+
proc.stdout?.on("data", (data) => {
|
|
94
|
+
this.handleOutput(language, data.toString());
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
proc.stderr?.on("data", (data) => {
|
|
98
|
+
console.error(`[${language}-lsp]`, data.toString().trim());
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
proc.on("error", (err) => {
|
|
102
|
+
server.status = "error";
|
|
103
|
+
server.error = err.message;
|
|
104
|
+
this.emit("error", { language, error: err.message });
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
proc.on("exit", (code) => {
|
|
108
|
+
server.status = "stopped";
|
|
109
|
+
this.emit("exit", { language, code });
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Initialize the server
|
|
113
|
+
await this.initialize(language);
|
|
114
|
+
|
|
115
|
+
server.status = "running";
|
|
116
|
+
this.emit("ready", { language });
|
|
117
|
+
} catch (error: any) {
|
|
118
|
+
server.status = "error";
|
|
119
|
+
server.error = error.message;
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async stop(language: Language): Promise<void> {
|
|
125
|
+
const server = this.servers.get(language);
|
|
126
|
+
if (!server?.process) return;
|
|
127
|
+
|
|
128
|
+
// Send shutdown request
|
|
129
|
+
try {
|
|
130
|
+
await this.sendRequest(language, "shutdown", {});
|
|
131
|
+
this.sendNotification(language, "exit", {});
|
|
132
|
+
} catch {
|
|
133
|
+
// Force kill if graceful shutdown fails
|
|
134
|
+
server.process.kill("SIGTERM");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
server.status = "stopped";
|
|
138
|
+
server.process = null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async stopAll(): Promise<void> {
|
|
142
|
+
for (const language of this.servers.keys()) {
|
|
143
|
+
await this.stop(language);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
getStatus(language: Language): LspServer["status"] {
|
|
148
|
+
return this.servers.get(language)?.status ?? "stopped";
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private async initialize(language: Language): Promise<void> {
|
|
152
|
+
await this.sendRequest(language, "initialize", {
|
|
153
|
+
processId: process.pid,
|
|
154
|
+
rootUri: "file:///home/user/workspace",
|
|
155
|
+
capabilities: {
|
|
156
|
+
textDocument: {
|
|
157
|
+
synchronization: {
|
|
158
|
+
didOpen: true,
|
|
159
|
+
didChange: true,
|
|
160
|
+
didClose: true,
|
|
161
|
+
},
|
|
162
|
+
completion: {
|
|
163
|
+
completionItem: {
|
|
164
|
+
snippetSupport: true,
|
|
165
|
+
documentationFormat: ["markdown", "plaintext"],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
hover: {
|
|
169
|
+
contentFormat: ["markdown", "plaintext"],
|
|
170
|
+
},
|
|
171
|
+
publishDiagnostics: {
|
|
172
|
+
relatedInformation: true,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
workspace: {
|
|
176
|
+
workspaceFolders: true,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
workspaceFolders: [
|
|
180
|
+
{
|
|
181
|
+
uri: "file:///home/user/workspace",
|
|
182
|
+
name: "workspace",
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
this.sendNotification(language, "initialized", {});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async sendRequest(language: Language, method: string, params: any): Promise<any> {
|
|
191
|
+
const server = this.servers.get(language);
|
|
192
|
+
if (!server?.process || server.status !== "running") {
|
|
193
|
+
throw new Error(`LSP server for ${language} is not running`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const id = server.nextRequestId++;
|
|
197
|
+
const request: LspRequest = {
|
|
198
|
+
jsonrpc: "2.0",
|
|
199
|
+
id,
|
|
200
|
+
method,
|
|
201
|
+
params,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
return new Promise((resolve, reject) => {
|
|
205
|
+
server.pendingRequests.set(id, { resolve, reject });
|
|
206
|
+
this.send(language, request);
|
|
207
|
+
|
|
208
|
+
// Timeout after 30 seconds
|
|
209
|
+
setTimeout(() => {
|
|
210
|
+
if (server.pendingRequests.has(id)) {
|
|
211
|
+
server.pendingRequests.delete(id);
|
|
212
|
+
reject(new Error(`Request ${method} timed out`));
|
|
213
|
+
}
|
|
214
|
+
}, 30000);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
sendNotification(language: Language, method: string, params: any): void {
|
|
219
|
+
const notification: LspNotification = {
|
|
220
|
+
jsonrpc: "2.0",
|
|
221
|
+
method,
|
|
222
|
+
params,
|
|
223
|
+
};
|
|
224
|
+
this.send(language, notification);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private send(language: Language, message: object): void {
|
|
228
|
+
const server = this.servers.get(language);
|
|
229
|
+
if (!server?.process?.stdin) return;
|
|
230
|
+
|
|
231
|
+
const content = JSON.stringify(message);
|
|
232
|
+
const header = `Content-Length: ${Buffer.byteLength(content)}\r\n\r\n`;
|
|
233
|
+
server.process.stdin.write(header + content);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private handleOutput(language: Language, data: string): void {
|
|
237
|
+
const buffer = (this.messageBuffer.get(language) || "") + data;
|
|
238
|
+
this.messageBuffer.set(language, buffer);
|
|
239
|
+
|
|
240
|
+
// Parse LSP messages
|
|
241
|
+
while (true) {
|
|
242
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
243
|
+
if (headerEnd === -1) break;
|
|
244
|
+
|
|
245
|
+
const header = buffer.slice(0, headerEnd);
|
|
246
|
+
const contentLengthMatch = header.match(/Content-Length: (\d+)/);
|
|
247
|
+
if (!contentLengthMatch) break;
|
|
248
|
+
|
|
249
|
+
const contentLength = parseInt(contentLengthMatch[1], 10);
|
|
250
|
+
const messageStart = headerEnd + 4;
|
|
251
|
+
const messageEnd = messageStart + contentLength;
|
|
252
|
+
|
|
253
|
+
if (buffer.length < messageEnd) break;
|
|
254
|
+
|
|
255
|
+
const content = buffer.slice(messageStart, messageEnd);
|
|
256
|
+
this.messageBuffer.set(language, buffer.slice(messageEnd));
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const message = JSON.parse(content);
|
|
260
|
+
this.handleMessage(language, message);
|
|
261
|
+
} catch (e) {
|
|
262
|
+
console.error(`[${language}-lsp] Failed to parse message:`, e);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private handleMessage(language: Language, message: LspResponse | LspNotification): void {
|
|
268
|
+
const server = this.servers.get(language);
|
|
269
|
+
if (!server) return;
|
|
270
|
+
|
|
271
|
+
if ("id" in message && message.id !== undefined) {
|
|
272
|
+
// Response to a request
|
|
273
|
+
const pending = server.pendingRequests.get(message.id);
|
|
274
|
+
if (pending) {
|
|
275
|
+
server.pendingRequests.delete(message.id);
|
|
276
|
+
if (message.error) {
|
|
277
|
+
pending.reject(new Error(message.error.message));
|
|
278
|
+
} else {
|
|
279
|
+
pending.resolve(message.result);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
// Notification from server
|
|
284
|
+
this.emit("notification", { language, method: message.method, params: message.params });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// High-level API methods
|
|
289
|
+
|
|
290
|
+
async openDocument(language: Language, uri: string, text: string): Promise<void> {
|
|
291
|
+
const languageId = language === "typescript" ? "typescript" : language;
|
|
292
|
+
this.sendNotification(language, "textDocument/didOpen", {
|
|
293
|
+
textDocument: {
|
|
294
|
+
uri,
|
|
295
|
+
languageId,
|
|
296
|
+
version: 1,
|
|
297
|
+
text,
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async closeDocument(language: Language, uri: string): Promise<void> {
|
|
303
|
+
this.sendNotification(language, "textDocument/didClose", {
|
|
304
|
+
textDocument: { uri },
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async getCompletions(
|
|
309
|
+
language: Language,
|
|
310
|
+
uri: string,
|
|
311
|
+
line: number,
|
|
312
|
+
character: number
|
|
313
|
+
): Promise<any> {
|
|
314
|
+
return await this.sendRequest(language, "textDocument/completion", {
|
|
315
|
+
textDocument: { uri },
|
|
316
|
+
position: { line, character },
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async getHover(
|
|
321
|
+
language: Language,
|
|
322
|
+
uri: string,
|
|
323
|
+
line: number,
|
|
324
|
+
character: number
|
|
325
|
+
): Promise<any> {
|
|
326
|
+
return await this.sendRequest(language, "textDocument/hover", {
|
|
327
|
+
textDocument: { uri },
|
|
328
|
+
position: { line, character },
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async getDiagnostics(language: Language, uri: string): Promise<any[]> {
|
|
333
|
+
// Diagnostics are pushed via notifications, we need to trigger them
|
|
334
|
+
// by changing the document or waiting for them
|
|
335
|
+
// For now, return empty array - real implementation would track notifications
|
|
336
|
+
return [];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async getDefinition(
|
|
340
|
+
language: Language,
|
|
341
|
+
uri: string,
|
|
342
|
+
line: number,
|
|
343
|
+
character: number
|
|
344
|
+
): Promise<any> {
|
|
345
|
+
return await this.sendRequest(language, "textDocument/definition", {
|
|
346
|
+
textDocument: { uri },
|
|
347
|
+
position: { line, character },
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async getReferences(
|
|
352
|
+
language: Language,
|
|
353
|
+
uri: string,
|
|
354
|
+
line: number,
|
|
355
|
+
character: number
|
|
356
|
+
): Promise<any> {
|
|
357
|
+
return await this.sendRequest(language, "textDocument/references", {
|
|
358
|
+
textDocument: { uri },
|
|
359
|
+
position: { line, character },
|
|
360
|
+
context: { includeDeclaration: true },
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Singleton instance
|
|
366
|
+
export const lspManager = new LspManager();
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* PDF Generator - Thin CLI that delegates to services/pdf/renderer
|
|
4
|
+
*
|
|
5
|
+
* Usage: bun run pdf-generator.ts <output-path> [title]
|
|
6
|
+
* Content is read from stdin
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as fs from "fs";
|
|
10
|
+
import { markdownToDocNode, renderPdf } from "../services/pdf/renderer";
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
if (args.length < 1) {
|
|
15
|
+
console.error(JSON.stringify({ success: false, error: "Usage: bun run pdf-generator.ts <output-path> [title]" }));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const outputPath = args[0];
|
|
20
|
+
const title = args[1];
|
|
21
|
+
|
|
22
|
+
// Read content from stdin
|
|
23
|
+
let content = "";
|
|
24
|
+
for await (const chunk of Bun.stdin.stream()) {
|
|
25
|
+
content += new TextDecoder().decode(chunk);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!content.trim()) {
|
|
29
|
+
console.error(JSON.stringify({ success: false, error: "No content provided via stdin" }));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Ensure output directory exists
|
|
34
|
+
const outputDir = outputPath.substring(0, outputPath.lastIndexOf("/"));
|
|
35
|
+
if (outputDir && !fs.existsSync(outputDir)) {
|
|
36
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Convert markdown to document structure and render PDF
|
|
40
|
+
const docNode = markdownToDocNode(content, title);
|
|
41
|
+
await renderPdf(docNode, { outputPath });
|
|
42
|
+
|
|
43
|
+
const stats = fs.statSync(outputPath);
|
|
44
|
+
console.log(JSON.stringify({ success: true, path: outputPath, size: stats.size }));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
main().catch((err) => {
|
|
48
|
+
console.error(JSON.stringify({ success: false, error: err.message }));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
});
|