MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / createHttpShellHost

Function createHttpShellHost

packages/react/src/api/shell-host.ts:116–243  ·  view source on GitHub ↗
(options?: {
  readonly fetch?: typeof globalThis.fetch;
})

Source from the content-addressed store, hash-verified

114 * bearer (a desktop connection carries no auth and sends none).
115 */
116export const createHttpShellHost = (options?: {
117 readonly fetch?: typeof globalThis.fetch;
118}): HttpShellHost => {
119 const doFetch = options?.fetch ?? globalThis.fetch.bind(globalThis);
120
121 /**
122 * Every request this host makes, headed the same way as the typed API client
123 * (`api/client.tsx`'s `transformClient`): bearer when the connection carries
124 * one, plus the active org selector.
125 *
126 * The selector is what an org-scoped host (cloud) scopes the request by, and
127 * it fails CLOSED — a session request that omits it is rejected outright
128 * rather than falling back to the session's stored org. Without it here every
129 * artifact tool call 403'd with `no_organization`. Resolved per request, not
130 * captured at construction: the host is memoized for the page's lifetime,
131 * while the scope authority is set during render, so a captured value could
132 * outlive the org it named.
133 *
134 * Hosts without org scoping (local, desktop, self-host) produce no slug and
135 * so send no header, which is the same convention the neighboring clients
136 * follow.
137 */
138 const requestHeaders = (): Record<string, string> => {
139 const headers: Record<string, string> = {
140 "content-type": "application/json",
141 ...getExecutorOrganizationHeaders(),
142 };
143 const authorization = getExecutorServerAuthorizationHeader();
144 if (authorization) headers.authorization = authorization;
145 return headers;
146 };
147
148 const post = async (path: string, payload: Record<string, unknown>): Promise<unknown> => {
149 const response = await doFetch(`${getExecutorApiBaseUrl()}${path}`, {
150 method: "POST",
151 headers: requestHeaders(),
152 body: JSON.stringify(payload),
153 });
154 if (!response.ok) {
155 const text = await response.text();
156 // An approval that outlived its window is an ordinary outcome, not a
157 // fault: nothing ran, and the fix is to trigger the action again. Say that
158 // instead of surfacing the transport's error body, which reached the user
159 // as a raw `ExecutionNotFoundError` inside a JSON-RPC envelope.
160 if (response.status === APPROVAL_EXPIRED_STATUS) {
161 // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: the shell's tool proxy is Promise-based and surfaces rejections as component errors
162 throw new Error(APPROVAL_EXPIRED_MESSAGE);
163 }
164 // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: the shell's tool proxy is Promise-based and surfaces rejections as component errors
165 throw new Error(text || `Executor API request failed with ${response.status}`);
166 }
167 return response.json();
168 };
169
170 return {
171 getHostContext: () => ({ theme: prefersDark() ? "dark" : "light" }),
172
173 /**

Callers 2

ArtifactStageFunction · 0.90
shell-host.test.tsFile · 0.90

Calls 7

getExecutorApiBaseUrlFunction · 0.90
prefersDarkFunction · 0.85
requestHeadersFunction · 0.85
toShellToolResultFunction · 0.85
isResumeActionFunction · 0.85
parseResumeContentFunction · 0.85
postFunction · 0.70

Tested by

no test coverage detected