@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.
Files changed (2) hide show
  1. package/dist/init.js +25 -3
  2. 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.audio, {
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).send(Buffer.from(result.body.audio));
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).send(Buffer.from(result.body.audio));
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.6",
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" },