@makefinks/daemon 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/LICENSE +21 -0
- package/README.md +126 -0
- package/dist/cli.js +22 -0
- package/package.json +79 -0
- package/src/ai/agent-turn-runner.ts +130 -0
- package/src/ai/daemon-ai.ts +403 -0
- package/src/ai/exa-client.ts +21 -0
- package/src/ai/exa-fetch-cache.ts +104 -0
- package/src/ai/model-config.ts +99 -0
- package/src/ai/sanitize-messages.ts +83 -0
- package/src/ai/system-prompt.ts +363 -0
- package/src/ai/tools/fetch-urls.ts +187 -0
- package/src/ai/tools/grounding-manager.ts +94 -0
- package/src/ai/tools/index.ts +52 -0
- package/src/ai/tools/read-file.ts +100 -0
- package/src/ai/tools/render-url.ts +275 -0
- package/src/ai/tools/run-bash.ts +224 -0
- package/src/ai/tools/subagents.ts +195 -0
- package/src/ai/tools/todo-manager.ts +150 -0
- package/src/ai/tools/web-search.ts +91 -0
- package/src/app/App.tsx +711 -0
- package/src/app/components/AppOverlays.tsx +131 -0
- package/src/app/components/AvatarLayer.tsx +51 -0
- package/src/app/components/ConversationPane.tsx +476 -0
- package/src/avatar/DaemonAvatarRenderable.ts +343 -0
- package/src/avatar/daemon-avatar-rig.ts +1165 -0
- package/src/avatar-preview.ts +186 -0
- package/src/cli.ts +26 -0
- package/src/components/ApiKeyInput.tsx +99 -0
- package/src/components/ApiKeyStep.tsx +95 -0
- package/src/components/ApprovalPicker.tsx +109 -0
- package/src/components/ContentBlockView.tsx +141 -0
- package/src/components/DaemonText.tsx +34 -0
- package/src/components/DeviceMenu.tsx +166 -0
- package/src/components/GroundingBadge.tsx +21 -0
- package/src/components/GroundingMenu.tsx +310 -0
- package/src/components/HotkeysPane.tsx +115 -0
- package/src/components/InlineStatusIndicator.tsx +106 -0
- package/src/components/ModelMenu.tsx +411 -0
- package/src/components/OnboardingOverlay.tsx +446 -0
- package/src/components/ProviderMenu.tsx +177 -0
- package/src/components/SessionMenu.tsx +297 -0
- package/src/components/SettingsMenu.tsx +291 -0
- package/src/components/StatusBar.tsx +126 -0
- package/src/components/TokenUsageDisplay.tsx +92 -0
- package/src/components/ToolCallView.tsx +113 -0
- package/src/components/TypingInputBar.tsx +131 -0
- package/src/components/tool-layouts/components.tsx +120 -0
- package/src/components/tool-layouts/defaults.ts +9 -0
- package/src/components/tool-layouts/index.ts +22 -0
- package/src/components/tool-layouts/layouts/bash.ts +110 -0
- package/src/components/tool-layouts/layouts/grounding.tsx +98 -0
- package/src/components/tool-layouts/layouts/index.ts +8 -0
- package/src/components/tool-layouts/layouts/read-file.ts +59 -0
- package/src/components/tool-layouts/layouts/subagent.tsx +118 -0
- package/src/components/tool-layouts/layouts/system-info.ts +8 -0
- package/src/components/tool-layouts/layouts/todo.tsx +139 -0
- package/src/components/tool-layouts/layouts/url-tools.ts +220 -0
- package/src/components/tool-layouts/layouts/web-search.ts +110 -0
- package/src/components/tool-layouts/registry.ts +17 -0
- package/src/components/tool-layouts/types.ts +94 -0
- package/src/hooks/daemon-event-handlers.ts +944 -0
- package/src/hooks/keyboard-handlers.ts +399 -0
- package/src/hooks/menu-navigation.ts +147 -0
- package/src/hooks/use-app-audio-devices-loader.ts +71 -0
- package/src/hooks/use-app-callbacks.ts +202 -0
- package/src/hooks/use-app-context-builder.ts +159 -0
- package/src/hooks/use-app-display-state.ts +162 -0
- package/src/hooks/use-app-menus.ts +51 -0
- package/src/hooks/use-app-model-pricing-loader.ts +45 -0
- package/src/hooks/use-app-model.ts +123 -0
- package/src/hooks/use-app-openrouter-models-loader.ts +44 -0
- package/src/hooks/use-app-openrouter-provider-loader.ts +35 -0
- package/src/hooks/use-app-preferences-bootstrap.ts +212 -0
- package/src/hooks/use-app-sessions.ts +105 -0
- package/src/hooks/use-app-settings.ts +62 -0
- package/src/hooks/use-conversation-manager.ts +163 -0
- package/src/hooks/use-copy-on-select.ts +50 -0
- package/src/hooks/use-daemon-events.ts +396 -0
- package/src/hooks/use-daemon-keyboard.ts +397 -0
- package/src/hooks/use-grounding.ts +46 -0
- package/src/hooks/use-input-history.ts +92 -0
- package/src/hooks/use-menu-keyboard.ts +93 -0
- package/src/hooks/use-playwright-notification.ts +23 -0
- package/src/hooks/use-reasoning-animation.ts +97 -0
- package/src/hooks/use-response-timer.ts +55 -0
- package/src/hooks/use-tool-approval.tsx +202 -0
- package/src/hooks/use-typing-mode.ts +137 -0
- package/src/hooks/use-voice-dependencies-notification.ts +37 -0
- package/src/index.tsx +48 -0
- package/src/scripts/setup-browsers.ts +42 -0
- package/src/state/app-context.tsx +160 -0
- package/src/state/daemon-events.ts +67 -0
- package/src/state/daemon-state.ts +493 -0
- package/src/state/migrations/001-init.ts +33 -0
- package/src/state/migrations/index.ts +8 -0
- package/src/state/model-history-store.ts +45 -0
- package/src/state/runtime-context.ts +21 -0
- package/src/state/session-store.ts +359 -0
- package/src/types/index.ts +405 -0
- package/src/types/theme.ts +52 -0
- package/src/ui/constants.ts +157 -0
- package/src/utils/clipboard.ts +89 -0
- package/src/utils/debug-logger.ts +69 -0
- package/src/utils/formatters.ts +242 -0
- package/src/utils/js-rendering.ts +77 -0
- package/src/utils/markdown-tables.ts +234 -0
- package/src/utils/model-metadata.ts +191 -0
- package/src/utils/openrouter-endpoints.ts +212 -0
- package/src/utils/openrouter-models.ts +205 -0
- package/src/utils/openrouter-pricing.ts +59 -0
- package/src/utils/openrouter-reported-cost.ts +16 -0
- package/src/utils/paste.ts +33 -0
- package/src/utils/preferences.ts +289 -0
- package/src/utils/text-fragment.ts +39 -0
- package/src/utils/tool-output-preview.ts +250 -0
- package/src/utils/voice-dependencies.ts +107 -0
- package/src/utils/workspace-manager.ts +85 -0
- package/src/voice/audio-recorder.ts +579 -0
- package/src/voice/mic-level.ts +35 -0
- package/src/voice/tts/openai-tts-stream.ts +222 -0
- package/src/voice/tts/speech-controller.ts +64 -0
- package/src/voice/tts/tts-player.ts +257 -0
- package/src/voice/voice-input-controller.ts +96 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voice dependencies detection.
|
|
3
|
+
* Detects whether sox is available for voice input/output.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
export interface VoiceCapability {
|
|
9
|
+
available: boolean;
|
|
10
|
+
reason: string;
|
|
11
|
+
hint?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface VoiceDependencies {
|
|
15
|
+
sox: VoiceCapability;
|
|
16
|
+
/** Whether voice input (recording) is available */
|
|
17
|
+
canRecordAudio: boolean;
|
|
18
|
+
/** Whether voice output (TTS playback) is available */
|
|
19
|
+
canPlayAudio: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Cache the detection result
|
|
23
|
+
let cachedDependencies: VoiceDependencies | null = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if a binary exists on PATH by attempting to run it.
|
|
27
|
+
*/
|
|
28
|
+
async function checkBinaryOnPath(binary: string): Promise<boolean> {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const proc = spawn(binary, ["--version"], {
|
|
31
|
+
stdio: ["ignore", "ignore", "ignore"],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
proc.on("error", () => resolve(false));
|
|
35
|
+
proc.on("close", (code) => resolve(code === 0));
|
|
36
|
+
|
|
37
|
+
// Timeout after 2 seconds
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
proc.kill();
|
|
40
|
+
resolve(false);
|
|
41
|
+
}, 2000);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Detect sox availability.
|
|
47
|
+
*/
|
|
48
|
+
async function detectSox(): Promise<VoiceCapability> {
|
|
49
|
+
const available = await checkBinaryOnPath("sox");
|
|
50
|
+
|
|
51
|
+
if (available) {
|
|
52
|
+
return {
|
|
53
|
+
available: true,
|
|
54
|
+
reason: "sox is installed.",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
available: false,
|
|
60
|
+
reason: "sox is not installed.",
|
|
61
|
+
hint: process.platform === "darwin" ? "Run: brew install sox" : "Install sox using your package manager.",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Detect all voice dependencies.
|
|
67
|
+
* Results are cached after first detection.
|
|
68
|
+
*/
|
|
69
|
+
export async function detectVoiceDependencies(): Promise<VoiceDependencies> {
|
|
70
|
+
if (cachedDependencies) {
|
|
71
|
+
return cachedDependencies;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const sox = await detectSox();
|
|
75
|
+
|
|
76
|
+
cachedDependencies = {
|
|
77
|
+
sox,
|
|
78
|
+
// Voice input requires sox for recording
|
|
79
|
+
canRecordAudio: sox.available,
|
|
80
|
+
// Voice output requires sox for playback
|
|
81
|
+
canPlayAudio: sox.available,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return cachedDependencies;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Get the cached voice dependencies (returns null if not yet detected).
|
|
89
|
+
*/
|
|
90
|
+
export function getCachedVoiceDependencies(): VoiceDependencies | null {
|
|
91
|
+
return cachedDependencies;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Clear the cached voice dependencies (useful for testing).
|
|
96
|
+
*/
|
|
97
|
+
export function clearVoiceDependenciesCache(): void {
|
|
98
|
+
cachedDependencies = null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Synchronously check if sox is available.
|
|
103
|
+
* Uses cached result if available, otherwise returns false.
|
|
104
|
+
*/
|
|
105
|
+
export function isSoxAvailable(): boolean {
|
|
106
|
+
return cachedDependencies?.sox.available ?? false;
|
|
107
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace manager for DAEMON sessions.
|
|
3
|
+
* Each session gets a persistent workspace directory for file operations,
|
|
4
|
+
* cloning repositories, and other agent tasks that shouldn't pollute
|
|
5
|
+
* the user's current working directory.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { promises as fs } from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { getAppConfigDir } from "./preferences";
|
|
11
|
+
import { debug } from "./debug-logger";
|
|
12
|
+
|
|
13
|
+
const WORKSPACES_DIR = "workspaces";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get the root directory for all session workspaces.
|
|
17
|
+
* @returns Path to ~/.config/daemon/workspaces/
|
|
18
|
+
*/
|
|
19
|
+
export function getWorkspacesRoot(): string {
|
|
20
|
+
return path.join(getAppConfigDir(), WORKSPACES_DIR);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the workspace path for a specific session.
|
|
25
|
+
* Does not create the directory - use ensureWorkspaceExists for that.
|
|
26
|
+
* @param sessionId - The session UUID
|
|
27
|
+
* @returns Path to ~/.config/daemon/workspaces/{sessionId}/
|
|
28
|
+
*/
|
|
29
|
+
export function getWorkspacePath(sessionId: string): string {
|
|
30
|
+
return path.join(getWorkspacesRoot(), sessionId);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Ensure the workspace directory exists for a session.
|
|
35
|
+
* Creates the directory if it doesn't exist.
|
|
36
|
+
* @param sessionId - The session UUID
|
|
37
|
+
* @returns The absolute path to the workspace directory
|
|
38
|
+
*/
|
|
39
|
+
export async function ensureWorkspaceExists(sessionId: string): Promise<string> {
|
|
40
|
+
const workspacePath = getWorkspacePath(sessionId);
|
|
41
|
+
try {
|
|
42
|
+
await fs.mkdir(workspacePath, { recursive: true });
|
|
43
|
+
debug.info("workspace-created", { sessionId, path: workspacePath });
|
|
44
|
+
} catch (error) {
|
|
45
|
+
// If directory already exists, that's fine
|
|
46
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
47
|
+
if (!err.message.includes("EEXIST")) {
|
|
48
|
+
debug.error("workspace-create-failed", { sessionId, message: err.message });
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return workspacePath;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Delete the workspace directory for a session.
|
|
57
|
+
* Called when a session is deleted.
|
|
58
|
+
* @param sessionId - The session UUID
|
|
59
|
+
*/
|
|
60
|
+
export async function deleteWorkspace(sessionId: string): Promise<void> {
|
|
61
|
+
const workspacePath = getWorkspacePath(sessionId);
|
|
62
|
+
try {
|
|
63
|
+
await fs.rm(workspacePath, { recursive: true, force: true });
|
|
64
|
+
debug.info("workspace-deleted", { sessionId, path: workspacePath });
|
|
65
|
+
} catch (error) {
|
|
66
|
+
// Log but don't throw - cleanup failures shouldn't break session deletion
|
|
67
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
68
|
+
debug.error("workspace-delete-failed", { sessionId, message: err.message });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if a workspace exists for a session.
|
|
74
|
+
* @param sessionId - The session UUID
|
|
75
|
+
* @returns true if the workspace directory exists
|
|
76
|
+
*/
|
|
77
|
+
export async function workspaceExists(sessionId: string): Promise<boolean> {
|
|
78
|
+
const workspacePath = getWorkspacePath(sessionId);
|
|
79
|
+
try {
|
|
80
|
+
const stat = await fs.stat(workspacePath);
|
|
81
|
+
return stat.isDirectory();
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|