[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
147
application/state/useTerminalAiContexts.ts
Normal file
147
application/state/useTerminalAiContexts.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import type React from 'react';
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
|
||||
import { retainStableAiPanelContexts } from '../../domain/aiPanelContextsEqual';
|
||||
import {
|
||||
buildAITerminalSessionInfo,
|
||||
type AIPanelContext,
|
||||
} from '../../domain/buildAITerminalSessionInfo';
|
||||
import { collectSessionIds } from '../../domain/workspace';
|
||||
import type { TerminalContextReader } from '../../domain/terminalContextRead';
|
||||
import { detectLocalOs } from '../../lib/localShell';
|
||||
import type { Host, PortForwardingRule, TerminalSession, Workspace } from '../../types';
|
||||
|
||||
interface UseTerminalAiContextsOptions {
|
||||
hosts: Host[];
|
||||
hostsRef: React.MutableRefObject<Host[]>;
|
||||
portForwardingRules: PortForwardingRule[];
|
||||
portForwardingRulesRef: React.MutableRefObject<PortForwardingRule[]>;
|
||||
mountedAiTabIds: string[];
|
||||
sessionHostsMap: Map<string, Host>;
|
||||
sessions: TerminalSession[];
|
||||
sessionsRef: React.MutableRefObject<TerminalSession[]>;
|
||||
terminalContextReadersRef: React.MutableRefObject<Map<string, TerminalContextReader>>;
|
||||
workspaces: Workspace[];
|
||||
workspacesRef: React.MutableRefObject<Workspace[]>;
|
||||
}
|
||||
|
||||
export function useTerminalAiContexts({
|
||||
hosts,
|
||||
hostsRef,
|
||||
portForwardingRules,
|
||||
portForwardingRulesRef,
|
||||
mountedAiTabIds,
|
||||
sessionHostsMap,
|
||||
sessions,
|
||||
sessionsRef,
|
||||
terminalContextReadersRef,
|
||||
workspaces,
|
||||
workspacesRef,
|
||||
}: UseTerminalAiContextsOptions) {
|
||||
const previousAiContextsRef = useRef<Map<string, AIPanelContext> | null>(null);
|
||||
const aiContextsByTabId = useMemo(() => {
|
||||
const localOs = detectLocalOs(navigator.userAgent || navigator.platform);
|
||||
const sessionById = new Map<string, TerminalSession>(sessions.map((session) => [session.id, session]));
|
||||
const workspaceById = new Map<string, Workspace>(workspaces.map((workspace) => [workspace.id, workspace]));
|
||||
const allHosts = hosts;
|
||||
const tabIds = new Set<string>(mountedAiTabIds);
|
||||
|
||||
const contexts = new Map<string, AIPanelContext>();
|
||||
|
||||
for (const tabId of tabIds) {
|
||||
const workspace = workspaceById.get(tabId);
|
||||
if (workspace) {
|
||||
const sessionIds = collectSessionIds(workspace.root);
|
||||
contexts.set(tabId, {
|
||||
scopeType: 'workspace',
|
||||
scopeTargetId: workspace.id,
|
||||
scopeHostIds: sessionIds
|
||||
.map((sessionId) => sessionById.get(sessionId)?.hostId)
|
||||
.filter((hostId): hostId is string => !!hostId),
|
||||
scopeLabel: workspace.title,
|
||||
focusedSessionId: workspace.focusedSessionId,
|
||||
terminalSessions: sessionIds.map((sessionId) =>
|
||||
buildAITerminalSessionInfo(
|
||||
sessionById.get(sessionId),
|
||||
sessionHostsMap.get(sessionId),
|
||||
localOs,
|
||||
{ allHosts, portForwardingRules },
|
||||
),
|
||||
),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const session = sessionById.get(tabId);
|
||||
if (!session) continue;
|
||||
|
||||
contexts.set(tabId, {
|
||||
scopeType: 'terminal',
|
||||
scopeTargetId: session.id,
|
||||
scopeHostIds: session.hostId ? [session.hostId] : [],
|
||||
scopeLabel: session.hostLabel ?? '',
|
||||
terminalSessions: [
|
||||
buildAITerminalSessionInfo(
|
||||
session,
|
||||
sessionHostsMap.get(session.id),
|
||||
localOs,
|
||||
{ allHosts, portForwardingRules },
|
||||
),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
const retained = retainStableAiPanelContexts(previousAiContextsRef.current, contexts);
|
||||
previousAiContextsRef.current = retained;
|
||||
return retained;
|
||||
}, [sessions, workspaces, mountedAiTabIds, sessionHostsMap, hosts, portForwardingRules]);
|
||||
|
||||
const resolveAIExecutorContext = useCallback((scope: {
|
||||
type: 'terminal' | 'workspace';
|
||||
targetId?: string;
|
||||
label?: string;
|
||||
}) => {
|
||||
const latestWorkspaces = workspacesRef.current;
|
||||
const latestSessions = sessionsRef.current;
|
||||
const latestHosts = hostsRef.current;
|
||||
const localOs = detectLocalOs(navigator.userAgent || navigator.platform);
|
||||
const sessionIds = scope.type === 'workspace'
|
||||
? (() => {
|
||||
const workspace = scope.targetId ? latestWorkspaces.find((w) => w.id === scope.targetId) : undefined;
|
||||
return workspace?.root ? collectSessionIds(workspace.root) : [];
|
||||
})()
|
||||
: scope.targetId ? [scope.targetId] : [];
|
||||
|
||||
const workspaceName = scope.type === 'workspace'
|
||||
? latestWorkspaces.find((w) => w.id === scope.targetId)?.title ?? scope.label
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
sessions: sessionIds.map((sid) => {
|
||||
const session = latestSessions.find((s) => s.id === sid);
|
||||
const host = session ? sessionHostsMap.get(session.id) : undefined;
|
||||
return buildAITerminalSessionInfo(session, host, localOs, {
|
||||
allHosts: latestHosts,
|
||||
portForwardingRules: portForwardingRulesRef.current,
|
||||
});
|
||||
}),
|
||||
workspaceId: scope.type === 'workspace' ? scope.targetId : undefined,
|
||||
workspaceName,
|
||||
readTerminalContext: (request: Parameters<TerminalContextReader>[0]) => {
|
||||
const reader = terminalContextReadersRef.current.get(request.sessionId);
|
||||
if (!reader) {
|
||||
return Promise.resolve({
|
||||
ok: false as const,
|
||||
error: `Terminal session "${request.sessionId}" has no readable terminal buffer.`,
|
||||
});
|
||||
}
|
||||
return reader(request);
|
||||
},
|
||||
};
|
||||
}, [hostsRef, portForwardingRulesRef, sessionHostsMap, sessionsRef, terminalContextReadersRef, workspacesRef]);
|
||||
|
||||
return {
|
||||
aiContextsByTabId,
|
||||
resolveAIExecutorContext,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user