oversky 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.
Files changed (49) hide show
  1. package/dist/auth.d.ts +26 -0
  2. package/dist/auth.js +221 -0
  3. package/dist/auth.js.map +1 -0
  4. package/dist/config.d.ts +20 -0
  5. package/dist/config.js +52 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/connection.d.ts +14 -0
  8. package/dist/connection.js +93 -0
  9. package/dist/connection.js.map +1 -0
  10. package/dist/executor.d.ts +20 -0
  11. package/dist/executor.js +187 -0
  12. package/dist/executor.js.map +1 -0
  13. package/dist/heartbeat.d.ts +10 -0
  14. package/dist/heartbeat.js +27 -0
  15. package/dist/heartbeat.js.map +1 -0
  16. package/dist/index.d.ts +2 -0
  17. package/dist/index.js +172 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/permissions.d.ts +17 -0
  20. package/dist/permissions.js +60 -0
  21. package/dist/permissions.js.map +1 -0
  22. package/dist/registry.d.ts +10 -0
  23. package/dist/registry.js +23 -0
  24. package/dist/registry.js.map +1 -0
  25. package/dist/security.d.ts +18 -0
  26. package/dist/security.js +181 -0
  27. package/dist/security.js.map +1 -0
  28. package/dist/tools/bash.d.ts +15 -0
  29. package/dist/tools/bash.js +58 -0
  30. package/dist/tools/bash.js.map +1 -0
  31. package/dist/tools/edit.d.ts +19 -0
  32. package/dist/tools/edit.js +49 -0
  33. package/dist/tools/edit.js.map +1 -0
  34. package/dist/tools/glob.d.ts +13 -0
  35. package/dist/tools/glob.js +34 -0
  36. package/dist/tools/glob.js.map +1 -0
  37. package/dist/tools/grep.d.ts +21 -0
  38. package/dist/tools/grep.js +95 -0
  39. package/dist/tools/grep.js.map +1 -0
  40. package/dist/tools/read.d.ts +14 -0
  41. package/dist/tools/read.js +31 -0
  42. package/dist/tools/read.js.map +1 -0
  43. package/dist/tools/write.d.ts +14 -0
  44. package/dist/tools/write.js +47 -0
  45. package/dist/tools/write.js.map +1 -0
  46. package/dist/ui.d.ts +12 -0
  47. package/dist/ui.js +77 -0
  48. package/dist/ui.js.map +1 -0
  49. package/package.json +67 -0
