@cairnvibe/indexer 0.2.6 → 0.2.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.
- package/dist/init.js +25 -3
- package/package.json +1 -1
package/dist/init.js
CHANGED
|
@@ -35,6 +35,20 @@ function writeIfAbsent(filePath, content, result) {
|
|
|
35
35
|
node_fs_1.default.writeFileSync(filePath, content);
|
|
36
36
|
result.filesWritten.push(filePath);
|
|
37
37
|
}
|
|
38
|
+
// Found live: if your Next.js config transpiles @cairnvibe/sdk (needed when
|
|
39
|
+
// importing the client widget straight from source rather than a compiled
|
|
40
|
+
// dist), webpack bundling the `ws` package used by the speak/transcribe/
|
|
41
|
+
// realtime routes silently breaks its Deepgram WebSocket connections — every
|
|
42
|
+
// request hung for ~10s then failed with ECONNRESET, even though the exact
|
|
43
|
+
// same code worked outside Next's dev bundler. Marking `ws` as a server
|
|
44
|
+
// external package (next.config.js) fixes it.
|
|
45
|
+
const WS_TRANSPILE_WARNING = [
|
|
46
|
+
" If your next.config.js sets transpilePackages for @cairnvibe/sdk, also",
|
|
47
|
+
' add "ws" to serverExternalPackages (Next 15) or',
|
|
48
|
+
' experimental.serverComponentsExternalPackages (Next 14) — webpack-',
|
|
49
|
+
" bundling ws otherwise silently breaks the Deepgram speak/transcribe",
|
|
50
|
+
" connections it uses.",
|
|
51
|
+
];
|
|
38
52
|
function runInit(dir, options = {}) {
|
|
39
53
|
const absDir = node_path_1.default.resolve(dir);
|
|
40
54
|
const pkgPath = node_path_1.default.join(absDir, "package.json");
|
|
@@ -66,6 +80,8 @@ function runInit(dir, options = {}) {
|
|
|
66
80
|
widgetProps.push('speakEndpoint="/api/copilot/speak"', 'transcribeEndpoint="/api/copilot/transcribe"');
|
|
67
81
|
}
|
|
68
82
|
result.nextSteps.push("1. cp .env.example .env and fill in your key(s).", "2. Add the widget to app/layout.tsx:", ' import { Copilot } from "@cairnvibe/sdk";', ` <Copilot ${widgetProps.join(" ")} />`, "3. npx cairn build . (scans this Next.js app's source)", "4. npm run dev, then ask it a question.");
|
|
83
|
+
if (options.voice)
|
|
84
|
+
result.nextSteps.push(...WS_TRANSPILE_WARNING);
|
|
69
85
|
}
|
|
70
86
|
else if (result.framework === "next-pages-router") {
|
|
71
87
|
writeIfAbsent(node_path_1.default.join(absDir, "pages", "api", "copilot.ts"), NEXT_PAGES_API_ROUTE, result);
|
|
@@ -76,6 +92,8 @@ function runInit(dir, options = {}) {
|
|
|
76
92
|
widgetProps.push('speakEndpoint="/api/copilot/speak"', 'transcribeEndpoint="/api/copilot/transcribe"');
|
|
77
93
|
}
|
|
78
94
|
result.nextSteps.push("1. cp .env.example .env and fill in your key(s).", "2. Add the widget to pages/_app.tsx:", ' import { Copilot } from "@cairnvibe/sdk";', ` <Copilot ${widgetProps.join(" ")} />`, "3. npx cairn build . (scans this Next.js app's source)", "4. npm run dev, then ask it a question.");
|
|
95
|
+
if (options.voice)
|
|
96
|
+
result.nextSteps.push(...WS_TRANSPILE_WARNING);
|
|
79
97
|
}
|
|
80
98
|
else {
|
|
81
99
|
writeIfAbsent(node_path_1.default.join(absDir, "cairn-server.cjs"), STANDALONE_SERVER, result);
|
|
@@ -151,7 +169,7 @@ export async function POST(request: Request) {
|
|
|
151
169
|
if ("error" in result.body) {
|
|
152
170
|
return Response.json(result.body, { status: result.status });
|
|
153
171
|
}
|
|
154
|
-
return new Response(result.body.
|
|
172
|
+
return new Response(result.body.stream, {
|
|
155
173
|
status: result.status,
|
|
156
174
|
headers: { "content-type": result.body.contentType },
|
|
157
175
|
});
|
|
@@ -170,6 +188,7 @@ export async function POST(request: Request) {
|
|
|
170
188
|
}
|
|
171
189
|
`;
|
|
172
190
|
const NEXT_PAGES_SPEAK_ROUTE = `import type { NextApiRequest, NextApiResponse } from "next";
|
|
191
|
+
import { Readable } from "node:stream";
|
|
173
192
|
import { createSpeakHandler } from "@cairnvibe/sdk/speak-server";
|
|
174
193
|
|
|
175
194
|
const handler = createSpeakHandler({ apiKey: process.env.DEEPGRAM_API_KEY ?? "" });
|
|
@@ -180,7 +199,8 @@ export default async function speak(req: NextApiRequest, res: NextApiResponse) {
|
|
|
180
199
|
if ("error" in result.body) {
|
|
181
200
|
return res.status(result.status).json(result.body);
|
|
182
201
|
}
|
|
183
|
-
res.status(result.status).setHeader("content-type", result.body.contentType)
|
|
202
|
+
res.status(result.status).setHeader("content-type", result.body.contentType);
|
|
203
|
+
Readable.fromWeb(result.body.stream).pipe(res);
|
|
184
204
|
}
|
|
185
205
|
`;
|
|
186
206
|
const NEXT_PAGES_TRANSCRIBE_ROUTE = `import type { NextApiRequest, NextApiResponse } from "next";
|
|
@@ -241,11 +261,13 @@ app.post("/api/copilot", async (req, res) => {
|
|
|
241
261
|
|
|
242
262
|
if (process.env.DEEPGRAM_API_KEY) {
|
|
243
263
|
const { createSpeakHandler } = require("@cairnvibe/sdk/speak-server");
|
|
264
|
+
const { Readable } = require("node:stream");
|
|
244
265
|
const speak = createSpeakHandler({ apiKey: process.env.DEEPGRAM_API_KEY });
|
|
245
266
|
app.post("/api/copilot/speak", async (req, res) => {
|
|
246
267
|
const result = await speak(req.body?.text ?? "");
|
|
247
268
|
if ("error" in result.body) return res.status(result.status).json(result.body);
|
|
248
|
-
res.status(result.status).type(result.body.contentType)
|
|
269
|
+
res.status(result.status).type(result.body.contentType);
|
|
270
|
+
Readable.fromWeb(result.body.stream).pipe(res);
|
|
249
271
|
});
|
|
250
272
|
}
|
|
251
273
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cairnvibe/indexer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Cairn's analyzer and installer (the `cairn` CLI) — scans Next.js source or crawls any running app, and scaffolds the backend either way.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": { "access": "public" },
|