rex-claude 4.0.0 → 6.0.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.
Files changed (38) hide show
  1. package/dist/agents-JIZXXASP.js +853 -0
  2. package/dist/app-3VWDSH5F.js +248 -0
  3. package/dist/audio-US2J627E.js +196 -0
  4. package/dist/audit-ZVTGE4L4.js +8 -0
  5. package/dist/call-AQZ3Z5SE.js +143 -0
  6. package/dist/chunk-5ND7JYY3.js +62 -0
  7. package/dist/chunk-6SRV2I2H.js +56 -0
  8. package/dist/{setup-AO3MW46W.js → chunk-A7ZLQUOX.js} +93 -16
  9. package/dist/chunk-E5UYN3W7.js +105 -0
  10. package/dist/chunk-HAHJD3QH.js +147 -0
  11. package/dist/{init-DLFEGD6O.js → chunk-KR7ISYZH.js} +328 -29
  12. package/dist/chunk-LTOM55UV.js +154 -0
  13. package/dist/chunk-PDX44BCA.js +11 -0
  14. package/dist/chunk-PPGYFMU5.js +67 -0
  15. package/dist/{chunk-7AGI43F5.js → chunk-WBMVBMWB.js} +4 -2
  16. package/dist/{context-FN5O5YBI.js → context-XNCG2M5Q.js} +2 -1
  17. package/dist/daemon-5KNSNFTD.js +208 -0
  18. package/dist/gateway-YLP66MCQ.js +2273 -0
  19. package/dist/hammerspoon/rex-call-watcher.lua +186 -0
  20. package/dist/index.js +309 -15
  21. package/dist/init-RDZFIBLA.js +30 -0
  22. package/dist/install-63JBDPRU.js +41 -0
  23. package/dist/{llm-YRORUH7E.js → llm-RALIPIMI.js} +2 -1
  24. package/dist/mcp_registry-DX4GGSP6.js +514 -0
  25. package/dist/migrate-GDO37TI5.js +87 -0
  26. package/dist/{optimize-UKMAGQQE.js → optimize-5TE5RKZV.js} +2 -1
  27. package/dist/paths-4SECM6E6.js +38 -0
  28. package/dist/preload-I3MYBVNU.js +78 -0
  29. package/dist/projects-V6TSLO7E.js +17 -0
  30. package/dist/{prune-2PPIVDXK.js → prune-B7F5B5OF.js} +2 -1
  31. package/dist/recategorize-YXYIMQLZ.js +155 -0
  32. package/dist/router-2JD34COX.js +12 -0
  33. package/dist/self-improve-YK7RCYF4.js +197 -0
  34. package/dist/setup-KNDTVFO6.js +8 -0
  35. package/dist/skills-AIWFY5NH.js +374 -0
  36. package/dist/voice-RITC3EVC.js +248 -0
  37. package/package.json +12 -3
  38. package/dist/gateway-EKMU5D7J.js +0 -784
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env node
2
+ import "./chunk-PDX44BCA.js";
3
+
4
+ // src/app.ts
5
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
6
+ import { execSync } from "child_process";
7
+ import { dirname, join, resolve } from "path";
8
+ var COLORS = {
9
+ reset: "\x1B[0m",
10
+ green: "\x1B[32m",
11
+ yellow: "\x1B[33m",
12
+ red: "\x1B[31m",
13
+ bold: "\x1B[1m",
14
+ cyan: "\x1B[36m",
15
+ dim: "\x1B[2m"
16
+ };
17
+ var CANONICAL_APP = "/Applications/rex_app.app";
18
+ var LEGACY_APP = "/Applications/REX.app";
19
+ function ok(msg) {
20
+ console.log(` ${COLORS.green}\u2713${COLORS.reset} ${msg}`);
21
+ }
22
+ function info(msg) {
23
+ console.log(` ${COLORS.cyan}\u2139${COLORS.reset} ${msg}`);
24
+ }
25
+ function fail(msg) {
26
+ console.log(` ${COLORS.red}\u2717${COLORS.reset} ${msg}`);
27
+ }
28
+ function commandExists(cmd) {
29
+ try {
30
+ execSync(`command -v ${cmd}`, { stdio: "ignore" });
31
+ return true;
32
+ } catch {
33
+ return false;
34
+ }
35
+ }
36
+ function findRepoRoot(startDir = process.cwd()) {
37
+ let current = resolve(startDir);
38
+ while (true) {
39
+ const flutterPubspec = join(current, "packages", "flutter_app", "pubspec.yaml");
40
+ if (existsSync(flutterPubspec)) return current;
41
+ const parent = dirname(current);
42
+ if (parent === current) break;
43
+ current = parent;
44
+ }
45
+ return null;
46
+ }
47
+ function getBuildBundle(repoRoot, mode) {
48
+ return join(
49
+ repoRoot,
50
+ "packages",
51
+ "flutter_app",
52
+ "build",
53
+ "macos",
54
+ "Build",
55
+ "Products",
56
+ mode === "release" ? "Release" : "Debug",
57
+ "rex_app.app"
58
+ );
59
+ }
60
+ function buildFlutterApp(repoRoot, mode) {
61
+ if (!commandExists("flutter")) {
62
+ throw new Error("flutter command not found in PATH");
63
+ }
64
+ const flutterDir = join(repoRoot, "packages", "flutter_app");
65
+ const flag = mode === "release" ? "--release" : "--debug";
66
+ info(`Building Flutter macOS app (${mode})...`);
67
+ execSync(`flutter build macos ${flag}`, { cwd: flutterDir, stdio: "inherit" });
68
+ }
69
+ function gitInfo(repoRoot) {
70
+ let branch = "unknown";
71
+ let commit = "unknown";
72
+ let dirty = false;
73
+ try {
74
+ branch = execSync("git rev-parse --abbrev-ref HEAD", { cwd: repoRoot, encoding: "utf-8" }).trim();
75
+ } catch {
76
+ }
77
+ try {
78
+ commit = execSync("git rev-parse --short HEAD", { cwd: repoRoot, encoding: "utf-8" }).trim();
79
+ } catch {
80
+ }
81
+ try {
82
+ dirty = execSync("git status --porcelain", { cwd: repoRoot, encoding: "utf-8" }).trim().length > 0;
83
+ } catch {
84
+ }
85
+ return { branch, commit, dirty };
86
+ }
87
+ function installAppBundle(sourceBundle) {
88
+ if (!existsSync(sourceBundle)) throw new Error(`Build bundle not found: ${sourceBundle}`);
89
+ info(`Installing app to ${CANONICAL_APP}`);
90
+ rmSync(CANONICAL_APP, { recursive: true, force: true });
91
+ rmSync(LEGACY_APP, { recursive: true, force: true });
92
+ execSync(`ditto "${sourceBundle}" "${CANONICAL_APP}"`, { stdio: "ignore" });
93
+ try {
94
+ execSync(`ln -sfn "${CANONICAL_APP}" "${LEGACY_APP}"`, { stdio: "ignore" });
95
+ } catch {
96
+ }
97
+ }
98
+ function writeBuildInfo(infoData) {
99
+ const resourcesDir = join(CANONICAL_APP, "Contents", "Resources");
100
+ mkdirSync(resourcesDir, { recursive: true });
101
+ const infoPath = join(resourcesDir, "rex-build-info.json");
102
+ writeFileSync(infoPath, JSON.stringify(infoData, null, 2) + "\n");
103
+ }
104
+ function readBuildInfo() {
105
+ const infoPath = join(CANONICAL_APP, "Contents", "Resources", "rex-build-info.json");
106
+ if (!existsSync(infoPath)) return null;
107
+ try {
108
+ return JSON.parse(readFileSync(infoPath, "utf-8"));
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+ function relaunchApp() {
114
+ try {
115
+ execSync(`pkill -f '/Contents/MacOS/rex_app'`, { stdio: "ignore" });
116
+ } catch {
117
+ }
118
+ execSync(`open -a "${CANONICAL_APP}"`, { stdio: "ignore" });
119
+ }
120
+ function printAppInfo() {
121
+ console.log(`
122
+ ${COLORS.bold}REX App Info${COLORS.reset}`);
123
+ console.log(` Canonical path: ${CANONICAL_APP} ${existsSync(CANONICAL_APP) ? `${COLORS.green}(installed)${COLORS.reset}` : `${COLORS.red}(missing)${COLORS.reset}`}`);
124
+ console.log(` Legacy path: ${LEGACY_APP} ${existsSync(LEGACY_APP) ? `${COLORS.yellow}(alias present)${COLORS.reset}` : `${COLORS.dim}(none)${COLORS.reset}`}`);
125
+ const infoData = readBuildInfo();
126
+ if (infoData) {
127
+ console.log(` Source repo: ${infoData.sourceRepo}`);
128
+ console.log(` Git: ${infoData.git.branch}@${infoData.git.commit}${infoData.git.dirty ? " (dirty)" : ""}`);
129
+ console.log(` Build mode: ${infoData.buildMode}`);
130
+ console.log(` Installed at: ${infoData.installedAt}`);
131
+ } else {
132
+ console.log(` ${COLORS.dim}No build metadata found (install once via \`rex app update\`).${COLORS.reset}`);
133
+ }
134
+ try {
135
+ const running = execSync(`pgrep -fal '/Contents/MacOS/rex_app'`, { encoding: "utf-8" }).trim();
136
+ if (running) {
137
+ console.log(` Running process:
138
+ ${running.split("\n").map((line) => ` ${line}`).join("\n")}`);
139
+ } else {
140
+ console.log(` Running process: ${COLORS.dim}none${COLORS.reset}`);
141
+ }
142
+ } catch {
143
+ console.log(` Running process: ${COLORS.dim}none${COLORS.reset}`);
144
+ }
145
+ console.log("");
146
+ }
147
+ function usage() {
148
+ console.log(`
149
+ ${COLORS.bold}REX App Commands${COLORS.reset}
150
+ rex app update [--debug|--release] [--no-launch] [--no-build]
151
+ rex app info
152
+ rex app open
153
+ `);
154
+ }
155
+ function parseMode(args) {
156
+ if (args.includes("--release")) return "release";
157
+ return "debug";
158
+ }
159
+ function hasFlag(args, flag) {
160
+ return args.includes(flag);
161
+ }
162
+ function ensureDarwin() {
163
+ if (process.platform !== "darwin") {
164
+ throw new Error("App install/update is only supported on macOS");
165
+ }
166
+ }
167
+ function updateApp(args) {
168
+ ensureDarwin();
169
+ const mode = parseMode(args);
170
+ const launch = !hasFlag(args, "--no-launch");
171
+ const noBuild = hasFlag(args, "--no-build");
172
+ const repoRoot = findRepoRoot();
173
+ if (!repoRoot) {
174
+ throw new Error("REX monorepo not found from current directory (need packages/flutter_app)");
175
+ }
176
+ if (!noBuild) buildFlutterApp(repoRoot, mode);
177
+ let sourceBundle = getBuildBundle(repoRoot, mode);
178
+ if (!existsSync(sourceBundle)) {
179
+ const fallbackMode = mode === "debug" ? "release" : "debug";
180
+ const fallbackBundle = getBuildBundle(repoRoot, fallbackMode);
181
+ if (existsSync(fallbackBundle)) {
182
+ info(`Requested ${mode} bundle not found, using existing ${fallbackMode} bundle`);
183
+ sourceBundle = fallbackBundle;
184
+ } else {
185
+ throw new Error(`No built app found. Expected:
186
+ - ${sourceBundle}
187
+ - ${fallbackBundle}`);
188
+ }
189
+ }
190
+ installAppBundle(sourceBundle);
191
+ const git = gitInfo(repoRoot);
192
+ writeBuildInfo({
193
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
194
+ sourceRepo: repoRoot,
195
+ buildMode: mode,
196
+ sourceBundle,
197
+ git
198
+ });
199
+ ok(`Installed ${CANONICAL_APP}`);
200
+ info(`Source: ${repoRoot} (${git.branch}@${git.commit}${git.dirty ? ", dirty" : ""})`);
201
+ info(`Mode: ${mode}`);
202
+ if (launch) {
203
+ relaunchApp();
204
+ ok("REX app launched");
205
+ } else {
206
+ info("Launch skipped (--no-launch)");
207
+ }
208
+ }
209
+ function openApp() {
210
+ ensureDarwin();
211
+ if (existsSync(CANONICAL_APP)) {
212
+ execSync(`open -a "${CANONICAL_APP}"`, { stdio: "ignore" });
213
+ ok("REX app opened");
214
+ return;
215
+ }
216
+ if (existsSync(LEGACY_APP)) {
217
+ execSync(`open -a "${LEGACY_APP}"`, { stdio: "ignore" });
218
+ ok("REX app opened");
219
+ return;
220
+ }
221
+ throw new Error("No installed app found. Run `rex app update` first.");
222
+ }
223
+ async function app(args) {
224
+ const sub = args[0] ?? "help";
225
+ try {
226
+ switch (sub) {
227
+ case "update":
228
+ updateApp(args.slice(1));
229
+ break;
230
+ case "info":
231
+ printAppInfo();
232
+ break;
233
+ case "open":
234
+ openApp();
235
+ break;
236
+ case "help":
237
+ default:
238
+ usage();
239
+ break;
240
+ }
241
+ } catch (error) {
242
+ fail(error?.message ?? String(error));
243
+ process.exit(1);
244
+ }
245
+ }
246
+ export {
247
+ app
248
+ };
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+ import "./chunk-PDX44BCA.js";
3
+
4
+ // src/audio.ts
5
+ import { homedir } from "os";
6
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
7
+ import { join } from "path";
8
+ import { spawn, spawnSync } from "child_process";
9
+ var HOME = homedir();
10
+ var RUNTIME_DIR = join(HOME, ".rex-memory", "runtime");
11
+ var RECORDINGS_DIR = join(HOME, ".rex-memory", "recordings");
12
+ var STATE_FILE = join(RUNTIME_DIR, "audio-logger.json");
13
+ function ensureDirs() {
14
+ if (!existsSync(RUNTIME_DIR)) mkdirSync(RUNTIME_DIR, { recursive: true });
15
+ if (!existsSync(RECORDINGS_DIR)) mkdirSync(RECORDINGS_DIR, { recursive: true });
16
+ }
17
+ function ffmpegExists() {
18
+ const check = spawnSync("ffmpeg", ["-version"], { stdio: "ignore" });
19
+ return check.status === 0;
20
+ }
21
+ function readState() {
22
+ try {
23
+ if (existsSync(STATE_FILE)) {
24
+ const parsed = JSON.parse(readFileSync(STATE_FILE, "utf-8"));
25
+ return {
26
+ pid: typeof parsed.pid === "number" ? parsed.pid : null,
27
+ startedAt: typeof parsed.startedAt === "string" ? parsed.startedAt : null,
28
+ currentFile: typeof parsed.currentFile === "string" ? parsed.currentFile : null
29
+ };
30
+ }
31
+ } catch {
32
+ }
33
+ return { pid: null, startedAt: null, currentFile: null };
34
+ }
35
+ function writeState(state) {
36
+ ensureDirs();
37
+ writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
38
+ }
39
+ function isPidRunning(pid) {
40
+ if (!pid || pid <= 0) return false;
41
+ try {
42
+ process.kill(pid, 0);
43
+ return true;
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ function listRecordings() {
49
+ ensureDirs();
50
+ return readdirSync(RECORDINGS_DIR).filter((f) => f.endsWith(".wav")).sort().reverse();
51
+ }
52
+ function currentStatus() {
53
+ const state = readState();
54
+ const capturing = isPidRunning(state.pid);
55
+ const recordings = listRecordings();
56
+ return {
57
+ capturing,
58
+ pid: capturing ? state.pid : null,
59
+ recordingsDir: RECORDINGS_DIR,
60
+ recordingsCount: recordings.length,
61
+ currentFile: capturing ? state.currentFile : null,
62
+ startedAt: capturing ? state.startedAt : null
63
+ };
64
+ }
65
+ function printStatus(status, jsonMode) {
66
+ if (jsonMode) {
67
+ console.log(JSON.stringify(status, null, 2));
68
+ return;
69
+ }
70
+ console.log(`Audio logger: ${status.capturing ? "recording" : "idle"}`);
71
+ console.log(`Recordings: ${status.recordingsCount}`);
72
+ console.log(`Directory: ${status.recordingsDir}`);
73
+ if (status.currentFile) console.log(`Current: ${status.currentFile}`);
74
+ }
75
+ function startCapture(jsonMode) {
76
+ ensureDirs();
77
+ if (!ffmpegExists()) {
78
+ const msg = "ffmpeg not found. Install ffmpeg to use audio logger (brew install ffmpeg).";
79
+ if (jsonMode) {
80
+ console.log(JSON.stringify({ ok: false, error: msg }));
81
+ return;
82
+ }
83
+ console.error(msg);
84
+ process.exit(1);
85
+ }
86
+ const state = readState();
87
+ if (isPidRunning(state.pid)) {
88
+ const status2 = currentStatus();
89
+ if (jsonMode) {
90
+ console.log(JSON.stringify({ ok: true, alreadyRunning: true, ...status2 }, null, 2));
91
+ return;
92
+ }
93
+ console.log("Audio logger already running.");
94
+ return;
95
+ }
96
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[.:]/g, "-").replace("T", "_").slice(0, 19);
97
+ const output = join(RECORDINGS_DIR, `call-${ts}.wav`);
98
+ const input = process.env.REX_AUDIO_INPUT || ":0";
99
+ const args = [
100
+ "-hide_banner",
101
+ "-loglevel",
102
+ "error",
103
+ "-f",
104
+ "avfoundation",
105
+ "-i",
106
+ input,
107
+ "-ac",
108
+ "1",
109
+ "-ar",
110
+ "16000",
111
+ "-y",
112
+ output
113
+ ];
114
+ const child = spawn("ffmpeg", args, {
115
+ detached: true,
116
+ stdio: "ignore"
117
+ });
118
+ child.unref();
119
+ const nextState = {
120
+ pid: child.pid ?? null,
121
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
122
+ currentFile: output
123
+ };
124
+ writeState(nextState);
125
+ const status = currentStatus();
126
+ if (jsonMode) {
127
+ console.log(JSON.stringify({ ok: true, ...status }, null, 2));
128
+ } else {
129
+ console.log("Audio logger started.");
130
+ console.log(`Input: ${input}`);
131
+ console.log(`Output: ${output}`);
132
+ }
133
+ }
134
+ function stopCapture(jsonMode) {
135
+ const state = readState();
136
+ const wasRunning = isPidRunning(state.pid);
137
+ if (!state.pid || !wasRunning) {
138
+ writeState({ pid: null, startedAt: null, currentFile: null });
139
+ if (jsonMode) {
140
+ console.log(JSON.stringify({ ok: true, stopped: false, message: "Audio logger already stopped" }, null, 2));
141
+ return;
142
+ }
143
+ console.log("Audio logger already stopped.");
144
+ return;
145
+ }
146
+ try {
147
+ process.kill(state.pid, "SIGINT");
148
+ } catch {
149
+ try {
150
+ process.kill(state.pid, "SIGTERM");
151
+ } catch {
152
+ }
153
+ }
154
+ writeState({ pid: null, startedAt: null, currentFile: null });
155
+ if (jsonMode) {
156
+ console.log(JSON.stringify({ ok: true, stopped: true, file: state.currentFile }, null, 2));
157
+ } else {
158
+ console.log("Audio logger stopped.");
159
+ if (state.currentFile) console.log(`Saved: ${state.currentFile}`);
160
+ }
161
+ }
162
+ function printRecordings(jsonMode) {
163
+ const recordings = listRecordings().map((f) => join(RECORDINGS_DIR, f));
164
+ if (jsonMode) {
165
+ console.log(JSON.stringify({ count: recordings.length, recordings }, null, 2));
166
+ return;
167
+ }
168
+ if (recordings.length === 0) {
169
+ console.log("No recordings yet.");
170
+ return;
171
+ }
172
+ for (const file of recordings) console.log(file);
173
+ }
174
+ async function audio(args) {
175
+ const sub = args[0] || "status";
176
+ const jsonMode = args.includes("--json");
177
+ switch (sub) {
178
+ case "start":
179
+ startCapture(jsonMode);
180
+ return;
181
+ case "stop":
182
+ stopCapture(jsonMode);
183
+ return;
184
+ case "status":
185
+ printStatus(currentStatus(), jsonMode);
186
+ return;
187
+ case "list":
188
+ printRecordings(jsonMode);
189
+ return;
190
+ default:
191
+ console.log("Usage: rex audio <start|stop|status|list> [--json]");
192
+ }
193
+ }
194
+ export {
195
+ audio
196
+ };
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ audit
4
+ } from "./chunk-HAHJD3QH.js";
5
+ import "./chunk-PDX44BCA.js";
6
+ export {
7
+ audit
8
+ };
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ import "./chunk-PDX44BCA.js";
3
+
4
+ // src/call.ts
5
+ import { homedir } from "os";
6
+ import { existsSync, mkdirSync, readFileSync } from "fs";
7
+ import { join } from "path";
8
+ import { spawnSync } from "child_process";
9
+ var HOME = homedir();
10
+ var RUNTIME_DIR = join(HOME, ".rex-memory", "runtime");
11
+ var STATE_FILE = join(RUNTIME_DIR, "call-state.json");
12
+ var EVENTS_FILE = join(RUNTIME_DIR, "call-events.jsonl");
13
+ function ensureRuntime() {
14
+ if (!existsSync(RUNTIME_DIR)) mkdirSync(RUNTIME_DIR, { recursive: true });
15
+ }
16
+ function emptyState() {
17
+ return {
18
+ active: false,
19
+ app: "",
20
+ reason: "",
21
+ title: "",
22
+ startedAt: 0,
23
+ updatedAt: 0,
24
+ iso: ""
25
+ };
26
+ }
27
+ function readState() {
28
+ try {
29
+ if (!existsSync(STATE_FILE)) return emptyState();
30
+ const parsed = JSON.parse(readFileSync(STATE_FILE, "utf-8"));
31
+ return {
32
+ active: parsed.active === true,
33
+ app: typeof parsed.app === "string" ? parsed.app : "",
34
+ reason: typeof parsed.reason === "string" ? parsed.reason : "",
35
+ title: typeof parsed.title === "string" ? parsed.title : "",
36
+ startedAt: typeof parsed.startedAt === "number" ? parsed.startedAt : 0,
37
+ updatedAt: typeof parsed.updatedAt === "number" ? parsed.updatedAt : 0,
38
+ iso: typeof parsed.iso === "string" ? parsed.iso : ""
39
+ };
40
+ } catch {
41
+ return emptyState();
42
+ }
43
+ }
44
+ function printState(jsonMode) {
45
+ const state = readState();
46
+ if (jsonMode) {
47
+ console.log(JSON.stringify(state, null, 2));
48
+ return;
49
+ }
50
+ if (!state.active) {
51
+ console.log("No active call detected.");
52
+ return;
53
+ }
54
+ console.log(`Active call: ${state.app || "unknown app"}`);
55
+ if (state.reason) console.log(`Reason: ${state.reason}`);
56
+ if (state.title) console.log(`Title: ${state.title}`);
57
+ if (state.startedAt > 0) console.log(`Started: ${new Date(state.startedAt * 1e3).toISOString()}`);
58
+ }
59
+ function printEvents(jsonMode, tail) {
60
+ if (!existsSync(EVENTS_FILE)) {
61
+ if (jsonMode) {
62
+ console.log(JSON.stringify({ events: [] }, null, 2));
63
+ return;
64
+ }
65
+ console.log("No call events yet.");
66
+ return;
67
+ }
68
+ const lines = readFileSync(EVENTS_FILE, "utf-8").split("\n").filter(Boolean);
69
+ const sliced = tail > 0 ? lines.slice(-tail) : lines;
70
+ if (jsonMode) {
71
+ const events = sliced.map((line) => {
72
+ try {
73
+ return JSON.parse(line);
74
+ } catch {
75
+ return { raw: line };
76
+ }
77
+ });
78
+ console.log(JSON.stringify({ events }, null, 2));
79
+ return;
80
+ }
81
+ for (const line of sliced) console.log(line);
82
+ }
83
+ function selfCommand(args) {
84
+ const script = process.argv[1];
85
+ return spawnSync(process.execPath, [script, ...args], { encoding: "utf-8" });
86
+ }
87
+ function runAudioCommand(sub) {
88
+ selfCommand(["audio", sub]);
89
+ }
90
+ function isAudioCapturing() {
91
+ const res = selfCommand(["audio", "status", "--json"]);
92
+ if (res.status !== 0) return false;
93
+ try {
94
+ const parsed = JSON.parse((res.stdout || "").trim());
95
+ return parsed.capturing === true;
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+ async function watchAutoCapture() {
101
+ ensureRuntime();
102
+ console.log("REX call watcher daemon started (auto audio logger).");
103
+ let lastActive = null;
104
+ while (true) {
105
+ const state = readState();
106
+ if (lastActive === null || state.active !== lastActive) {
107
+ const capturing = isAudioCapturing();
108
+ if (state.active && !capturing) {
109
+ runAudioCommand("start");
110
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] call_start -> audio start (${state.app || "unknown"})`);
111
+ }
112
+ if (!state.active && capturing) {
113
+ runAudioCommand("stop");
114
+ console.log(`[${(/* @__PURE__ */ new Date()).toISOString()}] call_end -> audio stop`);
115
+ }
116
+ lastActive = state.active;
117
+ }
118
+ await new Promise((resolve) => setTimeout(resolve, 5e3));
119
+ }
120
+ }
121
+ async function call(args) {
122
+ const sub = args[0] || "status";
123
+ const jsonMode = args.includes("--json");
124
+ switch (sub) {
125
+ case "status":
126
+ printState(jsonMode);
127
+ return;
128
+ case "events": {
129
+ const tailIndex = args.findIndex((a) => a === "--tail");
130
+ const tail = tailIndex >= 0 ? parseInt(args[tailIndex + 1] || "20", 10) : 20;
131
+ printEvents(jsonMode, Number.isFinite(tail) ? tail : 20);
132
+ return;
133
+ }
134
+ case "watch":
135
+ await watchAutoCapture();
136
+ return;
137
+ default:
138
+ console.log("Usage: rex call <status|events|watch> [--json] [--tail N]");
139
+ }
140
+ }
141
+ export {
142
+ call
143
+ };
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ DAEMON_LOG_PATH,
4
+ ensureRexDirs
5
+ } from "./chunk-6SRV2I2H.js";
6
+
7
+ // src/logger.ts
8
+ import { appendFileSync, existsSync, readFileSync, writeFileSync } from "fs";
9
+ var LEVEL_PRIORITY = { debug: 0, info: 1, warn: 2, error: 3 };
10
+ var LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
11
+ var LEVEL_COLOR = { debug: "\x1B[2m", info: "\x1B[36m", warn: "\x1B[33m", error: "\x1B[31m" };
12
+ var RESET = "\x1B[0m";
13
+ var minLevel = "info";
14
+ var logToFile = true;
15
+ var logToConsole = true;
16
+ function configureLogger(opts) {
17
+ if (opts.level) minLevel = opts.level;
18
+ if (opts.file !== void 0) logToFile = opts.file;
19
+ if (opts.console !== void 0) logToConsole = opts.console;
20
+ }
21
+ function write(level, source, msg) {
22
+ if (LEVEL_PRIORITY[level] < LEVEL_PRIORITY[minLevel]) return;
23
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
24
+ const label = LEVEL_LABEL[level];
25
+ const fileLine = `[${ts}] [${label}] [${source}] ${msg}`;
26
+ if (logToConsole) {
27
+ const color = LEVEL_COLOR[level];
28
+ console.log(`${color}[${label}]${RESET} ${RESET}[${source}] ${msg}`);
29
+ }
30
+ if (logToFile) {
31
+ try {
32
+ ensureRexDirs();
33
+ appendFileSync(DAEMON_LOG_PATH, fileLine + "\n");
34
+ } catch {
35
+ }
36
+ }
37
+ }
38
+ function createLogger(source) {
39
+ return {
40
+ debug: (msg) => write("debug", source, msg),
41
+ info: (msg) => write("info", source, msg),
42
+ warn: (msg) => write("warn", source, msg),
43
+ error: (msg) => write("error", source, msg)
44
+ };
45
+ }
46
+ function rotateLog(maxLines = 1e4, keepLines = 5e3) {
47
+ if (!existsSync(DAEMON_LOG_PATH)) return;
48
+ try {
49
+ const content = readFileSync(DAEMON_LOG_PATH, "utf-8");
50
+ const lines = content.split("\n");
51
+ if (lines.length > maxLines) {
52
+ writeFileSync(DAEMON_LOG_PATH, lines.slice(-keepLines).join("\n"));
53
+ }
54
+ } catch {
55
+ }
56
+ }
57
+
58
+ export {
59
+ configureLogger,
60
+ createLogger,
61
+ rotateLog
62
+ };