@tensorkit/raydr 0.0.0-stage → 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.
@@ -0,0 +1,217 @@
1
+ // ../../apps/server/dist/config.js
2
+ import { randomBytes } from "node:crypto";
3
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { dirname, join } from "node:path";
6
+ var DEFAULT_VOICE_ID = "21m00Tcm4TlvDq8ikWAM";
7
+ var SPEECH_PROVIDERS = ["elevenlabs", "openai", "kokoro"];
8
+ var TRANSCRIPTION_PROVIDERS = ["elevenlabs", "openai", "whisper"];
9
+ var DEFAULT_SPEECH_INSTRUCTIONS = "Speak like a calm colleague reading a status update aloud; unhurried, warm, no announcer energy";
10
+ var DEFAULT_LOCAL_PORT = 47392;
11
+ function oneOf(name, value, allowed, fallback) {
12
+ if (!value)
13
+ return fallback;
14
+ if (allowed.includes(value))
15
+ return value;
16
+ throw new Error(`${name} must be one of ${allowed.join(", ")} (got "${value}")`);
17
+ }
18
+ var warned = /* @__PURE__ */ new Set();
19
+ function envVar(name) {
20
+ const value = process.env[name];
21
+ if (value !== void 0 && value !== "")
22
+ return value;
23
+ const legacy = name.replace(/^RAYDR_/, "RAYDAR_");
24
+ const old = process.env[legacy];
25
+ if (legacy !== name && old !== void 0 && old !== "") {
26
+ deprecate(legacy, name);
27
+ return old;
28
+ }
29
+ return void 0;
30
+ }
31
+ function deprecate(legacy, name) {
32
+ if (warned.has(legacy))
33
+ return;
34
+ warned.add(legacy);
35
+ console.error(`${legacy} is deprecated; rename it to ${name} (still honoured this release)`);
36
+ }
37
+ function migrateDir(oldDir, newDir) {
38
+ if (!existsSync(newDir) && existsSync(oldDir)) {
39
+ try {
40
+ mkdirSync(dirname(newDir), { recursive: true });
41
+ renameSync(oldDir, newDir);
42
+ console.error(`moved ${oldDir} to ${newDir} (raydar is now raydr)`);
43
+ } catch (err) {
44
+ console.error(`could not move ${oldDir} to ${newDir}: ${err.message}`);
45
+ }
46
+ }
47
+ return newDir;
48
+ }
49
+ function xdg(kind) {
50
+ const fallback = {
51
+ XDG_CONFIG_HOME: join(homedir(), ".config"),
52
+ XDG_STATE_HOME: join(homedir(), ".local", "state"),
53
+ XDG_CACHE_HOME: join(homedir(), ".cache"),
54
+ XDG_DATA_HOME: join(homedir(), ".local", "share")
55
+ }[kind];
56
+ return process.env[kind] ?? fallback;
57
+ }
58
+ function migrateEnvFile(envFile) {
59
+ let text;
60
+ try {
61
+ text = readFileSync(envFile, "utf8");
62
+ } catch {
63
+ return 0;
64
+ }
65
+ const present = new Set(text.split("\n").map((l) => l.trim().split("=")[0] ?? "").filter((k) => k.startsWith("RAYDR_")));
66
+ let renamed = 0;
67
+ const out = text.split("\n").map((line) => {
68
+ const m = /^(\s*)RAYDAR_([A-Z0-9_]+)(\s*=.*)$/.exec(line);
69
+ if (!m)
70
+ return line;
71
+ const key = `RAYDR_${m[2]}`;
72
+ if (present.has(key)) {
73
+ renamed++;
74
+ return null;
75
+ }
76
+ renamed++;
77
+ return `${m[1]}${key}${m[3]}`;
78
+ }).filter((l) => l !== null).join("\n");
79
+ if (renamed > 0) {
80
+ const tmp = `${envFile}.tmp-${process.pid}`;
81
+ writeFileSync(tmp, out, { mode: 384 });
82
+ renameSync(tmp, envFile);
83
+ }
84
+ return renamed;
85
+ }
86
+ function defaultLocalDir() {
87
+ return envVar("RAYDR_LOCAL_DIR") ?? migrateDir(join(xdg("XDG_DATA_HOME"), "raydar"), join(xdg("XDG_DATA_HOME"), "raydr"));
88
+ }
89
+ function defaultCacheDir() {
90
+ return join(migrateDir(join(xdg("XDG_CACHE_HOME"), "raydar"), join(xdg("XDG_CACHE_HOME"), "raydr")), "audio");
91
+ }
92
+ var DEFAULT_PORT = 47391;
93
+ function defaultStateDir() {
94
+ return envVar("RAYDR_STATE_DIR") ?? migrateDir(join(xdg("XDG_STATE_HOME"), "raydar"), join(xdg("XDG_STATE_HOME"), "raydr"));
95
+ }
96
+ function defaultEnvFile() {
97
+ return envVar("RAYDR_ENV_FILE") ?? join(migrateDir(join(xdg("XDG_CONFIG_HOME"), "raydar"), join(xdg("XDG_CONFIG_HOME"), "raydr")), "env");
98
+ }
99
+ function parseEnvFile(text) {
100
+ const out = {};
101
+ for (const raw of text.split("\n")) {
102
+ const line = raw.trim();
103
+ if (!line || line.startsWith("#"))
104
+ continue;
105
+ const eq = line.indexOf("=");
106
+ if (eq < 0)
107
+ continue;
108
+ const key = line.slice(0, eq).trim();
109
+ let value = line.slice(eq + 1).trim();
110
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
111
+ value = value.slice(1, -1);
112
+ }
113
+ out[key] = value;
114
+ }
115
+ return out;
116
+ }
117
+ function newAccessKey() {
118
+ return randomBytes(24).toString("base64url");
119
+ }
120
+ function loadConfig(envFile = defaultEnvFile(), options = {}) {
121
+ const fromFile = existsSync(envFile) ? parseEnvFile(readFileSync(envFile, "utf8")) : {};
122
+ const get = (key) => {
123
+ const legacy = key.replace(/^RAYDR_/, "RAYDAR_");
124
+ let v = envVar(key) ?? fromFile[key];
125
+ if ((v === void 0 || v === "") && legacy !== key && fromFile[legacy]) {
126
+ deprecate(legacy, key);
127
+ v = fromFile[legacy];
128
+ }
129
+ return v === void 0 || v === "" ? void 0 : v;
130
+ };
131
+ if (options.generateKey && existsSync(envFile)) {
132
+ const renamed = migrateEnvFile(envFile);
133
+ if (renamed > 0) {
134
+ console.log(`renamed ${renamed} RAYDAR_ line(s) to RAYDR_ in ${envFile}`);
135
+ Object.assign(fromFile, parseEnvFile(readFileSync(envFile, "utf8")));
136
+ }
137
+ }
138
+ let accessKey = get("RAYDR_ACCESS_KEY") ?? "";
139
+ if (!accessKey && options.generateKey) {
140
+ accessKey = newAccessKey();
141
+ mkdirSync(dirname(envFile), { recursive: true });
142
+ const prefix = existsSync(envFile) && !readFileSync(envFile, "utf8").endsWith("\n") ? "\n" : "";
143
+ writeFileSync(envFile, `${prefix}RAYDR_ACCESS_KEY=${accessKey}
144
+ `, { flag: "a", mode: 384 });
145
+ chmodSync(envFile, 384);
146
+ console.log(`Generated RAYDR_ACCESS_KEY and saved it to ${envFile}`);
147
+ } else if (existsSync(envFile) && (statSync(envFile).mode & 63) !== 0) {
148
+ console.warn(`warning: ${envFile} is readable by other users; run chmod 600 on it`);
149
+ }
150
+ return {
151
+ host: get("RAYDR_HOST") ?? "127.0.0.1",
152
+ port: Number(get("RAYDR_PORT") ?? DEFAULT_PORT),
153
+ accessKey,
154
+ speechProvider: oneOf("RAYDR_SPEECH_PROVIDER", get("RAYDR_SPEECH_PROVIDER"), SPEECH_PROVIDERS, "elevenlabs"),
155
+ transcriptionProvider: oneOf("RAYDR_TRANSCRIPTION_PROVIDER", get("RAYDR_TRANSCRIPTION_PROVIDER"), TRANSCRIPTION_PROVIDERS, "elevenlabs"),
156
+ elevenLabsApiKey: get("ELEVENLABS_API_KEY") ?? null,
157
+ elevenLabsVoiceId: get("ELEVENLABS_VOICE_ID") ?? DEFAULT_VOICE_ID,
158
+ openAiApiKey: get("OPENAI_API_KEY") ?? null,
159
+ openAiVoice: get("RAYDR_OPENAI_VOICE") ?? "alloy",
160
+ speechInstructions: get("RAYDR_SPEECH_INSTRUCTIONS") ?? DEFAULT_SPEECH_INSTRUCTIONS,
161
+ kokoroVoice: get("RAYDR_KOKORO_VOICE") ?? "af_heart",
162
+ whisperModel: get("RAYDR_WHISPER_MODEL") ?? "small.en",
163
+ localPort: positiveOr(Number(get("RAYDR_LOCAL_PORT")), DEFAULT_LOCAL_PORT),
164
+ localDir: get("RAYDR_LOCAL_DIR") ?? defaultLocalDir(),
165
+ cacheDir: get("RAYDR_CACHE_DIR") ?? defaultCacheDir(),
166
+ maxSpeechChars: positiveOr(Number(get("RAYDR_MAX_SPEECH_CHARS")), 1500),
167
+ stateDir: get("RAYDR_STATE_DIR") ?? defaultStateDir(),
168
+ sessionsMode: get("RAYDR_SESSIONS") === "all" ? "all" : "opt-in",
169
+ webDir: get("RAYDR_WEB_DIR") ?? null,
170
+ envFile
171
+ };
172
+ }
173
+ function positiveOr(n, fallback) {
174
+ return Number.isFinite(n) && n > 0 ? n : fallback;
175
+ }
176
+
177
+ // src/session-state.ts
178
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
179
+ import { join as join2 } from "node:path";
180
+ function sessionFile(stateDir, sessionId) {
181
+ return join2(stateDir, "sessions", `${sessionId}.json`);
182
+ }
183
+ function validSessionId(id) {
184
+ return typeof id === "string" && /^[A-Za-z0-9-]{8,64}$/.test(id);
185
+ }
186
+ function isEnabled(stateDir, sessionId) {
187
+ return validSessionId(sessionId) && existsSync2(sessionFile(stateDir, sessionId));
188
+ }
189
+ function enableSession(stateDir, record) {
190
+ mkdirSync2(join2(stateDir, "sessions"), { recursive: true, mode: 448 });
191
+ writeFileSync2(sessionFile(stateDir, record.sessionId), `${JSON.stringify(record, null, 2)}
192
+ `, {
193
+ mode: 384
194
+ });
195
+ }
196
+ function disableSession(stateDir, sessionId) {
197
+ const file = sessionFile(stateDir, sessionId);
198
+ if (!existsSync2(file)) return false;
199
+ unlinkSync(file);
200
+ return true;
201
+ }
202
+
203
+ export {
204
+ SPEECH_PROVIDERS,
205
+ TRANSCRIPTION_PROVIDERS,
206
+ migrateEnvFile,
207
+ DEFAULT_PORT,
208
+ defaultStateDir,
209
+ defaultEnvFile,
210
+ parseEnvFile,
211
+ newAccessKey,
212
+ loadConfig,
213
+ validSessionId,
214
+ isEnabled,
215
+ enableSession,
216
+ disableSession
217
+ };
@@ -0,0 +1,6 @@
1
+ // src/version.ts
2
+ var VERSION = "0.1.0";
3
+
4
+ export {
5
+ VERSION
6
+ };
package/dist/hook.js ADDED
@@ -0,0 +1,95 @@
1
+ import {
2
+ DEFAULT_PORT,
3
+ defaultEnvFile,
4
+ defaultStateDir,
5
+ isEnabled,
6
+ parseEnvFile
7
+ } from "./chunk-KYTKA3YV.js";
8
+
9
+ // src/hook.ts
10
+ import { readFileSync } from "node:fs";
11
+ import { request } from "node:http";
12
+ var CONNECT_TIMEOUT_MS = 1e3;
13
+ var TOTAL_BUDGET_MS = 4e3;
14
+ async function runHook(event, urlOverride) {
15
+ const off = process.env.RAYDR_OFF || process.env.RAYDAR_OFF;
16
+ if (off === "1" || off === "true") return;
17
+ const deadline = Date.now() + TOTAL_BUDGET_MS;
18
+ const payload = await readStdin(deadline - Date.now());
19
+ if (payload === null) return;
20
+ const settings = loadSettings();
21
+ if (settings.sessionsMode !== "all") {
22
+ const sessionId = sessionIdOf(payload) ?? process.env.CLAUDE_CODE_SESSION_ID;
23
+ if (!sessionId || !isEnabled(settings.stateDir, sessionId)) return;
24
+ }
25
+ const url = new URL(`${urlOverride ?? settings.url}/hooks/${event}`);
26
+ const headers = {
27
+ "content-type": "application/json",
28
+ "content-length": Buffer.byteLength(payload)
29
+ };
30
+ if (settings.accessKey) headers.authorization = `Bearer ${settings.accessKey}`;
31
+ const pane = process.env.HERDR_PANE_ID;
32
+ if (pane && !pane.startsWith("$")) headers["X-Raydr-Pane"] = pane;
33
+ const remaining = deadline - Date.now();
34
+ if (remaining <= 0) return;
35
+ await new Promise((resolve) => {
36
+ const connectTimeout = Math.min(CONNECT_TIMEOUT_MS, remaining);
37
+ const req = request(url, { method: "POST", headers, timeout: connectTimeout }, (res) => {
38
+ res.resume();
39
+ res.on("end", resolve);
40
+ res.on("error", () => resolve());
41
+ });
42
+ const total = setTimeout(() => req.destroy(), remaining);
43
+ req.on("timeout", () => req.destroy());
44
+ req.on("error", () => resolve());
45
+ req.on("close", () => {
46
+ clearTimeout(total);
47
+ resolve();
48
+ });
49
+ req.end(payload);
50
+ });
51
+ }
52
+ function sessionIdOf(payload) {
53
+ try {
54
+ const parsed = JSON.parse(payload);
55
+ const id = parsed?.session_id;
56
+ return typeof id === "string" ? id : void 0;
57
+ } catch {
58
+ return void 0;
59
+ }
60
+ }
61
+ function loadSettings() {
62
+ let fromFile = {};
63
+ try {
64
+ fromFile = parseEnvFile(readFileSync(defaultEnvFile(), "utf8"));
65
+ } catch {
66
+ }
67
+ const get = (key) => process.env[key] || fromFile[key] || process.env[key.replace(/^RAYDR_/, "RAYDAR_")] || fromFile[key.replace(/^RAYDR_/, "RAYDAR_")] || void 0;
68
+ const host = get("RAYDR_HOST") ?? "127.0.0.1";
69
+ const port = get("RAYDR_PORT") ?? String(DEFAULT_PORT);
70
+ const stateDirFromFile = fromFile.RAYDR_STATE_DIR || fromFile.RAYDAR_STATE_DIR;
71
+ return {
72
+ url: `http://${host}:${port}`,
73
+ accessKey: get("RAYDR_ACCESS_KEY"),
74
+ sessionsMode: get("RAYDR_SESSIONS") === "all" ? "all" : "opt-in",
75
+ stateDir: get("RAYDR_STATE_DIR") || stateDirFromFile || defaultStateDir()
76
+ };
77
+ }
78
+ function readStdin(timeoutMs) {
79
+ return new Promise((resolve) => {
80
+ const chunks = [];
81
+ const timer = setTimeout(() => resolve(null), timeoutMs);
82
+ process.stdin.on("data", (c) => chunks.push(c));
83
+ process.stdin.on("end", () => {
84
+ clearTimeout(timer);
85
+ resolve(Buffer.concat(chunks).toString("utf8"));
86
+ });
87
+ process.stdin.on("error", () => {
88
+ clearTimeout(timer);
89
+ resolve(null);
90
+ });
91
+ });
92
+ }
93
+ export {
94
+ runHook
95
+ };
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ var [command, ...rest] = process.argv.slice(2);
5
+ if (command === "--version" || command === "-v" || command === "version") {
6
+ import("./version-IVSD7EGG.js").then(({ VERSION }) => {
7
+ console.log(VERSION);
8
+ });
9
+ } else if (command === "hook") {
10
+ const urlIndex = rest.indexOf("--url");
11
+ const url = urlIndex >= 0 ? rest[urlIndex + 1] : void 0;
12
+ const urlValueIndex = urlIndex >= 0 ? urlIndex + 1 : -1;
13
+ const event = rest.find((a, i) => !a.startsWith("--") && i !== urlValueIndex);
14
+ if (event) {
15
+ import("./hook.js").then(({ runHook }) => runHook(event, url)).catch(() => {
16
+ }).finally(() => process.exit(0));
17
+ } else {
18
+ process.exit(0);
19
+ }
20
+ } else {
21
+ import("./main.js").then(({ main }) => main(process.argv.slice(2))).catch((err) => {
22
+ console.error(`raydr: ${err.message}`);
23
+ process.exit(1);
24
+ });
25
+ }