package/dist/auth.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ export declare function getToken(): string | null;
2
+ export declare function saveToken(token: string, expiresAt?: number): void;
3
+ export declare function removeToken(): void;
4
+ /**
5
+ * Start a local HTTP server to receive the OAuth callback token.
6
+ * Opens the browser to the server's daemon auth page, which redirects
7
+ * back to localhost with the JWT.
8
+ */
9
+ export declare function loginFlow(serverUrl: string): Promise<string>;
10
+ /**
11
+ * Prompt the user for a token directly in the terminal (fallback).
12
+ */
13
+ export declare function loginPrompt(): Promise<string>;
14
+ /**
15
+ * Device authorization flow — like `gh auth login`.
16
+ * 1. Request a one-time device code from the server
17
+ * 2. Print a URL for the user to open in their browser
18
+ * 3. Poll until the user approves the code in the browser
19
+ * 4. Receive and save the daemon JWT
20
+ */
21
+ export declare function loginWithUrl(serverUrl: string): Promise<string>;
22
+ /**
23
+ * Login via email/password against the API's /api/auth/login endpoint.
24
+ * Works with local dev servers without needing the /auth/daemon OAuth route.
25
+ */
26
+ export declare function loginWithCredentials(serverUrl: string): Promise<string>;
package/dist/auth.js ADDED
@@ -0,0 +1,221 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import http from 'node:http';
4
+ import { exec } from 'node:child_process';
5
+ import { createInterface } from 'node:readline';
6
+ import { getConfigDir } from './config.js';
7
+ const AUTH_PATH = path.join(getConfigDir(), 'auth.json');
8
+ export function getToken() {
9
+ try {
10
+ if (fs.existsSync(AUTH_PATH)) {
11
+ const raw = fs.readFileSync(AUTH_PATH, 'utf-8');
12
+ const data = JSON.parse(raw);
13
+ if (data.expiresAt && Date.now() > data.expiresAt) {
14
+ return null;
15
+ }
16
+ return data.token || null;
17
+ }
18
+ }
19
+ catch {
20
+ // Fall through
21
+ }
22
+ return null;
23
+ }
24
+ export function saveToken(token, expiresAt) {
25
+ const dir = getConfigDir();
26
+ if (!fs.existsSync(dir)) {
27
+ fs.mkdirSync(dir, { recursive: true });
28
+ }
29
+ const data = { token };
30
+ if (expiresAt) {
31
+ data.expiresAt = expiresAt;
32
+ }
33
+ fs.writeFileSync(AUTH_PATH, JSON.stringify(data, null, 2) + '\n', 'utf-8');
34
+ fs.chmodSync(AUTH_PATH, 0o600);
35
+ }
36
+ export function removeToken() {
37
+ try {
38
+ if (fs.existsSync(AUTH_PATH)) {
39
+ fs.unlinkSync(AUTH_PATH);
40
+ }
41
+ }
42
+ catch {
43
+ // Ignore
44
+ }
45
+ }
46
+ /**
47
+ * Start a local HTTP server to receive the OAuth callback token.
48
+ * Opens the browser to the server's daemon auth page, which redirects
49
+ * back to localhost with the JWT.
50
+ */
51
+ export async function loginFlow(serverUrl) {
52
+ return new Promise((resolve, reject) => {
53
+ const server = http.createServer((req, res) => {
54
+ const url = new URL(req.url || '/', `http://localhost`);
55
+ const token = url.searchParams.get('token');
56
+ const expiresAt = url.searchParams.get('expiresAt');
57
+ if (token) {
58
+ saveToken(token, expiresAt ? parseInt(expiresAt, 10) : undefined);
59
+ res.writeHead(200, { 'Content-Type': 'text/html' });
60
+ res.end('<html><body><h2>Login successful! You can close this tab.</h2></body></html>');
61
+ server.close();
62
+ resolve(token);
63
+ }
64
+ else {
65
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
66
+ res.end('Missing token');
67
+ }
68
+ });
69
+ server.listen(0, '127.0.0.1', () => {
70
+ const addr = server.address();
71
+ if (!addr || typeof addr === 'string') {
72
+ server.close();
73
+ reject(new Error('Failed to start callback server'));
74
+ return;
75
+ }
76
+ const callbackUrl = `http://127.0.0.1:${addr.port}/callback`;
77
+ const loginUrl = `${serverUrl}/auth/daemon?callback=${encodeURIComponent(callbackUrl)}`;
78
+ console.log(`\nOpen this URL in your browser to log in:\n ${loginUrl}\n`);
79
+ // Try to open browser
80
+ const openCmd = process.platform === 'darwin'
81
+ ? 'open'
82
+ : process.platform === 'win32'
83
+ ? 'start'
84
+ : 'xdg-open';
85
+ exec(`${openCmd} "${loginUrl}"`, () => {
86
+ // Ignore errors - user can open manually
87
+ });
88
+ });
89
+ // Timeout after 5 minutes
90
+ setTimeout(() => {
91
+ server.close();
92
+ reject(new Error('Login timed out after 5 minutes'));
93
+ }, 300_000);
94
+ });
95
+ }
96
+ /**
97
+ * Prompt the user for a token directly in the terminal (fallback).
98
+ */
99
+ export async function loginPrompt() {
100
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
101
+ return new Promise((resolve) => {
102
+ rl.question('Paste your JWT token: ', (token) => {
103
+ rl.close();
104
+ if (token.trim()) {
105
+ saveToken(token.trim());
106
+ resolve(token.trim());
107
+ }
108
+ else {
109
+ resolve('');
110
+ }
111
+ });
112
+ });
113
+ }
114
+ /**
115
+ * Device authorization flow — like `gh auth login`.
116
+ * 1. Request a one-time device code from the server
117
+ * 2. Print a URL for the user to open in their browser
118
+ * 3. Poll until the user approves the code in the browser
119
+ * 4. Receive and save the daemon JWT
120
+ */
121
+ export async function loginWithUrl(serverUrl) {
122
+ // Step 1: Get a device code
123
+ const codeRes = await fetch(`${serverUrl}/api/daemons/device-code`, {
124
+ method: 'POST',
125
+ headers: { 'Content-Type': 'application/json' },
126
+ });
127
+ if (!codeRes.ok) {
128
+ const body = await codeRes.text();
129
+ throw new Error(`Failed to get device code (${codeRes.status}): ${body}`);
130
+ }
131
+ const { code, verificationUrl, expiresIn } = await codeRes.json();
132
+ // Step 2: Show the URL to the user
133
+ console.log('');
134
+ console.log(` Open this URL in your browser to log in:\n`);
135
+ console.log(` ${verificationUrl}\n`);
136
+ console.log(` Or go to ${serverUrl}/login/device and enter code: ${code}\n`);
137
+ console.log(` Waiting for approval...`);
138
+ // Try to open browser automatically
139
+ const openCmd = process.platform === 'darwin'
140
+ ? 'open'
141
+ : process.platform === 'win32'
142
+ ? 'start'
143
+ : 'xdg-open';
144
+ exec(`${openCmd} "${verificationUrl}"`, () => { });
145
+ // Step 3: Poll for approval
146
+ const pollInterval = 2000; // 2s
147
+ const deadline = Date.now() + expiresIn * 1000;
148
+ while (Date.now() < deadline) {
149
+ await new Promise((r) => setTimeout(r, pollInterval));
150
+ try {
151
+ const statusRes = await fetch(`${serverUrl}/api/daemons/device-code/${encodeURIComponent(code)}/status`);
152
+ if (!statusRes.ok) {
153
+ if (statusRes.status === 404) {
154
+ throw new Error('Device code expired. Please try again.');
155
+ }
156
+ continue; // Transient error, keep polling
157
+ }
158
+ const data = await statusRes.json();
159
+ if (data.status === 'approved' && data.token) {
160
+ const expiresAt = data.expiresAt ? new Date(data.expiresAt).getTime() : undefined;
161
+ saveToken(data.token, expiresAt);
162
+ return data.token;
163
+ }
164
+ }
165
+ catch (err) {
166
+ if (err instanceof Error && err.message.includes('expired')) {
167
+ throw err;
168
+ }
169
+ // Transient network error, keep polling
170
+ }
171
+ }
172
+ throw new Error('Login timed out. Please try again.');
173
+ }
174
+ /**
175
+ * Login via email/password against the API's /api/auth/login endpoint.
176
+ * Works with local dev servers without needing the /auth/daemon OAuth route.
177
+ */
178
+ export async function loginWithCredentials(serverUrl) {
179
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
180
+ const ask = (question) => new Promise((resolve) => rl.question(question, resolve));
181
+ try {
182
+ const email = await ask(' Email: ');
183
+ const password = await ask(' Password: ');
184
+ rl.close();
185
+ console.log(' Authenticating...');
186
+ const res = await fetch(`${serverUrl}/api/auth/login`, {
187
+ method: 'POST',
188
+ headers: { 'Content-Type': 'application/json' },
189
+ body: JSON.stringify({ email: email.trim(), password: password.trim() }),
190
+ });
191
+ if (!res.ok) {
192
+ const body = await res.text();
193
+ throw new Error(`Login failed (${res.status}): ${body}`);
194
+ }
195
+ const data = await res.json();
196
+ if (!data.token) {
197
+ throw new Error('No token in login response');
198
+ }
199
+ // Now use the session token to get a long-lived daemon token
200
+ const daemonRes = await fetch(`${serverUrl}/api/daemons/token`, {
201
+ method: 'POST',
202
+ headers: {
203
+ 'Content-Type': 'application/json',
204
+ Authorization: `Bearer ${data.token}`,
205
+ },
206
+ });
207
+ if (daemonRes.ok) {
208
+ const daemonData = await daemonRes.json();
209
+ saveToken(daemonData.token, new Date(daemonData.expiresAt).getTime());
210
+ return daemonData.token;
211
+ }
212
+ // Fallback: use the session token directly if daemon token endpoint fails
213
+ saveToken(data.token);
214
+ return data.token;
215
+ }
216
+ catch (err) {
217
+ rl.close();
218
+ throw err;
219
+ }
220
+ }
221
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface PermissionsConfig {
2
+ autoApproveReads: boolean;
3
+ autoApproveWrites: boolean;
4
+ autoApproveBash: boolean;
5
+ blockedPaths: string[];
6
+ allowedWritePaths: string[];
7
+ }
8
+ export interface LimitsConfig {
9
+ maxFileReadSize: number;
10
+ bashTimeout: number;
11
+ maxConcurrentTools: number;
12
+ }
13
+ export interface DaemonConfig {
14
+ serverUrl: string;
15
+ permissions: PermissionsConfig;
16
+ limits: LimitsConfig;
17
+ }
18
+ export declare function loadConfig(): DaemonConfig;
19
+ export declare function saveConfig(config: DaemonConfig): void;
20
+ export declare function getConfigDir(): string;
package/dist/config.js ADDED
@@ -0,0 +1,52 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ const CONFIG_DIR = path.join(os.homedir(), '.oversky');
5
+ const CONFIG_PATH = path.join(CONFIG_DIR, 'daemon.json');
6
+ const DEFAULT_CONFIG = {
7
+ // TODO: Change to https://oversky.ai once custom domain is configured
8
+ serverUrl: 'https://desxylhmg82bz.cloudfront.net',
9
+ permissions: {
10
+ autoApproveReads: true,
11
+ autoApproveWrites: false,
12
+ autoApproveBash: false,
13
+ blockedPaths: ['.env', '.env.*', '*.pem', '*.key', '.ssh/'],
14
+ allowedWritePaths: ['src/**', 'tests/**'],
15
+ },
16
+ limits: {
17
+ maxFileReadSize: 524288, // 500KB
18
+ bashTimeout: 120000, // 120s
19
+ maxConcurrentTools: 5,
20
+ },
21
+ };
22
+ function ensureConfigDir() {
23
+ if (!fs.existsSync(CONFIG_DIR)) {
24
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
25
+ }
26
+ }
27
+ export function loadConfig() {
28
+ try {
29
+ if (fs.existsSync(CONFIG_PATH)) {
30
+ const raw = fs.readFileSync(CONFIG_PATH, 'utf-8');
31
+ const parsed = JSON.parse(raw);
32
+ return {
33
+ ...DEFAULT_CONFIG,
34
+ ...parsed,
35
+ permissions: { ...DEFAULT_CONFIG.permissions, ...parsed.permissions },
36
+ limits: { ...DEFAULT_CONFIG.limits, ...parsed.limits },
37
+ };
38
+ }
39
+ }
40
+ catch {
41
+ // Fall through to default
42
+ }
43
+ return { ...DEFAULT_CONFIG };
44
+ }
45
+ export function saveConfig(config) {
46
+ ensureConfigDir();
47
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf-8');
48
+ }
49
+ export function getConfigDir() {
50
+ return CONFIG_DIR;
51
+ }
52
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AAsBzB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACvD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEzD,MAAM,cAAc,GAAiB;IACnC,sEAAsE;IACtE,SAAS,EAAE,sCAAsC;IACjD,WAAW,EAAE;QACX,gBAAgB,EAAE,IAAI;QACtB,iBAAiB,EAAE,KAAK;QACxB,eAAe,EAAE,KAAK;QACtB,YAAY,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC;QAC3D,iBAAiB,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC;KAC1C;IACD,MAAM,EAAE;QACN,eAAe,EAAE,MAAM,EAAE,QAAQ;QACjC,WAAW,EAAE,MAAM,EAAE,OAAO;QAC5B,kBAAkB,EAAE,CAAC;KACtB;CACF,CAAC;AAEF,SAAS,eAAe;IACtB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,OAAO;gBACL,GAAG,cAAc;gBACjB,GAAG,MAAM;gBACT,WAAW,EAAE,EAAE,GAAG,cAAc,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,WAAW,EAAE;gBACrE,MAAM,EAAE,EAAE,GAAG,cAAc,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE;aACvD,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,0BAA0B;IAC5B,CAAC;IACD,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAoB;IAC7C,eAAe,EAAE,CAAC;IAClB,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AACjF,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { type Socket } from 'socket.io-client';
2
+ import { type DaemonConfig } from './config.js';
3
+ export interface DaemonConnection {
4
+ socket: Socket;
5
+ daemonId: string;
6
+ disconnect: () => void;
7
+ }
8
+ /**
9
+ * Connect to the OverSky server and register as a daemon.
10
+ * Handles reconnection with exponential backoff, heartbeat, and tool dispatch.
11
+ *
12
+ * Exported for library use (e.g., embedding in a desktop app).
13
+ */
14
+ export declare function connectDaemon(serverUrl: string, token: string, cwd: string, config: DaemonConfig): DaemonConnection;
@@ -0,0 +1,93 @@
1
+ import { io } from 'socket.io-client';
2
+ import os from 'node:os';
3
+ import { generateDaemonId, getDeviceName } from './registry.js';
4
+ import { startHeartbeat, stopHeartbeat } from './heartbeat.js';
5
+ import { executeToolRequest } from './executor.js';
6
+ import { showConnecting, showConnected, showDisconnected, showReconnecting, showError, showInfo, } from './ui.js';
7
+ const SUPPORTED_TOOLS = ['bash', 'read', 'write', 'edit', 'glob', 'grep'];
8
+ const VERSION = '0.1.0';
9
+ /**
10
+ * Connect to the OverSky server and register as a daemon.
11
+ * Handles reconnection with exponential backoff, heartbeat, and tool dispatch.
12
+ *
13
+ * Exported for library use (e.g., embedding in a desktop app).
14
+ */
15
+ export function connectDaemon(serverUrl, token, cwd, config) {
16
+ const daemonId = generateDaemonId(cwd);
17
+ const deviceName = getDeviceName();
18
+ showConnecting(serverUrl);
19
+ const socket = io(serverUrl, {
20
+ path: '/ws/agentic',
21
+ auth: { token },
22
+ transports: ['websocket', 'polling'],
23
+ reconnection: true,
24
+ reconnectionDelay: 1000,
25
+ reconnectionDelayMax: 5000,
26
+ reconnectionAttempts: 50,
27
+ });
28
+ // --- Connection lifecycle ---
29
+ socket.on('connect', () => {
30
+ showConnected();
31
+ // Register with the server
32
+ socket.emit('daemon:register', {
33
+ daemonId,
34
+ deviceName,
35
+ capabilities: SUPPORTED_TOOLS,
36
+ cwd,
37
+ platform: os.platform(),
38
+ arch: os.arch(),
39
+ version: VERSION,
40
+ nodeVersion: process.version,
41
+ });
42
+ });
43
+ socket.on('daemon:registered', (data) => {
44
+ showInfo(`Registered as ${data.daemonId}`);
45
+ startHeartbeat(socket, daemonId);
46
+ });
47
+ socket.on('disconnect', (reason) => {
48
+ stopHeartbeat();
49
+ showDisconnected(reason);
50
+ });
51
+ socket.io.on('reconnect_attempt', (attempt) => {
52
+ showReconnecting(attempt);
53
+ });
54
+ socket.io.on('reconnect', () => {
55
+ showConnected();
56
+ // Re-register after reconnection
57
+ socket.emit('daemon:register', {
58
+ daemonId,
59
+ deviceName,
60
+ capabilities: SUPPORTED_TOOLS,
61
+ cwd,
62
+ platform: os.platform(),
63
+ arch: os.arch(),
64
+ version: VERSION,
65
+ nodeVersion: process.version,
66
+ });
67
+ });
68
+ socket.io.on('reconnect_failed', () => {
69
+ showError('Reconnection failed after maximum attempts');
70
+ });
71
+ socket.on('connect_error', (err) => {
72
+ showError(`Connection error: ${err.message}`);
73
+ });
74
+ // --- Tool execution ---
75
+ socket.on('daemon:tool:request', async (data) => {
76
+ const response = await executeToolRequest(data, config, cwd);
77
+ socket.emit('daemon:tool:response', response);
78
+ });
79
+ // --- Error handling ---
80
+ socket.on('error:unauthorized', (data) => {
81
+ showError(`Unauthorized: ${data.event}${data.sessionId ? ` (session: ${data.sessionId})` : ''}`);
82
+ });
83
+ socket.on('error:validation', (data) => {
84
+ showError(`Validation error: ${data.event} - ${data.message}`);
85
+ });
86
+ // --- Cleanup ---
87
+ const disconnect = () => {
88
+ stopHeartbeat();
89
+ socket.disconnect();
90
+ };
91
+ return { socket, daemonId, disconnect };
92
+ }
93
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type DaemonConfig } from './config.js';
2
+ export interface ToolRequest {
3
+ toolName: string;
4
+ toolInput: Record<string, unknown>;
5
+ toolUseId: string;
6
+ sessionId: string;
7
+ timeout?: number;
8
+ }
9
+ export interface ToolResponse {
10
+ toolUseId: string;
11
+ sessionId: string;
12
+ result: unknown;
13
+ error: string | null;
14
+ duration: number;
15
+ }
16
+ /**
17
+ * Dispatch a tool request to the appropriate executor.
18
+ * Handles permission checking, execution, timing, and error wrapping.
19
+ */
20
+ export declare function executeToolRequest(request: ToolRequest, config: DaemonConfig, cwd: string): Promise<ToolResponse>;
@@ -0,0 +1,187 @@
1
+ import { checkPermission } from './permissions.js';
2
+ import { showToolExecution, showToolResult, showPermissionDenied } from './ui.js';
3
+ import { executeBash } from './tools/bash.js';
4
+ import { executeRead } from './tools/read.js';
5
+ import { executeWrite } from './tools/write.js';
6
+ import { executeEdit } from './tools/edit.js';
7
+ import { executeGlob } from './tools/glob.js';
8
+ import { executeGrep } from './tools/grep.js';
9
+ /** Active executions for concurrency limiting */
10
+ let activeCount = 0;
11
+ /** Wrap a promise with a timeout. Rejects if the promise doesn't settle in time. */
12
+ function withTimeout(promise, ms, message) {
13
+ return new Promise((resolve, reject) => {
14
+ const timer = setTimeout(() => reject(new Error(message)), ms);
15
+ promise.then((val) => { clearTimeout(timer); resolve(val); }, (err) => { clearTimeout(timer); reject(err); });
16
+ });
17
+ }
18
+ /**
19
+ * Dispatch a tool request to the appropriate executor.
20
+ * Handles permission checking, execution, timing, and error wrapping.
21
+ */
22
+ export async function executeToolRequest(request, config, cwd) {
23
+ const start = Date.now();
24
+ const { toolName, toolInput, toolUseId, sessionId } = request;
25
+ // Concurrency check
26
+ if (activeCount >= config.limits.maxConcurrentTools) {
27
+ return {
28
+ toolUseId,
29
+ sessionId,
30
+ result: null,
31
+ error: `Too many concurrent tool executions (max ${config.limits.maxConcurrentTools})`,
32
+ duration: Date.now() - start,
33
+ };
34
+ }
35
+ // Permission check
36
+ const decision = await checkPermission({ toolName, toolInput }, config, cwd);
37
+ if (decision === 'deny') {
38
+ showPermissionDenied(toolName);
39
+ return {
40
+ toolUseId,
41
+ sessionId,
42
+ result: null,
43
+ error: 'Permission denied by user',
44
+ duration: Date.now() - start,
45
+ };
46
+ }
47
+ activeCount++;
48
+ showToolExecution(toolName, describeTool(toolName, toolInput));
49
+ try {
50
+ // Apply timeout to all tools (Bash uses its own timeout internally,
51
+ // but non-Bash tools need an outer timeout to prevent indefinite hangs)
52
+ const timeout = request.timeout || (toolName.toLowerCase() === 'bash' ? config.limits.bashTimeout : 30_000);
53
+ const rawResult = await withTimeout(dispatch(toolName, toolInput, config, cwd, request.timeout), timeout, `${toolName} timed out after ${timeout}ms`);
54
+ const result = normalizeResult(toolName, rawResult);
55
+ const duration = Date.now() - start;
56
+ showToolResult(toolName, duration, true);
57
+ return { toolUseId, sessionId, result, error: null, duration };
58
+ }
59
+ catch (err) {
60
+ const duration = Date.now() - start;
61
+ const errorMsg = err instanceof Error ? err.message : String(err);
62
+ showToolResult(toolName, duration, false);
63
+ return { toolUseId, sessionId, result: null, error: errorMsg, duration };
64
+ }
65
+ finally {
66
+ activeCount--;
67
+ }
68
+ }
69
+ /**
70
+ * Route to the correct tool executor.
71
+ */
72
+ async function dispatch(toolName, toolInput, config, cwd, timeout) {
73
+ // SDK sends PascalCase tool names (Bash, Read, Write, etc.)
74
+ // Normalize to lowercase for matching
75
+ switch (toolName.toLowerCase()) {
76
+ case 'bash':
77
+ return executeBash({
78
+ command: toolInput.command,
79
+ cwd: toolInput.cwd,
80
+ timeout: timeout || config.limits.bashTimeout,
81
+ }, cwd, config.limits.bashTimeout);
82
+ case 'read':
83
+ return executeRead({
84
+ file_path: toolInput.file_path,
85
+ offset: toolInput.offset,
86
+ limit: toolInput.limit,
87
+ }, cwd, config.limits.maxFileReadSize);
88
+ case 'write':
89
+ return executeWrite({
90
+ file_path: toolInput.file_path,
91
+ content: toolInput.content,
92
+ }, cwd);
93
+ case 'edit':
94
+ return executeEdit({
95
+ file_path: toolInput.file_path,
96
+ old_string: toolInput.old_string,
97
+ new_string: toolInput.new_string,
98
+ replace_all: toolInput.replace_all,
99
+ }, cwd);
100
+ case 'glob':
101
+ return executeGlob({
102
+ pattern: toolInput.pattern,
103
+ path: toolInput.path,
104
+ }, cwd);
105
+ case 'grep':
106
+ return executeGrep({
107
+ pattern: toolInput.pattern,
108
+ path: toolInput.path,
109
+ glob: toolInput.glob,
110
+ output_mode: toolInput.output_mode,
111
+ '-i': toolInput['-i'],
112
+ '-n': toolInput['-n'],
113
+ '-C': toolInput['-C'],
114
+ '-A': toolInput['-A'],
115
+ '-B': toolInput['-B'],
116
+ head_limit: toolInput.head_limit,
117
+ }, cwd);
118
+ default:
119
+ throw new Error(`Unknown tool: ${toolName}`);
120
+ }
121
+ }
122
+ /**
123
+ * Normalize tool results to a string so the server can interpolate them
124
+ * directly into `_denyTool()` output without getting "[object Object]".
125
+ */
126
+ function normalizeResult(toolName, result) {
127
+ if (typeof result === 'string') {
128
+ return result;
129
+ }
130
+ if (result == null) {
131
+ return '';
132
+ }
133
+ const r = result;
134
+ switch (toolName.toLowerCase()) {
135
+ case 'bash': {
136
+ // BashOutput: { output, exitCode, timedOut? }
137
+ let text = r.output || '';
138
+ if (r.timedOut) {
139
+ text += '\n[Command timed out]';
140
+ }
141
+ if (r.exitCode !== 0 && r.exitCode != null) {
142
+ text += `\n[Exit code: ${r.exitCode}]`;
143
+ }
144
+ return text;
145
+ }
146
+ case 'read':
147
+ // ReadOutput: { content, totalLines, truncated? }
148
+ return r.content || '';
149
+ case 'write':
150
+ // WriteOutput: { file_path, bytes, created }
151
+ return `${r.created ? 'Created' : 'Updated'} ${r.file_path} (${r.bytes} bytes)`;
152
+ case 'edit':
153
+ // EditOutput: { file_path, replacements, lineRange }
154
+ return `Edited ${r.file_path}: ${r.replacements} replacement(s) at lines ${r.lineRange?.start}-${r.lineRange?.end}`;
155
+ case 'glob': {
156
+ // GlobOutput: { files, count }
157
+ const files = r.files;
158
+ return files?.length ? files.join('\n') : 'No files found';
159
+ }
160
+ case 'grep':
161
+ // GrepOutput: { output, matchCount, tool }
162
+ return r.output || 'No matches found';
163
+ default:
164
+ return JSON.stringify(result, null, 2);
165
+ }
166
+ }
167
+ function describeTool(toolName, toolInput) {
168
+ switch (toolName.toLowerCase()) {
169
+ case 'bash': {
170
+ const cmd = (toolInput.command || '');
171
+ return cmd.length > 60 ? cmd.slice(0, 60) + '...' : cmd;
172
+ }
173
+ case 'read':
174
+ return toolInput.file_path;
175
+ case 'write':
176
+ return toolInput.file_path;
177
+ case 'edit':
178
+ return toolInput.file_path;
179
+ case 'glob':
180
+ return toolInput.pattern;
181
+ case 'grep':
182
+ return `/${toolInput.pattern}/`;
183
+ default:
184
+ return toolName;
185
+ }
186
+ }
187
+ //# sourceMappingURL=executor.js.map