mirror of
https://github.com/ByteAtATime/raycast-linux.git
synced 2025-08-31 03:07:23 +00:00
176 lines
5.1 KiB
TypeScript
176 lines
5.1 KiB
TypeScript
import { createInterface } from 'readline';
|
|
import { writeLog, writeOutput } from './io';
|
|
import { runPlugin } from './plugin';
|
|
import { instances, navigationStack, toasts, browserExtensionState } from './state';
|
|
import { batchedUpdates, updateContainer } from './reconciler';
|
|
import { preferencesStore } from './preferences';
|
|
import type { FlareInstance } from './types';
|
|
import { handleResponse } from './api/rpc';
|
|
import { handleOAuthResponse, handleTokenResponse } from './api/oauth';
|
|
import { handleAiStreamChunk, handleAiStreamEnd, handleAiStreamError } from './api/ai';
|
|
|
|
process.on('unhandledRejection', (reason: unknown) => {
|
|
writeLog(`--- UNHANDLED PROMISE REJECTION ---`);
|
|
const stack = reason && typeof reason === 'object' && 'stack' in reason ? reason.stack : reason;
|
|
writeLog(stack);
|
|
});
|
|
|
|
const rl = createInterface({ input: process.stdin });
|
|
|
|
rl.on('line', (line) => {
|
|
batchedUpdates(() => {
|
|
try {
|
|
const command: { action: string; payload: unknown } = JSON.parse(line);
|
|
|
|
if (command.action.endsWith('-response')) {
|
|
const { requestId, result, error, state, code } = command.payload as {
|
|
requestId: string;
|
|
result?: unknown;
|
|
error?: string;
|
|
state?: string;
|
|
code?: string;
|
|
};
|
|
|
|
if (command.action === 'oauth-authorize-response') {
|
|
if (state && code) {
|
|
handleOAuthResponse(requestId, code, state, error);
|
|
}
|
|
} else if (command.action.startsWith('oauth-')) {
|
|
handleTokenResponse(requestId, result, error);
|
|
} else {
|
|
handleResponse(requestId, result, error);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (command.action === 'ai-stream-chunk') {
|
|
const payload = command.payload as { requestId: string; text: string };
|
|
handleAiStreamChunk(payload);
|
|
return;
|
|
}
|
|
|
|
if (command.action === 'ai-stream-end') {
|
|
const payload = command.payload as { requestId: string; full_text: string };
|
|
handleAiStreamEnd(payload);
|
|
return;
|
|
}
|
|
|
|
if (command.action === 'ai-stream-error') {
|
|
const payload = command.payload as { requestId: string; error: string };
|
|
handleAiStreamError(payload);
|
|
return;
|
|
}
|
|
|
|
switch (command.action) {
|
|
case 'run-plugin': {
|
|
const { pluginPath, mode, aiAccessStatus } = command.payload as {
|
|
pluginPath?: string;
|
|
commandName?: string;
|
|
mode?: 'view' | 'no-view';
|
|
aiAccessStatus: boolean;
|
|
};
|
|
runPlugin(pluginPath, mode, aiAccessStatus);
|
|
break;
|
|
}
|
|
case 'get-preferences': {
|
|
const { pluginName } = command.payload as { pluginName: string };
|
|
const preferences = preferencesStore.getAllPreferences();
|
|
writeOutput({
|
|
type: 'preference-values',
|
|
payload: {
|
|
pluginName,
|
|
values: preferences[pluginName] || {}
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
case 'set-preferences': {
|
|
const { pluginName, values } = command.payload as {
|
|
pluginName: string;
|
|
values: Record<string, unknown>;
|
|
};
|
|
preferencesStore.setPreferenceValues(pluginName, values);
|
|
break;
|
|
}
|
|
case 'pop-view': {
|
|
const previousElement = navigationStack.pop();
|
|
if (previousElement) {
|
|
updateContainer(previousElement);
|
|
} else {
|
|
writeOutput({ type: 'go-back-to-plugin-list', payload: {} });
|
|
}
|
|
break;
|
|
}
|
|
case 'dispatch-event': {
|
|
const { instanceId, handlerName, args } = command.payload as {
|
|
instanceId: number;
|
|
handlerName: string;
|
|
args: unknown[];
|
|
};
|
|
|
|
const instance = instances.get(instanceId);
|
|
if (!instance) {
|
|
writeLog(`Instance ${instanceId} not found.`);
|
|
return;
|
|
}
|
|
|
|
if (!('props' in instance)) {
|
|
return;
|
|
}
|
|
|
|
const flareInstance = instance as FlareInstance;
|
|
|
|
const props = flareInstance._unserializedProps;
|
|
const handler = props?.[handlerName];
|
|
|
|
if (typeof handler === 'function') {
|
|
handler(...args);
|
|
} else {
|
|
writeLog(
|
|
`Handler ${handlerName} not found or not a function on instance ${instanceId}`
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
case 'dispatch-toast-action': {
|
|
const { toastId, actionType } = command.payload as {
|
|
toastId: number;
|
|
actionType: 'primary' | 'secondary';
|
|
};
|
|
const toast = toasts.get(toastId);
|
|
if (toast) {
|
|
const action = actionType === 'primary' ? toast.primaryAction : toast.secondaryAction;
|
|
if (action?.onAction) {
|
|
action.onAction(toast);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'trigger-toast-hide': {
|
|
const { toastId } = command.payload as { toastId: number };
|
|
const toast = toasts.get(toastId);
|
|
toast?.hide();
|
|
break;
|
|
}
|
|
case 'browser-extension-connection-status': {
|
|
const { isConnected } = command.payload as { isConnected: boolean };
|
|
browserExtensionState.isConnected = isConnected;
|
|
break;
|
|
}
|
|
default:
|
|
writeLog(`Unknown command action: ${command.action}`);
|
|
}
|
|
} catch (err: unknown) {
|
|
const error =
|
|
err instanceof Error
|
|
? { message: err.message, stack: err.stack }
|
|
: { message: String(err) };
|
|
writeLog(`ERROR: ${error.message} \n ${error.stack ?? ''}`);
|
|
writeOutput({ type: 'error', payload: error.message });
|
|
|
|
throw err;
|
|
}
|
|
});
|
|
});
|
|
|
|
writeLog('Node.js Sidecar started successfully with React Reconciler.');
|